hecks 2.9.0 → 3.0.1

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 (600) hide show
  1. checksums.yaml +4 -4
  2. data/exe/hecks +51 -6
  3. data/lib/hecks/adapters/driven/heki/journal.rb +1 -1
  4. data/lib/hecks/adapters/driven/lambda/client.rb +1 -1
  5. data/lib/hecks/adapters/driven/lambda.rb +1 -1
  6. data/lib/hecks/adapters/driven/memory.rb +16 -3
  7. data/lib/hecks/adapters/driven/postgres/reconnect.rb +6 -2
  8. data/lib/hecks/adapters/driven/postgres/shared_connection.rb +180 -0
  9. data/lib/hecks/adapters/driven/postgres.rb +13 -4
  10. data/lib/hecks/adapters/driven/prism.rb +41 -8
  11. data/lib/hecks/adapters/driven/tenant_provisioner.rb +73 -16
  12. data/lib/hecks/behaviors/expectations.rb +1 -1
  13. data/lib/hecks/behaviors.rb +1 -1
  14. data/lib/hecks/bench/cli.rb +62 -0
  15. data/lib/hecks/bench/environment.rb +71 -0
  16. data/lib/hecks/bench/postgres_probe.rb +30 -0
  17. data/lib/hecks/bench/report.rb +76 -0
  18. data/lib/hecks/bench/ruby_runner.rb +93 -0
  19. data/lib/hecks/bench/run.rb +55 -0
  20. data/lib/hecks/bench/rust_runner.rb +101 -0
  21. data/lib/hecks/bench/stats.rb +46 -0
  22. data/lib/hecks/bench/suite.rb +98 -0
  23. data/lib/hecks/bench/workload.rb +89 -0
  24. data/lib/hecks/bench.rb +17 -0
  25. data/lib/hecks/bluebook/assembly/contracts.rb +2 -0
  26. data/lib/hecks/bluebook/attribute.rb +1 -1
  27. data/lib/hecks/bluebook/behaviour/aggregate.rb +30 -1
  28. data/lib/hecks/bluebook/behaviour/domain_port.rb +7 -0
  29. data/lib/hecks/bluebook/chapter.rb +6 -2
  30. data/lib/hecks/bluebook/domain_port.rb +36 -5
  31. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +10 -0
  32. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +8 -0
  33. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -1
  34. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +31 -3
  35. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +122 -3
  36. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +1 -1
  37. data/lib/hecks/bluebook/dsl/query_builder.rb +27 -1
  38. data/lib/hecks/bluebook/dsl/rule_reference.rb +1 -1
  39. data/lib/hecks/bluebook/dsl/world_builder.rb +29 -2
  40. data/lib/hecks/bluebook/expression/canonical_form.rb +1 -1
  41. data/lib/hecks/bluebook/expression/resolver.rb +3 -2
  42. data/lib/hecks/bluebook/hexagon.rb +13 -5
  43. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +1 -0
  44. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +4 -1
  45. data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +246 -0
  46. data/lib/hecks/bluebook/meta_validator.rb +9 -4
  47. data/lib/hecks/bluebook/model_check.rb +23 -0
  48. data/lib/hecks/bluebook/pattern_subset.rb +38 -0
  49. data/lib/hecks/bluebook/query.rb +20 -5
  50. data/lib/hecks/bluebook/smoke_test.rb +1 -1
  51. data/lib/hecks/canonical_json.rb +30 -0
  52. data/lib/hecks/chapters.rb +102 -0
  53. data/lib/hecks/cli/behaviors.rb +61 -0
  54. data/lib/hecks/cli/canonicalise.rb +25 -0
  55. data/lib/hecks/cli/check_engine_agreement.rb +32 -0
  56. data/lib/hecks/cli/check_era.rb +88 -0
  57. data/lib/hecks/cli/console.rb +66 -0
  58. data/lib/hecks/cli/deploy_template_diff.rb +40 -0
  59. data/lib/hecks/cli/doc_coverage.rb +32 -0
  60. data/lib/hecks/cli/document.rb +2 -2
  61. data/lib/hecks/cli/expression_projection.rb +41 -0
  62. data/lib/hecks/cli/follow.rb +88 -0
  63. data/lib/hecks/cli/history.rb +44 -0
  64. data/lib/hecks/cli/ir.rb +1 -1
  65. data/lib/hecks/cli/mcp.rb +3 -3
  66. data/lib/hecks/cli/model_check.rb +15 -8
  67. data/lib/hecks/cli/pattern_cases.rb +47 -0
  68. data/lib/hecks/cli/present.rb +39 -0
  69. data/lib/hecks/cli/project_cli.rb +224 -32
  70. data/lib/hecks/cli/project_diagrams.rb +4 -3
  71. data/lib/hecks/cli/project_glossary.rb +44 -0
  72. data/lib/hecks/cli/reference.rb +46 -0
  73. data/lib/hecks/cli/refresh_projections.rb +44 -0
  74. data/lib/hecks/cli/refresh_rspec_runtime_baseline.rb +150 -0
  75. data/lib/hecks/cli/release.rb +70 -0
  76. data/lib/hecks/cli/release_gem.rb +59 -0
  77. data/lib/hecks/cli/rspec_io_parallel_files.rb +137 -0
  78. data/lib/hecks/cli/rspec_shard_files.rb +66 -0
  79. data/lib/hecks/cli/run.rb +3 -3
  80. data/lib/hecks/cli/seed_semantics_corpus.rb +71 -0
  81. data/lib/hecks/cli/serve_query_ir_mcp.rb +30 -0
  82. data/lib/hecks/cli/shape.rb +70 -0
  83. data/lib/hecks/cli/smoke_http.rb +230 -0
  84. data/lib/hecks/cli/smoke_test.rb +5 -3
  85. data/lib/hecks/cli/spec_example.rb +27 -0
  86. data/lib/hecks/cli/statements.rb +32 -0
  87. data/lib/hecks/cli/stores.rb +4 -5
  88. data/lib/hecks/cli/stress_concurrency_specs.rb +177 -0
  89. data/lib/hecks/cli.rb +201 -52
  90. data/lib/hecks/codemod.rb +324 -0
  91. data/lib/hecks/corpus.rb +423 -0
  92. data/lib/hecks/deploy/bluebook/deploy.bluebook +591 -23
  93. data/lib/hecks/deploy/bluebook/deploy.hecksagon +41 -4
  94. data/lib/hecks/deploy/oidc.json +82 -2
  95. data/lib/hecks/doc/reference.rb +351 -0
  96. data/lib/hecks/doors/cli_door.rb +196 -0
  97. data/lib/hecks/doors/cli_runner.rb +297 -0
  98. data/lib/hecks/{facade → doors}/command_request.rb +1 -1
  99. data/lib/hecks/{facade → doors}/handle.rb +2 -2
  100. data/lib/hecks/{facade → doors}/json_door.rb +4 -4
  101. data/lib/hecks/doors/launcher_options.rb +129 -0
  102. data/lib/hecks/{cli → doors}/mcp_door.rb +7 -7
  103. data/lib/hecks/doors/mcp_door_scope.rb +97 -0
  104. data/lib/hecks/{facade/surface → doors/ruby_door}/aggregate_door.rb +8 -3
  105. data/lib/hecks/{facade/surface → doors/ruby_door}/chapter.rb +3 -3
  106. data/lib/hecks/{facade/surface.rb → doors/ruby_door.rb} +36 -5
  107. data/lib/hecks/doors.rb +61 -0
  108. data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +1 -1
  109. data/lib/hecks/embryonaut_bluebook.rb +1 -1
  110. data/lib/hecks/engine_agreement.rb +156 -0
  111. data/lib/hecks/facade.rb +24 -37
  112. data/lib/hecks/forms/app.rb +6 -6
  113. data/lib/hecks/forms/banking_presentation.rb +50 -0
  114. data/lib/hecks/forms/field_shape.rb +2 -1
  115. data/lib/hecks/forms/port_argument.rb +1 -1
  116. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +393 -0
  117. data/lib/hecks/fuzzing/combination_miner.rb +86 -0
  118. data/lib/hecks/fuzzing/concurrent_dispatch.rb +214 -0
  119. data/lib/hecks/fuzzing/coverage_campaign.rb +143 -0
  120. data/lib/hecks/fuzzing/differential.rb +156 -0
  121. data/lib/hecks/fuzzing/domain_generator.rb +615 -0
  122. data/lib/hecks/fuzzing/era_boundary.rb +71 -0
  123. data/lib/hecks/fuzzing/form_census.rb +136 -0
  124. data/lib/hecks/fuzzing/generated_domain_check.rb +93 -0
  125. data/lib/hecks/fuzzing/invalid_value_generator.rb +64 -0
  126. data/lib/hecks/fuzzing/isolated_boot.rb +301 -0
  127. data/lib/hecks/fuzzing/nondeterministic.rb +51 -0
  128. data/lib/hecks/fuzzing/outside_world.rb +45 -0
  129. data/lib/hecks/fuzzing/persistence_parity.rb +102 -0
  130. data/lib/hecks/fuzzing/properties/corrections.rb +88 -0
  131. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +314 -0
  132. data/lib/hecks/fuzzing/properties/guards.rb +190 -0
  133. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +300 -0
  134. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +93 -0
  135. data/lib/hecks/fuzzing/properties/outbox.rb +89 -0
  136. data/lib/hecks/fuzzing/properties/querying.rb +175 -0
  137. data/lib/hecks/fuzzing/properties.rb +162 -0
  138. data/lib/hecks/fuzzing/qa_settings.rb +123 -0
  139. data/lib/hecks/fuzzing/replay.rb +505 -0
  140. data/lib/hecks/fuzzing/rotation_priority.rb +48 -0
  141. data/lib/hecks/fuzzing/rust_gap_manifest.rb +108 -0
  142. data/lib/hecks/fuzzing/self_consistency.rb +339 -0
  143. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +385 -0
  144. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +132 -0
  145. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +95 -0
  146. data/lib/hecks/fuzzing/sequence_generator/picker.rb +77 -0
  147. data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +109 -0
  148. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +237 -0
  149. data/lib/hecks/fuzzing/sequence_generator.rb +189 -0
  150. data/lib/hecks/fuzzing/shrinker.rb +166 -0
  151. data/lib/hecks/fuzzing/structural_skips.rb +33 -0
  152. data/lib/hecks/fuzzing/sweep_depth.rb +43 -0
  153. data/lib/hecks/fuzzing/target_capabilities.rb +163 -0
  154. data/lib/hecks/fuzzing/value_generator.rb +216 -0
  155. data/lib/hecks/fuzzing.rb +23 -0
  156. data/lib/hecks/grammar/evolve.rb +336 -0
  157. data/lib/hecks/hecks/adapters/codebase/codemods.rb +45 -0
  158. data/lib/hecks/hecks/adapters/codebase/conformance.rb +71 -0
  159. data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +202 -0
  160. data/lib/hecks/hecks/adapters/codebase/gem_registry.rb +80 -0
  161. data/lib/hecks/hecks/adapters/codebase/kernel_tables.rb +54 -0
  162. data/lib/hecks/hecks/adapters/codebase/language.rb +240 -0
  163. data/lib/hecks/hecks/adapters/codebase/npm_registry.rb +102 -0
  164. data/lib/hecks/hecks/adapters/codebase/publishing.rb +142 -0
  165. data/lib/hecks/hecks/adapters/codebase/regeneration.rb +67 -0
  166. data/lib/hecks/hecks/adapters/codebase/release_facts.rb +90 -0
  167. data/lib/hecks/hecks/adapters/codebase/ruby_child.rb +83 -0
  168. data/lib/hecks/hecks/adapters/codebase/secret_vault.rb +36 -0
  169. data/lib/hecks/hecks/adapters/codebase/source_tree.adapter +7 -0
  170. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +189 -0
  171. data/lib/hecks/hecks/adapters/codebase/sqlite_fixture.rb +71 -0
  172. data/lib/hecks/hecks/adapters/codebase/style.rb +171 -0
  173. data/lib/hecks/hecks/adapters/codebase/test_runner.rb +55 -0
  174. data/lib/hecks/hecks/adapters/codebase/test_suite.rb +165 -0
  175. data/lib/hecks/hecks/adapters/codebase/tree.rb +111 -0
  176. data/lib/hecks/hecks/adapters/console_capture.rb +79 -0
  177. data/lib/hecks/hecks/adapters/deploy_toolchain.adapter +7 -0
  178. data/lib/hecks/hecks/adapters/deploy_toolchain.rb +108 -0
  179. data/lib/hecks/hecks/adapters/git.adapter +7 -0
  180. data/lib/hecks/hecks/adapters/git.rb +102 -0
  181. data/lib/hecks/hecks/adapters/host_http.adapter +7 -0
  182. data/lib/hecks/hecks/adapters/host_http.rb +65 -0
  183. data/lib/hecks/hecks/adapters/in_process_boot.adapter +7 -0
  184. data/lib/hecks/hecks/adapters/in_process_boot.rb +197 -0
  185. data/lib/hecks/hecks/adapters/in_process_operations.rb +202 -0
  186. data/lib/hecks/hecks/adapters/journal_store/changes.rb +143 -0
  187. data/lib/hecks/hecks/adapters/journal_store/compaction.rb +145 -0
  188. data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +132 -0
  189. data/lib/hecks/hecks/adapters/journal_store/examination.rb +135 -0
  190. data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +163 -0
  191. data/lib/hecks/hecks/adapters/journal_store/readings.rb +187 -0
  192. data/lib/hecks/hecks/adapters/journal_store.adapter +7 -0
  193. data/lib/hecks/hecks/adapters/journal_store.rb +64 -0
  194. data/lib/hecks/hecks/adapters/local_files.adapter +7 -0
  195. data/lib/hecks/hecks/adapters/local_files.rb +46 -0
  196. data/lib/hecks/hecks/adapters/pg_admin.adapter +7 -0
  197. data/lib/hecks/hecks/adapters/pg_admin.rb +245 -0
  198. data/lib/hecks/hecks/adapters/process_pool.adapter +7 -0
  199. data/lib/hecks/hecks/adapters/process_pool.rb +119 -0
  200. data/lib/hecks/hecks/adapters/rust_toolchain.adapter +7 -0
  201. data/lib/hecks/hecks/adapters/rust_toolchain.rb +179 -0
  202. data/lib/hecks/hecks/adapters/rust_workspace.rb +150 -0
  203. data/lib/hecks/hecks/adapters/shell.adapter +7 -0
  204. data/lib/hecks/hecks/adapters/shell.rb +49 -0
  205. data/lib/hecks/hecks/adapters/terminal.adapter +7 -0
  206. data/lib/hecks/hecks/adapters/terminal.rb +74 -0
  207. data/lib/hecks/hecks/codebase.behaviors +172 -0
  208. data/lib/hecks/hecks/codebase.bluebook +2453 -0
  209. data/lib/hecks/hecks/context_map.hecksagon +13 -0
  210. data/lib/hecks/hecks/custodian.behaviors +179 -0
  211. data/lib/hecks/hecks/custodian.bluebook +1847 -0
  212. data/lib/hecks/hecks/deploy.behaviors +74 -0
  213. data/lib/hecks/hecks/deploy.world +12 -0
  214. data/lib/hecks/hecks/environments/memory.world +10 -0
  215. data/lib/hecks/hecks/governance.world +7 -0
  216. data/lib/hecks/hecks/hecks.behaviors +62 -0
  217. data/lib/hecks/hecks/hecks.bluebook +124 -0
  218. data/lib/hecks/hecks/hecks.hecksagon +390 -0
  219. data/lib/hecks/hecks/hecks.world +42 -0
  220. data/lib/hecks/kernel_capabilities.rb +194 -0
  221. data/lib/hecks/language/bluebook/bluebook.bluebook +11 -0
  222. data/lib/hecks/language/bluebook/query.bluebook +7 -0
  223. data/lib/hecks/language/bluebook/vocabulary.bluebook +4 -4
  224. data/lib/hecks/language/hecksagon/domain_port.bluebook +10 -0
  225. data/lib/hecks/language/hecksagon/hecksagon.bluebook +20 -0
  226. data/lib/hecks/mcp_stdio_guard.rb +2 -2
  227. data/lib/hecks/persistence_legacy_fixture/regenerate.rb +161 -0
  228. data/lib/hecks/persistence_legacy_fixture.rb +113 -0
  229. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +8 -5
  230. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +4 -1
  231. data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +15 -2
  232. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +1 -1
  233. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +1 -1
  234. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +1 -1
  235. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +32 -15
  236. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +15 -5
  237. data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +227 -0
  238. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +1 -1
  239. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +43 -24
  240. data/lib/hecks/ports/persistence/plugins/era/translation.rb +1 -0
  241. data/lib/hecks/ports/persistence/state_codec.rb +24 -5
  242. data/lib/hecks/projection_files.rb +285 -0
  243. data/lib/hecks/projections/bootstrap_table.rb +1 -1
  244. data/lib/hecks/projections/deploy/fargate.rb +5 -5
  245. data/lib/hecks/projections/deploy/lambda.rb +43 -32
  246. data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
  247. data/lib/hecks/projections/deploy/preview/template.rb +1 -1
  248. data/lib/hecks/projections/deploy/scripts/deploy-service.sh.tmpl +1 -1
  249. data/lib/hecks/projections/deploy/scripts/expected-era.header +1 -1
  250. data/lib/hecks/projections/deploy/scripts/hosting.mk.tmpl +3 -3
  251. data/lib/hecks/projections/deploy/scripts/smoke-after-deploy.sh.tmpl +1 -1
  252. data/lib/hecks/projections/deploy/shared.rb +1 -1
  253. data/lib/hecks/projections/diagrams.rb +3 -3
  254. data/lib/hecks/projections/model/deviations.rb +1 -1
  255. data/lib/hecks/projections/parser_table.rb +4 -4
  256. data/lib/hecks/projections/reference.rb +2 -3
  257. data/lib/hecks/projections/rust_vocabulary.rb +1 -1
  258. data/lib/hecks/projector/cli_projector.rb +106 -37
  259. data/lib/hecks/projector.rb +1 -1
  260. data/lib/hecks/quality_control/adapters/agent.adapter +7 -0
  261. data/lib/hecks/quality_control/adapters/agent.rb +58 -0
  262. data/lib/hecks/quality_control/adapters/angle_tools.adapter +7 -0
  263. data/lib/hecks/quality_control/adapters/angle_tools.rb +15 -0
  264. data/lib/hecks/quality_control/adapters/clearance_recorder.rb +36 -0
  265. data/lib/hecks/quality_control/adapters/clearance_tools.adapter +7 -0
  266. data/lib/hecks/quality_control/adapters/clearance_tools.rb +17 -0
  267. data/lib/hecks/quality_control/adapters/git_pr.adapter +7 -0
  268. data/lib/hecks/quality_control/adapters/git_pr.rb +226 -0
  269. data/lib/hecks/quality_control/adapters/github_checks.adapter +9 -0
  270. data/lib/hecks/quality_control/adapters/github_checks.rb +83 -0
  271. data/lib/hecks/quality_control/adapters/github_ci_webhook.rb +56 -0
  272. data/lib/hecks/quality_control/adapters/github_issues.adapter +7 -0
  273. data/lib/hecks/quality_control/adapters/github_issues.rb +49 -0
  274. data/lib/hecks/quality_control/adapters/qa_tool.rb +59 -0
  275. data/lib/hecks/quality_control/adapters/sweep_tools.adapter +7 -0
  276. data/lib/hecks/quality_control/adapters/sweep_tools.rb +51 -0
  277. data/lib/hecks/quality_control/adapters/target_tools.adapter +7 -0
  278. data/lib/hecks/quality_control/adapters/target_tools.rb +43 -0
  279. data/lib/hecks/quality_control/cli/child.rb +45 -0
  280. data/lib/hecks/quality_control/cli/qa_concurrency_racer.rb +45 -0
  281. data/lib/hecks/quality_control/cli/qa_discover_external_domains.rb +248 -0
  282. data/lib/hecks/quality_control/cli/qa_domain_novelty.rb +163 -0
  283. data/lib/hecks/quality_control/cli/qa_generated_domains.rb +450 -0
  284. data/lib/hecks/quality_control/cli/qa_log_bug.rb +195 -0
  285. data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +220 -0
  286. data/lib/hecks/quality_control/cli/qa_open_pr.rb +305 -0
  287. data/lib/hecks/quality_control/cli/qa_postgres_migrate.rb +154 -0
  288. data/lib/hecks/quality_control/cli/qa_postgres_role.rb +159 -0
  289. data/lib/hecks/quality_control/cli/qa_pr_check.rb +232 -0
  290. data/lib/hecks/quality_control/cli/qa_seed_angles.rb +142 -0
  291. data/lib/hecks/quality_control/cli/qa_seed_targets.rb +69 -0
  292. data/lib/hecks/quality_control/cli/qa_sweep/all_mode.rb +280 -0
  293. data/lib/hecks/quality_control/cli/qa_sweep/checks.rb +329 -0
  294. data/lib/hecks/quality_control/cli/qa_sweep/dials.rb +92 -0
  295. data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +117 -0
  296. data/lib/hecks/quality_control/cli/qa_sweep/parsing.rb +175 -0
  297. data/lib/hecks/quality_control/cli/qa_sweep/release_mode.rb +60 -0
  298. data/lib/hecks/quality_control/cli/qa_sweep.rb +611 -0
  299. data/lib/hecks/quality_control/cli/qa_tick.rb +274 -0
  300. data/lib/hecks/quality_control/quality_control.bluebook +2224 -0
  301. data/lib/hecks/quality_control/quality_control.hecksagon +13 -0
  302. data/lib/hecks/quality_control/quality_control.ports.hecksagon +54 -0
  303. data/lib/hecks/query_ir.rb +346 -0
  304. data/lib/hecks/query_ir_mcp.rb +198 -0
  305. data/lib/hecks/release/runner/ci_publisher.rb +2 -2
  306. data/lib/hecks/release/runner/clean_tree.rb +42 -0
  307. data/lib/hecks/release/runner/console.rb +6 -0
  308. data/lib/hecks/release/runner/gem_publisher.rb +14 -7
  309. data/lib/hecks/release/runner/npm_publisher.rb +6 -36
  310. data/lib/hecks/release/runner/preflight.rb +26 -2
  311. data/lib/hecks/release/runner/published.rb +8 -26
  312. data/lib/hecks/release/runner/tagger.rb +30 -15
  313. data/lib/hecks/release/runner.rb +57 -6
  314. data/lib/hecks/router/namespace_installer.rb +3 -3
  315. data/lib/hecks/runtime/adapter_lookup.rb +124 -0
  316. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +4 -0
  317. data/lib/hecks/runtime/dependency_planning.rb +20 -2
  318. data/lib/hecks/runtime/dispatcher.rb +53 -3
  319. data/lib/hecks/runtime/interpreting.rb +3 -0
  320. data/lib/hecks/runtime/loader.rb +65 -12
  321. data/lib/hecks/runtime/outbox.rb +1 -1
  322. data/lib/hecks/runtime/policy_interpreter.rb +5 -2
  323. data/lib/hecks/runtime/port_operation_interpreter.rb +2 -10
  324. data/lib/hecks/runtime/query_interpreter.rb +77 -0
  325. data/lib/hecks/runtime/reaction_outcome.rb +89 -0
  326. data/lib/hecks/runtime/refusal_wording.rb +23 -1
  327. data/lib/hecks/runtime/registry/verification.rb +163 -4
  328. data/lib/hecks/runtime/registry.rb +57 -3
  329. data/lib/hecks/runtime/remote_dispatcher.rb +31 -2
  330. data/lib/hecks/runtime/value/coercion.rb +23 -135
  331. data/lib/hecks/runtime/value/field_checks.rb +172 -0
  332. data/lib/hecks/runtime/value.rb +4 -1
  333. data/lib/hecks/runtime.rb +20 -6
  334. data/lib/hecks/rust_build/conformance.rb +158 -0
  335. data/lib/hecks/rust_build/conformance_fuzz.rb +71 -0
  336. data/lib/hecks/rust_build/coverage.rb +218 -0
  337. data/lib/hecks/rust_build/native_build.rb +124 -0
  338. data/lib/hecks/rust_build/project_rust/root_mod.rb +120 -0
  339. data/lib/hecks/rust_build/project_rust.rb +332 -0
  340. data/lib/hecks/rust_build/wasm.rb +105 -0
  341. data/lib/hecks/rust_build/wasm_browser.rb +113 -0
  342. data/lib/hecks/rust_build.rb +144 -0
  343. data/lib/hecks/storehouse.rb +16 -16
  344. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +2 -2
  345. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +1 -1
  346. data/lib/hecks/three_zero/forms.yml +127 -0
  347. data/lib/hecks/three_zero.rb +12 -0
  348. data/lib/hecks/tools/argument_gate_matrix.rb +316 -0
  349. data/lib/hecks/tools/comment_style.rb +1070 -0
  350. data/lib/hecks/tools/corpus_report.rb +87 -0
  351. data/lib/hecks/tools/deploy_recipe.rb +255 -0
  352. data/lib/hecks/tools/deploy_recipe_lint.rb +282 -0
  353. data/lib/hecks/tools/drop_implicit_append_fields.rb +95 -0
  354. data/lib/hecks/tools/era_projection_backfill.rb +94 -0
  355. data/lib/hecks/tools/era_reattest.rb +146 -0
  356. data/lib/hecks/tools/evolve_run.rb +189 -0
  357. data/lib/hecks/tools/fuzz_sweep.rb +373 -0
  358. data/lib/hecks/tools/heki_compaction.rb +100 -0
  359. data/lib/hecks/tools/hoist_local_givens.rb +246 -0
  360. data/lib/hecks/tools/journal_compaction.rb +89 -0
  361. data/lib/hecks/tools/oidc_manifests.rb +70 -0
  362. data/lib/hecks/tools/query_ir_run.rb +81 -0
  363. data/lib/hecks/tools/regeneration_run.rb +166 -0
  364. data/lib/hecks/tools/rust_comment_style.rb +642 -0
  365. data/lib/hecks/tools/sequence_script.rb +62 -0
  366. data/lib/hecks/tools/tail_merge.rb +117 -0
  367. data/lib/hecks/tools/tenant_provisioning.rb +142 -0
  368. data/lib/hecks/tools/translation_audit.rb +208 -0
  369. data/lib/hecks/tools/translation_scaffold.rb +131 -0
  370. data/lib/hecks/tools.rb +86 -0
  371. data/lib/hecks/version.rb +1 -1
  372. data/lib/hecks.rb +32 -15
  373. data/qa/settings.yml +66 -0
  374. data/rust/.cargo/config.toml +6 -0
  375. data/rust/.gitignore +2 -0
  376. data/rust/Cargo.lock +54 -0
  377. data/rust/Cargo.toml +61 -0
  378. data/rust/build/.cargo/config.toml +3 -0
  379. data/rust/build/.gitignore +1 -0
  380. data/rust/build/Cargo.lock +7 -0
  381. data/rust/build/Cargo.toml +19 -0
  382. data/rust/build/src/build_artifact.rs +47 -0
  383. data/rust/build/src/cargo_sync.rs +248 -0
  384. data/rust/build/src/json.rs +421 -0
  385. data/rust/build/src/lineage_pass.rs +285 -0
  386. data/rust/build/src/main.rs +51 -0
  387. data/rust/build/src/optional_pass.rs +150 -0
  388. data/rust/build/src/pipeline.rs +264 -0
  389. data/rust/build/src/reserved_names.rs +76 -0
  390. data/rust/build/src/resolve.rs +124 -0
  391. data/rust/build/src/root.rs +25 -0
  392. data/rust/build/src/sidecars.rs +73 -0
  393. data/rust/build/src/subprocess.rs +51 -0
  394. data/rust/build/src/tmp.rs +31 -0
  395. data/rust/codegen/.cargo/config.toml +3 -0
  396. data/rust/codegen/.gitignore +1 -0
  397. data/rust/codegen/Cargo.lock +7 -0
  398. data/rust/codegen/Cargo.toml +20 -0
  399. data/rust/codegen/src/attr.rs +32 -0
  400. data/rust/codegen/src/bridging.rs +481 -0
  401. data/rust/codegen/src/commands.rs +1226 -0
  402. data/rust/codegen/src/constraints.rs +109 -0
  403. data/rust/codegen/src/dependency_planning.rs +209 -0
  404. data/rust/codegen/src/domain_generator.rs +1288 -0
  405. data/rust/codegen/src/exemplar.rs +230 -0
  406. data/rust/codegen/src/expr_emitter.rs +87 -0
  407. data/rust/codegen/src/fielded.rs +192 -0
  408. data/rust/codegen/src/hecks_naming.rs +33 -0
  409. data/rust/codegen/src/json.rs +335 -0
  410. data/rust/codegen/src/json_codec.rs +697 -0
  411. data/rust/codegen/src/literal.rs +207 -0
  412. data/rust/codegen/src/main.rs +290 -0
  413. data/rust/codegen/src/manifest.rs +227 -0
  414. data/rust/codegen/src/mutations.rs +784 -0
  415. data/rust/codegen/src/naming.rs +286 -0
  416. data/rust/codegen/src/ports.rs +208 -0
  417. data/rust/codegen/src/prelude.rs +116 -0
  418. data/rust/codegen/src/queries.rs +786 -0
  419. data/rust/codegen/src/reactions.rs +532 -0
  420. data/rust/codegen/src/read_models.rs +697 -0
  421. data/rust/codegen/src/reference_specs.rs +40 -0
  422. data/rust/codegen/src/registry.rs +966 -0
  423. data/rust/codegen/src/reserved_names.rs +76 -0
  424. data/rust/codegen/src/shared.rs +93 -0
  425. data/rust/codegen/src/skip_reason.rs +25 -0
  426. data/rust/codegen/src/types.rs +362 -0
  427. data/rust/host/.cargo/config.toml +3 -0
  428. data/rust/host/.gitignore +1 -0
  429. data/rust/host/Cargo.lock +4297 -0
  430. data/rust/host/Cargo.toml +61 -0
  431. data/rust/host/HECKS_RELEASE +1 -0
  432. data/rust/host/rds-ca-bundle.pem +2736 -0
  433. data/rust/host/src/api.rs +1596 -0
  434. data/rust/host/src/approval.rs +652 -0
  435. data/rust/host/src/auth.rs +1287 -0
  436. data/rust/host/src/bin/expr_harness.rs +46 -0
  437. data/rust/host/src/bin/lineage_harness.rs +119 -0
  438. data/rust/host/src/bin/mint_harness.rs +135 -0
  439. data/rust/host/src/checkout.rs +265 -0
  440. data/rust/host/src/dispatch.rs +1443 -0
  441. data/rust/host/src/expr_json.rs +1117 -0
  442. data/rust/host/src/field_hints.rs +52 -0
  443. data/rust/host/src/ir.rs +733 -0
  444. data/rust/host/src/journal.rs +874 -0
  445. data/rust/host/src/lambda_client.rs +378 -0
  446. data/rust/host/src/log.rs +147 -0
  447. data/rust/host/src/main.rs +474 -0
  448. data/rust/host/src/mint.rs +1865 -0
  449. data/rust/host/src/payments/keystore.rs +330 -0
  450. data/rust/host/src/payments/tests.rs +1400 -0
  451. data/rust/host/src/payments.rs +767 -0
  452. data/rust/host/src/presentation.rs +698 -0
  453. data/rust/host/src/presentation_write.rs +1431 -0
  454. data/rust/host/src/query_step.rs +149 -0
  455. data/rust/host/src/rate_limit.rs +904 -0
  456. data/rust/host/src/reference_transform.rs +241 -0
  457. data/rust/host/src/reference_validate.rs +338 -0
  458. data/rust/host/src/resend.rs +296 -0
  459. data/rust/host/src/secrets.rs +106 -0
  460. data/rust/host/src/server.rs +663 -0
  461. data/rust/host/src/storage_shape.rs +175 -0
  462. data/rust/host/src/test_pg.rs +40 -0
  463. data/rust/host/src/ui_schema.rs +1351 -0
  464. data/rust/host/src/wasm_runner.rs +191 -0
  465. data/rust/host/src/web/newsletter/cooldown.rs +126 -0
  466. data/rust/host/src/web/newsletter/tests.rs +104 -0
  467. data/rust/host/src/web/newsletter.rs +310 -0
  468. data/rust/host/src/web/newsletter_send.rs +357 -0
  469. data/rust/host/src/web/registrations.rs +1505 -0
  470. data/rust/host/src/web.rs +2937 -0
  471. data/rust/lsp/.gitignore +1 -0
  472. data/rust/lsp/Cargo.lock +7 -0
  473. data/rust/lsp/Cargo.toml +16 -0
  474. data/rust/lsp/README.md +133 -0
  475. data/rust/lsp/src/diagnostics.rs +165 -0
  476. data/rust/lsp/src/json.rs +385 -0
  477. data/rust/lsp/src/main.rs +399 -0
  478. data/rust/lsp/src/outline.rs +177 -0
  479. data/rust/lsp/src/rpc.rs +67 -0
  480. data/rust/parser/.cargo/config.toml +5 -0
  481. data/rust/parser/.gitignore +1 -0
  482. data/rust/parser/Cargo.lock +7 -0
  483. data/rust/parser/Cargo.toml +21 -0
  484. data/rust/parser/src/build/closed_sets.rs +27 -0
  485. data/rust/parser/src/build/identity.rs +276 -0
  486. data/rust/parser/src/build/mod.rs +12 -0
  487. data/rust/parser/src/build/naming.rs +141 -0
  488. data/rust/parser/src/build/pattern_subset.rs +161 -0
  489. data/rust/parser/src/build/query_derive.rs +72 -0
  490. data/rust/parser/src/build/query_inference.rs +269 -0
  491. data/rust/parser/src/build/query_options.rs +100 -0
  492. data/rust/parser/src/build/read_model.rs +94 -0
  493. data/rust/parser/src/build/references.rs +32 -0
  494. data/rust/parser/src/canonical.rs +114 -0
  495. data/rust/parser/src/diag.rs +55 -0
  496. data/rust/parser/src/emit.rs +953 -0
  497. data/rust/parser/src/expr/ast_json.rs +147 -0
  498. data/rust/parser/src/expr/evaluator.rs +81 -0
  499. data/rust/parser/src/expr/mod.rs +84 -0
  500. data/rust/parser/src/expr/resolver.rs +497 -0
  501. data/rust/parser/src/ir.rs +387 -0
  502. data/rust/parser/src/keywords.rs +585 -0
  503. data/rust/parser/src/lex.rs +662 -0
  504. data/rust/parser/src/main.rs +252 -0
  505. data/rust/parser/src/parse/aggregate.rs +612 -0
  506. data/rust/parser/src/parse/chapter.rs +517 -0
  507. data/rust/parser/src/parse/command.rs +637 -0
  508. data/rust/parser/src/parse/domain_port.rs +127 -0
  509. data/rust/parser/src/parse/entity.rs +527 -0
  510. data/rust/parser/src/parse/file.rs +67 -0
  511. data/rust/parser/src/parse/hecksagon.rs +231 -0
  512. data/rust/parser/src/parse/lifecycle.rs +162 -0
  513. data/rust/parser/src/parse/mod.rs +1262 -0
  514. data/rust/parser/src/parse/policy.rs +87 -0
  515. data/rust/parser/src/parse/process_manager.rs +305 -0
  516. data/rust/parser/src/parse/query.rs +100 -0
  517. data/rust/parser/src/parse/read_model.rs +214 -0
  518. data/rust/parser/src/parse/value_object.rs +250 -0
  519. data/rust/parser/src/ruby_value.rs +422 -0
  520. data/rust/project/bridging.rb +257 -0
  521. data/rust/project/commands.rb +877 -0
  522. data/rust/project/constraints.rb +86 -0
  523. data/rust/project/dependency_planning.rb +178 -0
  524. data/rust/project/domain_generator.rb +980 -0
  525. data/rust/project/exemplar.rb +148 -0
  526. data/rust/project/expr_emitter.rb +72 -0
  527. data/rust/project/fielded.rb +141 -0
  528. data/rust/project/json_codec.rb +641 -0
  529. data/rust/project/mutations.rb +721 -0
  530. data/rust/project/naming.rb +205 -0
  531. data/rust/project/ports.rb +65 -0
  532. data/rust/project/queries.rb +493 -0
  533. data/rust/project/reactions.rb +317 -0
  534. data/rust/project/read_models.rb +690 -0
  535. data/rust/project/reference_specs.rb +27 -0
  536. data/rust/project/registry.rb +396 -0
  537. data/rust/project/skip_reason.rb +29 -0
  538. data/rust/project/types.rb +225 -0
  539. data/rust/project/write_if_changed.rb +83 -0
  540. data/rust/project.rb +30 -0
  541. data/rust/project_rust_pipeline.rb +383 -0
  542. data/rust/src/exemplar/commands.rs +279 -0
  543. data/rust/src/exemplar/constraints.rs +18 -0
  544. data/rust/src/exemplar/fielded.rs +280 -0
  545. data/rust/src/exemplar/json.rs +277 -0
  546. data/rust/src/exemplar/mod.rs +17 -0
  547. data/rust/src/exemplar/mutations.rs +80 -0
  548. data/rust/src/exemplar/queries.rs +33 -0
  549. data/rust/src/exemplar/reactions.rs +88 -0
  550. data/rust/src/exemplar/read_models.rs +48 -0
  551. data/rust/src/exemplar/registry.rs +166 -0
  552. data/rust/src/exemplar/types.rs +44 -0
  553. data/rust/src/kernel/attribute_shapes/composite.rs +88 -0
  554. data/rust/src/kernel/attribute_shapes/list.rs +18 -0
  555. data/rust/src/kernel/attribute_shapes/mod.rs +44 -0
  556. data/rust/src/kernel/attribute_shapes/optional.rs +9 -0
  557. data/rust/src/kernel/attribute_shapes/scalar.rs +39 -0
  558. data/rust/src/kernel/cli.rs +585 -0
  559. data/rust/src/kernel/dispatch.rs +1090 -0
  560. data/rust/src/kernel/expr.rs +458 -0
  561. data/rust/src/kernel/expression_operators/arithmetic.rs +128 -0
  562. data/rust/src/kernel/expression_operators/comparison.rs +48 -0
  563. data/rust/src/kernel/expression_operators/enumeration.rs +232 -0
  564. data/rust/src/kernel/expression_operators/logical.rs +13 -0
  565. data/rust/src/kernel/expression_operators/membership.rs +18 -0
  566. data/rust/src/kernel/expression_operators/mod.rs +68 -0
  567. data/rust/src/kernel/expression_operators/pattern_match.rs +73 -0
  568. data/rust/src/kernel/expression_operators/positional.rs +126 -0
  569. data/rust/src/kernel/expression_operators/presence.rs +101 -0
  570. data/rust/src/kernel/expression_operators/sign_test.rs +16 -0
  571. data/rust/src/kernel/expression_operators/sized.rs +35 -0
  572. data/rust/src/kernel/expression_operators/text.rs +79 -0
  573. data/rust/src/kernel/expression_operators/to_string.rs +18 -0
  574. data/rust/src/kernel/json.rs +779 -0
  575. data/rust/src/kernel/mod.rs +133 -0
  576. data/rust/src/kernel/named_query.rs +219 -0
  577. data/rust/src/kernel/orchestrate.rs +1810 -0
  578. data/rust/src/kernel/pattern.rs +307 -0
  579. data/rust/src/kernel/query_comparators.rs +206 -0
  580. data/rust/src/kernel/query_ordering.rs +103 -0
  581. data/rust/src/kernel/read_model.rs +914 -0
  582. data/rust/src/kernel/reference_lookup.rs +149 -0
  583. data/rust/src/kernel/repository.rs +468 -0
  584. data/rust/src/kernel/routing.rs +612 -0
  585. data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +118 -0
  586. data/rust/src/kernel/vocab/entity_dispatch_order.rs +114 -0
  587. data/rust/src/kernel/vocab/field_hint.rs +70 -0
  588. data/rust/src/kernel/vocab/mod.rs +20 -0
  589. data/rust/src/kernel/vocab/query_comparator.rs +65 -0
  590. data/rust/src/kernel/vocab/refusal_template.rs +1592 -0
  591. data/rust/src/lib.rs +8 -0
  592. data/rust/src/main.rs +16 -0
  593. data/rust/web/.gitignore +1 -0
  594. data/rust/web/Cargo.lock +119 -0
  595. data/rust/web/Cargo.toml +18 -0
  596. data/rust/web/src/lib.rs +8 -0
  597. metadata +477 -12
  598. data/lib/hecks/facade/cli_door.rb +0 -96
  599. data/lib/hecks/facade/cli_runner.rb +0 -127
  600. data/lib/hecks/mcp_door_scope.rb +0 -95
