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
@@ -0,0 +1,172 @@
1
+ require_relative "../errors"
2
+ require_relative "../refusal_wording"
3
+ require_relative "../../rendering"
4
+
5
+ module Hecks
6
+ module Runtime
7
+ class Value
8
+ # Boundary checks that refuse a mistyped, unknown, missing or malformed field before an
9
+ # invariant reads it; extended into `Value` alongside `Coercion`, which calls them.
10
+ module FieldChecks
11
+ # C3.8 boundary check — refuses a mistyped argument before it breaks a predicate.
12
+ private def check_bare_primitive(owner, attribute, value)
13
+ type = attribute.type.to_s
14
+ expected = NUMERIC[type]
15
+ mistyped = if expected
16
+ !value.is_a?(expected)
17
+ elsif NON_NUMERIC_SCALARS.include?(type)
18
+ COMPOSITE_SHAPES.any? { |shape| value.is_a?(shape) }
19
+ else
20
+ false
21
+ end
22
+ if mistyped
23
+ raise TypeMismatch,
24
+ RefusalWording.render_site("TypeMismatch", "numeric_field",
25
+ type: owner.hecks_name, field: attribute.name,
26
+ expected: type, offered: Rendering.describe(value))
27
+ end
28
+
29
+ check_numeric_bounds(owner.hecks_name, attribute.name, value)
30
+ end
31
+
32
+ # C3.3/C3.4 — value bounds enforced at every boundary: an Integer must fit
33
+ # signed 64 bits, a Float must be finite.
34
+ INT64_RANGE = (-(2**63))..((2**63) - 1)
35
+
36
+ private def check_numeric_bounds(type_name, field_name, given)
37
+ if given.is_a?(Integer) && !INT64_RANGE.cover?(given)
38
+ raise TypeMismatch,
39
+ RefusalWording.render_site("TypeMismatch", "integer_range",
40
+ type: type_name, field: field_name, offered: Rendering.describe(given))
41
+ end
42
+ return unless given.is_a?(Float) && !given.finite?
43
+
44
+ raise TypeMismatch,
45
+ RefusalWording.render_site("TypeMismatch", "non_finite_field",
46
+ type: type_name, field: field_name, offered: Rendering.describe(given))
47
+ end
48
+
49
+ # A value object refuses an undeclared key the same way a command's own
50
+ # payload does, checked before any other field-content check.
51
+ private def check_unknown_fields(value_object, fields)
52
+ known = value_object.attributes.map { |attribute| attribute.name.to_sym }
53
+ unknown = (fields.keys.map(&:to_sym) - known).sort
54
+ return if unknown.empty?
55
+
56
+ declared = value_object.attributes.map(&:name)
57
+ raise UnknownArgument,
58
+ RefusalWording.render_site("UnknownArgument", "unknown_args",
59
+ command: value_object.hecks_name, unknown: unknown,
60
+ declared: declared)
61
+ end
62
+
63
+ # C3.7 — every non-optional, non-list field must arrive (or construction
64
+ # refuses); a `default:` has already been filled in by `apply_defaults`.
65
+ private def check_required_fields(value_object, fields)
66
+ value_object.attributes.each do |attribute|
67
+ next if attribute.optional? || attribute.list?
68
+ next unless fields[attribute.name].nil?
69
+
70
+ raise TypeMismatch,
71
+ RefusalWording.render_site("TypeMismatch", "numeric_field",
72
+ type: value_object.hecks_name, field: attribute.name,
73
+ expected: attribute.type, offered: "nil")
74
+ end
75
+ end
76
+
77
+ # Checked before invariants, because an invariant reading a mistyped field
78
+ # is exactly the thing that would otherwise explode.
79
+ NUMERIC = { "Integer" => Integer, "Float" => Numeric }.freeze
80
+ private def check_numeric_fields(value_object, fields)
81
+ value_object.attributes.each do |attribute|
82
+ expected = NUMERIC[attribute.type.to_s]
83
+ next unless expected
84
+
85
+ given = fields[attribute.name]
86
+ next if given.nil?
87
+
88
+ offered_items(attribute, given).each do |item|
89
+ unless item.is_a?(expected)
90
+ raise TypeMismatch,
91
+ RefusalWording.render_site("TypeMismatch", "numeric_field",
92
+ type: value_object.hecks_name, field: attribute.name,
93
+ expected: attribute.type, offered: Rendering.describe(item))
94
+ end
95
+
96
+ # `is_a?(expected)` alone waves NaN/Infinity through — both are real
97
+ # Floats. `-0.0` is deliberately left unchecked: finite and legitimate.
98
+ check_numeric_bounds(value_object.hecks_name, attribute.name, item)
99
+ end
100
+ end
101
+ end
102
+
103
+ # A `list_of` field holds an Array whatever its element type, so a lone scalar
104
+ # offered for it is refused; nil stays legitimate, as it is for any optional field.
105
+ private def check_list_shapes(value_object, fields)
106
+ value_object.attributes.each do |attribute|
107
+ given = fields[attribute.name]
108
+ next unless attribute.list? && !given.nil? && !given.is_a?(Array)
109
+
110
+ raise TypeMismatch, RefusalWording.render_site(
111
+ "TypeMismatch", "numeric_field", type: value_object.hecks_name, field: attribute.name,
112
+ expected: "list_of(#{attribute.type})", offered: Rendering.describe(given)
113
+ )
114
+ end
115
+ end
116
+
117
+ # The values a field's element-level checks apply to: each element of a list, or the
118
+ # one value of a scalar field.
119
+ private def offered_items(attribute, given) = attribute.list? && given.is_a?(Array) ? given : [given]
120
+
121
+ # A scalar field (String, or a boolean) must not arrive as a composite
122
+ # (Array/Hash) standing in for a leaf value. A String field additionally
123
+ # must not arrive as any other scalar, except inside `judge_bootstrapping?`.
124
+ COMPOSITE_SHAPES = [Array, ::Hash].freeze
125
+ NON_NUMERIC_SCALARS = %w[String TrueClass FalseClass].freeze
126
+ private def check_scalar_shapes(value_object, fields)
127
+ value_object.attributes.each do |attribute|
128
+ type = attribute.type.to_s
129
+ next unless NON_NUMERIC_SCALARS.include?(type)
130
+
131
+ given = fields[attribute.name]
132
+ next if given.nil?
133
+
134
+ offered_items(attribute, given).each do |item|
135
+ composite = COMPOSITE_SHAPES.any? { |shape| item.is_a?(shape) }
136
+ non_string_scalar = type == "String" && !composite && !item.is_a?(String) && !judge_bootstrapping?
137
+ next unless composite || non_string_scalar
138
+
139
+ raise TypeMismatch,
140
+ RefusalWording.render_site("TypeMismatch", "numeric_field",
141
+ type: value_object.hecks_name, field: attribute.name,
142
+ expected: attribute.type, offered: Rendering.describe(item))
143
+ end
144
+ end
145
+ end
146
+
147
+ # A field declared with a pattern must match it, refused as a TypeMismatch
148
+ # rather than surfacing later as a broken predicate. The pattern itself is
149
+ # already vetted by PatternSubset when the bluebook is declared. `^` and `$` match the
150
+ # whole value, as in Rust, so a newline cannot smuggle text past an anchored pattern.
151
+ private def check_patterns(value_object, fields)
152
+ value_object.attributes.each do |attribute|
153
+ pattern = attribute.pattern
154
+ next unless pattern
155
+
156
+ given = fields[attribute.name]
157
+ next if given.nil?
158
+
159
+ offered_items(attribute, given).each do |item|
160
+ next if item.is_a?(String) && Regexp.new(Hecks::Bluebook::PatternSubset.whole_string(pattern)).match?(item)
161
+
162
+ raise TypeMismatch,
163
+ RefusalWording.render_site("TypeMismatch", "pattern_mismatch",
164
+ type: value_object.hecks_name, field: attribute.name,
165
+ pattern: pattern, offered: Rendering.describe(item))
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
@@ -1,6 +1,7 @@
1
1
  require "json"
