hecks 1.3.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 (373) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hecks/adapters/driven/claude_code.rb +72 -7
  3. data/lib/hecks/adapters/driven/d1.rb +187 -23
  4. data/lib/hecks/adapters/driven/folder.rb +83 -10
  5. data/lib/hecks/adapters/driven/google_authentication.rb +33 -12
  6. data/lib/hecks/adapters/driven/governance_authorization.rb +87 -18
  7. data/lib/hecks/adapters/driven/heki/journal.rb +13 -4
  8. data/lib/hecks/adapters/driven/heki/saga_store.rb +56 -10
  9. data/lib/hecks/adapters/driven/heki/snapshot.rb +1 -1
  10. data/lib/hecks/adapters/driven/heki.rb +106 -9
  11. data/lib/hecks/adapters/driven/identity_registry.rb +12 -2
  12. data/lib/hecks/adapters/driven/in_memory_ordering.rb +25 -3
  13. data/lib/hecks/adapters/driven/in_process_key_vault.adapter +3 -0
  14. data/lib/hecks/adapters/driven/in_process_key_vault.rb +53 -0
  15. data/lib/hecks/adapters/driven/lambda/client.rb +67 -14
  16. data/lib/hecks/adapters/driven/lambda.rb +82 -35
  17. data/lib/hecks/adapters/driven/local_storage.rb +83 -10
  18. data/lib/hecks/adapters/driven/memory.rb +205 -9
  19. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +21 -1
  20. data/lib/hecks/adapters/driven/postgres/codec.rb +27 -11
  21. data/lib/hecks/adapters/driven/postgres/outbox.rb +40 -2
  22. data/lib/hecks/adapters/driven/postgres/reconnect.rb +23 -7
  23. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +14 -14
  24. data/lib/hecks/adapters/driven/postgres.rb +175 -28
  25. data/lib/hecks/adapters/driven/prism.rb +50 -11
  26. data/lib/hecks/adapters/driven/secure_random_identity.rb +3 -0
  27. data/lib/hecks/adapters/driven/sql_query_builder.rb +34 -22
  28. data/lib/hecks/adapters/driven/sqlite/codec.rb +38 -10
  29. data/lib/hecks/adapters/driven/sqlite/projection.rb +60 -32
  30. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +12 -12
  31. data/lib/hecks/adapters/driven/sqlite.rb +181 -21
  32. data/lib/hecks/adapters/driven/system_clock.rb +3 -0
  33. data/lib/hecks/adapters/driven/tenant_provisioner.adapter +3 -0
  34. data/lib/hecks/adapters/driven/tenant_provisioner.rb +66 -0
  35. data/lib/hecks/adapters/driven.rb +6 -4
  36. data/lib/hecks/adapters/driving/github_webhook.rb +31 -18
  37. data/lib/hecks/behaviors/dsl.rb +60 -2
  38. data/lib/hecks/behaviors/expectations.rb +190 -29
  39. data/lib/hecks/behaviors/ir.rb +12 -1
  40. data/lib/hecks/behaviors/rspec.rb +9 -1
  41. data/lib/hecks/behaviors/runner.rb +21 -2
  42. data/lib/hecks/behaviors.rb +9 -1
  43. data/lib/hecks/bluebook/aggregate.rb +43 -13
  44. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +17 -10
  45. data/lib/hecks/bluebook/assembly/build.rb +26 -5
  46. data/lib/hecks/bluebook/assembly/contract.rb +98 -23
  47. data/lib/hecks/bluebook/assembly/contracts.rb +59 -52
  48. data/lib/hecks/bluebook/assembly/marks.rb +159 -30
  49. data/lib/hecks/bluebook/assembly/specializer.rb +38 -21
  50. data/lib/hecks/bluebook/assembly.rb +32 -14
  51. data/lib/hecks/bluebook/attribute.rb +26 -12
  52. data/lib/hecks/bluebook/behaviour/aggregate.rb +43 -11
  53. data/lib/hecks/bluebook/behaviour/attribute.rb +18 -5
  54. data/lib/hecks/bluebook/behaviour/chapter.rb +76 -5
  55. data/lib/hecks/bluebook/behaviour/command.rb +55 -25
  56. data/lib/hecks/bluebook/behaviour/domain_port.rb +27 -7
  57. data/lib/hecks/bluebook/behaviour/entity.rb +20 -8
  58. data/lib/hecks/bluebook/behaviour/hexagon.rb +30 -4
  59. data/lib/hecks/bluebook/behaviour/lifecycle.rb +27 -6
  60. data/lib/hecks/bluebook/behaviour/policy.rb +42 -17
  61. data/lib/hecks/bluebook/behaviour/process_manager.rb +39 -8
  62. data/lib/hecks/bluebook/behaviour/query.rb +6 -1
  63. data/lib/hecks/bluebook/behaviour/read_model.rb +29 -8
  64. data/lib/hecks/bluebook/behaviour/traits.rb +48 -12
  65. data/lib/hecks/bluebook/behaviour/value_object.rb +21 -9
  66. data/lib/hecks/bluebook/capabilities.rb +27 -0
  67. data/lib/hecks/bluebook/chapter.rb +51 -9
  68. data/lib/hecks/bluebook/command.rb +62 -17
  69. data/lib/hecks/bluebook/domain_port.rb +34 -9
  70. data/lib/hecks/bluebook/dsl/adapter_builder.rb +24 -0
  71. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +49 -49
  72. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +282 -123
  73. data/lib/hecks/bluebook/dsl/attribute_collector.rb +112 -75
  74. data/lib/hecks/bluebook/dsl/binding_proxy.rb +81 -2
  75. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +486 -117
  76. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +179 -47
  77. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
  78. data/lib/hecks/bluebook/dsl/command_builder.rb +284 -122
  79. data/lib/hecks/bluebook/dsl/const_shim.rb +46 -15
  80. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +90 -25
  81. data/lib/hecks/bluebook/dsl/entity_builder.rb +191 -61
  82. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
  83. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +130 -30
  84. data/lib/hecks/bluebook/dsl/identity_declaration.rb +38 -21
  85. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
  86. data/lib/hecks/bluebook/dsl/policy_builder.rb +86 -36
  87. data/lib/hecks/bluebook/dsl/port_builder.rb +38 -7
  88. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +56 -22
  89. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +111 -47
  90. data/lib/hecks/bluebook/dsl/query_builder.rb +37 -8
  91. data/lib/hecks/bluebook/dsl/read_model_builder.rb +127 -52
  92. data/lib/hecks/bluebook/dsl/rule_reference.rb +97 -43
  93. data/lib/hecks/bluebook/dsl/translation_builder.rb +150 -44
  94. data/lib/hecks/bluebook/dsl/value_object_builder.rb +68 -20
  95. data/lib/hecks/bluebook/dsl/word_gate.rb +59 -53
  96. data/lib/hecks/bluebook/dsl/world_builder.rb +51 -8
  97. data/lib/hecks/bluebook/entity.rb +40 -11
  98. data/lib/hecks/bluebook/expression/ast_json.rb +128 -36
  99. data/lib/hecks/bluebook/expression/ast_reader.rb +32 -3
  100. data/lib/hecks/bluebook/expression/canonical_form.rb +55 -16
  101. data/lib/hecks/bluebook/expression/evaluator.rb +221 -43
  102. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +54 -18
  103. data/lib/hecks/bluebook/expression/resolver.rb +369 -128
  104. data/lib/hecks/bluebook/hexagon.rb +35 -1
  105. data/lib/hecks/bluebook/lifecycle.rb +12 -1
  106. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +2 -1
  107. data/lib/hecks/bluebook/meta_validator/judge.rb +126 -108
  108. data/lib/hecks/bluebook/meta_validator/plan.rb +81 -46
  109. data/lib/hecks/bluebook/meta_validator/port_judge.rb +3 -2
  110. data/lib/hecks/bluebook/meta_validator/readings.rb +200 -50
  111. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +68 -41
  112. data/lib/hecks/bluebook/meta_validator/shapes.rb +166 -21
  113. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +286 -46
  114. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +11 -10
  115. data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -5
  116. data/lib/hecks/bluebook/meta_validator.rb +235 -139
  117. data/lib/hecks/bluebook/model_check.rb +434 -104
  118. data/lib/hecks/bluebook/pattern_subset.rb +32 -10
  119. data/lib/hecks/bluebook/policy.rb +15 -13
  120. data/lib/hecks/bluebook/process_manager.rb +27 -14
  121. data/lib/hecks/bluebook/project_discovery.rb +5 -0
  122. data/lib/hecks/bluebook/project_loader.rb +40 -0
  123. data/lib/hecks/bluebook/project_register.rb +50 -6
  124. data/lib/hecks/bluebook/query.rb +31 -4
  125. data/lib/hecks/bluebook/read_model.rb +35 -15
  126. data/lib/hecks/bluebook/reference.rb +26 -13
  127. data/lib/hecks/bluebook/smoke_test.rb +46 -23
  128. data/lib/hecks/bluebook/synthesizer.rb +46 -12
  129. data/lib/hecks/bluebook/translation.rb +34 -5
  130. data/lib/hecks/bluebook/value_object.rb +29 -11
  131. data/lib/hecks/bluebook.rb +5 -6
  132. data/lib/hecks/codemod.rb +138 -50
  133. data/lib/hecks/construct.rb +21 -7
  134. data/lib/hecks/corpus.rb +438 -0
  135. data/lib/hecks/deploy/bluebook/deploy.hecksagon +19 -0
  136. data/lib/hecks/doc/reference.rb +200 -31
  137. data/lib/hecks/embryonaut_bluebook.rb +38 -15
  138. data/lib/hecks/facade/cli_door.rb +69 -10
  139. data/lib/hecks/facade/cli_runner.rb +105 -24
  140. data/lib/hecks/facade/command_request.rb +23 -0
  141. data/lib/hecks/facade/handle.rb +155 -35
  142. data/lib/hecks/facade/json_door.rb +106 -25
  143. data/lib/hecks/facade/surface/aggregate_door.rb +50 -27
  144. data/lib/hecks/facade/surface/chapter.rb +26 -17
  145. data/lib/hecks/facade/surface.rb +16 -3
  146. data/lib/hecks/facade.rb +15 -4
  147. data/lib/hecks/forms/app.rb +46 -30
  148. data/lib/hecks/forms/command_form_renderer.rb +70 -9
  149. data/lib/hecks/forms/field_renderer.rb +142 -6
  150. data/lib/hecks/forms/field_shape.rb +183 -19
  151. data/lib/hecks/forms/html.rb +51 -7
  152. data/lib/hecks/forms/index_renderer.rb +14 -2
  153. data/lib/hecks/forms/page.rb +14 -0
  154. data/lib/hecks/forms/params.rb +120 -23
  155. data/lib/hecks/forms/port_argument.rb +14 -2
  156. data/lib/hecks/forms/query_form_renderer.rb +65 -2
  157. data/lib/hecks/forms/record_renderer.rb +60 -2
  158. data/lib/hecks/forms/record_table.rb +28 -1
  159. data/lib/hecks/forms/reference_options.rb +24 -0
  160. data/lib/hecks/forms/value_object_shape.rb +13 -3
  161. data/lib/hecks/forms.rb +24 -4
  162. data/lib/hecks/fqn.rb +59 -1
  163. data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
  164. data/lib/hecks/framework/bluebook/privacy.bluebook +155 -0
  165. data/lib/hecks/framework/oidc.json +15 -0
  166. data/lib/hecks/framework.rb +79 -25
  167. data/lib/hecks/freezer.rb +27 -11
  168. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +241 -106
  169. data/lib/hecks/fuzzing/combination_miner.rb +178 -0
  170. data/lib/hecks/fuzzing/concurrent_dispatch.rb +241 -45
  171. data/lib/hecks/fuzzing/coverage_campaign.rb +161 -0
  172. data/lib/hecks/fuzzing/differential.rb +192 -0
  173. data/lib/hecks/fuzzing/domain_generator.rb +871 -0
  174. data/lib/hecks/fuzzing/era_boundary.rb +68 -19
  175. data/lib/hecks/fuzzing/form_census.rb +121 -20
  176. data/lib/hecks/fuzzing/generated_domain_check.rb +171 -0
  177. data/lib/hecks/fuzzing/invalid_value_generator.rb +45 -6
  178. data/lib/hecks/fuzzing/isolated_boot.rb +137 -80
  179. data/lib/hecks/fuzzing/nondeterministic.rb +79 -0
  180. data/lib/hecks/fuzzing/persistence_parity.rb +111 -21
  181. data/lib/hecks/fuzzing/properties/corrections.rb +34 -9
  182. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +300 -51
  183. data/lib/hecks/fuzzing/properties/guards.rb +86 -42
  184. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +80 -32
  185. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +40 -35
  186. data/lib/hecks/fuzzing/properties/outbox.rb +70 -32
  187. data/lib/hecks/fuzzing/properties/querying.rb +82 -28
  188. data/lib/hecks/fuzzing/properties.rb +84 -51
  189. data/lib/hecks/fuzzing/qa_settings.rb +164 -0
  190. data/lib/hecks/fuzzing/replay.rb +241 -133
  191. data/lib/hecks/fuzzing/rotation_priority.rb +48 -28
  192. data/lib/hecks/fuzzing/rust_gap_manifest.rb +139 -0
  193. data/lib/hecks/fuzzing/self_consistency.rb +315 -137
  194. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +46 -40
  195. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +18 -11
  196. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +13 -12
  197. data/lib/hecks/fuzzing/sequence_generator/picker.rb +21 -12
  198. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +59 -32
  199. data/lib/hecks/fuzzing/sequence_generator.rb +158 -23
  200. data/lib/hecks/fuzzing/shrinker.rb +309 -0
  201. data/lib/hecks/fuzzing/structural_skips.rb +37 -130
  202. data/lib/hecks/fuzzing/sweep_depth.rb +12 -4
  203. data/lib/hecks/fuzzing/target_capabilities.rb +107 -29
  204. data/lib/hecks/fuzzing/value_generator.rb +110 -22
  205. data/lib/hecks/fuzzing.rb +5 -0
  206. data/lib/hecks/grammar/evolve.rb +188 -12
  207. data/lib/hecks/grammar.rb +53 -7
  208. data/lib/hecks/ir.rb +51 -20
  209. data/lib/hecks/language/bluebook/bluebook.bluebook +41 -0
  210. data/lib/hecks/language/bluebook/policy.bluebook +11 -1
  211. data/lib/hecks/language/bluebook/vocabulary.bluebook +348 -13
  212. data/lib/hecks/language/hecksagon/hecksagon.bluebook +11 -0
  213. data/lib/hecks/language/oidc.json +5 -0
  214. data/lib/hecks/literal.rb +41 -9
  215. data/lib/hecks/naming.rb +112 -31
  216. data/lib/hecks/ports/access_control.rb +53 -2
  217. data/lib/hecks/ports/agent/answers.rb +83 -6
  218. data/lib/hecks/ports/agent.rb +119 -35
  219. data/lib/hecks/ports/authentication.rb +44 -4
  220. data/lib/hecks/ports/authorization.rb +50 -11
  221. data/lib/hecks/ports/clock.rb +42 -23
  222. data/lib/hecks/ports/extraction.rb +16 -0
  223. data/lib/hecks/ports/identity_assignment.rb +23 -2
  224. data/lib/hecks/ports/identity_generation.rb +17 -3
  225. data/lib/hecks/ports/identity_resolution.rb +17 -1
  226. data/lib/hecks/ports/key_vault.port +6 -0
  227. data/lib/hecks/ports/key_vault.rb +58 -0
  228. data/lib/hecks/ports/loading.rb +4 -0
  229. data/lib/hecks/ports/persistence/append_only.rb +172 -8
  230. data/lib/hecks/ports/persistence/binding_policy.rb +34 -0
  231. data/lib/hecks/ports/persistence/codec_boundary.rb +178 -0
  232. data/lib/hecks/ports/persistence/execution.rb +4 -0
  233. data/lib/hecks/ports/persistence/null_saga_store.rb +12 -1
  234. data/lib/hecks/ports/persistence/plugin.rb +42 -4
  235. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +218 -25
  236. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +77 -9
  237. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +81 -24
  238. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +29 -18
  239. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +144 -60
  240. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +103 -8
  241. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +98 -23
  242. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +282 -109
  243. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +63 -25
  244. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +118 -66
  245. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +51 -28
  246. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +34 -5
  247. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +25 -12
  248. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +129 -34
  249. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +51 -6
  250. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +33 -9
  251. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +16 -0
  252. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +95 -6
  253. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +25 -2
  254. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +314 -90
  255. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +68 -10
  256. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +9 -3
  257. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +9 -2
  258. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +48 -8
  259. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +16 -1
  260. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +36 -5
  261. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +23 -3
  262. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +58 -19
  263. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +96 -5
  264. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +15 -0
  265. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +9 -1
  266. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -2
  267. data/lib/hecks/ports/persistence/plugins/era.rb +12 -2
  268. data/lib/hecks/ports/persistence/remote_runtime.rb +9 -2
  269. data/lib/hecks/ports/persistence/repository_factory.rb +23 -3
  270. data/lib/hecks/ports/persistence/state_codec.rb +319 -0
  271. data/lib/hecks/ports/persistence.rb +36 -1
  272. data/lib/hecks/ports/projection.rb +61 -7
  273. data/lib/hecks/ports/query/in_memory.rb +41 -8
  274. data/lib/hecks/ports/query/ordering.rb +21 -6
  275. data/lib/hecks/ports/query.rb +35 -0
  276. data/lib/hecks/ports.rb +1 -0
  277. data/lib/hecks/projections/bootstrap_table.rb +147 -0
  278. data/lib/hecks/projections/diagrams.rb +315 -79
  279. data/lib/hecks/projections/glossary/html.rb +91 -3
  280. data/lib/hecks/projections/glossary/markdown.rb +50 -3
  281. data/lib/hecks/projections/glossary/mermaid.rb +49 -1
  282. data/lib/hecks/projections/glossary/sections.rb +1 -1
  283. data/lib/hecks/projections/glossary/sentences.rb +110 -5
  284. data/lib/hecks/projections/glossary.rb +173 -31
  285. data/lib/hecks/projections/ir.rb +1 -1
  286. data/lib/hecks/projections/model/deviations.rb +62 -17
  287. data/lib/hecks/projections/model.rb +75 -21
  288. data/lib/hecks/projections/oidc.rb +45 -8
  289. data/lib/hecks/projections/parser_table.rb +53 -12
  290. data/lib/hecks/projections/reference.rb +15 -3
  291. data/lib/hecks/projections/rust_vocabulary.rb +646 -0
  292. data/lib/hecks/projections/shape.rb +10 -2
  293. data/lib/hecks/projections/statements.rb +69 -22
  294. data/lib/hecks/projections/vocabulary.rb +26 -9
  295. data/lib/hecks/projections.rb +5 -3
  296. data/lib/hecks/projector/cli_projector.rb +245 -37
  297. data/lib/hecks/projector/docs_projector.rb +154 -28
  298. data/lib/hecks/projector/exporter.rb +104 -29
  299. data/lib/hecks/projector/ir_projector.rb +7 -1
  300. data/lib/hecks/projector/narrate_projector.rb +144 -23
  301. data/lib/hecks/projector/target.rb +42 -18
  302. data/lib/hecks/projector.rb +86 -18
  303. data/lib/hecks/query_ir.rb +94 -47
  304. data/lib/hecks/query_specification/common/comparators.rb +19 -3
  305. data/lib/hecks/query_specification/common/comparison.rb +109 -27
  306. data/lib/hecks/query_specification/common/dsl.rb +65 -9
  307. data/lib/hecks/query_specification/common/null_policy.rb +61 -15
  308. data/lib/hecks/query_specification/common/null_semantics.rb +4 -0
  309. data/lib/hecks/query_specification/common/options.rb +25 -0
  310. data/lib/hecks/query_specification/field_path.rb +69 -15
  311. data/lib/hecks/query_specification/hop_path.rb +57 -20
  312. data/lib/hecks/query_specification/read_model/specification.rb +4 -0
  313. data/lib/hecks/rendering.rb +9 -3
  314. data/lib/hecks/router/namespace_installer.rb +16 -3
  315. data/lib/hecks/router.rb +56 -1
  316. data/lib/hecks/runtime/aggregate_lock.rb +20 -11
  317. data/lib/hecks/runtime/boot_gates.rb +21 -3
  318. data/lib/hecks/runtime/caller.rb +40 -8
  319. data/lib/hecks/runtime/capability_graph.rb +13 -2
  320. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +40 -41
  321. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +45 -67
  322. data/lib/hecks/runtime/command_interpreter.rb +195 -113
  323. data/lib/hecks/runtime/command_rules/admissibility.rb +231 -80
  324. data/lib/hecks/runtime/command_rules/arithmetic.rb +154 -96
  325. data/lib/hecks/runtime/command_rules/authorization.rb +38 -17
  326. data/lib/hecks/runtime/command_rules/emission.rb +18 -1
  327. data/lib/hecks/runtime/command_rules/references.rb +145 -55
  328. data/lib/hecks/runtime/command_rules.rb +3 -0
  329. data/lib/hecks/runtime/dependency_planning.rb +56 -11
  330. data/lib/hecks/runtime/dispatcher.rb +263 -92
  331. data/lib/hecks/runtime/entity_element.rb +304 -67
  332. data/lib/hecks/runtime/entity_interpreter.rb +149 -88
  333. data/lib/hecks/runtime/errors.rb +37 -23
  334. data/lib/hecks/runtime/event.rb +15 -10
  335. data/lib/hecks/runtime/identity.rb +91 -23
  336. data/lib/hecks/runtime/instance.rb +103 -18
  337. data/lib/hecks/runtime/interpreting.rb +24 -16
  338. data/lib/hecks/runtime/invocation.rb +358 -0
  339. data/lib/hecks/runtime/loader.rb +105 -19
  340. data/lib/hecks/runtime/outbox.rb +164 -26
  341. data/lib/hecks/runtime/policy_interpreter.rb +73 -60
  342. data/lib/hecks/runtime/port_operation_interpreter.rb +42 -19
  343. data/lib/hecks/runtime/query_interpreter.rb +93 -74
  344. data/lib/hecks/runtime/reaction_invocation.rb +73 -28
  345. data/lib/hecks/runtime/read_model_interpreter.rb +60 -44
  346. data/lib/hecks/runtime/rebuild_sweep.rb +32 -4
  347. data/lib/hecks/runtime/reference_hop.rb +48 -6
  348. data/lib/hecks/runtime/refusal_wording.rb +142 -115
  349. data/lib/hecks/runtime/registry/saga_persistence.rb +32 -21
  350. data/lib/hecks/runtime/registry/verification.rb +153 -28
  351. data/lib/hecks/runtime/registry.rb +202 -32
  352. data/lib/hecks/runtime/remote_dispatcher.rb +125 -24
  353. data/lib/hecks/runtime/routing.rb +36 -154
  354. data/lib/hecks/runtime/saga_interpreter/correlation.rb +25 -27
  355. data/lib/hecks/runtime/saga_interpreter.rb +90 -76
  356. data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
  357. data/lib/hecks/runtime/tenant_check.rb +33 -13
  358. data/lib/hecks/runtime/tenant_scope.rb +23 -5
  359. data/lib/hecks/runtime/value/admission.rb +75 -30
  360. data/lib/hecks/runtime/value/coercion.rb +555 -142
  361. data/lib/hecks/runtime/value/entity_list_coercion.rb +132 -60
  362. data/lib/hecks/runtime/value.rb +71 -21
  363. data/lib/hecks/runtime.rb +39 -7
  364. data/lib/hecks/storehouse.rb +368 -72
  365. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +130 -0
  366. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +32 -0
  367. data/lib/hecks/version.rb +3 -3
  368. data/lib/hecks/vocabulary.rb +205 -4
  369. data/lib/hecks.rb +91 -11
  370. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +19 -11
  371. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +24 -12
  372. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +38 -14
  373. metadata +28 -2
