hecks 3.13.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 (440) 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.bluebook +4 -4
  7. data/examples/pizzas/bluebook/pizzas.hecksagon +5 -3
  8. data/examples/pizzas/config.ru +22 -0
  9. data/lib/hecks/adapters/driven/prism.rb +0 -2
  10. data/lib/hecks/adapters/driven.rb +0 -2
  11. data/lib/hecks/adapters/driving/json.rb +1 -7
  12. data/lib/hecks/bluebook/dsl/attribute_collector.rb +15 -5
  13. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -2
  14. data/lib/hecks/bluebook/dsl/derivation_words.rb +58 -0
  15. data/lib/hecks/bluebook/dsl/translation_builder.rb +6 -37
  16. data/lib/hecks/bluebook/expression/ast_json.rb +0 -9
  17. data/lib/hecks/bluebook/hecksagon.rb +1 -1
  18. data/lib/hecks/bluebook/meta_validator/translation_judge/rules.rb +7 -10
  19. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +4 -8
  20. data/lib/hecks/bluebook/meta_validator.rb +0 -11
  21. data/lib/hecks/bluebook/named_pattern.rb +33 -0
  22. data/lib/hecks/bluebook/translation.rb +18 -10
  23. data/lib/hecks/bluebook/translation_expression/evaluator.rb +63 -0
  24. data/lib/hecks/bluebook/translation_expression/parser.rb +127 -0
  25. data/lib/hecks/bluebook/translation_expression.rb +39 -0
  26. data/lib/hecks/bluebook/translation_plan.rb +70 -0
  27. data/lib/hecks/bluebook.rb +3 -0
  28. data/lib/hecks/chapters.rb +3 -3
  29. data/lib/hecks/cli/domain_stub.rb +1 -1
  30. data/lib/hecks/cli/interview_aggregate.rb +4 -4
  31. data/lib/hecks/composer/adapters/claude_composer.adapter +3 -0
  32. data/lib/hecks/composer/adapters/claude_composer.rb +83 -0
  33. data/lib/hecks/composer/adapters/fake_agent.adapter +3 -0
  34. data/lib/hecks/composer/adapters/fake_agent.rb +55 -0
  35. data/lib/hecks/composer/adapters/jev.adapter +3 -0
  36. data/lib/hecks/composer/adapters/jev.rb +66 -0
  37. data/lib/hecks/composer/agent.rb +113 -0
  38. data/lib/hecks/composer/assistant.rb +110 -0
  39. data/lib/hecks/composer/behavior_run.rb +53 -0
  40. data/lib/hecks/composer/bluebook/composer.bluebook +178 -0
  41. data/lib/hecks/composer/bluebook/composer.hecksagon +16 -0
  42. data/lib/hecks/composer/board.rb +65 -0
  43. data/lib/hecks/composer/board_edit/command_text.rb +39 -0
  44. data/lib/hecks/composer/board_edit/source.rb +84 -0
  45. data/lib/hecks/composer/board_edit.rb +144 -0
  46. data/lib/hecks/composer/check.rb +89 -0
  47. data/lib/hecks/composer/domains.rb +81 -0
  48. data/lib/hecks/composer/jobs.rb +134 -0
  49. data/lib/hecks/composer/layout.rb +37 -0
  50. data/lib/hecks/composer/live.rb +150 -0
  51. data/lib/hecks/composer/outline.rb +48 -0
  52. data/lib/hecks/composer/page.html +989 -0
  53. data/lib/hecks/composer/panels.rb +142 -0
  54. data/lib/hecks/composer/parse.rb +83 -0
  55. data/lib/hecks/composer/plain.rb +28 -0
  56. data/lib/hecks/composer/ports/composer_agent.port +7 -0
  57. data/lib/hecks/composer/session.rb +141 -0
  58. data/lib/hecks/composer/toolbox.rb +166 -0
  59. data/lib/hecks/composer/web.rb +156 -0
  60. data/lib/hecks/composer/workbench.rb +51 -0
  61. data/lib/hecks/composer/workspace.rb +81 -0
  62. data/lib/hecks/composer.rb +17 -0
  63. data/lib/hecks/corpus/adapters.rb +22 -0
  64. data/lib/hecks/corpus/rust.rb +1 -1
  65. data/lib/hecks/corpus/worlds.rb +19 -0
  66. data/lib/hecks/corpus.rb +5 -0
  67. data/lib/hecks/deploy/bluebook/deploy.bluebook +34 -34
  68. data/lib/hecks/forms/app/commands.rb +2 -3
  69. data/lib/hecks/forms/app/queries.rb +2 -3
  70. data/lib/hecks/forms/app/records.rb +17 -5
  71. data/lib/hecks/forms/command_form_renderer.rb +42 -16
  72. data/lib/hecks/forms/field_controls.rb +2 -2
  73. data/lib/hecks/forms/field_renderer.rb +1 -1
  74. data/lib/hecks/forms/field_shape.rb +36 -17
  75. data/lib/hecks/forms/index_renderer.rb +2 -1
  76. data/lib/hecks/forms/page.rb +1 -1
  77. data/lib/hecks/forms/params.rb +0 -13
  78. data/lib/hecks/forms/query_form_renderer.rb +7 -6
  79. data/lib/hecks/forms/record_renderer.rb +11 -7
  80. data/lib/hecks/forms/rule_words.rb +100 -0
  81. data/lib/hecks/forms/value_object_shape.rb +2 -1
  82. data/lib/hecks/framework/bluebook/change.bluebook +2 -2
  83. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -3
  84. data/lib/hecks/fuzzing/domain_generator/block_rendering.rb +1 -1
  85. data/lib/hecks/fuzzing/isolated_boot/worlds.rb +3 -6
  86. data/lib/hecks/fuzzing/replay/filters.rb +2 -6
  87. data/lib/hecks/gate/stages.yml +25 -0
  88. data/lib/hecks/grammar/evolve/seeds.rb +0 -6
  89. data/lib/hecks/grammar/evolve.rb +0 -3
  90. data/lib/hecks/grammar/translation.bluebook +1 -1
  91. data/lib/hecks/hecks/adapters/codebase/composing.rb +56 -0
  92. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +3 -1
  93. data/lib/hecks/hecks/adapters/journal_store/changes.rb +0 -26
  94. data/lib/hecks/hecks/adapters/journal_store/examination.rb +2 -23
  95. data/lib/hecks/hecks/adapters/journal_store/readings.rb +3 -11
  96. data/lib/hecks/hecks/adapters/journal_store.rb +0 -11
  97. data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +4 -11
  98. data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +0 -9
  99. data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +0 -4
  100. data/lib/hecks/hecks/codebase.behaviors +6 -0
  101. data/lib/hecks/hecks/codebase.bluebook +58 -37
  102. data/lib/hecks/hecks/composer.behaviors +68 -0
  103. data/lib/hecks/hecks/context_map.hecksagon +1 -1
  104. data/lib/hecks/hecks/custodian.behaviors +0 -6
  105. data/lib/hecks/hecks/custodian.bluebook +15 -92
  106. data/lib/hecks/hecks/hecks.bluebook +2 -2
  107. data/lib/hecks/hecks/hecks.hecksagon +2 -2
  108. data/lib/hecks/hecks/hecks.world +1 -1
  109. data/lib/hecks/hecks/rehearsal.bluebook +10 -16
  110. data/lib/hecks/hecks/tooling.bluebook +22 -22
  111. data/lib/hecks/kernel_capabilities/templates.rb +4 -4
  112. data/lib/hecks/kernel_capabilities.rb +1 -1
  113. data/lib/hecks/language/bluebook/aggregate.bluebook +1 -0
  114. data/lib/hecks/language/bluebook/command.bluebook +1 -0
  115. data/lib/hecks/language/bluebook/entity.bluebook +1 -0
  116. data/lib/hecks/language/bluebook/query.bluebook +1 -0
  117. data/lib/hecks/language/bluebook/shape.bluebook +8 -7
  118. data/lib/hecks/language/bluebook/vocabulary.bluebook +2 -3
  119. data/lib/hecks/language/hecksagon/port_operation.bluebook +1 -0
  120. data/lib/hecks/language/translation/translation_aggregate.bluebook +21 -24
  121. data/lib/hecks/ports/access_control.rb +2 -2
  122. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +3 -41
  123. data/lib/hecks/ports/persistence/plugins/era/lineage/coverage.rb +7 -7
  124. data/lib/hecks/ports/persistence/plugins/era/lineage/rewrite.rb +25 -2
  125. data/lib/hecks/ports/persistence/plugins/era/lineage/rules.rb +4 -4
  126. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +17 -25
  127. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/base_sql.rb +0 -12
  128. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/domain_rename.rb +1 -1
  129. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +2 -22
  130. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +0 -1
  131. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +9 -3
  132. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +3 -29
  133. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/refusals.rb +2 -26
  134. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +6 -33
  135. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -3
  136. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +2 -4
  137. data/lib/hecks/ports/persistence/plugins/era/translation/expression_sql.rb +52 -0
  138. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +23 -29
  139. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +1 -1
  140. data/lib/hecks/ports/persistence/plugins/era/translation.rb +0 -1
  141. data/lib/hecks/ports/persistence/plugins/era.rb +3 -6
  142. data/lib/hecks/ports.rb +0 -1
  143. data/lib/hecks/projection_files.rb +3 -1
  144. data/lib/hecks/projections/deploy/templates/lambda/nat_comment_oauth.tmpl +1 -1
  145. data/lib/hecks/projections/glossary/entries.rb +10 -4
  146. data/lib/hecks/projections/glossary/groups.rb +42 -0
  147. data/lib/hecks/projections/glossary/html/page.rb +7 -2
  148. data/lib/hecks/projections/glossary/html/parser.rb +1 -1
  149. data/lib/hecks/projections/glossary/html/sections.rb +31 -11
  150. data/lib/hecks/projections/glossary/html.rb +1 -2
  151. data/lib/hecks/projections/glossary/markdown.rb +31 -28
  152. data/lib/hecks/projections/glossary/mermaid.rb +3 -0
  153. data/lib/hecks/projections/glossary/page.css +143 -153
  154. data/lib/hecks/projections/glossary/page.js +63 -3
  155. data/lib/hecks/projections/glossary/sentences/actions.rb +88 -0
  156. data/lib/hecks/projections/glossary/sentences/paths.rb +77 -0
  157. data/lib/hecks/projections/glossary/sentences/queries.rb +90 -0
  158. data/lib/hecks/projections/glossary/sentences/value_objects.rb +32 -5
  159. data/lib/hecks/projections/glossary/sentences.rb +13 -3
  160. data/lib/hecks/projections/glossary/slugs.rb +4 -1
  161. data/lib/hecks/projections/glossary.rb +2 -1
  162. data/lib/hecks/projections/plain_english.rb +97 -0
  163. data/lib/hecks/projections/rust_vocabulary/arguments.rb +2 -2
  164. data/lib/hecks/projections/rust_vocabulary/enum_body.rb +20 -25
  165. data/lib/hecks/projections/rust_vocabulary/tests.rb +2 -2
  166. data/lib/hecks/projections/rust_vocabulary.rb +14 -6
  167. data/lib/hecks/projections/site/cms_editor/color.rb +0 -6
  168. data/lib/hecks/projections/statements/actions.rb +99 -0
  169. data/lib/hecks/projections/statements/relationships.rb +86 -0
  170. data/lib/hecks/projections/statements.rb +15 -24
  171. data/lib/hecks/projector/exporter/adapters.rb +112 -0
  172. data/lib/hecks/projector/exporter/translations.rb +18 -35
  173. data/lib/hecks/projector/exporter/worlds.rb +103 -0
  174. data/lib/hecks/projector/exporter.rb +5 -1
  175. data/lib/hecks/quality_control/adapters/agent.rb +6 -7
  176. data/lib/hecks/quality_control/adapters/agent_profile.rb +16 -114
  177. data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +1 -1
  178. data/lib/hecks/quality_control/quality_control.bluebook +51 -51
  179. data/lib/hecks/runtime/loader/boot_steps.rb +0 -20
  180. data/lib/hecks/runtime/outbox.rb +2 -9
  181. data/lib/hecks/runtime/port_operation_interpreter.rb +1 -9
  182. data/lib/hecks/runtime/remote_dispatcher.rb +2 -2
  183. data/lib/hecks/runtime/value.rb +13 -0
  184. data/lib/hecks/rust_build/project_rust/target_ir.rb +23 -4
  185. data/lib/hecks/rust_build/wasm.rb +0 -12
  186. data/lib/hecks/rust_build/write_if_changed.rb +12 -12
  187. data/lib/hecks/rust_format.rb +77 -0
  188. data/lib/hecks/site/bluebook/site.bluebook +5 -5
  189. data/lib/hecks/sme/bluebook/sme.bluebook +12 -12
  190. data/lib/hecks/tickets/bluebook/tickets.bluebook +3 -3
  191. data/lib/hecks/tools/fuzz_sweep/findings.rb +3 -9
  192. data/lib/hecks/tools/fuzz_sweep.rb +1 -3
  193. data/lib/hecks/tools/translation_audit/verdicts.rb +3 -28
  194. data/lib/hecks/tools/translation_audit.rb +8 -10
  195. data/lib/hecks/tools/translation_scaffold/reporting.rb +1 -1
  196. data/lib/hecks/version.rb +1 -1
  197. data/lib/hecks/vocabulary.rb +2 -1
  198. data/lib/hecks.rb +3 -0
  199. data/prebuilt/{58208399bada10b0db99928c48e38789288fc58f7b71a72b21bfa386680e32ed.marshal → 02c09c73b272c1fb5a9ae6d87bb5e5e529208e00f6b4b597dc2c0fd70a9c0964.marshal} +0 -0
  200. data/prebuilt/{2b59737fbdbd5f44edda0fddce2611f45d76087b3a7c782d1f1a17ea58c77948.marshal → bb84a835cf6a15136bad5ca03aa5bf93a5b92f5f72444618d3ef2412b987fb32.marshal} +0 -0
  201. data/prebuilt/verdicts-f1f10da14c8e8f01b4c9b7e3e2d74be8cd2bf4676364ed17fa16975d2a076d75.json +1 -0
  202. data/rust/Cargo.toml +40 -0
  203. data/rust/build/Cargo.toml +36 -0
  204. data/rust/build/clippy.toml +4 -0
  205. data/rust/build/src/adapters_pass.rs +196 -0
  206. data/rust/build/src/binding_pass.rs +54 -13
  207. data/rust/build/src/build_artifact.rs +23 -5
  208. data/rust/build/src/cargo_sync.rs +69 -26
  209. data/rust/build/src/lineage_pass.rs +101 -87
  210. data/rust/build/src/main.rs +11 -2
  211. data/rust/build/src/pipeline.rs +80 -19
  212. data/rust/build/src/reserved_names.rs +6 -50
  213. data/rust/build/src/resolve.rs +82 -35
  214. data/rust/build/src/rustfmt.rs +100 -0
  215. data/rust/build/src/seams_pass.rs +85 -16
  216. data/rust/build/src/subprocess.rs +30 -6
  217. data/rust/build/src/tmp.rs +5 -2
  218. data/rust/build/src/translation_pass.rs +72 -22
  219. data/rust/build/src/world_pass.rs +109 -0
  220. data/rust/clippy.toml +4 -0
  221. data/rust/codegen/Cargo.toml +36 -0
  222. data/rust/codegen/clippy.toml +4 -0
  223. data/rust/codegen/src/asks.rs +40 -14
  224. data/rust/codegen/src/attr.rs +2 -2
  225. data/rust/codegen/src/bridging.rs +676 -117
  226. data/rust/codegen/src/commands.rs +979 -373
  227. data/rust/codegen/src/constraints.rs +88 -25
  228. data/rust/codegen/src/dependency_planning.rs +128 -34
  229. data/rust/codegen/src/domain_generator.rs +300 -170
  230. data/rust/codegen/src/exemplar.rs +87 -32
  231. data/rust/codegen/src/expr_emitter.rs +54 -6
  232. data/rust/codegen/src/fielded.rs +193 -41
  233. data/rust/codegen/src/hecks_naming.rs +124 -16
  234. data/rust/codegen/src/hecks_naming_tests.rs +81 -0
  235. data/rust/codegen/src/json.rs +80 -44
  236. data/rust/codegen/src/json_codec.rs +585 -149
  237. data/rust/codegen/src/literal.rs +104 -138
  238. data/rust/codegen/src/main.rs +145 -52
  239. data/rust/codegen/src/manifest.rs +76 -14
  240. data/rust/codegen/src/mutations.rs +1212 -239
  241. data/rust/codegen/src/naming.rs +111 -60
  242. data/rust/codegen/src/optional_pass.rs +90 -28
  243. data/rust/codegen/src/ports.rs +5 -4
  244. data/rust/codegen/src/prelude.rs +139 -49
  245. data/rust/codegen/src/queries.rs +601 -226
  246. data/rust/codegen/src/reactions.rs +268 -74
  247. data/rust/codegen/src/read_models.rs +606 -205
  248. data/rust/codegen/src/reference_specs.rs +16 -3
  249. data/rust/codegen/src/registry.rs +128 -79
  250. data/rust/codegen/src/reserved_names.rs +6 -50
  251. data/rust/codegen/src/shared.rs +71 -22
  252. data/rust/codegen/src/sidecars.rs +15 -5
  253. data/rust/codegen/src/skip_reason.rs +8 -2
  254. data/rust/codegen/src/types.rs +306 -108
  255. data/rust/host/Cargo.lock +1 -0
  256. data/rust/host/Cargo.toml +40 -4
  257. data/rust/host/HECKS_RELEASE +1 -1
  258. data/rust/host/clippy.toml +4 -0
  259. data/rust/host/src/api.rs +698 -218
  260. data/rust/host/src/auth.rs +491 -214
  261. data/rust/host/src/b64.rs +91 -0
  262. data/rust/host/src/bin/expr_harness.rs +6 -2
  263. data/rust/host/src/bin/lineage_harness.rs +47 -11
  264. data/rust/host/src/bin/mint_harness.rs +70 -37
  265. data/rust/host/src/bin/validate_harness.rs +16 -8
  266. data/rust/host/src/boot.rs +144 -68
  267. data/rust/host/src/boundary_fuzz/api.rs +130 -32
  268. data/rust/host/src/boundary_fuzz/auth.rs +108 -23
  269. data/rust/host/src/boundary_fuzz/expr_json.rs +85 -11
  270. data/rust/host/src/boundary_fuzz/ir.rs +164 -12
  271. data/rust/host/src/boundary_fuzz/server.rs +283 -36
  272. data/rust/host/src/boundary_fuzz/web.rs +56 -8
  273. data/rust/host/src/checkout.rs +78 -32
  274. data/rust/host/src/clock.rs +8 -0
  275. data/rust/host/src/commerce_ir.rs +333 -95
  276. data/rust/host/src/crypto.rs +33 -0
  277. data/rust/host/src/dispatch.rs +990 -331
  278. data/rust/host/src/expr_json.rs +1021 -273
  279. data/rust/host/src/extension.rs +24 -4
  280. data/rust/host/src/field_hints.rs +11 -7
  281. data/rust/host/src/fuzz_support.rs +85 -13
  282. data/rust/host/src/host.rs +100 -0
  283. data/rust/host/src/http.rs +22 -0
  284. data/rust/host/src/ir.rs +326 -16
  285. data/rust/host/src/journal.rs +277 -176
  286. data/rust/host/src/lambda_client.rs +219 -53
  287. data/rust/host/src/lib.rs +16 -4
  288. data/rust/host/src/log.rs +62 -11
  289. data/rust/host/src/mint.rs +1153 -326
  290. data/rust/host/src/needs.rs +185 -44
  291. data/rust/host/src/payments/keystore.rs +197 -45
  292. data/rust/host/src/payments/roles.rs +191 -0
  293. data/rust/host/src/payments/tests.rs +1445 -275
  294. data/rust/host/src/payments.rs +409 -121
  295. data/rust/host/src/platform_signin/attack_tests.rs +5 -5
  296. data/rust/host/src/platform_signin/config.rs +11 -11
  297. data/rust/host/src/platform_signin/cookie.rs +2 -2
  298. data/rust/host/src/platform_signin/extension.rs +2 -1
  299. data/rust/host/src/platform_signin/handler.rs +9 -8
  300. data/rust/host/src/platform_signin/jti.rs +4 -2
  301. data/rust/host/src/platform_signin/jwks.rs +2 -1
  302. data/rust/host/src/platform_signin/jws.rs +2 -37
  303. data/rust/host/src/platform_signin/known_answer_tests.rs +5 -1
  304. data/rust/host/src/platform_signin/mode_tests.rs +5 -3
  305. data/rust/host/src/platform_signin/pages.rs +20 -20
  306. data/rust/host/src/platform_signin/session.rs +8 -1
  307. data/rust/host/src/platform_signin/settings.rs +6 -6
  308. data/rust/host/src/platform_signin/strict_json.rs +12 -9
  309. data/rust/host/src/platform_signin/verify.rs +41 -41
  310. data/rust/host/src/platform_signin/wiring_support.rs +13 -18
  311. data/rust/host/src/presentation.rs +207 -63
  312. data/rust/host/src/presentation_write.rs +502 -187
  313. data/rust/host/src/query_step.rs +69 -18
  314. data/rust/host/src/rate_limit.rs +541 -142
  315. data/rust/host/src/reference_transform.rs +77 -25
  316. data/rust/host/src/reference_validate.rs +443 -104
  317. data/rust/host/src/resend.rs +190 -55
  318. data/rust/host/src/secrets.rs +116 -21
  319. data/rust/host/src/server.rs +483 -104
  320. data/rust/host/src/storage_shape.rs +88 -21
  321. data/rust/host/src/test_pg.rs +17 -7
  322. data/rust/host/src/ui_schema.rs +495 -207
  323. data/rust/host/src/wasm_runner.rs +21 -6
  324. data/rust/host/src/web/commerce.rs +101 -23
  325. data/rust/host/src/web/newsletter/cooldown.rs +10 -3
  326. data/rust/host/src/web/newsletter/template.rs +26 -7
  327. data/rust/host/src/web/newsletter/tests.rs +112 -23
  328. data/rust/host/src/web/newsletter.rs +209 -59
  329. data/rust/host/src/web/newsletter_send.rs +251 -79
  330. data/rust/host/src/web/registration_receipt/tests.rs +16 -4
  331. data/rust/host/src/web/registration_receipt.rs +100 -24
  332. data/rust/host/src/web/registrations.rs +1384 -332
  333. data/rust/host/src/web/resend_webhook.rs +138 -80
  334. data/rust/host/src/web.rs +2416 -508
  335. data/rust/lsp/Cargo.toml +36 -0
  336. data/rust/lsp/clippy.toml +4 -0
  337. data/rust/lsp/src/json.rs +29 -22
  338. data/rust/lsp/src/main.rs +81 -31
  339. data/rust/lsp/src/outline.rs +23 -7
  340. data/rust/lsp/src/rpc.rs +17 -4
  341. data/rust/parser/Cargo.toml +36 -0
  342. data/rust/parser/clippy.toml +4 -0
  343. data/rust/parser/src/build/closed_sets.rs +6 -1
  344. data/rust/parser/src/build/identity.rs +4 -3
  345. data/rust/parser/src/build/mod.rs +1 -0
  346. data/rust/parser/src/build/named_pattern.rs +26 -0
  347. data/rust/parser/src/build/naming.rs +2 -80
  348. data/rust/parser/src/build/pattern_subset.rs +6 -8
  349. data/rust/parser/src/build/query_derive.rs +3 -1
  350. data/rust/parser/src/build/query_inference.rs +14 -5
  351. data/rust/parser/src/build/read_model.rs +4 -1
  352. data/rust/parser/src/build/references.rs +4 -3
  353. data/rust/parser/src/canonical.rs +34 -15
  354. data/rust/parser/src/emit.rs +187 -105
  355. data/rust/parser/src/expr/ast_json.rs +134 -42
  356. data/rust/parser/src/expr/evaluator.rs +27 -9
  357. data/rust/parser/src/expr/mod.rs +54 -13
  358. data/rust/parser/src/expr/resolver.rs +299 -92
  359. data/rust/parser/src/ir.rs +35 -3
  360. data/rust/parser/src/keywords.rs +7168 -510
  361. data/rust/parser/src/lex.rs +11 -25
  362. data/rust/parser/src/lib.rs +1 -0
  363. data/rust/parser/src/main.rs +65 -6
  364. data/rust/parser/src/parse/adapter.rs +157 -0
  365. data/rust/parser/src/parse/aggregate.rs +19 -10
  366. data/rust/parser/src/parse/chapter.rs +16 -17
  367. data/rust/parser/src/parse/command.rs +35 -16
  368. data/rust/parser/src/parse/domain_port.rs +4 -7
  369. data/rust/parser/src/parse/entity.rs +21 -11
  370. data/rust/parser/src/parse/hecksagon.rs +10 -4
  371. data/rust/parser/src/parse/lifecycle.rs +9 -5
  372. data/rust/parser/src/parse/mod.rs +43 -91
  373. data/rust/parser/src/parse/needs.rs +21 -12
  374. data/rust/parser/src/parse/policy.rs +2 -2
  375. data/rust/parser/src/parse/process_manager.rs +9 -11
  376. data/rust/parser/src/parse/query.rs +8 -4
  377. data/rust/parser/src/parse/read_model.rs +57 -16
  378. data/rust/parser/src/parse/value_object.rs +7 -2
  379. data/rust/parser/src/parse/world.rs +322 -0
  380. data/rust/parser/src/ruby_value.rs +7 -6
  381. data/rust/src/exemplar/json.rs +4 -4
  382. data/rust/src/exemplar/mutations.rs +18 -0
  383. data/rust/src/exemplar/read_models.rs +1 -10
  384. data/rust/src/kernel/attribute_shapes/composite.rs +54 -14
  385. data/rust/src/kernel/attribute_shapes/list.rs +2 -2
  386. data/rust/src/kernel/attribute_shapes/mod.rs +8 -8
  387. data/rust/src/kernel/attribute_shapes/scalar.rs +2 -2
  388. data/rust/src/kernel/cli.rs +229 -112
  389. data/rust/src/kernel/dispatch.rs +323 -115
  390. data/rust/src/kernel/dispatch_guard_tests.rs +327 -65
  391. data/rust/src/kernel/expr.rs +203 -86
  392. data/rust/src/kernel/expression_operators/arithmetic.rs +138 -25
  393. data/rust/src/kernel/expression_operators/comparison.rs +87 -2
  394. data/rust/src/kernel/expression_operators/enumeration.rs +106 -22
  395. data/rust/src/kernel/expression_operators/logical.rs +9 -3
  396. data/rust/src/kernel/expression_operators/membership.rs +3 -1
  397. data/rust/src/kernel/expression_operators/mod.rs +34 -21
  398. data/rust/src/kernel/expression_operators/pattern_match.rs +46 -11
  399. data/rust/src/kernel/expression_operators/positional.rs +84 -30
  400. data/rust/src/kernel/expression_operators/presence.rs +52 -13
  401. data/rust/src/kernel/expression_operators/sign_test.rs +3 -1
  402. data/rust/src/kernel/expression_operators/sized.rs +24 -8
  403. data/rust/src/kernel/expression_operators/text.rs +120 -27
  404. data/rust/src/kernel/expression_operators/to_string.rs +10 -4
  405. data/rust/src/kernel/json.rs +230 -148
  406. data/rust/src/kernel/mod.rs +46 -33
  407. data/rust/src/kernel/named_query.rs +63 -15
  408. data/rust/src/kernel/needs.rs +112 -25
  409. data/rust/src/kernel/orchestrate.rs +980 -581
  410. data/rust/src/kernel/orchestrate_guard_tests.rs +538 -140
  411. data/rust/src/kernel/pattern.rs +107 -34
  412. data/rust/src/kernel/query_comparators.rs +118 -41
  413. data/rust/src/kernel/query_ordering.rs +13 -5
  414. data/rust/src/kernel/read_model.rs +479 -181
  415. data/rust/src/kernel/reference_lookup.rs +122 -26
  416. data/rust/src/kernel/repository.rs +293 -66
  417. data/rust/src/kernel/routing.rs +58 -42
  418. data/rust/src/kernel/ruby_float.rs +139 -0
  419. data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +57 -57
  420. data/rust/src/kernel/vocab/entity_dispatch_order.rs +54 -54
  421. data/rust/src/kernel/vocab/field_hint.rs +19 -25
  422. data/rust/src/kernel/vocab/mod.rs +6 -6
  423. data/rust/src/kernel/vocab/query_comparator.rs +23 -24
  424. data/rust/src/kernel/vocab/refusal_template.rs +501 -297
  425. data/rust/src/lib.rs +50 -0
  426. data/rust/src/main.rs +4 -1
  427. data/rust/web/Cargo.toml +36 -0
  428. data/rust/web/clippy.toml +4 -0
  429. metadata +85 -15
  430. data/examples/pizzas/bluebook/hecksagon/mock_stripe_payment_adapter.rb +0 -36
  431. data/lib/hecks/adapters/driven/google_authentication.adapter +0 -3
  432. data/lib/hecks/adapters/driven/google_authentication.rb +0 -99
  433. data/lib/hecks/adapters/driven/mock_stripe_adapter.adapter +0 -3
  434. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +0 -32
  435. data/lib/hecks/ports/authentication.port +0 -6
  436. data/lib/hecks/ports/authentication.rb +0 -67
  437. data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +0 -255
  438. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +0 -22
  439. data/prebuilt/verdicts-48318ca172edd21443b1ad02801c697fff100b746e828f09db987647641a5df2.json +0 -1
  440. data/rust/host/src/approval.rs +0 -652
