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
@@ -2,37 +2,13 @@ require "json"
2
2
 
3
3
  module Hecks
4
4
  module Translation
5
- # The closed, pure half of Postgres's own SQL compiler
6
- # (ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb)
7
- # — the part that turns one TranslationAggregate's declared rules into
8
- # a jsonb-transforming SQL expression. No database connection, no
9
- # watermark, no era chain, no catalog lookup: those live in
10
- # head_compiler.rb's own per-mint assembly, which calls into this
11
- # module instead of defining these methods itself.
12
- #
13
- # A module of its own — not private methods on
14
- # Adapters::PostgresEra::Lineage — so a second, adapter-agnostic
15
- # caller (Exporter.translation_aggregate's build-time SQL export,
16
- # feeding rust/host's own future boot-time mint) can call the exact
17
- # same code Ruby's own mint path runs, not a hand-ported duplicate
18
- # that could silently drift the way a hand-kept
19
- # `Exporter.translation_hash` can drift from `hecks_eras`/
20
- # `hecks_approvals`' real schema (leaving out rekeys and backfills).
5
+ # Compiles one TranslationAggregate's declared rules into a jsonb-transforming SQL
6
+ # expression, shared by Ruby's own mint path and any other SQL-emitting caller.
21
7
  module RuleCompiler
22
8
  module_function
23
9
 
24
- # One edge's rules over one jsonb state, compiled as a nested
25
- # expression tree — hecks_tr_* helpers composed innermost-first
26
- # in the reference transform's phase order (renames, moves,
27
- # converts, drops), computes last. `retype` compiles to nothing:
28
- # stored state never carries a type name.
29
- # Compiles a declared edge's rename/move/convert/drop/compute rules into one nested SQL
30
- # expression over the `state` jsonb column.
31
- #
32
- # @param declared [Bluebook::TranslationAggregate] this edge's declared rules for one
33
- # aggregate
34
- # @return [String] a SQL expression, `"state"` unchanged when `declared` declares none
35
- # of the five rule kinds
10
+ # Compiles one edge's rename/move/convert/drop/compute/backfill rules into a nested
11
+ # SQL expression over `state`, composed innermost-first in that phase order.
36
12
  def compile_rules(declared)
37
13
  expression = "state"
38
14
  declared.renames.each do |old_name, new_name|
@@ -54,108 +30,47 @@ module Hecks
54
30
  declared.computes.each do |compute|
55
31
  expression = compile_compute(expression, compute)
56
32
  end
57
- # Backfills last, same order `Lineage#translate` already applies
58
- # in-process (that method's own comment: "only where nothing
59
- # already answered") — now compiled here too, closing the gap
60
- # this rule kind otherwise leaves: `rust/host`'s boot-time mint
61
- # audit reads this exact compiled expression, never
62
- # `Lineage#translate`, so it cannot see a backfilled value unless
63
- # backfills compile to SQL like every other rule kind here. A
64
- # real, live gap for any new required value-object member with no
65
- # source data at all — `compute` cannot fill it either, since its
66
- # own guard requires a real, already-present field to consume
67
- # (found live: a client site's Attendee redesign, commit 4326dcd,
68
- # needed exactly this and had nothing that worked).
33
+ # Backfilled last, same order as `Lineage#translate`'s in-process pass, so the
34
+ # boot-time mint audit (which reads only this compiled SQL) sees the same result.
69
35
  declared.backfills.each do |backfill|
70
36
  expression = compile_backfill(expression, backfill)
71
37
  end
72
38
  expression
73
39
  end
74
40
 
75
- # Whether this edge's declared rules for this aggregate include a
76
- # rekey — checked directly off the raw IR object, the same way
77
- # every other rule kind is already read in `compile_rules`
78
- # (`declared.computes`, `declared.moves`, ...), not through the
79
- # `Ports::Persistence::Lineage` wrapper the app-level consumers
80
- # (coverage_check.rb, minter.rb, layer_two.rb) go through — this
81
- # module builds SQL straight off the IR either way.
82
- # Reports whether an edge's declared rules for an aggregate include a rekey.
83
- #
84
- # @param declared [Bluebook::TranslationAggregate, nil] this edge's declared rules for
85
- # one aggregate; nil for an aggregate the edge declares nothing about
86
- # @return [Boolean] true when `declared` is present and its `rekeys` is non-empty
41
+ # Reports whether an edge's declared rules for an aggregate include a rekey, read
42
+ # directly off the raw IR like every other rule kind `compile_rules` checks.
87
43
  def rekeyed?(declared) = declared && !declared.rekeys.empty?