@@ -16,9 +16,9 @@ module Hecks
16
16
 
17
17
  # Vendored addition, not (yet) upstream hecks (migration plan
18
18
  # task 4): a sentinel for "this keyword was never passed", distinct
19
- # from Ruby's own nil/false. `then_set`'s ORIGINAL default (`to:
19
+ # from Ruby's own nil/false. `then_set`'s original default (`to:
20
20
  # nil`) could not tell "not given" apart from "given, and the
21
- # value IS false" — `to || from` silently treats `to: false` the
21
+ # value is false" — `to || from` silently treats `to: false` the
22
22
  # same as an absent `to:` and falls through to `from` (also
23
23
  # absent), so `then_set :accepted, to: false` raised "names no
24
24
  # operation" for the one value most likely to be written that way
@@ -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
@@ -40,14 +53,14 @@ module Hecks
40
53
  @named_givens = named_givens
41
54
  @owner_attributes = owner_attributes
42
55
  @owner_constructs = owner_constructs
43
- # THE AGGREGATE-WIDE cross-entity pool — see
56
+ # The aggregate-wide cross-entity pool — see
44
57
  # `AggregateBuilder#entity`'s own comment and `EntityBuilder#
45
- # given`'s. Empty (never populated) for an AGGREGATE-owned
58
+ # given`'s. Empty (never populated) for an aggregate-owned
46
59
  # command, which already checks its own owner's `named_givens`