@@ -117,8 +117,8 @@ module Hecks
117
117
  [domain, aggregate]
118
118
  end
119
119
 
120
- # Not every aggregate in a routed domain is Lambda-bound (a `compute` rule can
121
- # only run against Postgres). Test the adapter's capability, not its name.
120
+ # Not every aggregate in a routed domain is Lambda-bound (a persistence binding
121
+ # may name another adapter). Test the adapter's capability, not its name.
122
122
  def remote_bound?(domain, aggregate)
123
123
  adapter_name = Ports::Persistence::BindingPolicy.resolve(@registry, domain, aggregate).adapter
124
124
  @registry.adapter_class(adapter_name) <= Ports::Persistence::RemoteRuntime
@@ -102,6 +102,19 @@ module Hecks
102
102
  end
103
103
  end
104
104
 
105
+ # Converts every Hash key to a Symbol, at every depth of parsed JSON.
106
+ #
107
+ # @param value [Hash, Array, Object] a Hash or Array is walked; any other value is a leaf
108
+ # @return [Hash{Symbol => Object}, Array, Object] a new structure of the same shape with
109
+ # Symbol keys; a leaf is returned as it came
110
+ def self.deep_symbolize(value)
111
+ case value
112
+ when Hash then value.to_h { |key, item| [key.to_sym, deep_symbolize(item)] }
113
+ when Array then value.map { |item| deep_symbolize(item) }
114
+ else value
115
+ end
116
+ end
117
+
105
118
  # `materialize`, but a single-attribute value object unwraps to its bare field.
