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
@@ -3,211 +3,34 @@ require_relative "bootstrap_table"
3
3
  module Hecks
4
4
  module Bluebook
5
5
  module DSL
6
- # Item #13's full metaprogrammed dispatch — slices 1-2 of 4, whole-
7
- # project table-unification survey. WordGate (the first slice,
8
- # already shipped) only checks a word's admissibility; this module
9
- # executes the safe, mechanical subset of what a word actually
10
- # does, read live off the same self-hosted grammar table — so a
11
- # builder method for one of these words no longer needs to be
12
- # hand-written at all. Called from `WordGate#method_missing`'s own
13
- # "admitted here, no builder method for it yet" branch, so it only
14
- # ever sees a word whose (context, word) pair the grammar already
15
- # admits.
16
- #
17
- # ## What is covered
18
- #
19
- # Scope, deliberately narrow and verified word-by-word — a full
20
- # audit read every real Ruby builder method's own source before
21
- # any of this was written, not inferred from the table's shape
22
- # alone (which fooled a first pass: `ReadModelBuilder#group_by`/
23
- # `#include` look like plain fills from `kind`/`fills` alone, but
24
- # actually wrap their arguments in a Hash/tuple). What's covered:
25
- #
26
- # - a single, non-variadic, positional Argument row whose value
27
- # is stored with nothing beyond a uniform, kind-driven
28
- # coercion (`COERCE_BY_KIND`) — or, for `kind: "text"`, no
29
- # coercion at all when the row's own `coerce: "false"` says so
30
- # (six real, audited exceptions: `.to_s` on an actual String
31
- # is a same-object no-op for every real corpus use, but a
32
- # genuine behavior change for a direct Ruby API call passing
33
- # something else)
34
- # - a scalar assign (`@ivar = value`) or list append (`@ivar <<
35
- # value`), chosen by whether the ivar `fills:` names is
36
- # already an Array at call time (every candidate's own
37
- # `initialize` pre-populates its accumulating ivars as `[]` —
38
- # read live, not assumed)
39
- # - a zero-argument word, either (a) one of several sibling
40
- # Keyword rows in the same context sharing the same `fills:`
41
- # target — derives its own stored value from its own word
42
- # name as a symbol (`core`/`generic`/`supporting` all fill
43
- # `classification`), or (b) the only Keyword row filling its
44
- # own `fills:` target — a bare marker, stores literal `true`
45
- # (`ReadModel#count`)
46
- # - an OPENS_BLOCK word named in `SAFE_OPENS_BLOCK` below,
47
- # verified to do nothing beyond "build the child, instance_
48
- # eval the block against it, append the result" — see that
49
- # constant's own comment
50
- # - (slice 2) a single-fill word whose row also names a `blank_
51
- # message:` — raises that exact Malformed text if the coerced
52
- # value is blank, before storing it, matching a real hand-
53
- # written guard exactly (`Translation#retired`/
54
- # `TranslationAggregate#drop`, the only two words in the whole
55
- # grammar with this precise shape — `required: "true"` alone
56
- # only gates Ruby's own arity, never a present-but-blank
57
- # value, and most `required: "true"` words do not raise on
58
- # blank, so this needed a real per-row signal, not an
59
- # assumption from `required:` alone)
60
- #
61
- # ## What stays hand-written
62
- #
63
- # Explicitly out of this slice (found live, during the audit, not
64
- # assumed) — stays hand-written, unaffected, until a later slice's
65
- # own new table columns (`gated_by:`/`calls:`) can name
66
- # what it really does: variadic accumulation with a real transform
67
- # (`attaches_to`); source-block extraction (`ensures`, `where`);
68
- # Struct/Hash-wrapping (`limit`, `group_by`, `rekey`); a guard
69
- # clause not reducible to a plain blank-check (`role`'s own "raise
70
- # if already set" uniqueness gate — a genuinely different shape
71
- # from `retired`/`drop`'s own blank-check, needing its own
72
- # design); a dynamically-built refusal message rather than one
73
- # fixed string (`TranslationAggregateBuilder#unresolved` — real
74
- # branching logic choosing between several message shapes, not a
75
- # simple "always refuses" flag; belongs with `calls:`, slice 4,
76
- # where a table row can name the real Ruby helper that already
77
- # builds it); a side effect beyond storage (`uses_framework`,
78
- # `uses_embryonaut_bluebook`); an opens-block word threading extra
79
- # constructor args to its child (`asks`/`tells`/`operation`,
80
- # `Bluebook#aggregate`, `Translation#aggregate`) or doing anything
81
- # beyond a bare fold after building (`Aggregate#policy`'s stamp,
82
- # `Aggregate#value_object`'s flatten, `Hecksagon#port`'s resolver-
83
- # swap-and-branch); the deferred-build queue pattern (`Aggregate`/
84
- # `Entity`'s own `command`/`entity`/`query`); the shadow_parsing?-
85
- # gated words (`has_many`/`has_one`/`belongs_to`/`then_set`/
86
- # `trigger`/`dispatch`/`one_of` — each delegates to its own
87
- # `legacy_*` method under `MetaValidator.shadow_parsing?`, else
88
- # refuses; a genuinely different two-branch shape per word, closer
89
- # to `calls:`'s own territory than a simple fill); and every
90
- # `File`-context word (routes through `Runtime.current_registry`,
91
- # a side effect this module has no business performing).
92
- #
93
- # ## Words that look table-safe and are not
94
- #
95
- # Two further, different reasons a word can look table-safe and
96
- # still be excluded, both found live rather than assumed:
97
- # - Bootstrap reachability. `WordGate`'s own gate steps aside
98
- # entirely while `MetaValidator.bootstrapping?` (the meta-
99
- # domain's own circularity — its grammar table doesn't exist
100
- # yet to read), and `load_grammar_into` loads real `.port`/
101
- # `.adapter` files, plus every core `.bluebook` chapter, during
102
- # that exact window. Any word those files actually call (found
103
- # to be nearly every common one — `vision`/`core`/`generic`/
104
- # `supporting`/`description`/`goal`/`emits`/`role`/`identified_
105
- # by` describe literally every self-hosted aggregate/command)
106
- # has no working method to fall back on if its hand-written
107
- # `def` is removed — `super` from `WordGate#method_missing`
108
- # just raises `NoMethodError`. `PortBuilder`/`AdapterBuilder`/
109
- # `BluebookBuilder` turned out to be entirely off-limits to
110
- # this slice for exactly this reason, confirmed by cold-
111
- # booting `MetaValidator.grammar_registry` after each
112
- # candidate removal, not assumed safe from reading the table
113
- # alone.
114
- # - A conflicting hand-written `method_missing`. `WorldBuilder`
115
- # and `HecksagonBuilder` each define their own `method_missing`
116
- # directly on the class (for genuinely open-ended settings/
117
- # collector verbs) — a method defined directly on a class always
118
- # wins over one from an included module in Ruby's own method
119
- # resolution, so `WordGate`'s (and this module's) own
120
- # `method_missing` never runs for their builders at all. Their
121
- # own candidate words (`realm`/`latest`, `subscribe`) stay
122
- # hand-written for this structural reason, independent of
123
- # whether their own behavior would otherwise qualify.
6
+ # Executes a grammar-admitted (context, word) pair straight off the DSL's own table when
7
+ # its row matches one of a few verified-safe shapes; anything else falls through unchanged.
124
8
  module GenericDispatch