47
60
  # directly and has no siblings to reach across; real only for
48
- # an ENTITY-owned command's own bare reference.
61
+ # an entity-owned command's own bare reference.
49
62
  @entity_shared_givens = entity_shared_givens
50
- # NORMALIZED the exact same way `StateTransition#from` already
63
+ # Normalized the exact same way `StateTransition#from` already
51
64
  # is — one state or several, a single spelling either way,
52
65
  # both read back through `Array(...)` at check time.
53
66
  @from = case from
@@ -57,20 +70,27 @@ module Hecks
57
70
  end
58
71
  end
59
72
 
60
- # A command carries ONE responsibility role — the language never
61
- # declared an OR between two roles, so a second `role` call would
73
+ # A command carries one responsibility role — the language never
74
+ # declared an or between two roles, so a second `role` call would
62
75
  # otherwise silently win while the first still looked declared,
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
67
- # (slice 4). This is a uniqueness gate on PRIOR STATE (`@role`
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
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
70
86
  # hand-written and is reached through `calls:` like `attribute`
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,29 +102,55 @@ 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
- # reference into a NAMED ARGUMENT, and a named argument is exactly the kind
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
105
151
 
106
- # `as:` MEANS "a named attribute", not "the root I act on" — so a command
107
- # can point at another instance of its OWN kind. Without this,
152
+ # `as:` means "a named attribute", not "the root I act on" — so a command
153
+ # can point at another instance of its own kind. Without this,
108
154
  # `reference_to Aggregate, as: :points_at` on a command owned by Aggregate
