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
@@ -0,0 +1,248 @@
1
+ require_relative "../errors"
2
+ require_relative "../refusal_wording"
3
+ require_relative "../../rendering"
4
+
5
+ module Hecks
6
+ module Runtime
7
+ class Value
8
+ # How a `list_of` attribute's own elements GET hydrated — entity-typed
9
+ # and value-object-typed alike — split out of `Coercion` (this file's
10
+ # sibling, extended into `Value` alongside it exactly the way
11
+ # `Admission` already is) once `Coercion` itself grew past
12
+ # `Metrics/ModuleLength`'s budget from two, unrelated fixes landing in
13
+ # the same method the same week (BUG#32's own `hydrate_entity_identity`
14
+ # and BUG#33's own `check_entity_list_identities` — see each method's
15
+ # own header, and this repo's `.rubocop_todo.yml` for the merge that
16
+ # made the split necessary). Cross-calls into `Coercion`'s own
17
+ # `for_attribute`/`build`/`fields_for`/`value_object_for`/
18
+ # `trusting_stored_state?` work unqualified here exactly as they did
19
+ # before the split, because both modules land on the same `Value`
20
+ # singleton class once extended — `self` never has to know which file
21
+ # a sibling method actually lives in.
22
+ module EntityListCoercion
23
+ # S17, ADR 0026 — searches the whole entity tree, not only the
24
+ # root's own direct children. `aggregate` here is always the
25
+ # root aggregate — `for_attribute`'s own `aggregate` argument is
26
+ # never reassigned as hydration recurses into a nested element,
27
+ # because coercion has to resolve value objects, and only the
28
+ # root answers `.value_object` at all (Entity's own header
29
+ # comment: an entity must not answer to it, or `Value.
30
+ # for_attribute` could no longer tell a piece from a head). So
31
+ # a nested entity — Dispatch, inside Handler — is not a direct
32
+ # child of the root the way Handler itself is, and a plain
33
+ # `aggregate.entities.find` stops one level short of it.
34
+ def find_entity(construct, name)
35
+ construct.entities.each do |candidate|
36
+ return candidate if candidate.hecks_name == name
37
+
38
+ found = find_entity(candidate, name)
39
+ return found if found
40
+ end
41
+ nil
42
+ end
43
+
44
+ # Frozen through: a list read back out of the store is an answer,
45
+ # not a handle on what is stored.
46
+ #
47
+ # ADR 0047 — this used to bail (`return value unless entity`) the
48
+ # moment `attribute.type` named a value object rather than an
49
+ # entity, handing back the raw, un-hydrated argument untouched.
50
+ # A `sets :field` mutation sourced from a whole-array argument (as
51
+ # opposed to element-by-element `append:`) went straight through
52
+ # `for_attribute`'s `:list` branch, so `Banking::CardPayment.
53
+ # Authorize`'s own `sets :tags` (`list_of(Tag)`) stored plain
54
+ # Ruby Hashes as its `tags` elements forever — never a real
55
+ # `Value`, never through `Tag`'s own `pattern:`/`invariant`
56
+ # checks. `remove:`'s `==` comparison (a real `Value` against a
57
+ # raw `Hash`) then always failed, since `Hash#==` refuses anything
58
+ # that isn't itself a compatible Hash — the bug ADR 0047 traces in
59
+ # full. Delegating to `hydrate_value_object_list` below closes
60
+ # that gap the same way the entity branch already worked: build a
61
+ # real, validated `Value` per element, reusing `for_attribute`'s
62
+ # own composite-construction path rather than inventing a second
63
+ # one.
64
+ # BUG#32 (QualityControl ledger) — `remove:`'s own single-target
65
+ # value used to fall straight into `Array(value).map { ... }`
66
+ # below unguarded, unlike this method's own delegated
67
+ # `hydrate_value_object_list` sibling, whose `value.is_a?(Array)`
68
+ # check exists for exactly this shape (see that method's own
69
+ # comment). For an entity-typed list, `remove:`'s scalar target
70
+ # (`Ledger.Void`'s `sequence: EntrySequence`) is never a Hash, so
71
+ # `Array(2)` merely wrapped it as `[2]` rather than shredding it —
72
+ # but the wrapping itself was still wrong: `MutationApplier#
73
+ # removed`'s `element == value` then compared a stored `Entry`
74
+ # Hash against a one-element Array, which can never be `==` a
75
+ # Hash, so nothing was ever removed and nothing ever refused
76
+ # either. Routed to `hydrate_entity_identity` instead, below.
77
+ def hydrate_entity_list(aggregate, attribute, value)
78
+ entity = find_entity(aggregate, attribute.type.to_s)
79
+ return hydrate_value_object_list(aggregate, attribute, value) unless entity
80
+
81
+ return hydrate_entity_identity(aggregate, entity, value) unless value.is_a?(Array)
82
+
83
+ hydrated = value.map do |element|
84
+ next element unless element.is_a?(Hash)
85
+
86
+ element.each_with_object({}) do |(name, field_value), acc|
87
+ key = name.to_sym
88
+ field = entity.attribute(key)
89
+ acc[key] = field ? for_attribute(aggregate, field, field_value) : field_value
90
+ end
91
+ end
92
+ # BUG#33 — only a genuine whole-list offering (`value.is_a?(Array)`)
93
+ # is a caller naming every element's own identity at once; the
94
+ # single-target shape this same method also hydrates (a `remove:`
95
+ # target, wrapped one level up by `Array()`) never reaches this
96
+ # check, and never should — see `check_entity_list_identities`'s
97
+ # own comment.
98
+ check_entity_list_identities(aggregate, entity, hydrated) if value.is_a?(Array)
99
+ Freezer.deep(hydrated)
100
+ end
101
+
102
+ # `MutationApplier#check_entity_collision`'s own guard
103
+ # (mutation_applier.rb), extended from a single caller-supplied
104
+ # append (BUG#13) to a whole-list replace — `sets :entries` bare
105
+ # (`Ledger.ReplaceEntries`, the corpus's first `list_of(ENTITY)`
106
+ # command argument/mutation, qa/stress_domains/corrections). The
107
+ # array branch above rebuilds each offered element's own declared
108
+ # fields but never checked the offered list itself for either way
109
+ # it can misname its own entities:
110
+ #
111
+ # - two elements sharing one identity — the same silent-duplicate
112
+ # hazard BUG#13's own comment describes: `EntityInterpreter#
113
+ # element_of`'s own `find_index` always matches the first
114
+ # match, so the second becomes permanently unaddressable by any
115
+ # later command.
116
+ # - an element missing its identity altogether — there is no
117
+ # auto-mint for a whole-list replace the way `MutationApplier#
118
+ # entity_element` mints one for a single append (which element
119
+ # would it mint for? every element here is the caller's own
120
+ # whole state, offered at once), so an absent identity is
121
+ # refused rather than guessed.
122
+ #
123
+ # Non-composite identities only, exactly BUG#13's own scope
124
+ # (`entity.identified_by` answers a single Symbol only when
125
+ # `identity_heads.size == 1`, `Behaviour::Traits#derive_identity`) —
126
+ # a composite identity's own duplicate/missing question is the
127
+ # same pre-existing, narrower gap BUG#13 documented and left open,
128
+ # not widened here.
129
+ #
130
+ # Skipped under `trusting_stored_state?`, the same guard `validate!`
131
+ # already gives every value object (C6.3): a record already
132
+ # written is trusted as it was, so tightening this check can never
133
+ # make an old, already-persisted record unreadable.
134
+ def check_entity_list_identities(aggregate, entity, elements)
135
+ identity = entity.identified_by
136
+ return unless identity
137
+ return if trusting_stored_state?
138
+
139
+ field = entity.attribute(identity)
140
+ seen = []
141
+ elements.each do |fields|
142
+ next unless fields.is_a?(Hash)
143
+
144
+ offered = fields[identity]
145
+ if offered.nil?
146
+ raise TypeMismatch,
147
+ RefusalWording.render_site("TypeMismatch", "numeric_field",
148
+ type: entity.hecks_name, field: identity,
149
+ expected: field&.type, offered: "nil")
150
+ end
151
+
152
+ if seen.include?(offered)
153
+ raise AlreadyExists,
154
+ RefusalWording.render_site("AlreadyExists", "entity_duplicate",
155
+ entity: entity.hecks_name, aggregate: aggregate.hecks_name,
156
+ identity: entity.identity_paths.join(", "),
157
+ offered: [Rendering.describe(offered)])
158
+ end
159
+ seen << offered
160
+ end
161
+ end
162
+
163
+ # BUG#32 — `remove:`'s own single-target value against an
164
+ # entity-typed list. An entity is never offered to `remove:`
165
+ # whole the way a value object is (`hydrate_value_object_list`'s
166
+ # own `element == value` full-value-equality shape) — an entity
167
+ # must never answer `.value_object` at all (`Entity`'s own header
168
+ # comment), so there is no whole-value shape here to rebuild in
169
+ # the first place, only the one field a caller would otherwise
170
+ # have to name to address that element any other way
171
+ # (`EntityElement#element_of`'s own `wants`). So this coerces the
172
+ # offered scalar against the entity's own identity field's
173
+ # declared type — not the entity's full shape — and hands back a
174
+ # real, correctly-typed `Value` ready to compare against each
175
+ # stored element's own identity field
176
+ # (`EntityElement.list_element_match?`, the shared rule both
177
+ # `MutationApplier#removed` and `EntityElement#
178
+ # removed_from_element` match against).
179
+ #
180
+ # A composite identity (more than one head) has no single field a
181
+ # bare `remove:` target could mean, so this passes the value
182
+ # through uncoerced rather than guessing which head — the same
183
+ # "nothing in this corpus needs it yet" boundary
184
+ # `MutationApplier#check_entity_collision`'s own header already
185
+ # draws for entity identity elsewhere in this runtime.
186
+ # `list_element_match?` treats an uncoerced value the same way
187
+ # it always treated the pre-fix wrapped Array: never a match, a
188
+ # documented no-op rather than a crash.
189
+ def hydrate_entity_identity(aggregate, entity, value)
190
+ return value if value.is_a?(self)
191
+
192
+ heads = entity.identity_heads
193
+ return value unless heads.one?
194
+
195
+ field = entity.attribute(heads.first)
196
+ return value unless field
197
+
198
+ for_attribute(aggregate, field, value)
199
+ end
200
+
201
+ # The value-object sibling of the entity branch above: an element
202
+ # already shaped like the target `Value` (or a `Hash`/scalar that
203
+ # `fields_for` can still open) is rebuilt through the same `build`
204
+ # a scalar composite attribute already uses (`for_attribute`'s own
205
+ # `coerced = ... build(value_object, fields_for(...), aggregate)`
206
+ # line) — same defaults, same `pattern:`/`admits:`/invariant
207
+ # checks, same `trusting_stored_state?` bypass on a trusted load.
208
+ # `attribute.type` naming neither an entity nor a value object
209
+ # (a `list_of(String)`, say) has no shape to rebuild into, so the
210
+ # element passes through unchanged, exactly as the entity branch's
211
+ # own non-Hash elements do.
212
+ #
213
+ # Not `Array(value).map` (unlike the entity branch above) — `value`
214
+ # here is not always genuinely list-shaped. `MutationApplier#
215
+ # removed`'s own `Value.for_attribute(aggregate, attribute, value)`
216
+ # call (`attribute` = the list attribute, `mutation.target`; `value`
217
+ # = the single remove-target argument, already a real `Value` by
218
+ # the time it gets here) reuses this exact branch — for `remove:`,
219
+ # not for a whole-list `sets`. `Array(a_real_Value)` alone would be
220
+ # harmless (`Value` defines neither `to_a` nor `to_ary`, so Kernel
221
+ # wraps it `[value]`), but `Array(a_Hash)` is not harmless: Ruby's
222
+ # `Array()` opens a bare Hash into its own `[[k, v], ...]` pairs,
223
+ # not `[hash]` — silently shredding a single-element Hash-shaped
224
+ # target into garbage instead of hydrating it. Branching on
225
+ # `value.is_a?(Array)` up front (true only for a genuine whole-list
226
+ # `sets`/hydrate load) keeps the single-target shape a single
227
+ # target, hydrated the same way, never listified.
228
+ def hydrate_value_object_list(aggregate, attribute, value)
229
+ return value unless aggregate.respond_to?(:value_object)
230
+
231
+ value_object = value_object_for(aggregate, attribute.type)
232
+ return value unless value_object
233
+
234
+ return hydrate_value_object_element(aggregate, attribute, value_object, value) unless value.is_a?(Array)
235
+
236
+ hydrated = value.map { |element| hydrate_value_object_element(aggregate, attribute, value_object, element) }
237
+ Freezer.deep(hydrated)
238
+ end
239
+
240
+ def hydrate_value_object_element(aggregate, attribute, value_object, element)
241
+ return element if element.is_a?(self) && element.type_name == value_object.hecks_name
242
+
243
+ build(value_object, fields_for(value_object, attribute.name, element), aggregate)
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end
@@ -2,25 +2,30 @@ require "json"
2
2
  require_relative "../rendering"
