hecks 1.2.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 (358) 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 +60 -2
  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/postgres_era.adapter +5 -0
  23. data/lib/hecks/adapters/driven/prism.rb +4 -4
  24. data/lib/hecks/adapters/driven/sql_query_builder.rb +34 -22
  25. data/lib/hecks/adapters/driven/sqlite/codec.rb +38 -10
  26. data/lib/hecks/adapters/driven/sqlite/projection.rb +60 -32
  27. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +12 -12
  28. data/lib/hecks/adapters/driven/sqlite.rb +181 -21
  29. data/lib/hecks/adapters/driven.rb +4 -4
  30. data/lib/hecks/adapters/driving/github_webhook.rb +145 -0
  31. data/lib/hecks/behaviors/dsl.rb +2 -2
  32. data/lib/hecks/behaviors/expectations.rb +51 -23
  33. data/lib/hecks/behaviors/ir.rb +1 -1
  34. data/lib/hecks/behaviors/rspec.rb +1 -1
  35. data/lib/hecks/behaviors/runner.rb +2 -2
  36. data/lib/hecks/behaviors.rb +1 -1
  37. data/lib/hecks/bluebook/aggregate.rb +13 -13
  38. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +10 -10
  39. data/lib/hecks/bluebook/assembly/build.rb +1 -1
  40. data/lib/hecks/bluebook/assembly/contract.rb +39 -16
  41. data/lib/hecks/bluebook/assembly/contracts.rb +40 -38
  42. data/lib/hecks/bluebook/assembly/marks.rb +18 -18
  43. data/lib/hecks/bluebook/assembly/specializer.rb +19 -21
  44. data/lib/hecks/bluebook/assembly.rb +9 -9
  45. data/lib/hecks/bluebook/attribute.rb +9 -9
  46. data/lib/hecks/bluebook/behaviour/aggregate.rb +11 -11
  47. data/lib/hecks/bluebook/behaviour/attribute.rb +5 -5
  48. data/lib/hecks/bluebook/behaviour/chapter.rb +23 -5
  49. data/lib/hecks/bluebook/behaviour/command.rb +23 -23
  50. data/lib/hecks/bluebook/behaviour/domain_port.rb +27 -3
  51. data/lib/hecks/bluebook/behaviour/entity.rb +8 -8
  52. data/lib/hecks/bluebook/behaviour/hexagon.rb +4 -4
  53. data/lib/hecks/bluebook/behaviour/lifecycle.rb +5 -5
  54. data/lib/hecks/bluebook/behaviour/policy.rb +12 -12
  55. data/lib/hecks/bluebook/behaviour/process_manager.rb +7 -7
  56. data/lib/hecks/bluebook/behaviour/query.rb +1 -1
  57. data/lib/hecks/bluebook/behaviour/read_model.rb +8 -8
  58. data/lib/hecks/bluebook/behaviour/traits.rb +12 -12
  59. data/lib/hecks/bluebook/behaviour/value_object.rb +6 -6
  60. data/lib/hecks/bluebook/capabilities.rb +27 -0
  61. data/lib/hecks/bluebook/chapter.rb +28 -9
  62. data/lib/hecks/bluebook/command.rb +12 -12
  63. data/lib/hecks/bluebook/domain_port.rb +9 -9
  64. data/lib/hecks/bluebook/dsl/adapter_builder.rb +24 -0
  65. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +49 -49
  66. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +96 -96
  67. data/lib/hecks/bluebook/dsl/attribute_collector.rb +41 -41
  68. data/lib/hecks/bluebook/dsl/binding_proxy.rb +22 -2
  69. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +111 -74
  70. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +48 -30
  71. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
  72. data/lib/hecks/bluebook/dsl/command_builder.rb +103 -103
  73. data/lib/hecks/bluebook/dsl/const_shim.rb +46 -15
  74. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +90 -25
  75. data/lib/hecks/bluebook/dsl/entity_builder.rb +56 -56
  76. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
  77. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +89 -30
  78. data/lib/hecks/bluebook/dsl/identity_declaration.rb +17 -17
  79. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
  80. data/lib/hecks/bluebook/dsl/policy_builder.rb +30 -21
  81. data/lib/hecks/bluebook/dsl/port_builder.rb +38 -7
  82. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +56 -22
  83. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +35 -35
  84. data/lib/hecks/bluebook/dsl/query_builder.rb +5 -5
  85. data/lib/hecks/bluebook/dsl/read_model_builder.rb +34 -34
  86. data/lib/hecks/bluebook/dsl/rule_reference.rb +41 -39
  87. data/lib/hecks/bluebook/dsl/translation_builder.rb +9 -9
  88. data/lib/hecks/bluebook/dsl/value_object_builder.rb +16 -16
  89. data/lib/hecks/bluebook/dsl/word_gate.rb +59 -53
  90. data/lib/hecks/bluebook/dsl/world_builder.rb +51 -8
  91. data/lib/hecks/bluebook/entity.rb +11 -11
  92. data/lib/hecks/bluebook/expression/ast_json.rb +20 -20
  93. data/lib/hecks/bluebook/expression/ast_reader.rb +3 -3
  94. data/lib/hecks/bluebook/expression/canonical_form.rb +9 -9
  95. data/lib/hecks/bluebook/expression/evaluator.rb +18 -18
  96. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +18 -18
  97. data/lib/hecks/bluebook/expression/resolver.rb +60 -62
  98. data/lib/hecks/bluebook/hexagon.rb +1 -1
  99. data/lib/hecks/bluebook/lifecycle.rb +1 -1
  100. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -1
  101. data/lib/hecks/bluebook/meta_validator/judge.rb +122 -86
  102. data/lib/hecks/bluebook/meta_validator/plan.rb +39 -39
  103. data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -2
  104. data/lib/hecks/bluebook/meta_validator/readings.rb +43 -43
  105. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +44 -37
  106. data/lib/hecks/bluebook/meta_validator/shapes.rb +25 -21
  107. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +145 -31
  108. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +6 -6
  109. data/lib/hecks/bluebook/meta_validator/world_judge.rb +5 -5
  110. data/lib/hecks/bluebook/meta_validator.rb +70 -70
  111. data/lib/hecks/bluebook/model_check.rb +301 -84
  112. data/lib/hecks/bluebook/pattern_subset.rb +9 -9
  113. data/lib/hecks/bluebook/policy.rb +15 -13
  114. data/lib/hecks/bluebook/process_manager.rb +14 -14
  115. data/lib/hecks/bluebook/project_register.rb +6 -6
  116. data/lib/hecks/bluebook/query.rb +4 -4
  117. data/lib/hecks/bluebook/read_model.rb +14 -14
  118. data/lib/hecks/bluebook/reference.rb +8 -8
  119. data/lib/hecks/bluebook/smoke_test.rb +19 -19
  120. data/lib/hecks/bluebook/synthesizer.rb +12 -12
  121. data/lib/hecks/bluebook/translation.rb +4 -4
  122. data/lib/hecks/bluebook/value_object.rb +6 -6
  123. data/lib/hecks/bluebook.rb +2 -2
  124. data/lib/hecks/codemod/legacy_dispatch_args.rb +299 -0
  125. data/lib/hecks/codemod/legacy_dispatch_recorder.rb +186 -0
  126. data/lib/hecks/codemod.rb +36 -35
  127. data/lib/hecks/construct.rb +6 -6
  128. data/lib/hecks/corpus.rb +317 -0
  129. data/lib/hecks/deprecation.rb +95 -0
  130. data/lib/hecks/doc/reference.rb +19 -19
  131. data/lib/hecks/embryonaut_bluebook.rb +11 -11
  132. data/lib/hecks/facade/cli_door.rb +69 -10
  133. data/lib/hecks/facade/cli_runner.rb +105 -24
  134. data/lib/hecks/facade/command_request.rb +23 -0
  135. data/lib/hecks/facade/handle.rb +79 -32
  136. data/lib/hecks/facade/json_door.rb +106 -25
  137. data/lib/hecks/facade/surface/aggregate_door.rb +42 -27
  138. data/lib/hecks/facade/surface/chapter.rb +26 -17
  139. data/lib/hecks/facade/surface.rb +16 -3
  140. data/lib/hecks/facade.rb +15 -4
  141. data/lib/hecks/forms/app.rb +46 -30
  142. data/lib/hecks/forms/command_form_renderer.rb +70 -9
  143. data/lib/hecks/forms/field_renderer.rb +142 -6
  144. data/lib/hecks/forms/field_shape.rb +183 -20
  145. data/lib/hecks/forms/html.rb +51 -7
  146. data/lib/hecks/forms/index_renderer.rb +14 -2
  147. data/lib/hecks/forms/params.rb +120 -23
  148. data/lib/hecks/forms/port_argument.rb +2 -2
  149. data/lib/hecks/forms/query_form_renderer.rb +2 -2
  150. data/lib/hecks/forms/record_renderer.rb +2 -2
  151. data/lib/hecks/forms/record_table.rb +1 -1
  152. data/lib/hecks/forms/value_object_shape.rb +3 -3
  153. data/lib/hecks/forms.rb +24 -4
  154. data/lib/hecks/fqn.rb +1 -1
  155. data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
  156. data/lib/hecks/framework.rb +48 -17
  157. data/lib/hecks/freezer.rb +11 -11
  158. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +90 -90
  159. data/lib/hecks/fuzzing/combination_miner.rb +119 -0
  160. data/lib/hecks/fuzzing/concurrent_dispatch.rb +361 -0
  161. data/lib/hecks/fuzzing/coverage_campaign.rb +118 -0
  162. data/lib/hecks/fuzzing/differential.rb +158 -0
  163. data/lib/hecks/fuzzing/domain_generator.rb +694 -0
  164. data/lib/hecks/fuzzing/era_boundary.rb +124 -0
  165. data/lib/hecks/fuzzing/form_census.rb +199 -0
  166. data/lib/hecks/fuzzing/generated_domain_check.rb +95 -0
  167. data/lib/hecks/fuzzing/invalid_value_generator.rb +6 -6
  168. data/lib/hecks/fuzzing/isolated_boot.rb +226 -38
  169. data/lib/hecks/fuzzing/nondeterministic.rb +67 -0
  170. data/lib/hecks/fuzzing/persistence_parity.rb +161 -0
  171. data/lib/hecks/fuzzing/properties/corrections.rb +100 -0
  172. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +321 -41
  173. data/lib/hecks/fuzzing/properties/guards.rb +129 -26
  174. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +32 -32
  175. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +31 -11
  176. data/lib/hecks/fuzzing/properties/outbox.rb +142 -0
  177. data/lib/hecks/fuzzing/properties/querying.rb +17 -17
  178. data/lib/hecks/fuzzing/properties.rb +84 -39
  179. data/lib/hecks/fuzzing/qa_settings.rb +152 -0
  180. data/lib/hecks/fuzzing/replay.rb +224 -114
  181. data/lib/hecks/fuzzing/rotation_priority.rb +94 -0
  182. data/lib/hecks/fuzzing/rust_gap_manifest.rb +113 -0
  183. data/lib/hecks/fuzzing/self_consistency.rb +676 -0
  184. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +526 -0
  185. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +101 -30
  186. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +55 -8
  187. data/lib/hecks/fuzzing/sequence_generator/picker.rb +31 -11
  188. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +123 -32
  189. data/lib/hecks/fuzzing/sequence_generator.rb +152 -31
  190. data/lib/hecks/fuzzing/shrinker.rb +197 -0
  191. data/lib/hecks/fuzzing/structural_skips.rb +39 -0
  192. data/lib/hecks/fuzzing/sweep_depth.rb +53 -0
  193. data/lib/hecks/fuzzing/target_capabilities.rb +180 -0
  194. data/lib/hecks/fuzzing/value_generator.rb +66 -14
  195. data/lib/hecks/fuzzing.rb +11 -0
  196. data/lib/hecks/grammar/evolve.rb +10 -10
  197. data/lib/hecks/grammar.rb +7 -7
  198. data/lib/hecks/ir.rb +13 -13
  199. data/lib/hecks/language/bluebook/bluebook.bluebook +41 -0
  200. data/lib/hecks/language/bluebook/policy.bluebook +11 -1
  201. data/lib/hecks/language/bluebook/vocabulary.bluebook +365 -15
  202. data/lib/hecks/language/oidc.json +5 -0
  203. data/lib/hecks/literal.rb +9 -9
  204. data/lib/hecks/naming.rb +89 -21
  205. data/lib/hecks/ports/access_control.rb +58 -2
  206. data/lib/hecks/ports/agent/answers.rb +83 -6
  207. data/lib/hecks/ports/agent.rb +119 -35
  208. data/lib/hecks/ports/authentication.rb +44 -4
  209. data/lib/hecks/ports/authorization.rb +53 -11
  210. data/lib/hecks/ports/clock.rb +42 -23
  211. data/lib/hecks/ports/extraction.rb +16 -0
  212. data/lib/hecks/ports/identity_assignment.rb +24 -2
  213. data/lib/hecks/ports/identity_generation.rb +17 -3
  214. data/lib/hecks/ports/identity_resolution.rb +18 -1
  215. data/lib/hecks/ports/loading.rb +4 -0
  216. data/lib/hecks/ports/persistence/append_only.rb +172 -8
  217. data/lib/hecks/ports/persistence/binding_policy.rb +34 -0
  218. data/lib/hecks/ports/persistence/codec_boundary.rb +178 -0
  219. data/lib/hecks/ports/persistence/execution.rb +4 -0
  220. data/lib/hecks/ports/persistence/null_saga_store.rb +12 -1
  221. data/lib/hecks/ports/persistence/plugin.rb +42 -4
  222. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +218 -25
  223. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +77 -9
  224. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +81 -24
  225. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +29 -18
  226. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +144 -60
  227. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +103 -8
  228. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +98 -23
  229. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +282 -109
  230. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +63 -25
  231. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +160 -58
  232. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +51 -28
  233. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +28 -5
  234. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +25 -12
  235. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +129 -34
  236. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +51 -6
  237. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +44 -6
  238. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +16 -0
  239. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +57 -4
  240. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +25 -2
  241. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +338 -83
  242. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +68 -10
  243. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +9 -3
  244. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +9 -2
  245. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +42 -8
  246. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +16 -1
  247. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +36 -5
  248. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +23 -3
  249. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +18 -19
  250. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +5 -5
  251. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +1 -1
  252. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -2
  253. data/lib/hecks/ports/persistence/plugins/era.rb +12 -2
  254. data/lib/hecks/ports/persistence/remote_runtime.rb +9 -2
  255. data/lib/hecks/ports/persistence/repository_factory.rb +29 -6
  256. data/lib/hecks/ports/persistence/state_codec.rb +319 -0
  257. data/lib/hecks/ports/persistence.rb +36 -1
  258. data/lib/hecks/ports/projection.rb +61 -7
  259. data/lib/hecks/ports/query/in_memory.rb +3 -3
  260. data/lib/hecks/ports/query/ordering.rb +6 -6
  261. data/lib/hecks/ports/query.rb +35 -0
  262. data/lib/hecks/projections/bootstrap_table.rb +112 -0
  263. data/lib/hecks/projections/diagrams.rb +75 -75
  264. data/lib/hecks/projections/glossary/html.rb +250 -0
  265. data/lib/hecks/projections/glossary/markdown.rb +105 -0
  266. data/lib/hecks/projections/glossary/mermaid.rb +110 -0
  267. data/lib/hecks/projections/glossary/page.css +271 -0
  268. data/lib/hecks/projections/glossary/page.js +72 -0
  269. data/lib/hecks/projections/glossary/sections.rb +17 -0
  270. data/lib/hecks/projections/glossary/sentences.rb +205 -0
  271. data/lib/hecks/projections/glossary.rb +214 -286
  272. data/lib/hecks/projections/ir.rb +1 -1
  273. data/lib/hecks/projections/model/deviations.rb +18 -17
  274. data/lib/hecks/projections/model.rb +25 -21
  275. data/lib/hecks/projections/oidc.rb +7 -7
  276. data/lib/hecks/projections/parser_table.rb +5 -5
  277. data/lib/hecks/projections/reference.rb +3 -3
  278. data/lib/hecks/projections/rust_vocabulary.rb +443 -0
  279. data/lib/hecks/projections/shape.rb +2 -2
  280. data/lib/hecks/projections/statements.rb +11 -11
  281. data/lib/hecks/projections/vocabulary.rb +9 -9
  282. data/lib/hecks/projections.rb +5 -3
  283. data/lib/hecks/projector/cli_projector.rb +29 -29
  284. data/lib/hecks/projector/docs_projector.rb +13 -13
  285. data/lib/hecks/projector/exporter.rb +42 -21
  286. data/lib/hecks/projector/ir_projector.rb +1 -1
  287. data/lib/hecks/projector/narrate_projector.rb +15 -22
  288. data/lib/hecks/projector/target.rb +13 -13
  289. data/lib/hecks/projector.rb +15 -15
  290. data/lib/hecks/query_ir.rb +47 -47
  291. data/lib/hecks/query_specification/common/comparators.rb +19 -3
  292. data/lib/hecks/query_specification/common/comparison.rb +132 -24
  293. data/lib/hecks/query_specification/common/dsl.rb +65 -9
  294. data/lib/hecks/query_specification/common/null_policy.rb +57 -13
  295. data/lib/hecks/query_specification/common/null_semantics.rb +4 -0
  296. data/lib/hecks/query_specification/common/options.rb +25 -0
  297. data/lib/hecks/query_specification/field_path.rb +69 -15
  298. data/lib/hecks/query_specification/hop_path.rb +57 -20
  299. data/lib/hecks/query_specification/read_model/specification.rb +4 -0
  300. data/lib/hecks/rendering.rb +3 -3
  301. data/lib/hecks/router/namespace_installer.rb +3 -3
  302. data/lib/hecks/router.rb +1 -1
  303. data/lib/hecks/runtime/aggregate_lock.rb +11 -11
  304. data/lib/hecks/runtime/boot_gates.rb +3 -3
  305. data/lib/hecks/runtime/caller.rb +8 -8
  306. data/lib/hecks/runtime/capability_graph.rb +2 -2
  307. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +22 -25
  308. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +64 -67
  309. data/lib/hecks/runtime/command_interpreter.rb +159 -102
  310. data/lib/hecks/runtime/command_rules/admissibility.rb +69 -69
  311. data/lib/hecks/runtime/command_rules/arithmetic.rb +137 -91
  312. data/lib/hecks/runtime/command_rules/authorization.rb +38 -17
  313. data/lib/hecks/runtime/command_rules/emission.rb +18 -1
  314. data/lib/hecks/runtime/command_rules/references.rb +94 -19
  315. data/lib/hecks/runtime/command_rules.rb +3 -0
  316. data/lib/hecks/runtime/dependency_planning.rb +11 -11
  317. data/lib/hecks/runtime/dispatcher.rb +283 -90
  318. data/lib/hecks/runtime/entity_element.rb +282 -46
  319. data/lib/hecks/runtime/entity_interpreter.rb +160 -69
  320. data/lib/hecks/runtime/errors.rb +19 -19
  321. data/lib/hecks/runtime/event.rb +6 -6
  322. data/lib/hecks/runtime/identity.rb +22 -22
  323. data/lib/hecks/runtime/instance.rb +39 -14
  324. data/lib/hecks/runtime/interpreting.rb +12 -12
  325. data/lib/hecks/runtime/invocation.rb +276 -0
  326. data/lib/hecks/runtime/loader.rb +14 -14
  327. data/lib/hecks/runtime/outbox.rb +23 -23
  328. data/lib/hecks/runtime/policy_interpreter.rb +54 -54
  329. data/lib/hecks/runtime/port_operation_interpreter.rb +22 -19
  330. data/lib/hecks/runtime/query_interpreter.rb +111 -56
  331. data/lib/hecks/runtime/reaction_invocation.rb +76 -9
  332. data/lib/hecks/runtime/read_model_interpreter.rb +40 -40
  333. data/lib/hecks/runtime/rebuild_sweep.rb +4 -4
  334. data/lib/hecks/runtime/reference_hop.rb +6 -6
  335. data/lib/hecks/runtime/refusal_wording.rb +92 -112
  336. data/lib/hecks/runtime/registry/saga_persistence.rb +21 -21
  337. data/lib/hecks/runtime/registry/verification.rb +36 -26
  338. data/lib/hecks/runtime/registry.rb +56 -27
  339. data/lib/hecks/runtime/remote_dispatcher.rb +38 -23
  340. data/lib/hecks/runtime/routing.rb +10 -88
  341. data/lib/hecks/runtime/saga_interpreter/correlation.rb +17 -17
  342. data/lib/hecks/runtime/saga_interpreter.rb +93 -56
  343. data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
  344. data/lib/hecks/runtime/tenant_check.rb +9 -9
  345. data/lib/hecks/runtime/tenant_scope.rb +5 -5
  346. data/lib/hecks/runtime/value/admission.rb +75 -30
  347. data/lib/hecks/runtime/value/coercion.rb +379 -226
  348. data/lib/hecks/runtime/value/entity_list_coercion.rb +248 -0
  349. data/lib/hecks/runtime/value.rb +28 -23
  350. data/lib/hecks/runtime.rb +7 -7
  351. data/lib/hecks/storehouse.rb +64 -64
  352. data/lib/hecks/version.rb +3 -3
  353. data/lib/hecks/vocabulary.rb +207 -5
  354. data/lib/hecks.rb +13 -11
  355. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +11 -11
  356. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +14 -12
  357. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +11 -11
  358. metadata +43 -2
