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
@@ -0,0 +1,156 @@
1
+ require "json"
2
+ require "rack"
3
+ require_relative "../composer"
4
+ require_relative "domains"
5
+ require_relative "workbench"
6
+
7
+ module Hecks
8
+ module Composer
9
+ # The Composer's driving side, on the hecksagon side: a Rack app that serves the editor page
10
+ # and the JSON routes it speaks to, over one workspace directory at a time. It binds no
11
+ # business meaning. The routes are the tables below: the page and files, the editor's own
12
+ # (save, remove, parse, check, ask), the domain list, and the lower panels', which {Panels}
13
+ # answers.
14
+ class Web
15
+ PAGE = File.expand_path("page.html", __dir__).freeze
16
+ PREFIX = "/composer".freeze
17
+
18
+ # @param workspace [String] the domain directory the editor opens
19
+ # @param agents [Runtime::Registry, nil] see {Workbench#initialize}
20
+ # @param root [String] the checkout whose domains the picker lists
21
+ # @return [Web] a Rack app
22
+ def self.app(workspace:, agents: nil, root: Parse::ROOT) = new(workspace: workspace, agents: agents, root: root)
23
+
24
+ # @param workspace [String] the domain directory the editor opens
25
+ # @param agents [Runtime::Registry, nil] see {Workbench#initialize}
26
+ # @param root [String] the checkout whose domains the picker lists
27
+ def initialize(workspace:, agents: nil, root: Parse::ROOT)
28
+ @agents = agents
29
+ @root = File.expand_path(root)
30
+ @bench = Workbench.new(workspace: workspace, agents: agents)
31
+ end
32
+
33
+ # @param env [Hash] the Rack environment
34
+ # @return [Array] the Rack response triple
35
+ def call(env)
36
+ request = Rack::Request.new(env)
37
+ route(request)
38
+ rescue Workspace::OutsideWorkspace, KeyError, JSON::ParserError => e
39
+ json(400, error: e.message)
40
+ rescue Domains::Unknown => e
41
+ json(404, error: e.message)
42
+ rescue Parse::ParserMissing => e
43
+ json(503, error: e.message)
44
+ end
45
+
46
+ private
47
+
48
+ # Routes that answer from the workspace alone, those that read a JSON body first, and those
49
+ # the lower panels ask (answered by {Panels}).
50
+ READS = { "GET " => :page, "GET #{PREFIX}" => :page, "GET #{PREFIX}/files" => :listing,
51
+ "GET #{PREFIX}/draft" => :conversation, "GET #{PREFIX}/domains" => :domains }.freeze
52
+ WRITES = { "PUT #{PREFIX}/file" => :save, "DELETE #{PREFIX}/file" => :remove,
53
+ "POST #{PREFIX}/parse" => :parse, "POST #{PREFIX}/check" => :check,
54
+ "POST #{PREFIX}/ask" => :ask, "POST #{PREFIX}/open" => :open_domain }.freeze
55
+ PANEL_ROUTES = { "POST #{PREFIX}/live/commands" => :commands, "POST #{PREFIX}/live/dispatch" => :dispatch,
56
+ "POST #{PREFIX}/live/records" => :records, "POST #{PREFIX}/behaviors" => :behaviors,
57
+ "POST #{PREFIX}/timeline" => :timeline, "POST #{PREFIX}/board/edit" => :board_edit,
58
+ "GET #{PREFIX}/catalog" => :catalog,
59
+ "POST #{PREFIX}/help" => :help, "POST #{PREFIX}/run" => :run,
60
+ "GET #{PREFIX}/eras" => :eras, "POST #{PREFIX}/job" => :start_job,
61
+ "GET #{PREFIX}/job" => :job_state, "DELETE #{PREFIX}/job" => :cancel_job }.freeze
62
+ private_constant :READS, :WRITES, :PANEL_ROUTES
63
+
64
+ def route(request)
65
+ key = "#{request.request_method} #{request.path_info.chomp("/")}"
66
+ answer_for(key, request) || json(404, error: "no such route")
67
+ end
68
+
69
+ # The response the key's table gives, or nil when no table has the key.
70
+ def answer_for(key, request)
71
+ return send(READS[key]) if READS.key?(key)
72
+ return send(WRITES[key], body(request)) if WRITES.key?(key)
73
+
74
+ json(200, @bench.panels.public_send(PANEL_ROUTES[key], body(request))) if PANEL_ROUTES.key?(key)
75
+ end
76
+
77
+ def workspace = @bench.workspace
78
+
79
+ def listing = json(200, files: files)
80
+
81
+ def conversation = json(200, draft: @bench.session.summary(@bench.slug))
82
+
83
+ # The known domains, and the directory the editor has open now.
84
+ def domains = json(200, domains: Domains.known(root: @root), current: File.basename(workspace))
85
+
86
+ # Opens a known domain as a scratch copy, replacing everything kept for the old workspace.
87
+ def open_domain(body)
88
+ id = body.fetch("id")
89
+ opened = Domains.open(root: @root, id: id)
90
+ @bench.close
91
+ @bench = Workbench.new(workspace: opened, agents: @agents)
92
+ json(200, opened: id, current: File.basename(opened))
93
+ end
94
+
95
+ def ask(body)
96
+ json(200, @bench.assistant.ask(body.fetch("prompt")))
97
+ rescue Agent::Unavailable => e
98
+ json(503, error: e.message)
99
+ rescue Agent::ValidationError => e
100
+ json(502, error: e.message)
101
+ end
102
+
103
+ def page = [200, { "content-type" => "text/html; charset=utf-8" }, [File.read(PAGE)]]
104
+
105
+ def files
106
+ Workspace.files(workspace).map { |path| { path: path, text: File.read(File.join(workspace, path)) } }
107
+ end
108
+
109
+ def save(body)
110
+ path = domain_path(body) or return refused(body)
111
+
112
+ target = File.join(workspace, path)
113
+ FileUtils.mkdir_p(File.dirname(target))
114
+ File.write(target, body.fetch("text"))
115
+ @bench.session.record(path: path, text: body.fetch("text"))
116
+ json(200, saved: path)
117
+ end
118
+
119
+ def remove(body)
120
+ path = domain_path(body) or return refused(body)
121
+
122
+ FileUtils.rm_f(File.join(workspace, path))
123
+ json(200, removed: path)
124
+ end
125
+
126
+ # The request's path when it names a domain file inside the workspace, else nil.
127
+ def domain_path(body)
128
+ path = body.fetch("path")
129
+ Workspace.refuse_escapes!([path])
130
+ path if Workspace::EXTENSIONS.include?(File.extname(path).delete_prefix("."))
131
+ end
132
+
133
+ def refused(body) = json(400, error: "#{body["path"].inspect} is not a domain file")
134
+
135
+ def parse(body)
136
+ refusal = Workspace.stage(dir: workspace, buffers: buffers(body)) { |stage| Parse.call(stage) }
137
+ json(200, diagnostic: refusal)
138
+ end
139
+
140
+ def check(body)
141
+ json(200, Check.call(dir: workspace, buffers: buffers(body), chapter_name: body["chapter_name"]))
142
+ end
143
+
144
+ def buffers(body) = body.fetch("buffers", {})
145
+
146
+ def body(request)
147
+ raw = request.body&.read.to_s
148
+ raw.empty? ? {} : JSON.parse(raw)
149
+ end
150
+
151
+ def json(status, payload)
152
+ [status, { "content-type" => "application/json" }, [JSON.generate(payload)]]
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,51 @@
1
+ require_relative "agent"
2
+ require_relative "assistant"
3
+ require_relative "panels"
4
+ require_relative "session"
5
+
6
+ module Hecks
7
+ module Composer
8
+ # Everything the editor keeps for one workspace, on the hecksagon side: the draft bookkeeping,
9
+ # the agent that edits it and the panels beneath the editor. Opening another domain builds a
10
+ # new workbench, so nothing of the old workspace carries over.
11
+ class Workbench
12
+ # @return [String] the domain directory this workbench is for
13
+ attr_reader :workspace
14
+
15
+ # @param workspace [String] the domain directory
16
+ # @param agents [Runtime::Registry, nil] a registry holding the `composer_agent` port and one
17
+ # adapter; the one `HECKS_COMPOSER_AGENT` names (`claude`, the default, or `fake`) when nil
18
+ def initialize(workspace:, agents: nil)
19
+ @workspace = File.expand_path(workspace)
20
+ @agents = agents
21
+ end
22
+
23
+ # @return [String] the workspace's directory name, which names its draft
24
+ def slug = File.basename(workspace)
25
+
26
+ # @return [Session] the draft bookkeeping, booted on first use
27
+ def session = @session ||= Session.new
28
+
29
+ # @return [Assistant] the agent loop over this workspace
30
+ def assistant = @assistant ||= Assistant.new(workspace: workspace, agents: agents, session: session)
31
+
32
+ # @return [Panels] what answers the lower panels
33
+ def panels = @panels ||= Panels.new(workspace: workspace, session: session)
34
+
35
+ # Releases what the panels booted.
36
+ # @return [void]
37
+ def close = @panels&.close
38
+
39
+ private
40
+
41
+ def agents = @agents ||= default_agents
42
+
43
+ # The adapter `HECKS_COMPOSER_AGENT` names: the real `claude`, or a scripted `fake`.
44
+ def default_agents
45
+ name = ENV.fetch("HECKS_COMPOSER_AGENT", "claude") == "fake" ? "fake_agent" : "claude_composer"
46
+ require_relative "adapters/#{name}"
47
+ Agent.registry_with(File.join(Agent::ADAPTERS, "#{name}.adapter"))
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,81 @@
1
+ require "fileutils"
2
+ require "tmpdir"
3
+
4
+ module Hecks
5
+ module Composer
6
+ # A workspace is a directory of domain files; an editor holds unsaved text for some of them.
7
+ # Staging lays that text over a copy of the files on disk so a check sees what is on screen
8
+ # and the directory itself stays untouched.
9
+ module Workspace
10
+ # The files a domain is made of; anything else in the directory is data, not declaration.
11
+ EXTENSIONS = %w[bluebook hecksagon world adapter port behaviors].freeze
12
+
13
+ # Raised when an unsaved buffer names a path outside the workspace.
14
+ class OutsideWorkspace < ArgumentError; end
15
+
16
+ module_function
17
+
18
+ # Copies the workspace's declaration files into a temporary directory, lays the buffers over
19
+ # them, and yields that directory.
20
+ #
21
+ # @param dir [String] the workspace directory
22
+ # @param buffers [Hash{String => String}] unsaved text by path relative to `dir`; a path
23
+ # with no file on disk yet is created
24
+ # @yieldparam stage [String] the temporary directory holding the staged files
25
+ # @return [Object] what the block returned
26
+ # @raise [OutsideWorkspace] if a buffer's path is absolute or climbs out of the workspace
27
+ def stage(dir:, buffers: {})
28
+ stage = materialize(dir: dir, buffers: buffers)
29
+ yield stage
30
+ ensure
31
+ FileUtils.rm_rf(stage) if stage
32
+ end
33
+
34
+ # Stages as {stage} does, but leaves the directory for the caller to remove, so a booted
35
+ # domain can keep reading its files after this returns.
36
+ #
37
+ # @param dir [String] the workspace directory
38
+ # @param buffers [Hash{String => String}] unsaved text by path relative to `dir`
39
+ # @return [String] the temporary directory holding the staged files
40
+ # @raise [OutsideWorkspace] if a buffer's path is absolute or climbs out of the workspace
41
+ def materialize(dir:, buffers: {})
42
+ refuse_escapes!(buffers.keys)
43
+ stage = Dir.mktmpdir("hecks-composer")
44
+ copy_declarations(dir, stage)
45
+ buffers.each { |path, text| write(File.join(stage, path), text) }
46
+ stage
47
+ rescue StandardError
48
+ FileUtils.rm_rf(stage) if stage
49
+ raise
50
+ end
51
+
52
+ # @param stage [String] a staged directory
53
+ # @return [Array<String>] the declaration files in it, relative and sorted
54
+ def files(stage)
55
+ Dir.glob("**/*.{#{EXTENSIONS.join(",")}}", base: stage).sort
56
+ end
57
+
58
+ # @api private
59
+ def copy_declarations(dir, stage)
60
+ files(dir).each do |relative|
61
+ FileUtils.mkdir_p(File.dirname(File.join(stage, relative)))
62
+ FileUtils.cp(File.join(dir, relative), File.join(stage, relative))
63
+ end
64
+ end
65
+
66
+ # @api private
67
+ def write(path, text)
68
+ FileUtils.mkdir_p(File.dirname(path))
69
+ File.write(path, text)
70
+ end
71
+
72
+ # @api private
73
+ def refuse_escapes!(paths)
74
+ offender = paths.find { |path| path.start_with?("/") || path.split("/").include?("..") }
75
+ return unless offender
76
+
77
+ raise OutsideWorkspace, "#{offender.inspect} is not inside the workspace"
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,17 @@
1
+ require_relative "composer/plain"
2
+ require_relative "composer/workspace"
3
+ require_relative "composer/parse"
4
+ require_relative "composer/outline"
5
+ require_relative "composer/layout"
6
+ require_relative "composer/behavior_run"
7
+ require_relative "composer/live"
8
+ require_relative "composer/toolbox"
9
+ require_relative "composer/jobs"
10
+ require_relative "composer/check"
11
+
12
+ module Hecks
13
+ # The Composer's outside, on the hecksagon side: staging a workspace, judging its text and
14
+ # projecting what it declares. The Composer chapter's bluebook is in `composer/bluebook/`.
15
+ module Composer
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ module Hecks
2
+ module Corpus
3
+ # Where `.adapter` files live, for the parser parity check. Extended onto `Corpus`.
4
+ module Adapters
5
+ # Directories that hold copies or build output, never a source `.adapter`.
6
+ EXCLUDED_DIRS = %w[vendor node_modules target tmp .git .aws-sam].freeze
7
+
8
+ ADAPTER_GLOBS = %w[lib/**/*.adapter examples/**/*.adapter spec/fixtures/**/*.adapter].freeze
9
+
10
+ # Every `.adapter` file the repo carries.
11
+ #
12
+ # @param root [String] repository root to search under
13
+ # @return [Array<String>] absolute paths, sorted, none under a vendored, `node_modules`,
14
+ # `target` or scratch directory
15
+ def adapter_files(root: Corpus::ROOT)
16
+ ADAPTER_GLOBS.flat_map { |glob| Dir.glob(File.join(root, glob)) }.uniq.sort.reject do |path|
17
+ path.delete_prefix("#{root}/").split("/").intersect?(EXCLUDED_DIRS)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -3,7 +3,7 @@ module Hecks
3
3
  # Every Rust-facing list reads `rust/Cargo.toml`'s `[features]` as its