3
3
  require_relative "value/invariant_violation"
4
4
  require_relative "value/coercion"
5
+ require_relative "value/entity_list_coercion"
5
6
  require_relative "value/admission"
6
7
 
7
8
  module Hecks
8
9
  module Runtime
9
10
  # A typed value object in hand: frozen fields, read by name. How one is
10
- # MADE — coerced from a raw argument, checked against its declared
11
+ # made — coerced from a raw argument, checked against its declared
11
12
  # numeric types, patterns and closed sets — is the class-side engine in
12
- # value/coercion.rb and value/admission.rb, extended here so the door
13
- # stays where it always was: `Value.for`, `Value.build`.
13
+ # value/coercion.rb, value/entity_list_coercion.rb (a `list_of`
14
+ # attribute's own elements split out once growing Coercion tripped
15
+ # Metrics/ModuleLength, see that file's own header), and
16
+ # value/admission.rb, extended here so the door stays where it always
17
+ # was: `Value.for`, `Value.build`.
14
18
  class Value
15
19
  extend Coercion
20
+ extend EntityListCoercion
16
21
  extend Admission
17
22
 
18
23
  attr_reader :value_object
19
24
 
20
- # FROZEN THROUGH, not just on top.
25
+ # Frozen through, not just on top.
21
26
  #
