hecks 1.4.0 → 1.5.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 (267) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hecks/adapters/driven/claude_code.rb +65 -0
  3. data/lib/hecks/adapters/driven/folder.rb +73 -0
  4. data/lib/hecks/adapters/driven/google_authentication.rb +25 -4
  5. data/lib/hecks/adapters/driven/governance_authorization.rb +57 -6
  6. data/lib/hecks/adapters/driven/heki/journal.rb +8 -0
  7. data/lib/hecks/adapters/driven/heki/saga_store.rb +53 -7
  8. data/lib/hecks/adapters/driven/heki/snapshot.rb +1 -1
  9. data/lib/hecks/adapters/driven/heki.rb +93 -2
  10. data/lib/hecks/adapters/driven/identity_registry.rb +10 -0
  11. data/lib/hecks/adapters/driven/in_memory_ordering.rb +22 -0
  12. data/lib/hecks/adapters/driven/in_process_key_vault.adapter +3 -0
  13. data/lib/hecks/adapters/driven/in_process_key_vault.rb +53 -0
  14. data/lib/hecks/adapters/driven/lambda/client.rb +35 -7
  15. data/lib/hecks/adapters/driven/lambda.rb +43 -2
  16. data/lib/hecks/adapters/driven/local_storage.rb +67 -1
  17. data/lib/hecks/adapters/driven/memory.rb +13 -13
  18. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +20 -0
  19. data/lib/hecks/adapters/driven/prism.rb +46 -7
  20. data/lib/hecks/adapters/driven/secure_random_identity.rb +3 -0
  21. data/lib/hecks/adapters/driven/system_clock.rb +3 -0
  22. data/lib/hecks/adapters/driven/tenant_provisioner.adapter +3 -0
  23. data/lib/hecks/adapters/driven/tenant_provisioner.rb +66 -0
  24. data/lib/hecks/adapters/driven.rb +2 -0
  25. data/lib/hecks/adapters/driving/github_webhook.rb +14 -1
  26. data/lib/hecks/behaviors/dsl.rb +58 -0
  27. data/lib/hecks/behaviors/expectations.rb +168 -7
  28. data/lib/hecks/behaviors/ir.rb +11 -0
  29. data/lib/hecks/behaviors/rspec.rb +8 -0
  30. data/lib/hecks/behaviors/runner.rb +19 -0
  31. data/lib/hecks/behaviors.rb +8 -0
  32. data/lib/hecks/bluebook/aggregate.rb +30 -0
  33. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +7 -0
  34. data/lib/hecks/bluebook/assembly/build.rb +25 -4
  35. data/lib/hecks/bluebook/assembly/contract.rb +66 -14
  36. data/lib/hecks/bluebook/assembly/contracts.rb +24 -19
  37. data/lib/hecks/bluebook/assembly/marks.rb +143 -14
  38. data/lib/hecks/bluebook/assembly/specializer.rb +19 -0
  39. data/lib/hecks/bluebook/assembly.rb +25 -7
  40. data/lib/hecks/bluebook/attribute.rb +17 -3
  41. data/lib/hecks/bluebook/behaviour/aggregate.rb +32 -0
  42. data/lib/hecks/bluebook/behaviour/attribute.rb +13 -0
  43. data/lib/hecks/bluebook/behaviour/chapter.rb +55 -2
  44. data/lib/hecks/bluebook/behaviour/command.rb +33 -3
  45. data/lib/hecks/bluebook/behaviour/domain_port.rb +20 -0
  46. data/lib/hecks/bluebook/behaviour/entity.rb +12 -0
  47. data/lib/hecks/bluebook/behaviour/hexagon.rb +26 -0
  48. data/lib/hecks/bluebook/behaviour/lifecycle.rb +23 -2
  49. data/lib/hecks/bluebook/behaviour/policy.rb +34 -9
  50. data/lib/hecks/bluebook/behaviour/process_manager.rb +32 -1
  51. data/lib/hecks/bluebook/behaviour/query.rb +5 -0
  52. data/lib/hecks/bluebook/behaviour/read_model.rb +21 -0
  53. data/lib/hecks/bluebook/behaviour/traits.rb +36 -0
  54. data/lib/hecks/bluebook/behaviour/value_object.rb +17 -5
  55. data/lib/hecks/bluebook/chapter.rb +23 -0
  56. data/lib/hecks/bluebook/command.rb +53 -8
  57. data/lib/hecks/bluebook/domain_port.rb +25 -0
  58. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +196 -37
  59. data/lib/hecks/bluebook/dsl/attribute_collector.rb +79 -42
  60. data/lib/hecks/bluebook/dsl/binding_proxy.rb +59 -0
  61. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +385 -53
  62. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +135 -21
  63. data/lib/hecks/bluebook/dsl/command_builder.rb +189 -27
  64. data/lib/hecks/bluebook/dsl/entity_builder.rb +139 -9
  65. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +41 -0
  66. data/lib/hecks/bluebook/dsl/identity_declaration.rb +23 -6
  67. data/lib/hecks/bluebook/dsl/policy_builder.rb +60 -19
  68. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +79 -15
  69. data/lib/hecks/bluebook/dsl/query_builder.rb +33 -4
  70. data/lib/hecks/bluebook/dsl/read_model_builder.rb +102 -27
  71. data/lib/hecks/bluebook/dsl/rule_reference.rb +60 -8
  72. data/lib/hecks/bluebook/dsl/translation_builder.rb +146 -40
  73. data/lib/hecks/bluebook/dsl/value_object_builder.rb +55 -7
  74. data/lib/hecks/bluebook/entity.rb +29 -0
  75. data/lib/hecks/bluebook/expression/ast_json.rb +115 -23
  76. data/lib/hecks/bluebook/expression/ast_reader.rb +29 -0
  77. data/lib/hecks/bluebook/expression/canonical_form.rb +48 -9
  78. data/lib/hecks/bluebook/expression/evaluator.rb +207 -29
  79. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +36 -0
  80. data/lib/hecks/bluebook/expression/resolver.rb +320 -77
  81. data/lib/hecks/bluebook/hexagon.rb +34 -0
  82. data/lib/hecks/bluebook/lifecycle.rb +11 -0
  83. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -0
  84. data/lib/hecks/bluebook/meta_validator/judge.rb +30 -26
  85. data/lib/hecks/bluebook/meta_validator/plan.rb +43 -8
  86. data/lib/hecks/bluebook/meta_validator/port_judge.rb +1 -0
  87. data/lib/hecks/bluebook/meta_validator/readings.rb +157 -7
  88. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +24 -4
  89. data/lib/hecks/bluebook/meta_validator/shapes.rb +141 -0
  90. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +149 -23
  91. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +5 -4
  92. data/lib/hecks/bluebook/meta_validator/world_judge.rb +1 -0
  93. data/lib/hecks/bluebook/meta_validator.rb +180 -84
  94. data/lib/hecks/bluebook/model_check.rb +268 -24
  95. data/lib/hecks/bluebook/pattern_subset.rb +23 -1
  96. data/lib/hecks/bluebook/process_manager.rb +13 -0
  97. data/lib/hecks/bluebook/project_discovery.rb +5 -0
  98. data/lib/hecks/bluebook/project_loader.rb +40 -0
  99. data/lib/hecks/bluebook/project_register.rb +44 -0
  100. data/lib/hecks/bluebook/query.rb +27 -0
  101. data/lib/hecks/bluebook/read_model.rb +21 -1
  102. data/lib/hecks/bluebook/reference.rb +21 -8
  103. data/lib/hecks/bluebook/smoke_test.rb +29 -6
  104. data/lib/hecks/bluebook/synthesizer.rb +34 -0
  105. data/lib/hecks/bluebook/translation.rb +30 -1
  106. data/lib/hecks/bluebook/value_object.rb +23 -5
  107. data/lib/hecks/bluebook.rb +3 -4
  108. data/lib/hecks/codemod.rb +107 -20
  109. data/lib/hecks/construct.rb +15 -1
  110. data/lib/hecks/corpus.rb +146 -25
  111. data/lib/hecks/deploy/bluebook/deploy.hecksagon +19 -0
  112. data/lib/hecks/doc/reference.rb +185 -16
  113. data/lib/hecks/embryonaut_bluebook.rb +32 -9
  114. data/lib/hecks/facade/handle.rb +76 -3
  115. data/lib/hecks/facade/surface/aggregate_door.rb +8 -0
  116. data/lib/hecks/forms/field_shape.rb +3 -0
  117. data/lib/hecks/forms/page.rb +14 -0
  118. data/lib/hecks/forms/port_argument.rb +12 -0
  119. data/lib/hecks/forms/query_form_renderer.rb +63 -0
  120. data/lib/hecks/forms/record_renderer.rb +58 -0
  121. data/lib/hecks/forms/record_table.rb +27 -0
  122. data/lib/hecks/forms/reference_options.rb +24 -0
  123. data/lib/hecks/forms/value_object_shape.rb +10 -0
  124. data/lib/hecks/fqn.rb +58 -0
  125. data/lib/hecks/framework/bluebook/privacy.bluebook +155 -0
  126. data/lib/hecks/framework/oidc.json +15 -0
  127. data/lib/hecks/framework.rb +43 -20
  128. data/lib/hecks/freezer.rb +17 -1
  129. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +159 -24
  130. data/lib/hecks/fuzzing/combination_miner.rb +59 -0
  131. data/lib/hecks/fuzzing/concurrent_dispatch.rb +109 -8
  132. data/lib/hecks/fuzzing/coverage_campaign.rb +56 -13
  133. data/lib/hecks/fuzzing/differential.rb +34 -0
  134. data/lib/hecks/fuzzing/domain_generator.rb +188 -11
  135. data/lib/hecks/fuzzing/era_boundary.rb +45 -15
  136. data/lib/hecks/fuzzing/form_census.rb +86 -0
  137. data/lib/hecks/fuzzing/generated_domain_check.rb +76 -0
  138. data/lib/hecks/fuzzing/invalid_value_generator.rb +39 -0
  139. data/lib/hecks/fuzzing/isolated_boot.rb +79 -22
  140. data/lib/hecks/fuzzing/nondeterministic.rb +13 -1
  141. data/lib/hecks/fuzzing/persistence_parity.rb +95 -3
  142. data/lib/hecks/fuzzing/properties/corrections.rb +25 -0
  143. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +158 -14
  144. data/lib/hecks/fuzzing/properties/guards.rb +44 -0
  145. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +48 -0
  146. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +18 -0
  147. data/lib/hecks/fuzzing/properties/outbox.rb +49 -11
  148. data/lib/hecks/fuzzing/properties/querying.rb +68 -14
  149. data/lib/hecks/fuzzing/properties.rb +24 -15
  150. data/lib/hecks/fuzzing/qa_settings.rb +12 -0
  151. data/lib/hecks/fuzzing/replay.rb +137 -29
  152. data/lib/hecks/fuzzing/rotation_priority.rb +41 -21
  153. data/lib/hecks/fuzzing/rust_gap_manifest.rb +46 -20
  154. data/lib/hecks/fuzzing/self_consistency.rb +189 -40
  155. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +12 -6
  156. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +1 -1
  157. data/lib/hecks/fuzzing/sequence_generator.rb +47 -0
  158. data/lib/hecks/fuzzing/shrinker.rb +114 -2
  159. data/lib/hecks/fuzzing/structural_skips.rb +18 -4
  160. data/lib/hecks/fuzzing/sweep_depth.rb +8 -0
  161. data/lib/hecks/fuzzing/target_capabilities.rb +61 -14
  162. data/lib/hecks/fuzzing/value_generator.rb +98 -10
  163. data/lib/hecks/grammar/evolve.rb +178 -2
  164. data/lib/hecks/grammar.rb +46 -0
  165. data/lib/hecks/ir.rb +38 -7
  166. data/lib/hecks/language/hecksagon/hecksagon.bluebook +11 -0
  167. data/lib/hecks/literal.rb +32 -0
  168. data/lib/hecks/naming.rb +88 -7
  169. data/lib/hecks/ports/access_control.rb +5 -10
  170. data/lib/hecks/ports/authorization.rb +3 -6
  171. data/lib/hecks/ports/identity_assignment.rb +1 -2
  172. data/lib/hecks/ports/identity_resolution.rb +1 -2
  173. data/lib/hecks/ports/key_vault.port +6 -0
  174. data/lib/hecks/ports/key_vault.rb +58 -0
  175. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +6 -0
  176. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +38 -2
  177. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +6 -0
  178. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +40 -0
  179. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +92 -1
  180. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +15 -0
  181. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +8 -0
  182. data/lib/hecks/ports/query/in_memory.rb +39 -6
  183. data/lib/hecks/ports/query/ordering.rb +15 -0
  184. data/lib/hecks/ports.rb +1 -0
  185. data/lib/hecks/projections/bootstrap_table.rb +43 -8
  186. data/lib/hecks/projections/diagrams.rb +243 -7
  187. data/lib/hecks/projections/glossary/html.rb +88 -0
  188. data/lib/hecks/projections/glossary/markdown.rb +47 -0
  189. data/lib/hecks/projections/glossary/mermaid.rb +48 -0
  190. data/lib/hecks/projections/glossary/sentences.rb +105 -0
  191. data/lib/hecks/projections/glossary.rb +161 -19
  192. data/lib/hecks/projections/model/deviations.rb +44 -0
  193. data/lib/hecks/projections/model.rb +51 -1
  194. data/lib/hecks/projections/oidc.rb +40 -3
  195. data/lib/hecks/projections/parser_table.rb +49 -8
  196. data/lib/hecks/projections/reference.rb +12 -0
  197. data/lib/hecks/projections/rust_vocabulary.rb +219 -16
  198. data/lib/hecks/projections/shape.rb +8 -0
  199. data/lib/hecks/projections/statements.rb +63 -16
  200. data/lib/hecks/projections/vocabulary.rb +17 -0
  201. data/lib/hecks/projector/cli_projector.rb +218 -10
  202. data/lib/hecks/projector/docs_projector.rb +145 -19
  203. data/lib/hecks/projector/exporter.rb +65 -11
  204. data/lib/hecks/projector/ir_projector.rb +6 -0
  205. data/lib/hecks/projector/narrate_projector.rb +136 -15
  206. data/lib/hecks/projector/target.rb +29 -5
  207. data/lib/hecks/projector.rb +74 -6
  208. data/lib/hecks/query_ir.rb +47 -0
  209. data/lib/hecks/query_specification/common/null_policy.rb +5 -3
  210. data/lib/hecks/rendering.rb +6 -0
  211. data/lib/hecks/router/namespace_installer.rb +13 -0
  212. data/lib/hecks/router.rb +55 -0
  213. data/lib/hecks/runtime/aggregate_lock.rb +9 -0
  214. data/lib/hecks/runtime/boot_gates.rb +18 -0
  215. data/lib/hecks/runtime/caller.rb +32 -0
  216. data/lib/hecks/runtime/capability_graph.rb +11 -0
  217. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +23 -21
  218. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +14 -15
  219. data/lib/hecks/runtime/command_interpreter.rb +42 -17
  220. data/lib/hecks/runtime/command_rules/admissibility.rb +165 -14
  221. data/lib/hecks/runtime/command_rules/arithmetic.rb +17 -5
  222. data/lib/hecks/runtime/command_rules/references.rb +118 -28
  223. data/lib/hecks/runtime/dependency_planning.rb +45 -0
  224. data/lib/hecks/runtime/dispatcher.rb +28 -50
  225. data/lib/hecks/runtime/entity_element.rb +161 -8
  226. data/lib/hecks/runtime/entity_interpreter.rb +44 -9
  227. data/lib/hecks/runtime/errors.rb +18 -4
  228. data/lib/hecks/runtime/event.rb +10 -5
  229. data/lib/hecks/runtime/identity.rb +71 -3
  230. data/lib/hecks/runtime/instance.rb +67 -7
  231. data/lib/hecks/runtime/interpreting.rb +13 -5
  232. data/lib/hecks/runtime/invocation.rb +118 -36
  233. data/lib/hecks/runtime/loader.rb +94 -8
  234. data/lib/hecks/runtime/outbox.rb +145 -7
  235. data/lib/hecks/runtime/policy_interpreter.rb +22 -9
  236. data/lib/hecks/runtime/port_operation_interpreter.rb +20 -0
  237. data/lib/hecks/runtime/query_interpreter.rb +40 -12
  238. data/lib/hecks/runtime/reaction_invocation.rb +53 -8
  239. data/lib/hecks/runtime/read_model_interpreter.rb +23 -7
  240. data/lib/hecks/runtime/rebuild_sweep.rb +28 -0
  241. data/lib/hecks/runtime/reference_hop.rb +42 -0
  242. data/lib/hecks/runtime/refusal_wording.rb +50 -0
  243. data/lib/hecks/runtime/registry/saga_persistence.rb +11 -0
  244. data/lib/hecks/runtime/registry/verification.rb +119 -4
  245. data/lib/hecks/runtime/registry.rb +157 -4
  246. data/lib/hecks/runtime/remote_dispatcher.rb +92 -6
  247. data/lib/hecks/runtime/routing.rb +27 -2
  248. data/lib/hecks/runtime/saga_interpreter/correlation.rb +10 -12
  249. data/lib/hecks/runtime/saga_interpreter.rb +27 -13
  250. data/lib/hecks/runtime/tenant_check.rb +26 -6
  251. data/lib/hecks/runtime/tenant_scope.rb +18 -0
  252. data/lib/hecks/runtime/value/coercion.rb +255 -33
  253. data/lib/hecks/runtime/value/entity_list_coercion.rb +102 -30
  254. data/lib/hecks/runtime/value.rb +50 -0
  255. data/lib/hecks/runtime.rb +32 -0
  256. data/lib/hecks/storehouse.rb +305 -9
  257. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +130 -0
  258. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +32 -0
  259. data/lib/hecks/version.rb +1 -1
  260. data/lib/hecks.rb +79 -1
  261. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -0
  262. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +12 -2
  263. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +29 -5
  264. metadata +11 -5
  265. data/lib/hecks/codemod/legacy_dispatch_args.rb +0 -299
  266. data/lib/hecks/codemod/legacy_dispatch_recorder.rb +0 -186
  267. data/lib/hecks/deprecation.rb +0 -95