88
44
 
89
- # The only two places `aggregate_id` needs to change — guarded so
90
- # the generated SQL for the overwhelming common case (no rekey
91
- # declared) stays the bare `aggregate_id` passthrough it always
92
- # was — this case only appears in an edge that actually declares
93
- # one.
94
- #
95
- # @param guard [String] a SQL boolean expression gating when the rekey applies, such as
96
- # `"operation = 'save'"`
97
- # @param declared [Bluebook::TranslationAggregate] this edge's declared rules; must
98
- # declare a rekey (`rekeyed?(declared)` true)
99
- # @return [String] a `"CASE WHEN ... END AS aggregate_id"` SQL expression
45
+ # Builds a `CASE WHEN ... END AS aggregate_id` expression that only overrides
46
+ # `aggregate_id` when `guard` matches, leaving the common no-rekey case untouched.
100
47
  def id_case(guard, declared)
101
48
  "CASE WHEN #{guard} THEN #{compile_id_expression(declared)} ELSE aggregate_id END AS aggregate_id"
102
49
  end
103
50
 
104
- # The rekey's own SQL — reading `state` directly, not the
105
- # progressively-built `expression` chain `compile_compute` reads
106
- # from. A rekey doesn't consume or move any field the way a move
107
- # or compute does, so there is no same-edge rename/move ordering
108
- # it needs to see first — it reads the record's stored fields
109
- # exactly as they already are, the same `__s` convention
110
- # `compile_compute` exposes.
111
- #
112
- # @param declared [Bluebook::TranslationAggregate] this edge's declared rules; its
113
- # first `rekeys` entry supplies the SQL
114
- # @return [String] a SQL expression evaluating the rekey's own SQL against the record's
115
- # current `state`
51
+ # Evaluates a rekey's own SQL against the record's current `state`, read directly
52
+ # rather than through `compile_rules`'s progressively-built expression chain.
116
53
  def compile_id_expression(declared)
117
54
  rekey = declared.rekeys.first
118
55
  "(SELECT (#{rekey.sql}) FROM (SELECT (state) AS __s) __outer)"
119
56
  end
120
57
 
121
- # A compute is the one rule whose SQL is its only implementation
122
- # — evaluated exclusively inside the compiled head, never
123
- # in-process. The old field is exposed under its own name (as
124
- # text, exactly as the author's expression expects to cast it).
125
- #
126
- # @param expression [String] the SQL expression built so far by `compile_rules`, read as
127
- # `__s` inside the compute's own SQL
128
- # @param compute [Bluebook::TranslationCompute] the declared compute rule
129
- # @return [String] `expression` wrapped so the compute's field lands at its declared
130
- # destination when the source field is present, unchanged otherwise
58
+ # Wraps `expression` so a compute's SQL result lands at its declared destination
59
+ # when the source path is present; the whole record stays readable as `__s`.
131
60
  def compile_compute(expression, compute)
132
61
  from = compute.from.to_s
133
62
  to = compute.to.to_s
134
- "(SELECT CASE WHEN __s ? #{text_literal(from)} THEN " \
135
- "hecks_tr_insert(__s - #{text_literal(from)}, #{path_literal(to)}, to_jsonb((#{compute.sql})), " \
63
+ "(SELECT CASE WHEN (__x).present THEN " \
64
+ "hecks_tr_insert((__x).remaining, #{path_literal(to)}, to_jsonb((#{compute.sql})), " \
136
65
  "#{text_literal("compute #{from} to: #{to}")}) " \
137
66
  "ELSE __s END " \
138
67
  "FROM (SELECT (#{expression}) AS __s) __outer, " \
