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,38 +5,11 @@ require_relative "../../rendering"
5
5
  module Hecks
6
6
  module Runtime
7
7
  class Value
8
- # How a `list_of` attribute's own elements GET hydrated — entity-typed
9
- # and value-object-typed alike — split out of `Coercion` (this file's
10
- # sibling, extended into `Value` alongside it exactly the way
11
- # `Admission` already is) once `Coercion` itself grew past
12
- # `Metrics/ModuleLength`'s budget from two, unrelated fixes landing in
13
- # the same method the same week (BUG#32's own `hydrate_entity_identity`
14
- # and BUG#33's own `check_entity_list_identities` — see each method's
15
- # own header, and this repo's `.rubocop_todo.yml` for the merge that
16
- # made the split necessary). Cross-calls into `Coercion`'s own
17
- # `for_attribute`/`build`/`fields_for`/`value_object_for`/
18
- # `trusting_stored_state?` work unqualified here, exactly as they do
19
- # within `Coercion` itself, because both modules land on the same
20
- # `Value` singleton class once extended — `self` never has to know
21
- # which file a sibling method actually lives in.
8
+ # Hydrates a `list_of` attribute's own elements, entity-typed and
9
+ # value-object-typed alike; extended into `Value` alongside `Coercion`.
22
10
  module EntityListCoercion
23
- # S17, ADR 0026 — searches the whole entity tree, not only the
24
- # root's own direct children. `aggregate` here is always the
25
- # root aggregate — `for_attribute`'s own `aggregate` argument is
26
- # never reassigned as hydration recurses into a nested element,
27
- # because coercion has to resolve value objects, and only the
28
- # root answers `.value_object` at all (Entity's own header
29
- # comment: an entity must not answer to it, or `Value.
30
- # for_attribute` could no longer tell a piece from a head). So
31
- # a nested entity — Dispatch, inside Handler — is not a direct
32
- # child of the root the way Handler itself is, and a plain
33
- # `aggregate.entities.find` stops one level short of it.
34
- #
35
- # @param construct [Bluebook::Aggregate, Bluebook::Entity] the construct to
36
- # search, and every entity nested under it
37
- # @param name [String] the entity's declared `hecks_name` to find
38
- # @return [Bluebook::Entity, nil] the matching entity anywhere in the tree, or
39
- # nil if none matches
11
+ # Searches the whole entity tree, not just the root's direct
12
+ # children; `aggregate` here is always the root aggregate. (ADR 0026)
40
13
  def find_entity(construct, name)
41
14
  construct.entities.each do |candidate|
42
15
  return candidate if candidate.hecks_name == name
@@ -47,54 +20,12 @@ module Hecks
47
20
  nil
48
21
  end
49
22
 
50
- # Hydrates a `list_of` attribute's offered value — each element of a
51
- # whole-list offering, or a single remove-target value, depending on
52
- # `attribute.type` and `value`'s own shape.
53
- #
54
- # Frozen through: a list read back out of the store is an answer,
55
- # not a handle on what is stored.
23
+ # Hydrates a `list_of` attribute's offered value: each element of a
24
+ # whole-list Array, or a single remove-target value.
56
25
  #