109
155
  # read as a second self-reference and was refused as naming two roots,
110
156
  # which is how the meta-domain's own Aggregate.Reference could not say the
@@ -131,19 +177,32 @@ module Hecks
131
177
 
132
178
  public
133
179
 
134
- # NO BLOCK is a REFERENCE, not a fresh declaration (S10, ADR
180
+ # Declares a precondition this command requires, or references one the owning aggregate
181
+ # (or a sibling piece) already declared.
182
+ #
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
- # reference it"): the SAME word, the SAME shape
138
- # (`AggregateBuilder#given`, block required there), so naming a
186
+ # reference it"): the same word, the same shape
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
- # named". Resolved against whatever the OWNING aggregate has
143
- # declared so far — see `AggregateBuilder#command`'s own
191
+ # named". Resolved against whatever the owning aggregate has
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
 
@@ -155,10 +214,10 @@ module Hecks
155
214
 
156
215
  private
157
216
 
158
- # PRIMITIVE 1 (RuleReference#resolve_hash_chain) — FIRST this
159
- # command's own owner (as always), THEN — only for a piece-owned
160
- # command, where it is real — a SIBLING piece's own entity-level
161
- # declaration under the SAME aggregate (`@entity_shared_givens`,
217
+ # Primitive 1 (RuleReference#resolve_hash_chain) — first this
218
+ # command's own owner (as always), then — only for a piece-owned
219
+ # command, where it is real — a sibling piece's own entity-level
220
+ # declaration under the same aggregate (`@entity_shared_givens`,
162
221
  # threaded from `EntityBuilder#given`'s own write-through).
