hecks 3.14.0 → 4.0.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 (419) hide show
  1. checksums.yaml +4 -4
  2. data/examples/pizzas/adapters/fake_processor.rb +34 -0
  3. data/examples/pizzas/adapters/payment_signature.rb +66 -0
  4. data/examples/pizzas/adapters/payment_webhook.rb +87 -0
  5. data/examples/pizzas/bluebook/pizzas.behaviors +33 -0
  6. data/examples/pizzas/bluebook/pizzas.hecksagon +5 -3
  7. data/examples/pizzas/config.ru +22 -0
  8. data/lib/hecks/adapters/driven/prism.rb +0 -2
  9. data/lib/hecks/adapters/driven.rb +0 -2
  10. data/lib/hecks/adapters/driving/json.rb +1 -7
  11. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -2
  12. data/lib/hecks/bluebook/dsl/derivation_words.rb +58 -0
  13. data/lib/hecks/bluebook/dsl/translation_builder.rb +6 -37
  14. data/lib/hecks/bluebook/expression/ast_json.rb +0 -9
  15. data/lib/hecks/bluebook/hecksagon.rb +1 -1
  16. data/lib/hecks/bluebook/meta_validator/translation_judge/rules.rb +7 -10
  17. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +4 -8
  18. data/lib/hecks/bluebook/meta_validator.rb +0 -11
  19. data/lib/hecks/bluebook/translation.rb +18 -10
  20. data/lib/hecks/bluebook/translation_expression/evaluator.rb +63 -0
  21. data/lib/hecks/bluebook/translation_expression/parser.rb +127 -0
  22. data/lib/hecks/bluebook/translation_expression.rb +39 -0
  23. data/lib/hecks/bluebook/translation_plan.rb +70 -0
  24. data/lib/hecks/bluebook.rb +2 -0
  25. data/lib/hecks/chapters.rb +3 -3
  26. data/lib/hecks/composer/adapters/claude_composer.adapter +3 -0
  27. data/lib/hecks/composer/adapters/claude_composer.rb +83 -0
  28. data/lib/hecks/composer/adapters/fake_agent.adapter +3 -0
  29. data/lib/hecks/composer/adapters/fake_agent.rb +55 -0
  30. data/lib/hecks/composer/adapters/jev.adapter +3 -0
  31. data/lib/hecks/composer/adapters/jev.rb +66 -0
  32. data/lib/hecks/composer/agent.rb +113 -0
  33. data/lib/hecks/composer/assistant.rb +110 -0
  34. data/lib/hecks/composer/behavior_run.rb +53 -0
  35. data/lib/hecks/composer/bluebook/composer.bluebook +178 -0
  36. data/lib/hecks/composer/bluebook/composer.hecksagon +16 -0
  37. data/lib/hecks/composer/board.rb +65 -0
  38. data/lib/hecks/composer/board_edit/command_text.rb +39 -0
  39. data/lib/hecks/composer/board_edit/source.rb +84 -0
  40. data/lib/hecks/composer/board_edit.rb +144 -0
  41. data/lib/hecks/composer/check.rb +89 -0
  42. data/lib/hecks/composer/domains.rb +81 -0
  43. data/lib/hecks/composer/jobs.rb +134 -0
  44. data/lib/hecks/composer/layout.rb +37 -0
  45. data/lib/hecks/composer/live.rb +150 -0
  46. data/lib/hecks/composer/outline.rb +48 -0
  47. data/lib/hecks/composer/page.html +989 -0
  48. data/lib/hecks/composer/panels.rb +142 -0
  49. data/lib/hecks/composer/parse.rb +83 -0
  50. data/lib/hecks/composer/plain.rb +28 -0
  51. data/lib/hecks/composer/ports/composer_agent.port +7 -0
  52. data/lib/hecks/composer/session.rb +141 -0
  53. data/lib/hecks/composer/toolbox.rb +166 -0
  54. data/lib/hecks/composer/web.rb +156 -0
  55. data/lib/hecks/composer/workbench.rb +51 -0
  56. data/lib/hecks/composer/workspace.rb +81 -0
  57. data/lib/hecks/composer.rb +17 -0
  58. data/lib/hecks/corpus/adapters.rb +22 -0
  59. data/lib/hecks/corpus/rust.rb +1 -1
  60. data/lib/hecks/corpus/worlds.rb +19 -0
  61. data/lib/hecks/corpus.rb +5 -0
  62. data/lib/hecks/forms/app/commands.rb +2 -3
  63. data/lib/hecks/forms/app/queries.rb +2 -3
  64. data/lib/hecks/forms/app/records.rb +17 -5
  65. data/lib/hecks/forms/command_form_renderer.rb +42 -16
  66. data/lib/hecks/forms/field_controls.rb +2 -2
  67. data/lib/hecks/forms/field_renderer.rb +1 -1
  68. data/lib/hecks/forms/field_shape.rb +36 -17
  69. data/lib/hecks/forms/index_renderer.rb +2 -1
  70. data/lib/hecks/forms/page.rb +1 -1
  71. data/lib/hecks/forms/params.rb +0 -13
  72. data/lib/hecks/forms/query_form_renderer.rb +7 -6
  73. data/lib/hecks/forms/record_renderer.rb +11 -7
  74. data/lib/hecks/forms/rule_words.rb +100 -0
  75. data/lib/hecks/forms/value_object_shape.rb +2 -1
  76. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -3
  77. data/lib/hecks/fuzzing/isolated_boot/worlds.rb +3 -6
  78. data/lib/hecks/fuzzing/replay/filters.rb +2 -6
  79. data/lib/hecks/gate/stages.yml +25 -0
  80. data/lib/hecks/grammar/evolve/seeds.rb +0 -6
  81. data/lib/hecks/grammar/evolve.rb +0 -3
  82. data/lib/hecks/grammar/translation.bluebook +1 -1
  83. data/lib/hecks/hecks/adapters/codebase/composing.rb +56 -0
  84. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +3 -1
  85. data/lib/hecks/hecks/adapters/journal_store/changes.rb +0 -26
  86. data/lib/hecks/hecks/adapters/journal_store/examination.rb +2 -23
  87. data/lib/hecks/hecks/adapters/journal_store/readings.rb +3 -11
  88. data/lib/hecks/hecks/adapters/journal_store.rb +0 -11
  89. data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +4 -11
  90. data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +0 -9
  91. data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +0 -4
  92. data/lib/hecks/hecks/codebase.behaviors +6 -0
  93. data/lib/hecks/hecks/codebase.bluebook +21 -0
  94. data/lib/hecks/hecks/composer.behaviors +68 -0
  95. data/lib/hecks/hecks/context_map.hecksagon +1 -1
  96. data/lib/hecks/hecks/custodian.behaviors +0 -6
  97. data/lib/hecks/hecks/custodian.bluebook +3 -80
  98. data/lib/hecks/hecks/hecks.hecksagon +2 -2
  99. data/lib/hecks/hecks/hecks.world +1 -1
  100. data/lib/hecks/hecks/rehearsal.bluebook +7 -13
  101. data/lib/hecks/kernel_capabilities/templates.rb +4 -4
  102. data/lib/hecks/kernel_capabilities.rb +1 -1
  103. data/lib/hecks/language/bluebook/vocabulary.bluebook +2 -3
  104. data/lib/hecks/language/translation/translation_aggregate.bluebook +21 -24
  105. data/lib/hecks/ports/access_control.rb +2 -2
  106. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +3 -41
  107. data/lib/hecks/ports/persistence/plugins/era/lineage/coverage.rb +7 -7
  108. data/lib/hecks/ports/persistence/plugins/era/lineage/rewrite.rb +25 -2
  109. data/lib/hecks/ports/persistence/plugins/era/lineage/rules.rb +4 -4
  110. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +17 -25
  111. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/base_sql.rb +0 -12
  112. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/domain_rename.rb +1 -1
  113. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +2 -22
  114. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +0 -1
  115. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +9 -3
  116. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +3 -29
  117. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/refusals.rb +2 -26
  118. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +6 -33
  119. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -3
  120. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +2 -4
  121. data/lib/hecks/ports/persistence/plugins/era/translation/expression_sql.rb +52 -0
  122. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +23 -29
  123. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +1 -1
  124. data/lib/hecks/ports/persistence/plugins/era/translation.rb +0 -1
  125. data/lib/hecks/ports/persistence/plugins/era.rb +3 -6
  126. data/lib/hecks/ports.rb +0 -1
  127. data/lib/hecks/projection_files.rb +3 -1
  128. data/lib/hecks/projections/deploy/templates/lambda/nat_comment_oauth.tmpl +1 -1
  129. data/lib/hecks/projections/glossary/entries.rb +10 -4
  130. data/lib/hecks/projections/glossary/groups.rb +42 -0
  131. data/lib/hecks/projections/glossary/html/page.rb +7 -2
  132. data/lib/hecks/projections/glossary/html/parser.rb +1 -1
  133. data/lib/hecks/projections/glossary/html/sections.rb +31 -11
  134. data/lib/hecks/projections/glossary/html.rb +1 -2
  135. data/lib/hecks/projections/glossary/markdown.rb +31 -28
  136. data/lib/hecks/projections/glossary/mermaid.rb +3 -0
  137. data/lib/hecks/projections/glossary/page.css +143 -153
  138. data/lib/hecks/projections/glossary/page.js +63 -3
  139. data/lib/hecks/projections/glossary/sentences/actions.rb +88 -0
  140. data/lib/hecks/projections/glossary/sentences/paths.rb +77 -0
  141. data/lib/hecks/projections/glossary/sentences/queries.rb +90 -0
  142. data/lib/hecks/projections/glossary/sentences/value_objects.rb +32 -5
  143. data/lib/hecks/projections/glossary/sentences.rb +13 -3
  144. data/lib/hecks/projections/glossary/slugs.rb +4 -1
  145. data/lib/hecks/projections/glossary.rb +2 -1
  146. data/lib/hecks/projections/plain_english.rb +97 -0
  147. data/lib/hecks/projections/rust_vocabulary/arguments.rb +2 -2
  148. data/lib/hecks/projections/rust_vocabulary/enum_body.rb +20 -25
  149. data/lib/hecks/projections/rust_vocabulary/tests.rb +2 -2
  150. data/lib/hecks/projections/rust_vocabulary.rb +14 -6
  151. data/lib/hecks/projections/site/cms_editor/color.rb +0 -6
  152. data/lib/hecks/projections/statements/actions.rb +99 -0
  153. data/lib/hecks/projections/statements/relationships.rb +86 -0
  154. data/lib/hecks/projections/statements.rb +15 -24
  155. data/lib/hecks/projector/exporter/adapters.rb +112 -0
  156. data/lib/hecks/projector/exporter/translations.rb +18 -35
  157. data/lib/hecks/projector/exporter/worlds.rb +103 -0
  158. data/lib/hecks/projector/exporter.rb +5 -1
  159. data/lib/hecks/quality_control/adapters/agent.rb +6 -7
  160. data/lib/hecks/quality_control/adapters/agent_profile.rb +16 -114
  161. data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +1 -1
  162. data/lib/hecks/runtime/loader/boot_steps.rb +0 -20
  163. data/lib/hecks/runtime/outbox.rb +2 -9
  164. data/lib/hecks/runtime/port_operation_interpreter.rb +1 -9
  165. data/lib/hecks/runtime/remote_dispatcher.rb +2 -2
  166. data/lib/hecks/runtime/value.rb +13 -0
  167. data/lib/hecks/rust_build/project_rust/target_ir.rb +23 -4
  168. data/lib/hecks/rust_build/wasm.rb +0 -12
  169. data/lib/hecks/rust_build/write_if_changed.rb +12 -12
  170. data/lib/hecks/rust_format.rb +77 -0
  171. data/lib/hecks/tools/fuzz_sweep/findings.rb +3 -9
  172. data/lib/hecks/tools/fuzz_sweep.rb +1 -3
  173. data/lib/hecks/tools/translation_audit/verdicts.rb +3 -28
  174. data/lib/hecks/tools/translation_audit.rb +8 -10
  175. data/lib/hecks/tools/translation_scaffold/reporting.rb +1 -1
  176. data/lib/hecks/version.rb +1 -1
  177. data/lib/hecks/vocabulary.rb +2 -1
  178. data/lib/hecks.rb +3 -0
  179. data/prebuilt/{a883a7e568667aa5ac904c82daff356bdf9e996252a36af6def4a4a43efb967e.marshal → 02c09c73b272c1fb5a9ae6d87bb5e5e529208e00f6b4b597dc2c0fd70a9c0964.marshal} +0 -0
  180. data/prebuilt/{1a6f596be124228be93eba1d86dbc9d2933190fa389b284331241cb9f27115a0.marshal → bb84a835cf6a15136bad5ca03aa5bf93a5b92f5f72444618d3ef2412b987fb32.marshal} +0 -0
  181. data/prebuilt/{verdicts-872993b1aa26d73fe48f28251374b8585fed145d02aab42be0f00de386b6ff80.json → verdicts-f1f10da14c8e8f01b4c9b7e3e2d74be8cd2bf4676364ed17fa16975d2a076d75.json} +1 -1
  182. data/rust/Cargo.toml +40 -0
  183. data/rust/build/Cargo.toml +36 -0
  184. data/rust/build/clippy.toml +4 -0
  185. data/rust/build/src/adapters_pass.rs +196 -0
  186. data/rust/build/src/binding_pass.rs +54 -13
  187. data/rust/build/src/build_artifact.rs +23 -5
  188. data/rust/build/src/cargo_sync.rs +69 -26
  189. data/rust/build/src/lineage_pass.rs +101 -87
  190. data/rust/build/src/main.rs +11 -2
  191. data/rust/build/src/pipeline.rs +80 -19
  192. data/rust/build/src/reserved_names.rs +6 -50
  193. data/rust/build/src/resolve.rs +82 -35
  194. data/rust/build/src/rustfmt.rs +100 -0
  195. data/rust/build/src/seams_pass.rs +85 -16
  196. data/rust/build/src/subprocess.rs +30 -6
  197. data/rust/build/src/tmp.rs +5 -2
  198. data/rust/build/src/translation_pass.rs +72 -22
  199. data/rust/build/src/world_pass.rs +109 -0
  200. data/rust/clippy.toml +4 -0
  201. data/rust/codegen/Cargo.toml +36 -0
  202. data/rust/codegen/clippy.toml +4 -0
  203. data/rust/codegen/src/asks.rs +40 -14
  204. data/rust/codegen/src/attr.rs +2 -2
  205. data/rust/codegen/src/bridging.rs +676 -117
  206. data/rust/codegen/src/commands.rs +979 -373
  207. data/rust/codegen/src/constraints.rs +88 -25
  208. data/rust/codegen/src/dependency_planning.rs +128 -34
  209. data/rust/codegen/src/domain_generator.rs +300 -170
  210. data/rust/codegen/src/exemplar.rs +87 -32
  211. data/rust/codegen/src/expr_emitter.rs +54 -6
  212. data/rust/codegen/src/fielded.rs +193 -41
  213. data/rust/codegen/src/hecks_naming.rs +124 -16
  214. data/rust/codegen/src/hecks_naming_tests.rs +81 -0
  215. data/rust/codegen/src/json.rs +80 -44
  216. data/rust/codegen/src/json_codec.rs +585 -149
  217. data/rust/codegen/src/literal.rs +104 -138
  218. data/rust/codegen/src/main.rs +145 -52
  219. data/rust/codegen/src/manifest.rs +76 -14
  220. data/rust/codegen/src/mutations.rs +1212 -239
  221. data/rust/codegen/src/naming.rs +111 -60
  222. data/rust/codegen/src/optional_pass.rs +90 -28
  223. data/rust/codegen/src/ports.rs +5 -4
  224. data/rust/codegen/src/prelude.rs +139 -49
  225. data/rust/codegen/src/queries.rs +601 -226
  226. data/rust/codegen/src/reactions.rs +268 -74
  227. data/rust/codegen/src/read_models.rs +606 -205
  228. data/rust/codegen/src/reference_specs.rs +16 -3
  229. data/rust/codegen/src/registry.rs +128 -79
  230. data/rust/codegen/src/reserved_names.rs +6 -50
  231. data/rust/codegen/src/shared.rs +71 -22
  232. data/rust/codegen/src/sidecars.rs +15 -5
  233. data/rust/codegen/src/skip_reason.rs +8 -2
  234. data/rust/codegen/src/types.rs +306 -108
  235. data/rust/host/Cargo.lock +1 -0
  236. data/rust/host/Cargo.toml +40 -4
  237. data/rust/host/HECKS_RELEASE +1 -1
  238. data/rust/host/clippy.toml +4 -0
  239. data/rust/host/src/api.rs +698 -218
  240. data/rust/host/src/auth.rs +491 -214
  241. data/rust/host/src/b64.rs +91 -0
  242. data/rust/host/src/bin/expr_harness.rs +6 -2
  243. data/rust/host/src/bin/lineage_harness.rs +47 -11
  244. data/rust/host/src/bin/mint_harness.rs +70 -37
  245. data/rust/host/src/bin/validate_harness.rs +16 -8
  246. data/rust/host/src/boot.rs +144 -68
  247. data/rust/host/src/boundary_fuzz/api.rs +130 -32
  248. data/rust/host/src/boundary_fuzz/auth.rs +108 -23
  249. data/rust/host/src/boundary_fuzz/expr_json.rs +85 -11
  250. data/rust/host/src/boundary_fuzz/ir.rs +164 -12
  251. data/rust/host/src/boundary_fuzz/server.rs +283 -36
  252. data/rust/host/src/boundary_fuzz/web.rs +56 -8
  253. data/rust/host/src/checkout.rs +78 -32
  254. data/rust/host/src/clock.rs +8 -0
  255. data/rust/host/src/commerce_ir.rs +333 -95
  256. data/rust/host/src/crypto.rs +33 -0
  257. data/rust/host/src/dispatch.rs +990 -331
  258. data/rust/host/src/expr_json.rs +1021 -273
  259. data/rust/host/src/extension.rs +24 -4
  260. data/rust/host/src/field_hints.rs +11 -7
  261. data/rust/host/src/fuzz_support.rs +85 -13
  262. data/rust/host/src/host.rs +100 -0
  263. data/rust/host/src/http.rs +22 -0
  264. data/rust/host/src/ir.rs +326 -16
  265. data/rust/host/src/journal.rs +277 -176
  266. data/rust/host/src/lambda_client.rs +219 -53
  267. data/rust/host/src/lib.rs +16 -4
  268. data/rust/host/src/log.rs +62 -11
  269. data/rust/host/src/mint.rs +1153 -326
  270. data/rust/host/src/needs.rs +185 -44
  271. data/rust/host/src/payments/keystore.rs +197 -45
  272. data/rust/host/src/payments/roles.rs +39 -7
  273. data/rust/host/src/payments/tests.rs +1427 -282
  274. data/rust/host/src/payments.rs +391 -112
  275. data/rust/host/src/platform_signin/attack_tests.rs +5 -5
  276. data/rust/host/src/platform_signin/config.rs +11 -11
  277. data/rust/host/src/platform_signin/cookie.rs +2 -2
  278. data/rust/host/src/platform_signin/extension.rs +2 -1
  279. data/rust/host/src/platform_signin/handler.rs +9 -8
  280. data/rust/host/src/platform_signin/jti.rs +4 -2
  281. data/rust/host/src/platform_signin/jwks.rs +2 -1
  282. data/rust/host/src/platform_signin/jws.rs +2 -37
  283. data/rust/host/src/platform_signin/known_answer_tests.rs +5 -1
  284. data/rust/host/src/platform_signin/mode_tests.rs +5 -3
  285. data/rust/host/src/platform_signin/pages.rs +20 -20
  286. data/rust/host/src/platform_signin/session.rs +8 -1
  287. data/rust/host/src/platform_signin/settings.rs +6 -6
  288. data/rust/host/src/platform_signin/strict_json.rs +12 -9
  289. data/rust/host/src/platform_signin/verify.rs +41 -41
  290. data/rust/host/src/platform_signin/wiring_support.rs +13 -18
  291. data/rust/host/src/presentation.rs +207 -63
  292. data/rust/host/src/presentation_write.rs +502 -187
  293. data/rust/host/src/query_step.rs +69 -18
  294. data/rust/host/src/rate_limit.rs +541 -142
  295. data/rust/host/src/reference_transform.rs +77 -25
  296. data/rust/host/src/reference_validate.rs +443 -104
  297. data/rust/host/src/resend.rs +190 -55
  298. data/rust/host/src/secrets.rs +116 -21
  299. data/rust/host/src/server.rs +483 -104
  300. data/rust/host/src/storage_shape.rs +88 -21
  301. data/rust/host/src/test_pg.rs +17 -7
  302. data/rust/host/src/ui_schema.rs +495 -207
  303. data/rust/host/src/wasm_runner.rs +21 -6
  304. data/rust/host/src/web/commerce.rs +101 -23
  305. data/rust/host/src/web/newsletter/cooldown.rs +10 -3
  306. data/rust/host/src/web/newsletter/template.rs +26 -7
  307. data/rust/host/src/web/newsletter/tests.rs +112 -23
  308. data/rust/host/src/web/newsletter.rs +209 -59
  309. data/rust/host/src/web/newsletter_send.rs +251 -79
  310. data/rust/host/src/web/registration_receipt/tests.rs +16 -4
  311. data/rust/host/src/web/registration_receipt.rs +100 -24
  312. data/rust/host/src/web/registrations.rs +1384 -332
  313. data/rust/host/src/web/resend_webhook.rs +138 -80
  314. data/rust/host/src/web.rs +2416 -508
  315. data/rust/lsp/Cargo.toml +36 -0
  316. data/rust/lsp/clippy.toml +4 -0
  317. data/rust/lsp/src/json.rs +29 -22
  318. data/rust/lsp/src/main.rs +81 -31
  319. data/rust/lsp/src/outline.rs +23 -7
  320. data/rust/lsp/src/rpc.rs +17 -4
  321. data/rust/parser/Cargo.toml +36 -0
  322. data/rust/parser/clippy.toml +4 -0
  323. data/rust/parser/src/build/closed_sets.rs +6 -1
  324. data/rust/parser/src/build/identity.rs +4 -3
  325. data/rust/parser/src/build/mod.rs +1 -1
  326. data/rust/parser/src/build/named_pattern.rs +4 -1
  327. data/rust/parser/src/build/naming.rs +2 -80
  328. data/rust/parser/src/build/pattern_subset.rs +6 -8
  329. data/rust/parser/src/build/query_derive.rs +3 -1
  330. data/rust/parser/src/build/query_inference.rs +14 -5
  331. data/rust/parser/src/build/read_model.rs +4 -1
  332. data/rust/parser/src/build/references.rs +4 -3
  333. data/rust/parser/src/canonical.rs +34 -15
  334. data/rust/parser/src/emit.rs +187 -105
  335. data/rust/parser/src/expr/ast_json.rs +134 -42
  336. data/rust/parser/src/expr/evaluator.rs +27 -9
  337. data/rust/parser/src/expr/mod.rs +54 -13
  338. data/rust/parser/src/expr/resolver.rs +299 -92
  339. data/rust/parser/src/ir.rs +35 -3
  340. data/rust/parser/src/keywords.rs +7168 -516
  341. data/rust/parser/src/lex.rs +11 -25
  342. data/rust/parser/src/lib.rs +1 -0
  343. data/rust/parser/src/main.rs +65 -6
  344. data/rust/parser/src/parse/adapter.rs +157 -0
  345. data/rust/parser/src/parse/aggregate.rs +19 -10
  346. data/rust/parser/src/parse/chapter.rs +16 -17
  347. data/rust/parser/src/parse/command.rs +35 -16
  348. data/rust/parser/src/parse/domain_port.rs +4 -7
  349. data/rust/parser/src/parse/entity.rs +21 -11
  350. data/rust/parser/src/parse/hecksagon.rs +10 -4
  351. data/rust/parser/src/parse/lifecycle.rs +9 -5
  352. data/rust/parser/src/parse/mod.rs +22 -91
  353. data/rust/parser/src/parse/needs.rs +21 -12
  354. data/rust/parser/src/parse/policy.rs +2 -2
  355. data/rust/parser/src/parse/process_manager.rs +9 -11
  356. data/rust/parser/src/parse/query.rs +8 -4
  357. data/rust/parser/src/parse/read_model.rs +57 -16
  358. data/rust/parser/src/parse/value_object.rs +7 -2
  359. data/rust/parser/src/parse/world.rs +322 -0
  360. data/rust/parser/src/ruby_value.rs +7 -6
  361. data/rust/src/exemplar/json.rs +4 -4
  362. data/rust/src/exemplar/mutations.rs +18 -0
  363. data/rust/src/exemplar/read_models.rs +1 -10
  364. data/rust/src/kernel/attribute_shapes/composite.rs +54 -14
  365. data/rust/src/kernel/attribute_shapes/list.rs +2 -2
  366. data/rust/src/kernel/attribute_shapes/mod.rs +8 -8
  367. data/rust/src/kernel/attribute_shapes/scalar.rs +2 -2
  368. data/rust/src/kernel/cli.rs +229 -112
  369. data/rust/src/kernel/dispatch.rs +323 -115
  370. data/rust/src/kernel/dispatch_guard_tests.rs +327 -65
  371. data/rust/src/kernel/expr.rs +203 -86
  372. data/rust/src/kernel/expression_operators/arithmetic.rs +138 -25
  373. data/rust/src/kernel/expression_operators/comparison.rs +87 -2
  374. data/rust/src/kernel/expression_operators/enumeration.rs +106 -22
  375. data/rust/src/kernel/expression_operators/logical.rs +9 -3
  376. data/rust/src/kernel/expression_operators/membership.rs +3 -1
  377. data/rust/src/kernel/expression_operators/mod.rs +34 -21
  378. data/rust/src/kernel/expression_operators/pattern_match.rs +46 -11
  379. data/rust/src/kernel/expression_operators/positional.rs +84 -30
  380. data/rust/src/kernel/expression_operators/presence.rs +52 -13
  381. data/rust/src/kernel/expression_operators/sign_test.rs +3 -1
  382. data/rust/src/kernel/expression_operators/sized.rs +24 -8
  383. data/rust/src/kernel/expression_operators/text.rs +120 -27
  384. data/rust/src/kernel/expression_operators/to_string.rs +10 -4
  385. data/rust/src/kernel/json.rs +230 -148
  386. data/rust/src/kernel/mod.rs +46 -33
  387. data/rust/src/kernel/named_query.rs +63 -15
  388. data/rust/src/kernel/needs.rs +112 -25
  389. data/rust/src/kernel/orchestrate.rs +980 -581
  390. data/rust/src/kernel/orchestrate_guard_tests.rs +538 -140
  391. data/rust/src/kernel/pattern.rs +107 -34
  392. data/rust/src/kernel/query_comparators.rs +118 -41
  393. data/rust/src/kernel/query_ordering.rs +13 -5
  394. data/rust/src/kernel/read_model.rs +479 -181
  395. data/rust/src/kernel/reference_lookup.rs +122 -26
  396. data/rust/src/kernel/repository.rs +293 -66
  397. data/rust/src/kernel/routing.rs +58 -42
  398. data/rust/src/kernel/ruby_float.rs +139 -0
  399. data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +57 -57
  400. data/rust/src/kernel/vocab/entity_dispatch_order.rs +54 -54
  401. data/rust/src/kernel/vocab/field_hint.rs +19 -25
  402. data/rust/src/kernel/vocab/mod.rs +6 -6
  403. data/rust/src/kernel/vocab/query_comparator.rs +23 -24
  404. data/rust/src/kernel/vocab/refusal_template.rs +501 -297
  405. data/rust/src/lib.rs +50 -0
  406. data/rust/src/main.rs +4 -1
  407. data/rust/web/Cargo.toml +36 -0
  408. data/rust/web/clippy.toml +4 -0
  409. metadata +82 -15
  410. data/examples/pizzas/bluebook/hecksagon/mock_stripe_payment_adapter.rb +0 -36
  411. data/lib/hecks/adapters/driven/google_authentication.adapter +0 -3
  412. data/lib/hecks/adapters/driven/google_authentication.rb +0 -99
  413. data/lib/hecks/adapters/driven/mock_stripe_adapter.adapter +0 -3
  414. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +0 -32
  415. data/lib/hecks/ports/authentication.port +0 -6
  416. data/lib/hecks/ports/authentication.rb +0 -67
  417. data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +0 -255
  418. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +0 -22
  419. data/rust/host/src/approval.rs +0 -652
