hecks 1.4.0 → 1.5.1

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 (275) 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.bluebook +105 -0
  112. data/lib/hecks/deploy/bluebook/deploy.hecksagon +19 -0
  113. data/lib/hecks/deploy/oidc.json +5 -0
  114. data/lib/hecks/doc/reference.rb +185 -16
  115. data/lib/hecks/embryonaut_bluebook.rb +32 -9
  116. data/lib/hecks/facade/handle.rb +76 -3
  117. data/lib/hecks/facade/surface/aggregate_door.rb +8 -0
  118. data/lib/hecks/forms/field_shape.rb +3 -0
  119. data/lib/hecks/forms/page.rb +14 -0
  120. data/lib/hecks/forms/port_argument.rb +12 -0
  121. data/lib/hecks/forms/query_form_renderer.rb +63 -0
  122. data/lib/hecks/forms/record_renderer.rb +58 -0
  123. data/lib/hecks/forms/record_table.rb +27 -0
  124. data/lib/hecks/forms/reference_options.rb +24 -0
  125. data/lib/hecks/forms/value_object_shape.rb +10 -0
  126. data/lib/hecks/fqn.rb +58 -0
  127. data/lib/hecks/framework/bluebook/compliance.bluebook +221 -0
  128. data/lib/hecks/framework/bluebook/privacy.bluebook +155 -0
  129. data/lib/hecks/framework/oidc.json +15 -0
  130. data/lib/hecks/framework.rb +43 -20
  131. data/lib/hecks/freezer.rb +17 -1
  132. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +159 -24
  133. data/lib/hecks/fuzzing/combination_miner.rb +59 -0
  134. data/lib/hecks/fuzzing/concurrent_dispatch.rb +109 -8
  135. data/lib/hecks/fuzzing/coverage_campaign.rb +56 -13
  136. data/lib/hecks/fuzzing/differential.rb +34 -0
  137. data/lib/hecks/fuzzing/domain_generator.rb +188 -11
  138. data/lib/hecks/fuzzing/era_boundary.rb +45 -15
  139. data/lib/hecks/fuzzing/form_census.rb +86 -0
  140. data/lib/hecks/fuzzing/generated_domain_check.rb +76 -0
  141. data/lib/hecks/fuzzing/invalid_value_generator.rb +39 -0
  142. data/lib/hecks/fuzzing/isolated_boot.rb +79 -22
  143. data/lib/hecks/fuzzing/nondeterministic.rb +13 -1
  144. data/lib/hecks/fuzzing/persistence_parity.rb +95 -3
  145. data/lib/hecks/fuzzing/properties/corrections.rb +25 -0
  146. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +158 -14
  147. data/lib/hecks/fuzzing/properties/guards.rb +44 -0
  148. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +48 -0
  149. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +18 -0
  150. data/lib/hecks/fuzzing/properties/outbox.rb +49 -11
  151. data/lib/hecks/fuzzing/properties/querying.rb +68 -14
  152. data/lib/hecks/fuzzing/properties.rb +24 -15
  153. data/lib/hecks/fuzzing/qa_settings.rb +12 -0
  154. data/lib/hecks/fuzzing/replay.rb +137 -29
  155. data/lib/hecks/fuzzing/rotation_priority.rb +41 -21
  156. data/lib/hecks/fuzzing/rust_gap_manifest.rb +46 -20
  157. data/lib/hecks/fuzzing/self_consistency.rb +189 -40
  158. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +12 -6
  159. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +1 -1
  160. data/lib/hecks/fuzzing/sequence_generator.rb +47 -0
  161. data/lib/hecks/fuzzing/shrinker.rb +114 -2
  162. data/lib/hecks/fuzzing/structural_skips.rb +18 -4
  163. data/lib/hecks/fuzzing/sweep_depth.rb +8 -0
  164. data/lib/hecks/fuzzing/target_capabilities.rb +61 -14
  165. data/lib/hecks/fuzzing/value_generator.rb +98 -10
  166. data/lib/hecks/grammar/evolve.rb +178 -2
  167. data/lib/hecks/grammar.rb +46 -0
  168. data/lib/hecks/ir.rb +38 -7
  169. data/lib/hecks/language/hecksagon/hecksagon.bluebook +11 -0
  170. data/lib/hecks/literal.rb +32 -0
  171. data/lib/hecks/naming.rb +88 -7
  172. data/lib/hecks/ports/access_control.rb +5 -10
  173. data/lib/hecks/ports/authorization.rb +3 -6
  174. data/lib/hecks/ports/identity_assignment.rb +1 -2
  175. data/lib/hecks/ports/identity_resolution.rb +1 -2
  176. data/lib/hecks/ports/key_vault.port +6 -0
  177. data/lib/hecks/ports/key_vault.rb +85 -0
  178. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +6 -0
  179. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +38 -2
  180. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +6 -0
  181. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +40 -0
  182. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +92 -1
  183. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +15 -0
  184. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +8 -0
  185. data/lib/hecks/ports/query/in_memory.rb +39 -6
  186. data/lib/hecks/ports/query/ordering.rb +15 -0
  187. data/lib/hecks/ports.rb +1 -0
  188. data/lib/hecks/projections/bootstrap_table.rb +43 -8
  189. data/lib/hecks/projections/deploy/fargate.rb +666 -0
  190. data/lib/hecks/projections/deploy/lambda.rb +2423 -0
  191. data/lib/hecks/projections/deploy/shared.rb +624 -0
  192. data/lib/hecks/projections/diagrams.rb +243 -7
  193. data/lib/hecks/projections/glossary/html.rb +88 -0
  194. data/lib/hecks/projections/glossary/markdown.rb +47 -0
  195. data/lib/hecks/projections/glossary/mermaid.rb +48 -0
  196. data/lib/hecks/projections/glossary/sentences.rb +105 -0
  197. data/lib/hecks/projections/glossary.rb +161 -19
  198. data/lib/hecks/projections/model/deviations.rb +44 -0
  199. data/lib/hecks/projections/model.rb +51 -1
  200. data/lib/hecks/projections/oidc.rb +40 -3
  201. data/lib/hecks/projections/parser_table.rb +49 -8
  202. data/lib/hecks/projections/reference.rb +12 -0
  203. data/lib/hecks/projections/rust_vocabulary.rb +219 -16
  204. data/lib/hecks/projections/shape.rb +8 -0
  205. data/lib/hecks/projections/statements.rb +63 -16
  206. data/lib/hecks/projections/vocabulary.rb +17 -0
  207. data/lib/hecks/projections.rb +3 -0
  208. data/lib/hecks/projector/cli_projector.rb +218 -10
  209. data/lib/hecks/projector/docs_projector.rb +145 -19
  210. data/lib/hecks/projector/exporter.rb +65 -11
  211. data/lib/hecks/projector/ir_projector.rb +6 -0
  212. data/lib/hecks/projector/narrate_projector.rb +136 -15
  213. data/lib/hecks/projector/target.rb +47 -10
  214. data/lib/hecks/projector.rb +101 -19
  215. data/lib/hecks/query_ir.rb +47 -0
  216. data/lib/hecks/query_specification/common/null_policy.rb +5 -3
  217. data/lib/hecks/rendering.rb +6 -0
  218. data/lib/hecks/router/namespace_installer.rb +13 -0
  219. data/lib/hecks/router.rb +55 -0
  220. data/lib/hecks/runtime/aggregate_lock.rb +9 -0
  221. data/lib/hecks/runtime/boot_gates.rb +18 -0
  222. data/lib/hecks/runtime/caller.rb +32 -0
  223. data/lib/hecks/runtime/capability_graph.rb +11 -0
  224. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +23 -21
  225. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +14 -15
  226. data/lib/hecks/runtime/command_interpreter.rb +42 -17
  227. data/lib/hecks/runtime/command_rules/admissibility.rb +165 -14
  228. data/lib/hecks/runtime/command_rules/arithmetic.rb +17 -5
  229. data/lib/hecks/runtime/command_rules/references.rb +118 -28
  230. data/lib/hecks/runtime/dependency_planning.rb +45 -0
  231. data/lib/hecks/runtime/dispatcher.rb +28 -50
  232. data/lib/hecks/runtime/entity_element.rb +161 -8
  233. data/lib/hecks/runtime/entity_interpreter.rb +44 -9
  234. data/lib/hecks/runtime/errors.rb +18 -4
  235. data/lib/hecks/runtime/event.rb +10 -5
  236. data/lib/hecks/runtime/identity.rb +71 -3
  237. data/lib/hecks/runtime/instance.rb +67 -7
  238. data/lib/hecks/runtime/interpreting.rb +13 -5
  239. data/lib/hecks/runtime/invocation.rb +118 -36
  240. data/lib/hecks/runtime/loader.rb +94 -8
  241. data/lib/hecks/runtime/outbox.rb +145 -7
  242. data/lib/hecks/runtime/policy_interpreter.rb +22 -9
  243. data/lib/hecks/runtime/port_operation_interpreter.rb +20 -0
  244. data/lib/hecks/runtime/query_interpreter.rb +40 -12
  245. data/lib/hecks/runtime/reaction_invocation.rb +53 -8
  246. data/lib/hecks/runtime/read_model_interpreter.rb +23 -7
  247. data/lib/hecks/runtime/rebuild_sweep.rb +28 -0
  248. data/lib/hecks/runtime/reference_hop.rb +42 -0
  249. data/lib/hecks/runtime/refusal_wording.rb +50 -0
  250. data/lib/hecks/runtime/registry/saga_persistence.rb +11 -0
  251. data/lib/hecks/runtime/registry/verification.rb +119 -4
  252. data/lib/hecks/runtime/registry.rb +157 -4
  253. data/lib/hecks/runtime/remote_dispatcher.rb +92 -6
  254. data/lib/hecks/runtime/routing.rb +27 -2
  255. data/lib/hecks/runtime/saga_interpreter/correlation.rb +10 -12
  256. data/lib/hecks/runtime/saga_interpreter.rb +27 -13
  257. data/lib/hecks/runtime/tenant_check.rb +26 -6
  258. data/lib/hecks/runtime/tenant_scope.rb +18 -0
  259. data/lib/hecks/runtime/value/coercion.rb +255 -33
  260. data/lib/hecks/runtime/value/entity_list_coercion.rb +102 -30
  261. data/lib/hecks/runtime/value.rb +50 -0
  262. data/lib/hecks/runtime.rb +32 -0
  263. data/lib/hecks/storehouse.rb +305 -9
  264. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +130 -0
  265. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +32 -0
  266. data/lib/hecks/version.rb +1 -1
  267. data/lib/hecks.rb +79 -1
  268. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -0
  269. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +12 -2
  270. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +29 -5
  271. metadata +14 -5
  272. data/lib/hecks/codemod/legacy_dispatch_args.rb +0 -299
  273. data/lib/hecks/codemod/legacy_dispatch_recorder.rb +0 -186
  274. data/lib/hecks/deprecation.rb +0 -95
  275. data/lib/hecks/framework/bluebook/compliance.bluebook +0 -1