106
119
  # Opt-in for `group_by` rows; `materialize` keeps the wrapped shape callers depend on.
107
120
  #
@@ -36,6 +36,7 @@ module Hecks
36
36
  ir = shaped(exporter.call(@registry).fetch(@domain_name))
37
37
  ir[:lineage] = shaped(exporter.lineage(@registry, @domain_name))
38
38
  ir[:persistence] = shaped(exporter.persistence(@registry, @domain_name))
39
+ add_persistence_wiring(ir, exporter)
39
40
  add_seams(ir, exporter)
40
41
  add_edges_and_source(ir)
41
42
  ir
@@ -43,8 +44,28 @@ module Hecks
43
44
 
44
45
  private
45
46
 
47
+ # The adapters and the world that the host checks the persistence against.
48
+ def add_persistence_wiring(document, exporter)
49
+ document[:adapters] = adapters(exporter, document[:persistence])
50
+ document[:world] = world(exporter)
51
+ end
52
+
46
53
  def shaped(payload) = self.class.json_shaped(payload)
47
54
 
55
+ # The bound persistence adapters' declarations, from the `.adapter` files under the
56
+ # source root's `lib/` and the domain's own directory, so the host can look them up.
57
+ def adapters(exporter, persistence)
58
+ files = exporter.adapter_files_under([File.join(RustBuild::ROOT, "lib"), File.dirname(@bluebook_dir)])
59
+ shaped(exporter.persistence_adapters(persistence, files))
60
+ end
61
+
62
+ # The target's world persistence, from `<domain directory name>.world` beside the bluebooks
63
+ # (names only), so the host can check what the world binds against the adapters.
64
+ def world(exporter)
65
+ path = File.join(@bluebook_dir, "#{File.basename(File.dirname(@bluebook_dir))}.world")
66
+ shaped(exporter.persistence_world(path, @domain_name))
67
+ end
68
+
48
69
  def add_seams(document, exporter)
