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,65 +2,20 @@ require_relative "../naming"
2
2
 
3
3
  module Hecks
4
4
  module Projector
5
- # A bluebook, projected as its own usage documentation.
6
- #
7
- # ## What this is for
8
- #
9
- # A chapter in this corpus already contains everything a user of it
10
- # needs: what each aggregate is (`description`), what each verb is
11
- # for (`goal`) and who issues it (`role`), which states it moves
12
- # between, what it refuses and in whose words (`given`, `invariant`,
13
- # `ensures`), and what each list is worth reading (`description` on
14
- # a query). None of that reaches the person who has to call the
15
- # domain. They read the source, or they read a document somebody
16
- # wrote beside the source and stopped updating.
17
- #
18
- # `docs/implemented/reference/` is the precedent and the contrast. `bin/reference`
19
- # generates it from the language's own Syntax chapter, so the reference
20
- # for the DSL cannot drift from the DSL. This is the same trick one level
21
- # down: the usage document for a domain, generated from that domain, so it
22
- # cannot drift from the domain either.
23
- #
24
- # ## Why a projection, not a generator script
25
- #
26
- # `Projector` is the repository's registry of "canonical IR in,
27
- # external artifact out" (§30 of the implementation plan), and this
28
- # is exactly that shape: one bluebook's IR in, markdown out, no
29
- # runtime needed and no store touched. Registered as `:docs` beside
30
- # `:ir`, and reachable the way every projector is —
31
- # `Projector.call(:docs, bluebook: ...)`.
32
- #
33
- # And as a method, which is the half that makes it get used.
34
- # `Facade::Surface` already installs a module per chapter carrying
35
- # `vision` and `aggregates`; `docs` joins them, so a booted domain answers
36
- # `QualityControl.docs` and an aggregate door answers
37
- # `QualityControl::Bug.docs`. A document you have to remember a script for
38
- # is a document nobody reads.
39
- #
40
- # ## What it deliberately does not do
41
- #
42
- # Invent. Every sentence below comes out of the chapter. Where a
43
- # chapter says nothing — an aggregate with no `description`, a
44
- # command with no `goal` — the document says nothing rather than
45
- # filling the gap with a restatement of the name, because a
46
- # generated paragraph that only rephrases an identifier teaches a
47
- # reader to skim the ones that do not.
5
+ # Projects a bluebook as its own usage documentation, in Markdown.
6
+ # Reach it as `Projector.call(:docs, bluebook: ...)` or `Domain.docs`.
48
7
  module DocsProjector
49
8
  module_function
50
9
 
51
- # Projects `bluebook` as its own usage documentation.
52
- #
53
- # `options[:heading]` sets the top heading level (default 1), so a
54
- # caller splicing this into a larger document can push it down.
10
+ # Projects `bluebook` as Markdown usage documentation.
55
11
  #
56
12
  # @param bluebook [Bluebook::Behaviour::Chapter] the chapter to document
57
13
  # @param options [Hash] optional inputs
58
14
  # @option options [Integer, String] :heading the top heading level; defaults to 1
59
15
  # @option options [String, Symbol, nil] :aggregate narrows the document to one
60
16
  # aggregate, omitting the chapter header and closing sections
61
- # @return [String] the document, as Markdown, ending in a newline
62
- # @raise [Runtime::NotFound] if `options[:aggregate]` names no aggregate `bluebook`
63
- # declares
17
+ # @return [String] the document, ending in a newline
18
+ # @raise [Runtime::NotFound] if `options[:aggregate]` names no declared aggregate
64
19
  def call(bluebook:, options: {})
65
20
  depth = (options[:heading] || 1).to_i
66
21
  only = options[:aggregate]
@@ -72,18 +27,7 @@ module Hecks
72
27
  "#{out.compact.join("\n").rstrip}\n"
73
28
  end
74
29
 
75
- # A name that names nothing is refused, not answered with an empty
76
- # document. Shipped the other way first: `options[:aggregate]` that
77
- # matched no head returned "" and exit 0, which is the silent-wrong-
78
- # answer shape this repository has already been bitten by twice in the
79
- # query engine. A misspelling should cost a sentence, not a puzzle.
80
- #
81
- # @param bluebook [Bluebook::Behaviour::Chapter] the chapter to look in
82
- # @param only [String, Symbol, nil] narrows the result to the one aggregate
83
- # named; nil answers every aggregate
84
- # @return [Array<Bluebook::Aggregate>, Bluebook::Aggregate] every declared
85
- # aggregate, or the single aggregate `only` names
86
- # @raise [Runtime::NotFound] if `only` names no aggregate `bluebook` declares
30
+ # An unknown aggregate name is refused rather than answered with an empty document.
87
31
  def aggregates(bluebook, only)
