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
@@ -11,6 +11,14 @@ module Hecks
11
11
  class << self
12
12
  attr_accessor :resolver
13
13
 
14
+ # Installs a resolver for the duration of a block, restoring the earlier one afterwards
15
+ # even if the block raises.
16
+ #
17
+ # @param resolver [#call] called with the missing constant's name as a Symbol; whatever
18
+ # it returns is what the bare constant evaluates to
19
+ # @yield the DSL code whose undeclared constants the resolver should answer
20
+ # @yieldreturn [Object] any value; it becomes this method's result
21
+ # @return [Object] whatever the block returns
14
22
  def with(resolver)
15
23
  previous = @resolver
16
24
  @resolver = resolver
@@ -19,45 +27,63 @@ module Hecks
19
27
  @resolver = previous
20
28
  end
21
29
 
30
+ # Reports whether a resolver is installed, meaning code is running inside a DSL block.
31
+ #
32
+ # @return [Boolean] true inside a `with` block, or after `resolver=` set one directly
22
33
  def active? = !@resolver.nil?
23
34
  end
24
35
 
25
- # THE SCOPED-CONSTANT BRIDGE (ADR 0025, docs/dsl-work-slices.md's
36
+ # The scoped-constant bridge (ADR 0025, docs/dsl-work-slices.md's
26
37
  # S0b) — a Symbol cannot answer `::`, so `Account::Debit` and
27
38
  # `admits: Account::LedgerDirection` could never resolve past
28
- # their FIRST segment while `Account` returned a bare Symbol
39
+ # their first segment while `Account` returned a bare Symbol
29
40
  # (`resolver = ->(const) { const }`, bluebook_builder.rb's own
30
- # comment on why that was right for a BARE name): Ruby's `::`
41
+ # comment on why that was right for a bare name): Ruby's `::`
31
42
  # operator raises `TypeError` on the returned value before any
32
43
  # DSL code runs at all, unless that value is itself a Module.
33
44
  #
34
- # A REAL `Module` subclass, not a decorated Symbol, for exactly
35
- # that reason — nothing else answers `::`. Every existing
36
- # consumer keeps working duck-typed, not because nothing
37
- # changed: `Attribute#spell`'s `type.is_a?(Module)` branch now
38
- # fires where it used to fall to `type.to_s`, and
39
- # `Naming.demodulise` (`path.split("::").last`) gives the
40
- # IDENTICAL string either way for a single segment — the two
41
- # branches were already equivalent for a name with no `::` in
42
- # it, which is every bareword before this.
45
+ # A real `Module` subclass, not a decorated Symbol, for exactly
46
+ # that reason — nothing else answers `::`. Every consumer of a
47
+ # bareword type keeps working duck-typed: `Attribute#spell`'s
48
+ # `type.is_a?(Module)` branch fires for one of these where a
49
+ # bare Symbol falls to `type.to_s`, and `Naming.demodulise`
50
+ # (`path.split("::").last`) gives the identical string either
51
+ # way for a single segment — the two branches are equivalent
52
+ # for a name with no `::` in it, which is every unscoped
53
+ # bareword.
43
54
  class ScopedConstant < Module
55
+ # Wraps a constant path, the form a `ConstShim` resolver hands back for a bareword.
56
+ #
57
+ # @param path [Symbol, String] one segment such as `:Account`, or a `::`-joined path
58
+ # @return [Bluebook::DSL::ConstShim::ScopedConstant] a module standing in for that path
44
59
  def self.for(path) = new(path.to_s)
45
60
 
61
+ # @param path [String] the constant path this module stands in for
46
62
  def initialize(path)
47
63
  super()
48
64
  @path = path
49
65
  end
50
66
 
51
- # ONE MORE SEGMENT, the same way an unresolved const anywhere
67
+ # Extends the path by one segment, so `Account::Debit` resolves past `Account`.
68
+ #
69
+ # One more segment, the same way an unresolved const anywhere
52
70
  # else does — `Account::Debit::Anything` keeps chaining rather
53
71
  # than refusing, since nothing here knows how deep a reference
54
72
  # is meant to go; the DSL keyword that finally reads `.to_s`
55
73
  # is the one place that does.
74
+ #
75
+ # @param name [Symbol] the segment written after `::`
76
+ # @return [Bluebook::DSL::ConstShim::ScopedConstant] a new constant for the longer path
56
77
  def const_missing(name) = ScopedConstant.for("#{@path}::#{name}")
57
78
 
58
79
  def to_s = @path
59
80
  def to_sym = @path.to_sym
60
81
  def inspect = @path