2
2
  require_relative "../rendering"
3
3
  require_relative "value/invariant_violation"
4
+ require_relative "value/field_checks"
4
5
  require_relative "value/coercion"
5
6
  require_relative "value/entity_list_coercion"
6
7
  require_relative "value/admission"
@@ -8,8 +9,10 @@ require_relative "value/admission"
8
9
  module Hecks
9
10
  module Runtime
10
11
  # A typed value object: frozen fields, read by name.
11
- # Construction lives in value/coercion.rb, entity_list_coercion.rb and admission.rb.
12
+ # Construction lives in value/coercion.rb, field_checks.rb, entity_list_coercion.rb and
13
+ # admission.rb.
12
14
  class Value
15
+ extend FieldChecks
13
16
  extend Coercion
14
17
  extend EntityListCoercion
15
18
  extend Admission
data/lib/hecks/runtime.rb CHANGED
@@ -42,24 +42,38 @@ module Hecks
42
42
  #
43
43
  # @param path [String] path to a domain directory, or a file inside one
44
44
  # @param shared [String, nil] a shared-root override
45
- # @param install_facade [Boolean] whether to install the Ruby facade constants
45
+ # @param install_doors [Boolean] whether to install the Ruby facade constants
46
+ # @param install_facade [Boolean, nil] the deprecated spelling of `install_doors`; warns
46
47
  # @param environment [String, nil] the environment name for `Adapters::Folder#load_domain`