88
32
  return bluebook.aggregates unless only
89
33
 
@@ -93,25 +37,10 @@ module Hecks
93
37
  "it declares #{bluebook.aggregates.map(&:hecks_name).sort.join(', ')}")
94
38
  end
95
39
 
96
- # Renders a Markdown heading line.
97
- #
98
- # @param depth [Integer] the heading level
99
- # @param text [String] the heading text
100
- # @return [String] a Markdown `#`-prefixed heading line
101
40
  def h(depth, text) = "#{'#' * depth} #{text}"
102
41
 
103
- # ── the chapter ───────────────────────────────────────────────────
104
-
105
- # Renders the chapter-level header: its title, vision, classification,
106
- # former name, and aggregate list.
107
- #
108
- # @param bluebook [Bluebook::Behaviour::Chapter] the chapter to document
109
- # @param depth [Integer] the heading level for the chapter's own title
110
- # @return [String] the header's Markdown source
111
42
  def chapter_header(bluebook, depth)
112
43
  out = [h(depth, bluebook.name), ""]
113
- # The vision first and as a quote. It is the one sentence in a chapter
114
- # written for somebody who does not know the domain yet.
115
44
  out += ["> #{bluebook.vision}", ""] if bluebook.vision
116
45
  out << "#{bluebook.classification.to_s.capitalize} domain." if bluebook.classification
117
46
  out << "Previously known as `#{bluebook.formerly_known_as}`." if bluebook.formerly_known_as
@@ -121,22 +50,8 @@ module Hecks
121
50
  out.join("\n")
122
51
  end
123
52
 
124
- # Builds a construct's own GitHub-style heading anchor.
125
- #
126
- # @param name [String, Symbol] the construct name to anchor
127
- # @return [String] the construct's own GitHub-style heading anchor
128
53
  def anchor(name) = Naming.snake(name).tr("_", "-")
129
54
 
130
- # What happens without anybody asking — the part of a domain a caller
131
- # cannot discover from any verb list, and the part most likely to surprise
132
- # them. A policy means one dispatch causes another, sometimes into a
133
- # different domain entirely; a saga means a sequence is being driven on
134
- # their behalf and can end in more than one place.
135
- #
136
- # @param bluebook [Bluebook::Behaviour::Chapter] the chapter to document
137
- # @param depth [Integer] the heading level for the "Reactions"/saga sections
138
- # @return [String, nil] the closing's Markdown source, or nil if `bluebook`
139
- # declares no policy and no process manager
140
55
  def closing(bluebook, depth)
141
56
  out = []
142
57
 
@@ -165,14 +80,6 @@ module Hecks
165
80
  out.empty? ? nil : out.join("\n")
166
81
  end
167
82
 
168
- # ── one aggregate ─────────────────────────────────────────────────
169
-
170
- # Documents one aggregate: its description, identity, references,
171
- # attributes, lifecycle, verbs, queries, and nested entities.
172
- #
173
- # @param aggregate [Bluebook::Aggregate] the aggregate to document
174
- # @param depth [Integer] the heading level for the aggregate's own title
175
- # @return [String] the aggregate's Markdown section
176
83
  def aggregate_section(aggregate, depth)
177
84
  out = [h(depth, aggregate.hecks_name), ""]
178
85
  out += [aggregate.description, ""] if aggregate.description
@@ -192,18 +99,10 @@ module Hecks
192
99
  out.compact.join("\n")
193
100
  end
194
101
 
195
- # Documents one entity nested under `aggregate`.
196
- #
197
- # @param aggregate [Bluebook::Aggregate] the entity's own owning aggregate
198
- # @param entity [Bluebook::Entity] the entity to document
199
- # @param depth [Integer] the heading level for the entity's own title
200
- # @return [String] the entity's Markdown section
201
102
  def entity_section(aggregate, entity, depth)
202
103
  out = [h(depth, "#{entity.hecks_name} (within #{aggregate.hecks_name})"), ""]
203
104
  out += [entity.description, ""] if entity.description
204
- # The thing a caller gets wrong first. An entity has no door of its
205
- # own: its verb is spelled through the aggregate that holds it, and
206
- # the parent's id travels alongside the entity's own identity.
105
+ # An entity has no door of its own; its verbs go through the holding aggregate.
207
106
  out << "Addressed through its holder — `#{aggregate.hecks_name}.#{entity.hecks_name}.<Verb>`, " \
