hecks 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (355) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hecks/adapters/driven/claude_code.rb +7 -7
  3. data/lib/hecks/adapters/driven/d1.rb +187 -23
  4. data/lib/hecks/adapters/driven/folder.rb +10 -10
  5. data/lib/hecks/adapters/driven/google_authentication.rb +8 -8
  6. data/lib/hecks/adapters/driven/governance_authorization.rb +31 -13
  7. data/lib/hecks/adapters/driven/heki/journal.rb +5 -4
  8. data/lib/hecks/adapters/driven/heki/saga_store.rb +5 -5
  9. data/lib/hecks/adapters/driven/heki.rb +13 -7
  10. data/lib/hecks/adapters/driven/identity_registry.rb +2 -2
  11. data/lib/hecks/adapters/driven/in_memory_ordering.rb +3 -3
  12. data/lib/hecks/adapters/driven/lambda/client.rb +34 -9
  13. data/lib/hecks/adapters/driven/lambda.rb +39 -33
  14. data/lib/hecks/adapters/driven/local_storage.rb +17 -10
  15. data/lib/hecks/adapters/driven/memory.rb +205 -9
  16. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +1 -1
  17. data/lib/hecks/adapters/driven/postgres/codec.rb +27 -11
  18. data/lib/hecks/adapters/driven/postgres/outbox.rb +40 -2
  19. data/lib/hecks/adapters/driven/postgres/reconnect.rb +23 -7
  20. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +14 -14
  21. data/lib/hecks/adapters/driven/postgres.rb +175 -28
  22. data/lib/hecks/adapters/driven/prism.rb +4 -4
  23. data/lib/hecks/adapters/driven/sql_query_builder.rb +34 -22
  24. data/lib/hecks/adapters/driven/sqlite/codec.rb +38 -10
  25. data/lib/hecks/adapters/driven/sqlite/projection.rb +60 -32
  26. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +12 -12
  27. data/lib/hecks/adapters/driven/sqlite.rb +181 -21
  28. data/lib/hecks/adapters/driven.rb +4 -4
  29. data/lib/hecks/adapters/driving/github_webhook.rb +18 -18
  30. data/lib/hecks/behaviors/dsl.rb +2 -2
  31. data/lib/hecks/behaviors/expectations.rb +23 -23
  32. data/lib/hecks/behaviors/ir.rb +1 -1
  33. data/lib/hecks/behaviors/rspec.rb +1 -1
  34. data/lib/hecks/behaviors/runner.rb +2 -2
  35. data/lib/hecks/behaviors.rb +1 -1
  36. data/lib/hecks/bluebook/aggregate.rb +13 -13
  37. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +10 -10
  38. data/lib/hecks/bluebook/assembly/build.rb +1 -1
  39. data/lib/hecks/bluebook/assembly/contract.rb +39 -16
  40. data/lib/hecks/bluebook/assembly/contracts.rb +40 -38
  41. data/lib/hecks/bluebook/assembly/marks.rb +18 -18
  42. data/lib/hecks/bluebook/assembly/specializer.rb +19 -21
  43. data/lib/hecks/bluebook/assembly.rb +9 -9
  44. data/lib/hecks/bluebook/attribute.rb +9 -9
  45. data/lib/hecks/bluebook/behaviour/aggregate.rb +11 -11
  46. data/lib/hecks/bluebook/behaviour/attribute.rb +5 -5
  47. data/lib/hecks/bluebook/behaviour/chapter.rb +23 -5
  48. data/lib/hecks/bluebook/behaviour/command.rb +23 -23
  49. data/lib/hecks/bluebook/behaviour/domain_port.rb +7 -7
  50. data/lib/hecks/bluebook/behaviour/entity.rb +8 -8
  51. data/lib/hecks/bluebook/behaviour/hexagon.rb +4 -4
  52. data/lib/hecks/bluebook/behaviour/lifecycle.rb +5 -5
  53. data/lib/hecks/bluebook/behaviour/policy.rb +12 -12
  54. data/lib/hecks/bluebook/behaviour/process_manager.rb +7 -7
  55. data/lib/hecks/bluebook/behaviour/query.rb +1 -1
  56. data/lib/hecks/bluebook/behaviour/read_model.rb +8 -8
  57. data/lib/hecks/bluebook/behaviour/traits.rb +12 -12
  58. data/lib/hecks/bluebook/behaviour/value_object.rb +6 -6
  59. data/lib/hecks/bluebook/capabilities.rb +27 -0
  60. data/lib/hecks/bluebook/chapter.rb +28 -9
  61. data/lib/hecks/bluebook/command.rb +12 -12
  62. data/lib/hecks/bluebook/domain_port.rb +9 -9
  63. data/lib/hecks/bluebook/dsl/adapter_builder.rb +24 -0
  64. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +49 -49
  65. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +96 -96
  66. data/lib/hecks/bluebook/dsl/attribute_collector.rb +41 -41
  67. data/lib/hecks/bluebook/dsl/binding_proxy.rb +22 -2
  68. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +111 -74
  69. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +48 -30
  70. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
  71. data/lib/hecks/bluebook/dsl/command_builder.rb +103 -103
  72. data/lib/hecks/bluebook/dsl/const_shim.rb +46 -15
  73. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +90 -25
  74. data/lib/hecks/bluebook/dsl/entity_builder.rb +56 -56
  75. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
  76. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +89 -30
  77. data/lib/hecks/bluebook/dsl/identity_declaration.rb +17 -17
  78. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
  79. data/lib/hecks/bluebook/dsl/policy_builder.rb +30 -21
  80. data/lib/hecks/bluebook/dsl/port_builder.rb +38 -7
  81. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +56 -22
  82. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +35 -35
  83. data/lib/hecks/bluebook/dsl/query_builder.rb +5 -5
  84. data/lib/hecks/bluebook/dsl/read_model_builder.rb +34 -34
  85. data/lib/hecks/bluebook/dsl/rule_reference.rb +41 -39
  86. data/lib/hecks/bluebook/dsl/translation_builder.rb +9 -9
  87. data/lib/hecks/bluebook/dsl/value_object_builder.rb +16 -16
  88. data/lib/hecks/bluebook/dsl/word_gate.rb +59 -53
  89. data/lib/hecks/bluebook/dsl/world_builder.rb +51 -8
  90. data/lib/hecks/bluebook/entity.rb +11 -11
  91. data/lib/hecks/bluebook/expression/ast_json.rb +20 -20
  92. data/lib/hecks/bluebook/expression/ast_reader.rb +3 -3
  93. data/lib/hecks/bluebook/expression/canonical_form.rb +9 -9
  94. data/lib/hecks/bluebook/expression/evaluator.rb +18 -18
  95. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +18 -18
  96. data/lib/hecks/bluebook/expression/resolver.rb +60 -62
  97. data/lib/hecks/bluebook/hexagon.rb +1 -1
  98. data/lib/hecks/bluebook/lifecycle.rb +1 -1
  99. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -1
  100. data/lib/hecks/bluebook/meta_validator/judge.rb +101 -87
  101. data/lib/hecks/bluebook/meta_validator/plan.rb +39 -39
  102. data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -2
  103. data/lib/hecks/bluebook/meta_validator/readings.rb +43 -43
  104. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +44 -37
  105. data/lib/hecks/bluebook/meta_validator/shapes.rb +25 -21
  106. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +145 -31
  107. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +6 -6
  108. data/lib/hecks/bluebook/meta_validator/world_judge.rb +5 -5
  109. data/lib/hecks/bluebook/meta_validator.rb +70 -70
  110. data/lib/hecks/bluebook/model_check.rb +170 -84
  111. data/lib/hecks/bluebook/pattern_subset.rb +9 -9
  112. data/lib/hecks/bluebook/policy.rb +15 -13
  113. data/lib/hecks/bluebook/process_manager.rb +14 -14
  114. data/lib/hecks/bluebook/project_register.rb +6 -6
  115. data/lib/hecks/bluebook/query.rb +4 -4
  116. data/lib/hecks/bluebook/read_model.rb +14 -14
  117. data/lib/hecks/bluebook/reference.rb +8 -8
  118. data/lib/hecks/bluebook/smoke_test.rb +19 -19
  119. data/lib/hecks/bluebook/synthesizer.rb +12 -12
  120. data/lib/hecks/bluebook/translation.rb +4 -4
  121. data/lib/hecks/bluebook/value_object.rb +6 -6
  122. data/lib/hecks/bluebook.rb +2 -2
  123. data/lib/hecks/codemod/legacy_dispatch_args.rb +299 -0
  124. data/lib/hecks/codemod/legacy_dispatch_recorder.rb +186 -0
  125. data/lib/hecks/codemod.rb +36 -35
  126. data/lib/hecks/construct.rb +6 -6
  127. data/lib/hecks/corpus.rb +317 -0
  128. data/lib/hecks/deprecation.rb +95 -0
  129. data/lib/hecks/doc/reference.rb +19 -19
  130. data/lib/hecks/embryonaut_bluebook.rb +11 -11
  131. data/lib/hecks/facade/cli_door.rb +69 -10
  132. data/lib/hecks/facade/cli_runner.rb +105 -24
  133. data/lib/hecks/facade/command_request.rb +23 -0
  134. data/lib/hecks/facade/handle.rb +79 -32
  135. data/lib/hecks/facade/json_door.rb +106 -25
  136. data/lib/hecks/facade/surface/aggregate_door.rb +42 -27
  137. data/lib/hecks/facade/surface/chapter.rb +26 -17
  138. data/lib/hecks/facade/surface.rb +16 -3
  139. data/lib/hecks/facade.rb +15 -4
  140. data/lib/hecks/forms/app.rb +46 -30
  141. data/lib/hecks/forms/command_form_renderer.rb +70 -9
  142. data/lib/hecks/forms/field_renderer.rb +142 -6
  143. data/lib/hecks/forms/field_shape.rb +180 -19
  144. data/lib/hecks/forms/html.rb +51 -7
  145. data/lib/hecks/forms/index_renderer.rb +14 -2
  146. data/lib/hecks/forms/params.rb +120 -23
  147. data/lib/hecks/forms/port_argument.rb +2 -2
  148. data/lib/hecks/forms/query_form_renderer.rb +2 -2
  149. data/lib/hecks/forms/record_renderer.rb +2 -2
  150. data/lib/hecks/forms/record_table.rb +1 -1
  151. data/lib/hecks/forms/value_object_shape.rb +3 -3
  152. data/lib/hecks/forms.rb +24 -4
  153. data/lib/hecks/fqn.rb +1 -1
  154. data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
  155. data/lib/hecks/framework.rb +48 -17
  156. data/lib/hecks/freezer.rb +11 -11
  157. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +90 -90
  158. data/lib/hecks/fuzzing/combination_miner.rb +119 -0
  159. data/lib/hecks/fuzzing/concurrent_dispatch.rb +140 -45
  160. data/lib/hecks/fuzzing/coverage_campaign.rb +118 -0
  161. data/lib/hecks/fuzzing/differential.rb +158 -0
  162. data/lib/hecks/fuzzing/domain_generator.rb +694 -0
  163. data/lib/hecks/fuzzing/era_boundary.rb +37 -18
  164. data/lib/hecks/fuzzing/form_census.rb +35 -20
  165. data/lib/hecks/fuzzing/generated_domain_check.rb +95 -0
  166. data/lib/hecks/fuzzing/invalid_value_generator.rb +6 -6
  167. data/lib/hecks/fuzzing/isolated_boot.rb +62 -62
  168. data/lib/hecks/fuzzing/nondeterministic.rb +67 -0
  169. data/lib/hecks/fuzzing/persistence_parity.rb +19 -21
  170. data/lib/hecks/fuzzing/properties/corrections.rb +9 -9
  171. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +154 -49
  172. data/lib/hecks/fuzzing/properties/guards.rb +42 -42
  173. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +32 -32
  174. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +22 -35
  175. data/lib/hecks/fuzzing/properties/outbox.rb +24 -24
  176. data/lib/hecks/fuzzing/properties/querying.rb +17 -17
  177. data/lib/hecks/fuzzing/properties.rb +64 -40
  178. data/lib/hecks/fuzzing/qa_settings.rb +152 -0
  179. data/lib/hecks/fuzzing/replay.rb +114 -114
  180. data/lib/hecks/fuzzing/rotation_priority.rb +10 -10
  181. data/lib/hecks/fuzzing/rust_gap_manifest.rb +113 -0
  182. data/lib/hecks/fuzzing/self_consistency.rb +141 -112
  183. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +35 -35
  184. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +18 -11
  185. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +13 -12
  186. data/lib/hecks/fuzzing/sequence_generator/picker.rb +21 -12
  187. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +58 -31
  188. data/lib/hecks/fuzzing/sequence_generator.rb +111 -23
  189. data/lib/hecks/fuzzing/shrinker.rb +197 -0
  190. data/lib/hecks/fuzzing/structural_skips.rb +23 -130
  191. data/lib/hecks/fuzzing/sweep_depth.rb +4 -4
  192. data/lib/hecks/fuzzing/target_capabilities.rb +49 -18
  193. data/lib/hecks/fuzzing/value_generator.rb +14 -14
  194. data/lib/hecks/fuzzing.rb +5 -0
  195. data/lib/hecks/grammar/evolve.rb +10 -10
  196. data/lib/hecks/grammar.rb +7 -7
  197. data/lib/hecks/ir.rb +13 -13
  198. data/lib/hecks/language/bluebook/bluebook.bluebook +41 -0
  199. data/lib/hecks/language/bluebook/policy.bluebook +11 -1
  200. data/lib/hecks/language/bluebook/vocabulary.bluebook +348 -13
  201. data/lib/hecks/language/oidc.json +5 -0
  202. data/lib/hecks/literal.rb +9 -9
  203. data/lib/hecks/naming.rb +25 -25
  204. data/lib/hecks/ports/access_control.rb +58 -2
  205. data/lib/hecks/ports/agent/answers.rb +83 -6
  206. data/lib/hecks/ports/agent.rb +119 -35
  207. data/lib/hecks/ports/authentication.rb +44 -4
  208. data/lib/hecks/ports/authorization.rb +53 -11
  209. data/lib/hecks/ports/clock.rb +42 -23
  210. data/lib/hecks/ports/extraction.rb +16 -0
  211. data/lib/hecks/ports/identity_assignment.rb +24 -2
  212. data/lib/hecks/ports/identity_generation.rb +17 -3
  213. data/lib/hecks/ports/identity_resolution.rb +18 -1
  214. data/lib/hecks/ports/loading.rb +4 -0
  215. data/lib/hecks/ports/persistence/append_only.rb +172 -8
  216. data/lib/hecks/ports/persistence/binding_policy.rb +34 -0
  217. data/lib/hecks/ports/persistence/codec_boundary.rb +178 -0
  218. data/lib/hecks/ports/persistence/execution.rb +4 -0
  219. data/lib/hecks/ports/persistence/null_saga_store.rb +12 -1
  220. data/lib/hecks/ports/persistence/plugin.rb +42 -4
  221. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +218 -25
  222. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +77 -9
  223. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +81 -24
  224. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +29 -18
  225. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +144 -60
  226. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +103 -8
  227. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +98 -23
  228. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +282 -109
  229. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +63 -25
  230. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +118 -66
  231. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +51 -28
  232. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +28 -5
  233. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +25 -12
  234. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +129 -34
  235. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +51 -6
  236. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +33 -9
  237. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +16 -0
  238. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +57 -4
  239. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +25 -2
  240. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +314 -90
  241. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +68 -10
  242. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +9 -3
  243. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +9 -2
  244. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +42 -8
  245. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +16 -1
  246. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +36 -5
  247. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +23 -3
  248. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +18 -19
  249. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +5 -5
  250. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +1 -1
  251. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -2
  252. data/lib/hecks/ports/persistence/plugins/era.rb +12 -2
  253. data/lib/hecks/ports/persistence/remote_runtime.rb +9 -2
  254. data/lib/hecks/ports/persistence/repository_factory.rb +23 -3
  255. data/lib/hecks/ports/persistence/state_codec.rb +319 -0
  256. data/lib/hecks/ports/persistence.rb +36 -1
  257. data/lib/hecks/ports/projection.rb +61 -7
  258. data/lib/hecks/ports/query/in_memory.rb +3 -3
  259. data/lib/hecks/ports/query/ordering.rb +6 -6
  260. data/lib/hecks/ports/query.rb +35 -0
  261. data/lib/hecks/projections/bootstrap_table.rb +112 -0
  262. data/lib/hecks/projections/diagrams.rb +75 -75
  263. data/lib/hecks/projections/glossary/html.rb +3 -3
  264. data/lib/hecks/projections/glossary/markdown.rb +3 -3
  265. data/lib/hecks/projections/glossary/mermaid.rb +1 -1
  266. data/lib/hecks/projections/glossary/sections.rb +1 -1
  267. data/lib/hecks/projections/glossary/sentences.rb +5 -5
  268. data/lib/hecks/projections/glossary.rb +15 -15
  269. data/lib/hecks/projections/ir.rb +1 -1
  270. data/lib/hecks/projections/model/deviations.rb +18 -17
  271. data/lib/hecks/projections/model.rb +25 -21
  272. data/lib/hecks/projections/oidc.rb +7 -7
  273. data/lib/hecks/projections/parser_table.rb +5 -5
  274. data/lib/hecks/projections/reference.rb +3 -3
  275. data/lib/hecks/projections/rust_vocabulary.rb +443 -0
  276. data/lib/hecks/projections/shape.rb +2 -2
  277. data/lib/hecks/projections/statements.rb +11 -11
  278. data/lib/hecks/projections/vocabulary.rb +9 -9
  279. data/lib/hecks/projections.rb +5 -3
  280. data/lib/hecks/projector/cli_projector.rb +29 -29
  281. data/lib/hecks/projector/docs_projector.rb +13 -13
  282. data/lib/hecks/projector/exporter.rb +42 -21
  283. data/lib/hecks/projector/ir_projector.rb +1 -1
  284. data/lib/hecks/projector/narrate_projector.rb +11 -11
  285. data/lib/hecks/projector/target.rb +13 -13
  286. data/lib/hecks/projector.rb +15 -15
  287. data/lib/hecks/query_ir.rb +47 -47
  288. data/lib/hecks/query_specification/common/comparators.rb +19 -3
  289. data/lib/hecks/query_specification/common/comparison.rb +109 -27
  290. data/lib/hecks/query_specification/common/dsl.rb +65 -9
  291. data/lib/hecks/query_specification/common/null_policy.rb +57 -13
  292. data/lib/hecks/query_specification/common/null_semantics.rb +4 -0
  293. data/lib/hecks/query_specification/common/options.rb +25 -0
  294. data/lib/hecks/query_specification/field_path.rb +69 -15
  295. data/lib/hecks/query_specification/hop_path.rb +57 -20
  296. data/lib/hecks/query_specification/read_model/specification.rb +4 -0
  297. data/lib/hecks/rendering.rb +3 -3
  298. data/lib/hecks/router/namespace_installer.rb +3 -3
  299. data/lib/hecks/router.rb +1 -1
  300. data/lib/hecks/runtime/aggregate_lock.rb +11 -11
  301. data/lib/hecks/runtime/boot_gates.rb +3 -3
  302. data/lib/hecks/runtime/caller.rb +8 -8
  303. data/lib/hecks/runtime/capability_graph.rb +2 -2
  304. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +22 -25
  305. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +42 -63
  306. data/lib/hecks/runtime/command_interpreter.rb +159 -102
  307. data/lib/hecks/runtime/command_rules/admissibility.rb +69 -69
  308. data/lib/hecks/runtime/command_rules/arithmetic.rb +137 -91
  309. data/lib/hecks/runtime/command_rules/authorization.rb +38 -17
  310. data/lib/hecks/runtime/command_rules/emission.rb +18 -1
  311. data/lib/hecks/runtime/command_rules/references.rb +35 -35
  312. data/lib/hecks/runtime/command_rules.rb +3 -0
  313. data/lib/hecks/runtime/dependency_planning.rb +11 -11
  314. data/lib/hecks/runtime/dispatcher.rb +283 -90
  315. data/lib/hecks/runtime/entity_element.rb +151 -67
  316. data/lib/hecks/runtime/entity_interpreter.rb +108 -82
  317. data/lib/hecks/runtime/errors.rb +19 -19
  318. data/lib/hecks/runtime/event.rb +6 -6
  319. data/lib/hecks/runtime/identity.rb +22 -22
  320. data/lib/hecks/runtime/instance.rb +39 -14
  321. data/lib/hecks/runtime/interpreting.rb +12 -12
  322. data/lib/hecks/runtime/invocation.rb +276 -0
  323. data/lib/hecks/runtime/loader.rb +14 -14
  324. data/lib/hecks/runtime/outbox.rb +23 -23
  325. data/lib/hecks/runtime/policy_interpreter.rb +54 -54
  326. data/lib/hecks/runtime/port_operation_interpreter.rb +22 -19
  327. data/lib/hecks/runtime/query_interpreter.rb +55 -64
  328. data/lib/hecks/runtime/reaction_invocation.rb +20 -20
  329. data/lib/hecks/runtime/read_model_interpreter.rb +40 -40
  330. data/lib/hecks/runtime/rebuild_sweep.rb +4 -4
  331. data/lib/hecks/runtime/reference_hop.rb +6 -6
  332. data/lib/hecks/runtime/refusal_wording.rb +92 -115
  333. data/lib/hecks/runtime/registry/saga_persistence.rb +21 -21
  334. data/lib/hecks/runtime/registry/verification.rb +36 -26
  335. data/lib/hecks/runtime/registry.rb +45 -28
  336. data/lib/hecks/runtime/remote_dispatcher.rb +38 -23
  337. data/lib/hecks/runtime/routing.rb +10 -153
  338. data/lib/hecks/runtime/saga_interpreter/correlation.rb +17 -17
  339. data/lib/hecks/runtime/saga_interpreter.rb +64 -64
  340. data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
  341. data/lib/hecks/runtime/tenant_check.rb +9 -9
  342. data/lib/hecks/runtime/tenant_scope.rb +5 -5
  343. data/lib/hecks/runtime/value/admission.rb +75 -30
  344. data/lib/hecks/runtime/value/coercion.rb +318 -127
  345. data/lib/hecks/runtime/value/entity_list_coercion.rb +34 -34
  346. data/lib/hecks/runtime/value.rb +21 -21
  347. data/lib/hecks/runtime.rb +7 -7
  348. data/lib/hecks/storehouse.rb +64 -64
  349. data/lib/hecks/version.rb +3 -3
  350. data/lib/hecks/vocabulary.rb +205 -4
  351. data/lib/hecks.rb +13 -11
  352. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +11 -11
  353. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +14 -12
  354. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +11 -11
  355. metadata +22 -2