125
9
  NOT_HANDLED = Object.new.freeze
126
10
 
127
11
  COERCE_BY_KIND = { "text" => :to_s, "symbol" => :to_sym }.freeze
128
12
 
129
- # (context, word) OPENS_BLOCK pairs verified, by reading every
130
- # real Ruby method, to do nothing beyond building the child and
131
- # appending it — see this file's own header for the full account
132
- # of what was excluded and why. Value: the ivar the built child
133
- # is appended into. Not derivable from `fills:` — every one of
134
- # these Keyword rows carries `fills: ""`; the append target is
135
- # implicit in the hand-written code today, never named by the
136
- # table at all, so this is the one place slice 1 hand-names a
137
- # fact the table doesn't yet carry, rather than guessing a
138
- # pluralization rule that would silently mismatch a future word.
13
+ # Not derivable from `fills:` (these rows all carry `fills: ""`) — the append target
14
+ # for each opens-block word must be named here explicitly.
139
15
  SAFE_OPENS_BLOCK = {
140
16
  %w[Bluebook policy] => :policies,
141
17
  %w[Bluebook process_manager] => :process_managers,
142
18
  %w[Bluebook read_model] => :read_models
143
19
  }.freeze
144
20
 
145
- # `WordGate#method_missing`'s own bootstrap-window fallback,
146
- # consulted only while `MetaValidator.bootstrapping?` (the real
147
- # table doesn't exist yet to read `keyword[:calls]` from). Names
148
- # the same (context, word) -> method pairs the real table's own
149
- # `calls:` column carries — every live row, projected ahead of
150
- # time into the committed lib/hecks/bluebook/dsl/bootstrap_table.rb
151
- # (bin/project_bootstrap_table, pinned by spec/bootstrap_table_spec.rb).
152
- #
153
- # Every row is carried, rather than a hand-kept subset checked for
154
- # bootstrap-reachability by grepping the core chapters. Carrying
155
- # all of them costs nothing: a builder with its own `def` never
156
- # reaches `method_missing`, and one without gains the same dispatch
157
- # it gets once bootstrapping ends. `["Type", word]` rows still cover
158
- # `list_of`/`one_of` in an attribute's type position for every
159
- # calling context (see `word_gate_dispatch`'s own header).
21
+ # Consulted only while `MetaValidator.bootstrapping?`, before the real grammar table
22
+ # exists to read `calls:` from; every row is carried since carrying them costs nothing.
160
23
  BOOTSTRAP_CALLS_FALLBACK = BootstrapTable::CALLS