208
107
  "passing the #{aggregate.hecks_name}'s `id` and this element's " \
209
108
  "`#{entity.identity_heads.join('`, `')}`."
@@ -215,14 +114,6 @@ module Hecks
215
114
  out.compact.join("\n")
216
115
  end
217
116
 
218
- # ── the shape ─────────────────────────────────────────────────────
219
-
220
- # Renders a holder's own non-reference attributes as a Markdown table.
221
- #
222
- # @param holder [Bluebook::Aggregate, Bluebook::Entity] the holder whose
223
- # attributes to render
224
- # @return [String, nil] the attribute/shape/rules table, or nil if `holder`
225
- # declares no non-reference attribute
226
117
  def attributes_table(holder)
227
118
  attributes = holder.attributes.reject(&:reference?)
228
119
  return nil if attributes.empty?
@@ -233,15 +124,7 @@ module Hecks
233
124
  table(%w[attribute shape rules], rows)
234
125
  end
235
126
 
236
- # A value object's fields, not its name. `commit` typed `CommitRef` tells
237
- # a caller nothing; `{ value: String }` tells them what to send, which is
238
- # the single most common thing to get wrong at this boundary — a bare
239
- # scalar where an object is wanted.
240
- #
241
- # @param attribute [Bluebook::Attribute] the attribute to describe
242
- # @param holder [Bluebook::Aggregate, Bluebook::Entity] `attribute`'s own holder
243
- # @return [String] the attribute's shape: its value object's own fields, or its
244
- # scalar type, with "list of" and "*(optional)*" applied as declared
127
+ # A value object's fields, not its name: callers need to know what shape to send.
245
128
  def shape_of(attribute, holder)
246
129
  value_object = value_object_for(attribute, holder)
247
130
  inner =
@@ -254,12 +137,6 @@ module Hecks
254
137
  attribute.optional? ? "#{shape} *(optional)*" : shape
255
138
  end
256
139
 
257
- # Lists an attribute's own rules: closed-set members, field patterns and
258
- # defaults, invariants, and its own default.
259
- #
260
- # @param attribute [Bluebook::Attribute] the attribute to describe
261
- # @param holder [Bluebook::Aggregate, Bluebook::Entity] `attribute`'s own holder
262
- # @return [String] the attribute's rules, joined with "; "; `""` if it has none
263
140
  def rules_of(attribute, holder)
264
141
  value_object = value_object_for(attribute, holder)
265
142
  rules = []
@@ -273,25 +150,13 @@ module Hecks
273
150
  rules.empty? ? "" : rules.join("; ")
274
151
  end
275
152
 
276
- # Names a closed set's own members.
277
- #
278
- # @param value_object [Bluebook::ValueObject, nil] the value object to check
279
- # @return [Array<Object>] every unique value across `value_object`'s own member
280
- # rows, or `[]` if `value_object` is nil or not a closed set
281
153
  def closed_members(value_object)
282
154
  return [] unless value_object&.closed_set?
283
155
 
284
156
  value_object.members.flat_map(&:values).uniq
285
157
  end
286
158
 
287
- # An entity holds no value objects of its own — its argument types are
288
- # declared on the aggregate above it.
289
- #
290
- # @param attribute [Bluebook::Attribute] the attribute whose type to resolve
291
- # @param holder [Bluebook::Aggregate, Bluebook::Entity] `attribute`'s own holder
292
- # @return [Bluebook::ValueObject, nil] the value object class `attribute`'s type
293
- # names, found on `holder` or its own owning aggregate; nil if `attribute`'s
294
- # type is not a value object
159
+ # An entity holds no value objects; its types are declared on the owning aggregate.
295
160
  def value_object_for(attribute, holder)
296
161
  scopes = [holder, holder.respond_to?(:hecks_owner) ? holder.hecks_owner : nil].compact
297
162
  scopes.each do |scope|
@@ -303,15 +168,6 @@ module Hecks
303
168
  nil
304
169
  end
305
170
 
306
- # ── the machine ───────────────────────────────────────────────────
307
-
308
- # Renders a holder's own lifecycle as a "starting state" sentence and a
309
- # verb/from/to table.
310
- #
311
- # @param holder [Bluebook::Aggregate, Bluebook::Entity] the holder to document
312
- # @param depth [Integer] the heading level for the "Lifecycle" section
313
- # @return [String, nil] the section's Markdown source, or nil if `holder`
314
- # declares no lifecycle
315
171
  def lifecycle_section(holder, depth)