49
70
  seams = SEAMS.to_h { |seam| [seam, exporter.public_send(seam, @registry, @domain_name)] }
50
71
  seams.each { |seam, value| document[seam] = shaped(value) unless value.empty? }
@@ -56,14 +77,12 @@ module Hecks
56
77
  "hecksagon, not a deploy-time guess, names the identity verbs."
57
78
  end
58
79
 
59
- # Edges carry their own precompiled SQL, so a boot-time mint only executes it. Committed
60
- # approvals sit beside the edges. The source text is verbatim: the era's integrity digest is
80
+ # Edges carry their own precompiled SQL, so a boot-time mint only executes it. The source
81
+ # text is verbatim: the era's integrity digest is
61
82
  # the SHA256 of it, not of anything re-derived from the parsed IR.
62
83
  def add_edges_and_source(document)
63
84
  edges = Hecks::Projector::Exporter.translations(@registry).select { |edge| edge[:domain] == @domain_name }
64
85
  document[:translations] = shaped(edges)
65
- approvals = Hecks::Translation::ApprovalFile.read_all(@bluebook_dir)
66
- document[:approvals] = shaped(approvals) unless approvals.empty?
67
86
  document[:source_text] = Hecks::Runtime::EraCheck.source_text_for(
68
87
  @registry.bluebooks.fetch(@domain_name), @bluebook_dir, registry: @registry
69
88
  )
