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
@@ -8,28 +8,19 @@ module Hecks
8
8
  module Adapters
9
9
  class PostgresEra
10
10
  module LineageManager
11
- # What a mint must prove before it may happen: the edge covers
12
- # the whole diff, no identity path was re-keyed, and the audit's
13
- # first two layers pass over the live compiled chain.
11
+ # What a mint must prove first: the edge covers the whole diff, no identity path was
12
+ # re-keyed, and the audit's first two layers pass over the compiled chain.
14
13
  module CoverageCheck
15
- # Refuses a mint whose translation edge leaves any part of the shape change
16
- # unexplained.
14
+ # Refuses a mint whose translation edge leaves any part of the shape change unexplained.
17
15
  #
18
- # Layer 1 against this edge specifically: every vanished or
19
- # retyped path in the held→current diff must be explained, and
20
- # every held aggregate must still be claimed. The refusal is
21
- # EraGuard's own, byte for byte.
16
+ # Every vanished or retyped path in the held-to-current diff must be explained and every
17
+ # held aggregate claimed; the refusal is EraGuard's own, byte for byte.
22
18
  #
23
- # @param registry [Runtime::Registry] the registry whose loaded translations may
24
- # claim a held aggregate through `was:` or `retired`
19
+ # @param registry [Runtime::Registry] where translations may claim a held aggregate
25
20
  # @param bluebook [Bluebook::Chapter] the domain as currently declared
26
- # @param held_bluebook [Bluebook::Chapter] the domain as the latest held era's
27
- # text declares it (a shadow parse)
21
+ # @param held_bluebook [Bluebook::Chapter] the domain as the latest held era declares it
28
22
  # @param edge [Bluebook::Translation] the one edge leaving the latest held era
29
- # @return [void]
30
- # @raise [Runtime::WiringError] if an identity path changed without a `rekey`
31
- # rule, a vanished or retyped path has no rule, a new required attribute has
32
- # nothing to fill it, or a held aggregate is neither renamed nor retired
23
+ # @raise [Runtime::WiringError] on an unruled identity change or an uncovered path
33
24
  def check_coverage!(registry, bluebook, held_bluebook, edge)
34
25
  bluebook.aggregates.each do |aggregate|
35
26
  rules = Ports::Persistence::Lineage.from_declared(edge.for_aggregate(aggregate.name), aggregate.name)
@@ -46,30 +37,19 @@ module Hecks
46
37
  Runtime::EraGuard.check_vanished_aggregates!(registry, bluebook, held_bluebook)
47
38
  end
48
39
 
49
- # Refuses a mint that changes an aggregate's identity paths without declaring
50
- # a `rekey`.
40
+ # Refuses a mint that changes an aggregate's identity paths without declaring a `rekey`.
51
41
  #
52
- # An identity-path change is a re-keying, not an ordinary
53
- # translation — stored ids were fixed at write time under the old
54
- # key, so this refuses unless the edge declares a `rekey` for
55
- # this aggregate covering exactly that. `rules.rekey?` (see
56
- # `Ports::Persistence::Lineage`'s own comment) is the single
57
- # source of truth every consumer of this fact asks — this is not
58
- # a second, independent check of `declared.rekeys`.
42
+ # Stored ids were fixed under the prior key, so the edge must declare a `rekey` for it;
43
+ # `rules.rekey?` is the one source of truth for that fact.
59
44
  #
60
45
  # @param bluebook [Bluebook::Chapter] the domain, named in the refusal
61
46
  # @param aggregate [Bluebook::Aggregate] the aggregate as currently declared
62
- # @param held_aggregate [Bluebook::Aggregate] the same aggregate as the held
63
- # era's text declares it
64
- # @param rules [Ports::Persistence::Lineage, nil] the edge's rules for this
65
- # aggregate; nil when the edge declares none, which cannot excuse a change
66
- # @return [nil] when the identity paths are unchanged or a `rekey` covers them
67
- # @raise [Runtime::WiringError] if the identity paths differ and no `rekey` rule
68
- # is declared
47
+ # @param held_aggregate [Bluebook::Aggregate] the same aggregate in the held era
48
+ # @param rules [Ports::Persistence::Lineage, nil] the edge's rules; nil excuses nothing
49
+ # @raise [Runtime::WiringError] if the identity paths differ and no `rekey` is declared
69
50
  def check_identity_unchanged!(bluebook, aggregate, held_aggregate, rules)
