hecks 2.8.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 (404) hide show
  1. checksums.yaml +4 -4
  2. data/exe/hecks +4 -6
  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 +17 -98
  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 +5 -48
  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 +31 -141
  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 +14 -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 +25 -31
  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 +16 -69
  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 -194
  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 -111
  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 +53 -267
  109. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +43 -305
  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 +6 -21
  114. data/lib/hecks/bluebook/model_check.rb +100 -597
  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 +4 -23
  128. data/lib/hecks/cli/document.rb +3 -21
  129. data/lib/hecks/cli/ir.rb +3 -5
  130. data/lib/hecks/cli/mcp.rb +2 -3
  131. data/lib/hecks/cli/mcp_door.rb +19 -114
  132. data/lib/hecks/cli/model_check.rb +18 -85
  133. data/lib/hecks/cli/project_cli.rb +7 -40
  134. data/lib/hecks/cli/project_diagrams.rb +0 -7
  135. data/lib/hecks/cli/run.rb +12 -58
  136. data/lib/hecks/cli/smoke_test.rb +2 -16
  137. data/lib/hecks/cli/stores.rb +1 -2
  138. data/lib/hecks/cli.rb +18 -24
  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 +82 -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 +9 -90
  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 +61 -64
  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 +6 -25
  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 +18 -124
  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 +150 -678
  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 +2 -15
  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 +42 -134
  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 +3 -6
  336. data/lib/hecks/release/runner/commands.rb +2 -4
  337. data/lib/hecks/release/runner/gem_publisher.rb +2 -3
  338. data/lib/hecks/release/runner/npm_publisher.rb +6 -20
  339. data/lib/hecks/release/runner/preflight.rb +4 -19
  340. data/lib/hecks/release/runner/tagger.rb +2 -9
  341. data/lib/hecks/release/runner.rb +3 -32
  342. data/lib/hecks/rendering.rb +4 -26
  343. data/lib/hecks/router/namespace_installer.rb +12 -36
  344. data/lib/hecks/router.rb +4 -8
  345. data/lib/hecks/runtime/aggregate_lock.rb +8 -33
  346. data/lib/hecks/runtime/boot_gates.rb +3 -18
  347. data/lib/hecks/runtime/caller.rb +19 -59
  348. data/lib/hecks/runtime/capability_graph.rb +5 -21
  349. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +16 -78
  350. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +23 -160
  351. data/lib/hecks/runtime/command_interpreter.rb +124 -306
  352. data/lib/hecks/runtime/command_rules/admissibility.rb +95 -366
  353. data/lib/hecks/runtime/command_rules/arithmetic.rb +66 -196
  354. data/lib/hecks/runtime/command_rules/authorization.rb +13 -45
  355. data/lib/hecks/runtime/command_rules/emission.rb +8 -21
  356. data/lib/hecks/runtime/command_rules/references.rb +23 -202
  357. data/lib/hecks/runtime/dependency_planning.rb +30 -102
  358. data/lib/hecks/runtime/dispatcher.rb +82 -241
  359. data/lib/hecks/runtime/entity_element.rb +40 -462
  360. data/lib/hecks/runtime/entity_interpreter.rb +64 -219
  361. data/lib/hecks/runtime/errors.rb +32 -115
  362. data/lib/hecks/runtime/event.rb +3 -25
  363. data/lib/hecks/runtime/identity.rb +18 -154
  364. data/lib/hecks/runtime/instance.rb +18 -150
  365. data/lib/hecks/runtime/interpreting.rb +11 -70
  366. data/lib/hecks/runtime/invocation.rb +28 -164
  367. data/lib/hecks/runtime/loader.rb +48 -186
  368. data/lib/hecks/runtime/outbox.rb +38 -261
  369. data/lib/hecks/runtime/policy_interpreter.rb +38 -200
  370. data/lib/hecks/runtime/port_operation_interpreter.rb +26 -87
  371. data/lib/hecks/runtime/query_interpreter.rb +45 -187
  372. data/lib/hecks/runtime/reaction_invocation.rb +36 -138
  373. data/lib/hecks/runtime/read_model_interpreter.rb +117 -208
  374. data/lib/hecks/runtime/rebuild_sweep.rb +8 -54
  375. data/lib/hecks/runtime/reference_hop.rb +8 -81
  376. data/lib/hecks/runtime/refusal_wording.rb +8 -89
  377. data/lib/hecks/runtime/registry/saga_persistence.rb +10 -102
  378. data/lib/hecks/runtime/registry/verification.rb +52 -225
  379. data/lib/hecks/runtime/registry/world_defaults.rb +4 -53
  380. data/lib/hecks/runtime/registry.rb +99 -244
  381. data/lib/hecks/runtime/remote_dispatcher.rb +37 -152
  382. data/lib/hecks/runtime/routing.rb +4 -15
  383. data/lib/hecks/runtime/saga_interpreter/correlation.rb +14 -61
  384. data/lib/hecks/runtime/saga_interpreter.rb +79 -263
  385. data/lib/hecks/runtime/saga_pending_dispatch.rb +2 -43
  386. data/lib/hecks/runtime/tenant_check.rb +6 -68
  387. data/lib/hecks/runtime/tenant_scope.rb +3 -28
  388. data/lib/hecks/runtime/value/admission.rb +16 -67
  389. data/lib/hecks/runtime/value/coercion.rb +155 -641
  390. data/lib/hecks/runtime/value/entity_list_coercion.rb +26 -219
  391. data/lib/hecks/runtime/value.rb +12 -84
  392. data/lib/hecks/runtime.rb +17 -65
  393. data/lib/hecks/storehouse.rb +141 -568
  394. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +8 -32
  395. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +6 -23
  396. data/lib/hecks/vendoring/git_environment.rb +2 -8
  397. data/lib/hecks/vendoring/git_source.rb +4 -10
  398. data/lib/hecks/vendoring.rb +7 -46
  399. data/lib/hecks/version.rb +3 -14
  400. data/lib/hecks.rb +26 -81
  401. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -46
  402. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +11 -80
  403. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +10 -99
  404. metadata +3 -2
