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
@@ -22,7 +22,11 @@ const OVERVIEW: &str = "overview";
22
22
  /// The whole config, as `PresentationConfig.load` returns it. A domain
23
23
  /// with no ConsoleSettings rows, or none of its relations, reads back
24
24
  /// an empty section rather than an error.
25
- pub async fn load(client: &Mutex<Client>, wasm_path: &Path, config: &LineageConfig) -> anyhow::Result<Value> {
25
+ pub async fn load(
26
+ client: &Mutex<Client>,
27
+ wasm_path: &Path,
28
+ config: &LineageConfig,
29
+ ) -> anyhow::Result<Value> {
26
30
  if let Some(rows) = kernel_rows(client, wasm_path).await? {
27
31
  return Ok(reshape(&rows.states, &rows.collections, &rows.overview));
28
32
  }
@@ -31,7 +35,10 @@ pub async fn load(client: &Mutex<Client>, wasm_path: &Path, config: &LineageConf
31
35
 
32
36
  /// The Ruby-written relations, split out so the kernel path above reads
33
37
  /// as the one decision it is, and so tests can drive this half directly.
34
- async fn load_from_relations(client: &Mutex<Client>, config: &LineageConfig) -> anyhow::Result<Value> {
38
+ async fn load_from_relations(
39
+ client: &Mutex<Client>,
40
+ config: &LineageConfig,
41
+ ) -> anyhow::Result<Value> {
35
42
  let guard = client.lock().await;
36
43
  let states = read_head_states(&*guard, &config.domain, STATE_STYLE).await?;
37
44
  let collections = read_head_states(&*guard, &config.domain, COLLECTION).await?;
@@ -49,7 +56,10 @@ async fn load_from_relations(client: &Mutex<Client>, config: &LineageConfig) ->
49
56
  /// Keeping the plain read a single SELECT (via `dispatch::read`'s
50
57
  /// snapshot) matters here: this runs on every first paint, alongside
51
58
  /// `/api/me` and `/api/ui-schema`.
52
- pub(crate) async fn kernel_rows(client: &Mutex<Client>, wasm_path: &Path) -> anyhow::Result<Option<KernelRows>> {
59
+ pub(crate) async fn kernel_rows(
60
+ client: &Mutex<Client>,
61
+ wasm_path: &Path,
62
+ ) -> anyhow::Result<Option<KernelRows>> {
53
63
  let state = dispatch::read(client, wasm_path).await?;
54
64
  let rows = bucket(state.get("instances"));
55
65
  if !rows.is_empty() {
@@ -57,8 +67,17 @@ pub(crate) async fn kernel_rows(client: &Mutex<Client>, wasm_path: &Path) -> any
57
67
  }
58
68
 
59
69
  // Empty either way — ask the kernel which kind of empty this is.
60
- let probe = dispatch::query(client, wasm_path, &format!("{CONFIG_DOMAIN}.Styles"), json!({})).await?;
61
- let answered = probe.get("queries").and_then(|q| q.as_array()).is_some_and(|q| !q.is_empty());
70
+ let probe = dispatch::query(
71
+ client,
72
+ wasm_path,
73
+ &format!("{CONFIG_DOMAIN}.Styles"),
74
+ json!({}),
75
+ )
76
+ .await?;
77
+ let answered = probe
78
+ .get("queries")
79
+ .and_then(|q| q.as_array())
80
+ .is_some_and(|q| !q.is_empty());
62
81
  if answered {
63
82
  Ok(Some(rows))
64
83
  } else {
@@ -69,12 +88,16 @@ pub(crate) async fn kernel_rows(client: &Mutex<Client>, wasm_path: &Path) -> any
69
88
  /// Every ConsoleSettings record in an instance map, split by aggregate.
70
89
  fn bucket(instances: Option<&Value>) -> KernelRows {
71
90
  let mut rows = KernelRows::default();
72
- let Some(instances) = instances.and_then(|i| i.as_object()) else { return rows };
91
+ let Some(instances) = instances.and_then(|i| i.as_object()) else {
92
+ return rows;
93
+ };
73
94
  for (key, state) in instances {
74
95
  // `Domain::Aggregate#id`, split on the first `#` — a StateStyle
75
96
  // id like `Agg:state` has none itself but would be lost to a
76
97
  // split on the last one. Ids feed `presentation_write`'s declare-vs-set check.
77
- let Some((qualified, id)) = key.split_once('#') else { continue };
98
+ let Some((qualified, id)) = key.split_once('#') else {
99
+ continue;
100
+ };
78
101
  match qualified {
79
102
  _ if qualified == format!("{CONFIG_DOMAIN}::StateStyle") => {
80
103
  rows.state_ids.insert(id.to_string());
@@ -84,7 +107,9 @@ fn bucket(instances: Option<&Value>) -> KernelRows {
84
107
  rows.collection_ids.insert(id.to_string());
85
108
  rows.collections.push(state.clone());
86
109
  }
87
- _ if qualified == format!("{CONFIG_DOMAIN}::Overview") => rows.overview.push(state.clone()),
110
+ _ if qualified == format!("{CONFIG_DOMAIN}::Overview") => {
111
+ rows.overview.push(state.clone());
112
+ }
88
113
  _ => {}
89
114
  }
90
115
  }
@@ -106,7 +131,7 @@ pub(crate) struct KernelRows {
106
131
  impl KernelRows {
107
132
  /// No row of any of the three kinds — the one answer that cannot
108
133
  /// tell a kernel without this chapter from one nobody has saved to.
109
- fn is_empty(&self) -> bool {
134
+ const fn is_empty(&self) -> bool {
110
135
  self.states.is_empty() && self.collections.is_empty() && self.overview.is_empty()
111
136
  }
112
137
 
@@ -146,7 +171,10 @@ async fn read_head_states<C: GenericClient>(
146
171
  continue;
147
172
  }
148
173
  let rows = client
149
- .query(&format!("SELECT state FROM {}", journal::quote_ident(&relation)), &[])
174
+ .query(
175
+ &format!("SELECT state FROM {}", journal::quote_ident(&relation)),
176
+ &[],
177
+ )
150
178
  .await?;
151
179
  return Ok(rows.iter().map(|row| row.get(0)).collect());
152
180
  }
@@ -170,21 +198,25 @@ fn head_view_candidates(host_domain: &str, storage_name: &str) -> Vec<String> {
170
198
  /// same as the unqualified SELECT below — never a same-named relation
171
199
  /// in some other schema.
172
200
  async fn relation_exists<C: GenericClient>(client: &C, relation: &str) -> anyhow::Result<bool> {
173
- let row = client.query_one("SELECT to_regclass($1) IS NOT NULL", &[&relation]).await?;
201
+ let row = client
202
+ .query_one("SELECT to_regclass($1) IS NOT NULL", &[&relation])
203
+ .await?;
174
204
  Ok(row.get(0))
175
205
  }
176
206
 
177
207
  /// `PresentationConfig.unwrap` — every scalar here is a single-attribute
178
208
  /// value object (`{"value": ...}`); anything else rides through untouched.
179
209
  fn unwrap(value: &Value) -> Value {
180
- match value.get("value") {
181
- Some(inner) => inner.clone(),
182
- None => value.clone(),
183
- }
210
+ value
211
+ .get("value")
212
+ .map_or_else(|| value.clone(), Clone::clone)
184
213
  }
185
214
 
186
215
  fn unwrap_str(value: Option<&Value>) -> String {
187
- value.map(unwrap).and_then(|v| v.as_str().map(String::from)).unwrap_or_default()
216
+ value
217
+ .map(unwrap)
218
+ .and_then(|v| v.as_str().map(String::from))
219
+ .unwrap_or_default()
188
220
  }
189
221
 
190
222
  /// Ruby truthiness for a stored field: absent and JSON `null` are both
@@ -198,9 +230,15 @@ fn present(value: Option<&Value>) -> Option<&Value> {
198
230
  /// merged in without overwriting an explicitly-modeled key. Accepts it
199
231
  /// either wrapped (a head field) or bare (a `list_of` element).
200
232
  fn merge_extra(entry: &mut Map<String, Value>, extra_json: Option<&Value>) {
201
- let Some(raw) = present(extra_json).map(unwrap) else { return };
202
- let Some(text) = raw.as_str().filter(|t| !t.is_empty()) else { return };
203
- let Ok(Value::Object(extra)) = serde_json::from_str::<Value>(text) else { return };
233
+ let Some(raw) = present(extra_json).map(unwrap) else {
234
+ return;
235
+ };
236
+ let Some(text) = raw.as_str().filter(|t| !t.is_empty()) else {
237
+ return;
238
+ };
239
+ let Ok(Value::Object(extra)) = serde_json::from_str::<Value>(text) else {
240
+ return;
241
+ };
204
242
  for (key, value) in extra {
205
243
  entry.entry(key).or_insert(value);
206
244
  }
@@ -217,7 +255,10 @@ fn reshape_states(rows: &[Value]) -> Value {
217
255
  entry.insert("tone".to_string(), unwrap(tone));
218
256
  }
219
257
  if let Some(attention) = present(row.get("attention")) {
220
- entry.insert("attention".to_string(), json!(unwrap(attention).as_str() == Some("true")));
258
+ entry.insert(
259
+ "attention".to_string(),
260
+ json!(unwrap(attention).as_str() == Some("true")),
261
+ );
221
262
  }
222
263
  merge_extra(&mut entry, row.get("extra_json"));
223
264
 
@@ -258,11 +299,24 @@ fn reshape_collection_row(row: &Value) -> Value {
258
299
  entry.insert("identity".to_string(), identity);
259
300
  }
260
301
 
261
- entry.insert("columns".to_string(), Value::Array(elements(row, "columns").iter().map(reshape_column).collect()));
302
+ entry.insert(
303
+ "columns".to_string(),
304
+ Value::Array(
305
+ elements(row, "columns")
306
+ .iter()
307
+ .map(reshape_column)
308
+ .collect(),
309
+ ),
310
+ );
262
311
  entry.insert("detail_fields".to_string(), reshape_detail_fields(row));
263
312
  entry.insert(
264
313
  "preconditions".to_string(),
265
- Value::Array(elements(row, "preconditions").iter().map(reshape_precondition).collect()),
314
+ Value::Array(
315
+ elements(row, "preconditions")
316
+ .iter()
317
+ .map(reshape_precondition)
318
+ .collect(),
319
+ ),
266
320
  );
267
321
  entry.insert("field_formats".to_string(), reshape_field_formats(row));
268
322
  merge_extra(&mut entry, row.get("extra_json"));
@@ -271,14 +325,19 @@ fn reshape_collection_row(row: &Value) -> Value {
271
325
 
272
326
  /// `Array(row[:columns])` — a null/absent list is the empty one.
273
327
  fn elements<'a>(row: &'a Value, field: &str) -> &'a [Value] {
274
- row.get(field).and_then(|v| v.as_array()).map(|a| a.as_slice()).unwrap_or(&[])
328
+ row.get(field)
329
+ .and_then(|v| v.as_array())
330
+ .map_or(&[][..], std::vec::Vec::as_slice)
275
331
  }
276
332
 
277
333
  fn reshape_identity(row: &Value) -> Option<Value> {
278
334
  let field = present(row.get("identity_field")).map(unwrap)?;
279
335
  let mut identity = Map::new();
280
336
  identity.insert("field".to_string(), field);
281
- identity.insert("strategy".to_string(), row.get("identity_strategy").map(unwrap).unwrap_or(Value::Null));
337
+ identity.insert(
338
+ "strategy".to_string(),
339
+ row.get("identity_strategy").map_or(Value::Null, unwrap),
340
+ );
282
341
  if let Some(source) = present(row.get("identity_source")) {
283
342
  identity.insert("source".to_string(), unwrap(source));
284
343
  }
@@ -293,9 +352,15 @@ fn reshape_identity(row: &Value) -> Option<Value> {
293
352
  /// wire and comes back as a real boolean, as Ruby's own read does.
294
353
  fn reshape_column(column: &Value) -> Value {
295
354
  let mut entry = Map::new();
296
- entry.insert("field".to_string(), column.get("field").cloned().unwrap_or(Value::Null));
355
+ entry.insert(
356
+ "field".to_string(),
357
+ column.get("field").cloned().unwrap_or(Value::Null),
358
+ );
297
359
  if let Some(sortable) = present(column.get("sortable")) {
298
- entry.insert("sortable".to_string(), json!(unwrap(sortable).as_str() == Some("true")));
360
+ entry.insert(
361
+ "sortable".to_string(),
362
+ json!(unwrap(sortable).as_str() == Some("true")),
363
+ );
299
364
  }
300
365
  if let Some(sort_default) = present(column.get("sort_default")) {
301
366
  entry.insert("sort_default".to_string(), unwrap(sort_default));
@@ -339,8 +404,14 @@ fn reshape_detail_fields(row: &Value) -> Value {
339
404
  /// meaning "the target must merely exist", matching Ruby's own hash.
340
405
  fn reshape_precondition(precondition: &Value) -> Value {
341
406
  let mut entry = Map::new();
342
- entry.insert("field".to_string(), precondition.get("field").cloned().unwrap_or(Value::Null));
343
- entry.insert("state".to_string(), precondition.get("state").cloned().unwrap_or(Value::Null));
407
+ entry.insert(
408
+ "field".to_string(),
409
+ precondition.get("field").cloned().unwrap_or(Value::Null),
410
+ );
411
+ entry.insert(
412
+ "state".to_string(),
413
+ precondition.get("state").cloned().unwrap_or(Value::Null),
414
+ );
344
415
  merge_extra(&mut entry, precondition.get("extra_json"));
345
416
  Value::Object(entry)
346
417
  }
@@ -348,7 +419,11 @@ fn reshape_precondition(precondition: &Value) -> Value {
348
419
  fn reshape_field_formats(row: &Value) -> Value {
349
420
  let mut formats = Map::new();
350
421
  for entry in elements(row, "field_formats") {
351
- let field = entry.get("field").and_then(|v| v.as_str()).unwrap_or_default().to_string();
422
+ let field = entry
423
+ .get("field")
424
+ .and_then(|v| v.as_str())
425
+ .unwrap_or_default()
426
+ .to_string();
352
427
  formats.insert(field, entry.get("format").cloned().unwrap_or(Value::Null));
353
428
  }
354
429
  Value::Object(formats)
@@ -357,7 +432,9 @@ fn reshape_field_formats(row: &Value) -> Value {
357
432
  /// `reshape_overview` — a singleton: no row reads as `{}`, never
358
433
  /// `{"stats": []}`, matching Ruby's "never configured" vs "empty".
359
434
  fn reshape_overview(rows: &[Value]) -> Value {
360
- let Some(row) = rows.first() else { return Value::Object(Map::new()) };
435
+ let Some(row) = rows.first() else {
436
+ return Value::Object(Map::new());
437
+ };
361
438
  let stats: Vec<Value> = elements(row, "stats").iter().map(reshape_stat).collect();
362
439
  json!({ "stats": stats })
363
440
  }
@@ -366,15 +443,24 @@ fn reshape_overview(rows: &[Value]) -> Value {
366
443
  /// (`where_json`) and is parsed back out here, exactly as Ruby does.
367
444
  fn reshape_stat(stat: &Value) -> Value {
368
445
  let mut entry = Map::new();
369
- entry.insert("label".to_string(), stat.get("label").cloned().unwrap_or(Value::Null));
370
- entry.insert("collection".to_string(), stat.get("collection").cloned().unwrap_or(Value::Null));
446
+ entry.insert(
447
+ "label".to_string(),
448
+ stat.get("label").cloned().unwrap_or(Value::Null),
449
+ );
450
+ entry.insert(
451
+ "collection".to_string(),
452
+ stat.get("collection").cloned().unwrap_or(Value::Null),
453
+ );
371
454
  if let Some(as_fold) = present(stat.get("as")) {
372
455
  entry.insert("as".to_string(), as_fold.clone());
373
456
  }
374
457
  if let Some(field) = present(stat.get("field")) {
375
458
  entry.insert("field".to_string(), field.clone());
376
459
  }
377
- if let Some(where_json) = present(stat.get("where_json")).and_then(|v| v.as_str()).filter(|t| !t.is_empty()) {
460
+ if let Some(where_json) = present(stat.get("where_json"))
461
+ .and_then(|v| v.as_str())
462
+ .filter(|t| !t.is_empty())
463
+ {
378
464
  if let Ok(parsed) = serde_json::from_str::<Value>(where_json) {
379
465
  entry.insert("where".to_string(), parsed);
380
466
  }
@@ -436,9 +522,14 @@ mod tests {
436
522
 
437
523
  #[test]
438
524
  fn attention_stored_as_the_string_false_reads_back_as_the_boolean_false() {
439
- let rows = vec![json!({"agg": {"value": "Client"}, "state": {"value": "active"}, "attention": {"value": "false"}})];
525
+ let rows = vec![
526
+ json!({"agg": {"value": "Client"}, "state": {"value": "active"}, "attention": {"value": "false"}}),
527
+ ];
440
528
 
441
- assert_eq!(reshape_states(&rows), json!({"Client": {"active": {"attention": false}}}));
529
+ assert_eq!(
530
+ reshape_states(&rows),
531
+ json!({"Client": {"active": {"attention": false}}})
532
+ );
442
533
  }
443
534
 
444
535
  #[test]
@@ -496,7 +587,10 @@ mod tests {
496
587
  // `pad` is not modelled as its own attribute — it rides through
497
588
  // the identity rule's own opaque blob and has to land inside
498
589
  // the identity entry, not beside it.
499
- assert_eq!(config["Contract"]["identity"], json!({"field": "number", "strategy": "sequence", "prefix": "C-", "pad": 3}));
590
+ assert_eq!(
591
+ config["Contract"]["identity"],
592
+ json!({"field": "number", "strategy": "sequence", "prefix": "C-", "pad": 3})
593
+ );
500
594
  }
501
595
 
502
596
  #[test]
@@ -506,7 +600,10 @@ mod tests {
506
600
  "field_formats": [], "preconditions": [{"field": "invoice_id"}]
507
601
  })];
508
602
 
509
- assert_eq!(reshape_collections(&rows)["Payment"]["preconditions"], json!([{"field": "invoice_id", "state": null}]));
603
+ assert_eq!(
604
+ reshape_collections(&rows)["Payment"]["preconditions"],
605
+ json!([{"field": "invoice_id", "state": null}])
606
+ );
510
607
  }
511
608
 
512
609
  #[test]
@@ -541,7 +638,10 @@ mod tests {
541
638
  })];
542
639
 
543
640
  let entry = &reshape_collections(&rows)["Invoice"];
544
- assert_eq!(entry["columns"], json!([{"field": "number", "sortable": false, "sort_default": "desc", "head": "No."}]));
641
+ assert_eq!(
642
+ entry["columns"],
643
+ json!([{"field": "number", "sortable": false, "sort_default": "desc", "head": "No."}])
644
+ );
545
645
  assert_eq!(entry["field_formats"], json!({"issued_on": "date"}));
546
646
  }
547
647
 
@@ -569,16 +669,24 @@ mod tests {
569
669
  #[test]
570
670
  fn a_domain_that_has_never_saved_an_overview_reads_back_an_empty_section() {
571
671
  assert_eq!(reshape_overview(&[]), json!({}));
572
- assert_eq!(reshape(&[], &[], &[]), json!({"states": {}, "collections": {}, "overview": {}}));
672
+ assert_eq!(
673
+ reshape(&[], &[], &[]),
674
+ json!({"states": {}, "collections": {}, "overview": {}})
675
+ );
573
676
  }
574
677
 
575
678
  #[test]
576
679
  fn an_explicitly_modelled_field_always_beats_the_same_key_in_the_extras_blob() {
577
- let rows = vec![json!({"agg": {"value": "Client"}, "tone": {"value": "good"},
680
+ let rows = vec![
681
+ json!({"agg": {"value": "Client"}, "tone": {"value": "good"},
578
682
  "extra_json": {"value": "{\"tone\":\"danger\",\"note\":\"n\"}"},
579
- "state": {"value": "active"}})];
683
+ "state": {"value": "active"}}),
684
+ ];
580
685
 
581
- assert_eq!(reshape_states(&rows), json!({"Client": {"active": {"tone": "good", "note": "n"}}}));
686
+ assert_eq!(
687
+ reshape_states(&rows),
688
+ json!({"Client": {"active": {"tone": "good", "note": "n"}}})
689
+ );
582
690
  }
583
691
 
584
692
  // What follows needs a real database: which relation this module
@@ -587,18 +695,26 @@ mod tests {
587
695
  // `cargo test`'s parallelism doesn't race two against one table.
588
696
 
589
697
  async fn scratch_db(name: &str) -> Mutex<Client> {
590
- let (admin, conn) = tokio_postgres::connect(&crate::test_pg::conninfo("postgres"), tokio_postgres::NoTls)
591
- .await
592
- .expect("connect to postgres");
698
+ let (admin, conn) =
699
+ tokio_postgres::connect(&crate::test_pg::conninfo("postgres"), tokio_postgres::NoTls)
700
+ .await
701
+ .expect("connect to postgres");
593
702
  tokio::spawn(async move {
594
703
  let _ = conn.await;
595
704
  });
596
- admin.batch_execute(&format!("DROP DATABASE IF EXISTS {name} WITH (FORCE)")).await.unwrap();
597
- admin.batch_execute(&format!("CREATE DATABASE {name}")).await.unwrap();
598
-
599
- let (client, conn) = tokio_postgres::connect(&crate::test_pg::conninfo(&name), tokio_postgres::NoTls)
705
+ admin
706
+ .batch_execute(&format!("DROP DATABASE IF EXISTS {name} WITH (FORCE)"))
707
+ .await
708
+ .unwrap();
709
+ admin
710
+ .batch_execute(&format!("CREATE DATABASE {name}"))
600
711
  .await
601
- .expect("connect to scratch db");
712
+ .unwrap();
713
+
714
+ let (client, conn) =
715
+ tokio_postgres::connect(&crate::test_pg::conninfo(name), tokio_postgres::NoTls)
716
+ .await
717
+ .expect("connect to scratch db");
602
718
  tokio::spawn(async move {
603
719
  let _ = conn.await;
604
720
  });
@@ -610,17 +726,26 @@ mod tests {
610
726
  /// A domain matching none of these fixtures' relations, so lookup
611
727
  /// falls through to whatever each test seeds directly.
612
728
  fn relations_only() -> LineageConfig {
613
- LineageConfig { domain: "Fixtures".to_string(), era: None, mirrored: None }
729
+ LineageConfig {
730
+ domain: "Fixtures".to_string(),
731
+ era: None,
732
+ mirrored: None,
733
+ }
614
734
  }
615
735
 
616
736
  async fn seed_head(client: &Mutex<Client>, relation: &str, id: &str, state: Value) {
617
737
  let guard = client.lock().await;
618
738
  guard
619
- .batch_execute(&format!("CREATE TABLE IF NOT EXISTS {relation} (id text primary key, state jsonb)"))
739
+ .batch_execute(&format!(
740
+ "CREATE TABLE IF NOT EXISTS {relation} (id text primary key, state jsonb)"
741
+ ))
620
742
  .await
621
743
  .unwrap();
622
744
  guard
623
- .execute(&format!("INSERT INTO {relation} (id, state) VALUES ($1, $2)"), &[&id, &state])
745
+ .execute(
746
+ &format!("INSERT INTO {relation} (id, state) VALUES ($1, $2)"),
747
+ &[&id, &state],
748
+ )
624
749
  .await
625
750
  .unwrap();
626
751
  }
@@ -631,10 +756,15 @@ mod tests {
631
756
 
632
757
  #[tokio::test]
633
758
  async fn a_kernel_that_carries_the_chapter_but_holds_no_rows_answers_some_empty() {
634
- let client = crate::dispatch::tests::scratch_db("hecks_host_presentation_chapter_no_rows").await;
635
- let wasm = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../dist/checkout_fixture.wasm");
759
+ let client =
760
+ crate::dispatch::tests::scratch_db("hecks_host_presentation_chapter_no_rows").await;
761
+ let wasm = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
762
+ .join("../dist/checkout_fixture.wasm");
636
763
 
637
- let rows = kernel_rows(&client, &wasm).await.expect("a read").expect("the chapter is compiled in");
764
+ let rows = kernel_rows(&client, &wasm)
765
+ .await
766
+ .expect("a read")
767
+ .expect("the chapter is compiled in");
638
768
 
639
769
  assert!(rows.states.is_empty());
640
770
  assert!(rows.collections.is_empty());
@@ -654,9 +784,14 @@ mod tests {
654
784
  let client = scratch_db("hecks_host_presentation_legacy_relation").await;
655
785
  seed_head(&client, "state_style_head", "Client:active", json!({"agg": {"value": "Client"}, "state": {"value": "active"}, "tone": {"value": "good"}})).await;
656
786
 
657
- let config = load_from_relations(&client, &relations_only()).await.expect("a config");
787
+ let config = load_from_relations(&client, &relations_only())
788
+ .await
789
+ .expect("a config");
658
790
 
659
- assert_eq!(config["states"], json!({"Client": {"active": {"tone": "good"}}}));
791
+ assert_eq!(
792
+ config["states"],
793
+ json!({"Client": {"active": {"tone": "good"}}})
794
+ );
660
795
  assert_eq!(config["collections"], json!({}));
661
796
  assert_eq!(config["overview"], json!({}));
662
797
  }
@@ -673,7 +808,9 @@ mod tests {
673
808
  )
674
809
  .await;
675
810
 
676
- let config = load_from_relations(&client, &relations_only()).await.expect("a config");
811
+ let config = load_from_relations(&client, &relations_only())
812
+ .await
813
+ .expect("a config");
677
814
 
678
815
  assert_eq!(config["states"]["Client"]["active"]["tone"], "good");
679
816
  }
@@ -683,16 +820,23 @@ mod tests {
683
820
  let client = scratch_db("hecks_host_presentation_no_relations").await;
684
821
 
685
822
  assert_eq!(
686
- load_from_relations(&client, &relations_only()).await.expect("a config"),
823
+ load_from_relations(&client, &relations_only())
824
+ .await
825
+ .expect("a config"),
687
826
  json!({"states": {}, "collections": {}, "overview": {}})
688
827
  );
689
828
  }
690
829
 
691
830
  #[test]
692
831
  fn a_malformed_extras_blob_is_ignored_rather_than_failing_the_whole_read() {
693
- let rows = vec![json!({"agg": {"value": "Client"}, "state": {"value": "active"},
694
- "tone": {"value": "good"}, "extra_json": {"value": "not json at all"}})];
832
+ let rows = vec![
833
+ json!({"agg": {"value": "Client"}, "state": {"value": "active"},
834
+ "tone": {"value": "good"}, "extra_json": {"value": "not json at all"}}),
835
+ ];
695
836
 
696
- assert_eq!(reshape_states(&rows), json!({"Client": {"active": {"tone": "good"}}}));
837
+ assert_eq!(
838
+ reshape_states(&rows),
839
+ json!({"Client": {"active": {"tone": "good"}}})
840
+ );
697
841
  }
698
842
  }