70
- # The full declared path lists, in declaration order — never the
71
- # single-head shortcut, which is nil for every composite identity
72
- # and so would let two different composites compare as unchanged.
51
+ # Full path lists, not the single-head shortcut: it is nil for every composite identity
52
+ # and would let two different composites compare equal.
73
53
  return if held_aggregate.identity_paths == aggregate.identity_paths
74
54
  return if rules&.rekey?
75
55
 
@@ -83,28 +63,16 @@ module Hecks
83
63
  "data explicitly"
84
64
  end
85
65
 
86
- # Previews every aggregate's translated head through the pending chain and
87
- # refuses the mint if the audit finds a violation.
88
- #
89
- # Layers 1 and 2 of the audit, over the live compiled chain —
90
- # before anything is minted, so a refusal leaves no half-born
91
- # era. (A convert meeting an unmapped value raises inside the
92
- # preview query itself: same rollback-shaped outcome.)
66
+ # Previews each aggregate's translated head through the pending chain and audits it.
67
+ # Runs before anything is minted, so a refusal leaves no half-born era.
93
68
  #
94
69
  # @param bluebook [Bluebook::Chapter] the domain as currently declared
95
- # @param lineage [Adapters::PostgresEra::Lineage] the domain's lineage, on an open
96
- # connection
97
- # @param chain [Array<Hash{Symbol => Bluebook::Translation}>] the full edge chain
98
- # ending in `edge`, as `edge_chain` returns it
70
+ # @param lineage [Adapters::PostgresEra::Lineage] the domain's lineage, on an open db
71
+ # @param chain [Array<Hash>] the full edge chain ending in `edge`, per `edge_chain`
99
72
  # @param ordinal [Integer] the ordinal of the era about to be minted
100
- # @param edge [Bluebook::Translation] the pending edge, whose per-aggregate rules
101
- # the audit checks
102
- # @return [nil] when no aggregate reports a violation
103
- # @raise [Runtime::WiringError] if Postgres refuses the translated preview query
104
- # (a `convert` meeting an unmapped value, for one), or the audit reports any
105
- # violation; the message lists them all
106
- # @raise [PG::Error] if Postgres refuses the "before" query; only the translated
107
- # preview is rescued into a `Runtime::WiringError`
73
+ # @param edge [Bluebook::Translation] the pending edge
74
+ # @raise [Runtime::WiringError] if a preview query fails or the audit reports a violation
75
+ # @raise [PG::Error] if Postgres refuses the "before" query
108
76
  def audit!(bluebook, lineage, chain, ordinal, edge)
109
77
  violations = []
110
78
  bluebook.aggregates.each do |aggregate|
@@ -5,65 +5,29 @@ module Hecks
5
5
  module Adapters
6
6
  class PostgresEra
7
7
  module LineageManager
8
- # The boot-time resolution: which era is this checkout? First
9
- # boot holds era 1; a quiet reboot changes nothing; a
10
- # held-but-superseded shape boots read-only-toward-the-fence; an
11
- # unheld shape goes to the minter.
8
+ # Boot-time era resolution: first boot holds era 1, a quiet reboot changes nothing,
9
+ # a superseded shape boots read-only, an unheld shape goes to the minter.
12
10
  module EraResolver
13
11
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
14
- # Resolves which era this boot is and records it on the registry, holding era 1
15
- # on a first boot and minting the next era when the shape is one no era holds.
12
+ # Resolves which era this boot is and records it on the registry.
16
13
  #