@@ -1,11 +1,11 @@
1
1
  module Hecks
2
2
  module Fuzzing
3
3
  module Properties
4
- # A DRY RUN LEAVES NO TRACE — `Dispatcher#dry_run?`'s whole contract
4
+ # **A dry run leaves no trace** — `Dispatcher#dry_run?`'s whole contract
5
5
  # ("the command evaluated hypothetically, nothing saved or emitted,
6
6
  # no reaction"), held to the store rather than trusted: `Replay`
7
7
  # snapshots every instance and the event count on either side of
8
- # each `{"dry_run": …}` step, in a SEPARATE `dry_run_traces` array
8
+ # each `{"dry_run": …}` step, in a separate `dry_run_traces` array
9
9
  # (same order as `history[:dry_runs]`, never merged into it — that
10
10
  # array stays the exact `{verb:, ok:, error?:}` shape the compiled
11
11
  # Rust binary's own `dry_run` answers, so `spec/rust_conformance_
@@ -37,7 +37,7 @@ module Hecks
37
37
  # Dispatch-binding and mutation-recomputation properties: a saga/
38
38
  # policy dispatch is bound to the value its own with_spec names, and a
39
39
  # command's append/remove/multiply/clamp mutations land on the same
40
- # after-state an independent recomputation of the SAME rule produces.
40
+ # after-state an independent recomputation of the same rule produces.
41
41
 