82
+
83
+ # Exposes the raw path under a name no ordinary `Module` answers, so `==` can compare
84
+ # two scoped constants without going through `to_s`.
85
+ #
86
+ # @return [String] the `::`-joined path, such as `"Account::Debit"`
61
87
  def hecks_path = @path
62
88
 
63
89
  def ==(other) = other.is_a?(ScopedConstant) ? @path == other.hecks_path : @path.to_sym == other
@@ -65,15 +91,20 @@ module Hecks
65
91
  def hash = @path.hash
66
92
  end
67
93
 
68
- # A SCOPED NAME IS WRITTEN AS TEXT, NOT AS A CONSTANT PATH — see the
94
+ # A scoped name is written as text, not as a constant path — see the
69
95
  # note on `admits:` in AttributeCollector. A resolver returning a
70
96
  # Module (so that `Vocabulary::QueryComparator` reaches a second
71
97
  # `const_missing`) was tried and cannot be made to hold : `Facade::
72
- # Surface` installs EVERY aggregate name as a top-level constant, so
98
+ # Surface` installs every aggregate name as a top-level constant, so
73
99
  # once any facade is built, `Vocabulary` resolves to that real module
74
100
  # and never reaches this hook at all. A spelling that works only
75
101
  # before a facade exists is worse than one that always works.
76
102
  module Hook
103
+ # Answers an undeclared top-level constant from the active resolver, if there is one.
104
+ #
105
+ # @param name [Symbol] the missing constant's name
106
+ # @return [Object] whatever the active resolver returns for `name`
107
+ # @raise [NameError] if no resolver is installed, as Ruby raises for any unknown constant
77
108
  def const_missing(name)
78
109
  resolver = ConstShim.resolver
79
110
  resolver ? resolver.call(name) : super
@@ -14,21 +14,27 @@ module Hecks
14
14
 
15
15
  include WordGate
16
16
 
17
- # `legacy_bare_port:` — ONLY `Hecks.port`'s own top-level method
17
+ # `legacy_bare_port:` — only `Hecks.port`'s own top-level method
18
18
  # (lib/hecks.rb) passes `true`. `PortBuilder#build` never refused
19
19
  # an empty build (no verb, no signal, nothing) — `Port.new(verb:
20
20
  # nil, signal: :reply)` is a real, allowed shape dsl_spec.rb's own
21
21
  # "a port" tests rely on (`signal`-only, no `verb` at all). The
22
- # AGGREGATE-scoped (`BindingProxy#port`) and hecksagon-ROOT
23
- # (`HecksagonBuilder#port_impl`) callers both reach this SAME
22
+ # aggregate-scoped (`BindingProxy#port`) and hecksagon-root
23
+ # (`HecksagonBuilder#port_impl`) callers both reach this same
24
24
  # class with `owner: nil` too when they're building the bare-verb
25
- # shape (`port_impl`'s own root-level port can be EITHER shape,
25
+ # shape (`port_impl`'s own root-level port can be either shape,
26
26
  # decided only after `build` returns) — so `owner.nil?` cannot be
27
27
  # the discriminator between "old Hecks.port semantics" and "real
28
28
  # DomainPort semantics"; those two callers correctly want the
29
29
  # stricter "declares no verb and no operations" refusal `build`
30
30
  # already raises below, unchanged. Only the literal top-level
31
31
  # `.port` file caller wants the older, looser rule.
32
+ #
33
+ # @param name [String] the port's name
34
+ # @param owner [String, nil] name of the aggregate the port is declared on, handed to
35
+ # each operation's builder; nil for a root-level or top-level port
36
+ # @param legacy_bare_port [Boolean] true only for `Hecks.port`: an empty body then builds
37
+ # a verbless `Port` rather than being refused
32
38
  def initialize(name, owner: nil, legacy_bare_port: false)
33
39
  @name = name
34
40
  @owner = owner
@@ -38,40 +44,63 @@ module Hecks
38
44
  @legacy_bare_port = legacy_bare_port
39
45
  end
40
46
 
41
- # WHAT THE OUTSIDE TELLS US an external fact arriving, translated
42
- # into this domain's own word for it. Spelled `operation` before it
43
- # had a twin, and `operation` still works: the corpus is full of it,
44
- # and renaming a word costs every chapter that uses it for no gain a
45
- # reader can feel.
47
+ # Declares an inbound operation: a fact the outside world delivers to this domain.
48
+ #
49
+ # **What the outside tells us** an external fact arriving, translated
50
+ # into this domain's own word for it. Spelled `operation` or `tells`;
51
+ # `operation` stays because the corpus is full of it, and renaming
52
+ # a word costs every chapter that uses it for no gain a reader can
53
+ # feel.
46
54
  #