17
- # A single order-dependent dispatch over a closed set of four
18
- # boot scenarios (first boot / quiet reboot / held-but-superseded
19
- # / unheld-shape-goes-to-minter), each already explained by its
20
- # own comment above, all sharing one `db` connection closed in
21
- # `ensure`. Splitting the branches into separate methods would
22
- # turn each `return` (which exits `check!`, closing `db`) into a
23
- # sentinel value threaded back up, obscuring the mutual
24
- # exclusivity that is the method's whole point.
25
- #
26
- # @param registry [Runtime::Registry] the booting registry; `resolved_eras` gets
27
- # this domain's ordinal on every path but a first boot (where the adapter
28
- # self-resolves to era 1), and `superseded_eras` gets the newest ordinal when
29
- # this checkout's shape is held but superseded
30
- # @param bluebook [Bluebook::Chapter] the domain being booted
31
- # @param current_text [String] the domain's bluebook source as it stands on disk,
32
- # held verbatim as the new era's text on a first boot or a mint
33
- # @param settings [Hash{Symbol, String => Object}] the world's persistence settings
34
- # for this binding; `database` is required, `schema`, `role` (a Postgres role to
35
- # grant this era's privileges to) and `allow_superuser` are honored
36
- # @param directory [String, nil] the domain's bluebook directory, where
37
- # `HECKS_SCAFFOLD=1` writes a translation edge; nil disables scaffolding
38
- # @return [Integer, nil] the ordinal of the era this boot minted, or nil when it
39
- # minted nothing (first boot, quiet reboot, or a held-but-superseded shape)
40
- # @raise [Runtime::WiringError] if the database cannot be reached, the connection
41
- # is a superuser or has BYPASSRLS without `allow_superuser`, a declared
42
- # `formerly_known_as` rename cannot be applied, a held text fails its integrity
43
- # check, or the shape is unheld and `mint!` refuses
44
- # @raise [Bluebook::DSL::Malformed] if a held era's text parses under neither the
45
- # current nor the legacy grammar
14
+ # @return [Integer, nil] the ordinal minted, or nil when this boot minted nothing
15
+ # @raise [Runtime::WiringError] if the connection is refused, a held text fails its
16
+ # integrity check, or the shape is unheld and `mint!` refuses
46
17
  def check!(registry:, bluebook:, current_text:, settings:, directory: nil)
47
18
  db = PostgresEra.connect_for(bluebook.name, settings)
48
19
  lineage = Lineage.new(db, bluebook.name, formerly_known_as: bluebook.formerly_known_as)
49
- # First, before ensure_base! provisions or verifies anything —
50
- # the one point every PostgresEra boot passes through, and the
51
- # earliest at which the connection's own role is known. See
52
- # check_fence_applies!'s own header (BUG#24) for why a
53
- # superuser connection refuses here by default.
20
+ # First, before ensure_base!: the earliest point the connection's role is known,
21
+ # so a superuser connection refuses here by default.
54
22
  lineage.check_fence_applies!(allow_superuser: PostgresEra.setting(settings, :allow_superuser, default: false))
55
23
  lineage.ensure_base!
56
- # Both spellings honored, key? first — never `||`, which cannot
57
- # tell a genuinely stored `false` apart from an absent key (see
58
- # PostgresEra.setting's own comment for the full reasoning).
24
+ # key? first, never `||`: a stored `false` must not read as an absent key.
59
25
  role = settings.key?(:role) ? settings[:role] : settings["role"]
60
26
 
61
27
  held = lineage.eras
62
28
  if held.empty?
63
29
  lineage.hold_first!(current_text, projection: Runtime::StorageShape.project(bluebook))
64
30
  bluebook.aggregates.each { |aggregate| lineage.ensure_first_head!(aggregate.storage_name) }
65
- # hold_first! already established era 1 as current for
66
- # every role; this one just needs its own privileges.
67
31
  lineage.grant_role!(role, aggregates: bluebook.aggregates, era: 1) if role
68
32
  return
69
33
  end
@@ -71,15 +35,8 @@ module Hecks
71
35
  current_shape = Runtime::StorageShape.project(bluebook)
72
36
  shapes = held.map { |era| [era, Runtime::StorageShape.project(shadow(era[:held_text]))] }