@@ -51,6 +51,21 @@ module Hecks
51
51
  # The wire carries the name, not the members. A reader resolves it
52
52
  # against the IR it holds, so the members are declared once and
53
53
  # copied nowhere — which is the same reason `admits` exists at all.
54
+ #
55
+ # @param name [Symbol, String] the attribute's name
56
+ # @param type [Module, Bluebook::Reference, String, Symbol] the bare constant naming
57
+ # a primitive or a value object, a `reference_to`-built Reference, or already-spelled
58
+ # text
59
+ # @param list [Boolean] whether this attribute holds a list of values rather than one
60
+ # @param default [Object, nil] the value a new record starts with when none is given
61
+ # @param optional [Boolean] whether a command may omit this attribute
62
+ # @param pattern [String, nil] a regex source the value must match; shared-engine
63
+ # subset only (`PatternSubset`)
64
+ # @param admits [String, nil] an already-declared closed set's aggregate-qualified
65
+ # name, such as `"Vocabulary::QueryComparator"`, that the value must belong to
66
+ # @param relationship [Symbol, nil] the DSL word that minted this attribute
67
+ # (`:reference_to`, `:has_many`, `:has_one` or `:belongs_to`), or `nil` for an
68
+ # ordinary attribute
54
69
  def initialize(name:, type:, list: false, default: nil, optional: false, pattern: nil,
55
70
  admits: nil, relationship: nil)
