hecks 2.9.0 → 3.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 (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 +4 -2
  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 +7 -4
  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 +6 -3
  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
@@ -10,8 +10,32 @@ module Hecks
10
10
  module Reattest
11
11
  module_function
12
12
 
13
- # Decides whether an edited era text still projects to the shape the era was frozen
14
- # with, refusing the edit when it does not.
13
+ # Says how an edited era text stands against the shape the era was frozen with, without
14
+ # refusing anything: the facts `Era.Permit`'s givens hold, and what `shape_guard!` raises on.
15
+ #
16
+ # @param text [String] the held era's bluebook source as it now stands
17
+ # @param stored_hash [String, nil] the era's minted shape hash (SHA-256 hex); nil if unnamed
18
+ # @param stored_projection [Hash{String => Object}, nil] the stored
19
+ # `Runtime::StorageShape.project` result as parsed JSON; nil when absent
20
+ # @return [Symbol] `:cosmetic` when the shape is unchanged, `:unnamed` when neither a
21
+ # projection nor a hash is stored, `:changed` when the text projects to another shape,
22
+ # `:unloadable` when the text does not load as a bluebook
23
+ def verdict(text:, stored_hash:, stored_projection: nil)
24
+ bluebook = shadow(text)
25
+ return :unloadable unless bluebook
26
+
27
+ # The stored projection is compared first: it is version-free, so a new canonical form
28
+ # cannot make a cosmetic edit look like a shape change. The hash is the fallback.
29
+ if stored_projection
30
+ edited = JSON.parse(JSON.generate(Runtime::StorageShape.project(bluebook)))
31
+ return edited == stored_projection ? :cosmetic : :changed
32
+ end
33
+ return :unnamed unless stored_hash
34
+
35
+ Runtime::StorageShape.mint_hash(bluebook) == stored_hash ? :cosmetic : :changed
36
+ end
37
+
38
+ # Refuses an edited era text that projects to another shape than the era froze with.
15
39
  #
16
40
  # @param domain [String] the domain's name, used in refusal messages
17
41
  # @param ordinal [Integer] the era's ordinal, used in refusal messages
@@ -23,36 +47,31 @@ module Hecks
23
47
  # projection nor a hash is stored
24
48
  # @raise [Runtime::WiringError] if `text` does not load, or projects to another shape
25
49
  def shape_guard!(domain:, ordinal:, text:, stored_hash:, stored_projection: nil)
26
- bluebook = shadow(text)
27
- unless bluebook
50
+ found = verdict(text: text, stored_hash: stored_hash, stored_projection: stored_projection)
51
+ return found if %i[cosmetic unnamed].include?(found)
52
+
53
+ if found == :unloadable
28
54
  raise Runtime::WiringError,
29
55
  "cannot re-attest era #{ordinal} of #{domain}: the edited text does not load as a " \
30
56
  "bluebook — a held era text is bootable source; restore a loadable text"
31
57
  end
58
+ raise Runtime::WiringError, changed_shape_message(domain, ordinal, text, stored_hash, stored_projection)
59
+ end
32
60
 
33
- # The stored projection is compared first: it is version-free, so a new canonical form
34
- # cannot make a cosmetic edit look like a shape change. The hash is the fallback.
61
+ # The refusal for an edit that changed the era's shape.
62
+ #
63
+ # @return [String] names the minted label when the era was compared by hash
64
+ def changed_shape_message(domain, ordinal, text, stored_hash, stored_projection)
65
+ head = "cannot re-attest era #{ordinal} of #{domain}: the edit changed the era's SHAPE, not just "
66
+ tail = "Attesting would retroactively redefine what era #{ordinal} meant for data already written " \
67
+ "under it; restore a text with the original shape"
35
68
  if stored_projection
36
- edited = JSON.parse(JSON.generate(Runtime::StorageShape.project(bluebook)))
37
- return :cosmetic if edited == stored_projection
38
-
39
- raise Runtime::WiringError,
40
- "cannot re-attest era #{ordinal} of #{domain}: the edit changed the era's SHAPE, not just " \
41
- "its text — the text no longer projects to the shape frozen for era #{ordinal}. " \
42
- "Attesting would retroactively redefine what era #{ordinal} meant for data already written " \
43
- "under it; restore a text with the original shape"
69
+ return "#{head}its text — the text no longer projects to the shape frozen for era #{ordinal}. #{tail}"
44
70
  end
45
- return :unnamed unless stored_hash
46
-
47
- computed = Runtime::StorageShape.mint_hash(bluebook)
48
- return :cosmetic if computed == stored_hash
49
71
 
50
- raise Runtime::WiringError,
51
- "cannot re-attest era #{ordinal} of #{domain}: the edit changed the era's SHAPE, not just " \
52
- "its text — its name #{stored_hash[0, Runtime::StorageShape::LABEL_LENGTH]} was minted from " \
53
- "a different shape (the text now projects to #{computed[0, Runtime::StorageShape::LABEL_LENGTH]}). " \
54
- "Attesting would retroactively redefine what era #{ordinal} meant for data already written " \
55
- "under it; restore a text with the original shape"
72
+ label = Runtime::StorageShape::LABEL_LENGTH
73
+ "#{head}its text — its name #{stored_hash[0, label]} was minted from a different shape (the text " \
74
+ "now projects to #{Runtime::StorageShape.mint_hash(shadow(text))[0, label]}). #{tail}"
56
75
  end
57
76
 
58
77
  # Parses bluebook source in a scratch registry, through a temporary file that is
@@ -9,3 +9,4 @@ require_relative "translation/rule_compiler"
9
9
  require_relative "translation/scaffold"
10
10
  require_relative "translation/audit"
11
11
  require_relative "translation/reattest"
12
+ require_relative "translation/approval_file"
@@ -211,7 +211,16 @@ module Hecks
211
211
  return true if attribute.nil? || value.nil? || attribute.reference?
212
212
  return composite_decoded?(aggregate, attribute.type.to_s, value) unless attribute.list?
213
213
 
214
- !value.is_a?(Array) || value.all? { |element| composite_decoded?(aggregate, attribute.type.to_s, element) }
214
+ return true unless value.is_a?(Array)
215
+
216
+ # The element type resolves once per list, not once per element.
217
+ fields = :unresolved
218
+ value.all? do |element|
219
+ next true unless element.is_a?(Hash)
220
+
221
+ fields = composite_fields(aggregate, attribute.type.to_s) if fields == :unresolved
222
+ fields.nil? || hash_decoded?(aggregate, fields, element)
223
+ end
215
224
  end
216
225
 
217
226
  # Checks one stored value object or entity, the mirror of `decode_composite`.
@@ -225,13 +234,23 @@ module Hecks
225
234
  def composite_decoded?(aggregate, type, value)
226
235
  return true unless value.is_a?(Hash)
227
236
 
237
+ fields = composite_fields(aggregate, type)
238
+ fields.nil? || hash_decoded?(aggregate, fields, value)
239
+ end
240
+
241
+ # Maps the declared keys of a stored value object or entity, looked up by type name.
242
+ #
243
+ # @param aggregate [Bluebook::Aggregate, Bluebook::Entity] the root construct that
244
+ # declares, or whose chapter declares, the type
245
+ # @param type [String] the declared type name
246
+ # @return [Hash{Symbol => Bluebook::Attribute, nil}, nil] the entity's fields, else the
247
+ # unambiguous value object's; nil when `type` names neither
248
+ def composite_fields(aggregate, type)
228
249
  entity = Runtime::Value.find_entity(aggregate, type)
229
- return hash_decoded?(aggregate, entity_fields(entity), value) if entity
250
+ return entity_fields(entity) if entity
230
251
 
231
252
  value_object = Runtime::Value.value_object_for(aggregate, type)
232
- return true unless value_object
233
-
234
- hash_decoded?(aggregate, value_object.attributes.to_h { |field| [field.name, field] }, value)
253
+ value_object&.attributes&.to_h { |field| [field.name, field] }
235
254
  end
236
255
  end
237
256
  end
@@ -0,0 +1,285 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "json"
5
+ require "tempfile"
6
+
7
+ module Hecks
8
+ # What each `project_*` generator of the language writes, held as data: absolute path to text.
9
+ #
10
+ # The tables of the language (the model's holding half, the vocabulary, the parser's keywords, the
11
+ # Rust vocabulary, reserved names, field hints, the expression tables and the DSL reference) are
12
+ # projected from the Bluebook chapter's own declarations. Building them apart from writing them
13
+ # lets a caller compare a projection with the tree before anything is changed: a
14
+ # `hecks project_*` verb writes the answer, and the Hecks domain's Language commands report
15
+ # drift and write only when confirmed.
16
+ module ProjectionFiles
17
+ # The repository root this file lives in.
18
+ ROOT = File.expand_path("../..", __dir__)
19
+
20
+ # Raised when a projection would leave the language unable to boot.
21
+ class Refused < StandardError; end
22
+
23
+ # The files a projection writes, and the ones it leaves stale.
24
+ #
25
+ # @!attribute [r] content
26
+ # @return [Hash{String => String}] absolute path to the text it should hold
27
+ # @!attribute [r] stale
28
+ # @return [Array<String>] absolute paths the projection does not emit and removes
29
+ Result = Struct.new(:content, :stale)
30
+
31
+ # Rust keyword and Cargo name tables, by the constant each becomes.
32
+ RESERVED_TABLES = {
33
+ "RUST_KEYWORDS" => "RustReservedWord",
34
+ "CARGO_RESERVED_DOMAIN_NAMES" => "CargoReservedName"
35
+ }.freeze
36
+
37
+ # Names of the projections, in the order a full regeneration runs them.
38
+ NAMES = %i[model vocabulary rust_vocabulary reserved_names parser_table bootstrap_table
39
+ field_hints expression_tables reference].freeze
40
+
41
+ module_function
42
+
43
+ # Builds one projection without writing anything.
44
+ #
45
+ # @param name [Symbol] one of `NAMES`
46
+ # @param root [String] the checkout the projection is for
47
+ # @return [Result] what it would write and remove
48
+ # @raise [ArgumentError] if the projection is not known
49
+ # @raise [Refused] if the projection would leave the language unable to boot
50
+ def build(name, root: ROOT)
51
+ raise ArgumentError, "no projection named #{name.inspect}" unless NAMES.include?(name.to_sym)
52
+
53
+ public_send(name, root)
54
+ end
55
+
56
+ # Writes one projection, each file through a temporary file and a rename so a reader never sees
57
+ # a partial file, and removes what it does not emit.
58
+ #
59
+ # @param name [Symbol] one of `NAMES`
60
+ # @param root [String] the checkout the projection is for
61
+ # @return [Array<String>] one `wrote <path>` or `removed <path>` line for each change
62
+ def write(name, root: ROOT)
63
+ result = build(name, root: root)
64
+ lines = result.content.map { |path, text| write_atomically(path, text) }
65
+ lines + result.stale.map { |path| File.delete(path) && "removed #{path}" }
66
+ end
67
+
68
+ # Writes one projection and prints what changed, as the `project_*` commands do.
69
+ #
70
+ # @param name [Symbol] one of `NAMES`
71
+ # @param root [String] the checkout the projection is for
72
+ # @return [void] the `wrote`/`removed` lines go to stdout
73
+ # @raise [SystemExit] with the reason on stderr when the projection is refused
74
+ def run(name, root: ROOT)
75
+ puts write(name, root: root)
76
+ rescue Refused => e
77
+ abort e.message
78
+ end
79
+
80
+ # Replaces a file through a same-directory temporary file and a rename.
81
+ #
82
+ # @param target [String] the file to write
83
+ # @param text [String] what it holds afterwards
84
+ # @return [String] the `wrote <path>` line
85
+ def write_atomically(target, text)
86
+ FileUtils.mkdir_p(File.dirname(target))
87
+ Tempfile.create(File.basename(target), File.dirname(target)) do |tmp|
88
+ tmp.write(text)
89
+ tmp.flush
90
+ File.rename(tmp.path, target)
91
+ end
92
+ "wrote #{target}"
93
+ end
94
+
95
+ # @param root [String] the checkout
96
+ # @return [Result] the model's holding half, one file for each thing the chapter holds
97
+ def model(root)
98
+ require "hecks"
99
+ files = Projector.call(:model, bluebook: chapter)
100
+ out = File.join(root, "lib/hecks/bluebook")
101
+ Result.new(files.to_h { |relative, text| [File.join(out, relative), text] }, [])
102
+ end
103
+
104
+ # @param root [String] the checkout
105
+ # @return [Result] `lib/hecks/vocabulary.rb`, the language's closed sets
106
+ def vocabulary(root)
107
+ require "hecks"
108
+ Result.new({ File.join(root, "lib/hecks/vocabulary.rb") => artifact_text(:vocabulary) }, [])
109
+ end
110
+
111
+ # @param root [String] the checkout
112
+ # @return [Result] one Rust enum a table under `rust/src/kernel/vocab/`; files it does
113
+ # not emit are stale
114
+ def rust_vocabulary(root)
115
+ require "hecks"
116
+ kernel = File.join(root, "rust/src/kernel")
117
+ files = Projector.call(:rust_vocabulary, bluebook: chapter)
118
+ content = files.to_h { |relative, text| [File.join(kernel, relative), text] }
119
+ Result.new(content, Dir.glob(File.join(kernel, "vocab", "*.rs")) - content.keys)
120
+ end
121
+
122
+ # @param root [String] the checkout
123
+ # @return [Result] `reserved_names.rs` for the codegen and build crates, which share no library
124
+ # @raise [Refused] if a vocabulary declares no members
125
+ def reserved_names(root)
126
+ require "hecks/vocabulary"
127
+ consts = RESERVED_TABLES.map do |const, vocabulary|
128
+ words = Hecks::Vocabulary.fetch(vocabulary)
129
+ raise Refused, "vocabulary #{vocabulary} declares no members" if words.empty?
130
+
131
+ lines = words.map { |word| " #{word.inspect}," }.join("\n")
132
+ "/// The `#{vocabulary}` vocabulary, #{words.size} entries.\n" \
133
+ "pub const #{const}: &[&str] = &[\n#{lines}\n];"
134
+ end
135
+ text = reserved_names_text(consts)
136
+ targets = %w[rust/codegen/src/reserved_names.rs rust/build/src/reserved_names.rs]
137
+ Result.new(targets.to_h { |target| [File.join(root, target), text] }, [])
138
+ end
139
+
140
+ # @param root [String] the checkout
141
+ # @return [Result] the Rust parser's keyword table
142
+ def parser_table(root)
143
+ require "hecks"
144
+ Result.new({ File.join(root, "rust/parser/src/keywords.rs") => artifact_text(:parser_table) }, [])
145
+ end
146
+
147
+ # @param root [String] the checkout
148
+ # @return [Result] the DSL's bootstrap-window fallbacks
149
+ def bootstrap_table(root)
150
+ require "hecks"
151
+ path = File.join(root, "lib/hecks/bluebook/dsl/bootstrap_table.rb")
152
+ Result.new({ path => artifact_text(:bootstrap_table) }, [])
153
+ end
154
+
155
+ # @param root [String] the checkout
156
+ # @return [Result] `rust/host/src/field_hints.rs`, the hints the host's form fields guess from
157
+ def field_hints(root)
158
+ require "hecks"
159
+ vocabulary = chapter.aggregates.find { |aggregate| aggregate.hecks_name == "Vocabulary" }
160
+ field_hint = vocabulary.value_objects.find { |value_object| value_object.hecks_name == "FieldHint" }
161
+ hints = field_hint.members.map(&:to_h)
162
+ Result.new({ File.join(root, "rust/host/src/field_hints.rs") => field_hints_text(hints) }, [])
163
+ end
164
+
165
+ # @param root [String] the checkout
166
+ # @return [Result] `lib/hecks/bluebook/expression/projection.json`
167
+ # @raise [Refused] if an admitted operator has no declared algebra, or the projection drops an
168
+ # operator the language's own guards evaluate through
169
+ def expression_tables(root)
170
+ require "hecks"
171
+ require "hecks/grammar"
172
+ operators = expression_operators(Grammar.expression)
173
+ dropped = Grammar.self_bearing_operators.except(*operators.map { |row| row[:symbol] })
174
+ raise Refused, dropped_message(dropped) unless dropped.empty?
175
+
176
+ dispatcher = Grammar.expression
177
+ text = "#{JSON.pretty_generate(operators: operators, normalisations: Grammar.admitted_normalisations(dispatcher))}\n"
178
+ Result.new({ File.join(root, "lib/hecks/bluebook/expression/projection.json") => text }, [])
179
+ end
180
+
181
+ # The DSL reference pages and the README regions generated from the same declarations.
182
+ #
183
+ # @param root [String] the checkout
184
+ # @return [Result] every page of `docs/implemented/reference/` and `README.md`
185
+ def reference(root)
186
+ require "hecks"
187
+ directory = File.join(root, "docs/implemented/reference")
188
+ pages = Projector.call(:reference, bluebook: chapter, options: { from: directory })
189
+ content = pages.to_h { |relative, text| [File.join(directory, relative), text] }
190
+ readme = File.join(root, "README.md")
191
+ content[readme] = Doc::Reference.render_readme(root, File.read(readme))
192
+ Result.new(content, [])
193
+ end
194
+
195
+ # @param projection [Symbol] a projection registered with the projector
196
+ # @return [String] what it projects from the Bluebook chapter, as the text a file holds
197
+ def artifact_text(projection)
198
+ artifact = Projector.call(projection, bluebook: chapter)
199
+ artifact.is_a?(String) ? artifact : "#{JSON.pretty_generate(artifact)}\n"
200
+ end
201
+
202
+ # @return [Bluebook::Aggregate] the Bluebook chapter, which every projection reads
203
+ def chapter
204
+ Bluebook::MetaValidator.grammar_registry.bluebook("Bluebook")
205
+ end
206
+
207
+ # @param dispatcher [Object] the expression dispatcher
208
+ # @return [Array<Hash>] each admitted operator, with the algebra a comparison declares
209
+ # @raise [Refused] if a comparison has no declared algebra
210
+ def expression_operators(dispatcher)
211
+ vocabulary = chapter.aggregates.find { |aggregate| aggregate.name == "Vocabulary" }
212
+ algebra = vocabulary.value_objects.find { |value_object| value_object.hecks_name == "Comparison" }
213
+ .members.to_h { |row| [row.to_h.values.first, row.to_h] }
214
+ Grammar.admitted_operators(dispatcher).map do |op|
215
+ row = { symbol: op[:symbol], category: op[:category], precedence: op[:precedence], arity: op[:arity] }
216
+ next row unless op[:category] == "comparison"
217
+
218
+ declared = algebra.fetch(op[:symbol]) do
219
+ raise Refused, "#{op[:symbol]} is admitted but Vocabulary::Comparison declares no algebra for it"
220
+ end
221
+ row.merge(compares_less_than: declared[:compares_less_than],
222
+ compares_equal: declared[:compares_equal], negated: declared[:negated])
223
+ end
224
+ end
225
+
226
+ # @param dropped [Hash{String => Array<String>}] operator to the guards evaluating through it
227
+ # @return [String] one line for each operator a projection would strand
228
+ def dropped_message(dropped)
229
+ dropped.map do |symbol, sites|
230
+ "#{symbol} is self-bearing — the language's own predicates evaluate through it " \
231
+ "(#{sites.first(3).join('; ')}) — rewrite those guards before retiring it"
232
+ end.join("\n")
233
+ end
234
+
235
+ # @param consts [Array<String>] the rendered Rust constants
236
+ # @return [String] the whole `reserved_names.rs`
237
+ def reserved_names_text(consts)
238
+ <<~RUST
239
+ // GENERATED by hecks project_reserved_names from the RustReservedWord and
240
+ // CargoReservedName vocabularies (lib/hecks/language/bluebook/
241
+ // vocabulary.bluebook). Do not hand-edit — re-run hecks project_reserved_names.
242
+
243
+ #{consts.join("\n\n")}
244
+ RUST
245
+ end
246
+
247
+ # @param hints [Array<Hash>] the rows of `Vocabulary::FieldHint`
248
+ # @return [String] the whole `field_hints.rs`
249
+ def field_hints_text(hints)
250
+ consts = hints.map { |hint| field_hint_constant(hint) }.join("\n")
251
+ <<~RUST
252
+ // GENERATED by hecks project_field_hints from Vocabulary::FieldHint
253
+ // (lib/hecks/language/bluebook/vocabulary.bluebook). Do not
254
+ // hand-edit — re-run hecks project_field_hints instead.
255
+ //
256
+ // web.rs's `text_field` matches these in the SAME precedence Ruby's
257
+ // own `text_field` does: EMAIL_HINT, then URL_HINT, then TEL_HINT —
258
+ // against `html_type`, first match wins — then, only if `html_type`
259
+ // stayed "text", TEXTAREA_HINT against `kind`.
260
+
261
+ use regex::Regex;
262
+ use std::sync::LazyLock;
263
+
264
+ #{consts}
265
+ RUST
266
+ end
267
+
268
+ # @param hint [Hash] one `FieldHint` row: its name, pattern and what it resolves to
269
+ # @return [String] the Rust `static` for it
270
+ def field_hint_constant(hint)
271
+ name = "#{hint[:name].upcase}_HINT"
272
+ <<~RUST
273
+ /// #{hint[:name].capitalize} hint, resolving to `#{hint[:resolves_to]}` on a match —
274
+ /// Vocabulary::FieldHint's own declared pattern, unmodified (the
275
+ /// same text `Regexp#source` reads off FieldShape's own
276
+ /// #{name}, which is built from this same row).
277
+ /// `(?i)` up front is this crate's spelling
278
+ /// of Ruby's trailing `/i` — the WHOLE pattern is case-insensitive
279
+ /// on both sides, never partially.
280
+ pub static #{name}: LazyLock<Regex> =
281
+ LazyLock::new(|| Regex::new(r#"(?i)#{hint[:pattern]}"#).expect("declared field-hint pattern must compile"));
282
+ RUST
283
+ end
284
+ end
285
+ end
@@ -21,7 +21,7 @@ module Hecks
21
21
  # KeywordSeed under lib/hecks/language/).
22
22
  #
23
23
  # Do not edit. spec/bootstrap_table_spec.rb re-projects this in memory
24
- # and refuses a diff — run bin/project_bootstrap_table instead.
24
+ # and refuses a diff — run hecks project_bootstrap_table instead.
25
25
  #
26
26
  # Plain data, no requires: this is read while the grammar table it was
27
27
  # projected from is still being built (`MetaValidator.bootstrapping?`).
@@ -253,7 +253,7 @@ module Hecks
253
253
  parameters_yaml = [always_params_yaml, oauth_params_yaml, owning_params_yaml, extra_params_yaml].reject(&:empty?).join("\n")
254
254
 
255
255
  template_yaml = <<~YAML
256
- # GENERATED by bin/project_deploy #{domain} — re-run it to refresh
256
+ # GENERATED by hecks deploy project #{domain} — re-run it to refresh
257
257
  # this file rather than hand-editing. Source: #{world_file}'s own
258
258
  # deployed_to("AwsFargate") block.
259
259
  #
@@ -569,7 +569,7 @@ module Hecks
569
569
  )
570
570
 
571
571
  dockerfile = <<~DOCKERFILE
572
- # GENERATED by bin/project_deploy #{domain} — re-run it to refresh this
572
+ # GENERATED by hecks deploy project #{domain} — re-run it to refresh this
573
573
  # file rather than hand-editing. Modeled on a Ruby deployment's own
574
574
  # Dockerfile shape: build the binary outside the image (this directory's own
575
575
  # Makefile, before `docker build` runs), ship only the result — one COPY,
@@ -600,7 +600,7 @@ module Hecks
600
600
  DOCKERFILE
601
601
 
602
602
  makefile_content = <<~MAKE
603
- # GENERATED by bin/project_deploy #{domain} — re-run it to refresh
603
+ # GENERATED by hecks deploy project #{domain} — re-run it to refresh
604
604
  # this file rather than hand-editing.
605
605
  #
606
606
  # `make deploy` — builds rust/host for this domain, pushes it to this
@@ -630,11 +630,11 @@ module Hecks
630
630
  \t@command -v aarch64-linux-gnu-gcc >/dev/null 2>&1 || { echo "aarch64-linux-gnu-gcc isn't on PATH. Install once with: brew tap messense/macos-cross-toolchains && brew install aarch64-unknown-linux-gnu"; exit 1; }
631
631
  # The .wasm/.ir.json sidecars main.rs requires at boot,
632
632
  # unconditionally — see template.yaml's own HECKS_WASM_PATH/
633
- # HECKS_IR_PATH comment for why. bin/project_wasm is the same
633
+ # HECKS_IR_PATH comment for why. `hecks build_wasm` is the same
634
634
  # generator the Lambda deploy path already uses to produce
635
635
  # rust/dist/#{domain_name}.wasm/.ir.json from this domain's own
636
636
  # .bluebook.
637
- \tcd $(ROOT) && bin/project_wasm $(DOMAIN)
637
+ \tcd $(ROOT) && HECKS_ENVIRONMENT=memory ruby exe/hecks build_wasm domain=$(DOMAIN) --wait
638
638
  \tcd $(ROOT)/rust/host && CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc rustup run stable cargo build --release --target aarch64-unknown-linux-gnu --bin bootstrap
639
639
  \tcp $(ROOT)/rust/host/target/aarch64-unknown-linux-gnu/release/bootstrap #{domain_name}-host
640
640
  \tcp $(ROOT)/rust/dist/#{domain_name}.wasm #{domain_name}.wasm