47
- # RENAMED FROM `tells` — item #13's full metaprogrammed dispatch
48
- # (slice 4c). `operation`/`tells` are TWO separate Keyword rows
55
+ # Answers both words — item #13's full metaprogrammed dispatch
56
+ # (slice 4c). `operation`/`tells` are two separate Keyword rows
49
57
  # (a word admitting two forms) that both name `calls: "tells_impl"`
50
- # — the routing between the two spellings now lives in the table,
58
+ # — the routing between the two spellings lives in the table,
51
59
  # not in a Ruby `alias`. Not bootstrap-reachable (checked
52
- # directly), so no BOOTSTRAP_CALLS_FALLBACK entry needed.
60
+ # directly), so its `BOOTSTRAP_CALLS_FALLBACK` row is never consulted.
61
+ #
62
+ # @param name [String] the operation's name, such as `"PaymentSettled"`
63
+ # @param to [Symbol, String, Module, nil] the aggregate the operation routes to, written
64
+ # as a bare constant; nil leaves routing to the operation's own attributes
65
+ # @yield the operation body (`attribute`, `emits`), evaluated against a
66
+ # `PortOperationBuilder`
67
+ # @return [Array<Bluebook::PortOperation>] every operation declared so far, this one last
68
+ # @raise [Bluebook::DSL::Malformed] if the body declares no `emits`, or uses `answers` or
69
+ # `refuses`, which belong to an `asks`
53
70
  def tells_impl(name, to: nil, &)
54
71
  @operations << PortOperationBuilder.build(name, to: to, owner: @owner, direction: :inbound, &)
55
72
  end
56
73
 
57
- # WHAT WE ASK OF THE OUTSIDE the direction this language did not
58
- # have. Before this, a domain could be CALLED by an adapter and never
59
- # call one. An `asks` is dispatched like any other port operation, so
74
+ # Declares an outbound operation: a question this domain puts to the outside world.
75
+ #
76
+ # **What we ask of the outside** the direction that lets a domain
77
+ # call an adapter, not only be called by one. An `asks` is dispatched
78
+ # like any other port operation, so
60
79
  # a `policy` can trigger it off an event, and it comes back as one of
61
80
  # the two events it named — which is what makes the outside world
62
81
  # something the model can reason about rather than a place exceptions
63
82
  # come from.
64
83
  #
65
- # RENAMED FROM `asks` item #13's full metaprogrammed dispatch
66
- # (slice 4c), same reasoning as tells_impl above.
84
+ # Answers the `asks` word through the table's `calls:` column — item
85
+ # #13's full metaprogrammed dispatch (slice 4c), same reasoning as
86
+ # `tells_impl` above.
87
+ #
88
+ # @param name [String] the operation's name
89
+ # @param to [Symbol, String, Module, nil] the aggregate the operation routes to, written
90
+ # as a bare constant; nil leaves routing to the operation's own attributes
91
+ # @yield the operation body (`attribute`, `answers`, `refuses`), evaluated against a
92
+ # `PortOperationBuilder`
93
+ # @return [Array<Bluebook::PortOperation>] every operation declared so far, this one last
94
+ # @raise [Bluebook::DSL::Malformed] if the body declares `emits`, or lacks either
95
+ # `answers` or `refuses`
67
96
  def asks_impl(name, to: nil, &)
68
97
  @operations << PortOperationBuilder.build(name, to: to, owner: @owner, direction: :outbound, &)
69
98
  end
70
99
 
71
- # THE DRIVEN HALF OF THE SAME WORD. `operation`/`emits` translates an
100
+ # The driven half of the same word. `operation`/`emits` translates an
72
101
  # inbound fact into this domain's own event vocabulary — there is no
73
102
  # channel back to a caller beyond the events it emits. `verb` is the
74
- # opposite direction: the domain calling OUT to a swappable adapter
103
+ # opposite direction: the domain calling out to a swappable adapter
75
104
  # and getting a real value back (a checkout URL, a fetched document),
76
105
  # exactly what `Hecks.port "name" do verb "x" end` already builds —
77
106
  # this is that same `Port`, reached from the same `port` call
@@ -83,9 +112,12 @@ module Hecks
83
112
  # coerce-and-assign with nothing else, now executed by
84
113
  # `GenericDispatch`.
85
114
 
115
+ # Names the verb aggregates call this port by, which makes it a driven `Port` rather than
116
+ # a `DomainPort` of operations.
117
+ #
86
118
  # `Hecks.port "x" do verb "y"; signal :effect end`'s own two words,
87
119
  # reachable here too — a bare-verb `DomainPortBuilder.build` falls