4
4
  # one source, rather than each consumer typing it out by hand.
5
5
  GENERATED_DIR = "rust/src/generated".freeze
6
- RUST_DOMAIN_KINDS = %i[example stress fixture].freeze
6
+ RUST_DOMAIN_KINDS = %i[example stress semantics fixture].freeze
7
7
 
8
8
  RustDomain = Struct.new(:feature, :dir, :kind)
9
9
 
@@ -0,0 +1,19 @@
1
+ module Hecks
2
+ module Corpus
3
+ # Where `.world` files live, for the parser parity check. Extended onto `Corpus`.
4
+ module Worlds
5
+ WORLD_GLOBS = %w[lib/**/*.world examples/**/*.world qa/**/*.world spec/fixtures/**/*.world].freeze
6
+
7
+ # Every `.world` file the repo carries.
8
+ #
9
+ # @param root [String] repository root to search under
10
+ # @return [Array<String>] absolute paths, sorted, none under a vendored, `node_modules`,
11
+ # `target` or scratch directory
12
+ def world_files(root: Corpus::ROOT)
13
+ WORLD_GLOBS.flat_map { |glob| Dir.glob(File.join(root, glob)) }.uniq.sort.reject do |path|
14
+ path.delete_prefix("#{root}/").split("/").intersect?(Adapters::EXCLUDED_DIRS)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
data/lib/hecks/corpus.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  require_relative "fuzzing/target_capabilities"
2
+ require_relative "corpus/adapters"
2
3
  require_relative "corpus/routes"