139
- "LATERAL (SELECT (__s ->> #{text_literal(from)}) AS #{quote(from)}) __fields)"
68
+ "LATERAL (SELECT hecks_tr_extract(__s, #{path_literal(from)}) AS __x) __extract, " \
69
+ "LATERAL (SELECT (__s #>> #{path_literal(from)}) AS #{quote(from)}) __fields)"
140
70
  end
141
71
 
142
- # A newly added, required attribute with no source at all — the
143
- # addition-side sibling of `compile_compute`'s own header, but with
144
- # no field to consume: `hecks_tr_extract`'s own `.present` flag
145
- # (already installed for `hecks_tr_drop`/`hecks_tr_move`/
146
- # `hecks_tr_convert` — no new database function needed) answers
147
- # "is this path — bare or a dotted value-object member alike —
148
- # already there," and `hecks_tr_insert` (already merge-safe: it
149
- # creates a missing intermediate container without disturbing any
150
- # sibling member already in it) fills it only when it is not, the
151
- # exact "never overwrites a value already there" rule `Lineage#
152
- # translate`'s own in-process backfill already holds itself to.
153
- #
154
- # @param expression [String] the SQL expression built so far by `compile_rules`, read as
155
- # `__s` inside this backfill's own check
156
- # @param backfill [Bluebook::TranslationBackfill] the declared backfill rule
157
- # @return [String] `expression` wrapped so the backfill's default lands at its declared
158
- # path when nothing is there yet, unchanged otherwise
72
+ # Wraps `expression` so a backfill's default lands at its declared path only when
73
+ # nothing is already there, the same rule `Lineage#translate`'s own pass holds to.
159
74
  def compile_backfill(expression, backfill)
160
75
  name = backfill.name.to_s
161
76
  default_json = JSON.generate(backfill.default)
@@ -165,32 +80,17 @@ module Hecks
165
80
  "FROM (SELECT (#{expression}) AS __s) __outer)"
166
81
  end
167
82
 
168
- # `PG::Connection.quote_ident` needs the `pg` gem loaded, not
169
- # connected — required here, lazily, the same "a domain that
170
- # never wires PostgresEra should never need the gem" reasoning
171
- # `PostgresEra.connect_for`'s own `require "pg"` already holds
172
- # itself to, so a build tool that exports translations for a
173
- # non-Postgres-bound domain (there are none today, but nothing
174
- # here should assume there never will be) doesn't gain a hard
175
- # dependency on `pg` just by loading this file.
176
- #
177
- # @param name [String, Symbol] the identifier to quote
178
- # @return [String] `name` as a double-quoted Postgres identifier
83
+ # Quotes a Postgres identifier; requires "pg" lazily so a non-Postgres-bound
84
+ # domain never gains a hard dependency on the gem just by loading this file.
179
85
  def quote(name)
180
86
  require "pg"
181
87
  PG::Connection.quote_ident(name.to_s)
182
88
  end
183
89
 
184
90
  # Renders a Ruby value as a single-quoted SQL text literal, escaping embedded quotes.
185
- #
186
- # @param text [String, Symbol, Object] the value to render; converted with `to_s`
187
- # @return [String] a single-quoted SQL literal
188
91
  def text_literal(text) = "'#{text.to_s.gsub("'", "''")}'"
189
92
 
190
93
  # Renders a dotted path as a SQL `text[]` array literal, one element per segment.
191
- #
192
- # @param path [String, Symbol] a bare or dotted path, such as `"price.cents"`
193
- # @return [String] a SQL `ARRAY[...]::text[]` expression
194
94
  def path_literal(path)
195
95
  segments = path.to_s.split(".").map { |segment| text_literal(segment) }
196
96
  "ARRAY[#{segments.join(', ')}]::text[]"
@@ -3,18 +3,16 @@ require_relative "../../storage_shape"
3
3
  module Hecks
4
4
  module Translation
5
5
  module Scaffold
6
- # Diff two eras' storage-shape projections into the edge's
7
- # declarations — confident rules by unique signature pairing,
8
- # everything ambiguous left unresolved for a human.
6
+ # Diffs two eras' storage shapes into the edge's declarations; rules are written only
7
+ # for unique signature pairs, everything else is left unresolved.
9
8
  module Differ