88
- # back to the SAME `Port` object `PortBuilder` produces (`build`,
120
+ # back to the same `Port` object `PortBuilder` produces (`build`,
89
121
  # below), so any `.port` file can migrate to being parsed by this
90
122
  # builder with zero change to its own text, or to any caller that
91
123
  # reads `.verb`/`.signal` off the `Port` it gets back. Ordinary
@@ -93,20 +125,40 @@ module Hecks
93
125
  # header is explicit that a word answered this way never reaches
94
126
  # its `method_missing`, so no new self-hosted grammar row is
95
127
  # needed for either word under this context.
128
+ #
129
+ # @param value [String, Symbol] the verb, such as `"charged_by"`
130
+ # @return [String] the verb as stored
96
131
  def verb(value) = @verb = value.to_s
132
+
133
+ # Sets whether a verb-shaped port hands a value back; one left unset signals `:reply`.
134
+ #
135
+ # @param value [Symbol, String] `:reply` when the adapter answers with a value,
136
+ # `:effect` when it is called only for its effect
137
+ # @return [Symbol] the signal as stored
97
138
  def signal(value) = @signal = value.to_sym
98
139
 
99
- # THE METHOD CONTRACT `PortBuilder#answers`'s own twin, added
100
- # here after the fact: a `.port` file migrated to parse through
101
- # this builder (the repoint `lib/hecks.rb#port`'s own comment
102
- # describes) can still declare one (`extraction.port`'s own
140
+ # Declares one method an adapter bound to a verb-shaped port must respond to.
141
+ #
142
+ # **The method contract** `PortBuilder#answers`'s own twin: a
143
+ # `.port` file parsed through this builder (the repoint
144
+ # `lib/hecks.rb#port`'s own comment describes) can declare one
145
+ # (`extraction.port`'s own
103
146
  # `answers :canonical`, real, live corpus text) — this builder's
104
147
  # bare-verb fallback needs to carry it through to the same `Port`
105
148
  # object `PortBuilder` itself would have built, or the migration
106
149
  # would silently drop a method-contract check for any `.port`
107
150
  # file that uses this word.
151
+ #
152
+ # @param name [Symbol, String] the method name, such as `:canonical`
153
+ # @return [Array<Symbol>] every method declared so far, this one last
108
154
  def answers(name) = @answers << name.to_sym
109
155
 
156
+ # Assembles whichever of the two port shapes the body declared.
157
+ #
158
+ # @return [Bluebook::Port, Bluebook::DomainPort] a `Port` when the body named a `verb`
159
+ # (or was empty under `legacy_bare_port:`), otherwise a `DomainPort` of its operations
160
+ # @raise [Bluebook::DSL::Malformed] if the body declares both a verb and operations,
161
+ # declares neither without `legacy_bare_port:`, or the port language refuses the `Port`
110
162
  def build
111
163
  if @verb && !@operations.empty?
112
164
  raise Malformed,
@@ -123,6 +175,19 @@ module Hecks
123
175
  DomainPort.new(name: @name, operations: @operations)
124
176
  end
125
177
 
178
+ # Evaluates a `port` block against a fresh builder and returns whichever shape it declared.
179
+ #
180
+ # @param name [String] the port's name
181
+ # @param owner [String, nil] name of the aggregate the port is declared on, or nil for a
182
+ # root-level or top-level port
183
+ # @param legacy_bare_port [Boolean] true only for `Hecks.port`, which lets an empty body
184
+ # build a verbless `Port`
185
+ # @yield the port body, evaluated with the builder as `self`; may be omitted
186
+ # @return [Bluebook::Port, Bluebook::DomainPort] a verb-shaped `Port`, or a `DomainPort`
187
+ # holding the declared operations
188
+ # @raise [Bluebook::DSL::Malformed] if the body declares both shapes, declares neither
189
+ # without `legacy_bare_port:`, holds an operation its builder refuses, or uses a word
190
+ # the `DomainPort` grammar does not admit
126
191
  def self.build(name, owner: nil, legacy_bare_port: false, &block)
127
192
  builder = new(name, owner: owner, legacy_bare_port: legacy_bare_port)
128
193
  builder.instance_eval(&block) if block
@@ -30,18 +30,18 @@ module Hecks
30
30
  @owner_value_objects = owner_value_objects
31
31
  @identity_name_prefix = identity_name_prefix || Naming.demodulise(name)
32
32
  @identity_value_object_installer = identity_value_object_installer
33
- # THE AGGREGATE-WIDE cross-entity given pool — ONE hash, the
34
- # SAME object, threaded unchanged through every piece nested
33
+ # The aggregate-wide cross-entity given pool — one hash, the
34
+ # same object, threaded unchanged through every piece nested
35
35
  # under one aggregate however deep (the identical shape