42
42
  # Holds dispatch_binding_fidelity and mutations_match_recompute, plus
43
43
  # the independently-written re-derivations (#resolve_dispatch_binding,
@@ -49,17 +49,17 @@ module Hecks
49
49
  # are not claimable feature strings at all — see FEATURE_COVERAGE's
50
50
  # own comment on this entry). history[:saga_dispatches]/[:policy_dispatches]
51
51
  # (Registry#saga_dispatch_log/#policy_dispatch_log — additive,
52
- # Ruby-only, NEVER touching saga_log/reaction_log, the byte-for-
52
+ # Ruby-only, never touching saga_log/reaction_log, the byte-for-
53
53
  # byte shape spec/rust_conformance_spec.rb holds Rust to) each
54
- # carry the RAW inputs a dispatch's own args were resolved from,
54
+ # carry the raw inputs a dispatch's own args were resolved from,
55
55
  # captured live at the moment the resolution actually ran — a
56
56
  # saga's own memory keeps changing across a run, so re-deriving
57
- # from history[:saga_instances]'s FINAL memory (the only other
57
+ # from history[:saga_instances]'s final memory (the only other
58
58
  # place it would be visible) would grade the wrong moment,
59
59
  # lifecycle_guard_and_given_violations_are_refused's own false
60
60
  # positive one item earlier, in a different shape.