@@ -29,7 +29,7 @@ Hecks.bluebook "Translation" do
29
29
  attribute :converts, list_of(TranslationConvert)
30
30
  attribute :drops, list_of(TranslationName)
31
31
  attribute :retypes, list_of(TranslationRetype)
32
- attribute :computes, list_of(TranslationCompute)
32
+ attribute :derives, list_of(TranslationDerive)
33
33
  attribute :rekeys, list_of(TranslationRekey)
34
34
  attribute :backfills, list_of(TranslationBackfill)
35
35
 
@@ -56,14 +56,13 @@ Hecks.bluebook "Translation" do
56
56
  attribute :to, TranslationName
57
57
  end
58
58
 
59
- value_object "TranslationCompute" do
60
- attribute :from, TranslationName
61
- attribute :to, TranslationName
62
- attribute :sql, TranslationLiteralText
59
+ value_object "TranslationDerive" do
60
+ attribute :to, TranslationName
61
+ attribute :expression, TranslationLiteralText
63
62
  end
64
63
 
65
64
  value_object "TranslationRekey" do
66
- attribute :sql, TranslationLiteralText
65
+ attribute :to, TranslationName
67
66
  end
68
67
 
69
68
  value_object "TranslationBackfill" do
@@ -146,28 +145,27 @@ Hecks.bluebook "Translation" do
146
145
  emits "TranslationRetypeAdded"