36
36
  # `@owner_value_objects` already threads — see this class'
37
37
  # own `entity` comment). `given`'s own block form writes
38
- # through to it; a SIBLING piece's bare command-level
38
+ # through to it; a sibling piece's bare command-level
39
39
  # reference reads from it via `CommandBuilder#
40
40
  # reference_named_given`.
41
41
  @owner_named_givens = owner_named_givens
42
- # ONE LEVEL WIDER STILL — the CHAPTER-WIDE, ENTITY-SCOPED pool
42
+ # **One level wider still** — the chapter-wide, entity-scoped pool
43
43
  # (the piece analogue of `AggregateBuilder#@chapter_named_givens`,
44
- # one level down). `@aggregate_name` names THIS piece's own
44
+ # one level down). `@aggregate_name` names this piece's own
45
45
  # root, so the write-through below can key itself
46
46
  # "AggregateName.EntityName" — the same dotted addressing
47
47
  # convention `declared_by:` already uses chapter-wide, one
@@ -51,10 +51,10 @@ module Hecks
51
51
  @aggregate_name = aggregate_name || Naming.demodulise(name)
52
52
  @chapter_entity_named_givens = chapter_entity_named_givens
53
53
  @chapter_entity_pending_givens = chapter_entity_pending_givens
54
- # DEFERRED CONSTRUCTION — see `AggregateBuilder#drain_pending!`'s
54
+ # **Deferred construction** — see `AggregateBuilder#drain_pending!`'s
55
55
  # own comment; the identical mechanism, one level down, so a
56
56
  # nested piece's own commands (Dispatch inside Handler) see
57
- # every SIBLING entity/command/query this piece goes on to
57
+ # every sibling entity/command/query this piece goes on to
58
58
  # declare, not just whatever came before it textually.
59
59
  @pending_entities = []
60
60
  @pending_commands = []
@@ -63,12 +63,12 @@ module Hecks
63
63
 
64
64
  def description(value) = @description = value
65
65
 
66
- # THE SAME FIELD AggregateBuilder's OWN reference_to BUILDS — a
66
+ # The same field AggregateBuilder's own reference_to builds — a
67
67
  # piece can hold a reference to another root exactly the way its
68
68
  # own head can (Card.assignee_id, a Team's own id), just never
69
- # to another PIECE, since there's no cross-piece addressing
69
+ # to another piece, since there's no cross-piece addressing
70
70
  # anywhere in this language to resolve one against.
71
- # RENAMED FROM `reference_to` — item #13's full metaprogrammed
71
+ # Renamed from `reference_to` — item #13's full metaprogrammed
72
72
  # dispatch (slice 4b). Bootstrap-reachable, in
73
73
  # GenericDispatch::BOOTSTRAP_CALLS_FALLBACK.
74
74
  def reference_to_impl(type, as: nil, optional: false)
@@ -108,8 +108,8 @@ module Hecks
108
108
  relationship_attribute(target, :belongs_to, as || Naming.snake(target).to_sym, optional: optional)
109
109
  end
110
110
 
111
- # A PIECE is known by a field, not by a whole value object.
112
- # `identified_by :sequence` names the SCALAR inside it, which is
111
+ # A piece is known by a field, not by a whole value object.
112
+ # `identified_by :sequence` names the scalar inside it, which is
113
113
  # what an id actually is — a LedgerEntry is entry 3, not entry
114
114
  # {"value":3}. `identified_by` itself is AttributeCollector's own
115
115
  # shared method (S9) — the two constructs cannot drift apart in
@@ -118,10 +118,10 @@ module Hecks
118
118
  # declare for the same reason a head may.
119
119
 
120
120
  # `from:` — see `AggregateBuilder#command`'s own comment; the
121
- # SAME guard, checked against this PIECE's own lifecycle field
121
+ # same guard, checked against this piece's own lifecycle field
122
122
  # (S10, ADR 0025 — a piece's own state machine is checkable the
123
123
  # same way a head's is).
124
- # RENAMED FROM `command`/`query`/`entity`/`lifecycle` (all below)
124
+ # Renamed from `command`/`query`/`entity`/`lifecycle` (all below)
125
125
  # — item #13's full metaprogrammed dispatch (slice 4c), same
126
126
  # reasoning as AggregateBuilder's own siblings: bootstrap-
127
127
  # reachable, in BOOTSTRAP_CALLS_FALLBACK.
@@ -133,14 +133,14 @@ module Hecks
133
133
  @pending_queries << [name, block]
134
134
  end
135
135
 