22
27
  # `@fields.freeze` alone stops a key being added or removed and
23
- # nothing else: the String, Array or Hash a field HOLDS stays
28
+ # nothing else: the String, Array or Hash a field holds stays
24
29
  # mutable, so `vo[:value] << "!"` edits a value object in place —
25
30
  # demonstrated on a real dispatch before this was written, not
26
31
  # supposed. Same shape as the three freezing bugs already fixed
@@ -28,7 +33,7 @@ module Hecks
28
33
  # was frozen and the contents were not.
29
34
  #
30
35
  # A value object is the one thing in the domain that has no
31
- # identity to change over — `with` already answers a NEW one rather
36
+ # identity to change over — `with` already answers a new one rather
32
37
  # than mutating — so freezing it through is what it always claimed
33
38
  # to be.
34
39
  def initialize(value_object, fields)
@@ -63,13 +68,13 @@ module Hecks
63
68
  # `materialize`, but a single-attribute value object (`sole_attribute`
64
69
  # — [[feedback_name_the_scalar_field]]) recurses into its own bare
65
70
  # field instead of building `{field: ...}` — a Board's own `label`
66
- # unwraps to `"Kanban"`, not `{value: "Kanban"}`. NOT a replacement
71
+ # unwraps to `"Kanban"`, not `{value: "Kanban"}`. Not a replacement
67
72
  # for `materialize` itself: every existing report/query/command