3
4
  require_relative "corpus/rust"
4
5
  require_relative "corpus/sources"
6
+ require_relative "corpus/worlds"
5
7
 
6
8
  module Hecks
7
9
  # The corpus: every place in this repo that holds a real domain, named once.
@@ -36,6 +38,7 @@ module Hecks
36
38
  deploy: "lib/hecks/deploy/bluebook/*.bluebook",
37
39
  site: "lib/hecks/site/bluebook/*.bluebook",
38
40
  tickets: "lib/hecks/tickets/bluebook/*.bluebook",
41
+ composer: "lib/hecks/composer/bluebook/*.bluebook",
39
42
  tenancy: "lib/hecks/tenancy/bluebook/*.bluebook",
40
43
  sme: "lib/hecks/sme/bluebook/*.bluebook",
41
44
  fixture: "spec/fixtures/**/*.bluebook"
@@ -43,8 +46,10 @@ module Hecks
43
46
 
44
47
  KINDS = (DIRECTORY_KINDS.keys + FILE_KINDS.keys).freeze
45
48
 
49
+ extend Adapters
46
50
  extend Rust
47
51
  extend Sources
52
+ extend Worlds
48
53
 
49
54
  module_function
50
55
 
@@ -451,7 +451,7 @@ Hecks.bluebook "Deploy" do
451
451
 