10
- # Diff two bluebook IRs into the edge's declarations.
9
+ # Diffs two bluebook IRs into the edge's declarations.
11
10
  #
12
11
  # @param held_bluebook [Bluebook::Chapter] the era being translated from
13
12
  # @param current_bluebook [Bluebook::Chapter] the era being translated to
14
- # @return [Hash{Symbol => Object}] `:aggregates` (Array<Scaffold::ScaffoldedAggregate>,
15
- # one per matched aggregate carrying a rename or a rule), `:retired` (Array<String>,
16
- # vanished aggregate names with no successor), `:unclaimed` (Array<String>, vanished
17
- # aggregate names left ambiguous)
13
+ # @return [Hash{Symbol => Object}] `:aggregates` (Array<Scaffold::ScaffoldedAggregate>),
14
+ # `:retired` (Array<String>, vanished names with no successor), `:unclaimed`
15
+ # (Array<String>, vanished names left ambiguous)
18
16
  def diff(held_bluebook, current_bluebook)
19
17
  held_shapes = projections(held_bluebook)
20
18
  current_shapes = projections(current_bluebook)
@@ -34,31 +32,19 @@ module Hecks
34
32
  { aggregates: aggregates, retired: matched[:retired], unclaimed: matched[:unclaimed] }
35
33
  end
36
34
 
37
- # Projects a bluebook's storage shape and indexes its aggregates by name.
38
- #
39
- # @param bluebook [Bluebook::Chapter] the bluebook to project
40
- # @return [Hash{String => Hash}] the bluebook's storage-shape aggregates (see
41
- # `Runtime::StorageShape.project`), keyed by aggregate name
35
+ # Projects a bluebook's storage shape, indexing its aggregates by name.
42
36
  def projections(bluebook)
43
37
  Runtime::StorageShape.project(bluebook)["aggregates"].to_h { |shape| [shape["name"], shape] }
44
38
  end
45
39
 
46
- # A vanished aggregate whose full shape reappears under exactly one
47
- # new name counts as a rename. `retired` is only confident when nothing
48
- # remains it could plausibly have become — a vanished aggregate
49
- # beside an unmatched new one might be a rename-plus-reshape, and
50
- # writing `retired` there would be a guess that strands data.
51
- # Anything ambiguous stays unclaimed, and the coverage gate names
52
- # it until a human decides.
40
+ # Pairs vanished aggregates with new ones whose full shape reappears under exactly one
41
+ # name. A vanished aggregate is `retired` only when no unmatched new one remains, since
42
+ # it could be a rename-plus-reshape and guessing would strand data.
53
43
  #
54
- # @param held_shapes [Hash{String => Hash}] the held era's aggregate shapes, keyed by
55
- # name (see `projections`)
56
- # @param current_shapes [Hash{String => Hash}] the current era's aggregate shapes,
57
- # keyed by name
58
- # @return [Hash{Symbol => Object}] `:pairs` (Hash{String => String, nil}, each current
59
- # name to its matched held name, or nil when unmatched), `:retired` (Array<String>,
60
- # vanished names with no successor), `:unclaimed` (Array<String>, vanished names
61
- # left ambiguous)
44
+ # @param held_shapes [Hash{String => Hash}] held aggregate shapes, keyed by name
45
+ # @param current_shapes [Hash{String => Hash}] current aggregate shapes, keyed by name
46
+ # @return [Hash{Symbol => Object}] `:pairs` (current name to held name, or nil),
47
+ # `:retired` (Array<String>), `:unclaimed` (Array<String>)
62
48
  def match_aggregates(held_shapes, current_shapes)
63
49
  pairs = {}
64
50
  current_shapes.each_key { |name| pairs[name] = held_shapes.key?(name) ? name : nil }
@@ -81,20 +67,14 @@ module Hecks
81
67
  { pairs: pairs, retired: retired, unclaimed: retired.empty? ? unclaimed : [] }
82
68
  end
83
69
 