@@ -29,6 +29,19 @@ module Hecks
29
29
  UNSET = Object.new.freeze
30
30
  private_constant :UNSET
31
31
 
32
+ # @param name [String] the command's name, as written after `command`
33
+ # @param owner [String, nil] name of the aggregate or entity this command belongs to
34
+ # @param from [String, Symbol, Array<String, Symbol>, nil] the lifecycle state(s) this
35
+ # command guards from; nil admits from any state
36
+ # @param named_givens [Hash{String => Bluebook::Given}] the owner's own given pool, for
37
+ # bare `given` references to resolve against
38
+ # @param owner_attributes [Array<Bluebook::Attribute>] the owner's own attributes, for
39
+ # implicit `sets`/`state` resolution
40
+ # @param owner_constructs [Array<Bluebook::ValueObject, Bluebook::Entity>] the owner's
41
+ # own value objects and entities, for an `append:` element type to resolve against
42
+ # @param entity_shared_givens [Hash{String => Bluebook::Given}] a sibling piece's own
43
+ # entity-level given pool, for a piece-owned command's bare reference; empty for an
44
+ # aggregate-owned command
32
45
  def initialize(name, owner: nil, from: nil, named_givens: {}, owner_attributes: [], owner_constructs: [],
33
46
  entity_shared_givens: {})