57
- # ADR 0047 — bailing (`return value unless entity`) the moment
58
- # `attribute.type` names a value object rather than an entity would
59
- # hand back the raw, un-hydrated argument untouched. A `sets :field`
60
- # mutation sourced from a whole-array argument (as opposed to
61
- # element-by-element `append:`) goes straight through
62
- # `for_attribute`'s `:list` branch, so `Banking::CardPayment.
63
- # Authorize`'s own `sets :tags` (`list_of(Tag)`) would store plain
64
- # Ruby Hashes as its `tags` elements forever — never a real
65
- # `Value`, never through `Tag`'s own `pattern:`/`invariant`
66
- # checks. `remove:`'s `==` comparison (a real `Value` against a
67
- # raw `Hash`) would then always fail, since `Hash#==` refuses
68
- # anything that isn't itself a compatible Hash — the bug ADR 0047
69
- # traces in full. Delegating to `hydrate_value_object_list` below
70
- # closes that gap the same way the entity branch already works:
71
- # build a real, validated `Value` per element, reusing
72
- # `for_attribute`'s own composite-construction path rather than
73
- # inventing a second one.
74
- # BUG#32 (QualityControl ledger) — without this, `remove:`'s own
75
- # single-target value would fall straight into
76
- # `Array(value).map { ... }` below unguarded, unlike this method's
77
- # own delegated `hydrate_value_object_list` sibling, whose
78
- # `value.is_a?(Array)` check exists for exactly this shape (see
79
- # that method's own comment). For an entity-typed list, `remove:`'s
80
- # scalar target (`Ledger.Void`'s `sequence: EntrySequence`) is
81
- # never a Hash, so `Array(2)` would merely wrap it as `[2]` rather
82
- # than shredding it — but the wrapping itself would still be wrong:
83
- # `MutationApplier#removed`'s `element == value` would then compare
84
- # a stored `Entry` Hash against a one-element Array, which can
85
- # never be `==` a Hash, so nothing would ever be removed and
86
- # nothing would ever refuse either. Routed to `hydrate_entity_identity`
87
- # instead, below.
88
- #
89
- # @param aggregate [Bluebook::Aggregate] the root aggregate `attribute` is
90
- # declared on
91
- # @param attribute [Bluebook::Attribute] the `list_of` attribute being hydrated
92
- # @param value [Array<Hash, Object>, Object] the offered value: a whole-list
93
- # Array (a `sets`/hydrate load) or a single remove-target value
94
- # @return [Object] a frozen Array of hydrated elements when `value` is a
95
- # whole-list Array and `attribute.type` names an entity; otherwise whatever
96
- # `hydrate_entity_identity` (a non-Array value naming an entity) or
97
- # `hydrate_value_object_list` (`attribute.type` naming no entity) returns
26
+ # Delegates non-entity types to `hydrate_value_object_list` so
27
+ # elements get the same `pattern:`/invariant checks other
28
+ # composites get. (ADR 0047)
98
29
  def hydrate_entity_list(aggregate, attribute, value)
99
30
  entity = find_entity(aggregate, attribute.type.to_s)
100
31
  return hydrate_value_object_list(aggregate, attribute, value) unless entity
@@ -110,61 +41,16 @@ module Hecks
110
41
  acc[key] = field ? for_attribute(aggregate, field, field_value) : field_value
111
42
  end
112
43
  end
113
- # BUG#33 — only a genuine whole-list offering (`value.is_a?(Array)`)
114
- # is a caller naming every element's own identity at once; the
115
- # single-target shape this same method also hydrates (a `remove:`
116
- # target, wrapped one level up by `Array()`) never reaches this
117
- # check, and never should — see `check_entity_list_identities`'s
118
- # own comment.
44
+ # Only a genuine whole-list offering names every element's own
45
+ # identity at once; a single `remove:` target never reaches this.
119
46
  check_entity_list_identities(aggregate, entity, hydrated) if value.is_a?(Array)
120
47
  Freezer.deep(hydrated)
121
48
  end
122
49
 