@@ -2,13 +2,8 @@ require_relative "word_gate"
2
2
  module Hecks
3
3
  module Bluebook
4
4
  module DSL
5
- # Parses a `.hecksagon` file's top-level DSL block into a `Hecksagon`
6
- # — a domain's own wiring: which adapter binds to which verb
7
- # (`persisted_by`, `projected_by`, ...), which framework/vendored
8
- # bluebooks it attaches, which external events it subscribes to, and
9
- # its bare chapter-root port. Kept separate from the bluebook itself
10
- # (the domain's own declared model) because wiring is an operational
11
- # decision, not a fact the domain states about itself.
5
+ # Parses a `.hecksagon` file's DSL block into a Hecksagon: a domain's adapter bindings,
6
+ # framework/vendored attachments and subscriptions — wiring, not part of the bluebook.
12
7
  class HecksagonBuilder
13
8
  GRAMMAR_CONTEXT = "Hecksagon".freeze
14
9
 
@@ -20,7 +15,6 @@ module Hecks
20
15
 
21
16
  attr_reader :binds, :subscriptions, :framework_members, :vendored_bluebooks
22
17
 
23
- # @param domain [String] name of the domain whose wiring this hecksagon declares
24
18
  def initialize(domain)
25
19
  @domain = domain
26
20
  @binds = []
@@ -31,173 +25,59 @@ module Hecks
31
25
  @translates = []
32
26
  end
33
27
 
34
- # Marks this chapter as a bounded context — a consumer-owned chapter
35
- # that `uses_framework` / `uses_embryonaut_bluebook` will not load.
36
- # Framework and vendored packages get the mark automatically from
37
- # those words; they never write `bounded` in their own bluebooks.
38
- # A bounded chapter wraps in its own module (no Object shortcut)
39
- # and must declare at least one `translates` ACL or boot refuses.
40
- #
41
- # @return [Boolean] true
28
+ # Marks this chapter as a bounded context; framework/vendored attachments set it too.
29
+ # Must declare at least one `translates` ACL or boot refuses.
42
30
  def bounded
43
31
  @bounded = true
44
32
  end
45
33
 
46
- # Subscribes this hecksagon to an event it takes from outside the domain's own bluebook.
47
- #
48
- # @param event [String, Symbol] the external event's name
49
- # @return [Array<String>] every subscription declared so far, this one last
34
+ # Subscribes to an event from outside this domain's own bluebook.
50
35
  def subscribe(event) = @subscriptions << event.to_s
51
36
 