34
47
  @name = name
@@ -63,7 +76,10 @@ module Hecks
63
76
  # exactly the failure mode `reference_to`'s own duplicate guard
64
77
  # (below) already exists to prevent for a command's root.
65
78
  #
66
- # Renamed from `role` — item #13's full metaprogrammed dispatch
79
+ # Sets the command's one responsibility role, refusing a second declaration.
80
+ #
81
+ # Answers the `role` word through the table's `calls:` column —
82
+ # item #13's full metaprogrammed dispatch
67
83
  # (slice 4). This is a uniqueness gate on prior state (`@role`
68
84
  # already set), not a pure function of the argument's own value —
69
85
  # a genuinely different shape than a plain fill, so it stays
@@ -71,6 +87,10 @@ module Hecks
71
87
  # was in slice 3. Bootstrap-reachable (every self-hosted command
72
88
  # declares a role), so also named in
73
89
  # `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`.
90
+ #
91
+ # @param value [String, Symbol] the role's name
92
+ # @return [Object] `value` as stored
93
+ # @raise [Bluebook::DSL::Malformed] if a role is already declared
74
94
  def role_impl(value)
75
95
  if @role
76
96
  raise Malformed,
@@ -82,23 +102,49 @@ module Hecks
82
102
  @role = value
83
103
  end
84
104
 
105
+ # Sets the human-readable description shown for this command.
106
+ #
107
+ # @param value [String] the description text
108
+ # @return [String] the description as stored
85
109
  def goal(value) = @goal = value
86
110
 
87
- # See AggregateBuilder#provenance's own comment — identical shape,
111
+ # Names where a concept adopted from a canonical source came from.
112
+ #
113
+ # See `AggregateBuilder#provenance_impl`'s own comment — identical shape,
88
114
  # one level down.
89
- # Renamed from `provenance` — item #13's full metaprogrammed
115
+ #
116
+ # Answers the `provenance` word through the table's `calls:`
117
+ # column — item #13's full metaprogrammed
90
118
  # dispatch (slice 4c). Bootstrap-reachable, in
91
- # GenericDispatch::BOOTSTRAP_CALLS_FALLBACK.
119
+ # `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`.
120
+ #
121
+ # @param from [Object] the canonical source, captured exactly as written
122
+ # @return [Object] `from` as stored
92
123
  def provenance_impl(from:) = @provenance = from
93
124
 
125
+ # Declares the aggregate this command acts on (with no `as:`), or a cross-reference to
126
+ # another aggregate (with `as:`).
127
+ #
94
128
  # `optional:` rides here as well as on a plain attribute : `as:` makes a
95
129
  # reference into a named argument, and a named argument is exactly the kind
96
130
  # of fact that may or may not be given. The meta-domain's Verb.Declare
97
131
  # points at the Entity a command belongs to — and most commands belong to no
98
132
  # entity at all.
99
- # Renamed from `reference_to` — item #13's full metaprogrammed
133
+ #
134
+ # Answers the `reference_to` word through the table's `calls:`
135
+ # column — item #13's full metaprogrammed
100
136
  # dispatch (slice 4b). Bootstrap-reachable, in
101
- # GenericDispatch::BOOTSTRAP_CALLS_FALLBACK.
137
+ # `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`.
138
+ #
139
+ # @param type [Module, Symbol, String] the referenced aggregate, written as a bare
140
+ # constant
141
+ # @param as [Symbol, nil] the attribute's name for a cross-reference; nil declares the
142
+ # root this command acts on instead, unless `type` is a different aggregate than the
143
+ # owner
144
+ # @param optional [Boolean] whether the reference may be absent
145
+ # @return [void]
146
+ # @raise [Bluebook::DSL::Malformed] if the command already acts on a root, or (for a
147
+ # cross-reference) `as` is already declared
102
148
  def reference_to_impl(type, as: nil, optional: false)
103
149
  demodulised = Naming.demodulise(type)
104
150
  # moved to the language: given "a command names what it acts on", on Verb.ActsOn
@@ -131,19 +177,32 @@ module Hecks
131
177
 
132
178
  public
133
179
 
180
+ # Declares a precondition this command requires, or references one the owning aggregate
181
+ # (or a sibling piece) already declared.
182
+ #
134
183
  # No block is a reference, not a fresh declaration (S10, ADR
135
184
  # 0025 — "a precondition shared across commands is declared
136
185
  # once. An aggregate declares it by name and commands
137
186
  # reference it"): the same word, the same shape
138
- # (`AggregateBuilder#given`, block required there), so naming a
187
+ # (`AggregateBuilder#given_impl`, block required there), so naming a
139
188
  # precondition back is spelled exactly like declaring one would
140
189
  # be, minus the block — one idea, one word, never a second
141
190
  # spelling ("requires"/"precondition") for "use the one already
142
191
  # named". Resolved against whatever the owning aggregate has
143
- # declared so far — see `AggregateBuilder#command`'s own
192
+ # declared so far — see `AggregateBuilder#command_impl`'s own
144
193
  # comment on why that means declaration order matters here.
145
- # Renamed from `given` — item #13's full metaprogrammed dispatch
146
- # (slice 4b), same reasoning as reference_to_impl above.
194
+ #
195
+ # Answers the `given` word through the table's `calls:` column —
196
+ # item #13's full metaprogrammed dispatch
197
+ # (slice 4b), same reasoning as `reference_to_impl` above.
198
+ #
199
+ # @param description [String] the rule's description; also the name the owning
200
+ # aggregate's rule is referenced by when no block is given
201
+ # @yield the predicate body; evaluated for its extracted source, never called directly
202
+ # @return [void]
203
+ # @raise [Bluebook::DSL::Malformed] if given a block whose source cannot be extracted, or
204
+ # given no block and the description names no precondition the owner (or a sibling
205
+ # piece under the same aggregate) declares
147
206
  def given_impl(description, &predicate)
148
207
  return reference_named_given(description) unless predicate