73
37
  if bluebook.formerly_known_as
74
- # A held era's shape is re-derived from its own frozen
75
- # historical text (shadow(era[:held_text])), so its "name"
76
- # is permanently the old one — the frozen text is authentic
77
- # record ("this domain really was called that, at the
78
- # time") and must never be rewritten. Only the in-memory
79
- # shape used for this comparison is normalized, and only
80
- # where it exactly matches the declared old name, so an
81
- # unrelated domain that happens to shape-match some other
82
- # domain's history still shows up as a real mismatch.
38
+ # Held text is frozen record and keeps the prior name; only the in-memory shape is
39
+ # normalized, and only on an exact match with the declared prior name.
83
40
  shapes = shapes.map do |era, shape|
84
41
  [era, shape["name"] == bluebook.formerly_known_as ? shape.merge("name" => bluebook.name) : shape]
85
42
  end
@@ -88,8 +45,6 @@ module Hecks
88
45
  latest, latest_shape = shapes.last
89
46
  if latest_shape == current_shape
90
47
  bluebook.aggregates.each { |aggregate| lineage.ensure_first_head!(aggregate.storage_name) } if latest[:ordinal] == 1
91
- # A quiet reboot changes no era, so there is nothing to
92
- # advance — only this role's own privileges, if it is new.
93
48
  lineage.grant_role!(role, aggregates: bluebook.aggregates, era: latest[:ordinal]) if role
94
49
  registry.resolved_eras[bluebook.name] = latest[:ordinal]
95
50
  return
@@ -97,24 +52,11 @@ module Hecks
97
52
 
98
53
  matched, = shapes.find { |_, shape| shape == current_shape }
99
54
  if matched
100
- # A held-but-superseded era — an old checkout still running.
101
- # It may keep booting and reading (PostgresEra is the one
102
- # adapter that recognizes this rather than refusing), but it
103
- # may not keep writing: the shared era fence was already
104
- # advanced past this ordinal by whichever mint superseded
105
- # it, and nothing here may roll that back. Granting only
106
- # this role's privileges, never advance_era!, is what keeps
107
- # that true — see advance_era!'s own warning against being
108
- # called with a superseded ordinal.
55
+ # A superseded era (an old checkout) may read but not write: never advance_era!
56
+ # here, since the fence is already past this ordinal.
109
57
  lineage.grant_role!(role, aggregates: bluebook.aggregates, era: matched[:ordinal]) if role
110
58
  registry.resolved_eras[bluebook.name] = matched[:ordinal]
111
- # ...and the boot remembers that it is superseded, not only
112
- # which era it is: `RepositoryFactory.build` hands this to
113
- # the adapter as `superseded_by:`, and `PostgresEra#append`
114
- # refuses on it before issuing the INSERT. That is the
115
- # in-process half of "may not keep writing" — the half that
116
- # holds even for a connection the RLS fence cannot bite
117
- # (a superuser under allow_superuser; BUG#24).
59
+ # The adapter refuses appends on this, even where the RLS fence cannot bind.
118
60
  registry.superseded_eras[bluebook.name] = latest[:ordinal]
119
61
  return
120
62
  end
@@ -6,26 +6,17 @@ module Hecks
6
6
  module Adapters
7
7
  class PostgresEra
8
8
  module LineageManager
9
- # Tail-merge, driven from bin/merge_tail: interleave the old
10
- # world's post-cut writes into the head by their recorded global
11
- # ordinals, under the full audit, in one transaction.
9
+ # Tail-merge (bin/merge_tail): interleaves the stale world's post-cut writes into the
10
+ # head by global ordinal, audited, in one transaction.
12
11
  module MergeCoordinator
13
- # Merges the writes old checkouts made after the last mint into the current
14
- # era's head, on a connection of its own that it closes afterwards.
12
+ # Merges writes old checkouts made after the last mint into the current era's head.
15
13
  #