84
- # Every changed path in one aggregate, resolved into rules by
85
- # signature matching over the full path set — top-level names and
86
- # dotted members alike. Unique signature pair: a rename (both
87
- # top-level) or a move (any dotted end). Same path, same members,
88
- # new type name: a retype. Anything else: unresolved, carrying its
89
- # type-compatible candidates (an empty list is the arrow toward
90
- # compute, or drop).
70
+ # Resolves every changed path in one aggregate into rules by signature matching:
71
+ # a unique pair is a rename (both top-level) or a move (any dotted end), same members
72
+ # under a new type name is a retype, anything else is unresolved with its candidates.
91
73
  #
92
- # @param held_shape [Hash{String => Object}] the held era's aggregate shape (see
93
- # `projections`)
74
+ # @param held_shape [Hash{String => Object}] the held era's aggregate shape
94
75
  # @param current_shape [Hash{String => Object}] the current era's aggregate shape
95
- # @return [Array<Hash{Symbol => Object}>] one rule Hash per changed path — `:kind`
96
- # plus `:from`/`:to` for `:rename`/`:move`/`:retype`, or `:from`/`:candidates` for
97
- # `:unresolved` (see `Renderer#render_rule`); `[]` when nothing changed
76
+ # @return [Array<Hash{Symbol => Object}>] one rule per changed path (see
77
+ # `Renderer#render_rule`); `[]` when nothing changed
98
78
  def attribute_rules(held_shape, current_shape)
99
79
  rules = []
100
80
  rules << identity_hint(held_shape, current_shape)
@@ -108,26 +88,13 @@ module Hecks
108
88
  vanished = vanished_paths(held_attrs, current_attrs, retyped)
109
89
  appeared = appeared_paths(held_attrs, current_attrs, retyped)
110
90
 
111
- # Mutates `rules` in place (not build-and-concat, like the two
112
- # passes above) because it must read its own earlier writes:
113
- # `taken`, below, is recomputed from `rules` at the top of every
114
- # iteration, so a target this same loop already claimed for an
115
- # earlier vanished path is excluded from a later one. Passing a
116
- # snapshot instead of the live array would let two vanished
117
- # paths both match the same appeared target.
91
+ # Mutates `rules` in place: each iteration must see targets claimed by earlier ones,
92
+ # or two vanished paths could match the same appeared target.
118
93
  resolve_vanished_rules!(rules, vanished, appeared)
119
94
  rules
120
95
  end
121
96
 
122
- # retype pass: same attribute name, same member structure, the
123
- # type's own name changed
124
- #
125
- # @param held_attrs [Hash{String => Hash}] the held era's attributes, keyed by name
126
- # @param current_attrs [Hash{String => Hash}] the current era's attributes, keyed by
127
- # name
128
- # @return [Array<Hash{Symbol => String}>] one `{kind: :retype, from:, to:}` rule per
129
- # attribute whose members are unchanged but whose type name changed; `[]` when none
130
- # match
97
+ # Retype rules for attributes whose members are unchanged but whose type name changed.
131
98
  def retype_rules(held_attrs, current_attrs)
132
99
  (held_attrs.keys & current_attrs.keys).filter_map do |name|
133
100
  held = held_attrs[name]
@@ -140,16 +107,7 @@ module Hecks
140
107
  end
141
108
  end
142
109
 
143
- # Resolves each vanished path into a rename, move or unresolved rule, appended to
144
- # `rules` in place.
145
- #
146
- # @param rules [Array<Hash{Symbol => Object}>] the rule list to append to; also read
147
- # for `:rename`/`:move` targets an earlier iteration already claimed
148
- # @param vanished [Hash{String => Object}] vanished paths to type signature (see
149
- # `vanished_paths`)
150
- # @param appeared [Hash{String => Object}] appeared paths to type signature (see
151
- # `appeared_paths`)
152
- # @return [void]
110
+ # Resolves each vanished path into a rename, move or unresolved rule appended to `rules`.
153
111
  def resolve_vanished_rules!(rules, vanished, appeared)
154
112
  vanished.each do |path, signature|
155
113
  matches = appeared.select { |_, candidate| candidate == signature }.keys
@@ -166,18 +124,9 @@ module Hecks
166
124
  end
167
125
  end
168
126
 