163
222
  # "customer is active" declared once on `Visit`, referenced bare
164
223
  # by `KeyIssuance.Return` — two different pieces, same aggregate,
@@ -179,11 +238,19 @@ module Hecks
179
238
 
180
239
  public
181
240
 
182
- # The POSTCONDITION — a given for the far side of the mutations,
241
+ # Declares a postcondition, checked against the settled record after the command's own
242
+ # mutations apply.
243
+ #
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,14 +272,14 @@ 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:`
213
280
  # is a genuinely different shape -- its source is always a literal
214
281
  # `[min, max]` pair, never an argument reference, and it bounds
215
- # the CURRENT value rather than combining it with an amount -- so
282
+ # the current value rather than combining it with an amount -- so
216
283
  # it does not reuse `arithmetic`/`arithmetic_value_object` at all;
217
284
  # see MutationApplier#apply's own `:clamp` branch.
218
285
  #
@@ -222,7 +289,7 @@ module Hecks
222
289
  # commands: "the runtime list-remove primitive (then_set remove:)
223
290
  # drops it from the list element-wise, with no read-modify-write
224
291
  # -- so a concurrent Add can never be lost"). Matches an element
225
- # by VALUE equality against `mutation.source` (resolved and
292
+ # by value equality against `mutation.source` (resolved and
226
293
  # Value-coerced the same way increment/decrement/multiply already
227
294
  # coerce their own amount -- see MutationApplier#removed).
228
295
  #
@@ -235,23 +302,23 @@ 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
- # when `to:` itself was NOT also given, so an explicit `to:`
308
+ # when `to:` itself was not also given, so an explicit `to:`
242
309
  # keyword always wins over a stray positional.
243
310
  #
244
311
  # `sets` is the word (ADR 0025 reverts `then_set` — the grammar
245
312
  # already declared `sets`, `was: "then_set"`, and 143 of 143 live
246
313
  # call sites are `then_set`, so this method was the one thing
247
- # still backwards). `to:` is OMITTABLE when it would only repeat
314
+ # still backwards). `to:` is omittable when it would only repeat
248
315
  # the target — `sets :number` alone already means `to: :number`
249
- # — and the REDUNDANT explicit spelling is refused outright
316
+ # — and the redundant explicit spelling is refused outright
250
317
  # (principle 1, "one idea, one spelling": `sets :number, to:
251
318
  # :number` says nothing `sets :number` doesn't). `from:` — a
252
319
  # pure synonym for `to:` the language's own refusal message had
253
320
  # already forgotten about — is gone; write `to:`.
254
- # THE OP EACH KWARG SELECTS — `spec/syntax_conformance_spec.rb`'s own
321
+ # The op each kwarg selects — `spec/syntax_conformance_spec.rb`'s own
255
322
  # "selects the same op..." check holds this constant to the self-
256
323
  # hosted table's own `Argument#selects` column (`"op=set"`,
257
324
  # `"op=append"`, ...; whole-project table-unification survey, item
@@ -262,24 +329,47 @@ 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
- # preserving `resolve_*!` reinsertion) is keyed off RUNTIME
271
- # STATE, not a pure function of a static row — stays hand-
341
+ # preserving `resolve_*!` reinsertion) is keyed off runtime
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
277
367
 
278
368
  to = positional_to if to.equal?(UNSET) && !positional_to.equal?(UNSET)
279
369
 
280
- # `to:` only ever REPEATS the target when it's a Symbol naming a
370
+ # `to:` only ever repeats the target when it's a Symbol naming a
281
371
  # field — a literal (`to: false`, the bare positional-boolean
282
- # shorthand, a String, ...) is a VALUE, never a redundant name,
372
+ # shorthand, a String, ...) is a value, never a redundant name,
283
373
  # so it never has `.to_sym` to compare in the first place.
284
374
  if to.is_a?(Symbol) && to == target.to_sym
285
375
  raise Malformed,
@@ -292,7 +382,7 @@ module Hecks
292
382
  .reject { |_, source| source.equal?(UNSET) }
293
383
  named = given.to_h { |kwarg, source| [KWARG_TO_OP.fetch(kwarg), source] }
294
384
 
295
- # THE OMITTABLE CASE. No operation was named at all — not even a
385
+ # **The omittable case**. No operation was named at all — not even a
296
386
  # bare `to:` — so this is `sets :field` alone, which means
297
387
  # exactly what the redundant, refused spelling above would have.
298
388
  named = { set: target } if named.empty?
@@ -307,31 +397,45 @@ 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
- # enforces it. This is the first rule to move ACROSS rather than be
430
+ # enforces it. This is the first rule to move across rather than be
327
431
  # duplicated : delete the declaration and an unnamed event is accepted,
328
432
  # which is what makes the meta-domain load-bearing rather than decorative.
329
433
  #
330
- # BARE CONSTANT ACCEPTED (ADR 0025, S6 — "events first-class"),
434
+ # Bare constant accepted (ADR 0025, S6 — "events first-class"),
331
435
  # `emits Account::AccountFrozen`, resolved through `ConstShim` the
332
436
  # same way `trigger`/`dispatch` already resolve a command