161
24
 
162
25
  module_function
163
26
 
164
- # Reports whether `try` would execute a (context, word) pair, without executing anything.
165
- #
166
- # **The static predicate** — does this (context, word) pair fall
167
- # within this slice's own verified scope, without executing
168
- # anything? The same row-shape checks `try` itself runs before
169
- # ever touching a real argument, shared so a conformance spec
170
- # (which has no real call, no real builder instance) can ask the
171
- # same question `method_missing` answers live.
172
- #
173
- # @param context [String] the grammar context, a builder's `GRAMMAR_CONTEXT` such as
174
- # `"Aggregate"`, or `"Type"`
175
- # @param word [String] the DSL word; a Symbol never matches a row
176
- # @param rows [Hash{Symbol => Array<Hash{Symbol => String}>}] the grammar table, with
177
- # `:keywords` and `:arguments` row lists; defaults to the live self-hosted table
178
- # @return [Boolean] true when `try` would execute the word rather than answer `NOT_HANDLED`
27
+ # Whether `try` would execute this (context, word) pair, without executing anything.
179
28
  def handles?(context, word, rows: MetaValidator::SyntaxBoot.call)
180
29
  !shape_for(context, word, rows).nil?
181
30
  end
182
31
 
183
- # Executes a grammar-admitted word straight off the table when its row shape is one of
184
- # the four verified safe ones.
185
- #
186
- # `NOT_HANDLED` for anything outside this slice's own verified
187
- # scope — the caller (`WordGate#method_missing`) falls through
188
- # to its own existing "not yet implemented" refusal, unchanged,
189
- # the exact same message a word not yet migrated to any slice
190
- # already gets today. A real `ArgumentError` — matching what a
191
- # hand-written method of the same arity would raise — for a
192
- # call whose shape the grammar admits but whose actual argument
193
- # count doesn't match; never a silent wrong answer.
194
- #
195
- # @param builder [Bluebook::DSL::WordGate] the builder instance the word was called on
196
- # @param context [String] the grammar context the word was admitted under
197
- # @param word [String] the DSL word being executed
198
- # @param args [Array<Object>] the call's positional arguments
199
- # @param kwargs [Hash{Symbol => Object}] the call's keyword arguments
200
- # @param block [Proc, nil] the block given to the word, if any
201
- # @param rows [Hash{Symbol => Array<Hash{Symbol => String}>}] the grammar table, with
202
- # `:keywords` and `:arguments` row lists
203
- # @return [Object] what executing the word produced — the target method's own result
204
- # for a `calls:` row, otherwise the stored value or the list appended to — or
205
- # `NOT_HANDLED` when the word is outside the verified scope
206
- # @raise [ArgumentError] if the word is table-executed and the positional argument count
207
- # does not match what its row admits
208
- # @raise [Bluebook::DSL::Malformed] if a single-fill row names a `blank_message:` and the
209
- # value is blank, or the `calls:` target itself refuses the declaration
210
- # @raise [Runtime::WiringError] if `shape_for` names a kind this method has no arm for
32
+ # Executes a table-admitted word via the shape `shape_for` found for it, or returns
33
+ # NOT_HANDLED so the caller can fall back to its own "not yet implemented" refusal.
211
34
  def try(builder, context, word, args, kwargs, block, rows)
212
35
  shape = shape_for(context, word, rows)
213
36
  return NOT_HANDLED unless shape