123
- # `MutationApplier#check_entity_collision`'s own guard
124
- # (mutation_applier.rb), extended from a single caller-supplied
125
- # append (BUG#13) to a whole-list replace — `sets :entries` bare
126
- # (`Ledger.ReplaceEntries`, the corpus's first `list_of(ENTITY)`
127
- # command argument/mutation, qa/stress_domains/corrections). The
128
- # array branch above rebuilds each offered element's own declared
129
- # fields but never checked the offered list itself for either way
130
- # it can misname its own entities:
131
- #
132
- # - two elements sharing one identity — the same silent-duplicate
133
- # hazard BUG#13's own comment describes: `EntityInterpreter#
134
- # element_of`'s own `find_index` always matches the first
135
- # match, so the second becomes permanently unaddressable by any
136
- # later command.
137
- # - an element missing its identity altogether — there is no
138
- # auto-mint for a whole-list replace the way `MutationApplier#
139
- # entity_element` mints one for a single append (which element
140
- # would it mint for? every element here is the caller's own
141
- # whole state, offered at once), so an absent identity is
142
- # refused rather than guessed.
143
- #
144
- # Non-composite identities only, exactly BUG#13's own scope
145
- # (`entity.identified_by` answers a single Symbol only when
146
- # `identity_heads.size == 1`, `Behaviour::Traits#derive_identity`) —
147
- # a composite identity's own duplicate/missing question is the
148
- # same pre-existing, narrower gap BUG#13 documented and left open,
149
- # not widened here.
150
- #
151
- # Refuses a whole-list offering that misnames its own entities' identities —
152
- # a duplicate identity across two elements, or an element missing one.
153
- #
154
- # Skipped under `trusting_stored_state?`, the same guard `validate!`
155
- # already gives every value object (C6.3): a record already
156
- # written is trusted as it was, so tightening this check can never
157
- # make an old, already-persisted record unreadable.
158
- #
159
- # @param aggregate [Bluebook::Aggregate] the aggregate `entity` belongs to,
160
- # named in a refusal
161
- # @param entity [Bluebook::Entity] the entity type `elements` are shaped as
162
- # @param elements [Array<Hash, Object>] the hydrated whole-list offering; a
163
- # non-Hash element is skipped, not checked
164
- # @return [void]
165
- # @raise [Runtime::TypeMismatch] if `entity` declares a single-field identity
166
- # and an element offers no value for it
167
- # @raise [Runtime::AlreadyExists] if two elements offer the same identity value
50
+ # Refuses a whole-list offering that misnames its own entities: a
51
+ # duplicate identity across two elements, or an element missing
52
+ # one. Single-field identities only; skipped once
53
+ # `trusting_stored_state?`.
168
54
  def check_entity_list_identities(aggregate, entity, elements)
169
55
  identity = entity.identified_by
170
56
  return unless identity
@@ -194,43 +80,10 @@ module Hecks
194
80
  end
195
81
  end
196
82
 
197
- # BUG#32 — `remove:`'s own single-target value against an
198
- # entity-typed list. An entity is never offered to `remove:`
199
- # whole the way a value object is (`hydrate_value_object_list`'s
200
- # own `element == value` full-value-equality shape) — an entity
201
- # must never answer `.value_object` at all (`Entity`'s own header
202
- # comment), so there is no whole-value shape here to rebuild in
203
- # the first place, only the one field a caller would otherwise
204
- # have to name to address that element any other way
205
- # (`EntityElement#element_of`'s own `wants`). So this coerces the
206
- # offered scalar against the entity's own identity field's
207
- # declared type — not the entity's full shape — and hands back a
208
- # real, correctly-typed `Value` ready to compare against each
209
- # stored element's own identity field
210
- # (`EntityElement.list_element_match?`, the shared rule both
211
- # `MutationApplier#removed` and `EntityElement#
212
- # removed_from_element` match against).
213
- #
214
- # A composite identity (more than one head) has no single field a
215
- # bare `remove:` target could mean, so this passes the value
216
- # through uncoerced rather than guessing which head — the same
217
- # "nothing in this corpus needs it yet" boundary
218
- # `MutationApplier#check_entity_collision`'s own header already
219
- # draws for entity identity elsewhere in this runtime.
220
- # Coerces a `remove:` target against an entity's own single-field
221
- # identity type, rather than the entity's full shape.
222
- #
223
- # `list_element_match?` treats an uncoerced value the same way
224
- # it always treated the pre-fix wrapped Array: never a match, a
225
- # documented no-op rather than a crash.
226
- #
227
- # @param aggregate [Bluebook::Aggregate] the aggregate `entity` belongs to
228
- # @param entity [Bluebook::Entity] the entity type `value` addresses
229
- # @param value [Object] the offered remove-target value
230
- # @return [Runtime::Value, Object] `value` already coerced (returned unchanged);
231
- # the value coerced against the identity field's declared type when `entity`
232
- # has a single identity head and a matching attribute; `value` unchanged
233
- # otherwise (no identity head, a composite identity, or no matching attribute)
83
+ # Coerces a `remove:` target against an entity's single-field
84
+ # identity type, rather than the entity's full shape. Passed
85
+ # through unchanged for a composite identity (more than one head)
86
+ # rather than guessing which head it means.
234
87
  def hydrate_entity_identity(aggregate, entity, value)