147
146
  end
148
147
 
149
- command "AddCompute" do
148
+ command "AddDerive" do
150
149
  role "Deployment"
151
- goal "Move data at an old path to a new one, transformed by a real SQL expression"
150
+ goal "Give a new field a value computed from the record's own text, read before any other rule runs"
152
151
 
153
- attribute :from, TranslationName
154
- attribute :to, TranslationName
155
- attribute :sql, TranslationLiteralText
152
+ attribute :to, TranslationName
153
+ attribute :expression, TranslationLiteralText
156
154
 
157
- sets :computes, append: { from: :from, to: :to, sql: :sql }
155
+ sets :derives, append: { to: :to, expression: :expression }
158
156
 
159
- emits "TranslationComputeAdded"
157
+ emits "TranslationDeriveAdded"
160
158
  end
161
159
 
162
- command "AddRekey" do
160
+ command "AddRekeyTo" do
163
161
  role "Deployment"
164
- goal "Recompute this aggregate's own identity by a real SQL expression"
162
+ goal "Take this aggregate's identity from the value of a field a derive computes"
165
163
 
166
- attribute :sql, TranslationLiteralText
164
+ attribute :to, TranslationName
167
165
 
168
- sets :rekeys, append: { sql: :sql }
166
+ sets :rekeys, append: { to: :to }
169
167
 