52
- # Attaches a framework member to this domain and loads it into the current registry.
53
- #
54
- # A framework/ member this domain wants attached —
55
- # Governance, Identity, whatever else lands beside them.
56
- # Attaching one is a wiring decision, the same kind `persisted_by`/
57
- # `projected_by` already are, so it lives here rather than as a
58
- # fact stated in the domain's own bluebook. Recorded onto this
59
- # hecksagon, the same way `subscribe` records onto its own
60
- # `subscriptions` — and loads the member's bluebook then its own
61
- # hecksagon into whatever registry this one is loading into, see
62
- # `Framework.load!`.
63
- #
64
- # @param name [String, Symbol] the member's name, such as `"Governance"`
65
- # @return [Boolean, nil] true when this call loaded the member's bluebook, nil when the
66
- # current registry already held it
67
- # @raise [Runtime::WiringError] if no framework member has that name
37
+ # Attaches a framework member (e.g. Governance) and loads it into the current registry.
68
38
  def uses_framework(name)
69
39
  @framework_members << name.to_s
70
40
  Hecks::Framework.load!(name)
71
- # Automatic bounded mark — the framework member is a bounded
72
- # context; the consumer's sibling `Hecks.hecksagon "Name"` is
73
- # the anti-corruption layer. Not written in the framework
74
- # bluebook itself.
41
+ # Marks the framework member as bounded; the consumer's own hecksagon is the
42
+ # anti-corruption layer.
75
43
  Hecks.current_registry&.mark_bounded(name.to_s)
76
44
  end
77
45
 
78
46
  # Attaches a vendored embryonaut bluebook to this domain and loads its files into the
79
- # current registry.
80
- #
81
- # A vendored, external bluebook this domain wants attached — same
82
- # wiring-decision shape `uses_framework` already is, one level
83
- # further out: not a member shipped inside hecks's own lib/,
84
- # but a separate package (embryonaut_bluebooks) vendored into this
85
- # project's own checkout. See EmbryonautBluebook's own header for
86
- # the full reasoning on why its root can't be a fixed constant the
87
- # way Framework::ROOT is, and for the recovery provenance.
88
- #
89
- # Recorded onto @vendored_bluebooks, same shape `uses_framework`
90
- # already gives @framework_members — a separate list on purpose:
91
- # `framework_members` is load-bearing for `refuse_ungoverned_roles!`
92
- # (`Registry::Verification`) and for Governance's own attachment
93
- # check; conflating the two would make a vendored bluebook attachment
94
- # satisfy a Governance check it has nothing to do with.
95
- #
96
- # @param name [String, Symbol] the vendored package's directory name under
97
- # `vendor/embryonaut_bluebooks/`
98
- # @return [Array<String>, nil] paths of the `.bluebook` files this call loaded, or nil
99
- # when the current registry already held the bluebook
100
- # @raise [Runtime::WiringError] if the current registry has no root to vendor from, or
101
- # no vendored bluebook of that name exists under it
47
+ # registry. Recorded separately from @framework_members, which is load-bearing for
48
+ # governance checks.
102
49
  def uses_embryonaut_bluebook(name)
103
50
  @vendored_bluebooks << name.to_s
104
51
  Hecks::EmbryonautBluebook.load!(name)
105
- # Automatic bounded mark — same as `uses_framework`. The
106
- # package's directory name Pascal-cases to the chapter
107
- # (`"membership"` → `Membership`).
52
+ # Marks the chapter bounded, same as `uses_framework`; the directory name Pascal-cases
53
+ # to the chapter name (`"membership"` → `Membership`).
108
54
  Hecks.current_registry&.mark_bounded(Hecks::Naming.pascal(name.to_s))
109
55
  end
110
56
 