235
88
  return value if value.is_a?(self)
236
89
 
@@ -243,47 +96,12 @@ module Hecks
243
96
  for_attribute(aggregate, field, value)
244
97
  end
245
98
 
246
- # Hydrates a `list_of` attribute's offered value when `attribute.type`
247
- # names a value object rather than an entity.
99
+ # Hydrates a `list_of` attribute's offered value when
100
+ # `attribute.type` names a value object rather than an entity.
248
101
  #
249
- # The value-object sibling of the entity branch above: an element
250
- # already shaped like the target `Value` (or a `Hash`/scalar that
251
- # `fields_for` can still open) is rebuilt through the same `build`
252
- # a scalar composite attribute already uses (`for_attribute`'s own
253
- # `coerced = ... build(value_object, fields_for(...), aggregate)`
254
- # line) — same defaults, same `pattern:`/`admits:`/invariant
255
- # checks, same `trusting_stored_state?` bypass on a trusted load.
256
- # `attribute.type` naming neither an entity nor a value object
257
- # (a `list_of(String)`, say) has no shape to rebuild into, so the
258
- # element passes through unchanged, exactly as the entity branch's
259
- # own non-Hash elements do.
260
- #
261
- # Not `Array(value).map` (unlike the entity branch above) — `value`
262
- # here is not always genuinely list-shaped. `MutationApplier#
263
- # removed`'s own `Value.for_attribute(aggregate, attribute, value)`
264
- # call (`attribute` = the list attribute, `mutation.target`; `value`
265
- # = the single remove-target argument, already a real `Value` by
266
- # the time it gets here) reuses this exact branch — for `remove:`,
267
- # not for a whole-list `sets`. `Array(a_real_Value)` alone would be
268
- # harmless (`Value` defines neither `to_a` nor `to_ary`, so Kernel
269
- # wraps it `[value]`), but `Array(a_Hash)` is not harmless: Ruby's
270
- # `Array()` opens a bare Hash into its own `[[k, v], ...]` pairs,
271
- # not `[hash]` — silently shredding a single-element Hash-shaped
272
- # target into garbage instead of hydrating it. Branching on
273
- # `value.is_a?(Array)` up front (true only for a genuine whole-list
274
- # `sets`/hydrate load) keeps the single-target shape a single
275
- # target, hydrated the same way, never listified.
276
- #
277
- # @param aggregate [Bluebook::Aggregate] the aggregate `attribute` is declared
278
- # on, or a construct that does not respond to `value_object` (an entity), in
279
- # which case `value` passes through unchanged
280
- # @param attribute [Bluebook::Attribute] the `list_of` attribute being hydrated
281
- # @param value [Array<Hash, Object>, Object] the offered value: a whole-list
282
- # Array, or a single remove-target value
283
- # @return [Object] `value` unchanged when `aggregate` does not respond to
284
- # `value_object`, or `attribute.type` names no value object; otherwise a
285
- # frozen Array of built `Value`s for a whole-list Array, or a single built
286
- # `Value` (via `hydrate_value_object_element`) otherwise
102
+ # Branches on `value.is_a?(Array)` rather than `Array(value)`: a
103
+ # bare Hash-shaped single target would otherwise be shredded into
104
+ # its own `[[k, v], ...]` pairs instead of hydrated whole.
287
105
  def hydrate_value_object_list(aggregate, attribute, value)
288
106
  return value unless aggregate.respond_to?(:value_object)
289
107
 
@@ -296,19 +114,8 @@ module Hecks
296
114
  Freezer.deep(hydrated)