333
- # reference (`Naming.event_ref`, that method's own header). NOT
334
- # yet a REQUIRED spelling, deliberately, unlike `trigger`/
437
+ # reference (`Naming.event_ref`, that method's own header). Not
438
+ # yet a required spelling, deliberately, unlike `trigger`/
335
439
  # `dispatch`'s own quoted-text refusal: those were safe to refuse
336
440
  # only because command references are already 100% migrated
337
441
  # across the live corpus (verified 2026-08-27) — `emits`/`on`
@@ -341,27 +445,41 @@ 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
347
455
 
348
- # THE RECORD'S OWN VALUE AS A MUTATION SOURCE — `sets :positions,
456
+ # The record's own value as a mutation source — `sets :positions,
349
457
  # append: { ply: state(:ply), knights: state(:knights) }` copies
350
- # what the record holds NOW into the new element; `sets :last,
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
 
357
- # THE SYNCHRONOUS COUSIN OF `trigger` — an AGGREGATE-level command
358
- # that hands its own dispatch to ONE nested entity command, checked
359
- # and applied within the SAME atomic dispatch rather than a second
472
+ # Declares a synchronous, atomic delegation of this command's own dispatch to one
473
+ # nested entity command.
474
+ #
475
+ # The synchronous cousin of `trigger` — an aggregate-level command
476
+ # that hands its own dispatch to one nested entity command, checked
477
+ # and applied within the same atomic dispatch rather than a second
360
478
  # one. Built because `trigger`/`saga`'s own dispatch (`Dispatcher
361
- # #reenter`) is a REACTION — the triggering command has already
479
+ # #reenter`) is a reaction — the triggering command has already
362
480
  # committed by the time it runs, and both `PolicyInterpreter#deliver`
363
481
  # and `SagaInterpreter#deliver_saga_dispatch` rescue a target's own
364
- # refusal and RECORD it rather than raising it back to the original
482
+ # refusal and record it rather than raising it back to the original
365
483
  # caller. That is correct for what those two exist for (an
366
484
  # eventually-consistent process that can compensate), and wrong for
367
485
  # a caller who needs a synchronous yes/no on whether the thing they
@@ -370,59 +488,66 @@ module Hecks
370
488
  # project. `delegates_to` fills exactly that gap: the target
371
489
  # entity command's own `given`/`ensures` are enforced as real,
372
490
  # unrescued Ruby exceptions, so a refusal deep in the entity's own
373
- # rules is the DELEGATING command's own refusal too, and nothing
491
+ # rules is the delegating command's own refusal too, and nothing
374
492
  # from either side is saved unless both sides pass.
375
493
  #
376
- # `target` is always ONE hop, `"Entity.Command"` — an aggregate
494
+ # `target` is always one hop, `"Entity.Command"` — an aggregate
377
495
  # names the entity it owns directly, same reach a bare `given`
378
496
  # reference already has (see `Knight`'s own comment on this
379
497
  # domain's shared givens), not a multi-segment dispatch chain.
380
- # `with:` resolves the SAME way `sets ..., append: {...}`'s own
498
+ # `with:` resolves the same way `sets ..., append: {...}`'s own
381
499
  # field map and a policy's own `trigger ..., with: {...}` already
382
- # do: each value names one of THIS command's own declared/implicit
500
+ # do: each value names one of this command's own declared/implicit
383
501
  # arguments, read at dispatch time and handed to the target under
384
502
  # its own key.
385
503
  #
386
- # MUTUALLY EXCLUSIVE with `sets`/`emits` on the SAME command — a
504
+ # Mutually exclusive with `sets`/`emits` on the same command — a
387
505
  # delegating command is a pure passthrough by design (see this
388
- # method's own header), so it declares no OTHER mutation or event of
389
- # its own; its result IS whatever the delegated entity command's own
506
+ # method's own header), so it declares no other mutation or event of
507
+ # its own; its result is whatever the delegated entity command's own
390
508
  # `sets`/`emits` produced. Enforced in `build`, once every builder
391
509
  # call has already run, so declaration order does not matter.
392
510
  #
393
- # STORED AS A MUTATION, not a new Command field — a real, deliberate
511
+ # Stored as a mutation, not a new Command field — a real, deliberate
394
512
  # choice, not a shortcut. `Command`'s own shape (givens/ensures/
395
513
  # mutations/emits/...) is not just Ruby: it round-trips through this
396
- # language's OWN self-hosted meta-domain (`Bluebook::MetaValidator`
514
+ # language's own self-hosted meta-domain (`Bluebook::MetaValidator`
397
515
  # dispatches every declaration into a "Bluebook" domain describing
398
- # itself, then REBUILDS the real runtime graph from what THAT domain
516
+ # itself, then rebuilds the real runtime graph from what that domain
399
517
  # holds — `Hecks.bluebook` registers what `MetaValidator.call`
400
518
  # returns, never the builder's own object graph directly, confirmed
401
519
  # by reading `meta_validator.rb`'s own `self.call`/`self.hold`).
402
- # A genuinely NEW top-level Command field needs the meta-domain's
520
+ # A genuinely new top-level Command field needs the meta-domain's
403
521
  # own grammar (`language/bluebook/behavior.bluebook` or wherever
404
522
  # Verb.Rule/Ensure/Change live) taught to carry it too — the same
405
523
  # scale of change as the real "item #13" migration this file's own
406
- # comments document throughout. A NEW MUTATION OP does not: `sets`'s
407
- # own `mutations:` field is ALREADY a fully round-tripped part of
524
+ # comments document throughout. A new mutation op does not: `sets`'s
525
+ # own `mutations:` field is already a fully round-tripped part of
408
526
  # that contract (`Assembly::CONTRACTS["Command"].fields[:mutations]`),
409
- # and an append-shaped mutation ALREADY carries a multi-key `fields:`
527
+ # and an append-shaped mutation already carries a multi-key `fields:`
410
528
  # hash the exact shape `with:` needs — so `delegates_to` rides that
411
529
  # existing, already-correct wire format under a new `op: :delegate`
412
530
  # instead of inventing a parallel one. `MutationOp`'s own closed set
413
531
  # (vocabulary.bluebook) gained `"delegate"` alongside `"append"`
414
- # for exactly this reason, and the THREE meta-domain touch points
532
+ # for exactly this reason, and the three meta-domain touch points
415
533
  # that hard-coded `op == "append"` for the multi-binding shape
416
534
  # (`meta_validator/readings.rb#mutation_rows`,
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,10 +559,12 @@ module Hecks
434
559
  @mutations << Mutation.new(target: target.to_s, op: :delegate, source: with)
435
560
  end
436
561
 
437
- # A COMMAND DECLARING WHAT PAST FACT IT AMENDS — the append-only
562
+ # Declares that this command amends a past event, rather than rewriting it.
563
+ #
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
440
- # what it was), always append a NEW fact on top. `event` names the
567
+ # what it was), always append a new fact on top. `event` names the
441
568
  # event this command corrects; `as:` optionally binds the located