452
452
  # The domain's own directory on disk, where `environments/<slug>.world` is written.
453
453
  value_object "DirectoryPath" do
454
- attribute :value, String, pattern: '[^\t\n\r]'
454
+ attribute :value, String, matches: :not_empty
455
455
  end
456
456
 
457
457
  value_object "Report" do
@@ -581,14 +581,14 @@ Hecks.bluebook "Deploy" do
581
581
  identified_by :run
582
582
 
583
583
  value_object "RunKey" do
584
- attribute :value, String, pattern: '[^ \t\n\r]'
584
+ attribute :value, String, matches: :present
585
585
 
586
586
  invariant("a run is named") { !value.to_s.empty? }
587
587
  end
588
588
 
589
589
  # A domain directory, whose `bluebook/` holds the `.world` that declares `deployed_to`.
590
590
  value_object "DomainPath" do
591
- attribute :value, String, pattern: '[^\t\n\r]'
591
+ attribute :value, String, matches: :not_empty
592
592
 
593
593
  invariant("a domain is named by its path") { !value.to_s.empty? }
594
594
  end
@@ -604,7 +604,7 @@ Hecks.bluebook "Deploy" do
604
604
 
605
605
  # Where the recipe is written, for a domain that lives in a client's repository.
606
606
  value_object "Location" do