136
- # S17, ADR 0026 — A PIECE NESTED INSIDE A PIECE. "A `Dispatch`
136
+ # S17, ADR 0026 — a piece nested inside a piece. "A `Dispatch`
137
137
  # [has] no life outside its `Handler`" (the ADR's own words) —
138
138
  # the same reason `Member` nests inside `ValueObject`, one level
139
139
  # further in. `owner_value_objects` passes straight through
140
140
  # unchanged, not re-derived from this entity's own attributes —
141
141
  # a piece mints no value objects of its own at any depth, so a
142
- # NESTED piece's bare `identified_by :field` still resolves
143
- # against the SAME root aggregate's value objects an outer
142
+ # nested piece's bare `identified_by :field` still resolves
143
+ # against the same root aggregate's value objects an outer
144
144
  # piece's already does (`AggregateBuilder#entity`'s own comment
145
145
  # names this pool ; there is exactly one of them, however deep
146
146
  # the nesting goes).
@@ -152,15 +152,15 @@ module Hecks
152
152
  @lifecycle = LifecycleBuilder.build(field, default: default, &)
153
153
  end
154
154
 
155
- # A PRECONDITION SHARED ACROSS THIS PIECE'S OWN COMMANDS, DECLARED
156
- # ONCE — the same move `AggregateBuilder#given` already makes,
155
+ # A precondition shared across this piece's own commands, declared
156
+ # once — the same move `AggregateBuilder#given` already makes,
157
157
  # one level down. Real, live redundancy this closes: banking's
158
158
  # own `LedgerEntry.Amend`/`LedgerEntry.Reverse` each repeated
159
159
  # `given("customer is active") { parent.customer.status ==
160
160
  # "active" }` and `given("account is open") { parent.status ==
161
161
  # "open" }`, byte for byte, because a piece had no way to declare
162
- # either once and reference it back — only the AGGREGATE could.
163
- # DECLARE BEFORE THE COMMANDS THAT REFERENCE IT, the same
162
+ # either once and reference it back — only the aggregate could.
163
+ # Declare before the commands that reference it, the same
164
164
  # ordering `AggregateBuilder#given`'s own comment names — though
165
165
  # since ADR 0028, `command` only queues a descriptor and actually
166
166
  # builds at `#drain_pending!` time, well after this whole block
@@ -168,36 +168,36 @@ module Hecks
168
168
  # order within the block no longer actually matters here; named
169
169
  # for the reader anyway, since `given`'s own resolution logic
170
170
  # (`CommandBuilder#reference_named_given`) still reads whatever
171
- # `@named_givens` holds AT THE COMMAND'S OWN BUILD TIME, not by
171
+ # `@named_givens` holds at the command's own build time, not by
172
172
  # magic.
173
- # RENAMED FROM `given` — item #13's full metaprogrammed dispatch
173
+ # Renamed from `given` — item #13's full metaprogrammed dispatch
174
174
  # (slice 4b), same reasoning as reference_to_impl above.
175
175
  #
176
- # BARENO BLOCKREFERENCES ANOTHER PIECE'S OWN DECLARATION,
177
- # ANYWHERE IN THE CHAPTER, not just a sibling under this same
176
+ # Bareno blockreferences another piece's own declaration,
177
+ # anywhere in the chapter, not just a sibling under this same
178
178
  # aggregate — one level wider than round 4's own cross-entity
179
179
  # sharing, mirroring `AggregateBuilder#given_impl`'s own
180
180
  # chapter-wide shape exactly one level down. Real, live corpus
181
181
  # this closes: `Account::LedgerEntry` and `SafeDepositBox::Visit`
182
- # — two pieces under two DIFFERENT aggregates — independently
182
+ # — two pieces under two different aggregates — independently
183
183
  # typed `given("customer is active") { parent.customer.status ==
184
184
  # "active" }` byte for byte; neither the aggregate-level chapter
185
- # pool (a DIFFERENT canonical — bare `customer.status`, the
185
+ # pool (a different canonical — bare `customer.status`, the
186
186
  # wrong scope for a piece's own command) nor the existing
187
187
  # same-aggregate cross-entity pool (`@owner_named_givens`, scoped
188
- # to ONE aggregate's own entity tree) could reach across the
188
+ # to one aggregate's own entity tree) could reach across the
189
189
  # aggregate boundary. Resolved against `@chapter_entity_named_
190
190
  # givens`, keyed "AggregateName.EntityName" — see
191
191
  # `#reference_named_chapter_entity_given`'s own comment for the
192
192
  # algorithm and `docs/implemented/resolution-rules/
193
193
  # chapter-entity-given.md` for the full write-up.
194
194
  #