61
61
  #
62
- # #resolve_dispatch_binding/#resolve_trigger_binding are SEPARATE,
62
+ # #resolve_dispatch_binding/#resolve_trigger_binding are separate,
63
63
  # independently-written re-derivations of SagaInterpreter#
64
64
  # dispatch_args/PolicyInterpreter#trigger_args's own resolution —
65
65
  # never calling either method again, which would only ever agree
@@ -99,8 +99,8 @@ module Hecks
99
99
 
100
100
  # SagaInterpreter#dispatch_args's own 4-branch resolution,
101
101
  # reproduced independently: a literal, the correlation key itself,
102
- # the CURRENT triggering event's own payload, or — the fallback —
103
- # the saga's own carried memory (seeded from the STARTING event's
102
+ # the current triggering event's own payload, or — the fallback —
103
+ # the saga's own carried memory (seeded from the starting event's
104
104
  # payload, at begin_saga).
105
105
  def resolve_dispatch_binding(entry)
106
106
  entry[:with_spec].to_h do |key, value|
@@ -116,7 +116,7 @@ module Hecks
116
116
  # PolicyInterpreter#trigger_args's own 2-branch resolution — a
117
117
  # policy holds no correlation and no memory, so `payload` (the
118
118
  # triggering event's own payload, already merged with a fan-out
119
- # row's id when there is one) is the WHOLE source.
119
+ # row's id when there is one) is the whole source.
120
120
  def resolve_trigger_binding(entry)
121
121
  entry[:with_spec].to_h do |key, value|
122
122
  resolved = value.is_a?(Symbol) ? entry[:payload][value] : value
@@ -137,32 +137,77 @@ module Hecks
137
137
  #
138
138
  # `history[:mutation_traces]` (Replay's own bounded, additive
139
139
  # extension — see #build_mutation_trace's own comment) carries a
140
- # per-step before/after snapshot of the ENTITY ELEMENT an
140
+ # per-step before/after snapshot of the entity element an
141
141
  # entity-dispatched command's own mutations acted on, materialized
142
142
  # to plain data, plus the step's own raw args — the delta
143
143
  # `aggregation_matches_recompute` never had to ask for, because
144
- # count/median are pure functions of FINAL state and a mutation
144
+ # count/median are pure functions of final state and a mutation
145
145
  # is not (the same "captured live, not re-derived from final
146
146
  # state" lesson item 8's own saga_dispatch_log already learned).
147
147
  #
148
148
  # #recompute_append/#recompute_remove/#recompute_multiply/
149
- # #recompute_clamp are SEPARATE, independently-written
150
- # reproductions of MutationApplier#appended/#removed and
149
+ # #recompute_clamp/#recompute_set are separate, independently-
150
+ # written reproductions of EntityElement#appended_to_element/
151
+ # #removed_from_element/#apply_to_element's own `:set` branch and
151
152
  # CommandRules::Arithmetic#multiply/#clamp — never calling either
152
- # again, which would only ever agree with itself. `:set`/
153
- # `:increment`/`:decrement` are out of scope on purpose (the four
154
- # "vendored, not yet upstream" ops this item exists for); a
155
- # command mixing them with a recomputable op still gets the
156
- # recomputable one checked.
153
+ # again, which would only ever agree with itself. Every one of
154
+ # these five reproduces the entity-scoped applier specifically
155
+ # (`EntityElement#apply_to_element`, entity_element.rb), never
156
+ # the aggregate-level `MutationApplier#apply` (mutation_applier.rb)
157
+ # `build_mutation_trace` (replay.rb) only ever captures an
158
+ # entity-owned command's own mutation in the first place (that
159
+ # method's own `command_name&.include?(".")` guard), so there is
160
+ # no aggregate-level trace for this property to ever compare
161
+ # against `owner_for_verb`'s own comment draws the identical
162
+ # aggregate/owner distinction for `:append`. `:increment`/
163
+ # `:decrement` remain out of scope (CommandRules::Arithmetic#
164
+ # arithmetic's own VO-vs-scalar branching and INT64_RANGE overflow
165
+ # checks are a materially larger reproduction than the other five
166
+ # ops combined — scoped out of this pass, not forgotten; see
167
+ # docs/decisions/0056); a command mixing them with a recomputable
168
+ # op still gets the recomputable one checked.
169
+ #
170
+ # `:set` closes a real, separate gap from the other four — this is
171
+ # not "the fifth op of a symmetrical set." `self_consistency.rb`'s
172
+ # own rehydration/idempotency checks (lib/hecks/fuzzing/
173
+ # self_consistency.rb) can never catch a bug in `EntityElement#
174
+ # apply_to_element`'s `:set` branch (entity_element.rb) no matter
175
+ # how much they run: both the "live" state they snapshot and the
176
+ # "rehydrated" state they fold from `Ports::Persistence::
177
+ # AppendOnly`'s own journal trace back to the same single
178
+ # `step_apply_mutations` call (entity_interpreter.rb) — the
179
+ # journal holds the full POST-mutation state, not a delta (that
180
+ # file's own header), so a wrong `:set` result is already baked
181
+ # into both sides of that comparison before either one runs. A
182
+ # Ruby/Rust differential check is subject to the identical
183
+ # structural blind spot whenever Rust's own generated `:set`
184
+ # handling was derived from — and so shares — the same
185
+ # misunderstanding Ruby's implementation has. `#recompute_set`
186
+ # is a genuinely third computation, independent of both: it
187
+ # re-derives the expected value from the mutation's own declared
188
+ # `source` (an argument or a literal — matching `apply_to_
189
+ # element`'s own `resolve_source`, which unlike the aggregate-
190
+ # level `apply` never special-cases a `StateRef` source at all)
191
+ # and the step's own captured `args`, then compares against what
192
+ # the real dispatch actually stored — closing exactly the class
193
+ # of bug the self-consistency and (whenever Rust's codegen shares
194
+ # a ruby-derived misunderstanding) differential checks cannot see.
195
+ # `spec/fuzzing/mutation_set_self_correctness_spec.rb` proves this
196
+ # concretely: a defect planted directly in `EntityElement#apply_
197
+ # to_element`'s `:set` branch leaves `SelfConsistency.check`
198
+ # completely clean while `mutations_match_recompute` names it.
157
199
  #