297
115
  end
298
116
 
299
- # Rebuilds one `list_of` element into a real, validated `Value`, unless it
300
- # already is one of `value_object`'s own type.
301
- #
302
- # @param aggregate [Bluebook::Aggregate] the aggregate `value_object` is
303
- # resolved against
304
- # @param attribute [Bluebook::Attribute] the `list_of` attribute `element`
305
- # belongs to
306
- # @param value_object [Class] the `Bluebook::ValueObject` subclass `element`
307
- # is built as
308
- # @param element [Object] the raw element to build: a Hash of fields, a scalar
309
- # `fields_for` can still open, or an already-built `Value`
310
- # @return [Runtime::Value] `element` unchanged if it is already a `Value` of
311
- # `value_object`'s own type; otherwise a freshly built one
117
+ # Rebuilds one `list_of` element into a real, validated `Value`,
118
+ # unless it already is one of `value_object`'s own type.
312
119
  def hydrate_value_object_element(aggregate, attribute, value_object, element)
313
120
  return element if element.is_a?(self) && element.type_name == value_object.hecks_name
314
121
 
@@ -7,14 +7,8 @@ require_relative "value/admission"
7
7
 
8
8
  module Hecks
9
9
  module Runtime
10
- # A typed value object in hand: frozen fields, read by name. How one is
11
- # made — coerced from a raw argument, checked against its declared
12
- # numeric types, patterns and closed sets — is the class-side engine in
13
- # value/coercion.rb, value/entity_list_coercion.rb (a `list_of`
14
- # attribute's own elements — split out once growing Coercion tripped
15
- # Metrics/ModuleLength, see that file's own header), and
16
- # value/admission.rb, extended here so the door stays where it always
17
- # was: `Value.for`, `Value.build`.
10
+ # A typed value object: frozen fields, read by name.
11
+ # Construction lives in value/coercion.rb, entity_list_coercion.rb and admission.rb.
18
12
  class Value
19
13
  extend Coercion
20
14
  extend EntityListCoercion
@@ -22,20 +16,7 @@ module Hecks
22
16
 
23
17
  attr_reader :value_object
24
18
 
25
- # Frozen through, not just on top.
26
- #
27
- # `@fields.freeze` alone stops a key being added or removed and
28
- # nothing else: the String, Array or Hash a field holds stays
29
- # mutable, so `vo[:value] << "!"` edits a value object in place —
30
- # demonstrated on a real dispatch before this was written, not
31
- # supposed. Same shape as the three freezing bugs already fixed
32
- # (list attributes, the event log, query rows), where the container
33
- # was frozen and the contents were not.
34
- #
35
- # A value object is the one thing in the domain that has no
36
- # identity to change over — `with` already answers a new one rather
37
- # than mutating — so freezing it through is what it always claimed
38
- # to be.
19
+ # Deep-freezes the fields so a held String, Array or Hash cannot be mutated in place.
39
20
  #
40
21
  # @param value_object [Bluebook::ValueObject] the declared type this instance is one of
41
22
  # @param fields [Hash] the type's fields, keyed by name (String or Symbol); deep-frozen
@@ -103,18 +84,8 @@ module Hecks
103
84
  end
104
85
  end
105
86
 
106
- # `materialize`, but a single-attribute value object (`sole_attribute`
107
- # — [[feedback_name_the_scalar_field]]) recurses into its own bare
108
- # field instead of building `{field: ...}` — a Board's own `label`
109
- # unwraps to `"Kanban"`, not `{value: "Kanban"}`. Not a replacement
110
- # for `materialize` itself: every existing report/query/command
111
- # caller keeps the wrapped shape it already depends on (Banking's
112
- # own `CustomerPortfolio` reads `payment[:amount][:cents]`, and
113
- # changing that out from under it would be a real breaking change,
114
- # not a bug fix). This is read_model_interpreter.rb's own opt-in,
115
- # used only for a `group_by`-declared head's own rows — grouping
116
- # needs a real scalar to key by regardless, so a report already
117
- # asking for that gets the unwrap for free.
87
+ # `materialize`, but a single-attribute value object unwraps to its bare field.
88
+ # Opt-in for `group_by` rows; `materialize` keeps the wrapped shape callers depend on.
118
89
  #