68
73
  # caller keeps the wrapped shape it already depends on (Banking's
69
74
  # own `CustomerPortfolio` reads `payment[:amount][:cents]`, and
70
75
  # changing that out from under it would be a real breaking change,
71
76
  # not a bug fix). This is read_model_interpreter.rb's own opt-in,
72
- # used ONLY for a `group_by`-declared head's own rows — grouping
77
+ # used only for a `group_by`-declared head's own rows — grouping
73
78
  # needs a real scalar to key by regardless, so a report already
74
79
  # asking for that gets the unwrap for free.
75
80
  def self.materialize_unwrapped(value)
@@ -78,12 +83,12 @@ module Hecks
78
83
  sole = value.value_object.sole_attribute
79
84
  return materialize_unwrapped(value[sole.name]) if sole
80
85
 
81
- # NOT `value.to_h` — `Value#to_h` materializes each field through
86
+ # Not `value.to_h` — `Value#to_h` materializes each field through
82
87
  # plain `materialize`, so a VO nested inside a multi-attribute VO
83
88
  # would already be a plain Hash by the time this method ever saw
84
89
  # it, and never reach the `when self` branch above. Read each
85
90
  # field straight off `value` instead, so recursion actually
86
- # happens through THIS method the whole way down.
91
+ # happens through this method the whole way down.
87
92
  value.value_object.attributes.to_h { |attr| [attr.name, materialize_unwrapped(value[attr.name])] }