158
200
  # `:unrecomputable` (never compared, never a finding) covers the
159
201
  # generator's own deliberate arg-malforming (`StepBuilder#malform`)
160
202
  # landing a non-Numeric amount/non-2-element bounds where
161
- # multiply/clamp need one — the SAME shape `guard_check`'s own
203
+ # multiply/clamp need one — the same shape `guard_check`'s own
162
204
  # AbsentArgument false positive taught: a step whose raw material
163
205
  # doesn't fit the op's own contract is inconclusive, not a claimed
164
- # mismatch.
165
- RECOMPUTABLE_MUTATION_OPS = %i[append remove multiply clamp].freeze
206
+ # mismatch. `#recompute_set` returns it for the same reason,
207
+ # whenever `Value.for_attribute`'s own coercion door raises on the
208
+ # resolved raw source (malformed fuzzer input, never a real
209
+ # corpus shape).
210
+ RECOMPUTABLE_MUTATION_OPS = %i[append remove multiply clamp set].freeze
166
211
 
167
212
  def mutations_match_recompute(history)
168
213
  bluebooks = history.fetch(:bluebooks)
@@ -196,12 +241,12 @@ module Hecks
196
241
 
197
242
  # `command_for_verb`'s own root-aggregate half (Guards) —
198
243
  # re-derived independently rather than read off `entry[:domain]`/
199
- # `entry[:aggregate]` (present on a REAL `build_mutation_trace`
244
+ # `entry[:aggregate]` (present on a real `build_mutation_trace`
200
245
  # entry, but not on every hand-built fixture this property is
201
246
  # tested against) so this works from `entry[:verb]` alone, the
202
247
  # one field every entry always carries. BUG#5's fix needs the
203
- # ROOT aggregate specifically — `Value.for_attribute` resolves a
204
- # value-object TYPE against the root's own namespace only, the
248
+ # root aggregate specifically — `Value.for_attribute` resolves a
249
+ # value-object type against the root's own namespace only, the
205
250
  # same reason `EntityElement#locate_chain` threads `root_aggregate`
206
251
  # through every hop separately from each hop's own `owner`.
207
252
  def aggregate_for_verb(bluebooks, verb)
@@ -211,7 +256,7 @@ module Hecks
211
256
  bluebooks[domain_name]&.aggregate(aggregate_name)
212
257
  end
213
258
 
214
- # `mutation.target`'s own DECLARING construct — the root
259
+ # `mutation.target`'s own declaring construct — the root
215
260
  # aggregate for an aggregate-owned command (`AddSlot`'s own
216
261
  # `:slots`), or the entity a dot-shaped command belongs to
217
262
  # (`Board.AddCard`'s own `:cards`, declared on `Board`, not on
@@ -242,50 +287,110 @@ module Hecks
242
287
  when :remove then recompute_remove(current, mutation.source, args)
243
288
  when :multiply then recompute_multiply(current, resolve_mutation_source(mutation.source, args))
244
289
  when :clamp then recompute_clamp(current, mutation.source)
290
+ when :set then recompute_set(mutation.source, args, aggregate, owner, mutation.target)
245
291
  end
246
292
  end
247
293
 
294
+ # `EntityElement#apply_to_element`'s own `:set` branch (entity_
295
+ # element.rb), reproduced independently — not `MutationApplier#
296
+ # apply`'s aggregate-level twin (mutation_applier.rb), a
297
+ # different method with a different shape: `build_mutation_trace`
298
+ # (replay.rb) only ever captures an entity-owned command's own
299
+ # mutation (`command_name&.include?(".")`, that method's own
300
+ # header), so `mutations_match_recompute` can only ever be
301
+ # checking `apply_to_element`'s branch, never `apply`'s — the
302
+ # same distinction `owner_for_verb`'s own comment already draws
303
+ # for `:append`. Confirmed by reading `apply_to_element` directly:
304
+ # its own `:set` branch resolves the source through `rules.
305
+ # resolve_source` unconditionally (no `StateRef` branch at all —
306
+ # unlike the aggregate-level `apply`, an entity-owned `sets` has
307
+ # no declared corpus site using `state(:x)` today, so this
308
+ # reproduces what ships, not a hypothetical), reads the
309
+ # attribute off `entity.attribute(mutation.target)` (the owning
310
+ # entity, `owner` here — `Board`, never `Workspace`), and coerces
311
+ # through `Value.for_attribute(aggregate, attribute, value)`
312
+ # (the root aggregate, for value-object namespace resolution
313
+ # only — `value_object_for(aggregate, attribute.type)` — the
314
+ # same aggregate/owner split `owner_for_verb`'s own comment
315
+ # explains for `recompute_append`). An earlier version of this
316
+ # method mirrored `MutationApplier#apply`'s `:set` branch instead
317
+ # (StateRef-aware, coerced against `aggregate.attribute` rather
318
+ # than `owner.attribute`) and false-positived on every real
319
+ # entity-owned `sets` in the corpus — `NestedPieces::Workspace.
320
+ # Board.Label` (`sets :label`) has no `:label` attribute on
321
+ # `Workspace` at all, so `Value.for(aggregate, :label, raw)`
322
+ # silently passed the raw, uncoerced String through instead of
323
+ # wrapping it as `BoardLabel`, and the comparison below then
324
+ # disagreed with the real, correctly-coerced `after` state on
325
+ # every single run — caught immediately by running this against
326
+ # `qa/stress_domains/nested_pieces` before this comment existed,
327
+ # not by inspection alone.
328
+ #
329
+ # Reusing `Value.for_attribute` for the coercion step, rather
330
+ # than re-deriving it, is the same "coercion is its own already-
331
+ # guaranteed door" reasoning `GUARANTEED_BY_CONSTRUCTION` states
332
+ # and `#coerce_recompute_append_arg` already leans on for BUG#5 —
333
+ # what this property exists to check is the source resolution and
334
+ # routing (did the right raw value, from the right source, land
335
+ # on the right target?), not whether `Value.for_attribute` itself
336
+ # coerces correctly (a separate, already-enforced concern).
337
+ # Rescued broadly: a `:set` mutation trace is only ever captured
338
+ # after a real, already-admitted dispatch (`Replay#build_
339
+ # mutation_trace`'s own header), so a raise here means this
340
+ # recomputation resolved the wrong raw material, not that the
341
+ # real dispatch was itself malformed — `:unrecomputable`, not a
342
+ # crash, the same discipline every other branch in this method
343
+ # already follows for the generator's own deliberate malforming.
344
+ def recompute_set(source, args, aggregate, owner, target)
345
+ raw = resolve_mutation_source(source, args)
346
+ attribute = owner&.attribute(target)
347
+ coerced = attribute ? Runtime::Value.for_attribute(aggregate, attribute, raw) : raw
348
+ Runtime::Value.materialize(coerced)
349
+ rescue StandardError
350
+ :unrecomputable
351
+ end
352
+
248
353
  # `EntityElement#appended_to_element`'s own field-mapping half,