@@ -31,7 +31,6 @@ module Hecks
31
31
  # @raise [Failure] when the target is not installed or a step fails
32
32
  def call(argv)
33
33
  domain = argv.first or raise Failure, "usage: hecks build_wasm <domain>"
34
- return build_with_rust(domain) if ENV["HECKS_PARSER"] == "rust" && ENV["HECKS_CODEGEN"] == "rust"
35
34
 
36
35
  require_target!
37
36
  rust_dir = RustBuild.rust_dir
@@ -48,17 +47,6 @@ module Hecks
48
47
  raise Failure, "project_rust failed for #{domain}" unless status.zero?
49
48
  end
50
49
 
51
- # With the Rust parser and codegen, `hecks-build` runs the same steps and writes the same
52
- # outputs.
53
- def build_with_rust(domain)
54
- build_dir = File.join(ROOT, "rust", "build")
55
- puts "== cargo build (hecks-build) =="
56
- RustBuild.command!("cargo", "build", chdir: build_dir)
57
- puts "== hecks-build #{domain} --wasm =="
58
- RustBuild.command!(File.join(build_dir, "target", "debug", "hecks-build"), domain, "--wasm")
59
- 0
60
- end
61
-
62
50
  def require_target!
63
51
  return if RustBuild.target_installed?(TARGET)
64
52
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "fileutils"
4
+ require_relative "../rust_format"
4
5
 
5
6
  module Hecks
6
7
  module RustBuild
@@ -9,17 +10,8 @@ module Hecks
9
10
  module WriteIfChanged
10
11
  module_function