88
93
  when Array then value.map { |item| materialize_unwrapped(item) }
89
94
  when Hash then value.transform_values { |item| materialize_unwrapped(item) }
@@ -91,17 +96,17 @@ module Hecks
91
96
  end
92
97
  end
93
98
 
94
- # REDUCES AN APPEND-ONLY SUB-LOG TO ITS CURRENT STATE — the same
99
+ # Reduces an append-only sub-log to its current state — the same
95
100
  # "a later fact supersedes an earlier one" reduction this runtime
96
- # already performs replaying an AGGREGATE's own command history
101
+ # already performs replaying an aggregate's own command history
97
102
  # into its current attributes, applied here to a single `list_of`
98
- # FIELD acting as its own miniature append-only log (a placement
103
+ # field acting as its own miniature append-only log (a placement
99
104
  # history, a tombstone-style soft-delete list, a versioned
100
105
  # setting). `rows` is what a `list_of` attribute hands back — an
101
106
  # Array of `Value`, in append order — and `key` names the field
102
107
  # that identifies "the same logical thing" across entries.
103
108
  #
104
- # GROUPING ONLY, NEVER INTERPRETATION. What counts as "removed,"
109
+ # **Grouping only, never interpretation**. What counts as "removed,"
105
110
  # how to order what survives — that meaning belongs to whichever
106
111
  # domain declared the field, never here: a generic reduction that
107
112
  # started guessing domain semantics would need to keep guessing
@@ -114,17 +119,17 @@ module Hecks
114
119
  def method_missing(name, *args)
115
120
  return @fields[name] if @fields.key?(name)
116
121
 
117
- # THE LANGUAGE RULE, not a convenience: ANY value object with
122
+ # The language rule, not a convenience: any value object with
118
123
  # exactly one declared attribute answers `.value`, whatever that
119
124
  # attribute is actually named — a single-attribute value object
120
- # is a NAME for a scalar, not a genuine group
125
+ # is a name for a scalar, not a genuine group
121
126
  # ([[feedback_name_the_scalar_field]], `Behaviour::ValueObject#
122
127
  # sole_attribute`), so `money.value` reads `Money`'s own `amount`
123
128
  # exactly as `label.value` reads a shorthand-declared `value`.
124
- # AFTER the real-field lookup above, on purpose: a field
125
- # literally named `value` is already answered there (and IS the
129
+ # After the real-field lookup above, on purpose: a field
130
+ # literally named `value` is already answered there (and is the
126
131
  # sole attribute whenever the count is one), so this branch only
127
- # ever aliases, never shadows. A MULTI-attribute value object
132
+ # ever aliases, never shadows. A multi-attribute value object
128
133
  # keeps its NoMethodError — `sole_attribute` answers nil for it,
129
134
  # and falling through to `super` is exactly the refusal it
130
135
  # always gave: with two or more fields there is no single value
@@ -146,15 +151,15 @@ module Hecks
146
151
 
147
152
  private
148
153
 