56
71
  @name = name.to_sym
@@ -77,7 +92,7 @@ module Hecks
77
92
  # built against somebody else's aggregate — silently, with no refusal —
78
93
  # and the attribute stops meaning what the file plainly says.
79
94
  #
80
- # DEMODULISED, so both paths spell it the same: `:Target` and
95
+ # Demodulised, so both paths spell it the same: `:Target` and
81
96
  # `QualityControl::Target` are both "Target". A plain class stays itself —
82
97
  # `String` demodulises to "String" — so the ordinary case is untouched.
83
98
  # This does not undo the constant leak; it makes the leak unable to change
@@ -91,8 +106,7 @@ module Hecks
91
106
  private :spell
92
107
 
93
108
  # Held because a declared vocabulary pins it — spec/vocabulary_conformance
94
- # holds `Primitive`'s members to this list. The `primitive?` predicate that
95
- # used to read it had no caller anywhere and is gone.
109
+ # holds `Primitive`'s members to this list.
96
110
  PRIMITIVES = Hecks::Vocabulary.fetch("Primitive")
97
111
 
98
112
  # `type` is spelled, never handed over. A Reference renders as
@@ -26,11 +26,17 @@ module Hecks
26
26
 
27
27
  # An aggregate is a member of its chapter's namespace — "Pizzas::Pizza" —