249
- # reproduced: the field map resolved the SAME two-tier way
354
+ # reproduced: the field map resolved the same two-tier way
250
355
  # (`MutationApplier#resolve_append_source` — a caller-supplied
251
356
  # arg, or the entity's own current field), then appended. (An
252
- # entity-dispatched command's own mutations DO reach here —
357
+ # entity-dispatched command's own mutations do reach here —
253
358
  # `#build_mutation_trace`'s own comment describing them as never
254
359
  # reaching "the entity_element branch" means `MutationApplier#
255
- # appended`'s own AGGREGATE-level entity_element fallback
360
+ # appended`'s own aggregate-level entity_element fallback
256
361
  # specifically, which really is unreached from here; entity-owned
257
362
  # append dispatches go through `EntityElement#appended_to_element`
258
- # instead, and DO reach this method.)
363
+ # instead, and do reach this method.)
259
364
  #
260
365
  # BUG#5 — an entity-owned `:append` whose target field is itself
261
366
  # value-object-typed (`Board.AddCard`'s own `sets :cards, append:
262
367
  # { sequence: :sequence }`, `CardSequence`-typed). A caller-
263
- # supplied arg reaches the REAL applier (`EntityElement#
368
+ # supplied arg reaches the real applier (`EntityElement#
264
369
  # appended_to_element`) already coerced: `Interpreting#
265
370
  # coerce_declared_arguments` runs `Value.for_attribute` over
266
- # EVERY arg the acting command itself declares, BEFORE dispatch
371
+ # every arg the acting command itself declares, before dispatch
267
372
  # ever reaches a mutation applier at all — independent of, and
268
373
  # earlier than, anything `appended_to_element`'s own value_object
269
- # check does. `before_scope[source]` (the entity's OWN current
374
+ # check does. `before_scope[source]` (the entity's own current
270
375
  # field) needs no such re-coercion here: it's already the
271
- # MATERIALIZED shape `build_mutation_trace` snapshotted it in
376
+ # materialized shape `build_mutation_trace` snapshotted it in
272
377
  # (`Value.materialize`, same as `entry[:after]`), not a raw value
273
378
  # sitting behind a live `Value`.
274
379
  #
275
- # BUG#12 — `owner`/`target` (new here) let this ALSO reproduce
380
+ # BUG#12 — `owner`/`target` (new here) let this also reproduce
276
381
  # `EntityElement#fill_declared_defaults`'s own entity-nested-in-
277
382
  # entity fallback (`appended_to_element`'s `else` branch, when
278
383
  # the appended element is itself an entity — `Card`, nested
279
384
  # inside `Board` — not a value object): `owner.attribute(target)
280
385
  # &.type` names the appended element's own type; when that names
281
386
  # an entity of `aggregate` rather than a value object, every one
282
- # of ITS OWN declared attributes `fields` doesn't already hold
387
+ # of its own declared attributes `fields` doesn't already hold
283
388
  # gets `Instance.default_for`'s own default — reused, not
284
389
  # reimplemented, for the identical "never agree with itself"
285
390
  # reason BUG#5's own coercion re-derivation above already gives:
286
391
  # `Instance.default_for` is pre-existing, independently-tested
287
392
  # machinery (an ordinary aggregate's own creation already runs
288
- # through it via `Instance.defaults`), not the NEW glue
393
+ # through it via `Instance.defaults`), not the new glue
289
394
  # (`fill_declared_defaults` itself) this property exists to
290
395
  # catch a drift in.
291
396
  def recompute_append(current, source_map, before_scope, args, aggregate, command, owner = aggregate, target = nil)
@@ -305,8 +410,8 @@ module Hecks
305
410
  # isn't an entity nested directly under `owner` (a value object,
306
411
  # or nothing declared at all — `owner.attribute` answering `nil`
307
412
  # for a target the DSL itself would already have refused at
308
- # build time). `owner.entities`, NOT `aggregate.entities` — a
309
- # piece nested inside a piece is a child of the OWNING entity
413
+ # build time). `owner.entities`, not `aggregate.entities` — a
414
+ # piece nested inside a piece is a child of the owning entity
310
415
  # (`Card` is `Board.entities`, never `Workspace.entities`), the
311
416
  # same distinction `EntityElement#appended_to_element`'s own fix
312
417
  # draws.
@@ -335,8 +440,8 @@ module Hecks
335
440
  # `Interpreting#coerce_declared_arguments`'s own coercion,
336
441
  # reproduced independently (never calling it again, the same
337
442
  # "never agree with itself" rule this whole module's header
338
- # comment gives) — a raw arg is coerced ONLY when its own name
339
- # (`source`) is one of the ACTING COMMAND's own declared
443
+ # comment gives) — a raw arg is coerced only when its own name
444
+ # (`source`) is one of the acting command's own declared
340
445
  # attributes, exactly the condition that method checks before a
341
446
  # real dispatch ever coerces it either. `command.attribute(source)`
342
447
  # answering `nil` (a source that names no declared attribute —
@@ -367,7 +472,7 @@ module Hecks
367
472
  # reproduced on plain materialized data instead of a real Value:
368
473
  # a single-numeric-field Hash (the VO-typed case — ListCount, one
369
474
  # Integer field) scales that field ; a bare Numeric scales itself.
370
- # `current ||= 0` — the SAME phantom-field fallback #multiply
475
+ # `current ||= 0` — the same phantom-field fallback #multiply
371
476
  # itself already gives (unaffected by this session's #clamp fix,
372
477
  # since #multiply never needed one).
373
478
  def recompute_multiply(current, amount)
@@ -387,8 +492,8 @@ module Hecks
387
492
  end
388
493
 
389
494
  # `CommandRules::Arithmetic#clamp`'s own two branches, reproduced
390
- # the same way #recompute_multiply is — including THIS SESSION'S
391
- # OWN `current ||= 0` fix (command_rules/arithmetic.rb), the one
495
+ # the same way #recompute_multiply is — including this session's
496
+ # own `current ||= 0` fix (command_rules/arithmetic.rb), the one
392
497
  # arithmetic op that didn't have it until now. `mutation.source`
393
498
  # is always a literal `[min, max]`, never an argument reference
394
499
  # (MutationApplier's own comment on why `resolve_source` is
@@ -411,18 +516,18 @@ module Hecks
411
516
  end
412
517
 
413
518
  # `CommandRules::Arithmetic#resolve_source`, reproduced: a
414
- # mutation's source is either the NAME OF AN ARGUMENT or a
415
- # LITERAL, told apart by type.
519
+ # mutation's source is either the name of an argument or a
520
+ # literal, told apart by type.
416
521
  def resolve_mutation_source(source, args)
417
522
  source.is_a?(Symbol) ? args[source] : source
418
523
  end
419
524
 
420
- # A generated step's own `args` arrive with STRING keys on every
525
+ # A generated step's own `args` arrive with string keys on every
421
526
  # nested Hash (the wire/JSON shape `spec/corpus/*.json` already
422
527
  # uses) while `history[:mutation_traces]`' own materialized
423
- # before/after state carries SYMBOL keys throughout (Runtime::
528
+ # before/after state carries symbol keys throughout (Runtime::
424
529
  # Value.materialize's own convention) — two hashes holding the