111
- # Declares a port at the hecksagon's root and attaches it to the registered bluebook.
112
- #
113
- # The primary port, bare at the root — belongs to the chapter as a
114
- # whole, not one aggregate. `BindingProxy#port` is the aggregate-
115
- # scoped sibling (`Payments::Payment.port("Gateway") do ... end`);
116
- # this is what's left when a port isn't about any one record. The
117
- # bluebook must already be built and registered, since a hecksagon
118
- # loads after its bluebook, and this attaches to that real, final
119
- # object directly rather than building a second copy MetaValidator
120
- # would have to know how to reconstruct.
121
- #
122
- # Answers the `port` word through the table's `calls:` column —
123
- # item #13's full metaprogrammed dispatch (slice 5). Not
124
- # bootstrap-reachable (checked directly — no core/attached chapter
125
- # declares a Hecksagon of its own). Reached through `WordGate`'s
126
- # `word_gate_dispatch`, called explicitly below since
127
- # `HecksagonBuilder`'s own class-level `method_missing` (the
128
- # open-verb catch-all beneath this) always wins over the module's —
129
- # see `word_gate.rb`'s own header for the full mechanism.
130
- #
131
- # @param name [String] the port's name
132
- # @yield the port body, evaluated against a `DomainPortBuilder`: either `verb`/`signal`
133
- # or `operation`/`tells`/`asks` blocks
134
- # @return [Bluebook::Port, Bluebook::DomainPort] the built port: a verb-shaped `Port`
135
- # registered on the current registry, or a `DomainPort` attached to the bluebook
136
- # @raise [Bluebook::DSL::Malformed] if the current registry holds no bluebook for this
137
- # domain, or the body declares both a verb and operations, neither, or an operation
138
- # the port grammar refuses
57
+ # Declares a port at the hecksagon's root — the chapter as a whole, not one aggregate.
139
58
  def port_impl(name, &block)
140
59
  bluebook_ir = Hecks.current_registry.bluebook(@domain) or
141
60
  raise Malformed, "#{@domain} declares no such bluebook — a port needs one to belong to"
142
61
 
143
- # See BindingProxy#port's own comment on why this resolver swap is
144
- # needed — ConstShim's active resolver is one global for the whole
145
- # dynamic extent, currently this file's own BindingProxy-minting
146
- # one, which would turn a bare constant inside an operation's
147
- # `reference_to`/`attribute` into another BindingProxy instead of
148
- # a name.
62
+ # Swaps the active ConstShim resolver to plain passthrough — without it a bare constant
63
+ # inside `reference_to`/`attribute` would resolve to a BindingProxy instead of a name.
149
64
  built = ConstShim.with(->(const) { const }) { DomainPortBuilder.build(name, &block) }
150
65
 
151
- # See BindingProxy#port's own comment on the same branch — a
152
- # `verb`-shaped port is a plain `Port`, registered the same
153
- # way `Hecks.port`'s top-level method already does, not attached
154
- # to this bluebook's own IR the way an operations-shaped
155
- # `DomainPort` is.
66
+ # A verb-shaped port is a plain `Port`, registered like `Hecks.port`'s top-level method,
67
+ # not attached to this bluebook's IR the way an operations-shaped `DomainPort` is.
156
68
  return Hecks.current_registry.add_port(built) if built.is_a?(Port)
157
69
 
158
70
  bluebook_ir.add_port(built)
159
71
  end
160
72
 
161
- # A translation boundary, not a business rule — `translates "Name"
162
- # do on Foreign::Domain::SomeEvent; trigger Local.Command; end`
163
- # builds the exact same `Policy` a `policy` block inside this
164
- # domain's own `.bluebook` would (same `on_impl`/`trigger_impl`,
165
- # same `PolicyInterpreter` runtime — reusing `PolicyBuilder`
166
- # directly, zero new runtime semantics). What's new is only where
167
- # it can be written: a cross-domain reaction is a wiring/context-
168
- # mapping decision (this chapter conforming to a foreign chapter's
169
- # published event), the same kind of decision `port`/
170
- # `uses_framework` already are — not a fact this domain's own
171
- # model states about itself, which `policy` (inside the bluebook)
172
- # remains the right word for.
173
- #
174
- # Must be a block, not `translates Event, into: Command` — a flat
175
- # call's arguments are evaluated eagerly, under this builder's own
176
- # `ConstShim` resolver (`BindingProxy.namespace`, set up by
177
- # `self.build` below), which mints a `BindingProxy` instance per
178
- # segment and cannot answer a further `::` (it is not a Module) —
179
- # a multi-segment reference like `Deploy::Tenant::TenantProvisioned`
180
- # would raise before this method ever ran. `port_impl`'s own
181
- # `ConstShim.with(->(const) { const })` swap only works because
182
- # its own callers never pass a multi-segment bare constant as an
183
- # eagerly-evaluated argument — everything of that shape lives
184
- # inside its block, evaluated later, under the swapped resolver.
185
- # A block defers evaluation the same way; a flat call cannot.
186
- #
187
- # @param name [String, Symbol] the reaction's name
188
- # @yield the `on`/`trigger` block, evaluated later under the scoped resolver
189
- # @return [Array<Bluebook::Policy>] the chapter's policies, with the new one appended
190
- # @raise [Malformed] if no bluebook is registered for this domain to attach the reaction to
73
+ # A cross-domain reaction, built as the same `Policy` an in-bluebook `policy` block would.
74
+ # Must be a block — the eager const resolver here can't handle multi-segment references.
191
75
  def translates(name, &block)