28
28
  # where everything else is declared on its owner and joins with ".".
29
+ #
30
+ # @return [String] the literal string `"::"`
29
31
  def hecks_separator = "::"
30
32
 
31
33
  # The hook the generated constructor calls once every declared
32
34
  # field is assigned. Nothing here is derivable from the
33
35
  # declaration, which is exactly why it is not generated.
36
+ #
37
+ # @return [Bluebook::Aggregate] self, once identity is derived, every
38
+ # declaration is indexed and its commands, value objects, entities and
39
+ # queries are stamped as owned by it
34
40
  def settle
35
41
  derive_identity
36
42
  index_declarations
@@ -40,6 +46,10 @@ module Hecks
40
46
  self
41
47
  end
42
48
 
49
+ # Builds every by-name finder index `settle` needs — attributes, value
50
+ # objects, commands, queries, ports and projected fields.
51
+ #
52
+ # @return [void]
43
53
  def index_declarations
44
54
  index_attributes(@attributes)
45
55
  @value_objects_by_name = index_by_hecks_name(@value_objects)
@@ -53,6 +63,11 @@ module Hecks
53
63
  @projected_fields_by_name = @projected_fields.to_h { |field| [field.name, field] }
54
64
  end
55
65
 
66
+ # Finds a declared `projects` field by its declared name.
67
+ #
68
+ # @param named [String, Symbol] the projected field's declared name
69
+ # @return [Bluebook::ProjectedField, nil] the field named `named`, or
70
+ # `nil` if none is declared under that name
56
71
  def projected_field(named) = @projected_fields_by_name[named.to_sym]
57
72
 
58
73
  # A value object is a class now, so `name` is Ruby's answer (the constant
@@ -60,7 +75,17 @@ module Hecks
60
75
  # out — once an attribute's type is the class there is nothing to find —
61
76
  # but every consumer still asks by type string, so it stays until they
62
77
  # stop.
78
+ #
79
+ # @param named [String, Symbol] the value object's declared name
80
+ # @return [Class, nil] the value object class (a `Bluebook::ValueObject`
81
+ # subclass) named `named`, or `nil` if none is declared under that name
63
82
  def value_object(named) = @value_objects_by_name[named.to_s]
83
+
84
+ # Finds a port attached to this aggregate by its declared name.
85
+ #
86
+ # @param named [String, Symbol] the port's declared name
87
+ # @return [Bluebook::DomainPort, nil] the port named `named`, or `nil` if
88
+ # none is attached under that name
64
89
  def port(named) = @ports_by_name[named.to_s]
65
90
 
66
91
  # A port is declared in the hecksagon, not the bluebook — the
@@ -71,11 +96,18 @@ module Hecks
71
96
  # calls it once per `port` declaration, having already stamped each
72
97
  # operation's reference attributes with `declared_in = self`, since
73
98
  # nothing upstream of a hecksagon load does that for it.
99
+ #
100
+ # @param port [Bluebook::DomainPort] the aggregate-scoped port to attach
101
+ # @return [void]
74
102
  def add_port(port)
75
103
  @ports << port
76
104
  @ports_by_name[port.name] = port
77
105
  end
78
106
 
107
+ # Names the table, file or key persistence adapters store this aggregate
108
+ # under.
109
+ #
110
+ # @return [String] the aggregate's name, snake-cased
79
111
  def storage_name = Naming.snake(@name)
80
112
  end
81
113
  end
@@ -6,8 +6,19 @@ module Hecks
6
6
  # in `aggregate.bluebook`'s own `Field`. These are the questions
7
7
  # readers ask about that shape, which no declaration states.
8
8
  module Attribute