170
- emits "TranslationRekeyAdded"
168
+ emits "TranslationRekeyToAdded"
171
169
  end
172
170
 
173
171
  command "AddBackfill" do
@@ -200,8 +198,8 @@ Hecks.bluebook "Translation" do
200
198
  member word: "convert", context: "TranslationAggregate", body: "none", inner: "", opens: "", fills: "converts", calls: "convert_impl"
201
199
  member word: "drop", context: "TranslationAggregate", body: "none", inner: "", opens: "", fills: "drops"
202
200
  member word: "retype", context: "TranslationAggregate", body: "none", inner: "", opens: "", fills: "retypes", calls: "retype_impl"
203
- member word: "compute", context: "TranslationAggregate", body: "none", inner: "", opens: "", fills: "computes", calls: "compute_impl"
204
- member word: "rekey", context: "TranslationAggregate", body: "none", inner: "", opens: "", fills: "rekeys", calls: "rekey_impl"
201
+ member word: "derive", context: "TranslationAggregate", body: "none", inner: "", opens: "", fills: "derives", calls: "derive_impl"
202
+ member word: "rekey_to", context: "TranslationAggregate", body: "none", inner: "", opens: "", fills: "rekeys", calls: "rekey_to_impl"
205
203
  member word: "backfill", context: "TranslationAggregate", body: "none", inner: "", opens: "", fills: "backfills", calls: "backfill_impl"