11
12
 
12
- # Records every path written or confirmed unchanged inside the block, then deletes
13
- # files left in `dir` that were never touched (orphans of removed aggregates).
14
- # Every write is recorded against every open ancestor directory.
15
- # `push_directory`/`pop_and_prune` are the same operation split for non-adjacent writes.
16
- def track_directory(dir)
17
- push_directory(dir)
18
- yield
19
- ensure
20
- pop_and_prune(dir)
21
- end
22
-
13
+ # Starts recording every path written or confirmed unchanged inside `dir`, against every open
14
+ # ancestor directory too; `pop_and_prune` closes it and deletes the files never touched.
23
15
  def push_directory(dir)
24
16
  FileUtils.mkdir_p(dir)
25
17
  stack.push([File.expand_path(dir), Set.new])
@@ -56,6 +48,7 @@ module Hecks
56
48
  # Returns whether it wrote.
57
49
  def call(path, content)
58
50
  record_touch(path)
51
+ content = RustFormat.call(content) if RustFormat.rust?(path)
59
52
  # `.b` on both sides: String#== is encoding-aware, so UTF-8 vs ASCII-8BIT
60
53
  # content can compare unequal even when the bytes are identical.
61
54
  return false if File.exist?(path) && File.binread(path) == content.b
@@ -64,13 +57,20 @@ module Hecks
64
57
  true
65
58
  end
66
59
 
60
+ # Fills `tmp` from the block, then lays it out with rustfmt when `path` is Rust source.
61
+ def write_formatted(tmp, path, &)
62
+ File.open(tmp, "w", &)
63
+ File.write(tmp, RustFormat.call(File.read(tmp))) if RustFormat.rust?(path)
64
+ end
65
+ private_class_method :write_formatted
66
+
67
67
  # Like `call`, for callers that build content by writing to an IO. Yields a sibling
68
68
  # temp file, then renames it into place only if the bytes differ. Returns whether it wrote.
69
69
  def block(path, &)
70
70
  record_touch(path)
71
71
  tmp = "#{path}.tmp-#{Process.pid}-#{rand(1_000_000)}"
72
72
  begin
73
- File.open(tmp, "w", &)
73
+ write_formatted(tmp, path, &)
74
74
  changed = !(File.exist?(path) && FileUtils.compare_file(tmp, path))
75
75
  FileUtils.mv(tmp, path) if changed
76
76
  changed
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+
5
+ module Hecks
6
+ # Runs generated Rust through rustfmt, so a generator writes what `cargo fmt --check` accepts.
7
+ #
8
+ # Every generator that writes a `.rs` file formats its text here before comparing it with the tree
9
+ # or writing it, so a drift check and a write see the same bytes. The edition is pinned in the
10
+ # call, not read from a `rustfmt.toml`, because a generator may run from any directory.
11
+ module RustFormat
12
+ # Raised when the rustfmt program cannot be found.
13
+ class Missing < StandardError; end
14
+
15
+ # Raised when rustfmt fails for a reason that is not the text: a toolchain that will not
16
+ # install, say. Falling back to the unformatted text would write the wrong bytes without a word.
17
+ class Failed < StandardError; end
18
+
19
+ EDITION = "2021"
20
+
21
+ # The most passes `call` makes; a text still changing after them keeps the last result.
22
+ PASSES = 3
23
+
24
+ module_function
25
+
26
+ # @param path [String] where the text will be written
27
+ # @return [Boolean] whether the path is Rust source
28
+ def rust?(path)
29
+ path.to_s.end_with?(".rs")
30
+ end
31
+
32
+ # Formats one Rust source text.
33
+ #
34
+ # Text rustfmt cannot parse is returned as it came, with a warning on stderr: the compiler will
35
+ # name the real fault, and a generator should not be the thing that hides it. Any other failure
36
+ # of rustfmt raises, since the text is not the problem.
37
+ #
38
+ # @param text [String] Rust source
39
+ # @return [String] the same source as rustfmt lays it out
40
+ # @raise [Missing] when rustfmt is not installed
41
+ # @raise [Failed] when rustfmt fails for a reason other than the text
42
+ def call(text)
43
+ PASSES.times do
44
+ formatted = pass(text)
45
+ return formatted if formatted == text
46
+
47
+ text = formatted
48
+ end
49
+ text
50
+ end
51
+
52
+ # One rustfmt run. rustfmt does not always reach a fixed point in one pass, so `call` repeats it
53
+ # until the text stops changing, as a later `cargo fmt --check` would have it.
54
+ #
55
+ # @param text [String] Rust source
56
+ # @return [String] the source after one pass, or unchanged when rustfmt cannot parse it
57
+ # @raise [Failed] when rustfmt fails for a reason other than the text
58
+ def pass(text)
59
+ out, err, status = Open3.capture3("rustfmt", "--edition", EDITION, "--emit", "stdout", stdin_data: text)
60
+ return out if status.success?
61
+ raise Failed, "rustfmt failed: #{err.lines.first&.strip}" unless err.include?("<stdin>")
62
+
63
+ warn "hecks: rustfmt could not format generated Rust, leaving it as generated: #{err.lines.first&.strip}"
64
+ text
65
+ rescue Errno::ENOENT
66
+ raise Missing, "rustfmt is not installed: generated Rust is formatted with it; run `rustup component add rustfmt`"
67
+ end
68
+
69
+ # Formats the Rust texts of a path-to-text map and leaves the others alone.
70
+ #
71
+ # @param content [Hash{String => String}] absolute path to text
72
+ # @return [Hash{String => String}] the same map with each `.rs` text formatted
73
+ def files(content)
74
+ content.to_h { |path, text| [path, rust?(path) ? call(text) : text] }
75
+ end
76
+ end
77
+ end
@@ -129,14 +129,14 @@ Hecks.bluebook "Site" do
129
129
  identified_by :run
130
130
 
131
131
  value_object "RunKey" do
132
- attribute :value, String, pattern: '[^ \t\n\r]'
132
+ attribute :value, String, matches: :present
133
133
 
134
134
  invariant("a run is named") { !value.to_s.empty? }
135
135
  end
136
136
 
137
137
  # A project directory, whose `bluebook/` holds the chapter that declares the route table.
138
138
  value_object "DomainPath" do
139
- attribute :value, String, pattern: '[^\t\n\r]'
139
+ attribute :value, String, matches: :not_empty
140
140
 
141
141
  invariant("a project is named by its path") { !value.to_s.empty? }
142
142
  end
@@ -144,7 +144,7 @@ Hecks.bluebook "Site" do
144
144
  # A directory or file anywhere on disk, relative to where the command runs: where routes.ts is
145
145
  # written when not in the project's generated/ directory, or the template to rewrite in place.
146
146
  value_object "Location" do
147
- attribute :value, String, pattern: '[^\t\n\r]'
147
+ attribute :value, String, matches: :not_empty
148
148
  end
149
149
 
150
150
  # The file extension of the generated module: `ts` (routes.ts), or `mts` (routes.mts), which
@@ -164,12 +164,12 @@ Hecks.bluebook "Site" do
164
164
 
165
165
  # Path patterns, comma separated: the behaviours a change is about to add to the live distribution.
166
166
  value_object "PathList" do
167
- attribute :value, String, pattern: '[^\t\n\r]'
167
+ attribute :value, String, matches: :not_empty
168
168
  end