192
76
  bluebook_ir = Hecks.current_registry.bluebook(@domain) or
193
77
  raise Malformed, "#{@domain} declares no such bluebook — translates needs one to attach its reaction to"
194
78
 
195
- # Same resolver BluebookBuilder itself uses for a `policy`
196
- # block's own `on`/`trigger` (bluebook_builder.rb) — a
197
- # `ScopedConstant`, not the bare passthrough `port_impl` swaps
198
- # to, because `on`/`trigger` here take genuinely multi-segment
199
- # references (`Deploy::Tenant::TenantProvisioned`), not a
200
- # single-segment type name.
79
+ # Same resolver `policy` blocks use for `on`/`trigger` — a ScopedConstant, not the bare
80
+ # passthrough `port_impl` swaps to, since these take multi-segment references.
201
81
  resolver = ->(const) { ConstShim::ScopedConstant.for(const) }
202
82
  built = ConstShim.with(resolver) { PolicyBuilder.build(name, &block) }
203
83
 
@@ -206,58 +86,18 @@ module Hecks
206
86
  end
207
87
 
208
88
  # Assembles the collected binds, subscriptions and attachments into a `Hecksagon`.
209
- #
210
- # No ungoverned-role check here — see
211
- # Registry::Verification#refuse_ungoverned_roles!. It lives outside
212
- # per-block `build`, alongside `environment:`
213
- # (Runtime::Loader.boot's comment has the provenance): a domain
214
- # split across multiple hecksagon blocks (base + an
215
- # `environments/<name>.hecksagon` overlay) would have every
216
- # block but the one declaring `uses_framework "Governance"`
217
- # refused here, even though `Registry#add_hecksagon` merges them
218
- # into one Hecksagon before anything ever dispatches against it.
219
- # Checking the merged result once, at verify! time — after every
220
- # file for this domain has loaded — is both more permissive (no
221
- # need to repeat `uses_framework` in every file) and strictly
222
- # more correct (a check against an incomplete, not-yet-merged
223
- # hecksagon can never see the real final shape).
224
- #
225
- # @return [Bluebook::Hecksagon] this block's wiring, which `Registry#add_hecksagon` merges
226
- # with any other block declared for the same domain
89
+ # No ungoverned-role check here — it runs once on the merged result at verify! time.
227
90
  def build
228
91
  Hecksagon.new(domain: @domain, binds: @binds, subscriptions: @subscriptions,
229
92
  framework_members: @framework_members, vendored_bluebooks: @vendored_bluebooks,
230
93
  bounded: @bounded, translates: @translates)
231
94
  end
232
95
 
233
- # Records any verb the grammar does not own as a domain-wide bind to the named adapter.
234
- #
235
- # **Domain-level default binds** — `persisted_by "Heki"` bare, at the top
236
- # of a hecksagon block, applies to every aggregate in this domain
237
- # that doesn't declare its own override. Mirrors `BindingProxy`'s own
238
- # `method_missing` one level down (`aggregate:` filled in there,
239
- # `nil` here) — generic over verb name, not hardcoded to
240
- # `persisted_by`/`projected_by` specifically, so any future verb
241
- # gets a domain-level default for free too. See `Hecksagon#bind_for`
242
- # for the fallback lookup this feeds.
243
- #
244
- # @param verb [Symbol] the word called, such as `:persisted_by`
245
- # @param args [Array<Object>] positional arguments; the first names the adapter
246
- # @param kwargs [Hash{Symbol => Object}] keyword arguments; `:role` is kept on the bind
247
- # @yield an optional block, called once after the bind is recorded
248
- # @return [Object] this builder after recording a bind, or the grammar word's own result
249
- # when `verb` is one the `Hecksagon` grammar admits
250
- # @raise [NoMethodError] if `verb` is no grammar word and names no adapter
251
- # @raise [Bluebook::DSL::Malformed] if the grammar admits `verb` only in another context,
252
- # or admits it here and its implementation refuses the declaration
96
+ # Records any verb the grammar doesn't own as a domain-wide default bind, e.g. bare
97
+ # `persisted_by "Heki"` at the top of a block, applied unless an aggregate overrides it.
253
98
  def method_missing(verb, *args, **kwargs, &block)