195
- # `declared_by:` is a PLAIN STRING ("Account.LedgerEntry"), not a
195
+ # `declared_by:` is a plain string ("Account.LedgerEntry"), not a
196
196
  # constant — unlike `AggregateBuilder#given_impl`'s own
197
197
  # `declared_by:`, which names a real aggregate constant. A piece
198
198
  # has no first-class, independently-addressable reference
199
199
  # anywhere in this language (only its owning aggregate does);
200
- # inventing one to make this ONE argument spelling symmetrical
200
+ # inventing one to make this one argument spelling symmetrical
201
201
  # with the aggregate-level word is a real, separate, unscoped
202
202
  # feature this fix does not need — ships textual now, the same
203
203
  # way `admits:` shipped textual before its own constant-bridge
@@ -209,40 +209,40 @@ module Hecks
209
209
  named = build_rule(Given, description, predicate, owner_name: @name, word: "given",
210
210
  extraction_failure: "its source could not be read, so no other runtime could ever evaluate it")
211
211
  @named_givens[description] = named
212
- # WRITE-THROUGH, first-declared-wins (`||=`) — a SECOND piece
212
+ # Write-through, first-declared-wins (`||=`) — a second piece
213
213
  # under the same aggregate independently declaring the exact
214
214
  # same description stays purely local to itself (no silent
215
215
  # overwrite of whatever the first piece already shared;
216
216
  # real, live case a fuzzer or a future codemod could easily
217
- # surface: two pieces phrasing an UNRELATED rule identically
217
+ # surface: two pieces phrasing an unrelated rule identically
218
218
  # by coincidence, same as an aggregate-level given already
219
219
  # tolerates today).
220
220
  @owner_named_givens[description] ||= named
221
- # WRITE-THROUGH, PER OWNER — the chapter-wide analogue of the
221
+ # **Write-through, per owner** — the chapter-wide analogue of the
222
222
  # line above, keyed by [description, this piece's own dotted
223
223
  # "Aggregate.Entity" name] rather than description alone, the
224
224
  # identical reasoning `AggregateBuilder#given_impl`'s own
225
- # chapter write-through gives: two DIFFERENT pieces (anywhere
226
- # in the chapter) independently declaring the SAME description
227
- # are two DISTINCT candidates a later bare reference chooses
225
+ # chapter write-through gives: two different pieces (anywhere
226
+ # in the chapter) independently declaring the same description
227
+ # are two distinct candidates a later bare reference chooses
228
228
  # between (via `declared_by:` once there is more than one),
229
229
  # never silently merged into one slot.
230
230
  @chapter_entity_named_givens[description] ||= {}
231
231
  @chapter_entity_named_givens[description]["#{@aggregate_name}.#{@name}"] ||= named
232
232
  end
233
233
 
234
- # A PIECE'S OWN SHAPE RULE (S10, ADR 0025's own "Rules" shape,
234
+ # A piece's own shape rule (S10, ADR 0025's own "Rules" shape,
235
235
  # one level down from `ValueObjectBuilder#invariant`, whose
236
236
  # extraction/error pattern this mirrors) — checked against
237
- # EVERY INSTANCE of this piece the aggregate holds, not once
237
+ # every instance of this piece the aggregate holds, not once
238
238
  # against the aggregate's own flat state
239
239
  # (`Admissibility#enforce_invariants`'s own recursive walk).
240
240
  # No reference-by-name form (unlike `given`) — no known corpus
241
- # need for a piece's own invariant to be shared with a SIBLING
241
+ # need for a piece's own invariant to be shared with a sibling
242
242
  # piece yet; if that need shows up, it is `given`'s own
243
243
  # cross-entity write-through pattern to extend, not a reason to
244
244
  # invent a second one here speculatively.
245
- # RENAMED FROM `invariant` — item #13's full metaprogrammed
245
+ # Renamed from `invariant` — item #13's full metaprogrammed
246
246
  # dispatch (slice 4b), same reasoning as given_impl above.
247
247
  def invariant_impl(description, &predicate)