442
569
  # instance for a `given`/`ensures` to reference, the same shape
443
570
  # `ensures`'s own `old` binding already has; `reason:` is not
@@ -446,33 +573,45 @@ module Hecks
446
573
  # here is why"), refused when blank the same way a `given`'s own
447
574
  # description is required to say something.
448
575
  #
449
- # STORED AS A MUTATION, not a new Command field — see the
576
+ # Stored as a mutation, not a new Command field — see the
450
577
  # KeywordSeed row's own comment (command.bluebook) for why: this
451
578
  # is the exact same choice `delegates_to` already made, for the
452
- # exact same reason. Rides the SAME multi-binding wire shape
579
+ # exact same reason. Rides the same multi-binding wire shape
453
580
  # `append`/`delegate` use — `as:`/`reason:`/`reverses:` assembled
454
581
  # by hand into one `source` hash, the way `sets_impl` assembles up
455
582
  # to seven kwargs into one `named` hash above.
456
583
  #
457
- # `reverses: true` NAMES an intent to auto-derive the corrective
584
+ # `reverses: true` names an intent to auto-derive the corrective
458
585
  # `sets` from the original event's own mutations, rather than the
459
586
  # author writing it — see `AggregateBuilder#seal_correction_targets`,
460
587
  # where that derivation actually happens (it needs every sibling
461
588
  # command in the aggregate already known, which this builder alone
462
- # cannot see). MUTUALLY EXCLUSIVE with an explicit `sets` on the
589
+ # cannot see). Mutually exclusive with an explicit `sets` on the
463
590
  # same command — two ways of saying the same thing is exactly the
464
591
  # redundancy `sets`'s own omittable-`to:` rule refuses elsewhere.
465
592
  #
466
- # `as:` IS ALWAYS STORED AS TEXT, never left a bare Symbol —
593
+ # `as:` is always stored as text, never left a bare Symbol —
467
594
  # `Mutation#classified_source`/`#appended_fields` (Behaviour::
468
595
  # Mutation) classify any bare Symbol field as `kind: "argument"`,
469
- # meaning "resolve this against one of THIS command's own declared
596
+ # meaning "resolve this against one of this command's own declared
470
597
  # attributes at dispatch time" (append/delegate's own meaning for a
471
598
  # Symbol). `as:` names no such thing — it is a plain label, not yet
472
599
  # wired into the expression evaluator (a future round's work, once
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,
@@ -485,10 +624,17 @@ module Hecks
485
624
  source: { as: as&.to_s, reason: reason.to_s, reverses: reverses })
486
625
  end
487
626
 
488
- # THE EFFECTS THAT WRITE A FIELD OF THE RECORD — `delegate` and
627
+ # The effects that write a field of the record — `delegate` and
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,
@@ -528,7 +689,7 @@ module Hecks
528
689
  private
529
690
 
530
691
  # C4.2 (docs/semantics/bluebook-semantics.md) — a command's effects
531
- # are ONE UPDATE SET over the pre-dispatch state, so a field
692
+ # are one update set over the pre-dispatch state, so a field
532
693
  # written twice has no meaning to give: last-wins would make
533
694
  # declaration order significant, which the update set says it is
534
695
  # not. Refused here, where the declaration can still be read whole.
@@ -548,21 +709,21 @@ module Hecks
548
709
  end
549
710
  end
550
711
 
551
- # RESOLUTION RULES — see `docs/resolution-rules/README.md` for the
712
+ # **Resolution rules** — see `docs/resolution-rules/README.md` for the
552
713
  # precise, language-agnostic algorithm each of `resolve_bare_set!`/
553
714
  # `resolve_append_fields!` implements (`implicit-command-attributes.md`
554
715
  # / `implicit-append-fields.md`) — the contract a Rust mirror is
555
716
  # written from, not inferred from this comment.
556
717
  #
557
- # `sets :field` ALONE (S5's own bare form — no `to:`, meaning
718
+ # `sets :field` alone (S5's own bare form — no `to:`, meaning
558
719
  # `to: :field`) already says the command accepts an argument
559
- # named `:field`; requiring a SEPARATE `attribute :field, Type`
720
+ # named `:field`; requiring a separate `attribute :field, Type`
560
721
  # line that retypes what the owning aggregate/entity already
561
722
  # declared is the same redundancy S10's `given` reference already
562
723
  # killed for preconditions ("a precondition shared across
563
724
  # commands is declared once... a command references it by
564
725
  # name"). Same move here, one level down: when the command
565
- # hasn't declared its own `:field`, import the OWNER's
726
+ # hasn't declared its own `:field`, import the owner's
566
727
  # already-built `Attribute` verbatim (same type, pattern,
567
728
  # optional, admits) instead of retyping it.
568
729
  #
@@ -576,7 +737,7 @@ module Hecks
576
737
  #
577
738
  # Declaration order matters here the same way it already does
578
739
  # for `identified_by`/`given` — the owner's own attribute must
579
- # exist by the time THIS builder's `build` runs, which every
740
+ # exist by the time this builder's `build` runs, which every
580
741
  # real bluebook already satisfies (the aggregate/entity always
581
742
  # declares its attributes before the commands that act on them).
582
743
  def resolve_implicit_attributes!
@@ -587,9 +748,9 @@ module Hecks
587
748
  end
588
749
  refuse_unknown_state_sources!(mutation)
589
750
  end
590
- # A SECOND, SEPARATE pass — not folded into the loop above — so
751
+ # A second, separate pass — not folded into the loop above — so
591
752
  # every mutation's own self-referential import (resolve_bare_set!)
592
- # has already landed before any mutation's SOURCE is checked
753
+ # has already landed before any mutation's source is checked
593
754
  # against the final `attributes` list. `sets :a, to: :b` declared
594
755
  # before `sets :b` (bare, importing :b from the owner) is real
595
756
  # and legal; checking inline, mutation by mutation, would refuse
@@ -601,8 +762,8 @@ module Hecks
601
762
  @mutations.each { |mutation| refuse_unknown_argument_sources!(mutation) }
602
763
  end
603
764
 
604
- # THE ONES `resolve_source` (CommandRules::Arithmetic) ONLY EVER
605
- # READS FROM `args` — never a fallback to the record's own current
765
+ # The ones `resolve_source` (CommandRules::Arithmetic) only ever
766
+ # reads from `args` — never a fallback to the record's own current
606
767
  # state the way `append`'s own per-field resolution legitimately
607
768
  # can (`MutationApplier#resolve_append_source`'s own `instance
608
769
  # [source]` fallback, a real, intentional second meaning this
@@ -612,8 +773,8 @@ module Hecks
612
773
  # runtime, not merely optional: no caller can ever supply a value
613
774
  # under a name the command never declared (ArgumentGate's own