@@ -218,40 +41,18 @@ module Hecks
218
41
  when :zero_arg then try_zero_arg(builder, shape[:keyword], args)
219
42
  when :single_fill then try_single_fill(builder, shape[:fills], shape[:argument], args, kwargs)
220
43
  else
221
- # `shape_for` names exactly four kinds and this case
222
- # matches all four — a backstop against the day a fifth is
223
- # added there without a matching arm here. Falling through
224
- # silently would return bare `nil` from what the caller
225
- # (`WordGate#method_missing`) reads as "handled" (anything
226
- # other than NOT_HANDLED counts), so a self-hosted DSL word
227
- # would silently execute as a no-op instead of raising the
228
- # same "not yet implemented" refusal a genuinely unmigrated
229
- # word already gets.
44
+ # Backstop: `shape_for` names exactly four kinds; falling through would return nil,
45
+ # which the caller reads as "handled", silently turning the word into a no-op.
230
46
  raise Runtime::WiringError,
231
47
  "no dispatcher handles shape #{shape[:kind].inspect} — add one before shape_for can produce it"
232
48
  end
233
49
  end
234
50
 
235
- # Classifies a (context, word) pair into the table-executable shape its rows describe.
236
- #
237
- # The one place row shape is judged — returns a small Hash naming
238
- # which of the four safe shapes (context, word) is, or `nil` if
239
- # it falls outside this slice's own verified scope. No argument
240
- # values are read here; this only ever looks at the table.
51
+ # Classifies a (context, word) pair into one of the four table-executable shapes, or
52
+ # nil when it falls outside this module's verified scope.
241
53
  #
242
- # An early-return classification chain: each `return` depends on
243
- # locals (`calls`, `fills`, `arguments`, `arg`) computed by the
244
- # checks before it. Splitting per shape would mean re-deriving or
245
- # threading those locals across method boundaries for a
246
- # classification that is only ever read top-to-bottom once, here.
247
- #
248
- # @param context [String] the grammar context to look the word up under
249
- # @param word [String] the DSL word; retired rows never match
250
- # @param rows [Hash{Symbol => Array<Hash{Symbol => String}>}] the grammar table, with
251
- # `:keywords` and `:arguments` row lists
252
- # @return [Hash{Symbol => Object}, nil] `:kind` is `:calls_through` (with `:calls`, the
253
- # target method name), `:opens_block` or `:zero_arg` (with `:keyword`, the row), or
254
- # `:single_fill` (with `:fills` and `:argument`); nil when the word is out of scope
54
+ # Early-return chain: each check depends on locals from the ones before it, read once
55
+ # top-to-bottom; splitting per shape would just re-thread the same locals across methods.
255
56
  # rubocop:disable-next Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
256
57
  def shape_for(context, word, rows)
257
58
  keyword = rows[:keywords].find { |k| k[:context] == context && k[:word] == word && k[:status] != "retired" }
@@ -277,44 +78,14 @@ module Hecks
277
78
  { kind: :single_fill, fills: fills, argument: arg }
278
79
  end
279
80
 
280
- # Forwards a word's whole call to the builder method its row's `calls:` column names.
281
- #
282
- # `keyword[:calls]` names a real Ruby method whose whole call —
283
- # every positional, every kwarg, the block, all of it — forwards
284
- # here unchanged. No argument-shape interpretation at all,
285
- # deliberately: the target method (`AttributeCollector#
286
- # attribute_impl`, etc.) already does its own, real, hand-
287
- # written argument handling; this is a pure, transparent `send`,
288
- # the lowest-risk possible shape for a word whose own logic is
289
- # too complex to re-derive from the table (type-quoting refusal,
290
- # closed-set synthesis, pattern validation, ...).
291
- #
292
- # @param builder [Bluebook::DSL::WordGate] the builder instance to send the call to
293
- # @param calls [String, Symbol] the target method's name, such as `"attribute_impl"`
294
- # @param args [Array<Object>] positional arguments, forwarded unchanged
295
- # @param kwargs [Hash{Symbol => Object}] keyword arguments, forwarded unchanged
296
- # @param block [Proc, nil] the block, forwarded unchanged
297
- # @return [Object] whatever the target method returns
298
- # @raise [Bluebook::DSL::Malformed] if the target method refuses the declaration
81
+ # Forwards a word's whole call, unchanged, to the builder method its row's `calls:`
82
+ # names — no argument-shape interpretation; the target method already does its own.
299
83
  def try_calls_through(builder, calls, args, kwargs, block)