316
172
  lifecycle = holder.lifecycle or return nil
317
173
 
@@ -323,15 +179,6 @@ module Hecks
323
179
  table(%w[verb from to], rows)].join("\n")
324
180
  end
325
181
 
326
- # ── the verbs ─────────────────────────────────────────────────────
327
-
328
- # Documents every command a holder declares.
329
- #
330
- # @param holder [Bluebook::Aggregate, Bluebook::Entity] the holder whose
331
- # commands to document
332
- # @param depth [Integer] the heading level for the "Verbs" section
333
- # @return [String, nil] the section's Markdown source, or nil if `holder`
334
- # declares no command
335
182
  def verbs_section(holder, depth)
336
183
  commands = holder.commands
337
184
  return nil if commands.empty?
@@ -341,13 +188,6 @@ module Hecks
341
188
  out.join("\n")
342
189
  end
343
190
 
344
- # Documents one command: its goal, role, arguments, refusals,
345
- # guarantees, and emitted events.
346
- #
347
- # @param command [Bluebook::Command] the command to document
348
- # @param holder [Bluebook::Aggregate, Bluebook::Entity] `command`'s own holder
349
- # @param depth [Integer] the heading level for the command's own title
350
- # @return [String] the command's Markdown section
351
191
  def command_entry(command, holder, depth)
352
192
  out = [h(depth, "#{command.hecks_name}#{' *(creates)*' if command.creates?}"), ""]
353
193
  out += [command.goal, ""] if command.goal
@@ -371,15 +211,6 @@ module Hecks
371
211
  out.join("\n")
372
212
  end
373
213
 
374
- # The argument table's own rows — a pure per-attribute mapping with
375
- # nothing to share with `command_entry`'s other sections, extracted
376
- # only to keep that method to the one shape every section there
377
- # follows: build a chunk, append it if non-empty.
378
- #
379
- # @param arguments [Array<Bluebook::Attribute>] the command's own arguments
380
- # @param holder [Bluebook::Aggregate, Bluebook::Entity] the arguments' own holder
381
- # @return [Array<Array(String, String, String)>] one `[argument, shape, needed]`
382
- # row per argument
383
214
  def command_argument_rows(arguments, holder)
384
215
  arguments.map do |attribute|
385
216
  shape = attribute.reference? ? "id of a `#{attribute.type.target_name}`" : shape_of(attribute, holder)
@@ -387,15 +218,7 @@ module Hecks
387
218
  end
388
219
  end
389
220
 
390
- # Every way this verb can say no, gathered from the three places a
391
- # chapter states them — the lifecycle it is an edge of, its own
392
- # `given`s, and the fact that a reference has to resolve. A caller
393
- # reading only the argument list learns none of these, and they are
394
- # most of what a domain is.
395
- #
396
- # @param command [Bluebook::Command] the command to gather refusals for
397
- # @param holder [Bluebook::Aggregate, Bluebook::Entity] `command`'s own holder
398
- # @return [Array<String>] every way `command` can refuse, stated as sentences
221
+ # Refusals come from the lifecycle edge, the command's `given`s and reference resolution.
399
222
  def refusals_of(command, holder)
400
223
  refusals = []
401
224
 
@@ -416,14 +239,6 @@ module Hecks
416
239
  refusals + command.givens.map { |given| "not: #{given.description}" }
417
240
  end
418
241
 
419
- # ── the reads ─────────────────────────────────────────────────────
420
-
421
- # Renders a list of queries as one Markdown paragraph per query.
422
- #
423
- # @param queries [Array<Bluebook::Query>] the queries to document
424
- # @param depth [Integer] the heading level for `title`
425
- # @param title [String] the section's own heading text
426
- # @return [String, nil] the section's Markdown source, or nil if `queries` is empty
427
242
  def queries_section(queries, depth, title)
428
243
  return nil if queries.empty?
429
244
 
@@ -440,12 +255,6 @@ module Hecks
440
255
  out.join("\n")
441
256
  end
442
257
 
443
- # Renders a Markdown pipe table.
444
- #
445
- # @param headers [Array<String>] the column headers
446
- # @param rows [Array<Array<String>>] each row's own cell values, matching
447
- # `headers`' width
448
- # @return [String] the rendered table, ending in a blank line
449
258
  def table(headers, rows)
450
259
  lines = ["| #{headers.join(' | ')} |", "|#{headers.map { '---' }.join('|')}|"]