@@ -14,31 +14,34 @@ module Hecks
14
14
  include AttributeCollector
15
15
  include WordGate
16
16
 
17
- # `to:` — THE SANCTIONED REPLACEMENT for `reference_to` inside an
18
- # operation body, added here rather than left as a documented-but-
19
- # unbuilt promise: reference_to_impl's own refusal message has told
20
- # authors to "pass the receiving aggregate in to:" since #335, but
21
- # no `to:` argument existed anywhere in DomainPort's own grammar
22
- # (domain_port.bluebook) for operation/tells/asks to receive it —
23
- # confirmed by grep across every lib/hecks/language file, not
24
- # assumed.
17
+ # `to:` — the sanctioned replacement for `reference_to` inside an
18
+ # operation body: `reference_to_impl`'s own refusal message tells
19
+ # authors to "pass the receiving aggregate in to:", and this is
20
+ # the argument DomainPort's own grammar (domain_port.bluebook)
21
+ # gives operation/tells/asks to receive it.
25
22
  #
26
- # GENUINE ROUTING METADATA, NOT AN ATTRIBUTE — matching
23
+ # **Genuine routing metadata, not an attribute** — matching
27
24
  # rust/parser/src/parse/domain_port.rs's own header comment ("The
28
25
  # receiving aggregate is routing metadata supplied by to:, not an