149
208
 
@@ -179,11 +238,19 @@ module Hecks
179
238
 
180
239
  public
181
240
 
241
+ # Declares a postcondition, checked against the settled record after the command's own
242
+ # mutations apply.
243
+ #
182
244
  # The postcondition — a given for the far side of the mutations,
183
245
  # evaluated against the settled record with `old` naming the state
184
246
  # as it stood before them: `ensures("...") { old.balance.cents ==
185
247
  # balance.cents + amount.cents }`. Same extraction, same Rule
186
- # shape, same refusal form; EnsuresNotMet instead of GivenNotMet.
248
+ # shape, same refusal form; `EnsuresNotMet` instead of `GivenNotMet`.
249
+ #
250
+ # @param description [String] the rule's description
251
+ # @yield the predicate body; evaluated for its extracted source, never called directly
252
+ # @return [void]
253
+ # @raise [Bluebook::DSL::Malformed] if the block's source could not be extracted
187
254
  def ensures(description, &predicate)
188
255
  @ensures << build_rule(Given, description, predicate, owner_name: @name, word: "ensures",
189
256
  extraction_failure: "a postcondition is carried as text, and this one has none")
@@ -205,8 +272,8 @@ module Hecks
205
272
  # Vendored addition, not (yet) upstream hecks (migration plan
206
273
  # task 4, i106 in-DSL math): `multiply:`/`clamp:` -- per-tick organ
207
274
  # math (miette's body/organs/bluebook: strength decays ×0.98,
208
- # weight/strength clamp to [0, 1]) that used to be shell-side awk
209
- # and moved into the bluebook itself. `multiply:` mirrors
275
+ # weight/strength clamp to [0, 1]), moved into the bluebook itself
276
+ # from shell-side awk. `multiply:` mirrors
210
277
  # increment/decrement's shape exactly (a Numeric amount, applied
211
278
  # by CommandRules::Arithmetic -- see that file's own comment on
212
279
  # the matching Float-support widening this required). `clamp:`
@@ -235,7 +302,7 @@ module Hecks
235
302
  # boolean occurrences of the bare-positional-second-arg shape
236
303
  # anywhere in the corpus). Folded into `to:` itself rather than
237
304
  # given its own mutation op -- semantically identical, same
238
- # UNSET-sentinel discipline the `to: false` fix already
305
+ # `UNSET`-sentinel discipline the `to: false` fix already
239
306
  # established (a positional `false` must read as "set to
240
307
  # false," not "absent," same as the keyword form). Only applied
241
308
  # when `to:` itself was not also given, so an explicit `to:`
@@ -262,15 +329,38 @@ module Hecks
262
329
  KWARG_TO_OP = { to: :set, append: :append, increment: :increment, decrement: :decrement,
263
330
  multiply: :multiply, clamp: :clamp, remove: :remove }.freeze
264
331
 
265
- # Renamed from `sets` — item #13's full metaprogrammed dispatch
332
+ # Declares one mutation this command applies to `target`, its op selected by whichever
333
+ # single keyword (or the omitted, self-referential form) names a source.
334
+ #
335
+ # Answers the `sets` word through the table's `calls:` column —
336
+ # item #13's full metaprogrammed dispatch
266
337
  # (slice 4c). The `KWARG_TO_OP` op-selection mapping is already
267
- # table-verified (`Argument#selects`), but the REST (UNSET-
338
+ # table-verified (`Argument#selects`), but the REST (`UNSET`-
268
339
  # sentinel discipline, redundant-spelling refusal, omittable-
269
340
  # `to:` fallback, one-mutation-only refusal, the position-
270
341
  # preserving `resolve_*!` reinsertion) is keyed off runtime
271
342
  # state, not a pure function of a static row — stays hand-
272
343
  # written, reached through `calls:` like everything else here.
273
- # Bootstrap-reachable, in BOOTSTRAP_CALLS_FALLBACK.
344
+ # Bootstrap-reachable, in `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`.
345
+ #
346
+ # @param target [Symbol, String] the field this mutation writes
347
+ # @param positional_to [Object] a bare positional value, folded into `to:` when `to:`
348
+ # itself is not given; the corpus's only real use is a boolean shorthand
349
+ # (`sets :deployed, true`)
350
+ # @param to [Object] the value or field reference to set `target` to; a Symbol equal to
351
+ # `target` is redundant and refused, and omitting `to:` entirely means the same thing
352
+ # @param append [Object] a Hash of fields for a new list element, or a bare value for the
353
+ # one-field shorthand
354
+ # @param increment [Object] the amount to add to `target`'s current value
355
+ # @param decrement [Object] the amount to subtract from `target`'s current value
356
+ # @param multiply [Object] the amount to multiply `target`'s current value by
357
+ # @param clamp [Array(Object, Object)] the `[min, max]` pair to bound `target`'s current
358
+ # value to
359
+ # @param remove [Object] the value to remove from `target`'s own list, by equality
360
+ # @return [void]
361
+ # @raise [Bluebook::DSL::Malformed] if `to:` redundantly repeats `target`, or more than
362
+ # one of `to:`/`append:`/`increment:`/`decrement:`/`multiply:`/`clamp:`/`remove:` (or
363
+ # the folded `positional_to`) is given
274
364
  def sets_impl(target, positional_to = UNSET, to: UNSET, append: UNSET,
275
365
  increment: UNSET, decrement: UNSET, multiply: UNSET, clamp: UNSET, remove: UNSET)
276
366
  # moved to the language: given "a mutation names a target", on Verb.Change
@@ -307,20 +397,34 @@ module Hecks
307
397
  @mutations << Mutation.new(target: target.to_sym, op: op, source: normalize_append_source(op, source))
308
398
  end
309
399
 
310
- # Legacy under shadow-parsing (S0a's own bridge) — frozen era text
311
- # minted before this rename still parses; live source refuses it,
400
+ # Refuses the retired `then_set` spelling, except while shadow-parsing frozen era text,
401
+ # where it is read as the legacy mutation shape.
402
+ #
403
+ # Legacy under shadow-parsing (S0a's own bridge) — frozen era
404
+ # text still parses under it; live source refuses it,
312
405
  # naming the replacement.