@@ -0,0 +1,332 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "json"
5
+ require_relative "../../hecks"
6
+ require_relative "../bluebook/meta_validator"
7
+ require_relative "../ports/persistence/plugins/era"
8
+ require_relative "../../../rust/project"
9
+
10
+ module Hecks
11
+ module RustBuild
12
+ # Generates Rust source for one domain into the workspace's `src/generated/`, and keeps the
13
+ # workspace's `Cargo.toml` features in step; `rust/project.rb` (`RustProjection`) holds the
14
+ # architecture of the generator itself.
15
+ #
16
+ # ProjectRust.call(["path/to/domain"])
17
+ #
18
+ # The era plugin is required unconditionally so the generator works for any domain, lineage
19
+ # capable or not; `pg` stays lazy inside it, so nothing here opens a database connection.
20
+ # With `HECKS_PARSER=rust` and `HECKS_CODEGEN=rust` together, the whole pipeline runs through
21
+ # `rust/project_rust_pipeline.rb` instead, with no Ruby load of the domain.
22
+ class ProjectRust
23
+ # The optional attachments `rust/host` reads from `ir.json`, each omitted when nothing
24
+ # attached provides it: exporter method, then the key it is written under.
25
+ SEAMS = %i[authorization membership identity newsletter newsletter_issues payments
26
+ registrations payment_connection].freeze
27
+
28
+ # @param argv [Array<String>] the domain's directory
29
+ # @return [Integer] the exit status
30
+ # @raise [Failure] when the domain's name cannot be a Rust module and Cargo feature name
31
+ def self.call(argv)
32
+ domain = argv.first or raise Failure, "usage: hecks project_rust <domain>"
33
+ new(domain).call
34
+ end
35
+
36
+ # @param domain [String] the domain's directory
37
+ def initialize(domain)
38
+ @domain = domain
39
+ @mod_name = File.basename(domain)
40
+ end
41
+
42
+ # @return [Integer] the exit status
43
+ def call
44
+ validate_name!
45
+ if ENV["HECKS_PARSER"] == "rust" && ENV["HECKS_CODEGEN"] == "rust"
46
+ require_relative "../../../rust/project_rust_pipeline"
47
+ RustProjectPipeline.call(@domain)
48
+ else
49
+ generate
50
+ end
51
+ 0
52
+ end
53
+
54
+ private
55
+
56
+ # The name becomes a directory, a `pub mod` identifier and a Cargo feature, so it is checked
57
+ # before any side effect (the `meta/` directory is rewritten below).
58
+ def validate_name!
59
+ return if RustProjection::Projector.valid_domain_mod_name?(@mod_name)
60
+
61
+ raise Failure, "hecks project_rust: domain name #{@mod_name.inspect} (from #{@domain.inspect}) can't " \
62
+ "be used as-is — it has to double as a Rust module identifier and a Cargo feature " \
63
+ "name, and this one is either not a plain lowercase identifier, is a Rust keyword, " \
64
+ "or collides with a reserved Cargo.toml key " \
65
+ "(#{RustProjection::Projector::CARGO_RESERVED_DOMAIN_NAMES.join(', ')}). " \
66
+ "Rename the domain directory."
67
+ end
68
+
69
+ def generate
70
+ load_registry
71
+ @rust_dir = ENV.fetch("HECKS_RUST_DIR", File.join(RustBuild::ROOT, "rust"))
72
+ @out_root = File.join(@rust_dir, "src/generated")
73
+ FileUtils.mkdir_p(@out_root)
74
+ build_target_ir
75
+ write_meta
76
+ write_target
77
+ write_root_mod
78
+ sync_cargo_features
79
+ end
80
+
81
+ # Round-trips a value through JSON so generators see the string-keyed shape a real `ir.json`
82
+ # carries, never live Ruby symbols.
83
+ def json_shaped(payload) = JSON.parse(JSON.generate(payload), symbolize_names: true)
84
+
85
+ # `root:` is the domain's own directory: a domain that declares `uses_embryonaut_bluebook`
86
+ # resolves its vendored chapters beneath it.
87
+ def load_registry
88
+ @registry = Hecks::Runtime::Registry.new(root: File.expand_path(@domain))
89
+ Hecks.with_registry(@registry) do
90
+ lib = File.expand_path("..", __dir__)
91
+ %w[ports/persistence.port ports/extraction.port adapters/driven/memory.adapter
92
+ adapters/driven/prism.adapter adapters/driven/postgres_era.adapter].each do |file|
93
+ Kernel.load(File.join(lib, file))
94
+ end
95
+ @bluebook_dir = File.join(@domain, "bluebook")
96
+ Hecks::Adapters::Folder.new.load_bluebooks(@bluebook_dir)
97
+ load_siblings
98
+ load_environment
99
+ end
100
+ @domain_name = @registry.bluebooks.keys.first
101
+ end
102
+
103
+ # Translation edges, hecksagons (whose `uses_framework` loads a framework chapter into the
104
+ # same registry) and worlds (whose `default_adapter` binds what a hecksagon leaves out), in
105
+ # the order the language loads them.
106
+ def load_siblings
107
+ Dir[File.join(@bluebook_dir, "translations", "*.bluebook")].each { |file| Kernel.load(file) }
108
+ Dir.glob(File.join(@bluebook_dir, "*.hecksagon")).each { |file| Kernel.load(file) }
109
+ Dir.glob(File.join(@bluebook_dir, "*.world")).each { |file| Kernel.load(file) }
110
+ end
111
+
112
+ # Production is the default environment when `environments/production.hecksagon` exists
113
+ # (`rust/host` is the production runtime); `HECKS_PROJECT_ENVIRONMENT` overrides it.
114
+ def load_environment
115
+ environment = ENV.fetch("HECKS_PROJECT_ENVIRONMENT") do
116
+ File.exist?(File.join(@bluebook_dir, "environments", "production.hecksagon")) ? "production" : nil
117
+ end
118
+ return if environment.nil? || environment.empty?
119
+
120
+ folder = Hecks::Adapters::Folder.new
121
+ folder.load_each(@bluebook_dir, [File.join("environments", "#{environment}.hecksagon")])
122
+ folder.load_each(@bluebook_dir, [File.join("environments", "#{environment}.world")])
123
+ end
124
+
125
+ # The target's IR, with the binding facts that ride beside it (they are not shape facts, so
126
+ # `bluebook.to_h` never mentions them).
127
+ def build_target_ir
128
+ exporter = Hecks::Projector::Exporter
129
+ @ir = json_shaped(exporter.call(@registry).fetch(@domain_name))
130
+ @ir[:lineage] = json_shaped(exporter.lineage(@registry, @domain_name))
131
+ @ir[:persistence] = json_shaped(exporter.persistence(@registry, @domain_name))
132
+ seams = SEAMS.to_h { |seam| [seam, exporter.public_send(seam, @registry, @domain_name)] }
133
+ seams.each { |seam, value| @ir[seam] = json_shaped(value) unless value.empty? }
134
+ if !seams[:membership].empty? && seams[:identity].empty?
135
+ raise Failure, "#{@domain_name} provides membership but not identity — rust/host Google sign-in " \
136
+ "cannot register or link an identity from ir.json. Attach Identity " \
137
+ "(`uses_framework \"Identity\"` plus a sibling Hecks.hecksagon \"Identity\") so the " \
138
+ "hecksagon, not a deploy-time guess, names the identity verbs."
139
+ end
140
+ add_edges_and_source
141
+ end
142
+
143
+ # Edges carry their own precompiled SQL, so a boot-time mint only executes it. Committed
144
+ # approvals sit beside the edges. The source text is verbatim: the era's integrity digest is
145
+ # the SHA256 of it, not of anything re-derived from the parsed IR.
146
+ def add_edges_and_source
147
+ edges = Hecks::Projector::Exporter.translations(@registry).select { |edge| edge[:domain] == @domain_name }
148
+ @ir[:translations] = json_shaped(edges)
149
+ approvals = Hecks::Translation::ApprovalFile.read_all(@bluebook_dir)
150
+ @ir[:approvals] = json_shaped(approvals) unless approvals.empty?
151
+ @ir[:source_text] = Hecks::Runtime::EraCheck.source_text_for(
152
+ @registry.bluebooks.fetch(@domain_name), @bluebook_dir, registry: @registry
153
+ )
154
+ end
155
+
156
+ # The self-hosted language goes through the same codegen as the target, read from the
157
+ # grammar registry `MetaValidator` boots with.
158
+ def write_meta
159
+ meta_ir = json_shaped(
160
+ Hecks::Projector::Exporter.call(Hecks::Bluebook::MetaValidator.grammar_registry).fetch("Bluebook")
161
+ )
162
+ dir = File.join(@out_root, "meta")
163
+ # Tracked per directory: several domains coexist on disk, and `pop_and_prune` deletes only
164
+ # the files this run never touched. `active` is a set of feature re-exports in the root
165
+ # `mod.rs`, never a directory.
166
+ RustProjection::WriteIfChanged.push_directory(dir)
167
+ FileUtils.rm_rf(File.join(@out_root, "active"))
168
+ result = RustProjection::DomainGenerator.call(
169
+ meta_ir, "the self-hosted language (lib/hecks/language/bluebook)", dir, "meta"
170
+ )
171
+ write_merged(File.join(dir, "merged.rs"), result[:aggregates], result[:queries], result[:read_models],
172
+ policies: RustProjection::Projector.emit_policy_table(meta_ir[:name], meta_ir[:policies],
173
+ meta_ir[:aggregates]),
174
+ cross: RustProjection::Projector.emit_cross_domain_policy_table(meta_ir[:name],
175
+ meta_ir[:policies]),
176
+ managers: meta_ir[:process_managers],
177
+ keys: [[meta_ir[:name], result[:aggregates].map { |a| a[:name] }]])
178
+ append_merged_mod(File.join(dir, "mod.rs"))
179
+ RustProjection::WriteIfChanged.pop_and_prune(dir)
180
+ end
181
+
182
+ def write_target
183
+ dir = File.join(@out_root, @mod_name)
184
+ RustProjection::WriteIfChanged.push_directory(dir)
185
+ result = RustProjection::DomainGenerator.call(@ir, @domain, dir, @mod_name)
186
+ chapters = write_chapters
187
+ aggregates = result[:aggregates] + chapters[:aggregates]
188
+ write_merged(File.join(dir, "merged.rs"), aggregates, result[:queries] + chapters[:queries],
189
+ result[:read_models] + chapters[:read_models],
190
+ policies: RustProjection::Projector.emit_merged_policy_table(chapters[:policy_sources]),
191
+ cross: RustProjection::Projector.emit_merged_cross_domain_policy_table(chapters[:policy_sources]),
192
+ managers: @ir[:process_managers] + chapters[:process_managers],
193
+ keys: reference_keys(result[:aggregates], chapters))
194
+ append_merged_mod(File.join(dir, "mod.rs"))
195
+ RustProjection::WriteIfChanged.pop_and_prune(dir)
196
+ end
197
+
198
+ # Every other bluebook the target's `uses_framework` calls pulled into the registry, each
199
+ # generated into its own module. A framework chapter is a function of its bluebook alone, so
200
+ # regenerating one is safe whichever domain last touched it.
201
+ def write_chapters
202
+ totals = { aggregates: [], queries: [], read_models: [], process_managers: [],
203
+ policy_sources: [{ domain_name: @domain_name, policies: @ir[:policies],
204
+ aggregates: @ir[:aggregates] }],
205
+ mod_names: { @mod_name => @domain_name } }
206
+ vendored = @registry.hecksagons.values.flat_map(&:vendored_bluebooks)
207
+ (@registry.bluebooks.keys - [@domain_name]).each { |chapter| write_chapter(chapter, vendored, totals) }
208
+ totals
209
+ end
210
+
211
+ def write_chapter(chapter_name, vendored, totals)
212
+ mod_name = chapter_name.downcase
213
+ totals[:mod_names][mod_name] = chapter_name
214
+ chapter_ir = json_shaped(Hecks::Projector::Exporter.call(@registry).fetch(chapter_name))
215
+ result = nil
216
+ RustProjection::WriteIfChanged.track_directory(File.join(@out_root, mod_name)) do
217
+ result = RustProjection::DomainGenerator.call(chapter_ir, "#{@domain} (#{attachment(chapter_name, vendored)})",
218
+ File.join(@out_root, mod_name), mod_name,
219
+ merged_module: false)
220
+ end
221
+ totals[:aggregates].concat(result[:aggregates])
222
+ totals[:queries].concat(result[:queries])
223
+ totals[:read_models].concat(result[:read_models])
224
+ totals[:process_managers].concat(chapter_ir[:process_managers])
225
+ totals[:policy_sources] << { domain_name: chapter_name, policies: chapter_ir[:policies],
226
+ aggregates: chapter_ir[:aggregates] }
227
+ end
228
+
229
+ # How the target attached a chapter, across every hecksagon the registry loaded: a chapter a
230
+ # sibling hecksagon vendors in is labelled as vendored.
231
+ def attachment(chapter_name, vendored)
232
+ name = vendored.find { |candidate| Hecks::Naming.pascal(candidate.to_s) == chapter_name }
233
+ name ? "uses_embryonaut_bluebook #{name.inspect}" : "uses_framework #{chapter_name.inspect}"
234
+ end
235
+
236
+ def reference_keys(target_aggregates, chapters)
237
+ chapters[:mod_names].map do |mod_name, chapter_name|
238
+ own = mod_name == @mod_name ? target_aggregates : chapters[:aggregates].select { |a| a[:chapter_mod] == mod_name }
239
+ [chapter_name, own.map { |a| a[:name] }]
240
+ end
241
+ end
242
+
243
+ # Writes a module's `merged.rs`: the registry and every dispatch table over its aggregates.
244
+ def write_merged(path, aggregates, queries, read_models, policies:, cross:, managers:, keys:)
245
+ projector = RustProjection::Projector
246
+ wrote = RustProjection::WriteIfChanged.block(path) do |file|
247
+ [projector.emit_registry(aggregates), projector.emit_reference_lookup(aggregates), policies, cross,
248
+ projector.emit_process_manager_table(managers), projector.emit_reference_key_table(keys),
249
+ projector.emit_creates_table(aggregates), projector.emit_identity_head_table(aggregates),
250
+ projector.emit_entity_identity_head_table(aggregates),
251
+ projector.emit_command_attributes_table(aggregates), projector.emit_query_table(queries),
252
+ projector.emit_query_arg_check_table(queries)].each do |table|
253
+ file.puts table
254
+ file.puts
255
+ end
256
+ # A read model's `group_by` function is written before the table names it.
257
+ read_models.each do |model|
258
+ next unless model[:group_by_fn_body]
259
+
260
+ file.puts model[:group_by_fn_body]
261
+ file.puts
262
+ end
263
+ file.puts projector.emit_read_model_table(read_models)
264
+ end
265
+ puts(wrote ? "wrote #{path}" : "#{path} unchanged")
266
+ end
267
+
268
+ # Appends the `pub mod merged;` line once; appending it again would grow the file and bump
269
+ # its mtime on every run.
270
+ def append_merged_mod(path)
271
+ return if File.exist?(path) && File.read(path).include?("pub mod merged;")
272
+
273
+ File.open(path, "a") { |file| file.puts "pub mod merged;" }
274
+ end
275
+
276
+ # `src/generated/mod.rs`: every domain ever generated, scanned off disk. A directory is a
277
+ # selectable domain exactly when it carries its own `merged.rs`; a framework chapter has
278
+ # none and is always compiled in.
279
+ def write_root_mod
280
+ all_dirs = Dir.children(@out_root).select { |name| File.directory?(File.join(@out_root, name)) }.sort
281
+ @domains = all_dirs.select { |name| File.exist?(File.join(@out_root, name, "merged.rs")) }
282
+ path = File.join(@out_root, "mod.rs")
283
+ wrote = RustProjection::WriteIfChanged.block(path) do |file|
284
+ RootMod.new(file, all_dirs, @domains, @mod_name).write
285
+ end
286
+ puts(wrote ? "wrote #{path}" : "#{path} unchanged")
287
+ end
288
+
289
+ # One Cargo feature per domain on disk (added when missing, never removed), with `default`
290
+ # set to this run's domain so a bare `cargo build` builds it. A feature is inserted right
291
+ # after `default = [...]`, not appended: past `[[bin]]` it would parse into that table.
292
+ def sync_cargo_features
293
+ path = File.join(@rust_dir, "Cargo.toml")
294
+ manifest = File.read(path)
295
+ manifest = manifest.sub(/\A/, FEATURES_HEADER) unless manifest.include?("[features]")
296
+ @domains.each { |name| manifest = add_feature(manifest, name) }
297
+ manifest = manifest.sub(/^default\s*=.*$/, "default = [#{@mod_name.inspect}]")
298
+ # An unchanged manifest is not rewritten: a new mtime makes Cargo rebuild the whole crate.
299
+ if RustProjection::WriteIfChanged.call(path, manifest)
300
+ puts "wrote #{path} (default feature: #{@mod_name})"
301
+ else
302
+ puts "#{path} unchanged (default feature already #{@mod_name})"
303
+ end
304
+ end
305
+
306
+ # Checked against the `[features]` table only: `[package]`'s `name` or a `[[bin]]` path can
307
+ # share a word with a domain name.
308
+ def add_feature(manifest, name)
309
+ table = manifest[/^\[features\](?:\n(?!\[).*)*$/] || ""
310
+ return manifest if table =~ /^#{Regexp.escape(name)}\s*=/
311
+
312
+ manifest.sub(/^default\s*=.*$/) { "#{Regexp.last_match(0)}\n#{name} = []" }
313
+ end
314
+
315
+ FEATURES_HEADER = <<~TOML
316
+ # One feature per generated domain: it selects which domain's own
317
+ # generated::<domain>::merged module `generated::active` re-exports
318
+ # (rust/src/generated/mod.rs). `hecks project_rust` adds a feature when a new domain is
319
+ # generated and never removes one (the domain's own generated/<name>/ directory is the
320
+ # source of truth for whether it exists). `default` tracks the domain generated most
321
+ # recently, so a bare `cargo build` keeps working; any other still-generated domain stays
322
+ # reachable via `--no-default-features --features <name>`.
323
+ [features]
324
+ default = []
325
+
326
+ TOML
327
+ private_constant :FEATURES_HEADER
328
+ end
329
+ end
330
+ end
331
+
332
+ require_relative "project_rust/root_mod"
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require_relative "../rust_build"
5
+ require_relative "project_rust"
6
+
7
+ module Hecks
8
+ module RustBuild
9
+ # Regenerates a domain's Rust source and cross-compiles it to `wasm32-wasip1`
10
+ # (docs/decisions/0012-wasm-via-wasi-stdio.md).
11
+ #
12
+ # Wasm.call(["path/to/domain"])
13
+ #
14
+ # Writes `<workspace>/dist/<domain>.wasm`, run with
15
+ # `wasmtime run rust/dist/<domain>.wasm < script.json`, and the domain's `ir.json` beside it,
16
+ # which `rust/host` reads at runtime. `HECKS_RUST_DIR` names the workspace to build in and
17
+ # `CARGO_TARGET_DIR` where build output goes, beside the workspace by default.
18
+ module Wasm
19
+ # The Cargo target the module is built for.
20
+ TARGET = "wasm32-wasip1"
21
+
22
+ module_function
23
+
24
+ # @param argv [Array<String>] the domain's directory
25
+ # @return [Integer] the exit status
26
+ # @raise [Failure] when the target is not installed or a step fails
27
+ def call(argv)
28
+ domain = argv.first or raise Failure, "usage: hecks build_wasm <domain>"
29
+ return build_with_rust(domain) if ENV["HECKS_PARSER"] == "rust" && ENV["HECKS_CODEGEN"] == "rust"
30
+
31
+ require_target!
32
+ rust_dir = RustBuild.rust_dir
33
+ scratch = scratch_copy(rust_dir)
34
+ puts "== regenerating #{scratch}/src/generated/ for #{domain} =="
35
+ status = RustBuild.with_env("HECKS_RUST_DIR" => scratch) { ProjectRust.call([domain]) }
36
+ raise Failure, "project_rust failed for #{domain}" unless status.zero?
37
+
38
+ compile(scratch)
39
+ publish(scratch, rust_dir, File.basename(domain))
40
+ 0
41
+ end
42
+
43
+ # With the Rust parser and codegen, `hecks-build` runs the same steps and writes the same
44
+ # outputs.
45
+ def build_with_rust(domain)
46
+ build_dir = File.join(ROOT, "rust", "build")
47
+ puts "== cargo build (hecks-build) =="
48
+ RustBuild.command!("cargo", "build", chdir: build_dir)
49
+ puts "== hecks-build #{domain} --wasm =="
50
+ RustBuild.command!(File.join(build_dir, "target", "debug", "hecks-build"), domain, "--wasm")
51
+ 0
52
+ end
53
+
54
+ def require_target!
55
+ return if system("rustup target list --installed 2>/dev/null | grep -qx #{TARGET}")
56
+
57
+ raise Failure, <<~MSG
58
+ #{TARGET} isn't installed for this toolchain. Install it once with:
59
+
60
+ rustup target add #{TARGET}
61
+
62
+ then re-run the build.
63
+ MSG
64
+ end
65
+
66
+ # Built in a scratch copy because generating rewrites `Cargo.toml` and `src/generated/`,
67
+ # which would dirty tracked files in a checkout.
68
+ def scratch_copy(rust_dir, name = "project_wasm")
69
+ scratch = if ENV.key?("HECKS_RUST_DIR")
70
+ File.join(rust_dir, "scratch", name)
71
+ else
72
+ File.join(ROOT, "tmp", name, "rust")
73
+ end
74
+ FileUtils.rm_rf(scratch)
75
+ FileUtils.mkdir_p(scratch)
76
+ %w[Cargo.toml Cargo.lock src].each { |entry| FileUtils.cp_r(File.join(rust_dir, entry), scratch) }
77
+ scratch
78
+ end
79
+
80
+ # `rustup run`, not bare `cargo`: a `cargo` earlier on PATH may never have seen
81
+ # `rustup target add` and fail with "can't find crate for `std`".
82
+ def compile(scratch)
83
+ puts "== cargo build --release --target #{TARGET} =="
84
+ target_dir = ENV.fetch("CARGO_TARGET_DIR", File.join(RustBuild.rust_dir, "target"))
85
+ RustBuild.command!("rustup", "run", "stable", "cargo", "build", "--release", "--target", TARGET,
86
+ chdir: scratch, env: { "CARGO_TARGET_DIR" => target_dir })
87
+ end
88
+
89
+ def publish(scratch, rust_dir, name)
90
+ dist = File.join(rust_dir, "dist")
91
+ FileUtils.mkdir_p(dist)
92
+ target_dir = ENV.fetch("CARGO_TARGET_DIR", File.join(rust_dir, "target"))
93
+ out = File.join(dist, "#{name}.wasm")
94
+ FileUtils.cp(File.join(target_dir, TARGET, "release", "rust.wasm"), out)
95
+ puts "wrote #{out}"
96
+ puts %(run it: wasmtime run #{out} < script.json)
97
+ sidecar = File.join(scratch, "src", "generated", name, "ir.json")
98
+ return unless File.exist?(sidecar)
99
+
100
+ FileUtils.cp(sidecar, File.join(dist, "#{name}.ir.json"))
101
+ puts "wrote #{File.join(dist, "#{name}.ir.json")}"
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require_relative "../rust_build"
5
+ require_relative "project_rust"
6
+ require_relative "wasm"
7
+
8
+ module Hecks
9
+ module RustBuild
10
+ # Builds the `rust/web` wasm-bindgen crate over a domain's generated code
11
+ # (docs/decisions/0015-wasm-bindgen-browser-projection.md).
12
+ #
13
+ # WasmBrowser.call(["path/to/domain"])
14
+ #
15
+ # Writes `<workspace>/dist/browser/<domain>/{<domain>.js,<domain>_bg.wasm,*.d.ts}`, an ES
16
+ # module whose `dispatch(json)` returns the same `{"instances","events","refusals"}` JSON as
17
+ # the WASI module.
18
+ module WasmBrowser
19
+ # The Cargo target the module is built for.
20
+ TARGET = "wasm32-unknown-unknown"
21
+
22
+ module_function
23
+
24
+ # @param argv [Array<String>] the domain's directory
25
+ # @return [Integer] the exit status
26
+ # @raise [Failure] when the target or the matching wasm-bindgen CLI is missing, or a step
27
+ # fails
28
+ def call(argv)
29
+ domain = argv.first or raise Failure, "usage: hecks build_browser_wasm <domain>"
30
+ rust_dir = RustBuild.rust_dir
31
+ web_dir = File.join(rust_dir, "web")
32
+ name = File.basename(domain)
33
+ require_target!
34
+ require_cli!(web_dir)
35
+ scratch = scratch_copy(rust_dir)
36
+ puts "== regenerating #{scratch}/src/generated/ for #{domain} =="
37
+ status = RustBuild.with_env("HECKS_RUST_DIR" => scratch) { ProjectRust.call([domain]) }
38
+ raise Failure, "project_rust failed for #{domain}" unless status.zero?
39
+
40
+ compile(File.join(scratch, "web"), web_dir)
41
+ bind(web_dir, name)
42
+ 0
43
+ end
44
+
45
+ # Generated in a scratch copy, as `Wasm` does, because generating rewrites `Cargo.toml` and
46
+ # `src/generated/`, which would dirty tracked files in a checkout. The web crate's path
47
+ # dependency `..` resolves to the scratch workspace.
48
+ def scratch_copy(rust_dir)
49
+ scratch = Wasm.scratch_copy(rust_dir, "project_wasm_browser")
50
+ FileUtils.mkdir_p(File.join(scratch, "web"))
51
+ %w[Cargo.toml Cargo.lock src].each do |entry|
52
+ from = File.join(rust_dir, "web", entry)
53
+ FileUtils.cp_r(from, File.join(scratch, "web")) if File.exist?(from)
54
+ end
55
+ scratch
56
+ end
57
+
58
+ def require_target!
59
+ return if system("rustup target list --installed 2>/dev/null | grep -qx #{TARGET}")
60
+
61
+ raise Failure, <<~MSG
62
+ #{TARGET} isn't installed for this toolchain. Install it once with:
63
+
64
+ rustup target add #{TARGET}
65
+
66
+ then re-run the build.
67
+ MSG
68
+ end
69
+
70
+ # The wasm-bindgen CLI must match the crate version pinned in `rust/web/Cargo.toml`; a
71
+ # mismatch fails only when the CLI runs, so the pin is read back, not hardcoded.
72
+ def require_cli!(web_dir)
73
+ pinned = File.read(File.join(web_dir, "Cargo.toml"))[/^wasm-bindgen = "=(.+)"/, 1] or
74
+ raise Failure, "rust/web/Cargo.toml: couldn't find a pinned (\"=x.y.z\") wasm-bindgen version"
75
+ installed = `wasm-bindgen --version 2>/dev/null`[/[\d.]+/]
76
+ return if installed == pinned
77
+
78
+ raise Failure, <<~MSG
79
+ wasm-bindgen CLI #{installed.inspect} != the pinned crate version #{pinned.inspect}. Install the matching CLI once with:
80
+
81
+ cargo install wasm-bindgen-cli --version #{pinned} --locked
82
+
83
+ then re-run the build.
84
+ MSG
85
+ end
86
+
87
+ def compile(scratch_web, web_dir)
88
+ puts "== cargo build --release --target #{TARGET} (rust/web) =="
89
+ target_dir = ENV.fetch("CARGO_TARGET_DIR", File.join(web_dir, "target"))
90
+ RustBuild.command!("rustup", "run", "stable", "cargo", "build", "--release", "--target", TARGET,
91
+ chdir: scratch_web, env: { "CARGO_TARGET_DIR" => target_dir })
92
+ end
93
+
94
+ def bind(web_dir, name)
95
+ out_dir = File.join(RustBuild.rust_dir, "dist", "browser", name)
96
+ FileUtils.mkdir_p(out_dir)
97
+ built = File.join(ENV.fetch("CARGO_TARGET_DIR", File.join(web_dir, "target")), TARGET, "release",
98
+ "rust_web.wasm")
99
+ puts "== wasm-bindgen --target web =="
100
+ RustBuild.command!("wasm-bindgen", "--target", "web", "--out-dir", out_dir, "--out-name", name, built)
101
+ puts "wrote #{out_dir}/"
102
+ puts <<~MSG
103
+ use it in a browser:
104
+ <script type="module">
105
+ import init, { dispatch } from "./#{name}.js";
106
+ await init();
107
+ const result = dispatch(JSON.stringify({ steps: [...] }));
108
+ </script>
109
+ MSG
110
+ end
111
+ end
112
+ end
113
+ end