29
26
  # operation attribute"), which anticipated this shape before either
30
27
  # side actually built it. Stored separately (below, threaded to
31
28
  # PortOperation as `to:`) rather than reusing reference_to_impl's
32
29
  # own attribute-adding path — Dispatcher#port_invocation
33
- # (lib/hecks/runtime/dispatcher.rb) is the ONE place that resolves
30
+ # (lib/hecks/runtime/dispatcher.rb) is the one place that resolves
34
31
  # routing at dispatch time; it gained a second, purely additive
35
- # branch for this (falls back to a plain attribute NAMED for the
32
+ # branch for this (falls back to a plain attribute named for the
36
33
  # owning aggregate's own identified_by field, the same "declare
37
34
  # only external facts with attribute" the refusal message
38
35
  # describes) rather than folding `to:` into identity_attribute's
39
36
  # existing Reference-attribute scan, which every operation already
40
37
  # in the corpus (Banking, pizzas, lifeadelics' own vendored
41
38
  # PaymentGateway) still relies on unchanged.
39
+ #
40
+ # @param name [String] the operation's name
41
+ # @param to [Symbol, String, Module, nil] the aggregate the operation routes to; stored
42
+ # demodulised, and nil when the operation names none
43
+ # @param owner [String, nil] name of the aggregate the enclosing port is declared on
44
+ # @param direction [Symbol] `:inbound` for `operation`/`tells`, `:outbound` for `asks`
42
45
  def initialize(name, to: nil, owner: nil, direction: :inbound)