313
- # Renamed from `then_set` — item #13's full metaprogrammed
314
- # dispatch (slice 5). Not bootstrap-reachable. Now has its own
406
+ #
407
+ # Answers the `then_set` word — item #13's full metaprogrammed
408
+ # dispatch (slice 5). Not bootstrap-reachable. Has its own
315
409
  # dedicated, `status: "deprecated"` Keyword row (syntax.bluebook)
316
410
  # rather than living only as `sets`'s own `was:` — see that
317
411
  # row's own comment for why.
412
+ #
413
+ # @param target [Symbol, String] the field this legacy mutation writes
414
+ # @param positional_to [Object] the bare second positional argument; `UNSET` when
415
+ # omitted, forwarded to `legacy_then_set` verbatim under shadow-parsing, along with
416
+ # every keyword argument this call received
417
+ # @return [void]
418
+ # @raise [Bluebook::DSL::Malformed] outside shadow-parsing, always; under
419
+ # shadow-parsing, if `legacy_then_set` names no operation or more than one
318
420
  def then_set_impl(target, positional_to = UNSET, **)
319
421
  return legacy_then_set(target, positional_to, **) if MetaValidator.shadow_parsing?
320
422
 
321
423
  raise Malformed, "#{@name}'s then_set is gone — sets is the word now"
322
424
  end
323
425
 
426
+ # Declares one event this command announces to the outside.
427
+ #
324
428
  # No raise here. "an event is named" is declared in the language itself —
325
429
  # language/bluebook/behavior.bluebook, on Command.Announce — and MetaValidator is what
326
430
  # enforces it. This is the first rule to move across rather than be
@@ -341,6 +445,10 @@ module Hecks
341
445
  # are accepted in live source until a full corpus migration
342
446
  # lands and the same refusal this file's `reference_to`/
343
447
  # `trigger_impl` already carry can be added here safely.
448
+ #
449
+ # @param event_name [String, Symbol, Module] the event, quoted text or a bare constant
450
+ # such as `Account::AccountFrozen`
451
+ # @return [Array<String>] every event declared so far, this one last
344
452
  def emits(event_name)
345
453
  @emits << Naming.event_ref(event_name)
346
454
  end
@@ -349,11 +457,21 @@ module Hecks
349
457
  # append: { ply: state(:ply), knights: state(:knights) }` copies
350
458
  # what the record holds now into the new element; `sets :last,
351
459
  # to: state(:current)` copies one field onto another. A bare
352
- # Symbol always names an argument (see `resolve_append_fields!`),
460
+ # Symbol always names an argument (`resolve_append_fields!`'s own
461
+ # comment has the full account),
353
462
  # so without this a command could not snapshot its own state at
354
463
  # all. `Literal::StateRef`'s own comment has the wire spelling.
464
+ #
465
+ # References the record's own current field as a mutation source, as opposed to a
466
+ # bare Symbol, which names an argument.
467
+ #
468
+ # @param name [Symbol, String] the field to read from the pre-dispatch record
469
+ # @return [Literal::StateRef] the wrapped reference
355
470
  def state(name) = StateRef.new(name.to_sym)
356
471
 
472
+ # Declares a synchronous, atomic delegation of this command's own dispatch to one
473
+ # nested entity command.
474
+ #
357
475
  # The synchronous cousin of `trigger` — an aggregate-level command
358
476
  # that hands its own dispatch to one nested entity command, checked
359
477
  # and applied within the same atomic dispatch rather than a second
@@ -417,12 +535,19 @@ module Hecks
417
535
  # `meta_validator/shapes.rb#mutation`, `assembly/marks.rb#mutation`)
418
536
  # now check for `:delegate` alongside it, each with a comment
419
537
  # pointing back here.
420
- # Renamed from `delegates_to` to `delegates_to_impl` on declaration
421
- # — matches `sets_impl`/`given_impl`/`reference_to_impl`'s own
538
+ # Answers the `delegates_to` word through the table's `calls:`
539
+ # column — matches `sets_impl`/`given_impl`/`reference_to_impl`'s own
422
540
  # convention (language/bluebook/syntax.bluebook's own Keyword row
423
541
  # for this word names `calls: "delegates_to_impl"`), the same
424
542
  # `word`-vs-`_impl` split every hand-written (not yet item-#13-
425
543
  # generic-dispatch-migrated) DSL word here already follows.
544
+ #
545
+ # @param target [String, Symbol] the delegated command, dotted `"Entity.Command"`
546
+ # @param with [Hash{Symbol => Symbol, Object}] projects this command's own arguments
547
+ # onto the target's; a Symbol value names one of this command's own arguments,
548
+ # anything else is a literal
549
+ # @return [Array<Bluebook::Mutation>] every mutation declared so far, this one last
550
+ # @raise [Bluebook::DSL::Malformed] if `target` is not `"Entity.Command"` shaped
426
551
  def delegates_to_impl(target, with: {})
427
552
  entity_name, _dot, command_name = target.to_s.rpartition(".")
428
553
  if entity_name.empty? || command_name.empty?
@@ -434,6 +559,8 @@ module Hecks
434
559
  @mutations << Mutation.new(target: target.to_s, op: :delegate, source: with)
435
560
  end
436
561
 
562
+ # Declares that this command amends a past event, rather than rewriting it.
563
+ #
437
564
  # A command declaring what past fact it amends — the append-only
438
565
  # answer to "what if this record's history turns out to have been
439
566
  # wrong": never rewrite the original event (the log stays exactly
@@ -473,6 +600,18 @@ module Hecks
473
600
  # a real runtime consumer exists) — so coercing it to a String here
474
601
  # keeps it out of that machinery entirely rather than silently
475
602
  # miscategorised as an unresolvable argument reference.