451
260
  rows.each { |row| lines << "| #{row.join(' | ')} |" }
@@ -3,13 +3,8 @@ require_relative "../ports/persistence"
3
3
 
4
4
  module Hecks
5
5
  module Projector
6
- # Registry-wide serialization to Hash/JSON: bluebook IR
7
- # (`call`/`json`), era-adapter lineage-capability flags (`lineage`),
8
- # and translation edges in both their digest-relevant declared shape
9
- # (`translation_hash`, what ApprovalDigest hashes) and their
10
- # consumer-ready compiled shape with precompiled SQL attached
11
- # (`translations`/`compiled_translation_aggregate`). Read directly by
12
- # bin/ir, bin/project_rust, and the translation/audit approval digest.
6
+ # Registry-wide serialization to Hash/JSON, for the canonical bluebook IR,
7
+ # binding facts, and translation edges (declared and compiled).
13
8
  module Exporter
14
9
  module_function
15
10
 
@@ -29,27 +24,10 @@ module Hecks
29
24
  JSON.pretty_generate(call(registry))
30
25
  end
31
26
 
32
- # A binding fact, deliberately not folded into `call`/`bluebook.to_h`
33
- # above — the canonical IR is runtime-independent by design (ADR
34
- # 0001: it describes what a bluebook declares, never which adapter
35
- # a deployment happens to bind it to), and "is this aggregate bound
36
- # to a lineage-capable adapter" is exactly the kind of fact that
37
- # answer can change per-deployment without the bluebook's own shape
38
- # changing at all. Consumers that need it (bin/project_rust's own
39
- # `ir.json` sidecar, rust/host's runtime era-aware seed overlay —
40
- # dispatch.rs) merge this in as a separate top-level key, the same
41
- # way `translations` already sits beside `call`'s output rather than
42
- # inside it.
27
+ # Whether each aggregate is bound to a lineage-capable adapter — a per-deployment
28
+ # binding fact, not part of the declared IR `call` exports (ADR 0001).
29
+ # Empty when the era persistence plugin isn't loaded, rather than raising.
43
30
  #
44
- # Reuses `Runtime::EraCheck`'s own capability predicates rather than
45
- # re-deriving them — the boot-time gate and this export must never
46
- # answer differently for the same aggregate.
47
- #
48
- # ADR 0033 — `Runtime::EraCheck` lives in the (optional) era
49
- # persistence plugin now; unloaded, this answers exactly what it
50
- # already answers for a domain with nothing lineage-capable bound —
51
- # `capable_aggregates: []` — rather than raising on an undefined
52
- # constant.
53
31
  # @param registry [Runtime::Registry] the booted registry `domain_name` is loaded in
54
32
  # @param domain_name [String] the domain to check era-adapter lineage capability for
55
33
  # @return [Hash{Symbol => Array<Hash{Symbol => String}>}] `:capable_aggregates`,
@@ -68,20 +46,9 @@ module Hecks
68
46
  { capable_aggregates: capable.map { |aggregate| { name: aggregate.name, storage_name: aggregate.storage_name } } }
69
47
  end
70
48
 
71
- # A binding fact, same shape/reasoning as `lineage` above: every
72
- # aggregate's declared persistence adapter name (`persisted_by`),
73
- # not part of the canonical bluebook shape `call` exports (ADR
74
- # 0001 — the IR describes what's declared, never which adapter a
75
- # deployment binds it to). Unlike `lineage`, this needs no era
76
- # plugin — `BindingPolicy` is core, always loaded — and covers
77
- # every aggregate, not just lineage-capable ones: `rust/host`
78
- # (`ir.rs`'s own `refuse_unsupported_persistence_adapters`) reads
79
- # this to refuse loudly, at boot, against a domain bound to an
80
- # adapter it has no backend for (Heki, Memory, Sqlite, D1,
81
- # LocalStorage — `rust/host` understands only Postgres/PostgresEra
82
- # today), rather than silently building up a second, disjoint
83
- # history nothing but Rust ever reads while the real state stays
84
- # wherever its own adapter actually wrote it.
49
+ # Every aggregate's declared persistence adapter (`persisted_by`) — a binding
50
+ # fact like `lineage`, not part of the declared IR `call` exports (ADR 0001).
51
+ #
85
52
  # @param registry [Runtime::Registry] the booted registry `domain_name` is loaded in
86
53
  # @param domain_name [String] the domain to export persistence bindings for
87
54
  # @return [Hash{Symbol => Array<Hash{Symbol => Object}>}] `:aggregates`, each a