16
- # @param registry [Runtime::Registry] the loaded registry; supplies the
17
- # translation edges the chain is built from
18
- # @param bluebook [Bluebook::Chapter] the domain whose tail is merged
19
- # @param settings [Hash{Symbol, String => Object}] the world's persistence
20
- # settings for this binding; `database` is required
21
14
  # @param winners [Hash{String => String}] record id to `"old"` or `"new"`, naming
22
- # which world's whole record wins for each id both worlds touched since the cut
15
+ # which world's record wins for each id both worlds touched since the cut
23
16
  # @return [true] when the merge committed
24
- # @raise [Runtime::WiringError] if the database cannot be reached, the domain
25
- # stands at era 1, the edge chain is broken, a record touched by both worlds
26
- # has no named winner, the post-merge audit reports violations, another mint
27
- # or merge holds the domain lock for 10 seconds, or Postgres refuses a
28
- # statement; every refusal rolls the merge back
17
+ # @raise [Runtime::WiringError] if the domain stands at era 1, the edge chain is
18
+ # broken, a contested record has no winner, or the audit reports violations;
19
+ # every refusal rolls the merge back
29
20
  def merge!(registry:, bluebook:, settings:, winners: {})
30
21
  db = PostgresEra.connect_for(bluebook.name, settings)
31
22
  lineage = Lineage.new(db, bluebook.name, formerly_known_as: bluebook.formerly_known_as)
@@ -7,34 +7,10 @@ module Hecks
7
7
  module Adapters
8
8
  class PostgresEra
9
9
  module LineageManager
10
- # The mint path: name the source era, find the one edge that
11
- # leaves it, hold the compute approval to what was actually
12
- # reviewed, check coverage, audit the live chain, and mint — or
13
- # refuse toward the authoring loop by name.
10
+ # Names the source era, resolves its one outgoing translation edge, and mints the
11
+ # next era once approval, coverage and the audited chain all check out.
14
12
  module Minter
15
- # Mints the next era for a drifted shape in one transaction, after proving the
16
- # edge is present, current, approved where it must be, covering, and audited.
17
- #
18
- # @param registry [Runtime::Registry] the booting registry; supplies the loaded
19
- # translation edges
20
- # @param bluebook [Bluebook::Chapter] the domain as currently declared
21
- # @param current_text [String] the domain's bluebook source, held verbatim as the
22
- # new era's text
23
- # @param lineage [Adapters::PostgresEra::Lineage] the domain's lineage, on an open
24
- # connection
25
- # @param latest [Hash{Symbol => Object}] the newest held era, as `Lineage#eras`
26
- # returns it (`:ordinal`, `:hash`, `:label`, `:held_text`, `:watermark`)
27
- # @param role [String, nil] a Postgres role to grant the new era's privileges to;
28
- # nil grants nothing
29
- # @param directory [String, nil] the domain's bluebook directory, where
30
- # `HECKS_SCAFFOLD=1` writes a missing edge; nil disables scaffolding
31
- # @return [Integer] the ordinal of the era just minted
32
- # @raise [Runtime::WiringError] if no edge leaves the latest era, more than one
33
- # does, the edge targets another shape, a compute or rekey edge lacks a current
34
- # approval, the edge leaves drift uncovered, the chain is broken, the audit
35
- # reports violations, or the mint transaction itself fails
36
- # @raise [Bluebook::DSL::Malformed] if the latest held text parses under neither
37
- # the current nor the legacy grammar
13
+ # Mints the next era for a drifted shape in one transaction.
38
14
  def mint!(registry, bluebook, current_text, lineage, latest, role: nil, directory: nil)
39
15
  ensure_named!(lineage, latest)
40
16
  latest = lineage.eras.last
@@ -57,28 +33,9 @@ module Hecks
57
33
  ordinal
58
34
  end
59
35
 