603
+ #
604
+ # @param event [String, Symbol, Module] the event this command corrects, quoted text or
605
+ # a bare constant
606
+ # @param as [Symbol, nil] binds the located instance under this label, for a
607
+ # `given`/`ensures` to reference; nil declares no binding
608
+ # @param reason [String, nil] why the correction is being made, carried as audit data;
609
+ # required, non-blank
610
+ # @param reverses [Boolean] true to auto-derive the corrective `sets` from the original
611
+ # event's own mutations, checked once every sibling command is known; mutually
612
+ # exclusive with an explicit `sets` on the same command
613
+ # @return [Array<Bluebook::Mutation>] every mutation declared so far, this one last
614
+ # @raise [Bluebook::DSL::Malformed] if `reason` is nil or blank
476
615
  def corrects_impl(event, as: nil, reason: nil, reverses: false)
477
616
  if reason.to_s.strip.empty?
478
617
  raise Malformed,
@@ -489,6 +628,13 @@ module Hecks
489
628
  # `corrects` name a command and an event, never a field.
490
629
  FIELD_EFFECTS = %i[set append remove increment decrement multiply clamp].freeze
491
630
 
631
+ # Resolves implicit attributes, refuses conflicting mutations, and assembles the
632
+ # declared rules and effects into a `Command`.
633
+ #
634
+ # @return [Bluebook::Command] the built command
635
+ # @raise [Bluebook::DSL::Malformed] if the same field is written twice, an argument or
636
+ # state source names an undeclared field, or a delegating command also declares its
637
+ # own mutations or events
492
638
  def build
493
639
  resolve_implicit_attributes!
494
640
  refuse_duplicate_targets!
@@ -516,6 +662,21 @@ module Hecks
516
662
  )
517
663
  end
518
664
 
665
+ # Evaluates a `command` block against a fresh builder and returns what it built.
666
+ #
667
+ # @param name [String] the command's name
668
+ # @param owner [String, nil] name of the aggregate or entity this command belongs to
669
+ # @param from [String, Symbol, Array<String, Symbol>, nil] the lifecycle state(s) this
670
+ # command guards from
671
+ # @param named_givens [Hash{String => Bluebook::Given}] the owner's own given pool
672
+ # @param owner_attributes [Array<Bluebook::Attribute>] the owner's own attributes
673
+ # @param owner_constructs [Array<Bluebook::ValueObject, Bluebook::Entity>] the owner's
674
+ # own value objects and entities
675
+ # @param entity_shared_givens [Hash{String => Bluebook::Given}] a sibling piece's own
676
+ # entity-level given pool
677
+ # @yield the command body, evaluated with the builder as `self`; may be omitted
678
+ # @return [Bluebook::Command] the built command
679
+ # @raise [Bluebook::DSL::Malformed] if the body fails any check `#build` raises
519
680
  def self.build(name, owner: nil, from: nil, named_givens: {}, owner_attributes: [], owner_constructs: [],
520
681
  entity_shared_givens: {}, &block)
