hecks 2.7.0 → 2.9.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 (463) hide show
  1. checksums.yaml +4 -4
  2. data/exe/hecks +11 -0
  3. data/lib/hecks/adapters/driven/claude_code.rb +4 -104
  4. data/lib/hecks/adapters/driven/d1.rb +44 -237
  5. data/lib/hecks/adapters/driven/folder.rb +17 -76
  6. data/lib/hecks/adapters/driven/google_authentication.rb +15 -49
  7. data/lib/hecks/adapters/driven/governance_authorization.rb +23 -70
  8. data/lib/hecks/adapters/driven/heki/journal.rb +8 -41
  9. data/lib/hecks/adapters/driven/heki/saga_store.rb +12 -54
  10. data/lib/hecks/adapters/driven/heki/snapshot.rb +4 -12
  11. data/lib/hecks/adapters/driven/heki.rb +13 -42
  12. data/lib/hecks/adapters/driven/identity_registry.rb +3 -14
  13. data/lib/hecks/adapters/driven/in_memory_ordering.rb +9 -26
  14. data/lib/hecks/adapters/driven/in_process_key_vault.rb +2 -14
  15. data/lib/hecks/adapters/driven/lambda/client.rb +10 -51
  16. data/lib/hecks/adapters/driven/lambda.rb +10 -84
  17. data/lib/hecks/adapters/driven/local_storage.rb +5 -40
  18. data/lib/hecks/adapters/driven/memory.rb +18 -99
  19. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +5 -21
  20. data/lib/hecks/adapters/driven/postgres/codec.rb +6 -28
  21. data/lib/hecks/adapters/driven/postgres/outbox.rb +3 -12
  22. data/lib/hecks/adapters/driven/postgres/reconnect.rb +4 -30
  23. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +34 -108
  24. data/lib/hecks/adapters/driven/postgres.rb +145 -134
  25. data/lib/hecks/adapters/driven/postgres_era.adapter +4 -15
  26. data/lib/hecks/adapters/driven/prism.rb +7 -30
  27. data/lib/hecks/adapters/driven/sql_query_builder.rb +21 -121
  28. data/lib/hecks/adapters/driven/sqlite/codec.rb +8 -40
  29. data/lib/hecks/adapters/driven/sqlite/projection.rb +26 -82
  30. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +40 -84
  31. data/lib/hecks/adapters/driven/sqlite.rb +128 -65
  32. data/lib/hecks/adapters/driven/tenant_provisioner.rb +15 -37
  33. data/lib/hecks/adapters/driven.rb +5 -36
  34. data/lib/hecks/adapters/driving/github_webhook.rb +8 -76
  35. data/lib/hecks/behaviors/dsl.rb +7 -25
  36. data/lib/hecks/behaviors/expectations.rb +32 -142
  37. data/lib/hecks/behaviors/ir.rb +7 -20
  38. data/lib/hecks/behaviors/rspec.rb +6 -17
  39. data/lib/hecks/behaviors/runner.rb +10 -34
  40. data/lib/hecks/behaviors.rb +4 -8
  41. data/lib/hecks/bluebook/aggregate.rb +9 -75
  42. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +9 -38
  43. data/lib/hecks/bluebook/assembly/build.rb +4 -15
  44. data/lib/hecks/bluebook/assembly/contract.rb +15 -136
  45. data/lib/hecks/bluebook/assembly/contracts.rb +54 -174
  46. data/lib/hecks/bluebook/assembly/marks.rb +31 -223
  47. data/lib/hecks/bluebook/assembly/specializer.rb +6 -52
  48. data/lib/hecks/bluebook/assembly.rb +6 -45
  49. data/lib/hecks/bluebook/attribute.rb +12 -61
  50. data/lib/hecks/bluebook/behaviour/aggregate.rb +10 -39
  51. data/lib/hecks/bluebook/behaviour/attribute.rb +3 -14
  52. data/lib/hecks/bluebook/behaviour/chapter.rb +17 -42
  53. data/lib/hecks/bluebook/behaviour/command.rb +14 -66
  54. data/lib/hecks/bluebook/behaviour/domain_port.rb +4 -20
  55. data/lib/hecks/bluebook/behaviour/entity.rb +7 -27
  56. data/lib/hecks/bluebook/behaviour/hexagon.rb +23 -41
  57. data/lib/hecks/bluebook/behaviour/lifecycle.rb +15 -40
  58. data/lib/hecks/bluebook/behaviour/policy.rb +23 -52
  59. data/lib/hecks/bluebook/behaviour/process_manager.rb +30 -57
  60. data/lib/hecks/bluebook/behaviour/read_model.rb +38 -26
  61. data/lib/hecks/bluebook/behaviour/traits.rb +27 -64
  62. data/lib/hecks/bluebook/behaviour/value_object.rb +5 -24
  63. data/lib/hecks/bluebook/capabilities.rb +17 -49
  64. data/lib/hecks/bluebook/chapter.rb +10 -48
  65. data/lib/hecks/bluebook/command.rb +23 -81
  66. data/lib/hecks/bluebook/domain_port.rb +14 -63
  67. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +50 -229
  68. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +50 -377
  69. data/lib/hecks/bluebook/dsl/attribute_collector.rb +33 -204
  70. data/lib/hecks/bluebook/dsl/binding_proxy.rb +17 -65
  71. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +86 -708
  72. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +56 -170
  73. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +1 -0
  74. data/lib/hecks/bluebook/dsl/command_builder.rb +74 -546
  75. data/lib/hecks/bluebook/dsl/const_shim.rb +8 -41
  76. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +12 -87
  77. data/lib/hecks/bluebook/dsl/entity_builder.rb +44 -330
  78. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +20 -269
  79. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +26 -189
  80. data/lib/hecks/bluebook/dsl/identity_declaration.rb +13 -88
  81. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +6 -20
  82. data/lib/hecks/bluebook/dsl/policy_builder.rb +16 -88
  83. data/lib/hecks/bluebook/dsl/port_builder.rb +2 -6
  84. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +22 -96
  85. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +31 -184
  86. data/lib/hecks/bluebook/dsl/query_builder.rb +11 -42
  87. data/lib/hecks/bluebook/dsl/read_model_builder.rb +75 -201
  88. data/lib/hecks/bluebook/dsl/rule_reference.rb +22 -163
  89. data/lib/hecks/bluebook/dsl/translation_builder.rb +28 -114
  90. data/lib/hecks/bluebook/dsl/value_object_builder.rb +10 -124
  91. data/lib/hecks/bluebook/dsl/word_gate.rb +16 -162
  92. data/lib/hecks/bluebook/dsl/world_builder.rb +19 -106
  93. data/lib/hecks/bluebook/entity.rb +13 -71
  94. data/lib/hecks/bluebook/expression/ast_json.rb +24 -190
  95. data/lib/hecks/bluebook/expression/ast_reader.rb +12 -54
  96. data/lib/hecks/bluebook/expression/canonical_form.rb +11 -88
  97. data/lib/hecks/bluebook/expression/evaluator.rb +47 -295
  98. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +30 -147
  99. data/lib/hecks/bluebook/expression/resolver.rb +37 -660
  100. data/lib/hecks/bluebook/hexagon.rb +5 -28
  101. data/lib/hecks/bluebook/lifecycle.rb +5 -16
  102. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -7
  103. data/lib/hecks/bluebook/meta_validator/judge.rb +111 -361
  104. data/lib/hecks/bluebook/meta_validator/plan.rb +33 -162
  105. data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -7
  106. data/lib/hecks/bluebook/meta_validator/readings.rb +56 -342
  107. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +24 -176
  108. data/lib/hecks/bluebook/meta_validator/shapes.rb +54 -268
  109. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +47 -308
  110. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +7 -43
  111. data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -18
  112. data/lib/hecks/bluebook/meta_validator.rb +79 -349
  113. data/lib/hecks/bluebook/model_check/client_profile.rb +10 -91
  114. data/lib/hecks/bluebook/model_check.rb +102 -600
  115. data/lib/hecks/bluebook/pattern_subset.rb +11 -69
  116. data/lib/hecks/bluebook/process_manager.rb +13 -83
  117. data/lib/hecks/bluebook/project_loader.rb +3 -39
  118. data/lib/hecks/bluebook/project_register.rb +12 -60
  119. data/lib/hecks/bluebook/query.rb +9 -48
  120. data/lib/hecks/bluebook/read_model.rb +57 -81
  121. data/lib/hecks/bluebook/reference.rb +7 -41
  122. data/lib/hecks/bluebook/smoke_test.rb +8 -98
  123. data/lib/hecks/bluebook/synthesizer.rb +13 -62
  124. data/lib/hecks/bluebook/translation.rb +26 -62
  125. data/lib/hecks/bluebook/value_object.rb +11 -36
  126. data/lib/hecks/bluebook.rb +5 -22
  127. data/lib/hecks/cache_dir.rb +84 -0
  128. data/lib/hecks/cli/document.rb +86 -0
  129. data/lib/hecks/cli/ir.rb +45 -0
  130. data/lib/hecks/cli/mcp.rb +31 -0
  131. data/lib/hecks/cli/mcp_door.rb +366 -0
  132. data/lib/hecks/cli/model_check.rb +203 -0
  133. data/lib/hecks/cli/project_cli.rb +90 -0
  134. data/lib/hecks/cli/project_diagrams.rb +36 -0
  135. data/lib/hecks/cli/run.rb +173 -0
  136. data/lib/hecks/cli/smoke_test.rb +45 -0
  137. data/lib/hecks/cli/stores.rb +53 -0
  138. data/lib/hecks/cli.rb +160 -0
  139. data/lib/hecks/construct.rb +8 -34
  140. data/lib/hecks/deploy/bluebook/deploy.bluebook +33 -142
  141. data/lib/hecks/deploy/bluebook/deploy.hecksagon +2 -9
  142. data/lib/hecks/embryonaut_bluebook/lock.rb +3 -35
  143. data/lib/hecks/embryonaut_bluebook/shape.rb +1 -17
  144. data/lib/hecks/embryonaut_bluebook/vendor.rb +8 -53
  145. data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +4 -19
  146. data/lib/hecks/embryonaut_bluebook.rb +4 -71
  147. data/lib/hecks/facade/cli_door.rb +24 -106
  148. data/lib/hecks/facade/cli_runner.rb +28 -172
  149. data/lib/hecks/facade/command_request.rb +13 -33
  150. data/lib/hecks/facade/handle.rb +34 -121
  151. data/lib/hecks/facade/json_door.rb +31 -146
  152. data/lib/hecks/facade/surface/aggregate_door.rb +39 -104
  153. data/lib/hecks/facade/surface/chapter.rb +10 -54
  154. data/lib/hecks/facade/surface.rb +8 -23
  155. data/lib/hecks/facade.rb +10 -25
  156. data/lib/hecks/forms/app.rb +15 -70
  157. data/lib/hecks/forms/command_form_renderer.rb +10 -29
  158. data/lib/hecks/forms/field_renderer.rb +17 -156
  159. data/lib/hecks/forms/field_shape.rb +30 -222
  160. data/lib/hecks/forms/html.rb +12 -47
  161. data/lib/hecks/forms/index_renderer.rb +1 -4
  162. data/lib/hecks/forms/page.rb +3 -9
  163. data/lib/hecks/forms/params.rb +14 -60
  164. data/lib/hecks/forms/port_argument.rb +2 -19
  165. data/lib/hecks/forms/query_form_renderer.rb +4 -79
  166. data/lib/hecks/forms/record_renderer.rb +8 -75
  167. data/lib/hecks/forms/record_table.rb +7 -50
  168. data/lib/hecks/forms/reference_options.rb +3 -30
  169. data/lib/hecks/forms/value_object_shape.rb +8 -35
  170. data/lib/hecks/forms.rb +5 -33
  171. data/lib/hecks/fqn.rb +5 -12
  172. data/lib/hecks/framework/bluebook/compliance.bluebook +16 -65
  173. data/lib/hecks/framework/bluebook/console_settings.bluebook +47 -134
  174. data/lib/hecks/framework/bluebook/framework.hecksagon +5 -14
  175. data/lib/hecks/framework/bluebook/governance.bluebook +13 -34
  176. data/lib/hecks/framework/bluebook/identity.bluebook +11 -26
  177. data/lib/hecks/framework/bluebook/privacy.bluebook +8 -33
  178. data/lib/hecks/framework.rb +11 -80
  179. data/lib/hecks/freezer.rb +5 -39
  180. data/lib/hecks/grammar/expression.bluebook +15 -40
  181. data/lib/hecks/grammar/translation.bluebook +3 -6
  182. data/lib/hecks/grammar.rb +16 -41
  183. data/lib/hecks/ir.rb +14 -83
  184. data/lib/hecks/language/adapter.bluebook +2 -7
  185. data/lib/hecks/language/bluebook/aggregate.bluebook +56 -221
  186. data/lib/hecks/language/bluebook/attaches/paging.bluebook +6 -15
  187. data/lib/hecks/language/bluebook/bluebook.bluebook +15 -83
  188. data/lib/hecks/language/bluebook/bluebook.hecksagon +3 -15
  189. data/lib/hecks/language/bluebook/command.bluebook +27 -144
  190. data/lib/hecks/language/bluebook/entity.bluebook +24 -97
  191. data/lib/hecks/language/bluebook/policy.bluebook +16 -67
  192. data/lib/hecks/language/bluebook/process_manager.bluebook +28 -151
  193. data/lib/hecks/language/bluebook/projection.bluebook +126 -60
  194. data/lib/hecks/language/bluebook/query.bluebook +12 -43
  195. data/lib/hecks/language/bluebook/shape.bluebook +23 -73
  196. data/lib/hecks/language/bluebook/syntax.bluebook +51 -262
  197. data/lib/hecks/language/bluebook/vocabulary.bluebook +98 -197
  198. data/lib/hecks/language/hecksagon/adapter_binding.bluebook +3 -10
  199. data/lib/hecks/language/hecksagon/domain_port.bluebook +4 -19
  200. data/lib/hecks/language/hecksagon/hecksagon.bluebook +6 -25
  201. data/lib/hecks/language/hecksagon/port_operation.bluebook +3 -14
  202. data/lib/hecks/language/oidc.json +35 -0
  203. data/lib/hecks/language/port.bluebook +4 -17
  204. data/lib/hecks/language/translation/translation.bluebook +3 -9
  205. data/lib/hecks/language/translation/translation_aggregate.bluebook +5 -24
  206. data/lib/hecks/language/world/wiring.bluebook +1 -3
  207. data/lib/hecks/language/world/world.bluebook +5 -7
  208. data/lib/hecks/literal.rb +8 -50
  209. data/lib/hecks/mcp_door_scope.rb +95 -0
  210. data/lib/hecks/mcp_stdio_guard.rb +4 -41
  211. data/lib/hecks/naming.rb +29 -137
  212. data/lib/hecks/ports/access_control.rb +7 -23
  213. data/lib/hecks/ports/agent/answers.rb +5 -23
  214. data/lib/hecks/ports/agent.rb +47 -172
  215. data/lib/hecks/ports/authentication.rb +12 -39
  216. data/lib/hecks/ports/authorization.rb +16 -43
  217. data/lib/hecks/ports/clock.rb +5 -45
  218. data/lib/hecks/ports/extraction.rb +7 -12
  219. data/lib/hecks/ports/identity_assignment.rb +8 -29
  220. data/lib/hecks/ports/identity_generation.rb +6 -29
  221. data/lib/hecks/ports/identity_resolution.rb +2 -10
  222. data/lib/hecks/ports/key_vault.rb +9 -45
  223. data/lib/hecks/ports/persistence/append_only.rb +62 -65
  224. data/lib/hecks/ports/persistence/binding_policy.rb +4 -14
  225. data/lib/hecks/ports/persistence/codec_boundary.rb +20 -80
  226. data/lib/hecks/ports/persistence/null_saga_store.rb +4 -20
  227. data/lib/hecks/ports/persistence/plugin.rb +13 -42
  228. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +38 -141
  229. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +15 -115
  230. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +16 -84
  231. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +7 -26
  232. data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +3 -22
  233. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +55 -181
  234. data/lib/hecks/ports/persistence/plugins/era/postgres_era/events.rb +77 -0
  235. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +12 -122
  236. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +28 -128
  237. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +73 -474
  238. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +31 -124
  239. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +45 -219
  240. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +13 -108
  241. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +18 -75
  242. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +9 -49
  243. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +6 -123
  244. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +23 -55
  245. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +14 -72
  246. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +7 -16
  247. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +14 -115
  248. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +13 -45
  249. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +127 -554
  250. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +13 -63
  251. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +2 -17
  252. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +3 -14
  253. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +13 -58
  254. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -17
  255. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +9 -26
  256. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +9 -25
  257. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +22 -122
  258. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +34 -123
  259. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +5 -12
  260. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +3 -9
  261. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -14
  262. data/lib/hecks/ports/persistence/plugins/era.rb +4 -23
  263. data/lib/hecks/ports/persistence/postgres_dump/connection.rb +7 -23
  264. data/lib/hecks/ports/persistence/postgres_dump.rb +4 -35
  265. data/lib/hecks/ports/persistence/remote_runtime.rb +2 -24
  266. data/lib/hecks/ports/persistence/repository_factory.rb +8 -27
  267. data/lib/hecks/ports/persistence/state_codec.rb +23 -103
  268. data/lib/hecks/ports/persistence.rb +7 -14
  269. data/lib/hecks/ports/projection.rb +42 -84
  270. data/lib/hecks/ports/query/in_memory.rb +10 -58
  271. data/lib/hecks/ports/query/ordering.rb +11 -32
  272. data/lib/hecks/ports/query.rb +14 -37
  273. data/lib/hecks/ports.rb +2 -8
  274. data/lib/hecks/projections/bootstrap_table.rb +11 -43
  275. data/lib/hecks/projections/deploy/fargate/assembly.rb +2 -15
  276. data/lib/hecks/projections/deploy/fargate/cdn.rb +1 -48
  277. data/lib/hecks/projections/deploy/fargate/cdn_default.rb +2 -11
  278. data/lib/hecks/projections/deploy/fargate/check.rb +5 -14
  279. data/lib/hecks/projections/deploy/fargate/containers.rb +3 -78
  280. data/lib/hecks/projections/deploy/fargate/extras.rb +3 -44
  281. data/lib/hecks/projections/deploy/fargate/monitoring.rb +0 -44
  282. data/lib/hecks/projections/deploy/fargate/settings.rb +2 -54
  283. data/lib/hecks/projections/deploy/fargate/yaml.rb +13 -58
  284. data/lib/hecks/projections/deploy/fargate.rb +35 -202
  285. data/lib/hecks/projections/deploy/lambda.rb +151 -679
  286. data/lib/hecks/projections/deploy/preview/access_yaml.rb +1 -33
  287. data/lib/hecks/projections/deploy/preview/containers.rb +6 -111
  288. data/lib/hecks/projections/deploy/preview/routing_yaml.rb +4 -56
  289. data/lib/hecks/projections/deploy/preview/script.rb +2 -14
  290. data/lib/hecks/projections/deploy/preview/settings.rb +3 -33
  291. data/lib/hecks/projections/deploy/preview/task_yaml.rb +2 -38
  292. data/lib/hecks/projections/deploy/preview/template.rb +2 -58
  293. data/lib/hecks/projections/deploy/preview.rb +2 -67
  294. data/lib/hecks/projections/deploy/scripts/settings.rb +3 -25
  295. data/lib/hecks/projections/deploy/scripts.rb +4 -57
  296. data/lib/hecks/projections/deploy/shared.rb +47 -146
  297. data/lib/hecks/projections/deploy/smoke.rb +4 -61
  298. data/lib/hecks/projections/deploy/template_diff/comparer.rb +3 -25
  299. data/lib/hecks/projections/deploy/template_diff/loader.rb +2 -11
  300. data/lib/hecks/projections/deploy/template_diff.rb +5 -57
  301. data/lib/hecks/projections/diagrams.rb +20 -582
  302. data/lib/hecks/projections/glossary/html.rb +14 -130
  303. data/lib/hecks/projections/glossary/markdown.rb +8 -69
  304. data/lib/hecks/projections/glossary/mermaid.rb +9 -66
  305. data/lib/hecks/projections/glossary/sensitivity.rb +5 -34
  306. data/lib/hecks/projections/glossary/sentences.rb +14 -159
  307. data/lib/hecks/projections/glossary.rb +30 -222
  308. data/lib/hecks/projections/ir.rb +3 -10
  309. data/lib/hecks/projections/model/deviations.rb +15 -42
  310. data/lib/hecks/projections/model.rb +12 -51
  311. data/lib/hecks/projections/oidc.rb +12 -56
  312. data/lib/hecks/projections/parser_table.rb +9 -28
  313. data/lib/hecks/projections/reference.rb +9 -16
  314. data/lib/hecks/projections/rust_vocabulary.rb +12 -250
  315. data/lib/hecks/projections/shape.rb +3 -29
  316. data/lib/hecks/projections/statements.rb +7 -86
  317. data/lib/hecks/projections/vocabulary.rb +6 -22
  318. data/lib/hecks/projections.rb +2 -16
  319. data/lib/hecks/projector/cli_projector.rb +60 -375
  320. data/lib/hecks/projector/docs_projector.rb +10 -201
  321. data/lib/hecks/projector/exporter.rb +46 -138
  322. data/lib/hecks/projector/ir_projector.rb +2 -9
  323. data/lib/hecks/projector/narrate_projector.rb +15 -189
  324. data/lib/hecks/projector/target.rb +13 -76
  325. data/lib/hecks/projector.rb +30 -121
  326. data/lib/hecks/query_specification/common/comparators.rb +5 -26
  327. data/lib/hecks/query_specification/common/comparison.rb +35 -143
  328. data/lib/hecks/query_specification/common/dsl.rb +8 -24
  329. data/lib/hecks/query_specification/common/null_policy.rb +20 -70
  330. data/lib/hecks/query_specification/common/options.rb +2 -6
  331. data/lib/hecks/query_specification/common/where_clause.rb +2 -10
  332. data/lib/hecks/query_specification/field_path.rb +37 -77
  333. data/lib/hecks/query_specification/hop_path.rb +12 -76
  334. data/lib/hecks/release/gem_pin.rb +2 -26
  335. data/lib/hecks/release/runner/ci_publisher.rb +93 -0
  336. data/lib/hecks/release/runner/commands.rb +66 -0
  337. data/lib/hecks/release/runner/console.rb +52 -0
  338. data/lib/hecks/release/runner/gem_publisher.rb +43 -0
  339. data/lib/hecks/release/runner/git.rb +48 -0
  340. data/lib/hecks/release/runner/npm_publisher.rb +75 -0
  341. data/lib/hecks/release/runner/preflight.rb +104 -0
  342. data/lib/hecks/release/runner/published.rb +55 -0
  343. data/lib/hecks/release/runner/tagger.rb +80 -0
  344. data/lib/hecks/release/runner.rb +215 -0
  345. data/lib/hecks/rendering.rb +4 -26
  346. data/lib/hecks/router/namespace_installer.rb +12 -36
  347. data/lib/hecks/router.rb +4 -8
  348. data/lib/hecks/runtime/aggregate_lock.rb +8 -33
  349. data/lib/hecks/runtime/boot_gates.rb +3 -18
  350. data/lib/hecks/runtime/caller.rb +19 -59
  351. data/lib/hecks/runtime/capability_graph.rb +5 -21
  352. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +16 -78
  353. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +23 -160
  354. data/lib/hecks/runtime/command_interpreter.rb +125 -307
  355. data/lib/hecks/runtime/command_rules/admissibility.rb +95 -366
  356. data/lib/hecks/runtime/command_rules/arithmetic.rb +66 -196
  357. data/lib/hecks/runtime/command_rules/authorization.rb +13 -45
  358. data/lib/hecks/runtime/command_rules/emission.rb +8 -21
  359. data/lib/hecks/runtime/command_rules/references.rb +23 -202
  360. data/lib/hecks/runtime/dependency_planning.rb +30 -102
  361. data/lib/hecks/runtime/dispatcher.rb +82 -241
  362. data/lib/hecks/runtime/entity_element.rb +40 -462
  363. data/lib/hecks/runtime/entity_interpreter.rb +65 -220
  364. data/lib/hecks/runtime/errors.rb +32 -115
  365. data/lib/hecks/runtime/event.rb +3 -25
  366. data/lib/hecks/runtime/identity.rb +18 -154
  367. data/lib/hecks/runtime/instance.rb +18 -150
  368. data/lib/hecks/runtime/interpreting.rb +11 -70
  369. data/lib/hecks/runtime/invocation.rb +28 -164
  370. data/lib/hecks/runtime/loader.rb +48 -186
  371. data/lib/hecks/runtime/outbox.rb +38 -261
  372. data/lib/hecks/runtime/policy_interpreter.rb +38 -200
  373. data/lib/hecks/runtime/port_operation_interpreter.rb +26 -87
  374. data/lib/hecks/runtime/query_interpreter.rb +45 -187
  375. data/lib/hecks/runtime/reaction_invocation.rb +36 -138
  376. data/lib/hecks/runtime/read_model_interpreter.rb +139 -208
  377. data/lib/hecks/runtime/rebuild_sweep.rb +8 -54
  378. data/lib/hecks/runtime/reference_hop.rb +8 -81
  379. data/lib/hecks/runtime/refusal_wording.rb +8 -89
  380. data/lib/hecks/runtime/registry/saga_persistence.rb +10 -102
  381. data/lib/hecks/runtime/registry/verification.rb +52 -225
  382. data/lib/hecks/runtime/registry/world_defaults.rb +4 -53
  383. data/lib/hecks/runtime/registry.rb +103 -244
  384. data/lib/hecks/runtime/remote_dispatcher.rb +37 -152
  385. data/lib/hecks/runtime/routing.rb +4 -15
  386. data/lib/hecks/runtime/saga_interpreter/correlation.rb +14 -61
  387. data/lib/hecks/runtime/saga_interpreter.rb +79 -263
  388. data/lib/hecks/runtime/saga_pending_dispatch.rb +2 -43
  389. data/lib/hecks/runtime/tenant_check.rb +6 -68
  390. data/lib/hecks/runtime/tenant_scope.rb +3 -28
  391. data/lib/hecks/runtime/value/admission.rb +16 -67
  392. data/lib/hecks/runtime/value/coercion.rb +155 -641
  393. data/lib/hecks/runtime/value/entity_list_coercion.rb +26 -219
  394. data/lib/hecks/runtime/value.rb +12 -84
  395. data/lib/hecks/runtime.rb +17 -65
  396. data/lib/hecks/storehouse.rb +146 -567
  397. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +8 -32
  398. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +6 -23
  399. data/lib/hecks/vendoring/git_environment.rb +2 -8
  400. data/lib/hecks/vendoring/git_source.rb +4 -10
  401. data/lib/hecks/vendoring.rb +7 -46
  402. data/lib/hecks/version.rb +3 -14
  403. data/lib/hecks/vocabulary.rb +5 -0
  404. data/lib/hecks.rb +32 -82
  405. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -46
  406. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +11 -80
  407. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +10 -99
  408. metadata +30 -59
  409. data/lib/hecks/bench/cli.rb +0 -74
  410. data/lib/hecks/bench/environment.rb +0 -100
  411. data/lib/hecks/bench/postgres_probe.rb +0 -41
  412. data/lib/hecks/bench/report.rb +0 -116
  413. data/lib/hecks/bench/ruby_runner.rb +0 -151
  414. data/lib/hecks/bench/run.rb +0 -55
  415. data/lib/hecks/bench/rust_runner.rb +0 -165
  416. data/lib/hecks/bench/stats.rb +0 -75
  417. data/lib/hecks/bench/suite.rb +0 -172
  418. data/lib/hecks/bench/workload.rb +0 -137
  419. data/lib/hecks/bench.rb +0 -28
  420. data/lib/hecks/codemod.rb +0 -443
  421. data/lib/hecks/corpus.rb +0 -479
  422. data/lib/hecks/doc/reference.rb +0 -585
  423. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -684
  424. data/lib/hecks/fuzzing/combination_miner.rb +0 -178
  425. data/lib/hecks/fuzzing/concurrent_dispatch.rb +0 -469
  426. data/lib/hecks/fuzzing/coverage_campaign.rb +0 -161
  427. data/lib/hecks/fuzzing/differential.rb +0 -192
  428. data/lib/hecks/fuzzing/domain_generator.rb +0 -871
  429. data/lib/hecks/fuzzing/era_boundary.rb +0 -154
  430. data/lib/hecks/fuzzing/form_census.rb +0 -285
  431. data/lib/hecks/fuzzing/generated_domain_check.rb +0 -171
  432. data/lib/hecks/fuzzing/invalid_value_generator.rb +0 -125
  433. data/lib/hecks/fuzzing/isolated_boot.rb +0 -658
  434. data/lib/hecks/fuzzing/nondeterministic.rb +0 -79
  435. data/lib/hecks/fuzzing/persistence_parity.rb +0 -253
  436. data/lib/hecks/fuzzing/properties/corrections.rb +0 -125
  437. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +0 -688
  438. data/lib/hecks/fuzzing/properties/guards.rb +0 -398
  439. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +0 -399
  440. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +0 -137
  441. data/lib/hecks/fuzzing/properties/outbox.rb +0 -180
  442. data/lib/hecks/fuzzing/properties/querying.rb +0 -260
  443. data/lib/hecks/fuzzing/properties.rb +0 -267
  444. data/lib/hecks/fuzzing/qa_settings.rb +0 -164
  445. data/lib/hecks/fuzzing/replay.rb +0 -930
  446. data/lib/hecks/fuzzing/rotation_priority.rb +0 -114
  447. data/lib/hecks/fuzzing/rust_gap_manifest.rb +0 -139
  448. data/lib/hecks/fuzzing/self_consistency.rb +0 -825
  449. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +0 -532
  450. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +0 -171
  451. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +0 -127
  452. data/lib/hecks/fuzzing/sequence_generator/picker.rb +0 -116
  453. data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +0 -138
  454. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +0 -344
  455. data/lib/hecks/fuzzing/sequence_generator.rb +0 -311
  456. data/lib/hecks/fuzzing/shrinker.rb +0 -309
  457. data/lib/hecks/fuzzing/structural_skips.rb +0 -53
  458. data/lib/hecks/fuzzing/sweep_depth.rb +0 -61
  459. data/lib/hecks/fuzzing/target_capabilities.rb +0 -255
  460. data/lib/hecks/fuzzing/value_generator.rb +0 -340
  461. data/lib/hecks/fuzzing.rb +0 -23
  462. data/lib/hecks/grammar/evolve.rb +0 -524
  463. data/lib/hecks/query_ir.rb +0 -482