149
- # THE `.value` ALIAS FOR INDEXED ACCESS — the same language rule
154
+ # **The `.value` alias for indexed access** — the same language rule
150
155
  # `method_missing` above enforces for method reads, applied to
151
156
  # `[]`/`key?`/`with`: `:value` names a single-attribute value
152
157
  # object's sole field whatever that field is actually called. A
153
- # REAL key always wins first (a field literally named `value` is
158
+ # real key always wins first (a field literally named `value` is
154
159
  # its own answer, and is the sole attribute anyway whenever the
155
160
  # count is one), so this only ever resolves a `:value` that would
156
- # otherwise MISS — it can never redirect a genuine field read.
157
- # `with(:value, x)` in particular NEEDS this: merging a literal
161
+ # otherwise miss — it can never redirect a genuine field read.
162
+ # `with(:value, x)` in particular needs this: merging a literal
158
163
  # `:value` key beside a sole field named `amount` would build a
159
164
  # two-key hash for a one-field shape and be refused (or worse,
160
165
  # stored) downstream — aliasing at the merge is what keeps the
data/lib/hecks/runtime.rb CHANGED
@@ -6,24 +6,24 @@
6
6
  # declaration is being collected into — sat on the top-level Hecks module
7
7
  # beside the DSL words.
8
8
  #
9
- # What lives here is what RUNS a domain :
9
+ # What lives here is what runs a domain :
10
10
  #
11
11
  # Runtime.boot(path) load a directory and return its Dispatcher
12
12
  # Runtime.with_registry(r) bind the ambient registry for the duration of a load
13
13
  # Runtime.current_registry the registry a declaration is landing in, or nil
14
14
  #
15
- # What does NOT live here is what DECLARES one — `Hecks.bluebook`,
15
+ # What does not live here is what declares one — `Hecks.bluebook`,
16
16
  # `.hecksagon`, `.port`, `.adapter`, `.world` are loading words and stay on the
17
17
  # top-level module, which now reads as a facade over this.
18
18
  #
19
19
  # runtime = Hecks::Runtime.boot("examples/pizzas/bluebook")
20
20
  # runtime.dispatch("Pizzas::Pizza.CreatePizza", name: "Margherita")
21
21
  #
22
- # NOTE, and it is the reason this file exists : `current_registry` is still
22
+ # Note, and it is the reason this file exists : `current_registry` is still
23
23
  # process-global. Each boot builds a fresh Registry, and `Loader.bind_runtime`
24
- # installs a fresh facade door whose modules close over THAT boot's dispatcher
24
+ # installs a fresh facade door whose modules close over that boot's dispatcher
25
25
  # — no class-level runtime binding remains, so two boots in one process no
26
- # longer share dispatch state, only the top-level NAME (the last-bound door
26
+ # longer share dispatch state, only the top-level name (the last-bound door
27
27
  # wins the constant, which is what per-boot install means). Owning the state
28
28
  # here was the first move ; the dispatcher reachable per-runtime rather than
29
29
  # through a constant was the second, and it is done — the door is the only
@@ -91,7 +91,7 @@ module Hecks
91
91
  # the block — who a command's declared `role`, if any, is checked
92
92
  # against.
93
93
  #
94
- # `as_of:` is OPTIONAL, same opt-in shape as `actor_id:` — a caller
94
+ # `as_of:` is optional, same opt-in shape as `actor_id:` — a caller
95
95
  # that wants a Governance `RoleAssignment`'s `starts_at` enforced
96
96
  # passes `as_of: Ports::Clock.now(registry)` here, at the door,
97
97
  # exactly where `cli_runner.rb` already merges `Clock.now` into a
@@ -99,7 +99,7 @@ module Hecks
99
99
  # itself — see `Ports::Clock`'s own header for why — so an unbound
100
100
  # `as_of` leaves `starts_at` unchecked, exactly as before.
101
101
  #
102
- # `scope:` is OPTIONAL too — a caller that states which scope it is
102
+ # `scope:` is optional too — a caller that states which scope it is
103
103
  # acting in gets that scope checked against the matching
104
104
  # `RoleAssignment`'s own `scope`, not just its `role_name`. See
105
105
  # `Runtime::Caller::Current`'s own header for why this lives here