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,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,85 +30,33 @@ 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 source is read as a path through
124
- # `hecks_tr_extract`, bare or a dotted value-object member alike,
125
- # the same way the destination goes through `path_literal`. The
126
- # source's value is exposed to the author's SQL as text under a
127
- # column named by the source exactly as declared (`price_cents`,
128
- # or `"price.cents"` for a dotted member), and the whole record
129
- # stays readable as `__s`.
130
- #
131
- # @param expression [String] the SQL expression built so far by `compile_rules`, read as
132
- # `__s` inside the compute's own SQL
133
- # @param compute [Bluebook::TranslationCompute] the declared compute rule
134
- # @return [String] `expression` wrapped so the compute's field lands at its declared
135
- # destination when the source path 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`.
136
60
  def compile_compute(expression, compute)
137
61
  from = compute.from.to_s
138
62
  to = compute.to.to_s
@@ -145,23 +69,8 @@ module Hecks
145
69
  "LATERAL (SELECT (__s #>> #{path_literal(from)}) AS #{quote(from)}) __fields)"
146
70
  end
147
71
 
148
- # A newly added, required attribute with no source at all — the
149
- # addition-side sibling of `compile_compute`'s own header, but with
150
- # no field to consume: `hecks_tr_extract`'s own `.present` flag
151
- # (already installed for `hecks_tr_drop`/`hecks_tr_move`/
152
- # `hecks_tr_convert` — no new database function needed) answers
153
- # "is this path — bare or a dotted value-object member alike —
154
- # already there," and `hecks_tr_insert` (already merge-safe: it
155
- # creates a missing intermediate container without disturbing any
156
- # sibling member already in it) fills it only when it is not, the
157
- # exact "never overwrites a value already there" rule `Lineage#
158
- # translate`'s own in-process backfill already holds itself to.
159
- #
160
- # @param expression [String] the SQL expression built so far by `compile_rules`, read as
161
- # `__s` inside this backfill's own check
162
- # @param backfill [Bluebook::TranslationBackfill] the declared backfill rule
163
- # @return [String] `expression` wrapped so the backfill's default lands at its declared
164
- # 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.
165
74
  def compile_backfill(expression, backfill)
166
75
  name = backfill.name.to_s
167
76
  default_json = JSON.generate(backfill.default)
@@ -171,32 +80,17 @@ module Hecks
171
80
  "FROM (SELECT (#{expression}) AS __s) __outer)"
172
81
  end
173
82
 
174
- # `PG::Connection.quote_ident` needs the `pg` gem loaded, not
175
- # connected — required here, lazily, the same "a domain that
176
- # never wires PostgresEra should never need the gem" reasoning
177
- # `PostgresEra.connect_for`'s own `require "pg"` already holds
178
- # itself to, so a build tool that exports translations for a
179
- # non-Postgres-bound domain (there are none today, but nothing
180
- # here should assume there never will be) doesn't gain a hard
181
- # dependency on `pg` just by loading this file.
182
- #
183
- # @param name [String, Symbol] the identifier to quote
184
- # @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.
185
85
  def quote(name)
186
86
  require "pg"
187
87
  PG::Connection.quote_ident(name.to_s)
188
88
  end
189
89
 
190
90
  # Renders a Ruby value as a single-quoted SQL text literal, escaping embedded quotes.
191
- #
192
- # @param text [String, Symbol, Object] the value to render; converted with `to_s`
193
- # @return [String] a single-quoted SQL literal
194
91
  def text_literal(text) = "'#{text.to_s.gsub("'", "''")}'"
195
92
 
196
93
  # Renders a dotted path as a SQL `text[]` array literal, one element per segment.
197
- #
198
- # @param path [String, Symbol] a bare or dotted path, such as `"price.cents"`
199
- # @return [String] a SQL `ARRAY[...]::text[]` expression
200
94
  def path_literal(path)
201
95
  segments = path.to_s.split(".").map { |segment| text_literal(segment) }
202
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
@@ -4,39 +4,28 @@ module Hecks
4
4
  module Ports
5
5
  module Persistence
6
6
  class PostgresDump
7
- # A Postgres connection URL, split so a password reaches the tools run
8
- # against it through their environment and never on a command line, where
9
- # any user of the machine can read it from the process list.
7
+ # A Postgres connection URL, split so a password reaches child-process tools
8
+ # through their environment, never on a command line the process list exposes.
10
9
  class Connection
11
- # @param url [String] a `postgres://` or `postgresql://` URL
12
- # @raise [PostgresDump::Error] if `url` is not a Postgres URL
13
10
  def initialize(url)
14
11
  @uri = URI.parse(url)
15
12
  raise Error, "not a postgres URL" unless %w[postgres postgresql].include?(@uri.scheme)
16
13
  end
17
14
 
18
- # Names the database the URL points at.
19
- #
20
- # @return [String] the database name, empty when the URL names none
15
+ # Names the database the URL points at, empty when the URL names none.
21
16
  def database
22
17
  @uri.path.to_s.delete_prefix("/")
23
18
  end
24
19
 
25
- # Points the same server and credentials at another database.
26
- #
27
- # @param name [String] the database to connect to instead
28
- # @return [Connection] a new connection; this one is unchanged
20
+ # Points the same server and credentials at another database; this one is unchanged.
29
21
  def with_database(name)
30
22
  copy = @uri.dup
31
23
  copy.path = "/#{name}"
32
24
  self.class.new(copy.to_s)
33
25
  end
34
26
 
35
- # Lists the connection settings the URL carries, in `PG.connect` keyword form.
36
- #
37
- # @return [Hash{Symbol => String, Integer}] `host`, `port`, `user`, `password`,
38
- # `dbname` and `sslmode`, each only when the URL gives one; the user and password
39
- # are percent-decoded
27
+ # Lists the connection settings the URL carries, in `PG.connect` keyword form; the
28
+ # user and password come back percent-decoded.
40
29
  def settings
41
30
  {
42
31
  host: (@uri.host unless @uri.host.to_s.empty?), port: @uri.port,
@@ -46,16 +35,11 @@ module Hecks
46
35
  end
47
36
 
48
37
  # Builds the libpq environment variables for a child process.
49
- #
50
- # @return [Hash{String => String}] `PGHOST`, `PGPORT`, `PGUSER`, `PGPASSWORD`,
51
- # `PGDATABASE` and `PGSSLMODE`, each only when `settings` has the matching key
52
38
  def env
53
39
  settings.to_h { |key, value| ["PG#{key.to_s.sub('dbname', 'database').upcase}", value.to_s] }
54
40
  end
55
41
 
56
- # Opens a connection with the `pg` gem.
57
- #
58
- # @return [PG::Connection] an open connection; the caller closes it
42
+ # Opens a connection with the `pg` gem; the caller closes it.
59
43
  def connect
60
44
  require "pg"
61
45
  PG.connect(**settings)