@@ -5,37 +5,21 @@ require_relative "../refusal_wording"
5
5
  module Hecks
6
6
  module Runtime
7
7
  class CommandInterpreter
8
- # The payload gate: a command takes the arguments it declares — all of
9
- # them, and no others.
8
+ # The payload gate: a command takes the arguments it declares, all of them and no others.
10
9
  module ArgumentGate
11
10
  private
12
11
 
13
- # Without this check, anything else would ride along in the payload
14
- # untouched — normalize_args walks only the declared attributes, so
15
- # a name the command never had is simply never looked at, and a
16
- # misspelled argument would do nothing, in silence.
12
+ # Refuses any argument the command does not declare.
13
+ # Addressing keys (`id`, the aggregate's identity heads, the reference key)
14
+ # and saga correlation keys are allowed through.
17
15
  #
18
- # The keys that are legitimately not attributes are the ones that address
19
- # the aggregate rather than describe it : `id`, whatever the aggregate is
20
- # identified by, and the reference key of the root a command reaches
21
- # through. Refusing those would refuse every dispatch there is.
22
- #
23
- # `extra_identity_heads:` — EntityInterpreter's own callers only. An
24
- # entity dispatch addresses not just the root aggregate but every
25
- # entity along the chain it walks to reach the piece the command
26
- # actually belongs to (`Handler.Dispatch.Bind` is two hops), and each
27
- # hop's own `identity_heads` is addressing the same way the root's
28
- # are — `element_of` (entity_element.rb) reads them straight out of
29
- # `args` to find the element, never as a fact the command itself
30
- # declares. `[]` for a plain aggregate/port-operation dispatch, which
31
- # has no chain to add.
16
+ # `extra_identity_heads:` carries the identity heads of each entity hop an entity
17
+ # dispatch walks; `[]` for a plain aggregate or port-operation dispatch.
32
18
  def refuse_unknown_arguments(domain, aggregate, command, args, extra_identity_heads: [])