119
90
  # @param value [Object] the value to materialize; anything that is not a `Runtime::Value`,
120
91
  # Array or Hash passes through unchanged
@@ -127,12 +98,7 @@ module Hecks
127
98
  sole = value.value_object.sole_attribute
128
99
  return materialize_unwrapped(value[sole.name]) if sole
129
100
 
130
- # Not `value.to_h` — `Value#to_h` materializes each field through
131
- # plain `materialize`, so a VO nested inside a multi-attribute VO
132
- # would already be a plain Hash by the time this method ever saw
133
- # it, and never reach the `when self` branch above. Read each
134
- # field straight off `value` instead, so recursion actually
135
- # happens through this method the whole way down.
101
+ # Not `value.to_h`: it materializes nested values to Hashes, ending the recursion.
136
102
  value.value_object.attributes.to_h { |attr| [attr.name, materialize_unwrapped(value[attr.name])] }
137
103
  when Array then value.map { |item| materialize_unwrapped(item) }
138
104
  when Hash then value.transform_values { |item| materialize_unwrapped(item) }
@@ -140,22 +106,8 @@ module Hecks
140
106
  end
141
107
  end
142
108
 
143
- # Reduces an append-only sub-log to its current state — the same
144
- # "a later fact supersedes an earlier one" reduction this runtime
145
- # already performs replaying an aggregate's own command history
146
- # into its current attributes, applied here to a single `list_of`
147
- # field acting as its own miniature append-only log (a placement
148
- # history, a tombstone-style soft-delete list, a versioned
149
- # setting). `rows` is what a `list_of` attribute hands back — an
150
- # Array of `Value`, in append order — and `key` names the field
151
- # that identifies "the same logical thing" across entries.
152
- #
153
- # **Grouping only, never interpretation**. What counts as "removed,"
154
- # how to order what survives — that meaning belongs to whichever
155
- # domain declared the field, never here: a generic reduction that
156
- # started guessing domain semantics would need to keep guessing
157
- # forever, once per shape of "gone" any caller ever invents. The
158
- # caller filters and sorts the result; this only groups it.
109
+ # Reduces an append-only `list_of` sub-log to its current state: the latest row per `key`.
110
+ # Grouping only; what counts as removed, and the ordering, belong to the caller.
159
111
  #
160
112
  # @param rows [Array<Runtime::Value>] a `list_of` attribute's own elements, in append order
161
113
  # @param key [Symbol] the method to call on each row to find "the same logical thing"
@@ -169,21 +121,8 @@ module Hecks
169
121
  def method_missing(name, *args)
170
122
  return @fields[name] if @fields.key?(name)
171
123
 
172
- # The language rule, not a convenience: any value object with
173
- # exactly one declared attribute answers `.value`, whatever that
174
- # attribute is actually named — a single-attribute value object
175
- # is a name for a scalar, not a genuine group
176
- # ([[feedback_name_the_scalar_field]], `Behaviour::ValueObject#
177
- # sole_attribute`), so `money.value` reads `Money`'s own `amount`
178
- # exactly as `label.value` reads a shorthand-declared `value`.
179
- # After the real-field lookup above, on purpose: a field
180
- # literally named `value` is already answered there (and is the
181
- # sole attribute whenever the count is one), so this branch only
182
- # ever aliases, never shadows. A multi-attribute value object
183
- # keeps its NoMethodError — `sole_attribute` answers nil for it,
184
- # and falling through to `super` is exactly the refusal it
185
- # always gave: with two or more fields there is no single value
186
- # `.value` could honestly mean.
124
+ # `.value` aliases the sole field of a single-attribute value object; after the real-field
125
+ # lookup so a field literally named `value` is never shadowed.
187
126
  if name == :value
188
127
  sole = @value_object.sole_attribute