9
+ # Says whether this attribute was declared `list`.
10
+ #
11
+ # @return [Boolean] whether this attribute holds a list of values rather than one
9
12
  def list? = @list
13
+
14
+ # Says whether this attribute holds a single value rather than a list.
15
+ #
16
+ # @return [Boolean] whether this attribute holds a single value rather than a list
10
17
  def scalar? = !@list
18
+
19
+ # Says whether this attribute's type is another aggregate reached via `reference_to`.
20
+ #
21
+ # @return [Boolean] whether this attribute's type is a `reference_to` another aggregate
11
22
  def reference? = @type.is_a?(Reference)
12
23
 
13
24
  # May this fact be left out?
@@ -20,6 +31,8 @@ module Hecks
20
31
  # Only a command enforces this. An aggregate's own attributes are filled
21
32
  # by the commands that set them, and a value object's by its
22
33
  # constructor ; neither is a payload anyone hands in.
34
+ #
35
+ # @return [Boolean] whether a command may omit this attribute from its payload
23
36
  def optional? = @optional
24
37
  end
25
38
  end
@@ -16,6 +16,9 @@ module Hecks
16
16
  # means), the ports table — which a `.hecksagon` fills later, so
17
17
  # the bluebook cannot declare it — and stamping its own children,
18
18
  # the same act an Aggregate performs one level down.
19
+ #
20
+ # @return [Bluebook::Chapter] self, once root marking, the ports table and ownership
21
+ # stamping are all set up
19
22
  def settle
20
23
  @hecks_root = true
21
24
  @ports = []
@@ -24,23 +27,54 @@ module Hecks
24
27
  self
25
28
  end
26
29
 
30
+ # Finds a declared aggregate by name.
31
+ #
32
+ # @param named [String, Symbol] the aggregate's declared name
33
+ # @return [Bluebook::Aggregate, nil] the aggregate, or `nil` if none is declared
34
+ # by that name
27
35
  def aggregate(named) = @aggregates.find { |a| a.name == named.to_s }
36
+
37
+ # Finds a declared read model by its own name or by the query name it answers.
38
+ #
39
+ # @param named [String, Symbol] the read model's declared name, or its query name
40
+ # @return [Bluebook::ReadModel, nil] the read model, or `nil` if none matches
28
41
  def read_model(named) = @read_models.find { |model| model.name == named.to_s || model.query_name == named.to_s }
42
+
43
+ # Finds a port declared at this chapter's root by name.
44
+ #
45
+ # @param named [String, Symbol] the port's declared name
46
+ # @return [Bluebook::DomainPort, nil] the port, or `nil` if none is declared
47
+ # by that name
29
48
  def port(named) = @ports_by_name[named.to_s]
30
49
 
31
50
  # What this chapter declared it provides — `{ key => local verb }`
32
51
  # for one capability, or nil when it declares none. Read by
33
- # everything that used to recognise the Governance chapter by its
34
- # name (`Registry#authorization_provider_for`).
52
+ # everything that resolves a capability's provider from what a
53
+ # chapter declares, rather than from the chapter's own name
54
+ # (`Registry#authorization_provider_for`).
55
+ #
56
+ # @param capability [String, Symbol] the capability's name, such as
57
+ # `Bluebook::Capabilities::AUTHORIZATION`
58
+ # @return [Hash{Symbol => String}, nil] each declared key mapped to its local verb,
59
+ # or `nil` if this chapter declares no `provides` row for that capability
35
60
  def provision(capability)
36
61
  rows = @provides.select { |row| row.capability == capability.to_s }
37
62
  rows.empty? ? nil : rows.to_h { |row| [row.key.to_sym, row.verb] }
38
63
  end
39
64
 
65
+ # Says whether this chapter declares that it provides a capability.
66
+ #
67
+ # @param capability [String, Symbol] the capability's name
68
+ # @return [Boolean] whether this chapter declares a `provides` row for that capability
40
69
  def provides?(capability) = !provision(capability).nil?
41
70
 
42
71
  # The declared verb for `key`, qualified with this chapter's own
43
72
  # name — the spelling `Dispatcher#dispatch`/`#query` take.
73
+ #
74
+ # @param capability [String, Symbol] the capability's name
75
+ # @param key [String, Symbol] the provided key to resolve
76
+ # @return [String, nil] the verb qualified as `"ChapterName::verb"`, or `nil` if this
77
+ # chapter provides no such capability or key
44
78
  def provided_verb(capability, key)
45
79
  local = provision(capability)&.fetch(key.to_sym, nil)
46
80
  local && "#{name}::#{local}"
@@ -49,11 +83,27 @@ module Hecks
49
83
  # A port is declared in the hecksagon, not the bluebook — so it
50
84
  # attaches after the chapter already exists, the same way an
51
85
  # aggregate's own ports do.
86
+ #
87
+ # @param port [Bluebook::DomainPort] the operations-shaped port to attach
88
+ # @return [void]
52
89
  def add_port(port)
53
90
  @ports << port
54
91
  @ports_by_name[port.name] = port
55
92
  end
56
93
 
94
+ # A TRANSLATED REACTION IS ALSO DECLARED IN THE HECKSAGON —
95
+ # `HecksagonBuilder#translates` builds an ordinary `Policy` (same
96
+ # shape, same `PolicyInterpreter` runtime, as one written inside a
97
+ # `policy` block in this chapter's own `.bluebook`) and attaches it
98
+ # here, after the chapter already exists, the same reasoning
99
+ # `add_port` gives one method up. Kept out of the bluebook itself
100
+ # on purpose: which FOREIGN domain's event this chapter reacts to
101
+ # is a wiring/context-mapping decision, not a fact the domain
102
+ # states about its own model.
103
+ def add_policy(policy)
104
+ @policies << policy
105
+ end
106
+
57
107
  # Every dispatchable name this chapter answers to, spelled exactly
58
108
  # as Dispatcher#dispatch takes it. Derived from the aggregates,
59
109
  # never declared — which is why Projections::OIDC can hold its own
@@ -68,6 +118,9 @@ module Hecks
68
118
  # already proved out for the meta-domain's own grammar (S17, ADR
69
119
  # 0026) — entities nest arbitrarily deep (`Dispatch`, inside