33
19
  addressing = [:id, *aggregate.identity_heads, *extra_identity_heads, reference_key(command)] +
34
20
  correlation_keys(domain)
35
21
  known = (command.attributes.map(&:name) + addressing).compact.map(&:to_sym)
36
- # Sorted. Payload order is whatever the caller happened to write, and
37
- # refusal wording is contract — pinned byte-for-byte by the corpus, so
38
- # it cannot depend on hash iteration order.
22
+ # Sorted: refusal wording is pinned byte-for-byte, so it cannot follow payload order.
39
23
  unknown = (args.keys.map(&:to_sym) - known).sort
40
24
  return if unknown.empty?
41
25
 
@@ -45,46 +29,18 @@ module Hecks
45
29
  declared: declared_names(command))
46
30
  end
47
31
 
48
- # And it takes all of them. The other half of the same sentence, missing
49
- # until fuzz went looking : a name the command never declared was refused,
50
- # while a name it did declare could simply be left out.
51
- #
52
- # Refusing `Customer.Register` for a missing `name` needs to be
53
- # deliberate, not an accident with a lie for a message. Without this
54
- # check, `then_set :name, to: :name` finds nothing to resolve, passes
55
- # the literal symbol on, and coercion reports `name is a PersonName —
56
- # pass its fields as an object`, which describes a mistake the caller
57
- # did not make. The real mistake — an argument simply missing — is
58
- # never the one named unless this refuses it on purpose.
59
- #
60
- # No command attribute anywhere in the corpus carries a default — checked,
61
- # all eight chapters, zero — so there is no optional argument for this to
62
- # step on. Every declared attribute is a fact the command needs.
32
+ # Refuses a declared, non-optional argument that is missing. Without it a missing
33
+ # argument surfaces later as a misleading coercion error about a mistake the caller
34
+ # did not make.
63
35
  #