189
128
  return @fields[sole.name] if sole
@@ -201,19 +140,8 @@ module Hecks
201
140
 
202
141
  private
203
142
 
204
- # **The `.value` alias for indexed access** — the same language rule
205
- # `method_missing` above enforces for method reads, applied to
206
- # `[]`/`key?`/`with`: `:value` names a single-attribute value
207
- # object's sole field whatever that field is actually called. A
208
- # real key always wins first (a field literally named `value` is
209
- # its own answer, and is the sole attribute anyway whenever the
210
- # count is one), so this only ever resolves a `:value` that would
211
- # otherwise miss — it can never redirect a genuine field read.
212
- # `with(:value, x)` in particular needs this: merging a literal
213
- # `:value` key beside a sole field named `amount` would build a
214
- # two-key hash for a one-field shape and be refused (or worse,
215
- # stored) downstream — aliasing at the merge is what keeps the
216
- # write half of the rule as true as the read half.
143
+ # Resolves `:value` to the sole field's name so `[]`, `key?`, `with` match `method_missing`.
144
+ # A real key always wins.
217
145
  def resolve_field(field)
218
146
  sym = field.to_sym
219
147
  return sym if @fields.key?(sym)
data/lib/hecks/runtime.rb CHANGED
@@ -1,33 +1,7 @@
1
1
  # Hecks::Runtime
2
2
  #
3
- # The runtime layer's face. `lib/hecks/runtime/` held thirteen files and
4
- # no parent, so the layer had no surface of its own : `lib/hecks.rb`
5
- # required each file by hand, and the runtime's own state — the registry a
6
- # declaration is being collected into — sat on the top-level Hecks module
7
- # beside the DSL words.
8
- #
9
- # What lives here is what runs a domain :
10
- #
11
- # Runtime.boot(path) load a directory and return its Dispatcher
12
- # Runtime.with_registry(r) bind the ambient registry for the duration of a load
13
- # Runtime.current_registry the registry a declaration is landing in, or nil
14
- #
15
- # What does not live here is what declares one — `Hecks.bluebook`,
16
- # `.hecksagon`, `.port`, `.adapter`, `.world` are loading words and stay on the
17
- # top-level module, which now reads as a facade over this.
18
- #
19
- # runtime = Hecks::Runtime.boot("examples/pizzas/bluebook")
20
- # runtime.dispatch("Pizzas::Pizza.CreatePizza", name: "Margherita")
21
- #
22
- # Note, and it is the reason this file exists : `current_registry` is still
23
- # process-global. Each boot builds a fresh Registry, and `Loader.bind_runtime`
24
- # installs a fresh facade door whose modules close over that boot's dispatcher
25
- # — no class-level runtime binding remains, so two boots in one process no
26
- # longer share dispatch state, only the top-level name (the last-bound door
27
- # wins the constant, which is what per-boot install means). Owning the state
28
- # here was the first move ; the dispatcher reachable per-runtime rather than
29
- # through a constant was the second, and it is done — the door is the only
30
- # constant left, and it is a projection, not the runtime.
3
+ # Boots a domain directory and holds the ambient registry and caller state a boot runs under.
4
+ # `current_registry` is process-global; each boot's dispatcher is per-boot.
31
5
 
32
6
  require_relative "runtime/event"
33
7
  require_relative "runtime/value"
@@ -55,41 +29,33 @@ require_relative "runtime/tenant_check"
55
29
  require_relative "runtime/loader"
56
30
 
57
31
  module Hecks
58
- # See the file header above for what this module is and owns; nested
59
- # here (rather than documented in place) only because the
60
- # `require_relative` calls above must run before Ruby reopens the
61
- # module they populate.
32
+ # The runtime layer: boots a domain directory and holds the ambient registry a boot runs under.
62
33
  module Runtime
63
34
  class << self
64
- # The registry declarations are currently landing in, or nil outside a
65
- # boot. Read by the DSL collectors on the top-level module and by the
66
- # extraction port.
35
+ # The registry declarations are currently landing in, or nil outside a boot.
67
36
  attr_reader :current_registry