248
248
  @invariants << build_rule(Invariant, description, predicate, owner_name: @name, word: "invariant",
@@ -283,25 +283,25 @@ module Hecks
283
283
 
284
284
  private
285
285
 
286
- # PRIMITIVE 2 (RuleReference#resolve_owner_keyed) — the CHAPTER-
287
- # WIDE, ENTITY-SCOPED analogue of `AggregateBuilder#
286
+ # Primitive 2 (RuleReference#resolve_owner_keyed) — the chapter-
287
+ # wide, entity-scoped analogue of `AggregateBuilder#
288
288
  # reference_named_chapter_given`; the three branches below are
289
- # this construct's OWN refusal wording, not shared, matching that
289
+ # this construct's own refusal wording, not shared, matching that
290
290
  # method's own precedent (`declared_by:` only exists on `given`
291
- # so far, at either scope). UNRESOLVED is deferred, not raised
291
+ # so far, at either scope). Unresolved is deferred, not raised
292
292
  # here — see `#pending_chapter_entity_given`, below.
293
293
  #
294
- # WRITES THROUGH TO `@owner_named_givens` TOO — not just
294
+ # **Writes through to `@owner_named_givens` too** — not just
295
295
  # `@named_givens` — or this piece resolving a description via the
296
- # WIDER, chapter pool would leave the NARROWER, same-aggregate
296
+ # wider, chapter pool would leave the narrower, same-aggregate
297
297
  # pool (`EntityBuilder#given_impl`'s own block-form write-through)
298
- # never populated for this description, breaking any SIBLING
298
+ # never populated for this description, breaking any sibling
299
299
  # piece's existing command-level bare reference
300
300
  # (`CommandBuilder#reference_named_given`) that depends on it —
301
301
  # real, live corpus: `SafeDepositBox::KeyIssuance.Return`'s own
302
302
  # bare `given("customer is active")` resolves through
303
303
  # `@owner_named_givens`, populated by `Visit`'s declaration
304
- # whether `Visit` types the predicate itself OR (now) references
304
+ # whether `Visit` types the predicate itself or (now) references
305
305
  # `Account::LedgerEntry`'s instead — this write keeps that
306
306
  # working unchanged either way, `||=` so nothing here overrides
307
307
  # an actual local declaration if one is ever added later.
@@ -329,10 +329,10 @@ module Hecks
329
329
  @owner_named_givens[description] ||= named
330
330
  end
331
331
 
332
- # A CHAPTER MAY BE SPLIT ACROSS FILES — the identical reason
332
+ # A chapter may be split across files — the identical reason
333
333
  # `AggregateBuilder#pending_chapter_given` defers rather than
334
334
  # raising the moment a bare reference outruns what's loaded so
335
- # far. Hands back a PLACEHOLDER `Given`, embedded by Ruby object
335
+ # far. Hands back a placeholder `Given`, embedded by Ruby object
336
336
  # reference in this piece's own `preconditions`, and queues the
337
337
  # request in `@chapter_entity_pending_givens` —
338
338
  # `BluebookBuilder#resolve_pending_chapter_entity_givens!`
@@ -344,22 +344,22 @@ module Hecks
344
344
  placeholder
345
345
  end
346
346
 
347
- # A PIECE'S OWN `one_of` LANDS ON ITS AGGREGATE. A type-position
347
+ # A piece's own `one_of` lands on its aggregate. A type-position
348
348
  # `one_of("never_moved", "moved")` on an entity attribute
349
349
  # synthesizes a closed-set value object — and until this, that
350
- # object was built and then DROPPED: `Entity.declare` carries no
350
+ # object was built and then dropped: `Entity.declare` carries no
351
351
  # value objects, so the synthesized set existed nowhere in the
352
352
  # finished graph. The attribute stayed typed "Moved" with nothing
353
353
  # to resolve it: runtime admission had no closed set to enforce
354
354
  # (the one_of was decorative), and the fuzzer's ValueGenerator
355
355
  # crashed every run on the first domain to declare one — a chess
356
356
  # King/Rook's own castling flag — with `does not know primitive
357
- # type "Moved"`. Installed through the SAME hook an entity's own
357
+ # type "Moved"`. Installed through the same hook an entity's own
358
358
  # identity value object already rides to the aggregate
359
359
  # (`identity_value_object_installer`, threaded unchanged through
360
360
  # nested pieces). Two sibling pieces synthesizing the same set
361
- # (King's and Rook's own `moved`) install it once; the same NAME
362
- # with a DIFFERENT member list is refused as the collision it is,
361
+ # (King's and Rook's own `moved`) install it once; the same name
362
+ # with a different member list is refused as the collision it is,
363
363
  # never first-wins silently.
364
364
  def install_closed_sets!
365
365
  return unless @identity_value_object_installer
@@ -435,7 +435,7 @@ module Hecks
435
435
 
436
436
  # `identified_by`'s own resolution pool (AttributeCollector#resolve_
437
437
  # pending_identity!'s hook, S9) — a piece mints no value objects of
438
- # its own, so a bare field's own type resolves against its OWNER
438
+ # its own, so a bare field's own type resolves against its owner
439
439
  # aggregate's, passed in at declaration (`AggregateBuilder#entity`).
440
440
  def identity_pool = @owner_value_objects
441
441