169
169
 
170
170
  # Policy references and the ids they stand for live, as `!Ref Name=id`, comma separated.
171
171
  value_object "PolicyRefs" do
172
- attribute :value, String, pattern: '[^\t\n\r]'
172
+ attribute :value, String, matches: :not_empty
173
173
  end
174
174
 
175
175
  # A running site's address, `https://example.org`: what `check_site` sends its requests to.
@@ -30,37 +30,37 @@ Hecks.bluebook "SME" do
30
30
  attribute :accepted_actions, Tally
31
31
 
32
32
  value_object "Reference" do
33
- attribute :value, String, pattern: '[^ \t\n\r]'
33
+ attribute :value, String, matches: :present
34
34
  end
35
35
 
36
36
  # The domain being discovered, spelled as its bluebook will spell it. Many interviews, with
37
37
  # different experts on different days, name the same subject; the domain is not modelled here.
38
38
  value_object "Subject" do
39
- attribute :value, String, pattern: '[^ \t\n\r]'
39
+ attribute :value, String, matches: :present
40
40
  end
41
41
 
42
42
  # Who is being interviewed.
43
43
  value_object "Expert" do
44
- attribute :value, String, pattern: '[^\t\n\r]'
44
+ attribute :value, String, matches: :not_empty
45
45
  end
46
46
 
47
47
  value_object "Question" do
48
- attribute :value, String, pattern: '[^\t\n\r]'
48
+ attribute :value, String, matches: :not_empty
49
49
  end
50
50
 
51
51
  # The expert's answer in their own words.
52
52
  value_object "Answer" do
53
- attribute :value, String, pattern: '[^\t\n\r]'
53
+ attribute :value, String, matches: :not_empty
54
54
  end
55
55
 
56
56
  # A label for grouping exchanges ("lending rules"); optional on every exchange.
57
57
  value_object "Topic" do
58
- attribute :value, String, pattern: '[^\t\n\r]'
58
+ attribute :value, String, matches: :not_empty
59
59
  end
60
60
 
61
61
  value_object "Exchange" do
62
- attribute :question, String, pattern: '[^\t\n\r]'
63
- attribute :answer, String, pattern: '[^\t\n\r]'
62
+ attribute :question, String, matches: :not_empty
63
+ attribute :answer, String, matches: :not_empty
64
64
  attribute :topic, String, optional: true
65
65
  end
66
66
 
@@ -76,7 +76,7 @@ Hecks.bluebook "SME" do
76
76
  end
77
77
 
78
78
  value_object "Name" do
79
- attribute :value, String, pattern: '[^ \t\n\r]'
79
+ attribute :value, String, matches: :present
80
80
  end
81
81
 
82
82
  # How many findings of a kind were accepted.
@@ -86,7 +86,7 @@ Hecks.bluebook "SME" do
86
86
  end
87
87
 
88
88
  value_object "Statement" do
89
- attribute :value, String, pattern: '[^\t\n\r]'
89
+ attribute :value, String, matches: :not_empty
90
90
  end
91
91
 
92
92
  # Whether an action brings its thing into being, as opposed to happening to one that exists.
@@ -97,12 +97,12 @@ Hecks.bluebook "SME" do
97
97
  # A list the expert gave in a breath, comma separated: the values a field may take, or the
98
98
  # fields an action needs to be told. Absent when the expert gave none.
99
99
  value_object "Listing" do
100
- attribute :value, String, pattern: '[^\t\n\r]'
100
+ attribute :value, String, matches: :not_empty
101
101
  end
102
102
 
103
103
  # Who may do an action, in the expert's words ("a maintainer", "QA engineer").
104
104
  value_object "Actor" do
105
- attribute :value, String, pattern: '[^\t\n\r]'
105
+ attribute :value, String, matches: :not_empty
106
106
  end
107
107
 
108
108
  # A thing the domain keeps track of, and the field that identifies one: an aggregate to be.
@@ -32,7 +32,7 @@ Hecks.bluebook "Tickets" do
32
32
  identified_by :finding
33
33
 
34
34
  value_object "FindingKey" do
35
- attribute :value, String, pattern: '[^ \t\n\r]'
35
+ attribute :value, String, matches: :present
36
36
 
37
37
  invariant("a finding is named") { !value.to_s.empty? }
38
38
  end
@@ -65,7 +65,7 @@ Hecks.bluebook "Tickets" do
65
65
  # The same finding refiled by the QA loop carries the same fingerprint, so a report of it while an
66
66
  # open finding holds it changes nothing.
67
67
  value_object "Fingerprint" do
68
- attribute :value, String, pattern: '[^ \t\n\r]'
68
+ attribute :value, String, matches: :present
69
69
  end
70
70
 
71
71
  value_object "Number" do
@@ -239,7 +239,7 @@ Hecks.bluebook "Tickets" do
239
239
  end
240
240
 
241
241
  value_object "FindingKey" do
242
- attribute :value, String, pattern: '[^ \t\n\r]'
242
+ attribute :value, String, matches: :present
243
243
  end
244
244
 
245
245
  lifecycle :status, default: "draft" do
@@ -48,19 +48,13 @@ module Hecks
48
48
  failure[:message].split("; ").all? { |chunk| matchers.any? { |regex| regex.match?(chunk) } }
49
49
  end
50
50
 
51
- # Skips only exact, already-tracked wiring gaps; any other WiringError is re-raised.
51
+ # Skips only exact, already-tracked wiring gaps (`SKIPPED_DOMAIN_REASONS`); none is tracked
52
+ # now, so any WiringError is re-raised.
52
53
  #
53
54
  # @param error [Hecks::Runtime::WiringError] what generating a sequence raised
54
55
  # @return [Symbol] a key of `SKIPPED_DOMAIN_REASONS`
55
56
  def known_unfuzzable_wiring_gap(error)
56
- case error.message
57
- when %r{a compute/rekey rule is declared}
58
- # compute/rekey needs an era/Postgres migration, so it cannot boot under IsolatedBoot's
59
- # Memory rebind. Real coverage lives in spec/adapters/driven/postgres_era/*_spec.rb.
60
- :skip_compute_rekey
61
- else
62
- raise error
63
- end
57
+ raise error
64
58
  end
65
59
 
66
60
  # Shrinks steps, then arguments, keeping a removal only while the same finding reproduces.
@@ -29,9 +29,7 @@ module Hecks
29
29
  KNOWN_FUZZ_FINDINGS = {}.freeze
30
30
 
31
31
  # Why a domain is skipped, by the reason `known_unfuzzable_wiring_gap` answers.
32
- SKIPPED_DOMAIN_REASONS = {
33
- skip_compute_rekey: "declares compute/rekey, which only an era/Postgres-bound boot can interpret"
34
- }.freeze
32
+ SKIPPED_DOMAIN_REASONS = {}.freeze
35
33
 
36
34
  # The persistence an ephemeral boot may use.
37
35
  ADAPTERS = %i[memory sqlite postgres].freeze
@@ -30,17 +30,14 @@ module Hecks
30
30
  end
31
31
 
32
32
  # @return [Integer] the exit status
33
- def conclude(db, lineage, edge, failed, approve)
33
+ def conclude(db, failed)
34
+ db.close
34
35
  if failed
35
- db.close
36
36
  puts "AUDIT REFUSED."
37
37
  return 1
38
38
  end
39
39
 
