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
@@ -8,7 +8,7 @@ use crate::registry::AggregateEntry;
8
8
 
9
9
  pub fn policy_event_qualifier(on_event: &str) -> Option<String> {
10
10
  if on_event.contains('.') {
11
- Some(on_event.splitn(2, '.').next().unwrap_or("").to_string())
11
+ Some(on_event.split('.').next().unwrap_or("").to_string())
12
12
  } else {
13
13
  None
14
14
  }
@@ -16,13 +16,18 @@ pub fn policy_event_qualifier(on_event: &str) -> Option<String> {
16
16
 
17
17
  pub fn policy_event_name(on_event: &str) -> String {
18
18
  if on_event.contains('.') {
19
- on_event.splitn(2, '.').nth(1).unwrap_or("").to_string()
19
+ on_event.split_once('.').map_or("", |x| x.1).to_string()
20
20
  } else {
21
21
  on_event.to_string()
22
22
  }
23
23
  }
24
24
 
25
- pub fn emit_policy_table(exemplar: &Exemplar, domain_name: &str, policies: &[Json], aggregates: &[Json]) -> String {
25
+ pub fn emit_policy_table(
26
+ exemplar: &Exemplar,
27
+ domain_name: &str,
28
+ policies: &[Json],
29
+ aggregates: &[Json],
30
+ ) -> String {
26
31
  let fns = where_fns(policies);
27
32
  let rows = local_policy_rows(domain_name, policies, aggregates);
28
33
  let table = exemplar.render(
@@ -38,7 +43,12 @@ pub fn emit_policy_table(exemplar: &Exemplar, domain_name: &str, policies: &[Jso
38
43
 
39
44
  // A policy's `where { … }` becomes a generated function the kernel calls.
40
45
  fn where_fn_name(policy: &Json) -> String {
41
- format!("where_{}", naming::dispatch_fn_name(&naming::rust_ident(&policy.get("name").map(Json::to_s).unwrap_or_default())))
46
+ format!(
47
+ "where_{}",
48
+ naming::dispatch_fn_name(&naming::rust_ident(
49
+ &policy.get("name").map(Json::to_s).unwrap_or_default()
50
+ ))
51
+ )
42
52
  }
43
53
 
44
54
  fn where_expr(policy: &Json) -> String {
@@ -48,13 +58,28 @@ fn where_expr(policy: &Json) -> String {
48
58
  }
49
59
  }
50
60
 
61
+ #[allow(
62
+ clippy::panic,
63
+ reason = "a drifted exemplar or invalid IR aborts generation on purpose"
64
+ )]
51
65
  fn where_fns(policies: &[Json]) -> Vec<String> {
52
66
  policies
53
67
  .iter()
54
- .filter(|policy| policy.get("where").map(Json::to_s).map(|w| !w.is_empty()).unwrap_or(false))
68
+ .filter(|policy| {
69
+ policy
70
+ .get("where")
71
+ .map(Json::to_s)
72
+ .is_some_and(|w| !w.is_empty())
73
+ })
55
74
  .map(|policy| {
56
- let ast = policy.get("where_ast").unwrap_or_else(|| panic!("policy with a where has no where_ast: {policy:?}"));
57
- format!("fn {}() -> crate::kernel::Expr {{\n use crate::kernel::Expr;\n {}\n}}", where_fn_name(policy), crate::expr_emitter::emit_ast(ast))
75
+ let ast = policy
76
+ .get("where_ast")
77
+ .unwrap_or_else(|| panic!("policy with a where has no where_ast: {policy:?}"));
78
+ format!(
79
+ "fn {}() -> crate::kernel::Expr {{\n use crate::kernel::Expr;\n {}\n}}",
80
+ where_fn_name(policy),
81
+ crate::expr_emitter::emit_ast(ast)
82
+ )
58
83
  })
59
84
  .collect()
60
85
  }
@@ -63,7 +88,7 @@ fn local_policy_rows(domain_name: &str, policies: &[Json], aggregates: &[Json])
63
88
  crate::asks::resolve_asks(policies, aggregates)
64
89
  .iter()
65
90
  .filter_map(|policy| {
66
- let target_domain = policy.get("target_domain").map(Json::to_s).unwrap_or_else(|| domain_name.to_string());
91
+ let target_domain = policy.get("target_domain").map_or_else(|| domain_name.to_string(), Json::to_s);
67
92
  if target_domain != domain_name {
68
93
  return None;
69
94
  }
@@ -71,10 +96,7 @@ fn local_policy_rows(domain_name: &str, policies: &[Json], aggregates: &[Json])
71
96
  let on_event = policy.get("on_event").map(Json::to_s).unwrap_or_default();
72
97
  let event_name = policy_event_name(&on_event);
73
98
  let qualifier = policy_event_qualifier(&on_event);
74
- let qualifier_expr = match qualifier {
75
- Some(q) => format!("Some({})", naming::ruby_inspect_string(&q)),
76
- None => "None".to_string(),
77
- };
99
+ let qualifier_expr = qualifier.map_or_else(|| "None".to_string(), |q| format!("Some({})", naming::ruby_inspect_string(&q)));
78
100
  let target_verb = format!("{target_domain}::{}", policy.get("trigger_command").map(Json::to_s).unwrap_or_default());
79
101
  let name = policy.get("name").map(Json::to_s).unwrap_or_default();
80
102
 
@@ -97,7 +119,7 @@ fn cross_domain_policy_rows(domain_name: &str, policies: &[Json]) -> Vec<String>
97
119
  policies
98
120
  .iter()
99
121
  .filter_map(|policy| {
100
- let target_domain = policy.get("target_domain").map(Json::to_s).unwrap_or_else(|| domain_name.to_string());
122
+ let target_domain = policy.get("target_domain").map_or_else(|| domain_name.to_string(), Json::to_s);
101
123
  if target_domain == domain_name {
102
124
  return None;
103
125
  }
@@ -105,10 +127,7 @@ fn cross_domain_policy_rows(domain_name: &str, policies: &[Json]) -> Vec<String>
105
127
  let on_event = policy.get("on_event").map(Json::to_s).unwrap_or_default();
106
128
  let event_name = policy_event_name(&on_event);
107
129
  let qualifier = policy_event_qualifier(&on_event);
108
- let qualifier_expr = match qualifier {
109
- Some(q) => format!("Some({})", naming::ruby_inspect_string(&q)),
110
- None => "None".to_string(),
111
- };
130
+ let qualifier_expr = qualifier.map_or_else(|| "None".to_string(), |q| format!("Some({})", naming::ruby_inspect_string(&q)));
112
131
  let target_verb = format!("{target_domain}::{}", policy.get("trigger_command").map(Json::to_s).unwrap_or_default());
113
132
  let name = policy.get("name").map(Json::to_s).unwrap_or_default();
114
133
 
@@ -124,7 +143,11 @@ fn cross_domain_policy_rows(domain_name: &str, policies: &[Json]) -> Vec<String>
124
143
  .collect()
125
144
  }
126
145
 
127
- pub fn emit_cross_domain_policy_table(exemplar: &Exemplar, domain_name: &str, policies: &[Json]) -> String {
146
+ pub fn emit_cross_domain_policy_table(
147
+ exemplar: &Exemplar,
148
+ domain_name: &str,
149
+ policies: &[Json],
150
+ ) -> String {
128
151
  let rows = cross_domain_policy_rows(domain_name, policies);
129
152
 
130
153
  if !rows.is_empty() {
@@ -153,8 +176,16 @@ pub struct PolicySource<'a> {
153
176
  /// Each source's `target_verb` is qualified against its own domain, never the target's;
154
177
  /// omitting a chapter's policies would leave them out of the table `kernel::orchestrate` reads.
155
178
  pub fn emit_merged_policy_table(exemplar: &Exemplar, sources: &[PolicySource]) -> String {
156
- let rows: Vec<String> = sources.iter().flat_map(|source| local_policy_rows(source.domain_name, source.policies, source.aggregates)).collect();
157
- let fns: Vec<String> = sources.iter().flat_map(|source| where_fns(source.policies)).collect();
179
+ let rows: Vec<String> = sources
180
+ .iter()
181
+ .flat_map(|source| {
182
+ local_policy_rows(source.domain_name, source.policies, source.aggregates)
183
+ })
184
+ .collect();
185
+ let fns: Vec<String> = sources
186
+ .iter()
187
+ .flat_map(|source| where_fns(source.policies))
188
+ .collect();
158
189
 
159
190
  let table = exemplar.render(
160
191
  "policy_table",
@@ -168,8 +199,14 @@ pub fn emit_merged_policy_table(exemplar: &Exemplar, sources: &[PolicySource]) -
168
199
  }
169
200
 
170
201
  /// The cross-domain policy table for `merged.rs`, folding in every attached chapter.
171
- pub fn emit_merged_cross_domain_policy_table(exemplar: &Exemplar, sources: &[PolicySource]) -> String {
172
- let rows: Vec<String> = sources.iter().flat_map(|source| cross_domain_policy_rows(source.domain_name, source.policies)).collect();
202
+ pub fn emit_merged_cross_domain_policy_table(
203
+ exemplar: &Exemplar,
204
+ sources: &[PolicySource],
205
+ ) -> String {
206
+ let rows: Vec<String> = sources
207
+ .iter()
208
+ .flat_map(|source| cross_domain_policy_rows(source.domain_name, source.policies))
209
+ .collect();
173
210
 
174
211
  if !rows.is_empty() {
175
212
  println!("cross-domain policy table: {} row(s) — delivered by rust/host's lambda_client.rs, not locally dispatched", rows.len());
@@ -189,18 +226,36 @@ fn with_value_parsed(raw: &str) -> Literal {
189
226
  }
190
227
 
191
228
  // Renders a parsed `with:` literal as a Rust expression building the equivalent `Json`.
229
+ #[allow(
230
+ clippy::panic,
231
+ reason = "a drifted exemplar or invalid IR aborts generation on purpose"
232
+ )]
192
233
  fn json_literal_expr(value: &Literal) -> String {
193
234
  match value {
194
235
  Literal::Hash(pairs) => {
195
- let fields: Vec<String> = pairs.iter().map(|(k, v)| format!("({}.to_string(), {})", naming::ruby_inspect_string(k), json_literal_expr(v))).collect();
236
+ let fields: Vec<String> = pairs
237
+ .iter()
238
+ .map(|(k, v)| {
239
+ format!(
240
+ "({}.to_string(), {})",
241
+ naming::ruby_inspect_string(k),
242
+ json_literal_expr(v)
243
+ )
244
+ })
245
+ .collect();
196
246
  format!("crate::kernel::Json::Object(vec![{}])", fields.join(", "))
197
247
  }
198
- Literal::Str(s) => format!("crate::kernel::Json::Str({}.to_string())", naming::ruby_inspect_string(s)),
248
+ Literal::Str(s) => format!(
249
+ "crate::kernel::Json::Str({}.to_string())",
250
+ naming::ruby_inspect_string(s)
251
+ ),
199
252
  Literal::Int(n) => format!("crate::kernel::Json::int({n})"),
200
253
  Literal::Float(n) => format!("crate::kernel::Json::Num({}f64, None)", ruby_float_text(*n)),
201
254
  Literal::Bool(b) => format!("crate::kernel::Json::Bool({b})"),
202
255
  Literal::Nil => "crate::kernel::Json::Null".to_string(),
203
- other => panic!("unsupported with: literal {other:?} — json_literal_expr doesn't cover this shape"),
256
+ other => panic!(
257
+ "unsupported with: literal {other:?} — json_literal_expr doesn't cover this shape"
258
+ ),
204
259
  }
205
260
  }
206
261
 
@@ -216,20 +271,29 @@ fn ruby_float_text(n: f64) -> String {
216
271
  // `compensates` (saga compensation) recurses one level; a compensation is not itself
217
272
  // compensable, so the recursion ends after one extra call.
218
273
  fn emit_dispatch_spec(exemplar: &Exemplar, spec: &Json, literal_fns: &mut Vec<String>) -> String {
219
- let with_spec = spec.get("with_spec").map(Json::each).unwrap_or(&[]);
274
+ let with_spec = spec.get("with_spec").map_or_default(Json::each);
220
275
  let with_pairs: Vec<String> = with_spec
221
276
  .iter()
222
277
  .map(|pair| {
223
278
  let kv = pair.as_array().unwrap_or(&[]);
224
279
  let key = kv.first().map(Json::to_s).unwrap_or_default();
225
280
  let raw = kv.get(1).map(Json::to_s).unwrap_or_default();
226
- format!("({}, {})", naming::ruby_inspect_string(&key), emit_with_value(exemplar, &raw, literal_fns))
281
+ format!(
282
+ "({}, {})",
283
+ naming::ruby_inspect_string(&key),
284
+ emit_with_value(exemplar, &raw, literal_fns)
285
+ )
227
286
  })
228
287
  .collect();
229
- let compensates = match spec.get("compensates") {
230
- Some(nested) => format!("Some(&{})", emit_dispatch_spec(exemplar, nested, literal_fns)),
231
- None => "None".to_string(),
232
- };
288
+ let compensates = spec.get("compensates").map_or_else(
289
+ || "None".to_string(),
290
+ |nested| {
291
+ format!(
292
+ "Some(&{})",
293
+ emit_dispatch_spec(exemplar, nested, literal_fns)
294
+ )
295
+ },
296
+ );
233
297
  format!(
234
298
  "crate::kernel::DispatchSpec {{ command_name: {}, with: &[{}], compensates: {} }}",
235
299
  naming::ruby_inspect_string(&spec.get("command_name").map(Json::to_s).unwrap_or_default()),
@@ -243,17 +307,29 @@ fn emit_dispatch_spec(exemplar: &Exemplar, spec: &Json, literal_fns: &mut Vec<St
243
307
  fn emit_with_value(exemplar: &Exemplar, raw: &str, literal_fns: &mut Vec<String>) -> String {
244
308
  let parsed = with_value_parsed(raw);
245
309
  if let Literal::Symbol(name) = &parsed {
246
- return format!("crate::kernel::WithValue::Ref({})", naming::ruby_inspect_string(name));
310
+ return format!(
311
+ "crate::kernel::WithValue::Ref({})",
312
+ naming::ruby_inspect_string(name)
313
+ );
247
314
  }
248
315
 
249
316
  let fn_name = format!("pm_literal_{}", literal_fns.len());
250
- literal_fns.push(exemplar.render("with_value_literal_fn", &[("tmpl_literal_fn", fn_name.clone()), ("tmpl_body_placeholder()", json_literal_expr(&parsed))]));
317
+ literal_fns.push(exemplar.render(
318
+ "with_value_literal_fn",
319
+ &[
320
+ ("tmpl_literal_fn", fn_name.clone()),
321
+ ("tmpl_body_placeholder()", json_literal_expr(&parsed)),
322
+ ],
323
+ ));
251
324
  format!("crate::kernel::WithValue::Literal({fn_name})")
252
325
  }
253
326
 
254
327
  fn emit_handler(exemplar: &Exemplar, handler: &Json, literal_fns: &mut Vec<String>) -> String {
255
- let dispatches = handler.get("dispatches").map(Json::each).unwrap_or(&[]);
256
- let dispatch_strs: Vec<String> = dispatches.iter().map(|d| emit_dispatch_spec(exemplar, d, literal_fns)).collect();
328
+ let dispatches = handler.get("dispatches").map_or_default(Json::each);
329
+ let dispatch_strs: Vec<String> = dispatches
330
+ .iter()
331
+ .map(|d| emit_dispatch_spec(exemplar, d, literal_fns))
332
+ .collect();
257
333
  format!(
258
334
  "crate::kernel::Handler {{ event_type: {}, from_state: {}, to_state: {}, dispatches: &[{}] }}",
259
335
  naming::ruby_inspect_string(&handler.get("event_type").map(Json::to_s).unwrap_or_default()),
@@ -268,9 +344,9 @@ pub fn emit_process_manager_table(exemplar: &Exemplar, process_managers: &[Json]
268
344
  let pm_exprs: Vec<String> = process_managers
269
345
  .iter()
270
346
  .map(|pm| {
271
- let handlers = pm.get("handlers").map(Json::each).unwrap_or(&[]);
347
+ let handlers = pm.get("handlers").map_or_default(Json::each);
272
348
  let handler_strs: Vec<String> = handlers.iter().map(|h| emit_handler(exemplar, h, &mut literal_fns)).collect();
273
- let states = pm.get("states").map(Json::each).unwrap_or(&[]);
349
+ let states = pm.get("states").map_or_default(Json::each);
274
350
  let initial_state = states.first().map(Json::to_s).unwrap_or_default();
275
351
  format!(
276
352
  "crate::kernel::ProcessManagerDef {{ name: {}, correlates_by: {}, starts_on: {}, ends_on: {}, initial_state: {}, handlers: &[{}] }}",
@@ -303,12 +379,19 @@ pub fn emit_reference_key_table(exemplar: &Exemplar, chapters: &[(String, Vec<St
303
379
  aggregate_names.iter().map(move |name| {
304
380
  let qualified = format!("{domain_name}::{name}");
305
381
  let key = crate::hecks_naming::snake(name);
306
- format!(" {} => Some({}),", naming::ruby_inspect_string(&qualified), naming::ruby_inspect_string(&key))
382
+ format!(
383
+ " {} => Some({}),",
384
+ naming::ruby_inspect_string(&qualified),
385
+ naming::ruby_inspect_string(&key)
386
+ )
307
387
  })
308
388
  })
309
389
  .collect();
310
390
 
311
- exemplar.render("reference_key_table", &[("\"tmpl_qualified\" => Some(\"tmpl_key\"),", arms.join("\n"))])
391
+ exemplar.render(
392
+ "reference_key_table",
393
+ &[("\"tmpl_qualified\" => Some(\"tmpl_key\"),", arms.join("\n"))],
394
+ )
312
395
  }
313
396
 
314
397
  /// Whether each verb creates the record it addresses; `split_routed_args` in
@@ -318,14 +401,24 @@ pub fn emit_creates_table(exemplar: &Exemplar, aggregates: &[AggregateEntry]) ->
318
401
  let mut arms: Vec<String> = Vec::new();
319
402
  for a in aggregates {
320
403
  for c in &a.commands {
321
- arms.push(format!(" {} => {},", naming::ruby_inspect_string(&c.verb), c.creates));
404
+ arms.push(format!(
405
+ " {} => {},",
406
+ naming::ruby_inspect_string(&c.verb),
407
+ c.creates
408
+ ));
322
409
  }
323
410
  for c in &a.entity_commands {
324
- arms.push(format!(" {} => false,", naming::ruby_inspect_string(&c.verb)));
411
+ arms.push(format!(
412
+ " {} => false,",
413
+ naming::ruby_inspect_string(&c.verb)
414
+ ));
325
415
  }
326
416
  }
327
417
 
328
- exemplar.render("creates_table", &[("\"tmpl_verb\" => true,", arms.join("\n"))])
418
+ exemplar.render(
419
+ "creates_table",
420
+ &[("\"tmpl_verb\" => true,", arms.join("\n"))],
421
+ )
329
422
  }
330
423
 
331
424
  /// The single-component identity head that `orchestrate.rs` routes by first.
@@ -337,18 +430,34 @@ pub fn emit_identity_head_table(exemplar: &Exemplar, aggregates: &[AggregateEntr
337
430
  if a.identified_by.len() != 1 {
338
431
  return None;
339
432
  }
340
- let head = a.identified_by[0].split('.').next().unwrap_or(&a.identified_by[0]);
433
+ let head = a.identified_by[0]
434
+ .split('.')
435
+ .next()
436
+ .unwrap_or(&a.identified_by[0]);
341
437
  let qualified = format!("{}::{}", a.domain_name, a.name);
342
- Some(format!(" {} => Some({}),", naming::ruby_inspect_string(&qualified), naming::ruby_inspect_string(head)))
438
+ Some(format!(
439
+ " {} => Some({}),",
440
+ naming::ruby_inspect_string(&qualified),
441
+ naming::ruby_inspect_string(head)
442
+ ))
343
443
  })
344
444
  .collect();
345
445
 
346
- exemplar.render("identity_head_table", &[("\"tmpl_qualified\" => Some(\"tmpl_head\"),", arms.join("\n"))])
446
+ exemplar.render(
447
+ "identity_head_table",
448
+ &[(
449
+ "\"tmpl_qualified\" => Some(\"tmpl_head\"),",
450
+ arms.join("\n"),
451
+ )],
452
+ )
347
453
  }
348
454
 
349
455
  /// Like `emit_identity_head_table`, for an entity's own identity, keyed by
350
456
  /// "Domain::Aggregate.Entity". Two-level-deep entity commands deliberately never resolve here.
351
- pub fn emit_entity_identity_head_table(exemplar: &Exemplar, aggregates: &[AggregateEntry]) -> String {
457
+ pub fn emit_entity_identity_head_table(
458
+ exemplar: &Exemplar,
459
+ aggregates: &[AggregateEntry],
460
+ ) -> String {
352
461
  let arms: Vec<String> = aggregates
353
462
  .iter()
354
463
  .flat_map(|a| {
@@ -356,30 +465,64 @@ pub fn emit_entity_identity_head_table(exemplar: &Exemplar, aggregates: &[Aggreg
356
465
  if e.identified_by.len() != 1 {
357
466
  return None;
358
467
  }
359
- let head = e.identified_by[0].split('.').next().unwrap_or(&e.identified_by[0]);
468
+ let head = e.identified_by[0]
469
+ .split('.')
470
+ .next()
471
+ .unwrap_or(&e.identified_by[0]);
360
472
  let qualified = format!("{}::{}.{}", a.domain_name, a.name, e.name);
361
- Some(format!(" {} => Some({}),", naming::ruby_inspect_string(&qualified), naming::ruby_inspect_string(head)))
473
+ Some(format!(
474
+ " {} => Some({}),",
475
+ naming::ruby_inspect_string(&qualified),
476
+ naming::ruby_inspect_string(head)
477
+ ))
362
478
  })
363
479
  })
364
480
  .collect();
365
481
 
366
- exemplar.render("entity_identity_head_table", &[("\"tmpl_qualified\" => Some(\"tmpl_head\"),", arms.join("\n"))])
482
+ exemplar.render(
483
+ "entity_identity_head_table",
484
+ &[(
485
+ "\"tmpl_qualified\" => Some(\"tmpl_head\"),",
486
+ arms.join("\n"),
487
+ )],
488
+ )
367
489
  }
368
490
 
369
491
  pub fn emit_command_attributes_table(exemplar: &Exemplar, aggregates: &[AggregateEntry]) -> String {
370
492
  let mut arms: Vec<String> = Vec::new();
371
493
  for a in aggregates {
372
494
  for c in &a.commands {
373
- let names = c.attributes.iter().map(|n| naming::ruby_inspect_string(n)).collect::<Vec<_>>().join(", ");
374
- arms.push(format!(" {} => &[{}],", naming::ruby_inspect_string(&c.verb), names));
495
+ let names = c
496
+ .attributes
497
+ .iter()
498
+ .map(|n| naming::ruby_inspect_string(n))
499
+ .collect::<Vec<_>>()
500
+ .join(", ");
501
+ arms.push(format!(
502
+ " {} => &[{}],",
503
+ naming::ruby_inspect_string(&c.verb),
504
+ names
505
+ ));
375
506
  }
376
507
  for c in &a.entity_commands {
377
- let names = c.attributes.iter().map(|n| naming::ruby_inspect_string(n)).collect::<Vec<_>>().join(", ");
378
- arms.push(format!(" {} => &[{}],", naming::ruby_inspect_string(&c.verb), names));
508
+ let names = c
509
+ .attributes
510
+ .iter()
511
+ .map(|n| naming::ruby_inspect_string(n))
512
+ .collect::<Vec<_>>()
513
+ .join(", ");
514
+ arms.push(format!(
515
+ " {} => &[{}],",
516
+ naming::ruby_inspect_string(&c.verb),
517
+ names
518
+ ));
379
519
  }
380
520
  }
381
521
 
382
- exemplar.render("command_attributes_table", &[("\"tmpl_verb\" => &[\"tmpl_attr\"],", arms.join("\n"))])
522
+ exemplar.render(
523
+ "command_attributes_table",
524
+ &[("\"tmpl_verb\" => &[\"tmpl_attr\"],", arms.join("\n"))],
525
+ )
383
526
  }
384
527
 
385
528
  // Qualifies the bare "Aggregate.query" `for_each` spelling against the policy's own domain
@@ -389,7 +532,11 @@ fn fan_out_verb_expr(domain_name: &str, policy: &Json) -> String {
389
532
  if for_each.is_empty() {
390
533
  return "None".to_string();
391
534
  }
392
- let qualified = if for_each.contains("::") { for_each } else { format!("{domain_name}::{for_each}") };
535
+ let qualified = if for_each.contains("::") {
536
+ for_each
537
+ } else {
538
+ format!("{domain_name}::{for_each}")
539
+ };
393
540
  format!("Some({})", naming::ruby_inspect_string(&qualified))
394
541
  }
395
542
 
@@ -403,7 +550,10 @@ fn fan_out_key_expr(policy: &Json, aggregates: &[Json]) -> String {
403
550
  let path = for_each.split('.').next().unwrap_or_default().to_string();
404
551
  let row_aggregate = path.rsplit("::").next().unwrap_or(&path).to_string();
405
552
 
406
- let trigger = policy.get("trigger_command").map(Json::to_s).unwrap_or_default();
553
+ let trigger = policy
554
+ .get("trigger_command")
555
+ .map(Json::to_s)
556
+ .unwrap_or_default();
407
557
  let mut parts = trigger.splitn(2, '.');
408
558
  let target_aggregate = parts.next().unwrap_or_default().to_string();
409
559
  let target_command = parts.next().unwrap_or_default().to_string();
@@ -421,14 +571,19 @@ fn fan_out_key_expr(policy: &Json, aggregates: &[Json]) -> String {
421
571
  })
422
572
  .and_then(|command| addressing_key_for(&command, &row_aggregate));
423
573
 
424
- match key {
425
- Some(name) => format!("Some({})", naming::ruby_inspect_string(&name)),
426
- None => "None".to_string(),
427
- }
574
+ key.map_or_else(
575
+ || "None".to_string(),
576
+ |name| format!("Some({})", naming::ruby_inspect_string(&name)),
577
+ )
428
578
  }
429
579
 
430
580
  fn addressing_key_for(command: &Json, aggregate_name: &str) -> Option<String> {
431
- if command.get("references").map(Json::to_s).unwrap_or_default() == aggregate_name {
581
+ if command
582
+ .get("references")
583
+ .map(Json::to_s)
584
+ .unwrap_or_default()
585
+ == aggregate_name
586
+ {
432
587
  return Some(crate::hecks_naming::snake(aggregate_name));
433
588
  }
434
589
 
@@ -471,11 +626,14 @@ mod tests {
471
626
 
472
627
  // Mirrors the fixtures in `spec/rust_project/reactions_merge_spec.rb`; no real corpus
473
628
  // domain exercises this merged-chapter shape.
474
- fn policy(name: &str, on_event: &str, trigger_command: &str, target_domain: Option<&str>) -> Json {
475
- let target_domain_json = match target_domain {
476
- Some(domain) => format!("\"{domain}\""),
477
- None => "null".to_string(),
478
- };
629
+ fn policy(
630
+ name: &str,
631
+ on_event: &str,
632
+ trigger_command: &str,
633
+ target_domain: Option<&str>,
634
+ ) -> Json {
635
+ let target_domain_json =
636
+ target_domain.map_or_else(|| "null".to_string(), |domain| format!("\"{domain}\""));
479
637
  Json::parse(&format!(
480
638
  "{{\"name\":\"{name}\",\"on_event\":\"{on_event}\",\"trigger_command\":\"{trigger_command}\",\
481
639
  \"target_domain\":{target_domain_json},\"for_each\":null,\"with_spec\":[]}}"
@@ -487,12 +645,30 @@ mod tests {
487
645
  fn merged_policy_table_carries_the_target_and_a_vendored_chapters_own_same_domain_policy() {
488
646
  let orders_policies = vec![policy("OnOrderPlaced", "OrderPlaced", "Invoice.Open", None)];
489
647
  let payments_policies = vec![
490
- policy("OnPaymentConfirmedByProcessor", "PaymentConfirmedByProcessor", "Payment.Succeed", None),
491
- policy("OnPaymentFailed", "PaymentFailed", "Ledger.Reverse", Some("Ledger")),
648
+ policy(
649
+ "OnPaymentConfirmedByProcessor",
650
+ "PaymentConfirmedByProcessor",
651
+ "Payment.Succeed",
652
+ None,
653
+ ),
654
+ policy(
655
+ "OnPaymentFailed",
656
+ "PaymentFailed",
657
+ "Ledger.Reverse",
658
+ Some("Ledger"),
659
+ ),
492
660
  ];
493
661
  let sources = vec![
494
- PolicySource { domain_name: "Orders", policies: &orders_policies, aggregates: &[] },
495
- PolicySource { domain_name: "Payments", policies: &payments_policies, aggregates: &[] },
662
+ PolicySource {
663
+ domain_name: "Orders",
664
+ policies: &orders_policies,
665
+ aggregates: &[],
666
+ },
667
+ PolicySource {
668
+ domain_name: "Payments",
669
+ policies: &payments_policies,
670
+ aggregates: &[],
671
+ },
496
672
  ];
497
673
 
498
674
  let table = emit_merged_policy_table(&Exemplar::load(), &sources);
@@ -510,12 +686,30 @@ mod tests {
510
686
  fn merged_cross_domain_policy_table_carries_only_a_vendored_chapters_own_cross_domain_policy() {
511
687
  let orders_policies = vec![policy("OnOrderPlaced", "OrderPlaced", "Invoice.Open", None)];
512
688
  let payments_policies = vec![
513
- policy("OnPaymentConfirmedByProcessor", "PaymentConfirmedByProcessor", "Payment.Succeed", None),
514
- policy("OnPaymentFailed", "PaymentFailed", "Ledger.Reverse", Some("Ledger")),
689
+ policy(
690
+ "OnPaymentConfirmedByProcessor",
691
+ "PaymentConfirmedByProcessor",
692
+ "Payment.Succeed",
693
+ None,
694
+ ),
695
+ policy(
696
+ "OnPaymentFailed",
697
+ "PaymentFailed",
698
+ "Ledger.Reverse",
699
+ Some("Ledger"),
700
+ ),
515
701
  ];
516
702
  let sources = vec![
517
- PolicySource { domain_name: "Orders", policies: &orders_policies, aggregates: &[] },
518
- PolicySource { domain_name: "Payments", policies: &payments_policies, aggregates: &[] },
703
+ PolicySource {
704
+ domain_name: "Orders",
705
+ policies: &orders_policies,
706
+ aggregates: &[],
707
+ },
708
+ PolicySource {
709
+ domain_name: "Payments",
710
+ policies: &payments_policies,
711
+ aggregates: &[],
712
+ },
519
713
  ];
520
714
 
521
715
  let table = emit_merged_cross_domain_policy_table(&Exemplar::load(), &sources);