607
- attribute :value, String, pattern: '[^\t\n\r]'
607
+ attribute :value, String, matches: :not_empty
608
608
  end
609
609
 
610
610
  # An `environments/<name>.world` overlay of the domain, which must exist.
@@ -707,14 +707,14 @@ Hecks.bluebook "Deploy" do
707
707
  identified_by :run
708
708
 
709
709
  value_object "RunKey" do
710
- attribute :value, String, pattern: '[^ \t\n\r]'
710
+ attribute :value, String, matches: :present
711
711
 
712
712
  invariant("a run is named") { !value.to_s.empty? }
713
713
  end
714
714
 
715
715
  # A domain directory, whose `bluebook/` holds the `.world` that declares `deployed_to`.
716
716
  value_object "DomainPath" do
717
- attribute :value, String, pattern: '[^\t\n\r]'
717
+ attribute :value, String, matches: :not_empty
718
718
 
719
719
  invariant("a domain is named by its path") { !value.to_s.empty? }
720
720
  end
@@ -722,7 +722,7 @@ Hecks.bluebook "Deploy" do
722
722
  # A directory the cleared worlds are written to, at their paths below `bluebook/`, instead of
723
723
  # over the originals.
724
724
  value_object "Location" do
725
- attribute :value, String, pattern: '[^\t\n\r]'
725
+ attribute :value, String, matches: :not_empty
726
726
  end