425
- # identical fact compare UNEQUAL by Ruby's own `Hash#==` unless
530
+ # identical fact compare unequal by Ruby's own `Hash#==` unless
426
531
  # both sides are normalized the same way first. Recursive, since
427
532
  # an appended/removed element can itself nest a value object
428
533
  # (RemoveTag's own `Tag` argument, `{"key"=>..., "value"=>...}`).
@@ -12,18 +12,18 @@ module Hecks
12
12
  # verb-to-declaration lookups (#command_for_verb,
13
13
  # #effective_guard_descriptions) each resolves a refusal against.
14
14
  module Guards
15
- # `Query#options`' OTHER HALF — TenantScope.apply's own contract
15
+ # `Query#options`' other half — TenantScope.apply's own contract
16
16
  # (tenant_scope.rb), independently restated as a property rather
17
17
  # than exercised only through whatever the generator happens to
18
- # try. NOT closed by the generator here on purpose: SafeDepositBox.
18
+ # try. Not closed by the generator here on purpose: SafeDepositBox.
19
19
  # Rented — the only real corpus query declaring `authorize` at
20
- # all — declares ZERO attributes of its own, so StepBuilder#args_for
20
+ # all — declares zero attributes of its own, so StepBuilder#args_for
21
21
  # always hands it `{}` and TenantScope.apply refuses every
22
22
  # generated attempt, unconditionally (confirmed: no successful ask
23
23
  # against an authorize-bearing query reaches this property via the
24
24
  # standard battery today). Extending the generator to invent a
25
25
  # `tenant:` value ran into a separate, real finding along the way —
26
- # SafeDepositBox is COMPOSITE-identified (`identified_by` is nil
26
+ # SafeDepositBox is composite-identified (`identified_by` is nil
27
27
  # for it — Runtime::Identified#derive_identity), so the generator's
28
28
  # existing `known_ids` pool (keyed by `aggregate.identified_by ||
29
29
  # "id"`) tracks a stray, never-real scalar for it rather than its
@@ -37,10 +37,10 @@ module Hecks
37
37
  # where a `tenant:` arg came from.
38
38
  #
39
39
  # Two claims, matching TenantScope.apply's own two branches: every
40
- # SUCCESSFUL answer's own tenant field agrees with the tenant arg
40
+ # successful answer's own tenant field agrees with the tenant arg
41
41
  # given (the WhereClause TenantScope injects is a Symbol reference
42
- # into args, resolved dynamically — this checks the OUTCOME, not
43
- # re-deriving that resolution) ; every ask MISSING a required
42
+ # into args, resolved dynamically — this checks the outcome, not
43
+ # re-deriving that resolution) ; every ask missing a required
44
44
  # tenant: refuses with the declared wording, never succeeds. A
45
45
  # refusal for an unrelated reason with the tenant arg present is
46
46
  # not this property's claim either way — skipped, not graded.
@@ -92,14 +92,14 @@ module Hecks
92
92
  offenders.empty? || offenders.join("; ")
93
93
  end
94
94
 
95
- # EVERY GIVEN/ENSURES REFUSAL A RUN ACTUALLY RAISED NAMES A RULE
96
- # THE COMMAND ACTUALLY DECLARES. `GivenNotMet`/`EnsuresNotMet` both
95
+ # Every given/ensures refusal a run actually raised names a rule
96
+ # the command actually declares. `GivenNotMet`/`EnsuresNotMet` both
97
97
  # quote their guard's own `description` verbatim
98
98
  # (command_rules/admissibility.rb: `"#{command.hecks_name} refused
99
- # — #{given.description}"`) — the SAME text `behavior.bluebook`'s
99
+ # — #{given.description}"`) — the same text `behavior.bluebook`'s
100
100
  # own `Rule`/Command.Ensure hold as `Rule#description`, so a
101
101
  # refusal whose quoted text is not among the refusing command's
102
- # OWN `guard_descriptions` (Behaviour::Command, both givens and
102
+ # own `guard_descriptions` (Behaviour::Command, both givens and
103
103
  # ensures) is either a stale message surviving a renamed rule, a
104
104
  # rule firing against the wrong command's own guard set, or the
105
105
  # wording drifting out from under the declaration it is supposed
@@ -107,7 +107,7 @@ module Hecks
107
107
  # guards, some through a cross-aggregate dereference) are exactly
108
108
  # the surface this exists to hold to its word.
109
109
  #
110
- # `kind:` is what tells a guard refusal apart from the FOUR other
110
+ # `kind:` is what tells a guard refusal apart from the four other
111
111
  # `RefusalWording` templates sharing the identical "X refused — Y"
112
112
  # shape (LifecycleRefused/transition_blocked, both TypeMismatch
113
113
  # object-reference templates, Unauthorized/role_mismatch) — see
@@ -138,27 +138,27 @@ module Hecks
138
138
  offenders.empty? || offenders.join("; ")
139
139
  end
140
140
 
141
- # ANGLE-8's OWN WRITE-SIDE HALF. `authorize_scopes_or_refuses`
141
+ # Angle-8's own write-side half. `authorize_scopes_or_refuses`
142
142
  # (above) enforces `TenantScope.apply`'s boundary, and that
143
- # boundary exists ONLY for queries/read models — `authorize
143
+ # boundary exists only for queries/read models — `authorize
144
144
  # policy, tenant: field` is a word `QuerySpecification::Common::
145
145
  # DSL#authorize_impl` grants to `QueryBuilder`/`ReadModelBuilder`
146
146
  # alone; `CommandBuilder` never includes that module, so no
147
147
  # bluebook can declare it on a command at all (confirmed by
148
- # reading the grammar directly, not inferred). A WRITE that
148
+ # reading the grammar directly, not inferred). A write that
149
149
  # carries a `reference_to` from one tenant-scoped record into
150
- # another's is checked by NOTHING at dispatch time: `TenantScope`
150
+ # another's is checked by nothing at dispatch time: `TenantScope`
151
151
  # never runs for a command, and no runtime `given`/`ensures`
152
152
  # anywhere in this corpus reads a cross-aggregate tenant field
153
153
  # either. `qa/stress_domains/tenant_ledger` exists to give this
154
154
  # property a real place to fire.
155
155
  #
156
- # THE RULE: an aggregate's own declared TENANT FIELD is whichever
157
- # field one of ITS OWN queries names in `authorize policy, tenant:
156
+ # The rule: an aggregate's own declared tenant field is whichever
157
+ # field one of its own queries names in `authorize policy, tenant:
158
158
  # :field` — the exact same declaration `authorize_scopes_or_
159
159
  # refuses` reads off a query above, reused here to name a field
160
- # on the AGGREGATE ITSELF that stores the tenant it belongs to.
161
- # For every STORED record (`history[:instances]` — a refused
160
+ # on the aggregate itself that stores the tenant it belongs to.
161
+ # For every stored record (`history[:instances]` — a refused
162
162
  # dispatch never writes one, so "a refusal is correct behaviour,
163
163
  # not a finding" holds by construction, the same way `history
164
164
  # [:instances]` already guarantees this for `stored_records_
@@ -166,25 +166,25 @@ module Hecks
166
166
  # tenant field, walk every `reference_to`-typed attribute it
167
167
  # carries (`Bluebook::Reference` — "a reference IS the id",
168
168
  # value/coercion.rb's own header, so the stored value is always a
169
- # plain id, never a nested payload) pointing at ANOTHER aggregate
170
- # that ALSO declares a tenant field: if the referenced record's
169
+ # plain id, never a nested payload) pointing at another aggregate
170
+ # that also declares a tenant field: if the referenced record's
171
171
  # own tenant value disagrees with the referencing record's own
172
172
  # tenant value, the write crossed a tenant boundary and nothing
173
173
  # refused it — a finding.
174
174
  #