521
682
  builder = new(name, owner: owner, from: from, named_givens: named_givens,
@@ -660,8 +821,8 @@ module Hecks
660
821
  # A symbol naming its own target — never a literal that merely
661
822
  # spells the same word. `sets :moved, to: "moved"` (a chess rook
662
823
  # recording that it has moved, into a closed set whose member is
663
- # literally "moved") used to read as the shorthand and import
664
- # the owner's `moved` attribute onto the command — a phantom
824
+ # literally "moved") must not read as the shorthand and import
825
+ # the owner's `moved` attribute onto the command — that would add a phantom
665
826
  # argument nothing ever passes, harmless at runtime only
666
827
  # because the owner's default filled it, and a real, silent
667
828
  # divergence for every projection that reads the command's
@@ -724,7 +885,8 @@ module Hecks
724
885
  return if self_ref_fields.empty?
725
886
 
726
887
  present = self_ref_fields.filter_map { |field| attributes.find { |attr| attr.name == field } }
727
- return if present.size == self_ref_fields.size # already fully declared — nothing to resolve
888
+ # Already fully declared — nothing to resolve.
889
+ return if present.size == self_ref_fields.size
728
890
 
729
891
  anchor = present.empty? ? attributes.length : present.map { |attr| attributes.index(attr) }.min
730
892
  attributes.reject! { |attr| present.include?(attr) }
@@ -18,6 +18,22 @@ module Hecks
18
18
  include RuleReference
19
19
  include WordGate
20
20
 
21
+ # @param name [String] the entity's name, as written after `entity`
22
+ # @param owner_value_objects [Array<Bluebook::ValueObject>] the owning aggregate's value
23
+ # objects, threaded through unchanged for this piece's `identified_by` to resolve
24
+ # against
25
+ # @param owner_named_givens [Hash{String => Bluebook::Given}] the aggregate-wide,
26
+ # cross-entity given pool, shared and written through by `given_impl`
27
+ # @param identity_name_prefix [String, nil] the prefix a synthesized identity value
28
+ # object's name takes; nil derives it from `name`
29
+ # @param identity_value_object_installer [#call, nil] called with a synthesized value
30
+ # object to install it onto the owning aggregate; nil when there is none
31
+ # @param aggregate_name [String, nil] the root aggregate's name, for chapter-wide given
32
+ # keys; nil derives it from `name`
33
+ # @param chapter_entity_named_givens [Hash{String => Hash{String => Bluebook::Given}}]
34
+ # the chapter-wide, entity-scoped given pool, shared and written through
35
+ # @param chapter_entity_pending_givens [Array<Hash>] unresolved chapter-wide bare given
36
+ # references, appended to when this piece's own reference cannot resolve yet
21
37
  def initialize(name, owner_value_objects: [], owner_named_givens: {},
22
38
  identity_name_prefix: nil, identity_value_object_installer: nil,
23
39
  aggregate_name: nil, chapter_entity_named_givens: {}, chapter_entity_pending_givens: [])
@@ -61,16 +77,31 @@ module Hecks
61
77
  @pending_queries = []
62
78
  end
63
79
 
80
+ # Sets the human-readable description shown for this entity.
81
+ #
82
+ # @param value [String] the description text
83
+ # @return [String] the description as stored
64
84
  def description(value) = @description = value
65
85
 
86
+ # Declares a reference from this piece to another aggregate's identity.
87
+ #
66
88
  # The same field AggregateBuilder's own reference_to builds — a
67
89
  # piece can hold a reference to another root exactly the way its
68
90
  # own head can (Card.assignee_id, a Team's own id), just never
69
91
  # to another piece, since there's no cross-piece addressing
70
92
  # anywhere in this language to resolve one against.
71
- # Renamed from `reference_to` — item #13's full metaprogrammed
93
+ #
94
+ # Answers the `reference_to` word through the table's `calls:`
95
+ # column — item #13's full metaprogrammed
72
96
  # dispatch (slice 4b). Bootstrap-reachable, in
73
- # GenericDispatch::BOOTSTRAP_CALLS_FALLBACK.
97
+ # `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`.
98
+ #
99
+ # @param type [Module, Symbol, String] the referenced aggregate, written as a bare
100
+ # constant
101
+ # @param as [Symbol, nil] the attribute's name; nil derives it from `type`
102
+ # @param optional [Boolean] whether the reference may be absent
103
+ # @return [void]
104
+ # @raise [Bluebook::DSL::Malformed] if `as` (or the derived name) is already declared
74
105
  def reference_to_impl(type, as: nil, optional: false)
75
106
  target = Naming.demodulise(type)
76
107
  relationship_attribute(target, :reference_to,
@@ -86,6 +117,14 @@ module Hecks
86
117
  # `*_impl` to match AggregateBuilder's own siblings, item #13's
87
118
  # full metaprogrammed dispatch convention.
88
119
  # rubocop:disable Naming/PredicatePrefix
120
+ # Declares a list-typed relationship to another entity, referenced by its plural name.
121
+ #
122
+ # @param type [Module, Symbol, String] the related entity's plural name, a bare constant
123
+ # @param as [Symbol, nil] the attribute's name; nil derives it from `type`
124
+ # @param options [Hash] must be empty; kept only to name which unsupported keyword was
125
+ # given in the refusal message
126
+ # @return [void]
127
+ # @raise [Bluebook::DSL::Malformed] if any keyword argument is given
89
128
  def has_many_impl(type, as: nil, **options)
90
129
  unless options.empty?
91
130
  raise Malformed,
@@ -97,12 +136,24 @@ module Hecks
97
136
  as || Naming.snake(plural).to_sym, list: true)
98
137
  end
99
138
 
139
+ # Declares a single-valued relationship to another entity.
140
+ #
141
+ # @param type [Module, Symbol, String] the related entity, a bare constant
142
+ # @param as [Symbol, nil] the attribute's name; nil derives it from `type`
143
+ # @param optional [Boolean] whether the relationship may be absent
144
+ # @return [void]
100
145
  def has_one_impl(type, as: nil, optional: false)
101
146
  target = Naming.demodulise(type)
102
147
  relationship_attribute(target, :has_one, as || Naming.snake(target).to_sym, optional: optional)
103
148
  end
104
149
  # rubocop:enable Naming/PredicatePrefix
105
150
 
151
+ # Declares a single-valued relationship to the entity that owns this one.
152
+ #
153
+ # @param type [Module, Symbol, String] the owning entity, a bare constant
154
+ # @param as [Symbol, nil] the attribute's name; nil derives it from `type`
155
+ # @param optional [Boolean] whether the relationship may be absent
156
+ # @return [void]
106
157
  def belongs_to_impl(type, as: nil, optional: false)
107
158
  target = Naming.demodulise(type)
108
159
  relationship_attribute(target, :belongs_to, as || Naming.snake(target).to_sym, optional: optional)
@@ -117,22 +168,39 @@ module Hecks
117
168
  # (`identified_by :branch_code, :box_number`), which a piece may
118
169
  # declare for the same reason a head may.
119
170
 
171
+ # Queues a command declared on this piece, built later once every sibling has been seen.
172
+ #
120
173
  # `from:` — see `AggregateBuilder#command`'s own comment; the
121
174
  # same guard, checked against this piece's own lifecycle field
122
175
  # (S10, ADR 0025 — a piece's own state machine is checkable the
123
176
  # same way a head's is).
124
- # Renamed from `command`/`query`/`entity`/`lifecycle` (all below)
125
- # — item #13's full metaprogrammed dispatch (slice 4c), same
177
+ #
178
+ # Answers the `command` word through the table's `calls:` column,
179
+ # along with its siblings `query`/`entity`/`lifecycle` below —
180
+ # item #13's full metaprogrammed dispatch (slice 4c), same
126
181
  # reasoning as AggregateBuilder's own siblings: bootstrap-
127
- # reachable, in BOOTSTRAP_CALLS_FALLBACK.
182
+ # reachable, in `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`.
183
+ #
184
+ # @param name [String] the command's name
185
+ # @param from [String, Symbol, Array<String, Symbol>, nil] the lifecycle state(s) this
186
+ # command guards from; nil admits from any state
187
+ # @yield the command body, evaluated against a `CommandBuilder` once drained
188
+ # @return [Array<Array>] every pending command queued so far, this one last
128
189
  def command_impl(name, from: nil, &block)
129
190
  @pending_commands << [name, from, block]
130
191
  end
131
192
 
193
+ # Queues a query declared on this piece, built later once every sibling has been seen.
194
+ #
195
+ # @param name [String] the query's name
196
+ # @yield the query body, evaluated against a `QueryBuilder` once drained
197
+ # @return [Array<Array>] every pending query queued so far, this one last
132
198
  def query_impl(name, &block)
133
199
  @pending_queries << [name, block]
134
200
  end
135
201
 
202
+ # Queues a piece nested inside this one, built later once every sibling has been seen.
203
+ #
136
204
  # S17, ADR 0026 — a piece nested inside a piece. "A `Dispatch`
137
205
  # [has] no life outside its `Handler`" (the ADR's own words) —
138
206
  # the same reason `Member` nests inside `ValueObject`, one level
@@ -144,10 +212,20 @@ module Hecks
144
212
  # piece's already does (`AggregateBuilder#entity`'s own comment
145
213
  # names this pool ; there is exactly one of them, however deep
146
214
  # the nesting goes).
215
+ # @param name [String] the nested piece's name
216
+ # @yield the piece body, evaluated against an `EntityBuilder` once drained
217
+ # @return [Array<Array>] every pending piece queued so far, this one last
147
218
  def entity_impl(name, &block)
148
219
  @pending_entities << [name, block]
149
220
  end
150
221
 
222
+ # Declares this piece's own state machine.
223
+ #
224
+ # @param field [Symbol, String] the attribute the state machine lives on
225
+ # @param default [String, Symbol] the state a new record starts in
226
+ # @yield the lifecycle body of `transition` rows, evaluated against a `LifecycleBuilder`
227
+ # @return [Bluebook::Lifecycle] the built state machine
228
+ # @raise [Bluebook::DSL::Malformed] if two transitions for one command overlap
151
229
  def lifecycle_impl(field, default:, &)
152
230
  @lifecycle = LifecycleBuilder.build(field, default: default, &)
153
231
  end
@@ -170,8 +248,12 @@ module Hecks
170
248
  # (`CommandBuilder#reference_named_given`) still reads whatever
171
249
  # `@named_givens` holds at the command's own build time, not by
172
250
  # magic.
173
- # Renamed from `given` — item #13's full metaprogrammed dispatch
174
- # (slice 4b), same reasoning as reference_to_impl above.
251
+ # Declares a rule this piece's own commands must satisfy, or references one a sibling
252
+ # piece anywhere in the chapter already declared.
253
+ #
254
+ # Answers the `given` word through the table's `calls:` column —
255
+ # item #13's full metaprogrammed dispatch
256
+ # (slice 4b), same reasoning as `reference_to_impl` above.
175
257
  #
176
258
  # Bare — no block — references another piece's own declaration,
177
259
  # anywhere in the chapter, not just a sibling under this same
@@ -203,6 +285,19 @@ module Hecks
203
285
  # way `admits:` shipped textual before its own constant-bridge
204
286
  # existed, revisited only if a genuine, separate need for
205
287
  # constant-addressed pieces shows up later.
288
+ #
289
+ # @param description [String] the rule's description; also the name a sibling piece
290
+ # references it by when no block is given
291
+ # @param declared_by [String, nil] disambiguates which piece's own rule to reference,
292
+ # as `"Aggregate.Entity"`, when more than one shares `description`; only meaningful
293
+ # with no block
294
+ # @yield the predicate body; evaluated for its extracted source, never called directly
295
+ # @return [void]
296
+ # @raise [Bluebook::DSL::Malformed] if given a block whose source cannot be extracted;
297
+ # given no block, the description is immediately ambiguous between more than one
298
+ # already-loaded piece with no `declared_by` to disambiguate; an unresolved reference
299
+ # defers instead, and may still raise once the whole chapter has loaded, if it then
300
+ # resolves to none or more than one candidate
206
301
  def given_impl(description, declared_by: nil, &predicate)
207
302
  return reference_named_chapter_entity_given(description, declared_by: declared_by) unless predicate
208
303
 
@@ -242,13 +337,28 @@ module Hecks
242
337
  # piece yet; if that need shows up, it is `given`'s own
243
338
  # cross-entity write-through pattern to extend, not a reason to
244
339
  # invent a second one here speculatively.
245
- # Renamed from `invariant` — item #13's full metaprogrammed
246
- # dispatch (slice 4b), same reasoning as given_impl above.
340
+ # Declares a rule every instance of this piece must satisfy.
341
+ #
342
+ # Answers the `invariant` word through the table's `calls:`
343
+ # column — item #13's full metaprogrammed
344
+ # dispatch (slice 4b), same reasoning as `given_impl` above.
345
+ #
346
+ # @param description [String] the rule's description
347
+ # @yield the predicate body; evaluated for its extracted source, never called directly
348
+ # @return [void]
349
+ # @raise [Bluebook::DSL::Malformed] if the block's source could not be extracted
247
350
  def invariant_impl(description, &predicate)
248
351
  @invariants << build_rule(Invariant, description, predicate, owner_name: @name, word: "invariant",
249
352
  extraction_failure: "it would be a rule the IR cannot carry")
250
353
  end
251
354
 
355
+ # Assembles the declared attributes, relationships, nested constructs and rules into an
356
+ # `Entity`.
357
+ #
358
+ # @return [Bluebook::Entity] the built piece
359
+ # @raise [Bluebook::DSL::Malformed] if identity resolution, a lifecycle guard on a
360
+ # command with no lifecycle, a lifecycle-field mutation outside a transition, or a
361
+ # synthesized closed set colliding with one the aggregate already holds fails
252
362
  def build
253
363
  drain_pending!
254
364
  resolve_pending_identity!
@@ -268,6 +378,26 @@ module Hecks
268
378
  )