40
- needs_approval = edge.aggregates.any? { |declared| !declared.computes.empty? || !declared.rekeys.empty? }
41
- record_approval(lineage, edge) if needs_approval && approve
42
- db.close
43
- print_passed(needs_approval, approve)
40
+ puts "AUDIT PASSED — review the samples above; intent is yours to approve."
44
41
  0
45
42
  end
46
43
 
@@ -79,28 +76,6 @@ module Hecks
79
76
  puts " before: #{JSON.generate(sample[:before])}" if sample[:before]
80
77
  puts " after: #{JSON.generate(sample[:after])}" if sample[:after]
81
78
  end
82
-
83
- # Binds to what was reviewed — this edge and the journal as it stands now — so it must
84
- # live in the database, not the repo.
85
- def record_approval(lineage, edge)
86
- lineage.record_approval!(
87
- from: edge.from, to: edge.to,
88
- edge_digest: Hecks::Translation::Audit.edge_digest(edge)
89
- )
90
- end
91
-
92
- def print_passed(needs_approval, approve)
93
- if needs_approval && approve
94
- puts "AUDIT PASSED — approval recorded in the database, bound to this edge and this journal."
95
- puts "A journal that advances before the mint invalidates it; re-run with --approve if that happens."
96
- elsif needs_approval
97
- puts "AUDIT PASSED — this edge carries a compute or rekey rule, and the samples above are " \
98
- "its ONLY verification."
99
- puts "If they show what you intended, run again with --approve; the mint refuses until you do."
100
- else
101
- puts "AUDIT PASSED — review the samples above; intent is yours to approve."
102
- end
103
- end
104
79
  end
105
80
  end
106
81
  end
@@ -12,13 +12,12 @@ require_relative "translation_audit/verdicts"
12
12
  module Hecks
13
13
  module Tools
14
14
  # Runs the translation audit's three layers standalone against a domain's latest or pending
15
- # edge. Read-only except `--approve`, which records the human approval in the database.
15
+ # edge. Read-only.
16
16
  #
17
- # hecks audit_translation <domain> [--approve]
17
+ # hecks audit_translation <domain>
18
18
  module TranslationAudit
19
- # What one audit run works on: the loaded domain, its open journal, and whether `--approve`
20
- # was given.
21
- Session = Struct.new(:registry, :bluebook, :directory, :db, :lineage, :approve)
19
+ # What one audit run works on: the loaded domain and its open journal.
20
+ Session = Struct.new(:registry, :bluebook, :directory, :db, :lineage)
22
21
 
23
22
  extend DomainLoading
24
23
  extend Edges
@@ -28,18 +27,17 @@ module Hecks
28
27
 
29
28
  # Audits the edge and prints each aggregate's verdict and samples.
30
29
  #
31
- # @param argv [Array<String>] the domain directory, and `--approve` to record approval
30
+ # @param argv [Array<String>] the domain directory
32
31
  # @param root [String] unused: the domain is read from the path given
33
32
  # @return [Integer] 0 when the audit passes or there is no edge, 1 when it is refused
34
33
  # @raise [SystemExit] when the domain cannot be loaded or is not lineage-capable
35
34
  def main(argv, **)
36
35
  argv = argv.dup
37
- approve = argv.delete("--approve")
38
- domain_path = argv.shift or abort "usage: hecks audit_translation <domain> [--approve]"
36
+ domain_path = argv.shift or abort "usage: hecks audit_translation <domain>"
39
37
 
40
38
  registry, bluebook, directory = load_domain(domain_path)
41
39
  db, lineage = open_lineage(registry, bluebook)
42
- audit(Session.new(registry, bluebook, directory, db, lineage, approve))
40
+ audit(Session.new(registry, bluebook, directory, db, lineage))
43
41
  end
44
42
 
45
43
  # @param session [Session] the loaded domain and its open journal
@@ -59,7 +57,7 @@ module Hecks
59
57
 
60
58
  edge = chain.last[:translation]
61
59
  failed = report(session.bluebook, session.lineage, edge, era, chain)
62
- conclude(session.db, session.lineage, edge, failed, session.approve)
60
+ conclude(session.db, failed)
63
61
  end
64
62
 
65
63
  # A journal with no era yet holds the bluebook as its first, so there is no edge to audit.
@@ -31,7 +31,7 @@ module Hecks
31
31
  "Check it with hecks audit_translation, then boot."
32
32
  elsif unresolved.positive?
33
33
  "#{unresolved} unresolved — decide what each became (rename/move/convert/drop, or " \
34
- "compute on PostgresEra), then boot."
34
+ "derive), then boot."
35
35
  end
36
36
  end
37
37
  end
data/lib/hecks/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Hecks
2
2
  # Read directly by the gemspec, so this file must never require the
3
3
  # rest of the gem (its dependencies aren't resolved yet at that point).
4
- VERSION = "3.13.0".freeze
4
+ VERSION = "4.0.0".freeze # rust/host/HECKS_RELEASE names the same release
5
5
  end
@@ -412,7 +412,6 @@ module Hecks
412
412
  {"script"=>"backfill_era_projections", "section"=>"Custodian", "aggregate"=>"Era", "verb"=>"backfill_projections"}.freeze,
413
413
  {"script"=>"scaffold_translation", "section"=>"Custodian", "aggregate"=>"Era", "verb"=>"scaffold_translation"}.freeze,
414
414
  {"script"=>"translation_audit", "section"=>"Custodian", "aggregate"=>"Era", "verb"=>"audit_translation"}.freeze,
415
- {"script"=>"translation_audit", "section"=>"Custodian", "aggregate"=>"Era", "verb"=>"approve_translation"}.freeze,
416
415
  {"script"=>"compact", "section"=>"Custodian", "aggregate"=>"Era", "verb"=>"compact"}.freeze,
417
416
  {"script"=>"heki_compact", "section"=>"Custodian", "aggregate"=>"Era", "verb"=>"compact_heki"}.freeze,
418
417
  {"script"=>"vendor_bluebook", "section"=>"Custodian", "aggregate"=>"Package", "verb"=>"vendor", "note"=>"waits: a refusal exits 1 with its reason on stderr"}.freeze,
@@ -514,6 +513,8 @@ module Hecks
514
513
  ].freeze,
515
514
  "RustReservedWord" => [
516
515
  {"word"=>"as"}.freeze,
516
+ {"word"=>"async"}.freeze,
517
+ {"word"=>"await"}.freeze,
517
518
  {"word"=>"break"}.freeze,
518
519
  {"word"=>"const"}.freeze,
519
520
  {"word"=>"continue"}.freeze,
data/lib/hecks.rb CHANGED
@@ -45,6 +45,9 @@ Hecks.autoload(:Corpus, File.expand_path("hecks/corpus", __dir__))
45
45
  # The chapters a hecksagon can `attaches` by name (ADR 0080); loads on first use.
46
46
  Hecks.autoload(:Chapters, File.expand_path("hecks/chapters", __dir__))
47
47
 
48
+ # The Composer's staging, parse stage and check; loads on first use.
49
+ Hecks.autoload(:Composer, File.expand_path("hecks/composer", __dir__))
50
+
48
51
  # Root namespace and public facade of the DSL/runtime: `Hecks.boot`/`.boot_files`
49
52
  # assemble a running domain from `.bluebook`/`.hecksagon`/`.world` files.
50
53
  module Hecks