43
46
  @name = name
44
47
  @to = to && Naming.demodulise(to)
@@ -47,23 +50,33 @@ module Hecks
47
50
  @emits = []
48
51
  end
49
52
 
50
- # ALWAYS an attribute, never the self-reference `CommandBuilder`
53
+ # Refuses `reference_to` inside an operation body, except while shadow-parsing, where it
54
+ # adds the reference attribute frozen era text declared.
55
+ #
56
+ # Always an attribute, never the self-reference `CommandBuilder`
51
57
  # spells — a port operation has no `creates?`/`acts_on` distinction
52
58
  # to protect, so there is nothing for the self-reference branch to be
53
- # FOR here. `reference_to Payment, as: :payment_id` reads the same
59
+ # for here. `reference_to Payment, as: :payment_id` reads the same
54
60
  # even though the target happens to equal the owning aggregate.
55
- # RENAMED FROM `reference_to` item #13's full metaprogrammed
56
- # dispatch (slice 4b). Bootstrap-reachable, in
57
- # GenericDispatch::BOOTSTRAP_CALLS_FALLBACK.
61
+ # Answers the `reference_to` word through the table's `calls:`
62
+ # column — item #13's full metaprogrammed dispatch (slice 4b).
63
+ # Bootstrap-reachable, in `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`.
58
64
  #
59
- # DISABLED, #335 — kept only for MetaValidator's own shadow-parsing
65
+ # Disabled, #335 — kept only for MetaValidator's own shadow-parsing
60
66
  # pass (the self-hosted grammar's own KeywordSeed/ArgumentSeed rows
61
67
  # for "reference_to" in this context are themselves declared using
62
68
  # this construct, one level up — deleting the Ruby method would
63
69
  # break the language describing itself, not just old domain
64
- # authors). Every REAL domain author reaches `to:` instead, above —
70
+ # authors). Every real domain author reaches `to:` instead, above —
65
71
  # a genuinely different mechanism now, not a relocated spelling of
66
72
  # this one (see `to:`'s own comment).
73
+ #
74
+ # @param type [Symbol, String, Module] the referenced aggregate, written as a bare constant
75
+ # @param as [Symbol, nil] the attribute's name; nil derives it from the target, as in
76
+ # `:payment_id` under shadow-parsing
77
+ # @return [nil] nothing useful; the reference lands in `attributes`
78
+ # @raise [Bluebook::DSL::Malformed] always outside shadow-parsing, since routing belongs
79
+ # in `to:`; under shadow-parsing, if the attribute name is already declared
67
80
  def reference_to_impl(type, as: nil)
68
81
  unless MetaValidator.shadow_parsing?
69
82
  raise Malformed,
@@ -74,9 +87,13 @@ module Hecks
74
87
  add_reference!(type, as: as)
75
88
  end
76
89
 
90
+ # Names one event an inbound operation records once the external fact has arrived.
91
+ #
92
+ # @param event_name [String, Symbol] the event's name, such as `"PaymentSettled"`
93
+ # @return [Array<String>] every event named so far, this one last
77
94
  def emits(event_name) = @emits << event_name.to_s
78
95
 
79
- # THE TWO HALVES OF AN `asks`. An outbound call has exactly two
96
+ # The two halves of an `asks`. An outbound call has exactly two
80
97
  # endings and the chapter names both — `answers` for what came back,
81
98
  # `refuses` for what the outside said instead. They are separate words
82
99
  # rather than two `emits` because a reader has to be able to tell them
@@ -87,6 +104,12 @@ module Hecks
87
104
  # kind-driven coerce-and-assign with nothing else, now executed
88
105
  # by `GenericDispatch`.
89
106
 
107
+ # Assembles the operation, refusing words that belong to the other direction.
108
+ #
109
+ # @return [Bluebook::PortOperation] the operation with its attributes, events and routing
110
+ # @raise [Bluebook::DSL::Malformed] if an inbound operation declares no `emits` or
111
+ # declares `answers`/`refuses`, or an outbound one declares `emits` or lacks either
112
+ # `answers` or `refuses`
90
113
  def build
91
114
  outbound = @direction == :outbound
92
115
  refuse_wrong_words!(outbound)
@@ -96,7 +119,7 @@ module Hecks
96
119
  direction: @direction, answers: @answers, refuses: @refuses, to: @to
97
120
  )
98
121
 
99
- # AN INBOUND OPERATION STILL HAS TO SAY SOMETHING. Only inbound: an
122
+ # An inbound operation still has to say something. Only inbound: an
100
123
  # `asks` says it with `answers`/`refuses` instead, and
101
124
  # `refuse_wrong_words!` above has already insisted on both.
102
125
  if !outbound && @emits.empty?
@@ -110,7 +133,7 @@ module Hecks
110
133
 
111
134
  private
112
135
 
113
- # THE ONE PLACE a reference attribute actually gets added — both
136
+ # The one place a reference attribute actually gets added — both
114
137
  # `to:` (initialize, above) and reference_to_impl's own shadow-
115
138
  # parsing branch call this, so there is exactly one real
116
139
  # implementation of "carry a Reference-typed external fact,"
@@ -120,7 +143,7 @@ module Hecks
120
143
  attribute_impl(as || default_reference_name(target), Reference.new(target))