@@ -99,13 +66,9 @@ module Hecks
99
66
  { aggregates: aggregates }
100
67
  end
101
68
 
102
- # A binding fact, same shape/reasoning as `lineage`/`persistence`
103
- # above: which chapter this domain's role checks resolve against
104
- # (`Registry#authorization_provider_for` — the domain's own chapter
105
- # or a framework member it attaches that declares `provides
106
- # "authorization"`), with that chapter's declared verbs qualified.
107
- # `rust/host` (auth.rs) reads this instead of naming Governance.
108
- # `{}` when nothing this domain attaches provides authorization.
69
+ # Which chapter this domain's role checks resolve against
70
+ # (`Registry#authorization_provider_for`); `{}` if none does.
71
+ #
109
72
  # @param registry [Runtime::Registry] the booted registry `domain_name` is loaded in
110
73
  # @param domain_name [String] the domain to export the authorization binding for
111
74
  # @return [Hash{Symbol => String, nil}] `:provider` (name), `:grant`, `:assignments`
@@ -126,11 +89,9 @@ module Hecks
126
89
  }
127
90
  end
128
91
 
129
- # Same seam as `authorization` — which chapter answers "who may
130
- # sign in" (`Registry#membership_provider_for`), with its declared
131
- # verbs qualified and the membership aggregate named off `admit`.
132
- # rust/host reads this instead of HECKS_MEMBERSHIP_AGGREGATE.
133
- # `{}` when nothing this domain attaches provides membership.
92
+ # Which chapter answers "who may sign in" (`Registry#membership_provider_for`),
93
+ # with the membership aggregate named off `admit`; `{}` if none does.
94
+ #
134
95
  # @param registry [Runtime::Registry] the booted registry `domain_name` is loaded in
135
96
  # @param domain_name [String] the domain to export the membership binding for
136
97
  # @return [Hash{Symbol => String, nil}] `:provider` (name), `:admit`, `:grant`,
@@ -151,12 +112,9 @@ module Hecks
151
112
  }
152
113
  end
153
114
 
154
- # Same seam as `authorization` — which chapter answers "who is this
155
- # authenticated pair" (`Registry#identity_provider_for`), with its
156
- # declared verbs qualified. rust/host reads this instead of naming
157
- # Identity::Identity.Register / ExternalIdentifier.Link. `{}` when
158
- # nothing this domain attaches provides identity. Breaking in 2.0:
159
- # Link's reference field is `identity`, never `identity_id`.
115
+ # Which chapter answers "who is this authenticated pair"; `{}` if none does.
116
+ # Breaking in 2.0: Link's reference field is `identity`, never `identity_id`.
117
+ #
160
118
  # @param registry [Runtime::Registry] the booted registry `domain_name` is loaded in
161
119
  # @param domain_name [String] the domain to export the identity binding for
162
120
  # @return [Hash{Symbol => String, nil}] `:provider` (name), `:register`, `:link`,
@@ -174,12 +132,9 @@ module Hecks
174
132
  }
175
133
  end
176
134
 
177
- # Same seam as `membership` — which chapter answers the guest
178
- # newsletter signup (`Registry#newsletter_provider_for`), with its
179
- # declared verbs qualified and the subscribing aggregate named off
180
- # `subscribe`. rust/host reads this instead of naming
181
- # Newsletter::Subscriber.Subscribe and friends. `{}` when nothing
182
- # this domain attaches provides newsletter.
135
+ # Which chapter answers the guest newsletter signup
136
+ # (`Registry#newsletter_provider_for`); `{}` if none does.
137
+ #
183
138
  # @param registry [Runtime::Registry] the booted registry `domain_name` is loaded in
184
139
  # @param domain_name [String] the domain to export the newsletter binding for
185
140
  # @return [Hash{Symbol => String, nil}] `:provider` (name), `:subscribe`, `:add_name`,
@@ -203,11 +158,8 @@ module Hecks
203
158
  end
204
159
 
205
160
  # Which chapter answers sending a newsletter issue
206
- # (`Registry#newsletter_issues_provider_for`), with its declared verbs
207
- # qualified and the issue and delivery aggregates named off them.
208
- # rust/host's send route reads this instead of naming
209
- # Newsletter::Issue.Send and Newsletter::Delivery.Record. `{}` when
210
- # nothing this domain attaches provides newsletter_issues.
161
+ # (`Registry#newsletter_issues_provider_for`); `{}` if none does.
162
+ #
211
163
  # @param registry [Runtime::Registry] the booted registry `domain_name` is loaded in