206
204
  member word: "unresolved", context: "TranslationAggregate", body: "none", inner: "", opens: "", fills: "", calls: "unresolved_impl"
207
205
 
@@ -234,10 +232,9 @@ Hecks.bluebook "Translation" do
234
232
  member keyword: "drop", context: "TranslationAggregate", at: "1", named: "", kind: "symbol", required: "true", fills: "name", blank_message: "a drop needs a name"
235
233
  member keyword: "retype", context: "TranslationAggregate", at: "1", named: "", kind: "text", required: "true", fills: "old_type"
236
234
  member keyword: "retype", context: "TranslationAggregate", at: "", named: "to", kind: "text", required: "true", fills: "to"
237
- member keyword: "compute", context: "TranslationAggregate", at: "1", named: "", kind: "text", required: "true", fills: "old_path"
238
- member keyword: "compute", context: "TranslationAggregate", at: "", named: "to", kind: "text", required: "true", fills: "to"
239
- member keyword: "compute", context: "TranslationAggregate", at: "", named: "sql", kind: "text", required: "true", fills: "sql"
240
- member keyword: "rekey", context: "TranslationAggregate", at: "", named: "sql", kind: "text", required: "true", fills: "sql"
235
+ member keyword: "derive", context: "TranslationAggregate", at: "1", named: "", kind: "text", required: "true", fills: "to"
236
+ member keyword: "derive", context: "TranslationAggregate", at: "", named: "as", kind: "text", required: "true", fills: "as"
237
+ member keyword: "rekey_to", context: "TranslationAggregate", at: "1", named: "", kind: "symbol", required: "true", fills: "to"
241
238
  member keyword: "backfill", context: "TranslationAggregate", at: "1", named: "", kind: "symbol", required: "true", fills: "name"
242
239
  member keyword: "backfill", context: "TranslationAggregate", at: "", named: "default", kind: "literal", required: "true", fills: "default"
243
240
  member keyword: "unresolved", context: "TranslationAggregate", at: "1", named: "", kind: "symbol", required: "true", fills: "name"
@@ -26,8 +26,8 @@ module Hecks
26
26
 
27
27
  # Admits a new person, in whatever way this domain's adapter defines admission.
28
28
  #
29
- # The keywords carry the names of a verified sign-in (`Ports::Authentication.verify`),
30
- # but nothing here wires the two together, so their shapes are adapter-defined.
29
+ # The keywords carry the names of a verified OIDC sign-in, but nothing here wires
30
+ # one to this port, so their shapes are adapter-defined.
31
31
  #
32
32
  # @param registry [Runtime::Registry] the booted registry to resolve the adapter against
33
33
  # @param email [Object] adapter-defined, forwarded unchanged; the person's email
@@ -14,7 +14,7 @@ module Hecks
14
14
  module EraCheck
15
15
  module_function
16
16
 
17
- # Runs both halves of the era gate over every bluebook in a registry.
17
+ # Runs the era gate over every bluebook in a registry.
18
18
  #
19
19
  # Looks up each bluebook's own source separately: `attaches` can share a boot between
20
20
  # the domain's own bluebook and a differently-sourced one, so one file read once and reused
@@ -23,27 +23,12 @@ module Hecks
23
23
  # @param registry [Runtime::Registry] the registry being booted
24
24
  # @param directory [String] the domain's bluebook directory, searched for each source
25
25
  # @return [void]
26
- # @raise [Runtime::WiringError] if a compute rule is bound to a non-lineage-capable adapter,
27
- # a persistence binding cannot be resolved, a lineage-bound bluebook has no findable
28
- # source, or `era_check!` refuses the boot
26
+ # @raise [Runtime::WiringError] if a persistence binding cannot be resolved, a lineage-bound
27
+ # bluebook has no findable source, or `era_check!` refuses the boot
29
28
  def check!(registry, directory)
30
- check_compute_rules_for_registry!(registry)
31
29
  check_lineage!(registry, directory)
32
30
  end
33
31
 
34
- # Refuses the boot when any bluebook's compute rule is bound away from Postgres.
35
- #
36
- # Runs unconditionally for every registry, unlike check_lineage!'s
37
- # capability-gated pass below (ADR 0031).
38
- #
39
- # @param registry [Runtime::Registry] the registry whose bluebooks are all checked
40
- # @return [void]
41
- # @raise [Runtime::WiringError] if an aggregate carrying a compute rule is bound to an
42
- # adapter that is not lineage-capable, or its persistence binding cannot be resolved
43
- def check_compute_rules_for_registry!(registry)
44
- registry.bluebooks.each_value { |bluebook| check_compute_rules!(registry, bluebook) }
45
- end
46
-
47
32
  # Hands every bluebook, with its own source text, to its adapter's era check.