64
- # `aggregate:` — present only for a port operation. An aggregate
65
- # command's own self-address never reaches this list at all
66
- # (`CommandBuilder#reference_to`'s bare self-reference mints no
67
- # attribute to be absent) — but `PortOperationBuilder#reference_to`
68
- # always mints one (this file's own header on `PortOperation`: "no
69
- # creates?/acts_on distinction to protect"), because an operation had
70
- # no other way to say which record it addressed before `to:` existed.
71
- # `to:` is that way now (`Dispatcher#port_invocation` promotes the
72
- # identity attribute's own value out of the payload and into
73
- # routing) — which leaves the attribute still declared, still
74
- # non-optional, and never present in `args` at all: without this
75
- # exemption, every operation with an identity attribute would refuse
76
- # its own well-formed calls, dispatched exactly the way `to:`
77
- # intends. Exempted here on the same terms an aggregate command's
78
- # self-address always was — an address, not a fact the operation
79
- # still needs handed back.
36
+ # `aggregate:` is set only for a port operation, whose identity attribute is
37
+ # promoted into routing by `to:` and so never appears in `args`; it is exempted.
80
38
  def refuse_absent_arguments(command, args, aggregate: nil)
81
39
  given = args.keys.map(&:to_sym)
82
40
  exempt = aggregate && command.respond_to?(:identity_attribute) &&