614
775
  # `refuse_unknown_arguments` already refuses that), so the source
615
- # resolves to nil FOREVER, indistinguishable from a legitimately
616
- # absent OPTIONAL argument until this check existed to tell them
776
+ # resolves to nil forever, indistinguishable from a legitimately
777
+ # absent optional argument until this check existed to tell them
617
778
  # apart. Mirrors `AggregateBuilder#seal_query_argument`'s
618
779
  # identical shape for a query's own where-clause argument —
619
780
  # same mistake, one construct over.
@@ -626,9 +787,9 @@ module Hecks
626
787
  # The bare self-referential shape (`sets :field` alone, `source
627
788
  # == target`) is `resolve_bare_set!`'s own territory, not this
628
789
  # check's — when neither the command nor the owner declares
629
- # that name, the MORE SPECIFIC, pre-existing refusal one level
790
+ # that name, the more specific, pre-existing refusal one level
630
791
  # up (`AggregateBuilder#seal_mutation_targets`, checking the
631
- # mutation's TARGET against the aggregate's own fields) is the
792
+ # mutation's target against the aggregate's own fields) is the
632
793
  # one that should fire, naming the field as a target problem,
633
794
  # not — confusingly — as a source problem this check would
634
795
  # otherwise misreport it as.
@@ -642,7 +803,7 @@ module Hecks
642
803
  "caller actually sent"
643
804
  end
644
805
 
645
- # `state(:name)` names one of the OWNER'S OWN fields — a snapshot
806
+ # `state(:name)` names one of the owner's own fields — a snapshot
646
807
  # of something the record actually holds. Refused at build, by
647
808
  # name, the way an unknown `given` reference is; nothing here can
648
809
  # read a field the aggregate never declared.
@@ -657,11 +818,11 @@ module Hecks
657
818
  end
658
819
 
659
820
  def resolve_bare_set!(mutation)
660
- # A SYMBOL naming its own target — never a literal that merely
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
@@ -673,9 +834,9 @@ module Hecks
673
834
  attributes << owner_attr if owner_attr
674
835
  end
675
836
 
676
- # ONE HOP DEEPER than `resolve_bare_set!` — an `append:` mutation
837
+ # One hop deeper than `resolve_bare_set!` — an `append:` mutation
677
838
  # (`sets :ledger, append: { narrative: :narrative, ... }`) builds
678
- # a NEW element of a LIST field, not the command's own root
839
+ # a new element of a list field, not the command's own root
679
840
  # record, so a bare self-referential field inside it (the hash
680
841
  # key equals its own value, same shorthand `resolve_bare_set!`
681
842
  # already reads) can't resolve against `@owner_attributes` — the
@@ -683,7 +844,7 @@ module Hecks
683
844
  # element's own construct does (`attribute :ledger,
684
845
  # list_of(LedgerEntry)`, and `LedgerEntry` is what actually
685
846
  # declares `:narrative`). Resolves the list field's own element
686
- # TYPE first (`element_type_for`), then that construct's own
847
+ # type first (`element_type_for`), then that construct's own
687
848
  # attribute of the same name — same verbatim-import, one level
688
849
  # further down the same reasoning `resolve_bare_set!`'s own
689
850
  # comment already gives.
@@ -693,21 +854,21 @@ module Hecks
693
854
  # its own key ever qualifies, identical to `resolve_bare_set!`'s
694
855
  # own target/source text comparison.
695
856
  #
696
- # POSITION-PRESERVING, not appended at the end — the exported IR
857
+ # Position-preserving, not appended at the end — the exported IR
697
858
  # is array-order-sensitive (attributes carry their own declared
698
859
  # order onto the wire), so an append's fields are resolved as
699
- # ONE CONTIGUOUS GROUP, in the mutation's own hash order,
700
- # reinserted at whichever position the group's leftmost STILL-
701
- # DECLARED member already occupies (or the end, if every member
860
+ # one contiguous group, in the mutation's own hash order,
861
+ # reinserted at whichever position the group's leftmost still-
862
+ # declared member already occupies (or the end, if every member
702
863
  # of the group is resolved). A plain `attributes << owner_attr`
703
864
  # here would only ever reproduce the original order when the
704
865
  # missing field happened to already be last — real, live
705
866
  # evidence: `Keyword#was`/`Argument#variadic` (both genuinely
706
867
  # last in their own append hash) round-tripped correctly under
707
- # the naive append; every OTHER field in the same hash did not,
868
+ # the naive append; every other field in the same hash did not,
708
869
  # caught by this codemod's own reboot-and-diff safety net rather
709
870
  # than silently landing wrong.
710
- # `anchor` is a snapshot of `present`'s position, taken BEFORE
871
+ # `anchor` is a snapshot of `present`'s position, taken before
711
872
  # `attributes.reject!` mutates the array below it — see this
712
873
  # method's own header comment for why: a naive append-at-end
713
874
  # silently scrambled real corpus field order (Keyword#was/
@@ -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) }
@@ -735,7 +897,7 @@ module Hecks
735
897
  attributes.insert(anchor, *group)
736
898
  end
737
899
 
738
- # The owner's own LIST attribute names its element type as TEXT
900
+ # The owner's own list attribute names its element type as text
739
901
  # (`Attribute#type`, unwrapped from `list_of(...)` at declare
740
902
  # time) — resolved against `@owner_constructs` (the owner's own
741
903
  # value objects and entities, the only two kinds an element can
@@ -749,7 +911,7 @@ module Hecks
749
911
  @owner_constructs.find { |construct| construct.hecks_name.to_s == list_attr.type.to_s }
750
912
  end
751
913
 
752
- # LEGACY — see `then_set`'s own comment. The ORIGINAL implementation,
914
+ # Legacy — see `then_set`'s own comment. The original implementation,
753
915
  # verbatim: `from:` still a synonym for `to:`, no omittable-`to:`
754
916
  # shorthand, no refusal for the redundant `to: target` spelling —
755
917
  # frozen era text was minted under this reading, and a legacy
@@ -780,11 +942,11 @@ module Hecks
780
942
  @mutations << Mutation.new(target: target.to_sym, op: op, source: normalize_append_source(op, source))
781
943
  end
782
944
 
783
- # `append:` NORMALLY binds several fields at once (`append: {
945
+ # `append:` normally binds several fields at once (`append: {
784
946
  # name: :name, amount: :amount }`) — `Mutation#appended_fields`/
785
947
  # `MutationApplier#appended`/the meta-validator Judge's own
786
948
  # `mutation_rows` all read `mutation.source` as a Hash
787
- # unconditionally. A BARE value (`append: :single_field`, or any
949
+ # unconditionally. A bare value (`append: :single_field`, or any
788
950
  # non-Hash literal) is the one-field shorthand: exactly what an
789
951
  # explicit `append: { value: :single_field }` would have meant,
790
952
  # named the same way a single-field value object's own implicit