48
33
  #
49
34
  # Capability-gated (ADR 0031): check_bluebook! below still returns
@@ -222,29 +207,6 @@ module Hecks
222
207
  "it could be found (checked #{directory.inspect} and the framework registry)"
223
208
  end
224
209
 
225
- # Refuses the boot when one bluebook's compute rule is bound away from Postgres.
226
- #
227
- # A compute rule's SQL is its only implementation, so it cannot
228
- # boot on any adapter but Postgres, independent of shape checks.
229
- #
230
- # @param registry [Runtime::Registry] the registry holding the declared translations
231
- # and the aggregate's bindings
232
- # @param bluebook [Bluebook::Chapter] the bluebook whose aggregates are checked
233
- # @return [void]
234
- # @raise [Runtime::WiringError] if an aggregate carrying a compute rule is bound to an
235
- # adapter that is not lineage-capable, or its persistence binding cannot be resolved
236
- def check_compute_rules!(registry, bluebook)
237
- bluebook.aggregates.each do |aggregate|
238
- lineage = Ports::Persistence::Lineage.for(registry, bluebook.name, aggregate)
239
- next unless lineage&.computes?
240
-
241
- adapter = adapter_for(registry, bluebook.name, aggregate)
242
- next if lineage_capable?(registry, adapter)
243
-
244
- raise WiringError, "compute rules require the Postgres adapter; #{aggregate.name} is bound to #{adapter}"
245
- end
246
- end
247
-
248
210
  # Names the adapter an aggregate's authoritative persistence bind points at.
249
211
  #
250
212
  # @param registry [Runtime::Registry] the registry holding the domain's hecksagon
@@ -12,7 +12,7 @@ module Hecks
12
12
  # attribute that is new outright, never a path that existed and moved.
13
13
  #
14
14
  # @param path [String, Symbol] a bare attribute name or a dotted value-object member path
15
- # @return [Boolean] true when a rename, move, convert, drop or compute names the path or
15
+ # @return [Boolean] true when a rename, move, convert, or drop names the path or
16
16
  # its top-level attribute as its source, or a backfill names the top-level attribute
17
17
  def explains?(path)
18
18
  path = path.to_s
@@ -30,8 +30,8 @@ module Hecks
30
30
  # the same way a backfill would.
31
31
  #
32
32
  # @param path [String, Symbol] the name of a top-level attribute new in the current shape
33
- # @return [Boolean] true when a rename, move, convert or compute lands a value in that
34
- # attribute, or a backfill names it
33
+ # @return [Boolean] true when a rename, move, convert or derive lands a value in
34
+ # that attribute, or a backfill names it
35
35
  def fills?(path)
36
36
  path = path.to_s
37
37
 
@@ -42,13 +42,13 @@ module Hecks
42
42
 
43
43
  private
44
44
 
45
- # Every path a move, convert, compute or drop reads from.
45
+ # Every path a move, convert or drop reads from.
46
46
  def sources
47
- [*@moves, *@converts, *@rules.computes].map(&:from) + @drops.map(&:to_s)
47
+ [*@moves, *@converts].map(&:from) + @drops.map(&:to_s)
48
48
  end
49
49
 
50
- # Every path a move, convert or compute writes to.
51
- def destinations = [*@moves, *@converts, *@rules.computes].map(&:to)
50
+ # Every path a move, convert or derive writes to.
51
+ def destinations = [*@moves, *@converts, *@rules.derives].map(&:to)
52
52
 
53
53
  def backfilled?(name) = @rules.backfills.any? { |backfill| backfill.name.to_s == name }
54
54
  end
@@ -2,12 +2,13 @@ module Hecks
2
2
  module Ports
3
3
  module Persistence
4
4
  class Lineage
5
- # The reference semantics of rename, move, convert, drop and backfill, applied in that
6
- # order to a deep copy of one entry's state.
5
+ # The reference semantics of derive, rename, move, convert, drop and backfill, applied in
6
+ # that order to a deep copy of one entry's state.
7
7
  module Rewrite
8
8
  private
9
9
 
10
10
  def rewrite(state)
11
+ apply_derives(state)
11
12
  apply_renames(state, @renames)
12
13
  @moves.each { |move| apply_move(state, move) }
13
14
  @converts.each { |convert| apply_convert(state, convert) }
@@ -17,6 +18,28 @@ module Hecks
17
18
  state
18
19
  end
19
20
 
21
+ # Every derive reads the state as stored, so the values are all computed before the
22
+ # first is written; a derive with no value writes nothing.
23
+ def apply_derives(state)
24
+ computed = @rules.derives.map { |derive| [derive, Bluebook::TranslationExpression.evaluate(derive.tree, state)] }
25
+ computed.each do |derive, value|
26
+ next if value.nil?
27
+
28
+ top, member = derive.to.split(".", 2)
29
+ insert(state, top.to_sym, member, value, rule: "derive #{derive.to}")
30
+ end
31
+ end
32
+
33
+ # The id a `rekey_to` gives a record: the value of its derive, read from the stored state;
34
+ # the record keeps its id where the derive has no value or the edge has no `rekey_to`.
35
+ def rekeyed_id(entry)
36
+ rekey = @rules.rekeys.first
37
+ return entry.id unless rekey
38
+
39
+ derive = @rules.derives.find { |candidate| candidate.to == rekey.to }
40
+ Bluebook::TranslationExpression.evaluate(derive.tree, entry.state) || entry.id
41
+ end
42
+
20
43
  def deep_dup(node)
21
44
  case node
22
45
  when Hash then node.transform_values { |value| deep_dup(value) }
@@ -6,18 +6,18 @@ module Hecks
6
6
  # an unknown keyword raises ArgumentError.
7
7
  #
8
8
  # - `retypes` — type names meaning the same shape
9
- # - `computes` — SQL-only rules, never applied in process
10
- # - `rekeys` — SQL rewrites of identity; only the first is read
9
+ # - `derives` — new fields computed from the record's own text
10
+ # - `rekeys` - the field whose derived value becomes the id
11
11
  # - `backfills` — defaults for a new attribute
12
12
  # - `ancestor_name` — the aggregate's name before the edge, if renamed
13
13
  # - `ancestor_storage_name` — snake_case storage name of `ancestor_name`
14
- Rules = Struct.new(:retypes, :computes, :rekeys, :backfills, :ancestor_name, :ancestor_storage_name,
14
+ Rules = Struct.new(:retypes, :derives, :rekeys, :backfills, :ancestor_name, :ancestor_storage_name,
15
15
  keyword_init: true) do
16
16
  # Builds the rules with every list defaulting to empty.
17
17
  #
18
18
  # @param given [Hash] any of the members; an unknown key raises ArgumentError
19
19
  # @return [Rules]
20
- def self.build(**given) = new(retypes: [], computes: [], rekeys: [], backfills: [], **given)
20
+ def self.build(**given) = new(retypes: [], derives: [], rekeys: [], backfills: [], **given)
21
21
  end
22
22
  end
23
23
  end
@@ -59,17 +59,23 @@ module Hecks
59
59
  ancestor_storage_name = renamed_aggregate ? Naming.snake(declared.was) : nil
60
60
 
61
61
  new(declared.renames, declared.moves, declared.converts, declared.drops,
62
- retypes: declared.retypes, computes: declared.computes, rekeys: declared.rekeys,
63
- backfills: declared.backfills,
64
- ancestor_name: ancestor_name, ancestor_storage_name: ancestor_storage_name)
62
+ **optional_rules(declared), ancestor_name: ancestor_name, ancestor_storage_name: ancestor_storage_name)
65
63
  end