727
727
 
728
728
  value_object "Report" do
@@ -813,14 +813,14 @@ Hecks.bluebook "Deploy" do
813
813
  identified_by :run
814
814
 
815
815
  value_object "RunKey" do
816
- attribute :value, String, pattern: '[^ \t\n\r]'
816
+ attribute :value, String, matches: :present
817
817
 
818
818
  invariant("a run is named") { !value.to_s.empty? }
819
819
  end
820
820
 
821
821
  # Makefile paths, comma separated; three generated fixture domains when absent.
822
822
  value_object "PathList" do
823
- attribute :value, String, pattern: '[^\t\n\r]'
823
+ attribute :value, String, matches: :not_empty
824
824
  end
825
825
 
826
826
  value_object "Report" do
@@ -909,7 +909,7 @@ Hecks.bluebook "Deploy" do
909
909
  identified_by :run
910
910
 
911
911
  value_object "RunKey" do
912
- attribute :value, String, pattern: '[^ \t\n\r]'
912
+ attribute :value, String, matches: :present
913
913
 
914
914
  invariant("a run is named") { !value.to_s.empty? }
915
915
  end
@@ -1020,13 +1020,13 @@ Hecks.bluebook "Deploy" do
1020
1020
  identified_by :run
1021
1021
 
1022
1022
  value_object "RunKey" do
1023
- attribute :value, String, pattern: '[^ \t\n\r]'
1023
+ attribute :value, String, matches: :present
1024
1024
 
1025
1025
  invariant("a run is named") { !value.to_s.empty? }
1026
1026
  end
1027
1027
 
1028
1028
  value_object "Location" do
1029
- attribute :value, String, pattern: '[^\t\n\r]'
1029
+ attribute :value, String, matches: :not_empty
1030
1030
 
1031
1031
  invariant("a project or script is named by its path") { !value.to_s.empty? }
1032
1032
  end
@@ -1159,13 +1159,13 @@ Hecks.bluebook "Deploy" do
1159
1159
  identified_by :run
1160
1160
 
1161
1161
  value_object "RunKey" do
1162
- attribute :value, String, pattern: '[^ \t\n\r]'
1162
+ attribute :value, String, matches: :present
1163
1163
 
1164
1164
  invariant("a run is named") { !value.to_s.empty? }
1165
1165
  end
1166
1166
 
1167
1167
  value_object "Location" do
1168
- attribute :value, String, pattern: '[^\t\n\r]'
1168
+ attribute :value, String, matches: :not_empty
1169
1169
 
1170
1170
  invariant("a project or script is named by its path") { !value.to_s.empty? }
1171
1171
  end
@@ -1187,7 +1187,7 @@ Hecks.bluebook "Deploy" do
1187
1187
 