121
144
  end
122
145
 
123
- # EACH DIRECTION REFUSES THE OTHER'S WORDS. `emits` on an `asks` looks
146
+ # **Each direction refuses the other's words**. `emits` on an `asks` looks
124
147
  # right and is not: it would name one ending and leave the other
125
148
  # nowhere. `answers` on a `tells` is worse — there is no channel back
126
149
  # to an inbound caller at all, so it would read as a promise the
@@ -147,10 +170,21 @@ module Hecks
147
170
  end
148
171
  end
149
172
 
173
+ # Evaluates one operation block against a fresh builder and returns what it built.
174
+ #
150
175
  # `private` above (scoping the instance methods between it and here)
151
176
  # doesn't reach a singleton method — correctly so: `.build` is this
152
177
  # builder's real public entry point (DomainPortBuilder calls it),
153
178
  # never meant to be private.
179
+ #
180
+ # @param name [String] the operation's name
181
+ # @param to [Symbol, String, Module, nil] the aggregate the operation routes to, or nil
182
+ # @param owner [String, nil] name of the aggregate the enclosing port is declared on
183
+ # @param direction [Symbol] `:inbound` for `operation`/`tells`, `:outbound` for `asks`
184
+ # @yield the operation body, evaluated with the builder as `self`; may be omitted
185
+ # @return [Bluebook::PortOperation] the built operation
186
+ # @raise [Bluebook::DSL::Malformed] if the body uses the other direction's words, omits
187
+ # the ones its own direction requires, or uses a word the grammar does not admit
154
188
  # rubocop:disable-next Lint/IneffectiveAccessModifier
155
189
  def self.build(name, to: nil, owner: nil, direction: :inbound, &block)
156
190
  builder = new(name, to: to, owner: owner, direction: direction)
@@ -7,7 +7,7 @@ module Hecks
7
7
  # correlates its instances (`correlates_by`), and the `transition`-
8
8
  # declared state machine whose `dispatch`es (each optionally paired
9
9
  # with its own per-dispatch `compensates`) become its handlers. States
10
- # are DERIVED from the transitions rather than declared separately
10
+ # are derived from the transitions rather than declared separately
11
11
  # (S7, ADR 0025).
12
12
  class ProcessManagerBuilder
13
13
  GRAMMAR_CONTEXT = "ProcessManager".freeze
@@ -21,13 +21,13 @@ module Hecks
21
21
  @handlers = []
22
22
  end
23
23
 
24
- # `starts_on Transfer::TransferRequested` — BARE CONSTANT
25
- # ACCEPTED (ADR 0025, S6 — "events first-class"), resolved
24
+ # `starts_on Transfer::TransferRequested` — bare constant
25
+ # accepted (ADR 0025, S6 — "events first-class"), resolved
26
26
  # through `ConstShim` the same way `on_impl`/`transition_impl`
27
27
  # resolve an event reference — but through `Naming.
28
- # event_name_ref`, NOT `Naming.event_ref` (that method's own
28
+ # event_name_ref`, not `Naming.event_ref` (that method's own
29
29
  # header has the full account: `SagaInterpreter` matches
30
- # `pm.starts_on`/`pm.ends_on` against a BARE `event.name`, never
30
+ # `pm.starts_on`/`pm.ends_on` against a bare `event.name`, never
31
31
  # a "." qualified one). A plain String still passes through
32
32
  # unchanged, both for `shadow_parse` and for any corpus site a
33
33
  # future pass hasn't migrated yet.
@@ -45,7 +45,7 @@ module Hecks
45
45
  # kind-driven coerce-and-assign with nothing else, executed by
46
46
  # `GenericDispatch`.
47
47
  #
48
- # `starts_on`/`ends_on` USED TO be table-driven the same way,
48
+ # `starts_on`/`ends_on` used to be table-driven the same way,
49
49
  # until ADR 0025 S6 gave each a second, `kind: "constant"`
50
50
  # ArgumentSeed row (`starts_on Transfer::TransferRequested`) —
51
51
  # `GenericDispatch::COERCE_BY_KIND` only knows `"text"`/
@@ -54,25 +54,25 @@ module Hecks
54
54
  # (`arguments.size == 1` below), so both words are hand-written
55
55
  # again, `calls:`-routed like `transition` already is.
56
56
 
57
- # ONE STATE-MACHINE VOCABULARY (S7, ADR 0025 — "events and
58
- # reactions"): the SAME word `Lifecycle#transition` already
57
+ # One state-machine vocabulary (S7, ADR 0025 — "events and
58
+ # reactions"): the same word `Lifecycle#transition` already
59
59
  # carries, one level over — `transition "AccountDebited" =>
60
60
  # "awaiting_credit", from: "requested" do ... end` replaces `on
61
61
  # "AccountDebited", transition: { "requested" => "awaiting_
62
62
  # credit" } do ... end`. Same bare rocket-pair argument shape
63
- # (not a NAMED `transition:` kwarg wrapping a second Hash), same
63
+ # (not a named `transition:` kwarg wrapping a second Hash), same
64
64
  # `from:` — including the array form Lifecycle's own commands
65
65
  # could already take and a process manager's own events could
66
- # not — and the states a procedure runs on are DERIVED from the
66
+ # not — and the states a procedure runs on are derived from the
67
67
  # transitions that name them, the same way `Behaviour::Lifecycle
68
68
  # #states` already derives an aggregate's ; `state "x"` lines
69
69
  # duplicated exactly what the transition list already said,
70
70
  # and could drift from it (`validate!`'s own "undeclared state"
71
71
  # check existed only because they could).
72
72
  #
73
- # `starts_on`/`ends_on` are NOT unified into this — verified
73
+ # `starts_on`/`ends_on` are not unified into this — verified
74
74
  # against the real corpus rather than assumed: Settlement's own
75
- # `ends_on "TransferSettled"` names an event NONE of its own
75
+ # `ends_on "TransferSettled"` names an event none of its own
76
76
  # transitions ever handle (`Transfer.Settle`'s own emission, a
77
77
  # full step downstream of the transition that dispatches it),
78
78
  # so "the terminal state's own event" is not a fact the
@@ -81,15 +81,15 @@ module Hecks
81
81
  # cover the case, which is not less vocabulary than keeping the
82
82
  # one that already says it correctly.
83
83
  #
84
- # EXPANDS IMMEDIATELY, unlike `Lifecycle#transition` (which
84
+ # Expands immediately, unlike `Lifecycle#transition` (which
85
85
  # defers to `Behaviour::Lifecycle#expand`, called at emission
86
86
  # time) — `ProcessManager`'s own IR constructor takes `states:`/
87
87
  # `handlers:` exactly as it always has, so the runtime
88
88
  # (`Behaviour::ProcessManager`, `SagaInterpreter`, saga
89
89
  # persistence/rehydration) needs no change at all: what changed
90
- # is how the DECLARATION reaches that same shape, not the shape
90
+ # is how the declaration reaches that same shape, not the shape
91
91
  # a real run ever sees or persists.
92
- # RENAMED FROM `transition` — item #13's full metaprogrammed
92
+ # Renamed from `transition` — item #13's full metaprogrammed
93
93
  # dispatch (slice 4c). Not bootstrap-reachable (checked
94
94
  # directly — no core/attached chapter declares a ProcessManager
95
95
  # of its own).
@@ -97,9 +97,9 @@ module Hecks
97
97
  mapping = mapping.dup
98
98
  from = mapping.delete(:from)
99
99
 