254
- # A closed-set grammar word (`port`, today) gets first refusal
255
- # — item #13's full metaprogrammed dispatch (slice 5); see
256
- # `WordGate#word_gate_dispatch`'s own header for why this class
257
- # needs to call it explicitly rather than including it the
258
- # ordinary way. Only once that says "not admitted" does the
259
- # genuinely open-ended `persisted_by "Heki"`-style bind
260
- # vocabulary below get a turn.
99
+ # Grammar words (like `port`) get first refusal via explicit dispatch; only when that's
100
+ # not admitted does the open-ended `persisted_by`-style bind vocabulary below apply.
261
101
  result = word_gate_dispatch(verb, args, kwargs, block)
262
102
  return result unless result.equal?(WordGate::NOT_ADMITTED)
263
103
 
@@ -272,14 +112,6 @@ module Hecks
272
112
 
273
113
  # Evaluates a `Hecks.hecksagon` block with bare `Domain::Aggregate` constants resolving to
274
114
  # bind-collecting proxies, and returns the wiring it declared.
275
- #
276
- # @param domain [String] name of the domain being wired
277
- # @yield the hecksagon body, evaluated with the builder as `self`; may be omitted
278
- # @return [Bluebook::Hecksagon] the declared wiring
279
- # @raise [Bluebook::DSL::Malformed] if a `port` names no registered bluebook or aggregate,
280
- # or declares a shape the port grammar refuses
281
- # @raise [Runtime::WiringError] if `uses_framework` or `uses_embryonaut_bluebook` names
282
- # something that cannot be found
283
115
  def self.build(domain, &block)
284
116
  builder = new(domain)
285
117
  resolver = ->(name) { BindingProxy.namespace(name, builder.binds) }
@@ -1,68 +1,19 @@
1
1
  module Hecks
2
2
  module Bluebook
3
3
  module DSL
4
- # `identified_by` — shared by AggregateBuilder and EntityBuilder
5
- # only (S9, ADR 0025 — "EntityBuilder's duplicate identified_by and
6
- # resolve_pending_identity! go"): a piece's own identity cannot
7
- # spell differently from its aggregate's, and until this slice it
8
- # was hand-duplicated onto both rather than shared.
9
- #
10
- # A separate module from AttributeCollector on purpose — every
11
- # `attribute()`-taking builder (Command, Query, PortOperation,
12
- # ValueObject, ...) `include`s that one too, and none of them
13
- # declares an identity of its own ; folding `identified_by` in
14
- # there made it answer for six builders that never earned the
15
- # word (`syntax_conformance_spec.rb` catches exactly this — a
16
- # builder answering a method the grammar never grants it).
17
- #
18
- # Requires its includer to also `include AttributeCollector`
19
- # (for `attributes`/`resolve_identity_field!`/`resolve_identity_
20
- # type!`, still declared there since every includer of that
21
- # module needs them) and to supply `identity_pool` (private) —
22
- # the value-object list a bare field's own type resolves
23
- # against: AggregateBuilder's own `@value_objects + closed_sets`,
24
- # or EntityBuilder's owner's, since a piece mints none of its own.
4
+ # `identified_by`, shared by AggregateBuilder and EntityBuilder (ADR 0025).
5
+ # The includer must include AttributeCollector and define a private `identity_pool`.
25
6
  module IdentityDeclaration
26
- # Records which of the three live `identified_by` forms a builder declared, deferring
27
- # resolution until build time.
28
- #
29
- # There are three live forms, deliberately distinguishable at the
30
- # declaration site:
7
+ # Records which `identified_by` form a builder declared; resolution waits for build time.
31
8
  #
32
9
  # identified_by AccountNumber, as: :number # one identity concept
33
10
  # identified_by do ... end # a bespoke concept
34
11
  # identified_by :branch, :number # an existing compound key
35
12
  #