169
- # Held paths that need explaining: whole attributes that vanished,
170
- # and members that vanished (or changed type) inside a kept
171
- # attribute — mirroring exactly what EraGuard will demand coverage
172
- # for.
173
- #
174
- # @param held_attrs [Hash{String => Hash}] the held era's attributes, keyed by name
175
- # @param current_attrs [Hash{String => Hash}] the current era's attributes, keyed by
176
- # name
177
- # @param retyped [Array<String>] type names already claimed by a `:retype` rule,
178
- # excluded here
179
- # @return [Hash{String => Object}] each vanished or changed path, dotted for a member,
180
- # valued by that path's type signature
127
+ # Held paths that need explaining: vanished attributes, and members that vanished or
128
+ # changed type inside a kept attribute — the set EraGuard demands coverage for.
129
+ # Returns each path (dotted for a member) valued by its type signature.
181
130
  def vanished_paths(held_attrs, current_attrs, retyped)
182
131
  paths = {}
183
132
  held_attrs.each do |name, held|
@@ -199,17 +148,7 @@ module Hecks
199
148
  paths
200
149
  end
201
150
 
202
- # Current paths that need explaining: whole attributes that appeared, and members
203
- # that appeared (or changed type) inside a kept attribute — the mirror of
204
- # `vanished_paths`.
205
- #
206
- # @param held_attrs [Hash{String => Hash}] the held era's attributes, keyed by name
207
- # @param current_attrs [Hash{String => Hash}] the current era's attributes, keyed by
208
- # name
209
- # @param retyped [Array<String>] type names already claimed by a `:retype` rule,
210
- # excluded here
211
- # @return [Hash{String => Object}] each appeared or changed path, dotted for a
212
- # member, valued by that path's type signature
151
+ # Current paths that need explaining; the mirror of `vanished_paths`.
213
152
  def appeared_paths(held_attrs, current_attrs, retyped)
214
153
  paths = {}
215
154
  current_attrs.each do |name, current|
@@ -230,58 +169,30 @@ module Hecks
230
169
  paths
231
170
  end
232
171
 
233
- # The one thing `attribute_rules` could not see before — the shape
234
- # projection already carries `"identity"` (`StorageShape
235
- # .project_aggregate`), it was simply never read here. An
236
- # unresolved placeholder, not a guess: `coverage_check.rb`'s own
237
- # `check_identity_unchanged!` is the real gate this only hints
238
- # toward, the same "tool proactively guides you" pattern the
239
- # generic unresolved message already gives unfed fields.
240
- #
241
- # @param held_shape [Hash{String => Object}] the held era's aggregate shape
242
- # @param current_shape [Hash{String => Object}] the current era's aggregate shape
243
- # @return [Hash{Symbol => Object}, nil] `{kind: :unresolved, from: :identity,
244
- # candidates: []}` when the identity paths differ; nil when they match
172
+ # An unresolved placeholder when the identity paths differ; nil when they match.
173
+ # It only hints: `check_identity_unchanged!` in `coverage_check.rb` is the real gate.
245
174
  def identity_hint(held_shape, current_shape)
246
175
  return if held_shape["identity"] == current_shape["identity"]
247
176
 
248
177
  { kind: :unresolved, from: :identity, candidates: [] }
249
178
  end
250
179
 
251
- # Finds appeared paths type-compatible with a vanished path's own signature.
252
- #
253
- # @param appeared [Hash{String => Object}] appeared paths to type signature (see
254
- # `appeared_paths`)
255
- # @param signature [Object] the vanished path's own type signature to match against
256
- # @return [Array<String>] appeared paths whose scalar signature (see `scalar_of`)
257
- # matches `signature`'s
180
+ # Appeared paths type-compatible with a vanished path's signature.
258
181
  def compatible_candidates(appeared, signature)
259
182
  appeared.select { |_, candidate| scalar_of(candidate) == scalar_of(signature) }.keys
260
183
  end
261
184
 
262
- # Reduces a type signature to the shape `compatible_candidates` compares by.
263
- #
264
- # @param signature [Object, Hash] a path's type signature
265
- # @return [Object] `signature["members"]` for a container Hash, else `signature`
266
- # unchanged
185
+ # Reduces a type signature to what `compatible_candidates` compares: members or the scalar.
267
186
  def scalar_of(signature) = signature.is_a?(Hash) ? signature["members"] : signature