100
- # ALWAYS REQUIRED, unlike `Lifecycle#transition`'s own `from:`
100
+ # Always required, unlike `Lifecycle#transition`'s own `from:`
101
101
  # — an aggregate's unconstrained transition is admitted from
102
- # ANY current state (`Behaviour::Lifecycle#applies_from?`
102
+ # any current state (`Behaviour::Lifecycle#applies_from?`
103
103
  # returns true for a nil `from`), a reading `SagaInterpreter#
104
104
  # advance_saga`'s own admission check does not share: it tests
105
105
  # `instance[:state] == handler.from_state` by plain equality,
@@ -120,14 +120,14 @@ module Hecks
120
120
  handler.instance_eval(&block) if block
121
121
 
122
122
  mapping.each do |event_type, target|
123
- # BARE CONSTANT ACCEPTED (ADR 0025, S6 — "events first-
123
+ # Bare constant accepted (ADR 0025, S6 — "events first-
124
124
  # class"), `transition Account::AccountDebited => "state"` —
125
- # `Naming.event_name_ref`, NOT the DOTTED `Naming.event_ref`
125
+ # `Naming.event_name_ref`, not the dotted `Naming.event_ref`
126
126
  # transform `PolicyBuilder#on_impl` uses (that method's own
127
127
  # header has the full account, found live wiring a real
128
128
  # migrated corpus site into `bin/model_check` for the first
129
129
  # time: `SagaInterpreter#advance_saga` matches `handler.
130
- # event_type` against a BARE `event.name`, never a "."
130
+ # event_type` against a bare `event.name`, never a "."
131
131
  # qualified one — a policy's own cross-aggregate match
132
132
  # works differently, splitting the qualifier apart from the
133
133
  # name rather than comparing the whole string). Writing the
@@ -163,12 +163,12 @@ module Hecks
163
163
 
164
164
  private
165
165
 
166
- # ONE DECLARED TRANSITION IS SEVERAL ROWS when `from` names more
166
+ # One declared transition is several rows when `from` names more
167
167
  # than one source state — `Behaviour::Lifecycle#expand`'s own
168
168
  # comment, the identical fan-out, one level over: a
169
169
  # `ProcessManagerHandler` only ever carries a single `from_state`,
170
170
  # so a `from: [...]` transition mints one row per source, each
171
- # carrying the SAME dispatches.
171
+ # carrying the same dispatches.
172
172
  def expand(event_type, transition, dispatches)
173
173
  sources = transition.from.nil? ? [nil] : Array(transition.from)
174
174
 
@@ -182,12 +182,12 @@ module Hecks
182
182
  end
183
183
  end
184
184
 
185
- # DERIVED, not declared (S7) — every state this procedure ever
185
+ # Derived, not declared (S7) — every state this procedure ever
186
186
  # runs on is already named by some transition's own `from_state`
187
187
  # or `to_state`; a state nothing transitions into or out of is
188
188
  # not a state this procedure has, the same reading
189
189
  # `Behaviour::Lifecycle#states` already gives an aggregate's own
190
- # field. FIRST-SEEN ORDER, walking declaration order — `begin_
190
+ # field. First-seen order, walking declaration order — `begin_
191
191
  # saga`'s own `pm.states.first` is what a fresh instance starts
192
192
  # in, so the order has to survive the derivation, not just the
193
193
  # membership.
@@ -201,15 +201,15 @@ module Hecks
201
201
  "nothing would tie its events to one instance"
202
202
  end
203
203
 
204
- # THE FIELD, NAMED — never the value object that carries it. A bare
204
+ # **The field, named** — never the value object that carries it. A bare
205
205
  # `correlates_by :end_to_end` reads whatever the payload holds under
206
- # that key AS the correlation key, and what a non-scalar key even
206
+ # that key as the correlation key, and what a non-scalar key even
207
207
  # is stays open (the object itself? its serialised text?).
208
208
  # Requiring the dotted spelling —
209
209
  # `:"end_to_end.value"` — makes every correlates_by name a scalar
210
210
  # by construction, the same discipline `identified_by` already
211
211
  # holds a head to. This is a syntactic check, not a type check: it
212
- # does not know or care whether the field IS a value object, only
212
+ # does not know or care whether the field is a value object, only
213
213
  # that the declaration cannot leave that question open.
214
214
  unless @correlates_by.to_s.include?(".")
215
215
  raise InvalidProcessManager, "#{@name} correlates_by #{@correlates_by.inspect}, which names a whole " \
@@ -231,7 +231,7 @@ module Hecks
231
231
  end
232
232
 
233
233
  # C10.3 — a leg is selected by (event, current state), so two
234
- # legs answering the SAME event from the SAME state would leave
234
+ # legs answering the same event from the same state would leave
235
235
  # the runtime to pick by declaration order, silently. Refused
236
236
  # here, where the declaration can still be read whole. (`from:
237
237
  # [...]` fan-out counts: `transition E => "a", from: ["x", "y"]`
@@ -253,7 +253,7 @@ module Hecks
253
253
  end
254
254
  end
255
255
 
256
- # THE BODY OF ONE `transition ... do ... end` block — collects the
256
+ # The body of one `transition ... do ... end` block — collects the
257
257
  # `dispatch` calls (each optionally opening its own `compensates`
258
258
  # via the nested `DispatchBuilder`) that fire when this transition
259
259
  # is taken.
@@ -266,17 +266,17 @@ module Hecks
266
266
 
267
267
  def initialize = @dispatches = []
268
268
 
269
- # THE COMMAND ITSELF (ADR 0025, "events and reactions" — command
269
+ # The command itself (ADR 0025, "events and reactions" — command
270
270
  # references become first-class), same shape and same reasons
271
271
  # as `PolicyBuilder#trigger`'s own header — bare constant live,
272
272
  # quoted text only under shadow-parsing (S0a's bridge; frozen
273
273
  # era text still writes `dispatch "Banking::Account.Debit"`).
274
274
  #
275
- # RENAMED FROM `dispatch` — item #13's full metaprogrammed
275
+ # Renamed from `dispatch` — item #13's full metaprogrammed
276
276
  # dispatch (slice 4), same reasoning as trigger_impl above.
277
277
  #
278
- # AN OPTIONAL BLOCK OPENS `compensates` ON THIS DISPATCH
279
- # SPECIFICALLY — per-dispatch saga compensation, replacing a
278
+ # An optional block opens `compensates` on this dispatch
279
+ # specifically — per-dispatch saga compensation, replacing a
280
280
  # hand-written list at the saga's own `on :refused` leg. Real,
281
281
  # live bug this closes: `examples/banking/bluebook/transfers_
282
282
  # and_payments.bluebook`'s own `Settlement` saga wrote a
@@ -315,9 +315,9 @@ module Hecks
315
315
  spec
316
316
  end
317
317
 
318
- # THE NESTED SCOPE `dispatch ... do ... end` OPENS — one word
318
+ # The nested scope `dispatch ... do ... end` opens — one word
319
319
  # only (`compensates`), the compensating half of the dispatch it
320
- # sits inside. Its own `compensates_impl` builds a SECOND
320
+ # sits inside. Its own `compensates_impl` builds a second
321
321
  # `DispatchSpec`, shape-identical to `HandlerBuilder#dispatch_
322
322
  # impl`'s own — a compensation takes the exact same two
323
323
  # arguments (a bare command constant, an optional `with:`)
@@ -29,7 +29,7 @@ module Hecks
29
29
  # exactly like any other. No "acts on itself" case to