83
41
  command.identity_attribute(aggregate.hecks_name)&.name
84
42
  required = command.attributes.reject(&:optional?).map { |attribute| attribute.name.to_sym } - [exempt]
85
- # Sorted, for the same reason the unknown list is : refusal wording is
86
- # contract, and a pinned wording cannot depend on the order a set
87
- # difference happens to be computed in.
43
+ # Sorted, for the same reason as the unknown list.
88
44
  absent = (required - given).sort
89
45
  return if absent.empty?
90
46
 
@@ -94,29 +50,11 @@ module Hecks
94
50
  declared: declared_names(command))
95
51
  end
96
52
 
97
- # A command that declares nothing still has to say so. `Account
98
- # .Freeze` is `reference_to Account` and no attributes at all, so
99
- # `{declared}` rendered empty and the sentence trailed off mid-
100
- # clause : "Freeze does not declare standing — it takes ". Read
101
- # against a real refusal that shape is worse than unhelpful — it
102
- # looks like the message itself is broken, right where a caller
103
- # is already trying to work out what went wrong.
104
- #
105
- # Only the empty case changes. Every command that declares even
106
- # one attribute renders exactly as it did before, because refusal
107
- # wording is contract and pinned byte-for-byte by the corpus (see
108
- # `refuse_unknown_arguments`' own note on sorting for why).
53
+ # An empty list must still read as a sentence; only the empty case is worded differently.
109
54
  def declared_names(command) = command.attributes.map(&:name)