36
- # One symbol is retired: it cannot say whether the author means a
37
- # value concept or a field-shaped database key. Frozen source still
38
- # reaches the old interpretation through `legacy_identified_by`.
39
- # Both `AggregateBuilder` and `EntityBuilder` name this method in their Keyword rows'
40
- # `calls:` column, so `GenericDispatch` forwards `identified_by` here unchanged, both
41
- # after boot (reading the live grammar table) and during it
42
- # (`GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`, which carries every `calls:`-routed row
43
- # unconditionally, not a hand-picked bootstrap-reachable subset — though this one is
44
- # exercised during boot too, since every self-hosted aggregate/entity declares an identity).
45
- # Dispatches across the three live forms documented above (value-
46
- # object + block, single type target, single/compound field
47
- # target), each an early return that sets exactly one pending
48
- # ivar. Splitting per form would need each branch's own `return`-
49
- # with-nil semantics and the shared `@name`/`identity_pool`
50
- # threaded back out as parameters, for no gain beyond what the
51
- # three-forms comment above already documents.
52
- #
53
- # @param targets [Array<Symbol, Module>] zero or more identity targets: a value-object
54
- # type (a bareword, resolved by `ConstShim` to a `Module`), one or more attribute-name
55
- # symbols for a field or compound key, or empty when `definition` is given
56
- # @param as [Symbol, nil] the field name to mint for a value-object-type target; not
57
- # accepted with a field or compound-key target
58
- # @yield a bespoke value-object body, `instance_eval`'d by `ValueObjectBuilder.build` to
59
- # mint the identity's own anonymous value object
60
- # @return [Array<Symbol>, Array<String>, nil] `targets` when a compound key is declared,
61
- # the resolved identity paths from `legacy_identified_by` in shadow-parsing mode, or
62
- # `nil` otherwise; callers reach this through `GenericDispatch`, which discards it
63
- # @raise [Bluebook::DSL::Malformed] if `identified_by` was already called, combines a
64
- # block with targets, names no identity, mixes a value-object type with field names,
65
- # passes `as:` with a field or compound key, or the block declares no attributes
13
+ # @param targets [Array<Symbol, Module>] a value-object type or attribute names
14
+ # @param as [Symbol, nil] the field name to mint for a value-object-type target
15
+ # @yield a bespoke value-object body for the identity's own value object
16
+ # @raise [Bluebook::DSL::Malformed] on a repeat declaration or an invalid mix of forms
66
17
  # rubocop:disable-next Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
67
18
  def identified_by_impl(*targets, as: nil, &definition)
68
19
  return legacy_identified_by(*targets, as: as, &definition) if MetaValidator.shadow_parsing?
@@ -84,20 +35,8 @@ module Hecks
84
35
  &definition
85
36
  )
86
37
  rescue NameError => e
87
- # A removed spelling must refuse loudly, not degrade into a raw
88
- # Ruby error — the one contract `EraGuard.shadow_parse` leans
89
- # on to know a normal parse genuinely could not read this text
90
- # (only `Malformed` triggers its shadow-mode retry, era_guard.rb's
91
- # own comment). The old `identified_by { name.value }` — a block
92
- # whose text was never called, only extracted (legacy_
93
- # identified_by, below) — is exactly this shape: read under the
94
- # current grammar it is instead instance_eval'd as a value-object
95
- # definition, and a bare identifier like `name` inside it resolves
96
- # to nothing WordGate#method_missing recognizes, so Ruby itself
97
- # raises NameError. Left uncaught, that NameError skipped
98
- # shadow_parse's rescue entirely and reached callers as a raw
99
- # crash instead of the frozen-era fallback that exists for
100
- # precisely this text.
38
+ # Must raise Malformed: EraGuard.shadow_parse retries frozen text only on
39
+ # Malformed, and a bare identifier in an old block form raises NameError here.
101
40
  raise Malformed,
102
41
  "#{@name}.identified_by do ... end could not be read as a value-object " \
103
42
  "definition: #{e.message}"
@@ -122,10 +61,7 @@ module Hecks
122
61
  raise Malformed,
123
62
  "#{@name}.identified_by takes no as: — name the declared field itself"
124
63
  end
125
- # Transitional compatibility: the self-hosted language and live
126
- # corpus still contain this form. Keep it readable until their
127
- # exemplar-led migration is complete; the final lifecycle cutover
128
- # replaces this assignment with the targeted refusal.
64
+ # Transitional: the self-hosted language and live corpus still use this form.
129
65
  @identity_field_pending = field
130
66
  return
131
67
  end
@@ -141,11 +77,7 @@ module Hecks
141
77
 
142
78
  private
143
79
 
144
- # Resolves whichever of the three `identified_by` shapes is
145
- # pending, against `identity_pool` — the includer's own private
146
- # hook. Called at build time, not at `identified_by`'s own call
147
- # time — see `AttributeCollector#resolve_identity_field!`'s own
148
- # comment on why.
80
+ # Resolves the pending identity against `identity_pool` at build time, not call time.
149
81
  def resolve_pending_identity!