1188
1188
  # A local image reference to push, as the script's LOCAL_IMAGE takes it.
1189
1189
  value_object "ImageName" do
1190
- attribute :value, String, pattern: '^[^ \t\n\r]+$'
1190
+ attribute :value, String, matches: :no_whitespace
1191
1191
  end
1192
1192
 
1193
1193
  value_object "Switch" do
@@ -1352,13 +1352,13 @@ Hecks.bluebook "Deploy" do
1352
1352
  identified_by :run
1353
1353
 
1354
1354
  value_object "RunKey" do
1355
- attribute :value, String, pattern: '[^ \t\n\r]'
1355
+ attribute :value, String, matches: :present
1356
1356
 
1357
1357
  invariant("a run is named") { !value.to_s.empty? }
1358
1358
  end
1359
1359
 
1360
1360
  value_object "Location" do
1361
- attribute :value, String, pattern: '[^\t\n\r]'
1361
+ attribute :value, String, matches: :not_empty
1362
1362
 
1363
1363
  invariant("a project or script is named by its path") { !value.to_s.empty? }
1364
1364
  end
@@ -1538,13 +1538,13 @@ Hecks.bluebook "Deploy" do
1538
1538
  identified_by :run
1539
1539
 
1540
1540
  value_object "RunKey" do
1541
- attribute :value, String, pattern: '[^ \t\n\r]'
1541
+ attribute :value, String, matches: :present
1542
1542
 
1543
1543
  invariant("a run is named") { !value.to_s.empty? }
1544
1544
  end
1545
1545
 
1546
1546
  value_object "Location" do
1547
- attribute :value, String, pattern: '[^\t\n\r]'
1547
+ attribute :value, String, matches: :not_empty
1548
1548
 
1549
1549
  invariant("a project or script is named by its path") { !value.to_s.empty? }
1550
1550
  end
@@ -1560,7 +1560,7 @@ Hecks.bluebook "Deploy" do
1560
1560
 
1561
1561
  # A Secrets Manager id or ARN; the script reads the password from it, so no value travels here.
1562
1562
  value_object "Secret" do
1563
- attribute :value, String, pattern: '^[^ \t\n\r]+$'
1563
+ attribute :value, String, matches: :no_whitespace
1564
1564
  end
1565
1565
 
1566
1566
  # A database's name; empty leaves it to the generated script's own default.
@@ -1826,13 +1826,13 @@ Hecks.bluebook "Deploy" do
1826
1826
  identified_by :run
1827
1827
 
1828
1828
  value_object "RunKey" do
1829
- attribute :value, String, pattern: '[^ \t\n\r]'
1829
+ attribute :value, String, matches: :present
1830
1830
 
1831
1831
  invariant("a run is named") { !value.to_s.empty? }
1832
1832
  end
1833
1833
 
1834
1834
  value_object "Location" do
1835
- attribute :value, String, pattern: '[^\t\n\r]'
1835
+ attribute :value, String, matches: :not_empty
1836
1836
 
1837
1837
  invariant("a project or script is named by its path") { !value.to_s.empty? }
1838
1838
  end
@@ -1846,7 +1846,7 @@ Hecks.bluebook "Deploy" do
1846
1846
  end
1847
1847
 
1848
1848
  value_object "Secret" do
1849
- attribute :value, String, pattern: '^[^ \t\n\r]+$'
1849
+ attribute :value, String, matches: :no_whitespace
1850
1850
  end
1851
1851
 
1852
1852
  # A database's name; empty leaves it to the generated script's own default.
@@ -1984,13 +1984,13 @@ Hecks.bluebook "Deploy" do
1984
1984
  identified_by :run
1985
1985
 
1986
1986
  value_object "RunKey" do
1987
- attribute :value, String, pattern: '[^ \t\n\r]'
1987
+ attribute :value, String, matches: :present
1988
1988
 