110
55
 
111
- # What a process manager correlates by is routing, not description. A saga
112
- # threads its correlation key through every leg it dispatches so the event
113
- # each leg emits carries it and the next step can be correlated — so the key
114
- # arrives on commands that never declare it, and legitimately.
115
- #
116
- # This is the weakest part of the gate. Correlation is the saga's business,
117
- # and the better shape is for the saga to stamp its own key onto the event
118
- # it caused rather than smuggle it through the command's payload. Until it
119
- # does, refusing the key here would break every saga in the corpus.
56
+ # A saga threads its correlation key through every leg's payload, so the key arrives on
57
+ # commands that never declare it. Refusing it would break every saga.
120
58
  def correlation_keys(domain)
121
59
  Array(@registry.bluebook(domain)&.process_managers)
122
60
  .filter_map { |saga| saga.correlates_by && saga.correlation_head }
@@ -4,9 +4,7 @@ require_relative "../entity_element"
4
4
  module Hecks
5
5
  module Runtime
6
6
  class CommandInterpreter
7
- # How a command's declared mutations land on the instance in hand —
8
- # set, append (with value-object or entity elements), and the
9
- # arithmetic pair.
7
+ # Applies a command's declared mutations (set, append, remove, arithmetic) to an instance.
10
8
  module MutationApplier
11
9
  private
12
10
 
@@ -19,23 +17,10 @@ module Hecks
19
17
  end
20
18
  end
21
19
 
22
- # A case statement over a closed, declared set — every mutation op
23
- # the grammar can emit gets its own branch, including the `else`
24
- # backstop for the day a new op reaches this method undeclared (see
25
- # its own comment). Splitting each branch into its own method would
26
- # not reduce what a reader has to hold at once (each op's own
27
- # comment already explains why it is shaped the way it is) and
28
- # would obscure that the set is closed and exhaustive.
20
+ # Applies one mutation: sources read `pre` (pre-dispatch state), targets write `instance`.
29
21
  # rubocop:disable Lint/DuplicateBranch -- :delegate and :corrects
30
- # both no-op here, for two unrelated documented reasons (see each
31
- # branch's own comment below); merging would blur that distinction.
22
+ # both no-op here, for unrelated reasons; merging would blur that.
32
23
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
33
- #
34
- # `pre` — the pre-dispatch state (C4.2): every source below reads
35
- # it, every target is written to `instance`. A command's effects
36
- # are one update set; declaration order carries no meaning, and
37
- # build refuses a field written twice (`CommandBuilder#
38
- # refuse_duplicate_targets!`).
39
24
  def apply(instance, aggregate, mutation, args, pre = instance)
40
25
  case mutation.op