70
120
  # `Handler`), so one flat level isn't enough.
121
+ #
122
+ # @return [Array<String>] every command verb reachable on this chapter, spelled
123
+ # `"Domain::Aggregate.command"` or, nested, `"Domain::Aggregate.Entity.command"`
71
124
  def verbs
72
125
  @aggregates.flat_map { |agg| aggregate_verbs(agg) }
73
126
  end
@@ -10,6 +10,9 @@ module Hecks
10
10
 
11
11
  # Indexed once — attributes are final once absorbed, and every
12
12
  # dispatch asks this finder by name.
13
+ #
14
+ # @return [Class] self — the command class, once its attributes are
15
+ # indexed
13
16
  def settle
14
17
  index_attributes(@attributes)
15
18
  self
@@ -26,6 +29,11 @@ module Hecks
26
29
  #
27
30
  # On an aggregate, a creating command acts on no existing root, so nil is
28
31
  # the truth: there is nothing there yet.
32
+ #
33
+ # @return [Class, Bluebook::Aggregate, nil] the entity class (a
34
+ # `Bluebook::Entity` subclass) this verb acts on when declared on an
35
+ # entity, the aggregate instance when declared on an aggregate and
36
+ # non-creating, or `nil` for a creating command
29
37
  def acts_on
30
38
  # Fully qualified, and it has to be: inside `module Behaviour`
31
39
  # the bare name `Entity` resolves to Behaviour::Entity — this
@@ -37,6 +45,10 @@ module Hecks
37
45
  creates? ? nil : hecks_owner
38
46
  end
39
47
 
48
+ # Whether this command creates a new root rather than acting on one.
49
+ #
50
+ # @return [Boolean] whether this command creates a new root — true when
51
+ # it declares no `reference_to` back to its own owner
40
52
  def creates? = @references.nil?
41
53
 
42
54
  # Every reason this verb can refuse on a rule — the descriptions of
@@ -47,6 +59,9 @@ module Hecks
47
59
  # the language wrote" reads this rather than re-deriving the two
48
60
  # collections; `compact` because a rule's description is optional
49
61
  # (behavior.bluebook's Rule) and an unnamed one quotes nothing.
62
+ #
63
+ # @return [Array<String>] every named given's and ensure's own
64
+ # description text, skipping unnamed rules
50
65
  def guard_descriptions = (@givens + @ensures).map(&:description).compact
51
66
 
52
67
  # The argument name that addresses an instance of `aggregate_name`
@@ -84,6 +99,11 @@ module Hecks
84
99
  # aggregate at all. A caller minting a fan-out dispatch is
85
100
  # expected to treat `nil` as "this command cannot be addressed by
86
101
  # a row of this aggregate," not to fall back on a guess.
102
+ #
103
+ # @param aggregate_name [String, Symbol] the aggregate a fan-out dispatch is
104
+ # addressing an instance of
105
+ # @return [String, nil] the argument name that addresses it, or nil if this command
106
+ # cannot be addressed by a row of that aggregate
87
107
  def addressing_key_for(aggregate_name)
88
108
  return Naming.reference_key(aggregate_name) if references.to_s == aggregate_name.to_s
89
109
 
@@ -96,11 +116,21 @@ module Hecks
96
116
  # a Struct, so these are instance methods.
97
117
  module Mutation
98
118
  # An append binds several fields at once, each from either a command
99
- # argument (a Symbol) or a literal. It used to spell the Symbol bare
100
- # and inspect the rest, which is the opposite of what a where-clause
101
- # did with the same two kinds — see Hecks::Literal.
119
+ # argument (a Symbol) or a literal. Spelled through `Literal.render`,
120
+ # the same self-describing form a where-clause's own value already
121
+ # uses, rather than a bare Symbol paired with an inspected literal —
122
+ # see Hecks::Literal.
123
+ #
124
+ # @return [Hash{Symbol => String}] the append's field bindings, each
125
+ # value rendered through `Literal.render`
102
126
  def appended_fields = source.transform_values { |value| Literal.render(value) }
103
127
 
128
+ # Classifies this mutation's source for the wire, the counterpart
129
+ # `Assembly::Marks#classified` reads back.
130
+ #
131
+ # @return [Hash{Symbol => Object}] `{kind: "argument", name:}` for a
132
+ # command argument, `{kind: "state", name:}` for a state
133
+ # self-reference, or `{kind: "literal", value:}` for a literal value
104
134
  def classified_source
105
135
  if source.is_a?(Symbol)
106
136
  { kind: "argument", name: source.to_s }
@@ -9,6 +9,8 @@ module Hecks
9
9
 
10
10
  # An operation declares no reference of its own — the owner is
11
11
  # what it acts for, and `identity_attribute` is how that is found.
12
+ #
13
+ # @return [nil] always `nil`
12
14
  def references = nil
13
15
 
14
16
  # Never a creating command — a port operation always acts on an
@@ -21,8 +23,16 @@ module Hecks
21
23
  # `target.command.creates?` on a port operation the same way it
22
24
  # already does on an ordinary command, and correctly lift a
23
25
  # same-aggregate policy's own Event.id as the operation's receiver.
26
+ #
27
+ # @return [Boolean] always `false`
24
28
  def creates? = false
25
29
 
30
+ # Finds the reference-typed attribute through which this operation addresses
31
+ # its owning aggregate.
32
+ #
33
+ # @param owner_name [String, Symbol] the owning aggregate's name
34
+ # @return [Bluebook::Attribute, nil] the reference-typed attribute targeting
35
+ # `owner_name`, or `nil` if this operation declares none
26
36
  def identity_attribute(owner_name)
27
37
  @attributes.find { |attribute| attribute.reference? && attribute.type.target_name == owner_name.to_s }
28
38
  end
@@ -37,11 +47,21 @@ module Hecks
37
47
  # reason `Command#addressing_key_for` is: `ReactionInvocation
38
48
  # #aggregate_aliases` calls it on whatever `target.command` holds,
39
49
  # a `PortOperation` now included since a policy can trigger one.
50
+ #
51
+ # @param aggregate_name [String, Symbol] the name of the aggregate a row of which
52
+ # would address this operation
53
+ # @return [Symbol, nil] the reference-typed attribute's name a caller passes to
54
+ # address that row, or `nil` if this operation cannot be addressed by one
40
55
  def addressing_key_for(aggregate_name) = identity_attribute(aggregate_name)&.name