268
187
 
269
- # Reads a container attribute's own members, keyed by name.
270
- #
271
- # @param attribute [Hash{String => Object}] one path's type signature
272
- # @return [Hash{String => Object}] each member's name to its type; `{}` when
273
- # `attribute` is not a container
188
+ # A container attribute's members keyed by name; `{}` for a scalar.
274
189
  def members_of(attribute)
275
190
  return {} unless container?(attribute)
276
191
 
277
192
  attribute["type"]["members"].to_h { |member| [member["name"], member["type"]] }
278
193
  end
279
194
 
280
- # Reports whether a type signature is a container (value object or entity), not a
281
- # scalar.
282
- #
283
- # @param attribute [Hash{String => Object}] one path's type signature
284
- # @return [Boolean] true if `attribute["type"]` is a Hash, false for a scalar
195
+ # Whether the attribute's type is a container (value object or entity), not a scalar.
285
196
  def container?(attribute) = attribute["type"].is_a?(Hash)
286
197
  end
287
198
  end
@@ -1,14 +1,13 @@
1
1
  module Hecks
2
2
  module Translation
3
3
  module Scaffold
4
- # Render an edge into .bluebook text — confident rules inline,
5
- # ambiguities as parse-refusing `unresolved` constructs, never
4
+ # Renders an edge as .bluebook text. Ambiguities become `unresolved` constructs, not
6
5
  # comments, so an unresolved file can only boot into a refusal.
7
6
  module Renderer
8
7
  # Renders a scaffolded edge as loadable `.bluebook` translation source.
9
8
  #
10
9
  # @param edge [Scaffold::Edge] the edge to render
11
- # @return [String] the edge's `.bluebook` text, ending in a newline
10
+ # @return [String] the `.bluebook` text, ending in a newline
12
11
  def render(edge)
13
12
  lines = ["Hecks.data_translation #{edge.domain.inspect}, from: #{edge.from.inspect}, to: #{edge.to.inspect} do"]
14
13
  edge.aggregates.each do |aggregate|
@@ -25,10 +24,8 @@ module Hecks
25
24
 
26
25
  # Renders one scaffolded rule as a line of `.bluebook` source.
27
26
  #
28
- # @param rule [Hash{Symbol => Object}] a rule Hash as `Differ#attribute_rules` builds
29
- # it: `:kind` plus `:from`/`:to`, or `:from`/`:candidates` for `:unresolved`
30
- # @return [String, nil] the rendered line; nil if `rule[:kind]` is none of `:rename`,
31
- # `:move`, `:retype` or `:unresolved`
27
+ # @param rule [Hash{Symbol => Object}] a rule as `Differ#attribute_rules` builds it
28
+ # @return [String, nil] the rendered line; nil for an unknown `rule[:kind]`
32
29
  def render_rule(rule)
33
30
  case rule[:kind]
34
31
  when :rename then "rename :#{rule[:from]}, to: :#{rule[:to]}"
@@ -40,11 +37,7 @@ module Hecks
40
37
  end
41
38
  end
42
39
 
43
- # Renders a path as bluebook source: a Symbol literal when bare, a String literal
44
- # when dotted.
45
- #
46
- # @param path [String, Symbol] a bare or dotted path
47
- # @return [String] `":name"` for a bare path, `path.inspect` for a dotted one
40
+ # Renders a bare path as a Symbol literal and a dotted path as a String literal.
48
41
  def render_path(path) = path.to_s.include?(".") ? path.to_s.inspect : ":#{path}"
49
42
  end
50
43
  end
@@ -3,16 +3,10 @@ require "fileutils"
3
3
  module Hecks
4
4
  module Translation
5
5
  module Scaffold
6
- # Put the rendered edge on disk, regenerating in place when a file
7
- # for the same shape pair already exists.
6
+ # Writes the rendered edge to disk, reusing the file for the same shape pair if present.
8
7
  module Writer