212
164
  # @param domain_name [String] the domain to export the issue-sending binding for
213
165
  # @return [Hash{Symbol => String, nil}] `:provider` (name), `:send_issue`,
@@ -230,11 +182,9 @@ module Hecks
230
182
  }
231
183
  end
232
184
 
233
- # Same seam as `newsletter` — which chapter answers scheduling sessions
234
- # and taking registrations (`Registry#registrations_provider_for`), with
235
- # its declared verbs qualified and the event and registration aggregates
236
- # named off them. `{}` when nothing this domain attaches provides
237
- # registrations.
185
+ # Which chapter answers scheduling sessions and taking registrations
186
+ # (`Registry#registrations_provider_for`); `{}` if none does.
187
+ #
238
188
  # @param registry [Runtime::Registry] the booted registry `domain_name` is loaded in
239
189
  # @param domain_name [String] the domain to export the registrations binding for
240
190
  # @return [Hash{Symbol => String, nil}] `:provider` (name), `:schedule`, `:request`
@@ -258,9 +208,8 @@ module Hecks
258
208
  end
259
209
 
260
210
  # Which chapter owns the payment-processor connection
261
- # (`Registry#payment_connection_provider_for`), with its declared verbs
262
- # qualified and the connection aggregate named off `connect`. `{}` when
263
- # nothing this domain attaches provides payment_connection.
211
+ # (`Registry#payment_connection_provider_for`); `{}` if none does.
212
+ #
264
213
  # @param registry [Runtime::Registry] the booted registry `domain_name` is loaded in
265
214
  # @param domain_name [String] the domain to export the payment-connection binding for
266
215
  # @return [Hash{Symbol => String, nil}] `:provider` (name), one qualified verb per
@@ -276,12 +225,9 @@ module Hecks
276
225
  { provider: provider.name, **verbs, aggregate: verbs[:connect]&.split(".")&.first }
277
226
  end
278
227
 
279
- # Same seam as `newsletter` — which chapter takes payments
280
- # (`Registry#payments_provider_for`), with its declared verbs
281
- # qualified and the paying aggregate named off `initiate`. rust/host
282
- # reads this instead of naming Payments::Payment.Initiate and the
283
- # PaymentGateway operations. `{}` when nothing this domain attaches
284
- # provides payments.
228
+ # Which chapter takes payments (`Registry#payments_provider_for`);
229
+ # `{}` if none does.
230
+ #
285
231
  # @param registry [Runtime::Registry] the booted registry `domain_name` is loaded in
286
232
  # @param domain_name [String] the domain to export the payments binding for
287
233
  # @return [Hash{Symbol => String, nil}] `:provider` (name), `:initiate`, `:succeeded`,
@@ -302,14 +248,9 @@ module Hecks
302
248
  }
303
249
  end
304
250
 
305
- # Translation IR, always as an array, with each aggregate's
306
- # precompiled SQL attached (`compiled_translation_aggregate`) —
307
- # this is the export a consumer embeds (`ir.json`'s `translations`
308
- # key), never the bare digest-relevant shape `edge_digest` hashes
309
- # (see that method's own header for why the two must stay
310
- # separate). `values:` tables serialize as `[key, value]` pairs,
311
- # never an object, because JSON object keys are always strings and
312
- # a convert's keys are typed.
251
+ # Every registered translation, with each aggregate's precompiled SQL attached —
252
+ # `values:` serializes as `[key, value]` pairs since a convert's keys are typed.
253
+ #
313
254
  # @param registry [Runtime::Registry] the booted registry to export translations from
314
255
  # @return [Array<Hash>] every registered translation, as `compiled_translation_hash`
315
256
  # builds
@@ -341,17 +282,9 @@ module Hecks
341
282
  JSON.pretty_generate(translations(registry))
342
283
  end
343
284
 
344
- # The digest-relevant shape — `ApprovalDigest.edge_digest` hashes
345
- # exactly this, and only this, for exactly the reason `compiled_
346
- # translation_aggregate` below must never be used for that
347
- # purpose: a digest bound to the compiled SQL, not just the
348
- # declared rules, would invalidate an existing human approval the
349
- # moment `Translation::RuleCompiler`'s own output format changed
350
- # for any reason — a compiler refactor, a cosmetic SQL-formatting
351
- # change — even when the declared rules an approver actually
352
- # reviewed never changed at all. The approval binds to what was
353
- # declared, not to what a particular compiler build happened to
354
- # emit from it.
285
+ # The digest-relevant shape `ApprovalDigest.edge_digest` hashes — declared rules only,
286
+ # never the compiled SQL, so a compiler-output change can't invalidate an approval.
287
+ #
355
288
  # @param translation [Bluebook::Translation] the translation to digest