41
56
  end
42
57
 
43
58
  # **What a port does** — one finder over its declared operations.
44
59
  module DomainPort
60
+ # Finds a declared operation by name.
61
+ #
62
+ # @param named [String, Symbol] the operation's declared name
63
+ # @return [Bluebook::PortOperation, nil] the operation, or `nil` if none is
64
+ # declared by that name
45
65
  def operation(named) = @operations.find { |op| op.hecks_name == named.to_s }
46
66
  end
47
67
  end
@@ -17,12 +17,19 @@ module Hecks
17
17
  include Indexed
18
18
  include Owns
19
19
 
20
+ # The hook `absorb` calls once every declared field is assigned.
21
+ #
22
+ # @return [Class] this entity's own class (a `Bluebook::Entity` subclass), self,
23
+ # once identity and indexes are derived
20
24
  def settle
21
25
  derive_identity
22
26
  index_declarations
23
27
  self
24
28
  end
25
29
 
30
+ # Builds every by-name index this entity answers finders through.
31
+ #
32
+ # @return [void]
26
33
  def index_declarations
27
34
  index_attributes(@attributes)
28
35
  @commands_by_name = index_by_hecks_name(@commands)
@@ -43,6 +50,9 @@ module Hecks
43
50
  # `.entities` is walked for its direct children. Entity's own
44
51
  # header comment already promises it stays "structurally
45
52
  # interchangeable with an aggregate" for exactly this reason.
53
+ #
54
+ # @return [Array<Class>] this entity's own nested entities (each a `Bluebook::Entity`
55
+ # subclass), or `[]` if it declares none
46
56
  def entities = @entities || []
47
57
 
48
58
  # A piece owns the verbs declared on it, so they can state an
@@ -52,6 +62,8 @@ module Hecks
52
62
  # subclass that will own them exists. `@entities` too now
53
63
  # (S17, ADR 0026) — a nested entity states its own owner chain
54
64
  # exactly the way a nested command does.
65
+ #
66
+ # @return [void]
55
67
  def stamp_children = stamp(@commands, @queries, @entities)
56
68
  end
57
69
  end
@@ -8,11 +8,25 @@ module Hecks
8
8
  # `HecksagonBuilder#method_missing`). Checked directly rather than
9
9
  # via `aggregate_name`/`Naming.demodulise`, which would need its own
10
10
  # nil-handling for the default row.
11
+ #
12
+ # @param aggregate_name [String, Symbol] the aggregate to find a bind for
13
+ # @param verb [String, Symbol] the port verb name to find a bind for
14
+ # @return [Bluebook::Bind, nil] the aggregate-specific bind when one is
15
+ # declared, the domain-level default bind for `verb` otherwise, or
16
+ # `nil` when neither exists
11
17
  def bind_for(aggregate_name, verb)
12
18
  @binds.find { |b| b.aggregate_name == aggregate_name.to_s && b.verb.to_s == verb.to_s } ||
13
19
  @binds.find { |b| b.aggregate.nil? && b.verb.to_s == verb.to_s }
14
20
  end
15
21
 
22
+ # Every bind for `verb`, aggregate-specific ones winning as a group over
23
+ # the domain-level default.
24
+ #
25
+ # @param aggregate_name [String, Symbol] the aggregate to find binds for
26
+ # @param verb [String, Symbol] the port verb name to find binds for
27
+ # @return [Array<Bluebook::Bind>] every aggregate-specific bind for
28
+ # `verb` when any are declared, otherwise every domain-level default
29
+ # bind for `verb`
16
30
  def binds_for(aggregate_name, verb)
17
31
  specific = @binds.select { |b| b.aggregate_name == aggregate_name.to_s && b.verb.to_s == verb.to_s }
18
32
  return specific if specific.any?
@@ -24,6 +38,11 @@ module Hecks
24
38
  # **What a world does** — settings lookup, with the adapter-specific
25
39
  # entry falling back to the verb's own.
26
40
  module World
41
+ # Finds the generic settings declared for a port verb.
42
+ #
43
+ # @param verb [String, Symbol] the port verb name, such as `"persistence"`
44
+ # @return [Hash{Symbol => Object}] the settings declared for `verb`, or
45
+ # `{}` when none are declared
27
46
  def for_verb(verb) = @settings.fetch(verb.to_s, {})
28
47
 
29
48
  # The generic `verb` entry (`persisted_by("Heki") do dir :default
@@ -37,6 +56,13 @@ module Hecks
37
56
  # names Heki, not Memory, so the fallback was never actually for
38
57
  # this bind. `{}` is exactly right when nothing was configured for
39
58
  # this adapter — Memory, which takes no values at all.
59
+ #
60
+ # @param verb [String, Symbol] the port verb name to find settings for
61
+ # @param adapter [String, Symbol] the adapter name, such as `"Heki"`, to
62
+ # find settings for
63
+ # @return [Hash{Symbol => Object}] the adapter-qualified settings when
64
+ # declared, the generic verb settings when they name this same
65
+ # adapter, or `{}` otherwise
40
66
  def for_binding(verb, adapter)
41
67
  qualified = @settings["#{verb}:#{adapter.to_s.downcase}"]
42
68
  return qualified if qualified
@@ -7,14 +7,35 @@ module Hecks
7
7
  # declared transition expands into the several rows the emission
8
8
  # carries when `from` names more than one source state.
9
9
  module Lifecycle
10
+ # Lists every state this lifecycle can be in.
11
+ #
12
+ # @return [Array<String>] every state this lifecycle can be in — the
13
+ # default plus each declared transition's own target, deduplicated
10
14
  def states
11
15
  ([default] + transitions.map { |_command, t| t.target }).uniq
12
16
  end
13
17
 
18
+ # Finds every transition a command declares.
19
+ #
20
+ # @param command [String, Symbol] the command name to find declared
21
+ # transitions for
22
+ # @return [Array<Bluebook::StateTransition>] every transition declared
23
+ # for `command`
14
24
  def transitions_for(command)
15
25
  transitions.select { |name, _| name == command.to_s }.map { |_, t| t }
16
26
  end
17
27
 