175
- # A DANGLING/UNRESOLVABLE REFERENCE IS SKIPPED — a different,
175
+ # A dangling/unresolvable reference is skipped — a different,
176
176
  # existence-shaped property's claim, not this one's (the same
177
177
  # "inconclusive, not a claimed pass" restraint `lifecycle_guard_
178
178
  # and_given_violations_are_refused` already documents for a
179
179
  # differently-shaped case). Comparison goes through `Ports::
180
- # Query::InMemory.comparable` (the SAME normalization `authorize_
180
+ # Query::InMemory.comparable` (the same normalization `authorize_
181
181
  # scopes_or_refuses` already applies to a query row's own tenant
182
182
  # field, just above) rather than `Runtime::Value#==` directly —
183
- # two single-attribute value objects with the SAME scalar but
184
- # DIFFERENT declared names (`LedgerRegion`/`TransferRegion`, this
183
+ # two single-attribute value objects with the same scalar but
184
+ # different declared names (`LedgerRegion`/`TransferRegion`, this
185
185
  # domain's own pair — a value object is always declared inside
186
186
  # the aggregate that owns it, so two independently tenant-scoped
187
- # aggregates can never share one) compare UNEQUAL under `Value#==`
187
+ # aggregates can never share one) compare unequal under `Value#==`
188
188
  # (`type_name` is part of that equality) despite meaning the
189
189
  # identical tenant, which would make every same-tenant write a
190
190
  # false positive.
@@ -230,7 +230,7 @@ module Hecks
230
230
  offenders.empty? || offenders.join("; ")
231
231
  end
232
232
 
233
- # THE FIELD AN AGGREGATE'S OWN QUERY NAMES AS TENANT-SCOPING
233
+ # The field an aggregate's own query names as tenant-scoping
234
234
  # shared by `commands_respect_tenant_scope` above for both sides
235
235
  # of a `reference_to`. `nil` for an aggregate with no `authorize
236
236
  # ..., tenant:` on any of its own queries — not every aggregate
@@ -241,16 +241,16 @@ module Hecks
241
241
  authorization&.tenant&.to_sym
242
242
  end
243
243
 
244
- # A DECLARED PROCESS MANAGER'S OWN COMMAND — `command.hecks_name`,
244
+ # A declared process manager's own command — `command.hecks_name`,
245
245
  # or an entity's own if the verb's second component is itself
246
246
  # dotted (`Aggregate.Entity.Command`, the same two shapes
247
247
  # `Dispatcher#dispatch` itself branches on). Shared by the guard
248
248
  # property above and available for anything else that needs to go
249
249
  # from a replayed verb back to its declaration.
250
250
  #
251
- # RESOLVED AGAINST `bluebooks` (the FULL map, `history[:bluebooks]`
251
+ # Resolved against `bluebooks` (the full map, `history[:bluebooks]`
252
252
  # — every loaded domain, keyed by name), never a single assumed
253
- # bluebook: a verb names its OWN domain (`Naming.split_verb`'s
253
+ # bluebook: a verb names its own domain (`Naming.split_verb`'s
254
254
  # first element), and that domain is not always the one Replay
255
255
  # happens to expose as `history[:bluebook]`. A fuzz run against
256
256
  # `lib/hecks/grammar` (Expression + Translation, in load
@@ -258,14 +258,14 @@ module Hecks
258
258
  # refusal read as "no declared command resolves that verb" purely
259
259
  # because `history[:bluebook]` was Expression, not Translation; the
260
260
  # refusal was real, this property's own domain resolution was not.
261
- # A DELEGATING DOOR REFUSES WITH ITS TARGET'S OWN WORDS. `delegates_to`
261
+ # A delegating door refuses with its target's own words. `delegates_to`
262
262
  # (CommandBuilder#delegates_to_impl) hands the whole dispatch to one
263
263
  # entity command, and that command's given is what refuses — raised
264
264
  # back through the door, in the door's name (chess: `Game.MoveKnight
265
265
  # refused — "it is that color's turn"`, a given Knight.Move declares
266
266
  # and MoveKnight, a pure passthrough, never could). Read the door's
267
267
  # own guards first, then every delegation target's; an offence is
268
- # only a description NEITHER declares. Found live mining chess's
268
+ # only a description neither declares. Found live mining chess's
269
269
  # history: every refused move through a door read as undeclared.
270
270
  def effective_guard_descriptions(bluebooks, verb, command)
271
271
  own = command.guard_descriptions
@@ -296,9 +296,9 @@ module Hecks
296
296
  end
297
297
  end
298
298
 
299
- # `guard_refusals_are_declared`'s OWN OPPOSITE DIRECTION. That
299
+ # `guard_refusals_are_declared`'s own opposite direction. That
300
300
  # property is passive and one-directional — for a refusal that
301
- # ALREADY HAPPENED, is the quoted text real declared text? It says
301
+ # already happened, is the quoted text real declared text? It says
302
302
  # nothing about a guard that should have refused and silently did
303
303
  # not — a call site that stopped calling enforce_givens/enforce_
304
304
  # lifecycle_guard would never appear in history[:refusals] at all,
@@ -306,7 +306,7 @@ module Hecks
306
306
  #
307
307
  # This one calls Admissibility#enforce_givens (which itself folds
308
308
  # in #enforce_lifecycle_guard whenever `declaring:` is passed)
309
- # DIRECTLY, against Replay's own pre-dispatch snapshot
309
+ # directly, against Replay's own pre-dispatch snapshot
310
310
  # (history[:guard_checks], one bounded, additive extension — see
311
311
  # that file's own comment at the capture site) — an independent
312
312
  # recomputation, not grading production against itself, the same
@@ -316,27 +316,27 @@ module Hecks
316
316
  # could mutate anything a cross-aggregate given dereferences) is
317
317
  # compared against `actual_refused` (GivenNotMet/LifecycleRefused
318
318
  # specifically — Replay's own comment on GUARD_REFUSAL_CLASSES
319
- # explains why ANY other refusal class, or an outright success,
319
+ # explains why any other refusal class, or an outright success,
320
320
  # both count as "the guard did not fire," since enforce_givens
321
- # runs FIRST in DISPATCH_ORDER).
321
+ # runs first in DISPATCH_ORDER).
322
322
  #
323
323
  # Aggregate#preconditions closes for free alongside this — a
324
324
  # no-block `given` reference (CommandBuilder#given) pushes the
325
- # SAME Given struct object `enforce_givens` already iterates
325
+ # same Given struct object `enforce_givens` already iterates
326
326
  # command.givens for, so there is no separate runtime path a
327
327
  # property could exercise beyond what this already reaches.
328
328
  # Entity#preconditions closes the identical way, one level down
329
329
  # (ADR 0028) — a piece's own bare `given` reference pushes the
330
- # SAME Given struct onto ITS OWN referencing command's givens,
330
+ # same Given struct onto its own referencing command's givens,
331
331
  # so LedgerEntry's own Amend/Reverse (banking) already exercise
332
332
  # this through the exact mechanism above, no separate path.
333
333
  #
334
334
  # Real targets: Account.Debit/CloseAccount (`from:` guards),
335
335
  # Credit/Debit (the named-once `given("customer is active")`
336
336
  # precondition) — FreezeAccount deliberately references the
337
- # DIFFERENT named precondition `"customer is not closed"` instead
337
+ # different named precondition `"customer is not closed"` instead
338
338
  # (a suspended customer must still be freezable), so it is not a
339
- # `"customer is active"` example, just the same MECHANISM.
339
+ # `"customer is active"` example, just the same mechanism.
340
340
  def lifecycle_guard_and_given_violations_are_refused(history)
341
341
  offenders = history.fetch(:guard_checks).filter_map do |check|
342
342
  next if check[:recomputed_refused] == check[:actual_refused]