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,7 @@
1
+ # Binds the `Agent` port to `Agent`; `hecks quality_control mine_combinations` asks it to write candidate domains.
2
+ # Required by name, not relative, so a copy of this directory shares the one implementation loaded.
3
+ require "hecks/quality_control/adapters/agent"
4
+
5
+ Hecks.adapter "Agent" do
6
+ port "Agent"
7
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+ require "shellwords"
5
+
6
+ module Hecks
7
+ module Adapters
8
+ # The `Agent` port's adapter: hands a prompt to an agent on the command line and lets it work
9
+ # in the checkout, the way `hecks quality_control mine_combinations` asks one for candidate
10
+ # domains.
11
+ #
12
+ # Not the interviewer behind the framework's `agent` port (`ClaudeCode`), which reads one
13
+ # JSON answer back: this agent edits files, so the only answer is whether it finished.
14
+ class Agent
15
+ # An agent that could not start, or ended with a failure.
16
+ class Failed < StandardError; end
17
+
18
+ # The command run when neither the caller nor `QA_MINER_AGENT` names one.
19
+ DEFAULT_COMMAND = ["claude", "-p", "--permission-mode", "acceptEdits",
20
+ "--allowedTools", "Read,Glob,Grep,Write,Edit"].freeze
21
+
22
+ # @param aggregate [Object, nil] unused
23
+ # @param settings [Hash] unused
24
+ # @param root [String, nil] unused
25
+ def initialize(aggregate: nil, settings: {}, root: nil); end
26
+
27
+ # The command an ask runs.
28
+ #
29
+ # @param command [String, Array<String>, nil] a command line, or its words
30
+ # @return [Array<String>] `command` as words; `QA_MINER_AGENT` else `DEFAULT_COMMAND` when nil
31
+ def command_for(command = nil)
32
+ command ||= ENV.fetch("QA_MINER_AGENT", nil)
33
+ return DEFAULT_COMMAND unless command
34
+
35
+ command.is_a?(String) ? Shellwords.split(command) : command
36
+ end
37
+
38
+ # Runs the agent with `prompt` on its standard input.
39
+ #
40
+ # @param prompt [String] what the agent is asked to do
41
+ # @param command [String, Array<String>, nil] the agent's command; see `command_for`
42
+ # @param chdir [String] the directory the agent works in
43
+ # @param log [String, nil] a file the agent's output is appended to
44
+ # @return [String] the agent's output
45
+ # @raise [Failed] when the agent cannot start or exits non-zero
46
+ def ask(prompt:, chdir:, command: nil, log: nil)
47
+ words = command_for(command)
48
+ output, status = Open3.capture2e(*words, stdin_data: prompt, chdir: chdir)
49
+ File.open(log, "a") { |file| file.puts(output) } if log
50
+ return output if status.success?
51
+
52
+ raise Failed, "agent exited #{status.exitstatus}: #{output.lines.last(5).join.strip}"
53
+ rescue SystemCallError => e
54
+ raise Failed, "agent could not start (#{words.first}): #{e.message}"
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,7 @@
1
+ # Binds the `AngleTools` port to `AngleTools`, which answers `Angle`'s query: the seed of the backlog.
2
+ # Required by name, not relative, so a copy of this directory shares the one implementation loaded.
3
+ require "hecks/quality_control/adapters/angle_tools"
4
+
5
+ Hecks.adapter "AngleTools" do
6
+ port "AngleTools"
7
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "qa_tool"
4
+
5
+ module Hecks
6
+ module Adapters
7
+ # The `AngleTools` port's adapter: answers `Angle`'s query by running the seed of the backlog.
8
+ class AngleTools < QaTool
9
+ # @return [Hash] `text:` each starting lead's outcome: proposed, or already on file
10
+ def seed
11
+ run_command("qa_seed_angles")
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hecks
4
+ module QA
5
+ # Records what CI said about one commit as a `QualityControl::Clearance`.
6
+ module ClearanceRecorder
7
+ module_function
8
+
9
+ # Conclusions that count as green; same vocabulary as `GithubChecks::PASSING`.
10
+ # A `check_suite` conclusion aggregates one GitHub App's runs only, so callers decide
11
+ # which suites may pass a commit.
12
+ PASSING = %w[success neutral skipped].freeze
13
+
14
+ # Returns the Clearance for `commit`, starting one if none exists.
15
+ # `AlreadyExists` from a redelivered webhook racing another start is swallowed.
16
+ def ensure_started(commit)
17
+ QualityControl::Clearance.find(commit) ||
18
+ QualityControl::Clearance.start!(commit: { value: commit })
19
+ rescue Hecks::Runtime::AlreadyExists
20
+ QualityControl::Clearance.find(commit)
21
+ end
22
+
23
+ # Starts and settles the Clearance for `commit`; the first verdict recorded stands.
24
+ # An already-settled record is returned unchanged, so redelivery cannot raise
25
+ # `LifecycleRefused`.
26
+ #
27
+ # @return the settled Clearance
28
+ def record(commit:, passed:, summary:)
29
+ cleared = ensure_started(commit)
30
+ return cleared if cleared.status != "running"
31
+
32
+ passed ? cleared.passed!(summary: { value: summary }) : cleared.failed!(refusal: { value: summary })
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,7 @@
1
+ # Binds the `ClearanceTools` port to `ClearanceTools`, which answers `Clearance`'s query: the PR check.
2
+ # Required by name, not relative, so a copy of this directory shares the one implementation loaded.
3
+ require "hecks/quality_control/adapters/clearance_tools"
4
+
5
+ Hecks.adapter "ClearanceTools" do
6
+ port "ClearanceTools"
7
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "qa_tool"
4
+
5
+ module Hecks
6
+ module Adapters
7
+ # The `ClearanceTools` port's adapter: answers `Clearance`'s query by running the PR check. A
8
+ # newly red PR ends the check with status 2, which is an answer; anything else it calls an
9
+ # error is refused with its report.
10
+ class ClearanceTools < QaTool
11
+ # @return [Hash] `text:` what the check found for each open PR the ledger tracks
12
+ def check_pull_requests
13
+ run_command("qa_pr_check", answers: [0, 2])
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ # Binds the `GitPr` port to `GitPr`; the ledger's scripts ask it for every `git` and `gh` call.
2
+ # Required by name, not relative, so a copy of this directory shares the one implementation loaded.
3
+ require "hecks/quality_control/adapters/git_pr"
4
+
5
+ Hecks.adapter "GitPr" do
6
+ port "GitPr"
7
+ end
@@ -0,0 +1,226 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+ require "json"
5
+
6
+ module Hecks
7
+ module Adapters
8
+ # The `GitPr` port's adapter: the `git` and `gh` calls behind a pull request the QA practice
9
+ # opens or checks, and the two rules that need the outside world to answer.
10
+ #
11
+ # The rules a ledger record can answer (the branch prefix, the bug being fixed, the angle
12
+ # being under investigation) are `given`s on `Patch.Open` and `Improvement.Open`. The two
13
+ # here need the repository or a count of today's pull requests: the per-day cap and the fix
14
+ # commit being an ancestor of `HEAD`.
15
+ class GitPr
16
+ # A rule the adapter enforces refused the request; nothing was opened.
17
+ class Refusal < StandardError; end
18
+
19
+ # `git` or `gh` could not do what was asked, whatever the request.
20
+ class CommandFailed < StandardError; end
21
+
22
+ # A commit sha as it may be handed to `git` as a revision.
23
+ SHA_PATTERN = /\A[0-9a-fA-F]{7,40}\z/
24
+
25
+ # The remote a branch is pushed to and `gh pr create --head` reads it from.
26
+ REMOTE = "origin"
27
+
28
+ # What `gh pr view` says when the branch simply has no pull request.
29
+ NO_PR = /no pull requests? found/i
30
+
31
+ # The fields `gh pr view` is asked for when a PR is opened or looked up by branch.
32
+ VIEW_FIELDS = "number,url,headRefName,headRefOid,title,state"
33
+
34
+ # @param aggregate [Object, nil] unused
35
+ # @param settings [Hash] unused
36
+ # @param root [String, nil] unused
37
+ # @param repo_dir [String] the checkout `git` and `gh` run in; `QA_REPO_DIR` picks one so a
38
+ # spec can use a throwaway repository and a fake `gh`
39
+ def initialize(aggregate: nil, settings: {}, root: nil, repo_dir: ENV.fetch("QA_REPO_DIR", Dir.pwd))
40
+ @repo_dir = repo_dir
41
+ end
42
+
43
+ # @return [String] the checkout's current branch
44
+ # @raise [Refusal] when the directory is not a git checkout
45
+ def branch
46
+ out, _err, status = git("rev-parse", "--abbrev-ref", "HEAD")
47
+ raise Refusal, "not a git checkout at #{@repo_dir}" unless status.success?
48
+
49
+ out
50
+ end
51
+
52
+ # @return [String] the commit `HEAD` names
53
+ def head = git("rev-parse", "HEAD").first
54
+
55
+ # Refuses a checkout with uncommitted work, since a PR opened from it would not carry it.
56
+ #
57
+ # @return [void]
58
+ # @raise [Refusal] when `git status` cannot be read or reports changes
59
+ def assert_clean_tree!
60
+ dirty, _err, status = git("status", "--porcelain")
61
+ raise Refusal, "could not read git status at #{@repo_dir}" unless status.success?
62
+ return if dirty.empty?
63
+
64
+ raise Refusal, "the working tree at #{@repo_dir} is dirty — commit or discard first:\n#{dirty}"
65
+ end
66
+
67
+ # Refuses one more pull request than the day's cap allows; a cap of zero is no cap.
68
+ #
69
+ # @param opened_today [Integer] how many PRs the ledger recorded since local midnight
70
+ # @param cap [Integer] `PR_CAP_PER_DAY`
71
+ # @return [void]
72
+ # @raise [Refusal] when `opened_today` has reached a positive `cap`
73
+ def assert_under_daily_cap!(opened_today:, cap:)
74
+ return unless cap.positive? && opened_today >= cap
75
+
76
+ raise Refusal, "#{opened_today} PR(s) already opened since local midnight, and " \
77
+ "QualityControlDials::PR_CAP_PER_DAY is #{cap} — leave this as an open Bug/Angle " \
78
+ "and open it tomorrow, or raise the dial in qa/settings.yml"
79
+ end
80
+
81
+ # Refuses a fix commit that is not part of the branch being opened.
82
+ #
83
+ # @param commit [String] the commit the ledger says fixes the bug
84
+ # @param owner [String] what the commit belongs to, worded into the refusal (`"BUG#1"`)
85
+ # @return [void]
86
+ # @raise [Refusal] when `commit` is not a sha or not an ancestor of `HEAD`
87
+ def assert_ancestor!(commit:, owner:)
88
+ assert_sha!(commit, owner)
89
+ _out, _err, status = git("merge-base", "--is-ancestor", "--", commit, "HEAD")
90
+ return if status.success?
91
+
92
+ raise Refusal, "#{owner}'s own fix commit #{commit[0, 7]} is not an ancestor of HEAD " \
93
+ "(#{head[0, 7]}) on #{branch} — the PR must carry the commit the ledger says fixes it"
94
+ end
95
+
96
+ # Refuses a fix commit that the pushed branch does not carry: `gh pr create --head` opens
97
+ # the pull request from the remote branch, not from the local checkout.
98
+ #
99
+ # @param branch [String] the head branch
100
+ # @param commit [String] the commit the ledger says fixes the bug
101
+ # @param owner [String] what the commit belongs to, worded into the refusal
102
+ # @return [void]
103
+ # @raise [Refusal] when the branch is not on the remote, or its tip does not carry `commit`
104
+ def assert_pushed!(branch:, commit:, owner:)
105
+ assert_sha!(commit, owner)
106
+ out, err, status = git("ls-remote", "--heads", REMOTE, "refs/heads/#{branch}")
107
+ raise Refusal, "could not read #{REMOTE} for #{branch}: #{err}" unless status.success?
108
+
109
+ tip = out.split.first.to_s
110
+ raise Refusal, "#{branch} is not pushed to #{REMOTE} — push it before opening the PR" if tip.empty?
111
+
112
+ assert_carries!(tip, commit, owner, "the pushed #{REMOTE}/#{branch}")
113
+ end
114
+
115
+ # Refuses a pull request whose head does not carry the fix commit.
116
+ #
117
+ # @param head [String] the PR's head commit, as `gh` reports it
118
+ # @param commit [String] the commit the ledger says fixes the bug
119
+ # @param owner [String] what the commit belongs to, worded into the refusal
120
+ # @return [void]
121
+ # @raise [Refusal] when `head` is not a sha, or does not carry `commit`
122
+ def assert_pr_head!(head:, commit:, owner:)
123
+ assert_sha!(commit, owner)
124
+ assert_sha!(head, "the PR head")
125
+ assert_carries!(head, commit, owner, "the PR head")
126
+ end
127
+
128
+ # The open PR for a branch.
129
+ #
130
+ # @param branch [String] the head branch
131
+ # @return [Hash{Symbol => Object}, nil] `gh pr view`'s fields, or nil when no PR is open
132
+ # @raise [CommandFailed] when `gh` failed for any reason but the branch having no PR
133
+ def open_pull_request(branch)
134
+ out, err, status = gh("pr", "view", branch, "--json", VIEW_FIELDS)
135
+ unless status.success?
136
+ return nil if err.match?(NO_PR)
137
+
138
+ raise CommandFailed, "gh pr view #{branch} failed — #{err.empty? ? out : err}"
139
+ end
140
+
141
+ view = JSON.parse(out, symbolize_names: true)
142
+ view[:state] == "OPEN" ? view : nil
143
+ end
144
+
145
+ # Opens a PR from `branch`.
146
+ #
147
+ # @param branch [String] the head branch
148
+ # @param title [String] the PR's title
149
+ # @param body [String] the PR's description
150
+ # @param draft [Boolean] open it as a draft
151
+ # @return [String] what `gh pr create` printed, the PR's URL
152
+ # @raise [CommandFailed] when `gh` refuses
153
+ def create_pull_request(branch:, title:, body:, draft: false)
154
+ args = ["pr", "create", "--head", branch, "--title", title, "--body", body]
155
+ args << "--draft" if draft
156
+ out, err, status = gh(*args)
157
+ raise CommandFailed, "gh pr create failed — #{err.empty? ? out : err}" unless status.success?
158
+
159
+ out
160
+ end
161
+
162
+ # Queues a squash merge for when the PR's checks pass.
163
+ #
164
+ # @param number [Integer] the PR's number
165
+ # @return [void]
166
+ # @raise [CommandFailed] when `gh` refuses
167
+ def merge_when_green(number)
168
+ _out, err, status = gh("pr", "merge", number.to_s, "--auto", "--squash")
169
+ raise CommandFailed, "gh pr merge --auto failed — #{err}" unless status.success?
170
+ end
171
+
172
+ # A tracked PR's state and head commit, read live: a push mints a new commit to clear.
173
+ #
174
+ # @param number [Integer] the PR's number
175
+ # @return [Hash{Symbol => Object}] `state`, `headRefOid` and `url`
176
+ # @raise [CommandFailed] when `gh` cannot read it
177
+ def pull_request(number)
178
+ parse(gh("pr", "view", number.to_s, "--json", "state,headRefOid,url"), "gh pr view failed")
179
+ end
180
+
181
+ # A tracked PR's checks, as `gh` reports them.
182
+ #
183
+ # @param number [Integer] the PR's number
184
+ # @return [Array<Hash{Symbol => Object}>] each check's `name`, `state`, `bucket`, `workflow`
185
+ # @raise [CommandFailed] when `gh` cannot read them
186
+ def checks(number)
187
+ parse(gh("pr", "checks", number.to_s, "--json", "name,state,bucket,workflow"), "gh pr checks failed")
188
+ end
189
+
190
+ private
191
+
192
+ def assert_sha!(value, owner)
193
+ return if value.to_s.match?(SHA_PATTERN)
194
+
195
+ raise Refusal, "#{owner}'s commit #{value.to_s.inspect} does not look like a sha"
196
+ end
197
+
198
+ def assert_carries!(tip, commit, owner, label)
199
+ _out, _err, status = git("merge-base", "--is-ancestor", "--", commit, tip)
200
+ return if status.success?
201
+
202
+ raise Refusal, "#{owner}'s own fix commit #{commit[0, 7]} is not an ancestor of #{label} " \
203
+ "(#{tip[0, 7]}) — the PR must carry the commit the ledger says fixes it"
204
+ end
205
+
206
+ def parse(result, failure)
207
+ out, err, status = result
208
+ raise CommandFailed, "#{failure} — #{err.strip.empty? ? out.strip : err.strip}" unless status.success?
209
+
210
+ JSON.parse(out, symbolize_names: true)
211
+ end
212
+
213
+ def git(*) = run_tool("git", *)
214
+
215
+ def gh(*) = run_tool("gh", *)
216
+
217
+ def run_tool(tool, *)
218
+ out, err, status = Open3.capture3(tool, *, chdir: @repo_dir)
219
+ [out.strip, err.strip, status]
220
+ rescue Errno::ENOENT
221
+ raise Refusal, "#{tool} is not installed or not on PATH (or #{@repo_dir} is missing) — " \
222
+ "the practice cannot open or check a pull request without it"
223
+ end
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,9 @@
1
+ # Binds the `CI` port to `GithubChecks` for any boot that loads the QualityControl chapter.
2
+ # The implementation is required by name, not relative to this file, because the `.adapter` glob
3
+ # never loads the sibling `.rb` and a copy of this directory (a fuzz boot) must share the one
4
+ # implementation already loaded.
5
+ require "hecks/quality_control/adapters/github_checks"
6
+
7
+ Hecks.adapter "GithubChecks" do
8
+ port "CI"
9
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+ require "json"
5
+
6
+ module Hecks
7
+ module Adapters
8
+ # The `CI` port adapter: answers `Clearance.CI.Run` from GitHub's check runs for a commit.
9
+ # Raises when no checks exist, any are still running, or any failed.
10
+ class GithubChecks
11
+ # All keywords are unused; the adapter takes no per-boot configuration.
12
+ def initialize(aggregate: nil, settings: {}, root: nil); end
13
+
14
+ # Check-run conclusions that count as green; `skipped` covers path-filtered workflows.
15
+ PASSING = %w[success neutral skipped].freeze
16
+
17
+ # A commit sha as GitHub accepts it in an API path.
18
+ SHA_PATTERN = /\A[0-9a-fA-F]{7,40}\z/
19
+
20
+ # Check runs asked for per page; GitHub's maximum.
21
+ PER_PAGE = 100
22
+
23
+ # Asks GitHub by commit sha, matching `Clearance`'s identity, so it works after the
24
+ # branch is gone. `{owner}/{repo}` is a `gh api` template resolved from the git remote.
25
+ #
26
+ # @param commit [Hash, String] a materialized value object (`{value: sha}`) or a bare sha
27
+ # @return [Hash] `{ summary: { value: ... } }` when every check is green
28
+ # @raise [RuntimeError] when the sha is malformed, `gh` is missing or fails, no checks
29
+ # exist, any are incomplete, or any failed
30
+ def run(commit:, **)
31
+ sha = sha_of(commit)
32
+ raise "not a commit sha: #{sha.inspect}" unless sha.match?(SHA_PATTERN)
33
+
34
+ runs = check_runs(sha)
35
+
36
+ raise "gh reports no checks at all against #{sha}" if runs.empty?
37
+
38
+ # Refuse rather than answer on an incomplete run; asking again once it settles is safe.
39
+ incomplete = runs.reject { |run| run["status"] == "completed" }
40
+ raise "checks against #{sha} are still running — asked before they settled" if incomplete.any?
41
+
42
+ failing = runs.reject { |run| PASSING.include?(run["conclusion"]) }
43
+ return { summary: { value: "#{runs.length} checks, all green (#{sha[0, 7]})" } } if failing.empty?
44
+
45
+ raise "#{failing.length} of #{runs.length} checks failed against #{sha[0, 7]}: " \
46
+ "#{failing.map { |run| run['name'] }.join(', ')}"
47
+ end
48
+
49
+ private
50
+
51
+ # Accepts a materialized value hash with symbol or string keys, or a bare string.
52
+ def sha_of(commit)
53
+ return commit if commit.is_a?(String)
54
+
55
+ (commit.key?(:value) ? commit[:value] : commit["value"]).to_s
56
+ end
57
+
58
+ # Every page of check runs: a red run past the first page must still be seen.
59
+ def check_runs(sha)
60
+ runs = []
61
+ page = 1
62
+ loop do
63
+ batch = check_run_page(sha, page)["check_runs"] || []
64
+ runs.concat(batch)
65
+ break if batch.length < PER_PAGE
66
+
67
+ page += 1
68
+ end
69
+ runs
70
+ end
71
+
72
+ def check_run_page(sha, page)
73
+ path = "repos/{owner}/{repo}/commits/#{sha}/check-runs?per_page=#{PER_PAGE}&page=#{page}"
74
+ out, err, status = Open3.capture3("gh", "api", path)
75
+ raise "gh api check-runs failed for #{sha}: #{err.strip.empty? ? out.strip : err.strip}" unless status.success?
76
+
77
+ JSON.parse(out)
78
+ rescue Errno::ENOENT
79
+ raise "gh is not installed or not on PATH — cannot read check runs for #{sha}"
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hecks/adapters/driving/github_webhook"
4
+ require_relative "clearance_recorder"
5
+
6
+ module Hecks
7
+ module Adapters
8
+ module Driving
9
+ # Records GitHub's `check_suite` webhook as a `QualityControl::Clearance`.
10
+ # GitHub sends one `check_suite` per GitHub App for a commit, so one suite is not the
11
+ # commit's verdict. A passing suite clears the commit only when it belongs to `CI_APP_SLUG`
12
+ # (Actions, which runs the gate); a passing suite from any other app is acknowledged and
13
+ # ignored. A failing suite from any app holds the commit red, and the first verdict stands.
14
+ class GithubCiWebhook < GithubWebhook
15
+ SHA_PATTERN = /\A[0-9a-fA-F]{7,40}\z/
16
+
17
+ # The GitHub App whose passing suite counts as the commit being green.
18
+ CI_APP_SLUG = "github-actions"
19
+
20
+ def handle_event(event, action, payload)
21
+ return ignored("not a check_suite event (got #{event.inspect})") unless event == "check_suite"
22
+ # `requested`/`rerequested` mean the suite has not finished, so there is no verdict yet.
23
+ return ignored("check_suite not yet completed (action=#{action.inspect})") unless action == "completed"
24
+
25
+ suite = payload["check_suite"] || {}
26
+ sha = suite["head_sha"].to_s
27
+
28
+ unless sha.match?(SHA_PATTERN)
29
+ return [422, { error: "MalformedCommit", message: "check_suite.head_sha #{sha.inspect} does not look like a sha" }]
30
+ end
31
+
32
+ settle(sha, suite)
33
+ end
34
+
35
+ private
36
+
37
+ def ignored(reason) = [200, { ok: true, ignored: reason }]
38
+
39
+ def settle(sha, suite)
40
+ conclusion = suite["conclusion"].to_s
41
+ passed = Hecks::QA::ClearanceRecorder::PASSING.include?(conclusion)
42
+ app = suite.dig("app", "slug").to_s
43
+ if passed && app != CI_APP_SLUG
44
+ return ignored("passing check_suite from app #{app.inspect}, not #{CI_APP_SLUG.inspect}")
45
+ end
46
+
47
+ summary = "check_suite #{suite['id']} conclusion=#{conclusion} for #{sha[0, 7]} — via webhook"
48
+
49
+ cleared = Hecks::QA::ClearanceRecorder.record(commit: sha, passed: passed, summary: summary)
50
+
51
+ [200, { ok: true, commit: sha, status: cleared.status, summary: summary }]
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,7 @@
1
+ # Binds the `IssueTracker` port to `GithubIssues` for any boot that loads the chapter.
2
+ # Required by name, not relative, so a copy of this directory shares the one implementation loaded.
3
+ require "hecks/quality_control/adapters/github_issues"
4
+
5
+ Hecks.adapter "GithubIssues" do
6
+ port "IssueTracker"
7
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+
5
+ module Hecks
6
+ module Adapters
7
+ # The `IssueTracker` port's adapter: files a `QualityControl::Ticket` as a GitHub issue.
8
+ # Raises when `gh` refuses, which the runtime records as the ticket's refusal.
9
+ class GithubIssues
10
+ # @param aggregate [Object, nil] unused
11
+ # @param settings [Hash] unused
12
+ # @param root [String, nil] unused
13
+ def initialize(aggregate: nil, settings: {}, root: nil); end
14
+
15
+ # Files the ticket the runtime hands over (its whole held state) with `gh issue create`.
16
+ #
17
+ # @param held [Hash] the ticket's fields; `repository`, `title` and `body` are materialized
18
+ # value objects (`{value: ...}`) or bare strings, and `pull_request` names a proposed fix
19
+ # @return [Hash{Symbol => Hash}] `number:` and `url:` of the issue, in the shape the
20
+ # ticket's `Filed` command takes
21
+ # @raise [RuntimeError] when `gh` exits non-zero or prints no issue URL
22
+ def file(**held)
23
+ repository = plain(held[:repository])
24
+ proposed = plain(held[:pull_request]).to_s
25
+ body = plain(held[:body]).to_s
26
+ body += "\n\nA fix is proposed in #{proposed}" unless proposed.empty?
27
+
28
+ out, err, status = Open3.capture3("gh", "issue", "create", "--repo", repository,
29
+ "--title", plain(held[:title]).to_s, "--body", body)
30
+ raise "gh issue create failed — #{err.strip.empty? ? out.strip : err.strip}" unless status.success?
31
+
32
+ url = out.strip.lines.last.to_s.strip
33
+ number = url[%r{/issues/(\d+)\z}, 1]
34
+ raise "gh issue create printed no issue URL: #{out.strip.inspect}" unless number
35
+
36
+ { number: { value: number.to_i }, url: { value: url } }
37
+ end
38
+
39
+ private
40
+
41
+ # A materialized value object (`{value: x}`, symbol or string keys) or the value itself.
42
+ def plain(field)
43
+ return field unless field.is_a?(Hash)
44
+
45
+ field.key?(:value) ? field[:value] : field["value"]
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+ require "shellwords"
5
+ require "hecks/runtime/errors"
6
+ require_relative "../cli/child"
7
+
8
+ module Hecks
9
+ module Adapters
10
+ # The base of the adapters that answer the QA scripts' queries (`SweepTools`,
11
+ # `ClearanceTools`, `AngleTools`, `TargetTools`): each starts one command of
12
+ # `Hecks::QualityControlCli` as its own OS process and answers what it printed.
13
+ #
14
+ # A child, not this process: the commands boot the ledger, fork, spawn and open Postgres
15
+ # connections of their own, and a crash or an `exit` in one must end only that run. The answer
16
+ # is the command's output, unchanged, so the launcher shows the bytes the script prints.
17
+ class QaTool
18
+ # A command ended in an error. It is a refusal, since the launcher and the ledger's policies
19
+ # treat it as one, but not a `given`'s: no description of the command's guards quotes it, so
20
+ # the fuzzer's guard checks must not read it as one.
21
+ class ToolRefused < Runtime::GivenNotMet; end
22
+
23
+ # @param aggregate [Object, nil] unused
24
+ # @param settings [Hash] unused
25
+ # @param root [String, nil] the checkout the commands run in; this repository when nil
26
+ def initialize(aggregate: nil, settings: {}, root: nil)
27
+ @root = root || File.expand_path("../../../..", __dir__)
28
+ end
29
+
30
+ private
31
+
32
+ # Runs one command and answers its output.
33
+ #
34
+ # @param command [String] a key of `QualityControlCli::Child::COMMANDS`
35
+ # @param args [Array<String>] the command's own arguments
36
+ # @param answers [Array<Integer>] the exit statuses that are an answer: a finding, or a
37
+ # judgment, is one, an operational error is not
38
+ # @return [Hash] `text:` what the command printed, stdout and stderr in
39
+ # order
40
+ # @raise [ToolRefused] with what the command printed, when it ended with another
41
+ # status
42
+ def run_command(command, *, answers: [0])
43
+ output, status = Open3.capture2e(*QualityControlCli::Child.argv(@root, command, *), chdir: @root)
44
+ return { text: output } if answers.include?(status.exitstatus)
45
+
46
+ raise ToolRefused, "#{output.strip}\n(#{command} ended with status #{status.exitstatus.inspect})"
47
+ end
48
+
49
+ # @param value [Hash, String, nil] a value object's materialized `{ value: "..." }`, or plain
50
+ # text
51
+ # @return [String, nil] the text, nil when absent
52
+ def plain(value) = value.is_a?(Hash) ? value[:value] : value
53
+
54
+ # @param value [Hash, String, nil] words in the command's own flag syntax
55
+ # @return [Array<String>] the words, split as a shell would
56
+ def words(value) = Shellwords.split(plain(value).to_s)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,7 @@
1
+ # Binds the `SweepTools` port to `SweepTools`, which answers `Sweep`'s queries: a tick, a sweep, the Heki migration, the ledger's role and a racer.
2
+ # Required by name, not relative, so a copy of this directory shares the one implementation loaded.
3
+ require "hecks/quality_control/adapters/sweep_tools"
4
+
5
+ Hecks.adapter "SweepTools" do
6
+ port "SweepTools"
7
+ end