60
- # Finds the one translation edge that leaves the held era, and
61
- # validates it: exactly one edge must leave (eras fork
62
- # mechanically — a second edge from the same source is a wiring
63
- # mistake, not a merge to resolve automatically), and it must
64
- # target the current shape's label (otherwise the edge is stale
65
- # and boot must not silently mint past it).
66
- #
67
- # @param registry [Runtime::Registry] the registry whose loaded `translations`
68
- # are searched
69
- # @param bluebook [Bluebook::Chapter] the domain as currently declared
70
- # @param lineage [Adapters::PostgresEra::Lineage] the domain's lineage, used only
71
- # when scaffolding a missing edge
72
- # @param latest [Hash{Symbol => Object}] the newest held era, already named;
73
- # `:label` is the edge's required source and `:ordinal` appears in refusals
74
- # @param label [String] the current shape's label, the edge's required target
75
- # @param ordinal [Integer] the ordinal of the era about to be minted
76
- # @param directory [String, nil] where `HECKS_SCAFFOLD=1` writes a missing edge;
77
- # nil disables scaffolding
78
- # @return [Bluebook::Translation] the single edge from `latest[:label]` to `label`
79
- # @raise [Runtime::WiringError] if no edge leaves the latest era (naming the
80
- # scaffold it wrote, when it wrote one), more than one does, or the one edge
81
- # targets a label other than `label`
36
+ # Finds the one translation edge leaving the held era and targeting the current
37
+ # shape's label; a second edge from the same source is a wiring mistake, not a
38
+ # merge to resolve automatically.
82
39
  def resolve_edge!(registry, bluebook, lineage, latest, label, ordinal, directory)
83
40
  edges = registry.translations.select { |t| t.domain == bluebook.name && t.from == latest[:label] }
84
41
  refuse_toward_the_scaffold!(registry, bluebook, lineage, latest, ordinal, directory) if edges.empty?
@@ -97,29 +54,10 @@ module Hecks
97
54
  edge
98
55
  end
99
56
 
100
- # Refuses a mint whose edge carries a compute or rekey rule without a recorded
101
- # approval that still matches the edge and the journal.
57
+ # Refuses a mint whose edge carries a compute or rekey rule without a recorded,
58
+ # edge-matching, journal-current approval.
102
59
  #
103
- # A compute's (and, the same way, a rekey's) only verification
104
- # is the audit's human-approved sample — mint stays
105
- # non-interactive by requiring the approval to already exist,
106
- # recorded in this database by `bin/translation_audit …
107
- # --approve` and bound to what was actually reviewed: this
108
- # edge's parsed content, and the journal as it stood when the
109
- # samples were read. A journal that has advanced past the
110
- # review invalidates it — the approved samples do not cover
111
- # the data.
112
- #
113
- # @param bluebook [Bluebook::Chapter] the domain, named in the refusal
114
- # @param lineage [Adapters::PostgresEra::Lineage] the domain's lineage, which holds
115
- # the recorded approvals and the journal's last ordinal
116
- # @param edge [Bluebook::Translation] the edge about to be minted through
117
- # @param ordinal [Integer] the ordinal of the era about to be minted
118
- # @return [nil] when the edge has no compute or rekey rule, or its approval is
119
- # current
120
- # @raise [Runtime::WiringError] if no approval is recorded for this shape pair, the
121
- # approval's edge digest differs from this edge's, or the journal has advanced
122
- # past the reviewed ordinal
60
+ # A compute or rekey's only verification is a human-approved audit sample.
123
61
  def ensure_compute_rekey_approved!(bluebook, lineage, edge, ordinal)
124
62
  return unless edge.aggregates.any? { |declared| !declared.computes.empty? || !declared.rekeys.empty? }
125
63
 
@@ -139,28 +77,8 @@ module Hecks
139
77
  "human approved no longer cover the data; re-run bin/translation_audit with --approve"
140
78
  end
141
79
 