66
64
 
65
+ # The rule lists an edge carries beyond renames, moves, converts and drops.
66
+ def self.optional_rules(declared)
67
+ { retypes: declared.retypes, derives: declared.derives,
68
+ rekeys: declared.rekeys, backfills: declared.backfills }
69
+ end
70
+ private_class_method :optional_rules
71
+
67
72
  # @param renames [Hash{Symbol => Symbol}] old attribute name to new name
68
73
  # @param moves [Array<Bluebook::TranslationMove>] from/to paths across a value object
69
74
  # @param converts [Array<Bluebook::TranslationConvert>] moves mapped through a values table
70
75
  # @param drops [Array<Symbol>] bare or dotted paths whose data is discarded
71
76
  # @param rules [Hash] the optional rule lists, as `Rules` names them: `retypes:`,
72
- # `computes:`, `rekeys:`, `backfills:`, `ancestor_name:` and `ancestor_storage_name:`
77
+ # `derives:`, `rekeys:`, `backfills:`, `ancestor_name:` and
78
+ # `ancestor_storage_name:`
73
79
  # @raise [ArgumentError] on a keyword `Rules` does not name
74
80
  def initialize(renames, moves = [], converts = [], drops = [], **rules)
75
81
  @renames = renames
@@ -79,14 +85,6 @@ module Hecks
79
85
  @rules = Rules.build(**rules)
80
86
  end
81
87
 
82
- # Reports whether any rule in this edge is a compute.
83
- #
84
- # Compute has no in-process implementation; an aggregate carrying
85
- # one refuses to boot anywhere but Postgres.
86
- #
87
- # @return [Boolean] true when the edge declares at least one compute rule
88
- def computes? = !@rules.computes.empty?
89
-
90
88
  # Reports whether this edge rewrites the aggregate's identity.
91
89
  #
92
90
  # The single source of truth for this; other call sites ask here
@@ -95,23 +93,16 @@ module Hecks
95
93
  # @return [Boolean] true when the edge declares at least one rekey rule
96
94
  def rekey? = !@rules.rekeys.empty?
97
95
 
98
- # Returns the SQL expression the edge's first rekey rule declares.
99
- #
100
- # Only the first rule is read, the same one-per-aggregate assumption
101
- # `compute` makes; more than one is a DSL-level decision to arbitrate elsewhere.
102
- #
103
- # @return [String, nil] the first rekey rule's SQL; nil when the edge declares no rekey
104
- def rekey_sql = @rules.rekeys.first&.sql
105
-
106
- # Rewrites one journal entry's state from the held shape into the current one.
96
+ # Rewrites one journal entry from the held shape into the current one.
107
97
  #
108
- # The reference semantics for rename, move, convert, drop, and the aggregate-level `was:`.
109
- # `compute` is not applied here — its SQL is the only implementation, audited separately.
98
+ # The reference semantics for derive, rename, move, convert, drop, `was:` and the id a
99
+ # `rekey_to` takes.
110
100
  #
111
101
  # @param entry [Ports::Persistence::Entry] the entry as stored, with Symbol
112
102
  # top-level keys and String keys nested in a value-object Hash
113
103
  # @return [Ports::Persistence::Entry] a new entry with translated state and the same
114
- # `operation`, `id` and `mirrors`; `entry` itself when it is not a save or has no state
104
+ # `operation` and `mirrors`, and the same `id` unless a `rekey_to` gives it a value;
105
+ # `entry` itself when it is not a save or has no state
115
106
  # @raise [Runtime::WiringError] if a convert value is missing from its `values` table, or a
116
107
  # move/convert would nest under a destination already holding a non-Hash value
117
108
  def translate(entry)
@@ -120,8 +111,9 @@ module Hecks
120
111
  # Deep, not shallow: a move or convert reaches into a nested
121
112
  # value-object hash, and a shallow dup would quietly mutate
122
113
  # the caller's copy of the original entry.
114
+ id = rekeyed_id(entry)
123
115
  state = rewrite(deep_dup(entry.state))
124
- Entry.new(operation: entry.operation, id: entry.id, state: state, mirrors: entry.mirrors)
116
+ Entry.new(operation: entry.operation, id: id, state: state, mirrors: entry.mirrors)
125
117
  end
126
118
 
127
119
  # Reports whether a declared retype pairs two type names as the same shape.
@@ -29,18 +29,6 @@ module Hecks
29
29
  )
30
30
  SQL
31
31
 
32
- # The Layer-3 approvals of translation edges.
33
- APPROVALS_SQL = <<~SQL.freeze
34
- CREATE TABLE IF NOT EXISTS hecks_approvals (
35
- domain text NOT NULL,
36
- from_label text NOT NULL,
37
- to_label text NOT NULL,
38
- edge_digest text NOT NULL,
39
- reviewed_ordinal bigint NOT NULL,
40
- approved_at timestamptz NOT NULL DEFAULT now()
41
- )
42
- SQL
43
-
44
32
  # An owned sequence default, not generated always as identity, which
45
33
  # partitioned tables only support from Postgres 17.
46
34
  JOURNAL_SQL = <<~SQL.freeze
@@ -76,7 +76,7 @@ module Hecks
76
76
  end
77
77
 
78
78
  def rename_rows!
79
- %w[hecks_eras hecks_era_texts hecks_approvals].each do |table|
79
+ %w[hecks_eras hecks_era_texts].each do |table|
80
80
  @db.exec_params("UPDATE #{table} SET domain = $1 WHERE domain = $2", [@domain, @formerly_known_as])
81
81
  end
82
82
  # hecks_attestations is created lazily on first reattest!, so it may not exist.
@@ -6,8 +6,8 @@ module Hecks
6
6
  module Adapters
7
7
  class PostgresEra
8
8
  class Lineage
9
- # CRUD over `hecks_eras`/`hecks_era_texts`/`hecks_approvals`: era text
10
- # integrity, archiving, Layer-3 approvals, and once-only era naming.
9
+ # CRUD over `hecks_eras`/`hecks_era_texts`: era text
10
+ # integrity, archiving, and once-only era naming.
11
11
  module EraStore
12
12
  # Verifies each held text's digest before returning it; a missing digest is backfilled.
13
13
  def eras
@@ -48,26 +48,6 @@ module Hecks
48
48
  )
49
49
  end
50
50
 
51
- # Always inserts a new row; `approval_for` picks the latest for a shape pair.
52
- def record_approval!(from:, to:, edge_digest:)
53
- @db.exec_params(
54
- "INSERT INTO hecks_approvals (domain, from_label, to_label, edge_digest, reviewed_ordinal) " \
55
- "VALUES ($1, $2, $3, $4, $5)",
56
- [@domain, from, to, edge_digest, last_ordinal]
57
- )
58
- end
59
-
60
- def approval_for(from:, to:)
61
- rows = @db.exec_params(
62
- "SELECT edge_digest, reviewed_ordinal FROM hecks_approvals " \
63
- "WHERE domain = $1 AND from_label = $2 AND to_label = $3 ORDER BY approved_at DESC, reviewed_ordinal DESC LIMIT 1",
64
- [@domain, from, to]
65
- )
66
- return nil if rows.ntuples.zero?
67
-
68
- { edge_digest: rows[0]["edge_digest"], reviewed_ordinal: rows[0]["reviewed_ordinal"].to_i }
69
- end
70
-
71
51
  # Sets hash/label only once; nothing ever recomputes a stored name to verify it.