356
289
  # @return [Hash{Symbol => Object}] `:domain` (String), `:from`/`:to` (the era
357
290
  # identifiers as declared), `:retired` (`Array<String>`), and `:aggregates`
@@ -386,42 +319,17 @@ module Hecks
386
319
  drops: aggregate.drops.map(&:to_s),
387
320
  retypes: aggregate.retypes.map { |retype| { from: retype.from, to: retype.to } },
388
321
  computes: aggregate.computes.map { |compute| { from: compute.from, to: compute.to, sql: compute.sql } },
389
- # **`rekeys`/`backfills` are digest-relevant too.** Without them, an
390
- # edge carrying only a rekey (no compute) would bind its approval
391
- # to nothing rekey-specific: any two rekey edges with otherwise-
392
- # identical renames/moves/converts/drops/retypes/computes would
393
- # produce the same digest regardless of what their `rekey sql:`
394
- # actually said, letting a rekey's own SQL change without
395
- # invalidating an existing approval. Same reasoning covers
396
- # `backfills`.
322
+ # `rekeys`/`backfills` are digest-relevant too — a rekey with no compute
323
+ # would otherwise collide with any other, letting its SQL change silently
324
+ # invalidate nothing.
397
325
  rekeys: aggregate.rekeys.map { |rekey| { sql: rekey.sql } },
398
326
  backfills: aggregate.backfills.map { |backfill| { name: backfill.name.to_s, default: backfill.default } }
399
327
  }
400
328
  end
401
329
 
402
- # The export shape — `translation_aggregate`'s own digest-relevant
403
- # fields, plus the precompiled SQL (`compiled_state_expression`/
404
- # `compiled_id_expression`) a consumer embedding this JSON
405
- # (rust/host's own boot-time mint) needs to execute the edge
406
- # without compiling SQL itself. The same call head_compiler.rb's
407
- # own `compile_rules(declared)`/`id_case(guard, declared)` make at
408
- # mint time, run here once at build/export time instead —
409
- # `Translation::RuleCompiler` is the one place this expression is
410
- # built, called from both here and from head_compiler.rb's real
411
- # per-mint assembly, so a consumer gets Ruby's own compiler's
412
- # output verbatim, never a second, independently-authored SQL
413
- # compiler that could drift from this one. `compiled_id_
414
- # expression` is nil unless this edge rekeys — the bare
415
- # `aggregate_id` passthrough head_compiler.rb itself falls back to
416
- # for the overwhelming common case.
417
- # ADR 0033 — `Translation::RuleCompiler` lives in the era plugin;
418
- # unloaded, there is nothing that can compile this SQL, so this
419
- # falls back to the bare declared fields (`translation_aggregate`
420
- # alone) rather than raising on an undefined constant. A consumer
421
- # embedding this JSON without the era plugin loaded gets the same
422
- # declared-rules shape, just without precompiled SQL to execute —
423
- # consistent with there being no mint/audit machinery to run it
424
- # against either.
330
+ # `translation_aggregate`'s fields, plus precompiled SQL from the same
331
+ # `Translation::RuleCompiler` mint time uses (ADR 0033 governs its fallback).
332
+ #
425
333
  # @param aggregate [Bluebook::TranslationAggregate] the aggregate's own
426
334
  # translation rules to compile and export
427
335
  # @return [Hash{Symbol => Object}] `translation_aggregate`'s own Hash, plus
@@ -1,14 +1,7 @@
1
1
  module Hecks
2
2
  module Projector
3
- # The trivial case, on purpose: canonical IR projected as canonical
4
- # IR. `Bluebook#to_h` already satisfies every §30 acceptance
5
- # criterion on its own — no live runtime needed to call it, output is
6
- # deterministic (the same golden fixtures `spec/ir_golden_spec.rb`
7
- # pins), and target-version metadata rides along for free (`§7`'s
8
- # `ir_version:` is the first key `to_h` emits). Registering it proves
9
- # the framework's call shape against a real, already-load-bearing
10
- # target before any genuinely new projector (`:rust`, `:ul`,
11
- # `:openid`) has to.
3
+ # Projects a bluebook as its own canonical IR, via `Bluebook#to_h`.
4
+ # Needs no live runtime, and the output is deterministic.
12
5
  module IRProjector
13
6
  module_function
14
7