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,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "qa_tool"
4
+
5
+ module Hecks
6
+ module Adapters
7
+ # The `SweepTools` port's adapter: answers `Sweep`'s queries by running the QA commands that
8
+ # work a whole pass: a tick, a sweep, the Heki migration, the ledger's role and a concurrency
9
+ # racer. A run that finds something ends with status 2, which is an answer; anything else the
10
+ # command calls an error is refused with its report.
11
+ class SweepTools < QaTool
12
+ # @return [Hash] `text:` one tick's report
13
+ def tick
14
+ run_command("qa_tick", answers: [0, 2])
15
+ end
16
+
17
+ # @param target [Hash, String, nil] the target to sweep; the rotation's pick when absent
18
+ # @param arguments [Hash, String, nil] the sweep's own flags, such as `--all --seeds 5`
19
+ # @return [Hash] `text:` the sweep's report
20
+ def run(target: nil, arguments: nil)
21
+ run_command("qa_sweep", *plain(target), *words(arguments), answers: [0, 2])
22
+ end
23
+
24
+ # @param domain [Hash, String] the domain directory whose bindings are the destination
25
+ # @param data [Hash, String] the Heki data directory read from
26
+ # @param arguments [Hash, String, nil] aggregate names, and `--force` to apply
27
+ # @return [Hash] `text:` what was, or would be, migrated
28
+ def migrate_ledger_from_heki(domain:, data:, arguments: nil)
29
+ run_command("qa_postgres_migrate", plain(domain), plain(data), *words(arguments))
30
+ end
31
+
32
+ # @param database [Hash, String] the database the role is made to own
33
+ # @param role [Hash, String, nil] the role's name; `hecks_qa` when absent
34
+ # @return [Hash] `text:` what was done, and what already held
35
+ def create_ledger_role(database:, role: nil)
36
+ run_command("qa_postgres_role", plain(database), *(role ? ["--role", plain(role)] : []))
37
+ end
38
+
39
+ # @param domain [Hash, String] the domain directory
40
+ # @param database [Hash, String] the scratch database
41
+ # @param schema [Hash, String] the schema this racer dispatches into
42
+ # @param verb [Hash, String] the command to dispatch
43
+ # @param step_arguments [Hash, String] the command's arguments, as JSON
44
+ # @return [Hash] `text:` `succeeded`, `refused` or `crashed:<class>: <message>`
45
+ def race(domain:, database:, schema:, verb:, step_arguments:)
46
+ run_command("qa_concurrency_racer", plain(domain), plain(database), plain(schema), plain(verb),
47
+ plain(step_arguments))
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,7 @@
1
+ # Binds the `TargetTools` port to `TargetTools`, which answers `Target`'s queries: the seed, generated domains, the miner, the novelty gate and the scan.
2
+ # Required by name, not relative, so a copy of this directory shares the one implementation loaded.
3
+ require "hecks/quality_control/adapters/target_tools"
4
+
5
+ Hecks.adapter "TargetTools" do
6
+ port "TargetTools"
7
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "qa_tool"
4
+
5
+ module Hecks
6
+ module Adapters
7
+ # The `TargetTools` port's adapter: answers `Target`'s queries by running the QA commands that
8
+ # enrol, generate and judge chapters: the seed, the generated-domain check, the combination
9
+ # miner, the novelty gate and the external-domain scan. A finding or a judgment is an answer;
10
+ # anything else the command calls an error is refused with its report.
11
+ class TargetTools < QaTool
12
+ # @return [Hash] `text:` each corpus domain's outcome: identified, or already on file
13
+ def seed
14
+ run_command("qa_seed_targets")
15
+ end
16
+
17
+ # @param arguments [Hash, String, nil] the command's own flags, such as `--domains 3 --rust`
18
+ # @return [Hash] `text:` the report of the generated domains' check
19
+ def check_generated_domains(arguments: nil)
20
+ run_command("qa_generated_domains", *words(arguments), answers: [0, 2])
21
+ end
22
+
23
+ # @param arguments [Hash, String, nil] the command's own flags, such as `--brief`
24
+ # @return [Hash] `text:` the miner's report
25
+ def mine_combinations(arguments: nil)
26
+ run_command("qa_mine_combinations", *words(arguments), answers: [0, 1])
27
+ end
28
+
29
+ # @param domain [Hash, String] the candidate stress domain's path
30
+ # @param arguments [Hash, String, nil] `--against` and the paths to compare with
31
+ # @return [Hash] `text:` the new form pairs it earns its place with, or that it earns none
32
+ def judge_novelty(domain:, arguments: nil)
33
+ run_command("qa_domain_novelty", plain(domain), *words(arguments), answers: [0, 1])
34
+ end
35
+
36
+ # @param arguments [Hash, String, nil] the command's own flags, such as `--max-depth 4`
37
+ # @return [Hash] `text:` the sibling domains that depend on hecks and are not enrolled
38
+ def discover_external_domains(arguments: nil)
39
+ run_command("qa_discover_external_domains", *words(arguments))
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hecks
4
+ module QualityControlCli
5
+ # How one QA command starts another as its own OS process, from `lib/` rather than through a
6
+ # launcher script: a sweep spawns racers and generated-domain checks, and a launcher query runs
7
+ # any of them. A fresh process is the point: its own boot, its own Postgres connections, and a
8
+ # crash that ends only itself.
9
+ module Child
10
+ # Each command's class, and how it is called: `:argv_root` is `call(ARGV, root:)`, `:argv` is
11
+ # `call(ARGV)` and `:root` is `call(root:)`. A command lives in
12
+ # `hecks/quality_control/cli/<command>`.
13
+ COMMANDS = {
14
+ "qa_tick" => ["QaTick", :argv_root],
15
+ "qa_sweep" => ["QaSweep", :argv_root],
16
+ "qa_pr_check" => ["QaPrCheck", :argv_root],
17
+ "qa_generated_domains" => ["QaGeneratedDomains", :argv_root],
18
+ "qa_mine_combinations" => ["QaMineCombinations", :argv_root],
19
+ "qa_domain_novelty" => ["QaDomainNovelty", :argv_root],
20
+ "qa_discover_external_domains" => ["QaDiscoverExternalDomains", :argv_root],
21
+ "qa_concurrency_racer" => ["QaConcurrencyRacer", :argv_root],
22
+ "qa_postgres_migrate" => ["QaPostgresMigrate", :argv],
23
+ "qa_postgres_role" => ["QaPostgresRole", :argv],
24
+ "qa_seed_angles" => ["QaSeedAngles", :root],
25
+ "qa_seed_targets" => ["QaSeedTargets", :root]
26
+ }.freeze
27
+
28
+ module_function
29
+
30
+ # @param root [String] the checkout whose `lib/` holds the command and where it runs
31
+ # @param command [String] a key of `COMMANDS`
32
+ # @param args [Array<String>] the command's own arguments
33
+ # @return [Array<String>] the argv that runs it under Bundler, ready for `Process.spawn`
34
+ # or `Open3` with `chdir: root`
35
+ # @raise [KeyError] when the command is not one of `COMMANDS`
36
+ def argv(root, command, *args)
37
+ klass, style = COMMANDS.fetch(command)
38
+ call = { argv_root: "ARGV, root: #{root.inspect}", argv: "ARGV", root: "root: #{root.inspect}" }.fetch(style)
39
+ code = "require #{"hecks/quality_control/cli/#{command}".inspect}; " \
40
+ "exit(Hecks::QualityControlCli::#{klass}.call(#{call}))"
41
+ ["bundle", "exec", "ruby", "-I", File.join(root, "lib"), "-e", code, "--", *args]
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require_relative "../../../hecks"
5
+ require_relative "../../ports/persistence/plugins/era"
6
+ require_relative "../../fuzzing/concurrent_dispatch"
7
+
8
+ module Hecks
9
+ module QualityControlCli
10
+ # The command behind `hecks quality_control race`: one racer of
11
+ # `Hecks::Fuzzing::ConcurrentDispatch`, run as its own OS process.
12
+ #
13
+ # It is spawned, not forked: the caller (`hecks quality_control ask run`) holds live
14
+ # `PostgresEra` connections, and fork duplicates their file descriptors and SSL state,
15
+ # corrupting them when a child exits.
16
+ #
17
+ # hecks quality_control race <domain-path> <database> <schema> <verb> <args-json>
18
+ #
19
+ # It prints one line, `succeeded`, `refused` or `crashed:<class>: <message>`, and always exits
20
+ # 0.
21
+ class QaConcurrencyRacer
22
+ USAGE = "usage: hecks quality_control race <domain-path> <database> <schema> <verb> <args-json>"
23
+
24
+ # Dispatches the one step and prints how it went.
25
+ #
26
+ # @param argv [Array<String>] the domain path, database, schema, verb and args JSON
27
+ # @param root [String] unused; a racer needs no checkout of its own
28
+ # @param out [IO] where the outcome goes
29
+ # @return [Integer] 0 always
30
+ # @raise [SystemExit] with the usage line when an argument is missing
31
+ def self.call(argv, root: nil, out: $stdout)
32
+ domain_path, database, schema, verb, args_json = argv
33
+ abort USAGE unless domain_path && database && schema && verb && args_json
34
+
35
+ step = { "verb" => verb, "args" => JSON.parse(args_json) }
36
+ outcome = Hecks::Fuzzing::ConcurrentDispatch.boot_preserving_schema(domain_path, database: database,
37
+ schema: schema) do |copy|
38
+ Hecks::Fuzzing::ConcurrentDispatch.dispatch_one(Hecks.boot(copy), step)
39
+ end
40
+ out.puts outcome
41
+ 0
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,248 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "open3"
5
+
6
+ module Hecks
7
+ module QualityControlCli
8
+ # The command behind `hecks quality_control discover_external_domains`: reports sibling-repo
9
+ # bluebook domains under `--projects-dir` that already depend on the hecks gem but are not
10
+ # enrolled yet. It never enrolls anything itself.
11
+ #
12
+ # hecks quality_control discover_external_domains
13
+ # hecks quality_control discover_external_domains --projects-dir ~/Projects
14
+ # hecks quality_control discover_external_domains --max-depth 4
15
+ # hecks quality_control discover_external_domains --known-path <path> ... # bypass the
16
+ # ledger read
17
+ class QaDiscoverExternalDomains
18
+ USAGE = "usage: hecks quality_control discover_external_domains [--projects-dir <path>] " \
19
+ "[--max-depth N] [--known-path <path> ...]"
20
+
21
+ DEFAULT_PROJECTS_DIR = File.expand_path("~/Projects").freeze
22
+ DEFAULT_MAX_DEPTH = 3
23
+
24
+ # Skipped everywhere in a sibling repo: VCS internals, vendored dependency trees, and this
25
+ # repo's own worktree scratch space.
26
+ SKIP_DIR_BASENAMES = %w[.git .hg .svn vendor node_modules .bundle tmp log coverage .yardoc .ruby-lsp
27
+ .parked bower_components .claude dist build].freeze
28
+
29
+ # Word-boundary matched so "hecksagain" (a real, separate gem) and "hecks_site" never
30
+ # false-positive as a hecks dependency.
31
+ GEMFILE_PATTERN = /gem\s*\(?\s*["']hecks["']/
32
+ GEMFILE_LOCK_PATTERN = /^\s+hecks\s+\(/
33
+
34
+ # Raised for a wrong command line or an unreadable ledger; it ends the run with status 1.
35
+ class Refused < StandardError
36
+ # @return [Boolean] whether the usage line follows the message
37
+ attr_reader :usage
38
+
39
+ # @param message [String] why the run stopped
40
+ # @param usage [Boolean] whether the usage line follows the message
41
+ def initialize(message, usage: true)
42
+ super(message)
43
+ @usage = usage
44
+ end
45
+ end
46
+
47
+ # Reports the candidates.
48
+ #
49
+ # @param argv [Array<String>] `--projects-dir`, `--max-depth` and `--known-path`
50
+ # @param root [String] the repository root
51
+ # @return [Integer] 0 once reported, 1 for a usage error or an unreadable ledger
52
+ def self.call(argv, root:)
53
+ new(root: root).call(argv)
54
+ end
55
+
56
+ # @param root [String] the repository root
57
+ def initialize(root:)
58
+ @root = root
59
+ end
60
+
61
+ # @param argv [Array<String>] `--projects-dir`, `--max-depth` and `--known-path`
62
+ # @return [Integer] the exit status
63
+ def call(argv)
64
+ options = parse(argv.dup)
65
+ return 0 if options == :help
66
+
67
+ candidates, skipped, siblings = discover(options)
68
+ report(options, candidates, skipped, siblings)
69
+ 0
70
+ rescue Refused => e
71
+ warn e.message
72
+ warn USAGE if e.usage
73
+ 1
74
+ end
75
+
76
+ private
77
+
78
+ def parse(argv)
79
+ options = { projects_dir: DEFAULT_PROJECTS_DIR, max_depth: DEFAULT_MAX_DEPTH, known_paths: nil }
80
+ until argv.empty?
81
+ case (arg = argv.shift)
82
+ when "-h", "--help"
83
+ puts USAGE
84
+ return :help
85
+ when "--projects-dir"
86
+ refuse!("--projects-dir needs a path") if argv.empty?
87
+ options[:projects_dir] = File.expand_path(argv.shift)
88
+ when "--max-depth"
89
+ refuse!("--max-depth needs a number") if argv.empty?
90
+ options[:max_depth] = Integer(argv.shift)
91
+ when "--known-path"
92
+ refuse!("--known-path needs a path") if argv.empty?
93
+ (options[:known_paths] ||= []) << File.expand_path(argv.shift)
94
+ else
95
+ refuse!("unexpected argument #{arg.inspect}")
96
+ end
97
+ end
98
+ refuse!("--max-depth must be >= 0") if options[:max_depth].negative?
99
+ refuse!("#{options[:projects_dir]} is not a directory") unless File.directory?(options[:projects_dir])
100
+ options
101
+ end
102
+
103
+ def refuse!(message)
104
+ raise Refused, message
105
+ end
106
+
107
+ def depends_on_hecks?(dir)
108
+ gemfile = File.join(dir, "Gemfile")
109
+ lockfile = File.join(dir, "Gemfile.lock")
110
+
111
+ (File.file?(gemfile) && File.read(gemfile).match?(GEMFILE_PATTERN)) ||
112
+ (File.file?(lockfile) && File.read(lockfile).match?(GEMFILE_LOCK_PATTERN))
113
+ end
114
+
115
+ # A monorepo's real hecks-dependent Ruby app can live below the sibling repo's own top level
116
+ # (a nested `Gemfile` there, not one at `repo_root`). Checked against the candidate's own
117
+ # directory first, then each ancestor up to and including `repo_root`, so the common case, the
118
+ # dependency declared at `repo_root` itself, still answers exactly as it always has.
119
+ def depends_on_hecks_anywhere_above?(entity_dir, repo_root)
120
+ root = File.expand_path(repo_root)
121
+ dir = File.expand_path(entity_dir)
122
+ loop do
123
+ return true if depends_on_hecks?(dir)
124
+ return false if dir == root
125
+
126
+ parent = File.dirname(dir)
127
+ return false if parent == dir # filesystem root reached without finding repo_root
128
+
129
+ dir = parent
130
+ end
131
+ end
132
+
133
+ # `repo_root` itself is depth 0, so a project that is one domain at its own top can be a
134
+ # candidate too.
135
+ def bluebook_shaped_dirs(repo_root, max_depth)
136
+ found = []
137
+ walk = lambda do |dir, depth|
138
+ entity_name = File.basename(dir)
139
+ found << [dir, entity_name] if File.file?(File.join(dir, "bluebook", "#{entity_name}.bluebook"))
140
+ return if depth >= max_depth
141
+
142
+ children_of(dir).each do |child|
143
+ next if SKIP_DIR_BASENAMES.include?(child)
144
+
145
+ child_path = File.join(dir, child)
146
+ next unless File.directory?(child_path) && !File.symlink?(child_path)
147
+ next if child == "bluebook" # the bluebook/ directory itself is never an entity dir
148
+
149
+ walk.call(child_path, depth + 1)
150
+ end
151
+ end
152
+ walk.call(repo_root, 0)
153
+ found
154
+ end
155
+
156
+ def children_of(dir)
157
+ Dir.children(dir).sort
158
+ rescue Errno::EACCES, Errno::ENOENT
159
+ []
160
+ end
161
+
162
+ # Shells out rather than booting the ledger in-process, so this is safe to run alongside a
163
+ # live qa_tick or qa_sweep.
164
+ def ledger_target_paths
165
+ out, err, status = Open3.capture3("bundle", "exec", "ruby", File.join(@root, "exe/hecks"),
166
+ "run", "qa/bluebook", "ask", "target.all", chdir: @root)
167
+ unless status.success?
168
+ raise Refused, "hecks quality_control discover_external_domains: could not read the ledger's targets " \
169
+ "(hecks run qa/bluebook ask target.all exited #{status.exitstatus}):\n#{err}\n" \
170
+ "pass --known-path <path> ... to run without the ledger", usage: false
171
+ end
172
+
173
+ JSON.parse(out).filter_map { |row| row.dig("path", "value") }
174
+ end
175
+
176
+ def sibling_repos(projects_dir)
177
+ hecks_root = begin
178
+ File.realpath(@root)
179
+ rescue Errno::ENOENT
180
+ @root
181
+ end
182
+ Dir.children(projects_dir).sort.filter_map do |child|
183
+ next if child == "hecks" # this repo, already fully covered by existing seeding
184
+
185
+ path = File.join(projects_dir, child)
186
+ next unless File.directory?(path) && !File.symlink?(path)
187
+
188
+ begin
189
+ next if File.realpath(path) == hecks_root
190
+ rescue Errno::ENOENT
191
+ next
192
+ end
193
+
194
+ path
195
+ end
196
+ end
197
+
198
+ # @return [Array(Array<Hash>, Array<String>, Array<String>)] the candidates, the siblings
199
+ # with no hecks dependency, and every sibling scanned
200
+ def discover(options)
201
+ known_raw = options[:known_paths] || ledger_target_paths
202
+ # Ledger paths may be stored root-relative or absolute; normalize both to compare.
203
+ known = known_raw.to_set { |path| path.start_with?("/") ? File.expand_path(path) : File.expand_path(path, @root) }
204
+ siblings = sibling_repos(options[:projects_dir])
205
+ candidates = []
206
+ skipped = []
207
+ siblings.each do |repo_path|
208
+ found = false
209
+ bluebook_shaped_dirs(repo_path, options[:max_depth]).each do |entity_dir, entity_name|
210
+ next unless depends_on_hecks_anywhere_above?(entity_dir, repo_path)
211
+
212
+ found = true
213
+ absolute = File.expand_path(entity_dir)
214
+ next if known.include?(absolute)
215
+
216
+ repo_name = File.basename(repo_path)
217
+ candidates << { repo: repo_name, reference: "#{repo_name}/#{entity_name}", path: absolute }
218
+ end
219
+ skipped << repo_path unless found
220
+ end
221
+ [candidates.sort_by { |c| [c[:repo], c[:reference]] }, skipped, siblings]
222
+ end
223
+
224
+ def report(options, candidates, skipped, siblings)
225
+ puts "scanned #{siblings.size} sibling(s) under #{options[:projects_dir]} " \
226
+ "(max depth #{options[:max_depth]}), #{siblings.size - skipped.size} depend on the hecks gem"
227
+ puts "no hecks dependency, skipped: #{skipped.map { |p| File.basename(p) }.join(', ')}" unless skipped.empty?
228
+ puts
229
+ if candidates.empty?
230
+ puts "no new candidates — every hecks-dependent sibling's bluebook-shaped domain is either " \
231
+ "already a Target or none was found in the shape <name>/bluebook/<name>.bluebook within " \
232
+ "--max-depth #{options[:max_depth]}."
233
+ return
234
+ end
235
+
236
+ puts "#{candidates.size} candidate(s) — report only, nothing identified. Review each, then run " \
237
+ "the command yourself to enroll it:"
238
+ puts
239
+ candidates.each do |c|
240
+ puts " #{c[:reference]}"
241
+ puts " path: #{c[:path]}"
242
+ puts " enroll: hecks run qa/bluebook identify reference=#{c[:reference]} path=#{c[:path]}"
243
+ puts
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "open3"
5
+ require_relative "../../../hecks"
6
+ require_relative "../../fuzzing/form_census"
7
+
8
+ module Hecks
9
+ module QualityControlCli
10
+ # The command behind `hecks quality_control judge_novelty`: does a new stress domain earn its
11
+ # place? It is the gate before a domain becomes a `Target`.
12
+ #
13
+ # hecks quality_control judge_novelty <domain-path> [--against <path> ...]
14
+ #
15
+ # A domain is new when it puts two forms together on one aggregate that no existing target does
16
+ # (`Hecks::Fuzzing::FormCensus`). It is compared against the ledger's `Target.path` rows unless
17
+ # `--against` names others. "Nothing new" can also mean the domain's form is not yet named in
18
+ # `FormCensus::FORMS`.
19
+ #
20
+ # Exit codes: 0 new pair found; 1 no new pair; 2 usage or wrong path shape.
21
+ class QaDomainNovelty
22
+ EXIT_NOVEL = 0
23
+ EXIT_NOTHING = 1
24
+ EXIT_USAGE = 2
25
+
26
+ USAGE = "usage: hecks quality_control judge_novelty <domain-path> [--against <path> ...]"
27
+
28
+ # Judges the candidate.
29
+ #
30
+ # @param argv [Array<String>] the candidate's path, then optionally `--against` and paths
31
+ # @param root [String] the repository root
32
+ # @return [Integer] 0 when the candidate earns its place, 1 when it does not, 2 for a usage
33
+ # error
34
+ def self.call(argv, root:)
35
+ new(root: root).call(argv)
36
+ end
37
+
38
+ # @param root [String] the repository root
39
+ def initialize(root:)
40
+ @root = root
41
+ end
42
+
43
+ # @param argv [Array<String>] the candidate's path, then optionally `--against` and paths
44
+ # @return [Integer] the exit status
45
+ def call(argv)
46
+ candidate, against = parse(argv)
47
+ refuse_unless_stress_domain_shaped!(normalize(candidate))
48
+ existing_source = against.empty? ? "the ledger's Target.path rows" : "--against"
49
+ existing = (against.empty? ? ledger_target_paths : against).map { |path| normalize(path) }.uniq
50
+ own = existing.select { |path| path == normalize(candidate) }
51
+ existing -= own
52
+ puts "note: #{candidate} is already a target — left out of the comparison" unless own.empty?
53
+
54
+ missing, present = existing.partition { |path| Hecks::Fuzzing::FormCensus.bluebook_files(path).nil? }
55
+ missing.each { |path| warn "skipping #{path}: no bluebook/*.bluebook on disk" }
56
+ judge(candidate, present, existing_source)
57
+ rescue UsageError => e
58
+ warn e.message
59
+ warn USAGE
60
+ EXIT_USAGE
61
+ rescue Stopped
62
+ EXIT_USAGE
63
+ end
64
+
65
+ private
66
+
67
+ # Raised for a wrong command line or path shape; it ends the run with `EXIT_USAGE`.
68
+ class UsageError < StandardError; end
69
+
70
+ # Raised once the reason has been printed; it ends the run with `EXIT_USAGE`.
71
+ class Stopped < StandardError; end
72
+
73
+ def parse(argv)
74
+ candidate = nil
75
+ against = []
76
+ mode = :candidate
77
+ argv.each do |arg|
78
+ if arg == "--against"
79
+ mode = :against
80
+ elsif mode == :against
81
+ against << arg
82
+ elsif candidate.nil?
83
+ candidate = arg
84
+ else
85
+ raise UsageError, "unexpected argument #{arg.inspect}"
86
+ end
87
+ end
88
+ raise UsageError, "no candidate domain given" if candidate.nil?
89
+ raise UsageError, "--against needs at least one path" if mode == :against && against.empty?
90
+
91
+ [candidate, against]
92
+ end
93
+
94
+ # `hecks project_rust` reads exactly `<name>/bluebook/<name>.bluebook`, so refuse anything
95
+ # else early.
96
+ def refuse_unless_stress_domain_shaped!(path)
97
+ expected = File.join(path, "bluebook", "#{File.basename(path)}.bluebook")
98
+ return if File.file?(expected)
99
+
100
+ raise UsageError, "#{path} is not shaped like a stress domain: expected #{expected} to exist " \
101
+ "(`<name>/bluebook/<name>.bluebook` — a flat file breaks `hecks project_rust`; " \
102
+ "see qa/stress_domains/ledger_ordering/NOTES.md)"
103
+ end
104
+
105
+ # The ledger's target paths via one read-only `ask`; never boots the ledger in this process.
106
+ def ledger_target_paths
107
+ out, err, status = Open3.capture3("bundle", "exec", "ruby", File.join(@root, "exe/hecks"),
108
+ "run", "qa/bluebook", "ask", "target.all", chdir: @root)
109
+ unless status.success?
110
+ warn "hecks quality_control judge_novelty: could not read the ledger's targets (hecks run qa/bluebook ask " \
111
+ "target.all exited #{status.exitstatus}):\n#{err}"
112
+ warn "pass --against <path> ... to run without the ledger"
113
+ raise Stopped
114
+ end
115
+
116
+ JSON.parse(out).map { |row| row.dig("path", "value") }.compact
117
+ end
118
+
119
+ def normalize(path) = File.expand_path(path, @root)
120
+
121
+ def judge(candidate, present, existing_source)
122
+ census = Hecks::Fuzzing::FormCensus.census(normalize(candidate))
123
+ candidate_pairs = Hecks::Fuzzing::FormCensus.covered_pairs(census)
124
+ existing_pairs = present.each_with_object(Hash.new { |h, k| h[k] = [] }) do |path, covered|
125
+ Hecks::Fuzzing::FormCensus.covered_pairs(Hecks::Fuzzing::FormCensus.census(path)).each do |pair, names|
126
+ covered[pair].concat(names.map { |name| "#{name} (#{path.delete_prefix("#{@root}/")})" })
127
+ end
128
+ end
129
+ new_pairs = candidate_pairs.reject { |pair, _| existing_pairs.key?(pair) }
130
+
131
+ puts "candidate: #{candidate} — #{census.size} aggregate(s), #{candidate_pairs.size} form pair(s) met"
132
+ puts "against: #{present.size} domain(s) from #{existing_source}, " \
133
+ "#{existing_pairs.size} form pair(s) met between them"
134
+ puts
135
+ return nothing_new(candidate) if new_pairs.empty?
136
+
137
+ width = new_pairs.keys.map(&:size).max
138
+ puts "new pair(s) — met on one aggregate here, on none of the existing targets:"
139
+ new_pairs.sort.each { |pair, names| puts " #{pair.ljust(width)} #{names.uniq.join(', ')}" }
140
+ puts
141
+ puts "#{new_pairs.size} new pair(s) — #{candidate} earns its place."
142
+ EXIT_NOVEL
143
+ end
144
+
145
+ def nothing_new(candidate)
146
+ puts "no new pair — every pair of forms #{candidate} puts together on one aggregate is already met " \
147
+ "by an existing target. Either the domain is not new, or the form it exists for is not yet " \
148
+ "named in Hecks::Fuzzing::FormCensus::FORMS (lib/hecks/fuzzing/form_census.rb) — name it " \
149
+ "there first, citing the gap it is one step from, and run this again."
150
+ puts
151
+ puts "READ THAT AS A QUESTION ABOUT THE CENSUS, NOT A VERDICT ON THE DOMAIN. This gate measures " \
152
+ "ONE AGGREGATE's own declared forms, so a domain whose point is a chapter-level construct — " \
153
+ "a policy, an `across` target, a process manager, a read model's own group_by/median, an " \
154
+ "outbox, a dry run — is invisible to it by construction, not by omission. `corrects` and " \
155
+ "`role_gated` were exactly that: declared all over the corpus, unnamed here, so this line " \
156
+ "told three stress domains built around retroactive correction that they were redundant " \
157
+ "(their own NOTES.md each say so). If that is your domain's case, say which construct it " \
158
+ "exists for in its NOTES.md and keep it."
159
+ EXIT_NOTHING
160
+ end
161
+ end
162
+ end
163
+ end