72
52
  def mint_name!(ordinal, hash, label)
73
53
  @db.exec_params(
@@ -118,7 +118,6 @@ module Hecks
118
118
  # NULL reads as 1.
119
119
  @db.exec("ALTER TABLE hecks_eras ADD COLUMN IF NOT EXISTS canon_form int")
120
120
  @db.exec(ERA_TEXTS_SQL)
121
- @db.exec(APPROVALS_SQL)
122
121
  end
123
122
 
124
123
  def create_journal!
@@ -28,8 +28,6 @@ module Hecks
28
28
  include FieldCache
29
29
  include TransformInstaller
30
30
 
31
- JOURNAL_COLUMNS = "ordinal, era, aggregate, aggregate_id, operation, state, mirrors".freeze
32
-
33
31
  # Longer identifiers are silently truncated by Postgres, so `qualified_name` hashes them.
34
32
  POSTGRES_IDENTIFIER_LIMIT = 63
35
33
 
@@ -100,7 +98,15 @@ module Hecks
100
98
  return full if full.bytesize <= POSTGRES_IDENTIFIER_LIMIT
101
99
 
102
100
  digest = Digest::SHA256.hexdigest(full)[0, 8]
103
- "#{full.byteslice(0, POSTGRES_IDENTIFIER_LIMIT - digest.bytesize - 1)}_#{digest}"
101
+ "#{whole_characters(full, POSTGRES_IDENTIFIER_LIMIT - digest.bytesize - 1)}_#{digest}"
102
+ end
103
+
104
+ # The longest prefix of `text` made of whole characters that fits in `limit` bytes, so a
105
+ # multi-byte character is dropped rather than cut in half.
106
+ def whole_characters(text, limit)
107
+ bytes = text.byteslice(0, limit)
108
+ bytes = bytes.byteslice(0, bytes.bytesize - 1) until bytes.valid_encoding?
109
+ bytes
104
110
  end
105
111
 
106
112
  def quote(name) = PG::Connection.quote_ident(name.to_s)
@@ -2,7 +2,6 @@ require_relative "../../../../../../runtime/registry"
2
2
  require_relative "../../storage_shape"
3
3
  require_relative "../../translation/audit"
4
4
  require_relative "../../translation/scaffold"
5
- require_relative "../../translation/approval_file"
6
5
  require_relative "context"
7
6
  require_relative "refusals"
8
7
 
@@ -11,7 +10,7 @@ module Hecks
11
10
  class PostgresEra
12
11
  module LineageManager
13
12
  # Names the source era, resolves its one outgoing translation edge, and mints the
14
- # next era once approval, coverage and the audited chain all check out.
13
+ # next era once coverage and the audited chain check out.
15
14
  module Minter
16
15
  include Refusals
17
16
 
@@ -20,8 +19,7 @@ module Hecks
20
19
  # @param context [Context] the era check so far; `latest` is the latest held era, named
21
20
  # here if it has no name yet
22
21
  # @return [Integer] the ordinal of the era minted
23
- # @raise [Runtime::WiringError] if the edge, its approval, its coverage or its audit
24
- # refuses
22
+ # @raise [Runtime::WiringError] if the edge, its coverage or its audit refuses
25
23
  def mint!(context)
26
24
  ensure_named!(context.lineage, context.latest)
27
25
  context = context.with(latest: context.lineage.eras.last)
@@ -41,29 +39,6 @@ module Hecks
41
39
  edges.first
42
40
  end
43
41
 
44
- # Refuses a mint whose edge carries a compute or rekey rule without an approval.
45
- #
46
- # A compute or rekey's only verification is a human-approved audit sample. Two approvals
47
- # satisfy it: one recorded in the journal that matches the edge and the journal's current
48
- # tip, or a committed `translations/<edge>.approval` that matches the edge's digest and
49
- # records a passed rehearsal on a compatible host release. A committed approval that
50
- # applies is written into the journal, so the journal stays the single history.
51
- def ensure_compute_rekey_approved!(context, edge)
52
- return unless Translation::ApprovalFile.needs_rehearsal?(edge)
53
-
54
- approval = context.lineage.approval_for(from: edge.from, to: edge.to)
55
- digest = Translation::Audit.edge_digest(edge)
56
- tip = context.lineage.last_ordinal
57
- return if approved_at_tip?(approval, digest, tip)
58
-
59
- if Translation::ApprovalFile.applicable(context.directory, edge)
60
- context.lineage.record_approval!(from: edge.from, to: edge.to, edge_digest: digest)
61
- return
62
- end
63
-
64
- refuse_unapproved!(context, edge, approval, tip)
65
- end
66
-
67
42
  # Diffs the held era against the current shape and writes the edge file —
68
43
  # confident rules inline, ambiguities as parse-refusing `unresolved` lines.
69
44
  def scaffold!(_registry, bluebook, lineage, latest, directory)
@@ -104,10 +79,9 @@ module Hecks
104
79
  chain
105
80
  end
106
81
 
107
- # The edge leaving the held era, once resolved, approved and found to cover the diff.
82
+ # The edge leaving the held era, once resolved and found to cover the diff.
108
83
  def verified_edge!(context)
109
84
  edge = resolve_edge!(context)
110
- ensure_compute_rekey_approved!(context, edge)
111
85
  check_coverage!(context.registry, context.bluebook, shadow(context.latest[:held_text]), edge)
112
86
  edge
113
87
  end
@@ -2,8 +2,8 @@ module Hecks
2
2
  module Adapters
3
3
  class PostgresEra
4
4
  module LineageManager
5
- # The refusals a mint raises: toward the scaffold when no edge exists, and for a forked,
6
- # stale or unapproved edge. Each is built from the era check's `Context`.
5
+ # The refusals a mint raises: toward the scaffold when no edge exists, and for a forked or
6
+ # stale edge. Each is built from the era check's `Context`.
7
7
  module Refusals
8
8
  # Refuses toward the authoring loop, or, under HECKS_SCAFFOLD=1, scaffolds the
9
9
  # missing edge first and names the file it wrote.
@@ -37,30 +37,6 @@ module Hecks
37
37
  "re-run hecks scaffold_translation", minting: false)
38
38
  end
39
39
 
40
- # Whether the journal already holds an approval of this edge at the journal's tip.
41
- def approved_at_tip?(approval, digest, tip)
42
- approval && approval[:edge_digest] == digest && approval[:reviewed_ordinal] == tip
43
- end
44
-
45
- def refuse_unapproved!(context, edge, approval, tip)
46
- if approval && approval[:edge_digest] == Translation::Audit.edge_digest(edge)
47
- raise era_refusal(context, "the journal advanced past the approved " \
48
- "review (ordinal #{approval[:reviewed_ordinal]} reviewed, #{tip} now) — the samples a " \
49
- "human approved no longer cover the data; re-run hecks audit_translation with --approve")
50
- end
51
-
52
- mismatch = Translation::ApprovalFile.host_mismatch(context.directory, edge)
53
- raise era_refusal(context, unapproved_reason(mismatch))
54
- end
55
-
56
- def unapproved_reason(mismatch)
57
- return "the committed approval does not apply — #{mismatch}" if mismatch
58
-
59
- "this edge carries a compute or rekey " \
60
- "rule, and the audit's human-approved sample is its only verification — run " \
61
- "hecks audit_translation with --approve, then boot again"
62
- end
63
-
64
40
  # Builds the refusal; a mint's own refusals say "cannot mint era N of X", the
65
41
  # edge-resolution ones say "cannot boot X".
66
42
  def era_refusal(context, text, minting: true)