150
82
  if @identity_type_pending
151
83
  type, as, insert_at = @identity_type_pending
@@ -162,11 +94,7 @@ module Hecks
162
94
  def identity_type?(target) = target.to_s.match?(/\A[A-Z]/)
163
95
 
164
96
  def refuse_second_identity!
165
- # During the staged migration a transitional one-symbol declaration
166
- # may be replaced by the new declaration later in the same builder.
167
- # This keeps existing builder fixtures/source readable while their
168
- # exemplar is migrated. Once one-symbol identity is retired this
169
- # compatibility branch disappears with it.
97
+ # Transitional: a one-symbol declaration may still be replaced by a later one.
170
98
  if @identity_field_pending && !@identity_type_pending && !@identity_fields_pending
171
99
  @identity_field_pending = nil
172
100
  return
@@ -178,10 +106,7 @@ module Hecks
178
106
  raise Malformed, "#{@name} declares identified_by more than once"
179
107
  end
180
108
 
181
- # Legacy — the two removed spellings (a value object + as:, and the
182
- # multi-line block), kept alive only for `EraGuard.shadow_parse`
183
- # (S0a, ADR 0025) to still make sense of frozen era text that used
184
- # them; unreachable outside `MetaValidator.shadow_parsing?`.
109
+ # Parses the spellings frozen era text uses, for `EraGuard.shadow_parse`.
185
110
  def legacy_identified_by(*targets, as:, &path)
186
111
  target = targets.first
187
112
  if target
@@ -2,11 +2,7 @@ require_relative "word_gate"
2
2
  module Hecks
3
3
  module Bluebook
4
4
  module DSL
5
- # Parses a `lifecycle :field, default: ... do transition ... end`
6
- # block into a `Lifecycle` — the field an aggregate or entity's own
7
- # state machine lives on, its starting value, and the `command =>
8
- # target_state` transition table (with an optional `from:` guard)
9
- # each `transition_impl` call adds a row to.
5
+ # Parses a `lifecycle :field, default: ... do transition ... end` block into a `Lifecycle`.
10
6
  class LifecycleBuilder
11
7
  GRAMMAR_CONTEXT = "Lifecycle".freeze
12
8
 
@@ -22,11 +18,7 @@ module Hecks
22
18
 
23
19
  # Records one transition row per command in `mapping`, all sharing its `from:` guard.
24
20
  #
25
- # Answers the `transition` word, which the grammar table routes here
26
- # through its `calls:` column — item #13's full metaprogrammed
27
- # dispatch (slice 4c). Bootstrap-reachable (syntax.bluebook's
28
- # own Keyword/Argument entities describe their `status`
29
- # lifecycle with it), so in `BOOTSTRAP_CALLS_FALLBACK`.
21
+ # Listed in `BOOTSTRAP_CALLS_FALLBACK` because syntax.bluebook uses it during boot.
30
22
  #
31
23
  # @param mapping [Hash{String, Symbol => String, Symbol, Array<String>}] command name to
32
24
  # target state, as in `"Purchase" => "sold"`; the optional `:from` key holds the
@@ -70,17 +62,11 @@ module Hecks
70
62
 
71
63
  private
72
64
 
73
- # C5.3 (docs/semantics/bluebook-semantics.md) — two transitions for
74
- # one command whose `from:` sets overlap (or where either has no
75
- # `from:` at all) were silently first-wins; refused where the state
76
- # machine can be read whole. Two transitions for one command from
77
- # disjoint states are the legitimate shape (`match_transition`
78
- # picks by the current state) and stay. A `from:` naming a state
79
- # nothing declares is not refused here: it is a reachability
80
- # finding `bin/model_check` already reports (unreachable state,
81
- # dead transition), and a bluebook may declare it on purpose.
65
+ # C5.3 (docs/semantics/bluebook-semantics.md): overlapping `from:` sets for one command
66
+ # are refused. Disjoint sets are legitimate. An undeclared `from:` state is left to
67
+ # `bin/model_check`.
82
68
  def refuse_ambiguity!
83
- return if MetaValidator.shadow_parsing? # frozen era text is history
69
+ return if MetaValidator.shadow_parsing? # frozen era text is exempt
84
70
 
85
71
  @transitions.each_with_index do |(command, transition), index|
86
72
  earlier = @transitions[0...index].find do |other_command, other|