9
- # Renders an edge and writes it under `directory/translations`, regenerating in
10
- # place when a file for the same shape pair already exists.
11
- #
12
- # The edge file, regenerated in place when one for the same shape
13
- # pair already exists (matched textually — an unresolved file
14
- # cannot be loaded to ask, that being the whole point of
15
- # unresolved).
8
+ # Renders an edge and writes it under `directory/translations`.
9
+ # An existing file is matched textually because an unresolved file cannot be loaded.
16
10
  #
17
11
  # @param directory [String] the domain's root directory
18
12
  # @param edge [Scaffold::Edge] the edge to render and write
@@ -4,20 +4,8 @@ require_relative "scaffold/writer"
4
4
 
5
5
  module Hecks
6
6
  module Translation
7
- # The scaffold writes translations; humans resolve ambiguity. It
8
- # diffs the held era's storage-shape projection against the current
9
- # one (scaffold/differ.rb) and writes the edge file
10
- # (scaffold/renderer.rb, scaffold/writer.rb): confident rules inline
11
- # (unique signature pairs → rename/move, aggregate renames → `was:`,
12
- # type renames with identical members → retype, aggregates gone
13
- # without a successor → retired), ambiguities as parse-refusing
14
- # `unresolved` constructs — never comments, so an unresolved file can
15
- # only boot into a refusal, never a guess. It never proposes a
16
- # `compute` (a computation takes domain judgment no mechanical diff
17
- # can infer) — an empty-candidate `unresolved` is what points the
18
- # author there. It never proposes a `drop` either: data loss is a
19
- # decision, not a default. Re-running regenerates the file, matched
20
- # by shape pair.
7
+ # Diffs the held era's storage shape against the current one and writes the edge file.
8
+ # Ambiguities become parse-refusing `unresolved` constructs; it never proposes compute or drop.
21
9
  module Scaffold
22
10
  Edge = Struct.new(:domain, :from, :to, :ordinal, :label, :aggregates, :retired, keyword_init: true)
23
11
  ScaffoldedAggregate = Struct.new(:name, :was, :rules, keyword_init: true)
@@ -11,34 +11,15 @@ module Hecks
11
11
  module Ports
12
12
  module Persistence
13
13
  module Plugins
14
- # ADR 0033 — requiring this file is installing the plugin. Nothing in
15
- # Hecks core requires it. A boot loads it on demand for a domain that
16
- # binds `PostgresEra` (`Runtime::Loader.load_bound_adapters!` resolves the
17
- # adapter, which autoloads this file, before the gates are collected). An
18
- # app that wants schema-translation support without binding `PostgresEra`
19
- # requires it explicitly, the same shape every adapter-specific spec
20
- # fixture already uses to load one particular `.adapter` file rather than
21
- # all of them.
14
+ # ADR 0033 — requiring this file installs the plugin; nothing in Hecks core
15
+ # requires it, so it loads only for a domain that binds `PostgresEra` (or asks explicitly).
22
16
  module Era
23
17
  module_function
24
18
 
25
19
  # Registers this plugin's era gates on one boot's gate list.
26
20
  #
27
- # `Runtime::Loader.run_boot_gates!` asks every loaded persistence
28
- # plugin to contribute here, generically — it never mentions
29
- # `EraCheck` or "era" by name. Two gates, both `:pre_verify`:
30
- #
31
- # `:era_compute_rules` — unconditional, whenever this plugin is
32
- # loaded at all. A `compute`/`rekey` rule requires Postgres
33
- # whatever adapter is actually bound (ADR 0031's own reasoning,
34
- # unchanged) — this is the rich, adapter-aware version of that
35
- # check; `Runtime::Loader`'s own structural backstop (plain
36
- # `Bluebook::Translation` data, no plugin-specific class) only
37
- # ever fires when no persistence plugin is loaded at all.
38
- #
39
- # `:era_check` — conditional, exactly ADR 0031's own gate,
40
- # unchanged: registered only when this registry has an aggregate
41
- # actually bound to a lineage-capable adapter.
21
+ # Two `:pre_verify` gates: `era_compute_rules` always runs; `era_check` (ADR 0031)
22
+ # runs only when the registry binds something lineage-capable.
42
23
  #
43
24
  # @param registry [Runtime::Registry] the registry being booted, asked whether any
44
25
  # bluebook's first aggregate is bound to a lineage-capable adapter