142
- # Refuses a mint that found no translation edge, naming the authoring tools —
143
- # or, with scaffolding on, the file it just wrote.
144
- #
145
- # No edge yet: the boot refuses toward the authoring loop —
146
- # naming both tools and the era ordinal. With HECKS_SCAFFOLD=1
147
- # the boot runs the scaffold first (an explicit flag, never a
148
- # silent side-effect) and the refusal names the file it wrote.
149
- #
150
- # @param registry [Runtime::Registry] the registry, forwarded to `scaffold!` when
151
- # scaffolding is on
152
- # @param bluebook [Bluebook::Chapter] the domain, named in the refusal
153
- # @param lineage [Adapters::PostgresEra::Lineage] the domain's lineage, forwarded
154
- # to `scaffold!` when scaffolding is on
155
- # @param latest [Hash{Symbol => Object}] the newest held era, forwarded to
156
- # `scaffold!` when scaffolding is on
157
- # @param ordinal [Integer] the ordinal of the era about to be minted, named in
158
- # the refusal
159
- # @param directory [String, nil] the domain's bluebook directory; scaffolding
160
- # requires this to be present as well as `HECKS_SCAFFOLD=1`
161
- # @return [void] never returns
162
- # @raise [Runtime::WiringError] always; the message names the scaffolded file
163
- # when `HECKS_SCAFFOLD=1` and `directory` wrote one
80
+ # Refuses toward the authoring loop, or, under HECKS_SCAFFOLD=1, scaffolds the
81
+ # missing edge first and names the file it wrote.
164
82
  def refuse_toward_the_scaffold!(registry, bluebook, lineage, latest, ordinal, directory)
165
83
  if ENV["HECKS_SCAFFOLD"] == "1" && directory
166
84
  path = scaffold!(registry, bluebook, lineage, latest, directory)
@@ -175,19 +93,8 @@ module Hecks
175
93
  "check it with bin/translation_audit, then boot again"
176
94
  end
177
95
 
178
- # Diffs the held era against the current shape and writes the edge
179
- # file — confident rules inline, ambiguities as parse-refusing
180
- # `unresolved` lines.
181
- #
182
- # @param _registry [Runtime::Registry] unused; kept so the signature matches its
183
- # caller's own arguments
184
- # @param bluebook [Bluebook::Chapter] the domain as currently declared
185
- # @param lineage [Adapters::PostgresEra::Lineage] the domain's lineage; named so
186
- # `ensure_named!` can mint a name for `latest` first if it has none
187
- # @param latest [Hash{Symbol => Object}] the newest held era, as `Lineage#eras`
188
- # returns it
189
- # @param directory [String] the domain's bluebook directory to write the edge under
190
- # @return [String] the path `Translation::Scaffold::Writer#write!` wrote
96
+ # Diffs the held era against the current shape and writes the edge file —
97
+ # confident rules inline, ambiguities as parse-refusing `unresolved` lines.
191
98
  def scaffold!(_registry, bluebook, lineage, latest, directory)
192
99
  ensure_named!(lineage, latest)
193
100
  latest = lineage.eras.last
@@ -209,15 +116,7 @@ module Hecks
209
116
 
210
117
  # Mints a name for an era that has none yet, leaving an already-named era untouched.
211
118
  #
212
- # Era names are minted once. An era held before any drift was
213
- # seen has no name yet; it gets one the moment an edge needs to
214
- # leave it.
215
- #
216
- # @param lineage [Adapters::PostgresEra::Lineage] the domain's lineage, which
217
- # records the minted name
218
- # @param era [Hash{Symbol => Object}] the era to name, as `Lineage#eras` returns it;
219
- # `:hash` present means it is already named
220
- # @return [void]
119
+ # Era names are minted once, the moment an edge first needs to leave that era.
221
120
  def ensure_named!(lineage, era)
222
121
  return if era[:hash]
223
122
 
@@ -10,26 +10,8 @@ require_relative "../../../../../runtime/registry"
10
10
  module Hecks
11
11
  module Adapters
12
12
  class PostgresEra
