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,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "json"
5
+ require "open3"
6
+ require "yaml"
7
+
8
+ module Hecks
9
+ module CLI
10
+ # The command behind `hecks refresh_runtime_baseline`: regenerates the committed runtime
11
+ # baselines the sharded CI jobs group from. It prefers one CI run's timings; a local run is
12
+ # the fallback.
13
+ #
14
+ # hecks refresh_runtime_baseline --from-run <run-id> # preferred: one CI run's timings
15
+ # hecks refresh_runtime_baseline [workers] # fallback: this machine only
16
+ #
17
+ # `--from-run` reads the runtime-log artifacts of a successful run and writes a family only
18
+ # when every leg uploaded one; a partial set would skew the split. Runner seconds differ from a
19
+ # laptop's, so a local baseline misses subprocess-heavy specs. The local mode refuses a red run.
20
+ module RefreshRspecRuntimeBaseline
21
+ REPO = "heckslabs/hecks"
22
+ USAGE = "usage: hecks refresh_runtime_baseline --from-run <run-id> | [workers]"
23
+
24
+ # Only `spec/..._spec.rb:<seconds>` lines; RSpec's "Run options" line must not be copied
25
+ # into a committed file. Seconds can arrive in exponent form (`9.69e-05`).
26
+ RUNTIME_LINE = %r{\A(?<file>spec/\S+_spec\.rb):(?<seconds>\d+(?:\.\d+)?(?:e-?\d+)?)\z}
27
+
28
+ module_function
29
+
30
+ # Refreshes the baselines from a CI run or a local run.
31
+ #
32
+ # @param argv [Array<String>] `--from-run RUN_ID`, or optionally the local worker count
33
+ # @param root [String] the checkout whose `.github/` baselines are rewritten
34
+ # @param out [IO] where what was written goes
35
+ # @return [Integer] the exit status, 0 once written
36
+ # @raise [SystemExit] when the run is unreadable or unsuccessful, the local suite is red, or
37
+ # no artifact family is complete
38
+ def call(argv, root:, out: $stdout)
39
+ if argv.first == "--from-run"
40
+ from_run(root, argv.fetch(1) { abort USAGE }, out)
41
+ else
42
+ from_local(root, argv.fetch(0, "6"), out)
43
+ end
44
+ 0
45
+ end
46
+
47
+ # Reads the leg counts from the workflow matrix so a resize cannot leave a stale count here.
48
+ #
49
+ # @param root [String] the checkout
50
+ # @return [Hash{String => Hash}] each artifact family's leg count and baseline file
51
+ def families(root)
52
+ {
53
+ "rspec-runtime-group" => { legs: matrix_legs(root, "ci-rspec.yml", "rspec_shard"),
54
+ baseline: ".github/rspec_runtime_baseline.log" },
55
+ "postgres-io-runtime-group" => { legs: matrix_legs(root, "ci-postgres-io-parallel.yml",
56
+ "rspec_postgres_io_parallel_shard"),
57
+ baseline: ".github/postgres_io_runtime_baseline.log" }
58
+ }
59
+ end
60
+
61
+ # @param root [String] the checkout
62
+ # @param workflow [String] the workflow file's name
63
+ # @param job [String] the job whose matrix is counted
64
+ # @return [Integer] how many groups the job's matrix has
65
+ def matrix_legs(root, workflow, job)
66
+ YAML.load_file(File.join(root, ".github/workflows", workflow)).dig("jobs", job, "strategy", "matrix", "group").size
67
+ end
68
+
69
+ # @param paths [Array<String>] runtime logs
70
+ # @return [Array<String>] one sorted `file:seconds` line per spec file
71
+ def baseline_lines(paths)
72
+ seconds_by_file = paths.flat_map { |path| File.readlines(path) }.filter_map do |line|
73
+ match = RUNTIME_LINE.match(line.strip) or next
74
+ [match[:file], Float(match[:seconds])]
75
+ end.to_h
76
+ seconds_by_file.map { |file, seconds| format("%<file>s:%<seconds>.2f", file: file, seconds: seconds) }.sort
77
+ end
78
+
79
+ # @param root [String] the checkout
80
+ # @param relative [String] the baseline's path under the checkout
81
+ # @param lines [Array<String>] the baseline's lines
82
+ # @param out [IO] where the note goes
83
+ # @return [void]
84
+ def write_baseline(root, relative, lines, out)
85
+ File.write(File.join(root, relative), "#{lines.join("\n")}\n")
86
+ out.puts "wrote #{relative}: #{lines.size} spec files"
87
+ end
88
+
89
+ # @param run_id [String] the CI run's id
90
+ # @return [void]
91
+ # @raise [SystemExit] when `gh` cannot read the run or it did not succeed
92
+ def refuse_unless_successful!(run_id)
93
+ out, status = Open3.capture2("gh", "run", "view", run_id, "-R", REPO, "--json", "conclusion")
94
+ abort "hecks refresh_runtime_baseline: could not read run #{run_id}" unless status.success?
95
+
96
+ conclusion = JSON.parse(out)["conclusion"]
97
+ return if conclusion == "success"
98
+
99
+ abort "hecks refresh_runtime_baseline: run #{run_id} concluded #{conclusion.inspect} — " \
100
+ "refusing to write a baseline from anything but a successful run"
101
+ end
102
+
103
+ # @param root [String] the checkout
104
+ # @param run_id [String] the CI run's id
105
+ # @param out [IO] where what was written goes
106
+ # @return [void]
107
+ # @raise [SystemExit] when no family of artifacts is complete
108
+ def from_run(root, run_id, out)
109
+ refuse_unless_successful!(run_id)
110
+ dir = File.join(root, "tmp/ci-runtime-#{run_id}")
111
+ FileUtils.rm_rf(dir)
112
+
113
+ written = families(root).count do |prefix, family|
114
+ system("gh", "run", "download", run_id, "-R", REPO, "-p", "#{prefix}-*", "-D", dir,
115
+ out: File::NULL, err: File::NULL)
116
+ logs = Dir[File.join(dir, "#{prefix}-*", "*.log")]
117
+ legs = logs.map { |path| File.basename(File.dirname(path)) }.uniq.size
118
+ if legs == family[:legs]
119
+ write_baseline(root, family[:baseline], baseline_lines(logs), out)
120
+ true
121
+ else
122
+ warn "skipping #{family[:baseline]}: run #{run_id} has #{legs} of #{family[:legs]} #{prefix}-* artifacts"
123
+ false
124
+ end
125
+ end
126
+ return unless written.zero?
127
+
128
+ abort "hecks refresh_runtime_baseline: run #{run_id} had no complete artifact family — nothing written"
129
+ end
130
+
131
+ # @param root [String] the checkout
132
+ # @param workers [String] how many `parallel_rspec` workers to time with
133
+ # @param out [IO] where what was written goes
134
+ # @return [void]
135
+ # @raise [SystemExit] when the local suite is red
136
+ def from_local(root, workers, out)
137
+ raw = "tmp/rspec_runtime_baseline.raw.log"
138
+ FileUtils.mkdir_p(File.join(root, "tmp"))
139
+ FileUtils.rm_f(File.join(root, raw))
140
+
141
+ formatters = "--tag ~io --tag ~fuzzing --format progress --format ParallelTests::RSpec::RuntimeLogger --out #{raw}"
142
+ green = system("bundle", "exec", "parallel_rspec", "spec", "-n", workers, "-o", formatters, chdir: root)
143
+ abort "hecks refresh_runtime_baseline: the spec run failed — not writing a baseline from a red suite" unless green
144
+
145
+ write_baseline(root, families(root).fetch("rspec-runtime-group")[:baseline],
146
+ baseline_lines([File.join(root, raw)]), out)
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+ require_relative "../release/runner"
5
+
6
+ module Hecks
7
+ module CLI
8
+ # The command behind `hecks publish`: tags the merged release commit, publishes the gem, and
9
+ # gets `@hecks/client` published to npm. See "Releasing" in CONTRIBUTING.md for the release PR.
10
+ #
11
+ # hecks publish --dry-run # every check and build, nothing tagged or published
12
+ # hecks publish # tag, gem, then wait for CI to publish the client
13
+ module Release
14
+ # The flags a run starts with, before any is named.
15
+ DEFAULT_FLAGS = { dry_run: false, gem_only: false, npm_only: false, npm_local: false,
16
+ no_wait: false, yes: false }.freeze
17
+
18
+ module_function
19
+
20
+ # Parses the flags and runs the release.
21
+ #
22
+ # @param argv [Array<String>] the command line's flags
23
+ # @param root [String] the repository root the release runs in
24
+ # @return [Integer] the release's exit status, or 2 for a flag it does not know
25
+ def call(argv, root:)
26
+ argv = argv.dup
27
+ flags = DEFAULT_FLAGS.dup
28
+ parser = parser_for(flags)
29
+ begin
30
+ parser.parse!(argv)
31
+ raise OptionParser::InvalidArgument, argv.join(" ") unless argv.empty?
32
+
33
+ return 0 if flags.delete(:help)
34
+
35
+ options = Hecks::Release::Runner::Options.new(**flags)
36
+ rescue OptionParser::ParseError, ArgumentError => e
37
+ warn "hecks publish: #{e.message}"
38
+ warn parser
39
+ return 2
40
+ end
41
+
42
+ Hecks::Release::Runner.new(root: root, options: options).call
43
+ end
44
+
45
+ # @param flags [Hash{Symbol => Boolean}] filled in as the parser reads each flag
46
+ # @return [OptionParser] the parser for `hecks publish`'s flags
47
+ def parser_for(flags)
48
+ OptionParser.new do |opts|
49
+ opts.banner = "Usage: hecks publish [--dry-run] [--gem-only | --npm-only] [--npm-local | --no-wait] [--yes]"
50
+ opts.separator("Tags the merged release commit, publishes the gem, and gets @hecks/client published.")
51
+ opts.separator("By default CI publishes the client from the tag and this waits for it.")
52
+ opts.on("--dry-run", "run every check and build; tag, push and publish nothing") { flags[:dry_run] = true }
53
+ opts.on("--gem-only", "publish the gem only") { flags[:gem_only] = true }
54
+ opts.on("--npm-only", "skip the gem: wait for CI's publish, or with --npm-local publish from here") do
55
+ flags[:npm_only] = true
56
+ end
57
+ opts.on("--npm-local", "publish @hecks/client from this machine (first publish, or CI is down)") do
58
+ flags[:npm_local] = true
59
+ end
60
+ opts.on("--no-wait", "do not wait for CI's publish to reach npm") { flags[:no_wait] = true }
61
+ opts.on("--yes", "answer the confirmations yes") { flags[:yes] = true }
62
+ opts.on("--help", "print this usage") do
63
+ puts opts
64
+ flags[:help] = true
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require_relative "../release/runner/commands"
5
+ require_relative "../hecks/adapters/codebase/gem_registry"
6
+ require_relative "../version"
7
+
8
+ module Hecks
9
+ module CLI
10
+ # The command behind `hecks publish_gem` and the release runner's gem step: builds the hecks
11
+ # gem and pushes it to rubygems.org, using a push-scoped API key from 1Password instead of
12
+ # `~/.gem/credentials`. It pushes the version it is given, `Hecks::VERSION` by default.
13
+ module ReleaseGem
14
+ module_function
15
+
16
+ # Builds and pushes the gem, refusing unless `op` is installed and the JS client is at the
17
+ # same version (`spec/hecks_client_version_spec.rb` pins that rule too).
18
+ #
19
+ # @param root [String] the repository root the gem is built from
20
+ # @param commands [#run!, #capture, nil] runs the child commands; a real one when nil
21
+ # @param out [IO] where progress goes
22
+ # @param err [IO] where a refusal goes
23
+ # @param version [String] the version to build and push
24
+ # @return [Integer] 0 once pushed, 1 when refused
25
+ def call(root:, commands: nil, out: $stdout, err: $stderr, version: Hecks::VERSION)
26
+ Dir.chdir(root) do
27
+ commands ||= Hecks::Release::Runner::Commands.new
28
+ registry = Hecks::Adapters::Codebase::GemRegistry.new(root: root, commands: commands)
29
+ refusal = refusal_for(root, commands, version)
30
+ if refusal
31
+ err.puts refusal
32
+ return 1
33
+ end
34
+
35
+ out.puts "Building and pushing hecks-#{version}.gem to rubygems.org " \
36
+ "(1Password will prompt for Touch ID)..."
37
+ registry.push!(version)
38
+ out.puts "Released hecks #{version}."
39
+ 0
40
+ end
41
+ end
42
+
43
+ # @param root [String] the repository root
44
+ # @param commands [#run!, #capture] runs the child commands
45
+ # @param version [String] the version being released
46
+ # @return [String, nil] why the release cannot start, or nil when it can
47
+ def refusal_for(root, commands, version = Hecks::VERSION)
48
+ unless Hecks::Adapters::Codebase::SecretVault.new(commands: commands).installed?
49
+ return "1Password CLI (op) not found. Install: brew install 1password-cli"
50
+ end
51
+
52
+ client_version = JSON.parse(File.read(File.join(root, "packages/hecks-client/package.json"))).fetch("version")
53
+ return nil if client_version == version
54
+
55
+ "packages/hecks-client is at #{client_version} but Hecks::VERSION is #{version}; bump the package first."
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "open3"
5
+
6
+ module Hecks
7
+ module CLI
8
+ # The command behind `hecks list_io_parallel_specs`: prints, one per line, the spec files with
9
+ # at least one example matching an RSpec tag filter. ci.yml hands the list to `parallel_rspec`
10
+ # instead of the whole `spec` directory.
11
+ #
12
+ # Given every spec file, parallel_tests ignores the runtime log unless it covers about 67% of
13
+ # them, so a tag-filtered job silently groups by file size. A dry run, not grep, picks the
14
+ # files so inherited tags, `~fuzzing` and shared examples count.
15
+ #
16
+ # The list is committed (`.github/postgres_io_spec_files.txt`). `--write FILE` refreshes it;
17
+ # `--check FILE` (CI's postgres_io_file_list job) fails on a stale list.
18
+ #
19
+ # hecks list_io_parallel_specs '<exclude-pattern regex>' -- --tag io --tag ~fuzzing
20
+ module RspecIoParallelFiles
21
+ module_function
22
+
23
+ # Lists the matching files, writes them, or checks a committed list.
24
+ #
25
+ # @param argv [Array<String>] `--check FILE` or `--write FILE` optionally, the exclude
26
+ # pattern, `--`, then the RSpec tag arguments
27
+ # @param root [String] the checkout whose `spec/` is searched; the dry run runs there
28
+ # @param out [IO] where the list goes when neither flag is given
29
+ # @param err [IO] where the tally and any staleness go
30
+ # @return [Integer] the exit status, 0 once done
31
+ # @raise [SystemExit] on a bad command line, an empty candidate set, a failed dry run, or a
32
+ # stale committed list
33
+ def call(argv, root: Dir.pwd, out: $stdout, err: $stderr)
34
+ argv = argv.dup
35
+ mode, list_path = argv.shift(2) if %w[--check --write].include?(argv.first)
36
+ abort "hecks list_io_parallel_specs: #{mode} needs a file path" if mode && list_path.nil?
37
+
38
+ exclude_arg, tag_args = parse(argv)
39
+ candidates = Dir.glob("spec/**/*_spec.rb", base: root).grep_v(Regexp.new(exclude_arg)).sort
40
+ refuse_empty_candidates(candidates)
41
+ files, example_count = matching_files(candidates, tag_args, root, err)
42
+ err.puts "hecks list_io_parallel_specs: #{files.size} of #{candidates.size} candidate files carry a " \
43
+ "matching example (#{example_count} examples total) under `#{tag_args.join(' ')}`"
44
+
45
+ case mode
46
+ when "--write" then File.write(File.expand_path(list_path, root), "#{files.join("\n")}\n")
47
+ when "--check" then check(files, File.expand_path(list_path, root), list_path, exclude_arg, tag_args, err)
48
+ else out.puts files
49
+ end
50
+ 0
51
+ end
52
+
53
+ # @param argv [Array<String>] the exclude pattern, `--`, and the tag arguments
54
+ # @return [Array(String, Array<String>)] the pattern and the tag arguments
55
+ # @raise [SystemExit] with the usage line when either is missing
56
+ def parse(argv)
57
+ exclude_arg, *rest = argv
58
+ separator = rest.index("--")
59
+ usage = "usage: hecks list_io_parallel_specs '<exclude-pattern regex>' -- <rspec tag args...>"
60
+ abort usage unless exclude_arg && separator
61
+ tag_args = rest[(separator + 1)..]
62
+ abort usage if tag_args.nil? || tag_args.empty?
63
+
64
+ [exclude_arg, tag_args]
65
+ end
66
+
67
+ # @param candidates [Array<String>] the spec files left after the exclude pattern
68
+ # @return [void]
69
+ # @raise [SystemExit] when there are none, which means a broken pattern or an empty checkout
70
+ def refuse_empty_candidates(candidates)
71
+ return unless candidates.empty?
72
+
73
+ abort "hecks list_io_parallel_specs: found ZERO candidate spec files after applying the exclude " \
74
+ "pattern — that's almost certainly a broken pattern or an empty checkout, not a real empty " \
75
+ "test suite. Refusing to silently hand parallel_rspec nothing to run."
76
+ end
77
+
78
+ # Asks RSpec for a dry run of the candidates under the tag filter.
79
+ #
80
+ # @param candidates [Array<String>] the spec files to ask about
81
+ # @param tag_args [Array<String>] the RSpec tag arguments
82
+ # @param root [String] the directory the dry run runs in
83
+ # @param err [IO] where a failed run's stderr goes
84
+ # @return [Array(Array<String>, Integer)] the files with a matching example, and how many
85
+ # examples matched
86
+ # @raise [SystemExit] when the dry run fails, prints no JSON, or matches nothing
87
+ def matching_files(candidates, tag_args, root, err)
88
+ command = ["bundle", "exec", "rspec", "--dry-run", *tag_args, "--format", "json", *candidates]
89
+ stdout, stderr, status = Open3.capture3(*command, chdir: root)
90
+ unless status.success?
91
+ err.puts stderr
92
+ abort "hecks list_io_parallel_specs: `#{command.join(' ')}` exited #{status.exitstatus} — " \
93
+ "see stderr above. Refusing to guess a file list from a failed dry run."
94
+ end
95
+
96
+ start = stdout.index('{"version"')
97
+ abort "hecks list_io_parallel_specs: couldn't find RSpec's JSON output in the dry-run's stdout:\n#{stdout}" unless start
98
+
99
+ examples = JSON.parse(stdout[start..])["examples"] || []
100
+ files = examples.map { |e| e.fetch("file_path").sub(%r{\A\./}, "") }.uniq.sort
101
+ return [files, examples.size] unless files.empty?
102
+
103
+ abort "hecks list_io_parallel_specs: #{candidates.size} candidate files, but the dry run matched ZERO " \
104
+ "examples under `#{tag_args.join(' ')}` — that's almost certainly a broken tag filter, not a " \
105
+ "real empty set. Refusing to silently hand parallel_rspec nothing to run."
106
+ end
107
+
108
+ # @param files [Array<String>] the files that carry a matching example
109
+ # @param path [String] the committed list's absolute path
110
+ # @param shown [String] the path as the person typed it
111
+ # @param exclude_arg [String] the exclude pattern, for the refresh command
112
+ # @param tag_args [Array<String>] the tag arguments, for the refresh command
113
+ # @param err [IO] where the difference goes
114
+ # @return [void]
115
+ # @raise [SystemExit] when the committed list differs
116
+ def check(files, path, shown, exclude_arg, tag_args, err)
117
+ committed = File.exist?(path) ? File.readlines(path, chomp: true).reject(&:empty?) : []
118
+ missing = files - committed
119
+ stale = committed - files
120
+ return if missing.empty? && stale.empty?
121
+
122
+ err.puts "#{shown} is out of date."
123
+ err.puts named("carries a matching example but is not listed (would never run in the shards)", missing) if missing.any?
124
+ err.puts named("listed but carries no matching example any more", stale) if stale.any?
125
+ abort "Refresh it and commit the result:\n " \
126
+ "hecks list_io_parallel_specs --write #{shown} '#{exclude_arg}' -- #{tag_args.join(' ')}"
127
+ end
128
+
129
+ # @param heading [String] what the paths have in common
130
+ # @param paths [Array<String>] the paths
131
+ # @return [String] the heading with the paths under it
132
+ def named(heading, paths)
133
+ " #{heading}:\n #{paths.join("\n ")}"
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "parallel_tests/test/runner"
4
+
5
+ module Hecks
6
+ module CLI
7
+ # The command behind `hecks shard_specs`: prints the spec files assigned to one group
8
+ # (1-indexed) of a runtime-balanced N-way split. `rspec_shard` in ci-rspec.yml passes the
9
+ # committed runtime baseline so every leg agrees.
10
+ #
11
+ # It does not use `parallel_rspec --only-group`, which forces one process per leg; this suite
12
+ # is CPU-bound, so each leg would use one of four vCPUs. The group's list comes from
13
+ # `tests_in_groups`, leaving `parallel_rspec` its own workers.
14
+ module RspecShardFiles
15
+ USAGE = "usage: hecks shard_specs <group 1-indexed> <num_groups> [runtime_log_path]"
16
+
17
+ module_function
18
+
19
+ # Prints the files of one group.
20
+ #
21
+ # @param argv [Array<String>] the group, the number of groups, and optionally a runtime log
22
+ # @param root [String] the checkout whose `spec/` is split
23
+ # @param out [IO] where the files go, one per line
24
+ # @param err [IO] where the group's size goes
25
+ # @return [Integer] the exit status, 0 once printed
26
+ # @raise [SystemExit] on missing or out-of-range arguments, or when there are no spec files
27
+ def call(argv, root: Dir.pwd, out: $stdout, err: $stderr)
28
+ group_arg, num_groups_arg, runtime_log = argv
29
+ abort USAGE unless group_arg && num_groups_arg
30
+
31
+ group = Integer(group_arg)
32
+ num_groups = Integer(num_groups_arg)
33
+ abort "group must be between 1 and #{num_groups}, got #{group}" unless (1..num_groups).cover?(group)
34
+
35
+ files = Dir.glob("spec/**/*_spec.rb", base: root)
36
+ if files.empty?
37
+ abort "hecks shard_specs: found ZERO spec files under spec/ — " \
38
+ "refusing to hand parallel_rspec nothing to run"
39
+ end
40
+
41
+ groups = Dir.chdir(root) { groups_of(files, num_groups, runtime_log && File.expand_path(runtime_log, root)) }
42
+ selected = groups[group - 1] || []
43
+ err.puts "hecks shard_specs: group #{group}/#{num_groups} has #{selected.size} of #{files.size} files"
44
+ out.puts selected
45
+ 0
46
+ end
47
+
48
+ # Splits the files by recorded runtime, falling back to file size on a cold cache.
49
+ #
50
+ # `allowed_missing_percent` covers gaps inside a log, not a missing file (`Runner.runtimes`
51
+ # raises `Errno::ENOENT`).
52
+ #
53
+ # @param files [Array<String>] the spec files, relative to the checkout
54
+ # @param num_groups [Integer] how many groups to split into
55
+ # @param runtime_log [String, nil] the runtime log's path, used when the file exists
56
+ # @return [Array<Array<String>>] the files of each group
57
+ def groups_of(files, num_groups, runtime_log)
58
+ options = { group_by: :runtime, allowed_missing_percent: 100 }
59
+ options[:runtime_log] = runtime_log if runtime_log && File.exist?(runtime_log)
60
+ ParallelTests::Test::Runner.tests_in_groups(files, num_groups, **options)
61
+ rescue Errno::ENOENT
62
+ ParallelTests::Test::Runner.tests_in_groups(files, num_groups, group_by: :filesize)
63
+ end
64
+ end
65
+ end
66
+ end
data/lib/hecks/cli/run.rb CHANGED
@@ -6,7 +6,7 @@ require_relative "../ports/persistence/plugins/era"
6
6
 