41
26
  when :set
@@ -52,24 +37,14 @@ module Hecks
52
37
  amount = @rules.resolve_source(mutation.source, args)
53
38
  attribute = aggregate.attribute(mutation.target)
54
39
  current = pre[mutation.target]
55
- # Vendored fix, not (yet) upstream hecks (migration plan
56
- # task 9): see #rewrap_arithmetic_result's own comment below
57
- # -- `amount` is wrapped only when `current` already is, not
58
- # merely because the target attribute exists.
40
+ # Wrap `amount` only when `current` is already a Value (see #rewrap_arithmetic_result).
59
41
  amount = Value.for_attribute(aggregate, attribute, amount) if attribute && current.is_a?(Value)
60
42
  result = @rules.arithmetic(current, amount, mutation.target, @rules.sign_of(mutation.op))
61
43
  instance[mutation.target] = rewrap_arithmetic_result(aggregate, attribute, current, result)
62
- # Vendored addition, not (yet) upstream hecks (migration
63
- # plan task 4): remove -- the list-removal counterpart to
64
- # append, matching an element by value (plan.bluebook's
65
- # RemoveDependency/DeactivateSprint: "a concurrent Add can
66
- # never be lost").
44
+ # Removes a list element by value, the counterpart to append.
67
45
  when :remove
68
46
  instance[mutation.target] = removed(pre, aggregate, mutation, args)
69
- # Vendored addition, not (yet) upstream hecks (migration
70
- # plan task 4, i106): multiply/clamp, the scale/bound pair
71
- # alongside increment/decrement's add/subtract pair -- see
72
- # CommandRules::Arithmetic#multiply/#clamp's own comments.
47
+ # Scales the current value; the counterpart to clamp below.
73
48
  when :multiply
74
49
  amount = @rules.resolve_source(mutation.source, args)
75
50
  attribute = aggregate.attribute(mutation.target)
@@ -77,65 +52,28 @@ module Hecks
77
52
  amount = Value.for_attribute(aggregate, attribute, amount) if attribute && current.is_a?(Value)
78
53
  result = @rules.multiply(current, amount, mutation.target)
79
54
  instance[mutation.target] = rewrap_arithmetic_result(aggregate, attribute, current, result)
80
- # Vendored addition, not (yet) upstream hecks (migration
81
- # plan task 4, i106): clamp -- bounds the current value into
82
- # [min, max]. mutation.source is always a literal [min, max]
83
- # pair, never an argument reference -- resolve_source would be
84
- # a no-op for an Array (it only special-cases Symbol), so it's
85
- # read straight.
55
+ # Bounds the current value into [min, max]; the source is a literal pair, read as is.
86
56
  when :clamp
87
57
  instance[mutation.target] = @rules.clamp(pre[mutation.target], mutation.source, mutation.target)
88
- # `delegates_to` — CommandBuilder#delegates_to's own comment gives
89
- # the full reasoning for storing it as a mutation at all. A real
90
- # no-op here, not a gap: it targets no field on this instance —
91
- # `CommandInterpreter#step_delegate_to_entity`, a later step in
92
- # the same dispatch, is what actually applies it, against the
93
- # target entity element `EntityElement.apply_to_element` reaches,
94
- # never through this method.
58
+ # `delegates_to`: a no-op here; CommandInterpreter#step_delegate_to_entity applies it.
95
59
  when :delegate
96
60
  nil
97
- # `corrects` — CommandBuilder#corrects_impl's own comment gives
98
- # the full reasoning for storing it as a mutation at all. A real
99
- # no-op here too: it targets no field on this instance either —
100
- # its own event name, and whether this record has actually
101
- # emitted it, is checked once, up front, by
102
- # CommandRules::Admissibility#enforce_correction_target, not
103
- # here. Whatever field this correction actually changes is an
104
- # ordinary declared (or, for `reverses: true`, derived — see
105
- # AggregateBuilder#seal_correction_targets) mutation of its own,
106
- # applied by one of the branches above like any other.
61
+ # `corrects`: a no-op here; its target event is checked up front by
62
+ # CommandRules::Admissibility#enforce_correction_target.
107
63
  when :corrects
108
64
  nil
109
65
  else
110
- # Every declared op has a `when` above — this is not a real
111
- # runtime path today, only a backstop against the day one
112
- # more mutation kind reaches the grammar and this method,
113
- # unlike every other one it could reach, does not: applying
114
- # nothing and refusing nothing would be the one silent
115
- # no-op in a language that otherwise refuses what it cannot
116
- # check.
66
+ # Backstop: an unhandled op must refuse, not silently apply nothing.
117
67
  raise Runtime::WiringError, "no mutation applier handles :#{mutation.op} — add one before declaring it"
118
68
  end
119
69
  end
120
70
  # rubocop:enable Lint/DuplicateBranch
121
71
  # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
122
72
 
123
- # A caller-supplied arg, first -- an append's own field can also
124
- # name something the subject already knows about itself, falling
125
- # back to the aggregate's own current field when it isn't one — a
126
- # caller appending at the end without computing or supplying a
127
- # position (`position: :next_position`, a field the command never
128
- # declares as an argument at all). `args.key?`, not a truthiness
129
- # check on the value — an explicitly-nil argument still counts as
130
- # "the caller named it," same distinction `assign_creation_attributes`
131
- # already draws.
132
- #
133
- # Extracted to its own method (pure refactor, ternary -> early
134
- # return, no behavior change) alongside `remove`'s own addition
135
- # below, purely for readability at this point in the file.
73
+ # A caller-supplied arg wins; otherwise the field falls back to the instance's own
74
+ # value. `args.key?` so an explicit nil still counts as supplied.
136
75
  def resolve_append_source(source, instance, args)
137
- # `state(:field)` — the record's own value, never an argument
138
- # (`Literal::StateRef`'s own comment).
76
+ # `state(:field)` reads the record's own value, never an argument.
139
77
  return instance[source.name] if source.is_a?(StateRef)
140
78
  return source unless source.is_a?(Symbol)
141
79
  return args[source] if args.key?(source)
@@ -149,12 +87,7 @@ module Hecks
149
87
  value_object = aggregate.value_object(element_type)
150
88
  value_object&.attributes&.each do |attribute|
151
89
  held = fields[attribute.name]
152
- # A single-field value unwraps to its scalar so it bridges
153
- # into the element's own (differently named) wrapper; a
154
- # multi-field one (a `state(:en_passant_square)` Square
155
- # copied off the record) has no scalar to stand in for it
156
- # and is handed across whole — `Value.for_attribute` keeps a
157
- # value of the element field's own type as it is.
90
+ # A single-field value unwraps to its scalar; a multi-field one passes through whole.
158
91
  fields[attribute.name] = Value.scalar(held) if held.is_a?(Value) && held.to_h.size == 1