300
84
  builder.send(calls, *args, **kwargs, &block)
301
85
  end
302
86
 
303
- # Builds a child construct from a block-opening word and appends it to the builder's list.
304
- #
305
- # @param builder [Bluebook::DSL::WordGate] the builder whose list receives the child
306
- # @param keyword [Hash{Symbol => String}] the word's Keyword row; `:opens` names the
307
- # child builder class without its `Builder` suffix, and the pair of `:context` and
308
- # `:word` must be a `SAFE_OPENS_BLOCK` key
309
- # @param args [Array<Object>] the call's positional arguments; exactly one, the child's
310
- # name
311
- # @param kwargs [Hash{Symbol => Object}] the call's keyword arguments
312
- # @param block [Proc, nil] the child's body, evaluated against the child builder
313
- # @return [Array<Object>, Object] the builder's list with the built child appended, or
314
- # `NOT_HANDLED` when any keyword argument was given
315
- # @raise [ArgumentError] if the call does not carry exactly one positional argument
316
- # @raise [KeyError] if the row's context and word are not in `SAFE_OPENS_BLOCK`
317
- # @raise [Bluebook::DSL::Malformed] if the child builder refuses its own body
87
+ # Builds a child construct from a block-opening word and appends it to the builder's
88
+ # list named by its row in `SAFE_OPENS_BLOCK`.
318
89
  def try_opens_block(builder, keyword, args, kwargs, block)
319
90
  return NOT_HANDLED unless kwargs.empty?
320
91
 
@@ -331,14 +102,6 @@ module Hecks
331
102
  end
332
103
 
333
104
  # Stores a zero-argument word's value into the instance variable its row's `fills:` names.
334
- #
335
- # @param builder [Bluebook::DSL::WordGate] the builder whose instance variable is set
336
- # @param keyword [Hash{Symbol => String}] the word's Keyword row, read for `:context`,
337
- # `:fills` and `:word`
338
- # @param args [Array<Object>] the call's positional arguments, which must be empty
339
- # @return [Symbol, true] the stored value: the word itself as a Symbol when sibling words
340
- # share the same `fills:` target (`core`/`generic`/`supporting`), otherwise `true`
341
- # @raise [ArgumentError] if any positional argument was given
342
105
  def try_zero_arg(builder, keyword, args)
343
106
  raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 0)" unless args.empty?
344
107
 
@@ -352,19 +115,7 @@ module Hecks
352
115
  end
353
116
 
354
117
  # Coerces a word's one positional argument and stores it, appending when the target
355
- # instance variable already holds an Array and assigning otherwise.
356
- #
357
- # @param builder [Bluebook::DSL::WordGate] the builder whose instance variable is filled
358
- # @param fills [String] the instance variable's name, without the `@`
359
- # @param arg [Hash{Symbol => String}] the word's single Argument row, read for `:kind`,
360
- # `:coerce` and `:blank_message`
361
- # @param args [Array<Object>] the call's positional arguments; exactly one
362
- # @param kwargs [Hash{Symbol => Object}] the call's keyword arguments
363
- # @return [Object] the stored value, or the Array it was appended to, or `NOT_HANDLED`
364
- # when any keyword argument was given
365
- # @raise [ArgumentError] if the call does not carry exactly one positional argument
366
- # @raise [Bluebook::DSL::Malformed] if the row names a `blank_message:` and the coerced
367
- # value is blank
118
+ # ivar already holds an Array and assigning otherwise.
368
119
  def try_single_fill(builder, fills, arg, args, kwargs)
369
120
  return NOT_HANDLED unless kwargs.empty?
370
121
 
@@ -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,168 +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.
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.
186
75
  def translates(name, &block)
187
76
  bluebook_ir = Hecks.current_registry.bluebook(@domain) or
188
77
  raise Malformed, "#{@domain} declares no such bluebook — translates needs one to attach its reaction to"
189
78
 
190
- # SAME RESOLVER BluebookBuilder ITSELF uses for a `policy`
191
- # block's own `on`/`trigger` (bluebook_builder.rb) — a
192
- # `ScopedConstant`, not the bare passthrough `port_impl` swaps
193
- # to, because `on`/`trigger` here take genuinely multi-segment
194
- # references (`Deploy::Tenant::TenantProvisioned`), not a
195
- # 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.
196
81
  resolver = ->(const) { ConstShim::ScopedConstant.for(const) }
197
82
  built = ConstShim.with(resolver) { PolicyBuilder.build(name, &block) }