30
30
  # distinguish here the way a command's own reference_to has —
31
31
  # a query has no root of its own to act on, only parameters.
32
- # RENAMED FROM `reference_to` — item #13's full metaprogrammed
32
+ # Renamed from `reference_to` — item #13's full metaprogrammed
33
33
  # dispatch (slice 4b). Bootstrap-reachable, in
34
34
  # GenericDispatch::BOOTSTRAP_CALLS_FALLBACK.
35
35
  def reference_to_impl(type, as: nil, optional: false)
@@ -69,14 +69,14 @@ module Hecks
69
69
 
70
70
  private
71
71
 
72
- # A block parameter names one of the OWNER's (the aggregate or entity
72
+ # A block parameter names one of the owner's (the aggregate or entity
73
73
  # this query is declared on) own already-declared attributes —
74
74
  # `query "ForDecision" do |decision| where decision: :decision end`
75
75
  # on Submission, whose own `attribute :decision, DecisionRef` already
76
76
  # says what `decision` is. Restating `attribute :decision, DecisionRef`
77
77
  # a second time inside the query was pure duplication; this derives
78
78
  # the same type from the owner instead. Only fills in a name the block
79
- # body did NOT already declare explicitly (checked AFTER instance_eval
79
+ # body did not already declare explicitly (checked after instance_eval
80
80
  # runs, so an existing bluebook still spelling it out both ways keeps
81
81
  # working unchanged — this only removes the need to, never refuses
82
82
  # the choice to). A block parameter matching nothing on the owner is
@@ -90,8 +90,8 @@ module Hecks
90
90
  owner_attr = owner_attributes.find { |a| a.name == param_name }
91
91
  next unless owner_attr
92
92
 
93
- # `Attribute.new` DIRECTLY, not the public `attribute(...)` DSL
94
- # entry — `owner_attr.type` is already SPELLED (a demodulised
93
+ # `Attribute.new` directly, not the public `attribute(...)` DSL
94
+ # entry — `owner_attr.type` is already spelled (a demodulised
95
95
  # String, `Attribute#spell`'s own doing), not a bareword the
96
96
  # bluebook author typed, so it must not run through
97
97
  # `AttributeCollector#attribute`'s quoted-type refusal (ADR
@@ -23,7 +23,7 @@ module Hecks
23
23
  @description = value
24
24
  end
25
25
 
26
- # RENAMED FROM `reference_to` — item #13's full metaprogrammed
26
+ # Renamed from `reference_to` — item #13's full metaprogrammed
27
27
  # dispatch (slice 4b). Bootstrap-reachable, in
28
28
  # GenericDispatch::BOOTSTRAP_CALLS_FALLBACK.
29
29
  def reference_to_impl(type, as: nil)
@@ -34,13 +34,13 @@ module Hecks
34
34
  end
35
35
 
36
36
  # Order-independent. `many:` is decided by comparing the included type
37
- # against the reference target, so this used to REFUSE an include
37
+ # against the reference target, so this used to refuse an include
38
38
  # declared before the reference — a rule guarding an implementation
39
39
  # limitation rather than a truth about read models. The includes are
40
40
  # collected raw and resolved at build, when the reference is known, so
41
41
  # there is no rule left to enforce.
42
- # RENAMED FROM `include`/`group_by` — item #13's full
43
- # metaprogrammed dispatch (slice 4c). `include` IS bootstrap-
42
+ # Renamed from `include`/`group_by` — item #13's full
43
+ # metaprogrammed dispatch (slice 4c). `include` is bootstrap-
44
44
  # reachable (every core chapter's own `read_model` names which
45
45
  # aggregates it includes with it — a first grep dismissed this
46
46
  # as `Module#include` noise and was wrong; the cold-boot test
@@ -48,13 +48,13 @@ module Hecks
48
48
  # BOOTSTRAP_CALLS_FALLBACK; `group_by` is not (no core read_model
49
49
  # groups). The class-level `include WordGate` this file's own
50
50
  # class body uses is `Module#include`, a different receiver,
51
- # unaffected by renaming this INSTANCE method either way.
51
+ # unaffected by renaming this instance method either way.
52
52
  def include_impl(type, as: nil)
53
53
  @includes ||= []
54
54
  @includes << [Naming.demodulise(type), as]
55
55
  end
56
56
 
57
- # `on:` (ADR 0055) — OVERRIDES of `QuerySpecification::Common::DSL`'s
57
+ # `on:` (ADR 0055) — overrides of `QuerySpecification::Common::DSL`'s
58
58
  # shared `where_impl`/`order_by_impl`/`limit_impl`/`offset_impl`,
59
59
  # scoped to `ReadModelBuilder` alone rather than added to the shared
60
60
  # module `Query` also mixes in: a plain `query` has no
@@ -63,10 +63,10 @@ module Hecks
63
63
  # means a `Query`'s own `where(..., on: X)` gets Ruby's own loud
64
64
  # `unknown keyword: :on` instead of quietly doing nothing.
65
65
  #
66
- # `on:` names the target by TYPE (`on: Character`), resolved the
66
+ # `on:` names the target by type (`on: Character`), resolved the
67
67
  # same way `reference_to`/`include` already resolve their own type
68
68
  # argument (`Naming.demodulise`) — not by the include's own `as:`
69
- # alias. A read model that `include`s the SAME type twice under two
69
+ # alias. A read model that `include`s the same type twice under two
70
70
  # different `as:` has no way to say which one `on:` means today; no
71
71
  # real corpus read model does this, so it's a real, deliberate scope
72
72
  # limit (see ADR 0055), not an oversight.
@@ -77,14 +77,14 @@ module Hecks
77
77
  # `status: "disputed"` captured as `**kwargs` (GenericDispatch's own
78
78
  # `builder.send(calls, *args, **kwargs, &block)`), and Ruby stops
79
79
  # auto-converting a bare `**hash` call into a plain positional Hash
80
- # THE MOMENT a method declares any real keyword parameter — so a
80
+ # the moment a method declares any real keyword parameter — so a
81
81
  # `(clauses, on: nil)` signature raised "wrong number of arguments
82
82
  # (given 0, expected 1)" on every ordinary `where(field: value)`
83
83
  # call, never reaching `on:` at all. `**rest` sidesteps this: Ruby
84
84
  # still auto-splits `on:` into the declared keyword and gathers
85
- # every OTHER key into `rest` regardless of how the caller wrote it.
85
+ # every other key into `rest` regardless of how the caller wrote it.
86
86
  #
87
- # `QuerySpecification::Common::WhereClause` etc — FULLY QUALIFIED,
87
+ # `QuerySpecification::Common::WhereClause` etc — fully qualified,
88
88
  # not the bare names `dsl.rb`'s own shared `where_impl` gets away
89
89
  # with. That file is lexically nested inside `Common` itself, so
90
90
  # `WhereClause` resolves directly; this class is nested inside
@@ -92,7 +92,7 @@ module Hecks
92
92
  # `QuerySpecification::Common` at all — a bare `WhereClause` here
93
93
  # falls through to `const_missing` and, mid-bluebook-load, that's
94
94
  # `ConstShim`, which resolves it against the self-hosted grammar
95
- # domain's OWN unrelated `WhereClause` construct instead (a `Module`,
95
+ # domain's own unrelated `WhereClause` construct instead (a `Module`,
96
96
  # not this `Struct`) — found directly by reproducing "undefined
97
97
  # method `new' for module WhereClause" against a real corpus load,
98
98
  # not guessed.