47
48
  # @return [Runtime::Dispatcher, Runtime::RemoteDispatcher] the dispatcher bound
48
49
  # to the booted domain
49
- def boot(path, shared: nil, install_facade: true, environment: nil)
50
- Loader.boot(path, shared: shared, install_facade: install_facade, environment: environment)
50
+ def boot(path, shared: nil, install_doors: true, install_facade: nil, environment: Runtime::Loader::FROM_ENV)
51
+ Loader.boot(path, shared: shared, install_doors: install_doors, environment: environment,
52
+ install_facade: install_facade)
53
+ end
54
+
55
+ # Loads a domain's declarations without binding any adapter. See Loader.describe.
56
+ #
57
+ # @param path [String] path to a domain directory, or a file inside one
58
+ # @param shared [String, nil] a shared-root override
59
+ # @param environment [String, nil] the environment overlay; defaults to `HECKS_ENVIRONMENT`
60
+ # @return [Runtime::Loader::Described] the loaded registry, answering `registry`
61
+ def describe(path, shared: nil, environment: Runtime::Loader::FROM_ENV)
62
+ Loader.describe(path, shared: shared, environment: environment)
51
63
  end
52
64
 
53
65
  # Loads only the given files of a domain; otherwise like `boot`. See Loader.boot_files.
54
66
  #
55
67
  # @param paths [String, Array<String>] one or more file paths within the domain
56
68
  # @param shared [String, nil] a shared-root override
57
- # @param install_facade [Boolean] whether to install the Ruby facade constants
69
+ # @param install_doors [Boolean] whether to install the Ruby facade constants
70
+ # @param install_facade [Boolean, nil] the deprecated spelling of `install_doors`; warns
58
71
  # @param environment [String, nil] the environment name for the selected-file loader
59
72
  # @return [Runtime::Dispatcher, Runtime::RemoteDispatcher] the dispatcher bound
60
73
  # to the booted domain
61
- def boot_files(paths, shared: nil, install_facade: true, environment: nil)
62
- Loader.boot_files(paths, shared: shared, install_facade: install_facade, environment: environment)
74
+ def boot_files(paths, shared: nil, install_doors: true, install_facade: nil, environment: Runtime::Loader::FROM_ENV)
75
+ Loader.boot_files(paths, shared: shared, install_doors: install_doors, environment: environment,
76
+ install_facade: install_facade)
63
77
  end