269
379
  end
270
380
 
381
+ # Evaluates an `entity` block against a fresh builder and returns what it built.
382
+ #
383
+ # @param name [String] the entity's name
384
+ # @param owner_value_objects [Array<Bluebook::ValueObject>] the owning aggregate's value
385
+ # objects, for `identified_by` to resolve against
386
+ # @param owner_named_givens [Hash{String => Bluebook::Given}] the aggregate-wide,
387
+ # cross-entity given pool
388
+ # @param identity_name_prefix [String, nil] the prefix a synthesized identity value
389
+ # object's name takes
390
+ # @param identity_value_object_installer [#call, nil] called to install a synthesized
391
+ # value object onto the owning aggregate
392
+ # @param aggregate_name [String, nil] the root aggregate's name, for chapter-wide given
393
+ # keys
394
+ # @param chapter_entity_named_givens [Hash{String => Hash{String => Bluebook::Given}}]
395
+ # the chapter-wide, entity-scoped given pool
396
+ # @param chapter_entity_pending_givens [Array<Hash>] unresolved chapter-wide bare given
397
+ # references
398
+ # @yield the entity body, evaluated with the builder as `self`; may be omitted
399
+ # @return [Bluebook::Entity] the built piece
400
+ # @raise [Bluebook::DSL::Malformed] if the body fails any check `#build` raises
271
401
  def self.build(name, owner_value_objects: [], owner_named_givens: {},
272
402
  identity_name_prefix: nil, identity_value_object_installer: nil,
273
403
  aggregate_name: nil, chapter_entity_named_givens: {}, chapter_entity_pending_givens: [], &block)