@@ -120,18 +120,18 @@ module Hecks
120
120
  @offset = QuerySpecification::Common::OffsetSpec.new(value: value, target: resolve_target(on))
121
121
  end
122
122
 
123
- # NAMES which of the eligible head's own fields to nest its rows
123
+ # Names which of the eligible head's own fields to nest its rows
124
124
  # under — one level per field, the leaf being that row with the
125
125
  # named fields removed (they're already spent, as the keys that
126
126
  # reached it). The same "exactly one many-side head" rule
127
127
  # `seal_query_options` already enforces for where/order_by/etc
128
128
  # applies here too (`seal_group_by`) — grouping is a question
129
- # about ONE collection's own rows, same as those are.
129
+ # about one collection's own rows, same as those are.
130
130
  def group_by_impl(*fields)
131
131
  # Hash rows, `{field:}`, not bare symbols — same shape
132
132
  # `aggregate_heads` already uses for exactly the reason it
133
133
  # does: the language's own self-hosted grammar (`projection
134
- # .bluebook`'s `GroupByField`) has to have SOMETHING to read a
134
+ # .bluebook`'s `GroupByField`) has to have something to read a
135
135
  # `field:` off of when `Judge` walks this list generically: a
136
136
  # bare `Symbol` has no attribute of its own to read.
137
137
  @group_by = fields.map { |field| { field: field.to_sym } }
@@ -139,23 +139,23 @@ module Hecks
139
139
 
140
140
  # `count` -- a bare row count over the eligible many-side head's
141
141
  # own rows (after `where`/`order_by`/`limit`/`offset` apply, the
142
- # same rows `group_by` itself would nest) -- ANSWERS "how many
143
- # match", not "which ones". A sibling REDUCTION to `group_by`,
142
+ # same rows `group_by` itself would nest) -- answers "how many
143
+ # match", not "which ones". A sibling reduction to `group_by`,
144
144
  # not a filter: `seal_aggregation` refuses combining it with
145
145
  # `group_by` or with `median`, the same "exactly one many-side
146
146
  # head" rule `seal_group_by` already enforces for the same
147
147
  # reason -- a bare marker, so `@count` is left unset (nil, not
148
- # false) rather than defaulted, matching the "ABSENT is not
149
- # EMPTY" reading `Lifecycle`'s own optional fields already rely
148
+ # false) rather than defaulted, matching the "absent is not
149
+ # empty" reading `Lifecycle`'s own optional fields already rely
150
150
  # on for the Judge's setter dispatch (Behaviour::ReadModel#
151
151
  # count?, ReadModelInterpreter#aggregation_target).
152
152
  # `count` — item #13's full metaprogrammed dispatch, slice 1
153
- # (whole-project table-unification survey): the ONLY Keyword row
153
+ # (whole-project table-unification survey): the only Keyword row
154
154
  # filling `count` — a bare marker, now stored as literal `true`
155
155
  # by `GenericDispatch` off that same table fact.
156
156
 
157
- # `median(field)` -- the median VALUE of one numeric field
158
- # across the eligible many-side head's own rows. EVEN COUNT: the
157
+ # `median(field)` -- the median value of one numeric field
158
+ # across the eligible many-side head's own rows. Even count: the
159
159
  # average of its two middle values (the standard definition,
160
160
  # not "the lower of the two") -- see
161
161
  # Runtime::ReadModelInterpreter#median for where that lands and
@@ -168,16 +168,16 @@ module Hecks
168
168
  # same shape as `count`, above (a bare, kind-driven coerce-and-
169
169
  # assign).
170
170
 
171
- # `reference_to` is now OPTIONAL — a read model with no root is a
172
- # BULK one: every `include`d head reads its own aggregate whole
171
+ # `reference_to` is now optional — a read model with no root is a
172
+ # bulk one: every `include`d head reads its own aggregate whole
173
173
  # (no FK match against a root that doesn't exist), and dispatch
174
- # takes no id argument at all. This used to be REQUIRED, on the
174
+ # takes no id argument at all. This used to be required, on the
175
175
  # assumption a read model was always "one root record's own
176
176
  # cross-aggregate view" — true of every real corpus report so
177
177
  # far, but not a truth about read models themselves: `group_by`'s
178
- # own real use (nesting an aggregate's OWN whole table by its own
178
+ # own real use (nesting an aggregate's own whole table by its own
179
179
  # field values) has no root to speak of. Still needs to describe
180
- # SOMETHING — zero includes AND no reference is refused.
180
+ # something — zero includes and no reference is refused.
181
181
  def build
182
182
  if !@reference_target && Array(@includes).empty?
183
183
  raise Malformed,
@@ -213,18 +213,18 @@ module Hecks
213
213
  # (the "one" side, the reference target itself, is a single row;
214
214
  # ordering, paging, or tenant-scoping one row means nothing). ADR
215
215
  # 0055 gave `where`/`order_by`/`limit`/`offset` an `on:` to name
216
- # WHICH many-side collection they mean, so this asks two questions
216
+ # which many-side collection they mean, so this asks two questions
217
217
  # now instead of one:
218
218
  #
219
219
  # 1. Does every declared `on:` actually name a many-side included
220
220
  # aggregate? Checked regardless of how many many-side heads
221
221
  # exist — a typo refuses immediately, not only once ambiguity
222
222
  # would otherwise bite.
223
- # 2. Is there still an UNTARGETED option declared (including
223
+ # 2. Is there still an untargeted option declared (including
224
224
  # `authorize`'s own `tenant:`, which has no `on:` of its own —
225
225
  # a real, deliberate scope limit, see ADR 0055)? An untargeted
226
226
  # option still needs exactly one many-side head to mean
227
- # anything unambiguous — the ORIGINAL rule, unchanged, and
227
+ # anything unambiguous — the original rule, unchanged, and
228
228
  # still worded the same way (`spec/runtime/
229
229
  # read_model_interpreter_spec.rb`'s existing refusal regex
230
230
  # still matches).
@@ -246,7 +246,7 @@ module Hecks
246
246
  end
247
247
 
248
248
  # Question 1 of `seal_query_options`'s own two, split out to keep
249
- # both under the same "one job per method" shape every OTHER seal in
249
+ # both under the same "one job per method" shape every other seal in
250
250
  # this file already holds to (each raises its own one Malformed, for
251
251
  # its own one reason).
252
252
  def validate_declared_targets!(many)
@@ -275,7 +275,7 @@ module Hecks
275
275
  end
276
276
 
277
277
  # Same shape as `seal_query_options`, same reason — `group_by`
278
- # answers a question about ONE collection's own rows, so zero or
278
+ # answers a question about one collection's own rows, so zero or
279
279
  # several many-side heads leaves it with no unambiguous target.
280
280
  def seal_group_by
281
281
  return unless @group_by&.any?
@@ -289,10 +289,10 @@ module Hecks
289
289
  "own rows; name which one by including only it"
290
290
  end
291
291
 
292
- # `count`/`median` are the OTHER two reductions a read model may
292
+ # `count`/`median` are the other two reductions a read model may
293
293
  # declare over its one eligible collection — same "exactly one
294
294
  # many-side head" rule as `seal_group_by`, plus a rule
295
- # `seal_group_by` doesn't need: a read model reports ONE shape,
295
+ # `seal_group_by` doesn't need: a read model reports one shape,
296
296
  # so `count` and `median` cannot both be declared, and neither
297
297
  # may combine with `group_by` (nesting rows and reducing them to
298
298
  # a scalar are answers to different questions ; a caller asking