64
78
 
65
79
  # Bind the ambient registry for the duration of the block, restoring
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "open3"
5
+ require_relative "../rust_build"
6
+ require_relative "native_build"
7
+
8
+ module Hecks
9
+ module RustBuild
10
+ # The differential harness (ADR 0010): replays a corpus script through the Ruby interpreter
11
+ # (`Fuzzing::Replay`) and prints or diffs the JSON surface a Rust artifact must reproduce.
12
+ #
13
+ # With no artifact it prints Ruby's result as JSON, which saved to a file becomes a pinned
14
+ # expectation. The artifact is another artifact's JSON file, `native` (the workspace's
15
+ # `target/{release,debug}/rust`), a `.wasm` path (run under `wasmtime`), an executable path,
16
+ # or `build` (the domain's own Cargo feature, built and run); a live one gets the script on
17
+ # stdin. `HECKS_RUST_DIR` names the workspace and `CARGO_TARGET_DIR` where its output went.
18
+ module Conformance
19
+ USAGE = "usage: hecks check_conformance <domain> <script.json> " \
20
+ "[rust_output.json | native | build | path/to/binary | path/to/module.wasm]"
21
+
22
+ # The refusal kinds are not compared for ad-hoc filter steps: C8.3
23
+ # (docs/semantics/bluebook-semantics.md) is open (RuntimeError in Ruby, TypeMismatch in
24
+ # Rust), and only the message is byte-exact.
25
+ FILTER_VERB = "filter "
26
+
27
+ module_function
28
+
29
+ # @param argv [Array<String>] the domain, the script and optionally the artifact
30
+ # @return [Integer] 0 when the result is printed or matches, 1 on a mismatch
31
+ # @raise [Failure] when the artifact cannot be run
32
+ def call(argv)
33
+ require_relative "../../hecks"
34
+ require_relative "../fuzzing"
35
+ domain, script, other = argv
36
+ raise Failure, USAGE unless domain && script
37
+
38
+ ours = comparable(Hecks::Fuzzing::Replay.call(domain, JSON.parse(File.read(script)).fetch("steps")))
39
+ return print_result(ours) unless other
40
+
41
+ theirs = normalize(JSON.parse(artifact_output(domain, script, other)))
42
+ report(domain, script, ours, theirs)
43
+ end
44
+
45
+ def print_result(result)
46
+ puts JSON.pretty_generate(result)
47
+ 0
48
+ end
49
+
50
+ # @param result [Hash] `Fuzzing::Replay`'s answer
51
+ # @return [Hash{String => Object}] the surface a Rust artifact must reproduce
52
+ def comparable(result)
53
+ surface = {
54
+ "instances" => result[:instances].transform_values { |state| JSON.parse(JSON.generate(state)) },
55
+ "events" => result[:events].map { |event| event_surface(event) },
56
+ "refusals" => result[:refusals].map do |refusal|
57
+ { "verb" => refusal[:verb], "error" => refusal[:error], "kind" => refusal[:kind]&.split("::")&.last }
58
+ end
59
+ }
60
+ # `verb` and `ok` only, as `Hecks::Fuzzing::Differential` compares; omitted when none.
61
+ dry_runs = result[:dry_runs].map { |dry| { "verb" => dry[:verb].to_s, "ok" => dry[:ok] } }
62
+ surface["dry_runs"] = dry_runs unless dry_runs.empty?
63
+ surface
64
+ end
65
+
66
+ def event_surface(event)
67
+ { "name" => event[:name], "aggregate" => event[:aggregate], "id" => event[:id].to_s,
68
+ "payload" => JSON.parse(JSON.generate(event[:payload])) }
69
+ end
70
+
71
+ # The artifact's JSON for the script: live from a binary, or read from a file.
72
+ def artifact_output(domain, script, other)
73
+ case other
74
+ when "native" then run_binary(native_binary || missing_native!, script)
75
+ when "build" then run_binary(built_binary(domain), script)
76
+ when /\.wasm\z/
77
+ raise Failure, "#{other}: no such file — run hecks build_wasm #{domain} first" unless File.exist?(other)
78
+
79
+ run_process(["wasmtime", "run", other], File.read(script))
80
+ when ->(path) { !path.end_with?(".json") && File.file?(path) && File.executable?(path) }
81
+ # A pinned per-domain binary, as named by a sweep's `replay:` line.
82
+ run_binary(other, script)
83
+ else File.read(other)
84
+ end
85
+ end
86
+
87
+ def run_binary(binary, script) = run_process([binary], File.read(script))
88
+
89
+ def run_process(command, stdin_data)
90
+ stdout, status = Open3.capture2(*command, stdin_data: stdin_data)
91
+ raise Failure, "#{command.join(' ')} exited #{status.exitstatus}:\n#{stdout}" unless status.success?
92
+
93
+ stdout
94
+ end
95
+
96
+ def target_dir = ENV.fetch("CARGO_TARGET_DIR", File.join(RustBuild.rust_dir, "target"))
97
+
98
+ def native_binary
99
+ %w[release debug].map { |profile| File.join(target_dir, profile, "rust") }.find { |path| File.executable?(path) }
100
+ end
101
+
102
+ def missing_native!
103
+ raise Failure, "no native rust binary found under rust/target/{release,debug}/rust — run " \
104
+ "`cd rust && cargo build` first"
105
+ end
106
+
107
+ # Built on demand because `native` runs whichever domain the crate's default feature names.
108
+ def built_binary(domain)
109
+ feature = File.basename(domain.chomp("/"))
110
+ NativeBuild.build_rust_for(feature, RustBuild.rust_dir) or
111
+ raise Failure, "no `#{feature}` feature in rust/Cargo.toml — run hecks project_rust #{domain} first"
112
+ end
113
+
114
+ # Strips what a Rust run carries that Ruby has no analog for: `emitted_*` snapshot flags
115
+ # (ADR 0048) and the wall-clock `occurred_at` stamp two runs can never match.
116
+ def normalize(theirs)
117
+ if theirs["instances"].is_a?(Hash)
118
+ theirs["instances"].each_value do |record|
119
+ record.reject! { |key, _| key.start_with?("emitted_") } if record.is_a?(Hash)
120
+ end
121
+ end
122
+ theirs["events"].each { |event| event.delete("occurred_at") if event.is_a?(Hash) } if theirs["events"].is_a?(Array)
123
+ theirs
124
+ end
125
+
126
+ def report(domain, script, ours, theirs)
127
+ [ours, theirs].each { |side| drop_filter_kinds(side) }
128
+ mismatches = mismatches(ours, theirs)
129
+ if mismatches.empty?
130
+ puts "#{domain} / #{script}: matches."
131
+ return 0
132
+ end
133
+
134
+ warn "#{domain} / #{script}: #{mismatches.size} mismatch(es)\n\n#{mismatches.join("\n\n")}"
135
+ 1
136
+ end
137
+
138
+ def drop_filter_kinds(side)
139
+ return unless side["refusals"].is_a?(Array)
140
+
141
+ side["refusals"].each { |r| r.delete("kind") if r.is_a?(Hash) && r["verb"].to_s.start_with?(FILTER_VERB) }
142
+ end
143
+
144
+ def mismatches(ours, theirs)
145
+ found = %w[instances events refusals].filter_map do |key|
146
+ next if ours[key] == theirs[key]
147
+
148
+ "#{key}:\n ruby: #{ours[key].inspect}\n other: #{theirs[key].inspect}"
149
+ end
150
+ their_dry_runs = Array(theirs["dry_runs"]).map { |dry| dry.slice("verb", "ok") }
151
+ if (ours["dry_runs"] || []) != their_dry_runs
152
+ found << "dry_runs:\n ruby: #{ours['dry_runs'].inspect}\n other: #{their_dry_runs.inspect}"
153
+ end
154
+ found
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "json"
5
+ require "tmpdir"
6
+ require_relative "../cache_dir"
7
+ require_relative "../rust_build"
8
+ require_relative "conformance"
9
+
10
+ module Hecks
11
+ module RustBuild
12
+ # Runs generated fuzz sequences through `Conformance` against a native or `.wasm` artifact
13
+ # (ADR 0039), stopping at the first diverging seed with a reproduction command.
14
+ #
15
+ # ConformanceFuzz.call([domain, "native" | "path/to/module.wasm", seeds, steps])
16
+ #
17
+ # `seeds` and `steps` default to `SEEDS` and `STEPS` in the environment, then to 10 and 25.
18
+ module ConformanceFuzz
19
+ USAGE = "usage: hecks fuzz_conformance <domain> <native|path/to/module.wasm> [seeds] [steps]"
20
+
21
+ # What a domain directory's basename may be: a plain identifier, never `.` or `..`.
22
+ DOMAIN_NAME = /\A[A-Za-z0-9][A-Za-z0-9_-]*\z/
23
+
24
+ module_function
25
+
26
+ # @param argv [Array<String>] the domain, the artifact, and optionally seeds and steps
27
+ # @return [Integer] 0 when every sequence matched
28
+ # @raise [Failure] at the first seed that diverges, naming how to reproduce it
29
+ def call(argv)
30
+ require_relative "../../hecks"
31
+ require_relative "../fuzzing"
32
+ domain, artifact, seeds, steps = argv
33
+ raise Failure, USAGE unless domain && artifact
34
+
35
+ seeds = Integer(seeds || ENV["SEEDS"] || 10)
36
+ steps = Integer(steps || ENV["STEPS"] || 25)
37
+ scratch = scratch_dir(domain)
38
+ (1..seeds).each { |seed| replay(domain, artifact, seed, seeds, steps, scratch) }
39
+ FileUtils.rm_rf(scratch)
40
+ puts "#{domain}: #{seeds} generated sequence(s) x #{steps} step(s) each, all matched #{artifact}."
41
+ 0
42
+ end
43
+
44
+ # A fresh directory per run, under the cache root outside the gem, so concurrent runs never
45
+ # share one; it is kept when a seed diverges, so the failing script outlives the run.
46
+ #
47
+ # @param domain [String] the domain's directory; its basename names the scratch directory
48
+ # @return [String] the new directory
49
+ # @raise [Failure] when the basename cannot safely name a directory
50
+ def scratch_dir(domain)
51
+ name = File.basename(domain.to_s.chomp("/"))
52
+ raise Failure, "#{USAGE}\n`#{domain}` is not a domain directory name" unless name.match?(DOMAIN_NAME)
53
+
54
+ parent = Hecks::CacheDir.path("rust_conformance_fuzz")
55
+ FileUtils.mkdir_p(parent)
56
+ Dir.mktmpdir("#{name}-", parent)
57
+ end
58
+
59
+ def replay(domain, artifact, seed, seeds, steps, scratch)
60
+ sequence = Hecks::Fuzzing::SequenceGenerator.generate(domain, seed: seed, steps: steps)
61
+ path = File.join(scratch, "seed-#{seed}.json")
62
+ File.write(path, JSON.generate({ "steps" => sequence }))
63
+ return if Conformance.call([domain, path, artifact]).zero?
64
+
65
+ raise Failure, "hecks fuzz_conformance: seed #{seed}/#{seeds} diverged against #{artifact} " \
66
+ "(see the conformance output above) — " \
67
+ "reproduce with: hecks check_conformance #{domain} #{path} #{artifact}"
68
+ end
69
+ end
70
+ end
71
+ end