198
83
 
@@ -201,58 +86,18 @@ module Hecks
201
86
  end
202
87
 
203
88
  # Assembles the collected binds, subscriptions and attachments into a `Hecksagon`.
204
- #
205
- # No ungoverned-role check here — see
206
- # Registry::Verification#refuse_ungoverned_roles!. It lives outside
207
- # per-block `build`, alongside `environment:`
208
- # (Runtime::Loader.boot's comment has the provenance): a domain
209
- # split across multiple hecksagon blocks (base + an
210
- # `environments/<name>.hecksagon` overlay) would have every
211
- # block but the one declaring `uses_framework "Governance"`
212
- # refused here, even though `Registry#add_hecksagon` merges them
213
- # into one Hecksagon before anything ever dispatches against it.
214
- # Checking the merged result once, at verify! time — after every
215
- # file for this domain has loaded — is both more permissive (no
216
- # need to repeat `uses_framework` in every file) and strictly
217
- # more correct (a check against an incomplete, not-yet-merged
218
- # hecksagon can never see the real final shape).
219
- #
220
- # @return [Bluebook::Hecksagon] this block's wiring, which `Registry#add_hecksagon` merges
221
- # 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.
222
90
  def build
223
91
  Hecksagon.new(domain: @domain, binds: @binds, subscriptions: @subscriptions,
224
92
  framework_members: @framework_members, vendored_bluebooks: @vendored_bluebooks,
225
93
  bounded: @bounded, translates: @translates)
226
94
  end
227
95
 
228
- # Records any verb the grammar does not own as a domain-wide bind to the named adapter.
229
- #
230
- # **Domain-level default binds** — `persisted_by "Heki"` bare, at the top
231
- # of a hecksagon block, applies to every aggregate in this domain
232
- # that doesn't declare its own override. Mirrors `BindingProxy`'s own
233
- # `method_missing` one level down (`aggregate:` filled in there,
234
- # `nil` here) — generic over verb name, not hardcoded to
235
- # `persisted_by`/`projected_by` specifically, so any future verb
236
- # gets a domain-level default for free too. See `Hecksagon#bind_for`
237
- # for the fallback lookup this feeds.
238
- #
239
- # @param verb [Symbol] the word called, such as `:persisted_by`
240
- # @param args [Array<Object>] positional arguments; the first names the adapter
241
- # @param kwargs [Hash{Symbol => Object}] keyword arguments; `:role` is kept on the bind
242
- # @yield an optional block, called once after the bind is recorded
243
- # @return [Object] this builder after recording a bind, or the grammar word's own result
244
- # when `verb` is one the `Hecksagon` grammar admits
245
- # @raise [NoMethodError] if `verb` is no grammar word and names no adapter
246
- # @raise [Bluebook::DSL::Malformed] if the grammar admits `verb` only in another context,
247
- # 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.
248
98
  def method_missing(verb, *args, **kwargs, &block)
249
- # A closed-set grammar word (`port`, today) gets first refusal
250
- # — item #13's full metaprogrammed dispatch (slice 5); see
251
- # `WordGate#word_gate_dispatch`'s own header for why this class
252
- # needs to call it explicitly rather than including it the
253
- # ordinary way. Only once that says "not admitted" does the
254
- # genuinely open-ended `persisted_by "Heki"`-style bind
255
- # 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.
256
101
  result = word_gate_dispatch(verb, args, kwargs, block)
257
102
  return result unless result.equal?(WordGate::NOT_ADMITTED)
258
103
 
@@ -267,14 +112,6 @@ module Hecks
267
112
 
268
113
  # Evaluates a `Hecks.hecksagon` block with bare `Domain::Aggregate` constants resolving to
269
114
  # bind-collecting proxies, and returns the wiring it declared.
270
- #
271
- # @param domain [String] name of the domain being wired
272
- # @yield the hecksagon body, evaluated with the builder as `self`; may be omitted
273
- # @return [Bluebook::Hecksagon] the declared wiring
274
- # @raise [Bluebook::DSL::Malformed] if a `port` names no registered bluebook or aggregate,
275
- # or declares a shape the port grammar refuses
276
- # @raise [Runtime::WiringError] if `uses_framework` or `uses_embryonaut_bluebook` names
277
- # something that cannot be found
278
115
  def self.build(domain, &block)
279
116
  builder = new(domain)
280
117
  resolver = ->(name) { BindingProxy.namespace(name, builder.binds) }