159
92
  end
160
93
  element = if value_object
@@ -165,27 +98,11 @@ module Hecks
165
98
  fields)
166
99
  end
167
100
 
168
- # Frozen, like every other value the domain hands back — without
169
- # this, an appended list would come back mutable, letting a
170
- # caller push straight into an aggregate's own state after the
171
- # dispatch had finished.
101
+ # Frozen so a caller cannot push into the aggregate's state after dispatch.
172
102
  Freezer.deep(Array(instance[mutation.target]) + [element])
173
103
  end
174
104
 
175
- # Vendored addition, not (yet) upstream hecks (migration plan
176
- # task 4): the removal counterpart to #appended -- matches by
177
- # value equality, element-wise, no read-modify-write (plan.
178
- # bluebook's own words: "so a concurrent Add can never be lost")
179
- # -- unless the target list is itself entity-typed (BUG#32,
180
- # QualityControl ledger), in which case `EntityElement.
181
- # list_element_match?` matches by the entity's own identity
182
- # field instead -- see that method's own comment for the full
183
- # "why identity, not whole-value equality" reasoning. `mutation.
184
- # source` is a single field reference (`:dependency`), unlike
185
- # append's field-map -- resolved and Value-coerced the same way
186
- # increment/decrement already coerce their own amount, so the
187
- # comparison is against a like-shaped Value, not a raw scalar
188
- # against a wrapped one.
105
+ # Removes the element matching by value, or by identity for entity-typed lists.
189
106
  def removed(instance, aggregate, mutation, args)
190
107
  value = @rules.resolve_source(mutation.source, args)
191
108
  attribute = aggregate.attribute(mutation.target)
@@ -195,43 +112,8 @@ module Hecks
195
112
  end
196
113
  end
197
114
 
198
- # Vendored fix, not (yet) upstream hecks (migration plan
199
- # task 9): #apply's `:increment`/`:decrement`/`:multiply`
200
- # branches wrapping `amount` into a `Value` unconditionally
201
- # whenever the target attribute existed, never checking whether
202
- # `current` (the field's own existing value, read straight off
203
- # `instance[mutation.target]`) was also wrapped, would let the two
204
- # sides of the same arithmetic call disagree on Value-ness. On a
205
- # phantom-created field this is the common case, not an edge
206
- # one: `Instance.defaults`/`#default_for` leaves a VO-typed
207
- # attribute with no declared `default:` genuinely absent (nil),
208
- # and `CommandRules::Arithmetic#arithmetic`/`#multiply`'s own
209
- # `current ||= 0` then turns that nil into a raw, unwrapped
210
- # Integer `0` -- so `current.is_a?(Value) && amount.is_a?(Value)`
211
- # read false even though `amount` (correctly wrapped by the old
212
- # unconditional line) genuinely held a valid, correctly-typed
213
- # number, and the primitive path's `unless amount.is_a?(Numeric)`
214
- # guard refused it as a type mismatch the caller never made --
215
- # an artifact of this method's own asymmetric wrapping, not bad
216
- # input.
217
- #
218
- # Fixed at the call site (above) by wrapping `amount` only when
219
- # `current` is already a `Value` -- so an established VO-typed
220
- # field (a multi-field Money balance, say, already hydrated from
221
- # a prior save) keeps going through
222
- # `Arithmetic#arithmetic_value_object` exactly as before (the
223
- # "already-Value-wrapped field still mutates correctly" case
224
- # this fix must not regress), while a phantom field's raw
225
- # `current` and raw `amount` both take the plain-Numeric path
226
- # together. This method closes the other half: the plain-Numeric
227
- # path returns a bare Ruby number, and if the attribute is
228
- # itself VO-typed (the norm), that raw result needs the same
229
- # wrap `:set` already gives its own resolved value (`Value.for`)
230
- # before it's stored, so a field's stored shape doesn't depend
231
- # on which dispatch happened to mutate it first. A no-op
232
- # whenever `current` was already a Value (the VO branch already
233
- # returns one) or the mutation targets no declared attribute at
234
- # all.
115
+ # Wraps a plain-Numeric arithmetic result back into the attribute's Value type.
116
+ # Skipped when `current` was already a Value, since the Value path returns one.
235
117
  def rewrap_arithmetic_result(aggregate, attribute, current, result)
236
118
  return result if current.is_a?(Value) || attribute.nil? || result.is_a?(Value)
237
119
 
@@ -254,37 +136,18 @@ module Hecks
254
136
  EntityElement.check_entity_collision(aggregate, entity, current, fields)
255
137
  end
256
138
  fields[entity.lifecycle.field] ||= entity.lifecycle.default if entity.lifecycle
257
- # BUG#12 — every one of this entity's own declared attributes
258
- # the append mapping (and the identity/lifecycle filling just
259
- # above) didn't already touch gets its own default, the same
260
- # way a fresh aggregate's own attributes already do
261
- # (`Instance.defaults`) — see `EntityElement#
262
- # fill_declared_defaults`'s own comment for the full reasoning;
263
- # shared rather than reimplemented so this aggregate-level
264
- # entity creation and `EntityElement#appended_to_element`'s
265
- # entity-nested-in-entity one can never drift on what "the
266
- # default" means.
139
+ # Fill declared defaults for attributes the append mapping did not touch, shared with
140
+ # EntityElement so both entity-creation paths agree on what "the default" means.
267
141
  EntityElement.fill_declared_defaults(aggregate, entity, fields)
268
142
  end
269
143
 
270
- # The minted identity is one past the highest held (C4.5) — not
271
- # `size + 1`, which repeats an identity the moment the list has
272
- # ever shrunk. One strategy for the language, so the IR declares
273
- # none; the Rust generators mint by the same rule.
144
+ # One past the highest identity held, not `size + 1`, which repeats one after a shrink.
274
145
  def next_identity(current, entity)
275
146
  held = Array(current).map { |element| Value.scalar(element[entity.identified_by]).to_i }
276
147
  held.max.to_i + 1
277
148
  end
278
149
 
279
- # The entity-collision check lives in `EntityElement.check_entity_collision`
280
- # (entity_element.rb) — BUG#145 — shared by both `#entity_element` above
281
- # (an aggregate's own entity list, e.g. `Workspace.boards`) and
282
- # `EntityElement#appended_to_element`'s own nested-entity branch (an
283
- # entity's own entity list one hop further in, e.g. `Board.cards`),
284
- # which needs the exact same guard against a caller-supplied nested
285
- # identity silently duplicating. See that method's own call site and
286
- # comment; the check's own doc comment (heads/composite/auto-mint
287
- # reasoning) lives with the code, in entity_element.rb.
150
+ # The entity-collision guard is shared with EntityElement#appended_to_element.
288
151
  end
289
152
  end
290
153
  end