7
7
  module Hecks
8
8
  module CLI
9
- # The command behind `bin/run` and `hecks run`: dispatches one verb from the
9
+ # The command behind `hecks run`: dispatches one verb from the
10
10
  # command line, or executes a JSON step list and reports instances, events,
11
11
  # refusals, reactions, sagas and query rows.
12
12
  #
@@ -55,8 +55,8 @@ module Hecks
55
55
  # near miss deserves the runner's "did you mean" rather than the step-list
56
56
  # form's "no such script".
57
57
  def cli_form(domain, argv, program)
58
- runtime = Hecks.boot(domain, install_facade: false)
59
- text, status = Facade::CliRunner.call(runtime: runtime, argv: argv, program: program)
58
+ runtime = Hecks.boot(domain, install_doors: false)
59
+ text, status = Doors::CliRunner.call(runtime: runtime, argv: argv, program: program)
60
60
  status.zero? ? puts(text) : abort(text)
61
61
  exit 0
62
62
  end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require_relative "../../hecks"
5
+ require_relative "../fuzzing"
6
+
7
+ module Hecks
8
+ module CLI
9
+ # The command behind `hecks seed_semantics_corpus`: seeds (or deliberately re-seeds) the
10
+ # `expect` block of semantics-corpus fixtures from the Ruby runtime. Review the result
11
+ # against `docs/semantics/bluebook-semantics.md` before committing.
12
+ #
13
+ # hecks seed_semantics_corpus # fill fixtures missing an expect
14
+ # SEED=refusal_kind_lifecycle hecks seed_semantics_corpus # re-seed one, deliberately
15
+ #
16
+ # The seeded expectation is the definition, not a recording: both runtimes are compared to it.
17
+ # `occurred_at` is stripped from events (C7.3/C9.1); refusals keep verb, error and kind (C8.2).
18
+ module SeedSemanticsCorpus
19
+ module_function
20
+
21
+ # Seeds the fixtures that need it and reports which.
22
+ #
23
+ # @param root [String] the checkout whose `spec/corpus/semantics/` is seeded
24
+ # @param env [Hash{String => String}] `SEED` names the one fixture to re-seed
25
+ # @param out [IO] where the report goes
26
+ # @return [Integer] the exit status, always 0
27
+ def call(root:, env: ENV, out: $stdout)
28
+ only = env.fetch("SEED", nil)
29
+ seeded = Dir.glob(File.join(root, "spec/corpus/semantics", "*.json")).filter_map do |path|
30
+ fixture = JSON.parse(File.read(path))
31
+ name = File.basename(path, ".json")
32
+ next if only ? only != name : fixture.key?("expect")
33
+
34
+ fixture["expect"] = expectation_for(root, fixture)
35
+ File.write(path, "#{JSON.pretty_generate(fixture)}\n")
36
+ name
37
+ end
38
+ report(out, seeded)
39
+ 0
40
+ end
41
+
42
+ # Replays the fixture's script against the Ruby runtime. Each refusal's `"kind"` is reduced
43
+ # to its class name; `occurred_at` is stripped from events.
44
+ #
45
+ # @param root [String] the checkout the fixture's domain path is relative to
46
+ # @param fixture [Hash] the parsed fixture: `domain` and `steps`
47
+ # @return [Hash{String => Array}] its `"expect"` block: refusals, instances, events
48
+ def expectation_for(root, fixture)
49
+ result = Hecks::Fuzzing::Replay.call(File.join(root, fixture["domain"]), fixture["steps"])
50
+ {
51
+ "refusals" => JSON.parse(JSON.generate(result[:refusals])).each { |r| r["kind"] = r["kind"]&.split("::")&.last },
52
+ "instances" => JSON.parse(JSON.generate(result[:instances])),
53
+ "events" => JSON.parse(JSON.generate(result[:events])).each { |e| e.delete("occurred_at") }
54
+ }
55
+ end
56
+
57
+ # @param out [IO] where the report goes
58
+ # @param seeded [Array<String>] the fixtures written
59
+ # @return [void]
60
+ def report(out, seeded)
61
+ if seeded.empty?
62
+ out.puts "nothing to seed — every fixture carries its expect (pass SEED=<name> to re-seed one deliberately)"
63
+ else
64
+ out.puts "seeded: #{seeded.join(', ')}"
65
+ out.puts "review each expect against docs/semantics/bluebook-semantics.md before committing — " \
66
+ "it is the definition now"
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../mcp_stdio_guard"
4
+
5
+ module Hecks
6
+ module CLI
7
+ # The command behind `hecks serve_query_ir_mcp` and `hecks serve_query_ir_mcp`: an MCP server
8
+ # exposing `Hecks::QueryIR`'s queries as tools, over newline-delimited JSON-RPC on stdio.
9
+ #
10
+ # Stdio only and unauthenticated (ADR 0062); the protocol lives in `Hecks::QueryIrMcp` and the
11
+ # query logic in `Hecks::QueryIR`. The stdio guard runs before the runtime loads, so a refused
12
+ # transport never boots anything.
13
+ module ServeQueryIrMcp
14
+ module_function
15
+
16
+ # Enforces stdio, then serves until stdin closes.
17
+ #
18
+ # @param argv [Array<String>] the arguments the server was started with
19
+ # @return [void]
20
+ # @raise [SystemExit] when the guard refuses the transport
21
+ def call(argv)
22
+ Hecks::McpStdioGuard.enforce_stdio!(server: "hecks-query-ir")
23
+
24
+ require_relative "../../hecks"
25
+ require_relative "../query_ir_mcp"
26
+ Hecks::QueryIrMcp.start(argv: argv)
27
+ end
28
+ end
29
+ end
30
+ end