68
37
 
69
- # Load a bluebook directory and return the Dispatcher bound to it.
70
- # `install_facade:`, `environment:` — see Loader.boot.
38
+ # Loads a bluebook directory and returns the Dispatcher bound to it. See Loader.boot.
39
+ #
40
+ # Hecks::Runtime.boot("examples/pizzas/bluebook")
41
+ # .dispatch("Pizzas::Pizza.CreatePizza", name: "Margherita")
71
42
  #
72
43
  # @param path [String] path to a domain directory, or a file inside one
73
- # @param shared [String, nil] a shared-root override; see `Loader.boot`
74
- # @param install_facade [Boolean] whether to install the `Widget::Item.Add`-style
75
- # Ruby facade constants for this boot
76
- # @param environment [String, nil] the environment name passed through to
77
- # `Adapters::Folder#load_domain`
44
+ # @param shared [String, nil] a shared-root override
45
+ # @param install_facade [Boolean] whether to install the Ruby facade constants
46
+ # @param environment [String, nil] the environment name for `Adapters::Folder#load_domain`
78
47
  # @return [Runtime::Dispatcher, Runtime::RemoteDispatcher] the dispatcher bound
79
48
  # to the booted domain
80
49
  def boot(path, shared: nil, install_facade: true, environment: nil)
81
50
  Loader.boot(path, shared: shared, install_facade: install_facade, environment: environment)
82
51
  end
83
52
 
84
- # `paths` form — see Loader.boot_files.
53
+ # Loads only the given files of a domain; otherwise like `boot`. See Loader.boot_files.
85
54
  #
86
55
  # @param paths [String, Array<String>] one or more file paths within the domain
87
- # to load, instead of the whole directory
88
- # @param shared [String, nil] a shared-root override; see `Loader.boot_files`
89
- # @param install_facade [Boolean] whether to install the `Widget::Item.Add`-style
90
- # Ruby facade constants for this boot
91
- # @param environment [String, nil] the environment name passed through to the
92
- # selected-file loader
56
+ # @param shared [String, nil] a shared-root override
57
+ # @param install_facade [Boolean] whether to install the Ruby facade constants
58
+ # @param environment [String, nil] the environment name for the selected-file loader
93
59
  # @return [Runtime::Dispatcher, Runtime::RemoteDispatcher] the dispatcher bound
94
60
  # to the booted domain
95
61
  def boot_files(paths, shared: nil, install_facade: true, environment: nil)
@@ -110,23 +76,9 @@ module Hecks
110
76
  @current_registry = previous
111
77
  end
112
78
 
113
- # Bind the ambient caller (see Runtime::Caller) for the duration of
114
- # the block — who a command's declared `role`, if any, is checked
115
- # against.
116
- #
117
- # `as_of:` is optional, same opt-in shape as `actor_id:` — a caller
118
- # that wants a Governance `RoleAssignment`'s `starts_at` enforced
119
- # passes `as_of: Ports::Clock.now(registry)` here, at the door,
120
- # exactly where `cli_runner.rb` already merges `Clock.now` into a
121
- # command's own args. Nothing on the dispatch path calls the clock
122
- # itself — see `Ports::Clock`'s own header for why — so an unbound
123
- # `as_of` leaves `starts_at` unchecked, exactly as before.
79
+ # Binds the ambient caller (see Runtime::Caller) for the duration of the block.
80
+ # A command's declared `role` is checked against it.
124
81
  #
125
- # `scope:` is optional too — a caller that states which scope it is
126
- # acting in gets that scope checked against the matching
127
- # `RoleAssignment`'s own `scope`, not just its `role_name`. See
128
- # `Runtime::Caller::Current`'s own header for why this lives here
129
- # rather than as a command-level DSL construct.
130
82
  # @param role [String, Symbol] the role to check the caller against
131
83
  # @param actor_id [String, nil] who is calling, checked against a real Governance
132
84
  # `RoleAssignment` when given; string-equality only against `role` when nil