28
+ # Finds the state a command moves the record to.
29
+ #
30
+ # @param command [String, Symbol] the command name to find the target
31
+ # state for
32
+ # @param current_state [String, Symbol, nil] the record's current
33
+ # state, to pick the one declared transition that admits it; `nil`
34
+ # to skip that check and take the first declared transition
35
+ # @return [String, nil] the state `command` moves the record to, or
36
+ # `nil` when `command` declares no transition at all
37
+ # @raise [Runtime::WiringError] if `command` declares transitions but
38
+ # none of them admits `current_state`
18
39
  def target_for(command, current_state = nil)
19
40
  match_transition(command, current_state)&.target
20
41
  end
@@ -37,10 +58,10 @@ module Hecks
37
58
  return nil if matches.empty?
38
59
  return matches.first unless current_state
39
60
 
40
- # Not `|| matches.first` — that used to silently hand back an
61
+ # Not `|| matches.first` — that would silently hand back an
41
62
  # arbitrary declared transition for `command` whenever none of
42
63
  # them actually admitted `current_state`, picking a `target`
43
- # that command dispatch would in fact have refused (that
64
+ # that command dispatch would in fact refuse (that
44
65
  # refusal is `CommandRules::Admissibility#admissible_transition`'s
45
66
  # own job, which raises `LifecycleRefused` for exactly this
46
67
  # case rather than guessing — this module has no state subject
@@ -7,21 +7,37 @@ module Hecks
7
7
  # The bluebook's name for this construct, asked the same way of a class
8
8
  # that has crossed over and of an IR object that has not. Collapses into
9
9
  # Construct when this one crosses.
10
+ #
11
+ # @return [String] this policy's declared name
10
12
  def hecks_name = @name
11
13
 
14
+ # The domain-and-aggregate prefix of the triggering event's name.
15
+ #
16
+ # @return [String, nil] the part of `on_event` before its first `.`
17
+ # (the `"Domain::Aggregate"` prefix), or `nil` when `on_event`
18
+ # carries no dot
12
19
  def event_qualifier = Naming.qualifier(@on_event)
13
20
 
21
+ # The bare name of the triggering event, without its domain/aggregate
22
+ # prefix.
23
+ #
24
+ # @return [String] the part of `on_event` after its first `.` (the
25
+ # bare event name), or the whole string when it carries no dot
14
26
  def event_name = Naming.unqualified(@on_event)
15
27
 
16
28
  # Whether this policy fans out — `for_each` names a query, and a
17
29
  # non-empty one turns a single reaction into one dispatch per row
18
30
  # the query answers. Read by the interpreter that runs the fan-out
19
31
  # and by the property that checks it dispatched once per row.
32
+ #
33
+ # @return [Boolean] whether `for_each` names a query
20
34
  def fans_out? = !@for_each.to_s.empty?
21
35
 
22
36
  # Whether this policy is guarded — a non-empty `where` decides
23
37
  # whether the policy fires at all, read against the triggering
24
38
  # event's own payload.
39
+ #
40
+ # @return [Boolean] whether `where` is declared and non-empty
25
41
  def guarded? = !@where.to_s.empty?
26
42
 
27
43
  # The structured form of `where`, derived once — the same tree
@@ -29,6 +45,9 @@ module Hecks
29
45
  # here because a policy is consulted once per event, not once per
30
46
  # boot. Nil when there is no `where`, exactly as the wire carries
31
47
  # it.
48
+ #
49
+ # @return [Hash, nil] the JSON-shaped boolean AST for `where`, or
50
+ # `nil` when this policy is not guarded
32
51
  def where_ast
33
52
  if defined?(@where_ast)
34
53
  @where_ast
@@ -40,6 +59,9 @@ module Hecks
40
59
  # The rule-shaped reading of the guard, for `Evaluator.call_rule`
41
60
  # — a policy's `where` has no description (nothing refuses with
42
61
  # it; an unmet where is a silent skip).
62
+ #
63
+ # @return [Bluebook::Given] the guard as a rule, with no description
64
+ # and its `ast` already resolved via `where_ast`
43
65
  def where_rule = @where_rule ||= Given.new(description: nil, canonical: @where, ast: where_ast)
44
66
 
45
67
  # The fan-out query's route, split the way the runtime runs it:
@@ -48,6 +70,11 @@ module Hecks
48
70
  # names one ("Domain::Aggregate.query"). Deliberately independent
49
71
  # of `across`/`target_domain`, which name where `trigger` fires,
50
72
  # not where the fan-out's own query runs.
73
+ #
74
+ # @param default_domain [String, Symbol] the triggering event's own
75
+ # domain, used when `for_each` names no domain of its own
76
+ # @return [Array(String, String, String)] `[domain, aggregate_name,
77
+ # query_name]` the fan-out query resolves against
51
78
  def for_each_route(default_domain)
52
79
  path, query_name = @for_each.to_s.split(".", 2)
53
80
  domain, aggregate = path.to_s.include?("::") ? path.split("::", 2) : [default_domain.to_s, path]
@@ -55,15 +82,13 @@ module Hecks
55
82
  end
56
83
 
57
84
  # The argument name a fan-out dispatch mints each matched row's id
58
- # under used to be minted here, unconditionally, as `<aggregate>
59
- # _id` — a real bug: a target command declared on the very
60
- # aggregate it self-references (`Account.Freeze`) is addressed by
61
- # its own identity field's name, not a synthetic foreign key, and
62
- # every such dispatch refused. That question is not this policy's
63
- # to answer at all — it depends on the target command's own
64
- # declared shape, not on the aggregate name alone — so it now
65
- # lives on `Behaviour::Command#addressing_key_for`, asked of the
66
- # resolved target command by `PolicyInterpreter#addressing_key_for`.
85
+ # under is not minted here — it depends on the target command's own
86
+ # declared shape (a target command declared on the very aggregate it
87
+ # self-references, such as `Account.Freeze`, is addressed by its own
88
+ # identity field's name, not a synthetic foreign key), not on the
89
+ # aggregate name alone, so it lives on
90
+ # `Behaviour::Command#addressing_key_for`, asked of the resolved
91
+ # target command by `PolicyInterpreter#addressing_key_for`.
67
92
  end
68
93
  end
69
94
  end