1989
1989
  invariant("a run is named") { !value.to_s.empty? }
1990
1990
  end
1991
1991
 
1992
1992
  value_object "Location" do
1993
- attribute :value, String, pattern: '[^\t\n\r]'
1993
+ attribute :value, String, matches: :not_empty
1994
1994
 
1995
1995
  invariant("a project, script or file is named by its path") { !value.to_s.empty? }
1996
1996
  end
@@ -2134,13 +2134,13 @@ Hecks.bluebook "Deploy" do
2134
2134
  identified_by :run
2135
2135
 
2136
2136
  value_object "RunKey" do
2137
- attribute :value, String, pattern: '[^ \t\n\r]'
2137
+ attribute :value, String, matches: :present
2138
2138
 
2139
2139
  invariant("a run is named") { !value.to_s.empty? }
2140
2140
  end
2141
2141
 
2142
2142
  value_object "Location" do
2143
- attribute :value, String, pattern: '[^\t\n\r]'
2143
+ attribute :value, String, matches: :not_empty
2144
2144
 
2145
2145
  invariant("a project or script is named by its path") { !value.to_s.empty? }
2146
2146
  end
@@ -2152,7 +2152,7 @@ Hecks.bluebook "Deploy" do
2152
2152
 
2153
2153
  # A git branch name. A preview is never of a production branch.
2154
2154
  value_object "Branch" do
2155
- attribute :value, String, pattern: '^[^ \t\n\r]+$'
2155
+ attribute :value, String, matches: :no_whitespace
2156
2156
 
2157
2157
  invariant("a preview is never of main or master") { value != "main" && value != "master" }
2158
2158
  end
@@ -2510,13 +2510,13 @@ Hecks.bluebook "Deploy" do
2510
2510
  identified_by :run
2511
2511
 
2512
2512
  value_object "RunKey" do
2513
- attribute :value, String, pattern: '[^ \t\n\r]'
2513
+ attribute :value, String, matches: :present
2514
2514
 
2515
2515
  invariant("a run is named") { !value.to_s.empty? }
2516
2516
  end
2517
2517
 
2518
2518
  value_object "Location" do
2519
- attribute :value, String, pattern: '[^\t\n\r]'
2519
+ attribute :value, String, matches: :not_empty
2520
2520
 
2521
2521
  invariant("a project or script is named by its path") { !value.to_s.empty? }
2522
2522
  end
@@ -2656,13 +2656,13 @@ Hecks.bluebook "Deploy" do
2656
2656
  identified_by :run
2657
2657
 
2658
2658
  value_object "RunKey" do
2659
- attribute :value, String, pattern: '[^ \t\n\r]'
2659
+ attribute :value, String, matches: :present
2660
2660
 
2661
2661
  invariant("a run is named") { !value.to_s.empty? }
2662
2662
  end
2663
2663
 
2664
2664
  value_object "Location" do
2665
- attribute :value, String, pattern: '[^\t\n\r]'
2665
+ attribute :value, String, matches: :not_empty
2666
2666
 
2667
2667
  invariant("a template is named by its path") { !value.to_s.empty? }
2668
2668
  end
@@ -2785,14 +2785,14 @@ Hecks.bluebook "Deploy" do
2785
2785
  identified_by :run
2786
2786
 
2787
2787
  value_object "RunKey" do
2788
- attribute :value, String, pattern: '[^ \t\n\r]'
2788
+ attribute :value, String, matches: :present
2789
2789
 
2790
2790
  invariant("a run is named") { !value.to_s.empty? }
2791
2791
  end
2792
2792
 
2793
2793
  # Domain directories, comma separated; every domain of the checkout when absent.
2794
2794
  value_object "DomainList" do
2795
- attribute :value, String, pattern: '[^\t\n\r]'
2795
+ attribute :value, String, matches: :not_empty
2796
2796
  end
2797
2797
 
2798
2798
  value_object "Report" do