13
- # The PostgresEra side of the boot-time era gate. Where every other
14
- # adapter's era check can only hold texts and refuse, this one
15
- # acts: a drifted shape whose translation edge exists, matches by
16
- # shape label, covers the whole diff, and passes the audit mints
17
- # the next era — one transaction — and boots into it. Everything
18
- # else refuses, as loudly and as specifically as the situation
19
- # allows, naming both authoring tools.
20
- #
21
- # Minting happens here, once: era identity (SHA-256 of the canonical
22
- # storage-shape serialization; the short prefix is the label edges
23
- # and refusals use) is computed at mint time and stored in
24
- # hecks_eras. Everything else reads stored names and never hashes
25
- # anything — an era name is a storage fact, minted on this path or
26
- # absent, never reconstructed.
27
- #
28
- # One concern per file under lineage_manager/: era_resolver (which
29
- # era is this boot?), minter (the mint path and its refusals),
30
- # merge_coordinator (bin/merge_tail's driver), coverage_check
31
- # (what a mint must prove first). What stays here is what they
32
- # share: the edge chain, and the shadow parse.
13
+ # PostgresEra's era gate: mints the next era when an edge covers the drift, else refuses.
14
+ # Era names are hashed once at mint time and stored in hecks_eras, never reconstructed.
33
15
  module LineageManager
34
16
  extend EraResolver
35
17
  extend Minter
@@ -39,23 +21,14 @@ module Hecks
39
21
  module_function
40
22
 
41
23
  # Collects the translation edge for every step from era 1 to the current shape.
24
+ # Each mint required its own edge, so a gap in the chain means a deleted file.
42
25
  #
43
- # The full chain, one edge per step in original mint order —
44
- # never flattened. Each step is found by its stored labels;
45
- # every mint required its own edge, so a break in the chain is a
46
- # deleted file, and deserves its own refusal.
47
- #
48
- # @param registry [Runtime::Registry] the registry whose loaded `translations`
49
- # are searched
50
- # @param bluebook [Bluebook::Chapter] the domain; only edges declared for its
51
- # name are considered
52
- # @param eras [Array<Hash{Symbol => Object}>] the ancestor eras in ordinal order,
53
- # as `Lineage#eras` returns them; only `:label` is read
26
+ # @param registry [Runtime::Registry] the registry whose loaded `translations` are searched
27
+ # @param bluebook [Bluebook::Chapter] the domain; only edges for its name count
28
+ # @param eras [Array<Hash>] the ancestor eras in order, as `Lineage#eras` returns them
54
29
  # @param current_label [String] the shape label the last step must lead to
55
- # @return [Array<Hash{Symbol => Bluebook::Translation}>] one `{ translation: edge }`
56
- # per step, in mint order; `[]` when `eras` is empty
57
- # @raise [Runtime::WiringError] if no loaded translation leads one label to the
58
- # next
30
+ # @return [Array<Hash>] one `{ translation: edge }` per step, in mint order
31
+ # @raise [Runtime::WiringError] if no loaded translation leads one label to the next
59
32
  def edge_chain(registry, bluebook, eras, current_label)
60
33
  labels = eras.map { |era| era[:label] } + [current_label]
61
34
  (0...(labels.size - 1)).map do |index|
@@ -71,18 +44,13 @@ module Hecks
71
44
  end
72
45
  end
73
46
 
74
- # Parses a held era's bluebook text into a throwaway registry, leaving the live
75
- # one untouched.
47
+ # Parses a held era's bluebook text into a throwaway registry, not the live one.
76
48
  #
77
- # Held texts live in rows, not files, but the predicate
78
- # extractor reads source from disk at the eval path — so a
79
- # shadow parse writes the text to a scratch file first.
49
+ # The predicate extractor reads source from disk, so the text goes through a scratch file.
80
50
  #
81
- # @param source [String] bluebook source text, as held in `hecks_eras.held_text`
82
- # @return [Bluebook::Chapter, nil] the first bluebook the text declares, or nil
83
- # when it declares none
84
- # @raise [Bluebook::DSL::Malformed] if the text parses under neither the current
85
- # grammar nor the legacy shadow-parse fallback
51
+ # @param source [String] bluebook source text from `hecks_eras.held_text`
52
+ # @return [Bluebook::Chapter, nil] the first bluebook the text declares
53
+ # @raise [Bluebook::DSL::Malformed] if the text parses under neither grammar
86
54
  def shadow(source)
87
55
  file = Tempfile.new(["hecks-era-", ".bluebook"])
88
56
  file.write(source)