hecks 6.0.0 → 7.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 (754) hide show
  1. checksums.yaml +4 -4
  2. data/exe/hecks +19 -12
  3. data/lib/hecks/adapters/driven/claude_code.rb +2 -2
  4. data/lib/hecks/adapters/driven/folder.rb +10 -1
  5. data/lib/hecks/adapters/driven/folder_overlay.rb +15 -0
  6. data/lib/hecks/adapters/driven/heki.rb +1 -1
  7. data/lib/hecks/adapters/driven/in_process_key_vault.rb +1 -1
  8. data/lib/hecks/adapters/driven/memory/outbox_rows.rb +2 -2
  9. data/lib/hecks/adapters/driven/postgres/name_guard.rb +25 -0
  10. data/lib/hecks/adapters/driven/postgres/outbox.rb +3 -3
  11. data/lib/hecks/adapters/driven/postgres/repository.rb +1 -1
  12. data/lib/hecks/adapters/driven/postgres/shared_connection.rb +1 -1
  13. data/lib/hecks/adapters/driven/postgres.rb +3 -0
  14. data/lib/hecks/adapters/driven/postgres_era/boot.rb +28 -16
  15. data/lib/hecks/adapters/driven/postgres_era/class_methods.rb +27 -7
  16. data/lib/hecks/adapters/driven/postgres_era/dialect.rb +10 -5
  17. data/lib/hecks/adapters/driven/postgres_era/field_cache_provisioner.rb +39 -0
  18. data/lib/hecks/adapters/driven/postgres_era/journal.rb +52 -13
  19. data/lib/hecks/adapters/driven/postgres_era/lineage/chain_sql.rb +29 -49
  20. data/lib/hecks/adapters/driven/postgres_era/lineage/direct_sql.rb +73 -0
  21. data/lib/hecks/adapters/driven/postgres_era/lineage/direct_verified.json +10 -0
  22. data/lib/hecks/adapters/driven/postgres_era/lineage/direct_verified.rb +65 -0
  23. data/lib/hecks/adapters/driven/postgres_era/lineage/domain_rename.rb +77 -25
  24. data/lib/hecks/adapters/driven/postgres_era/lineage/domain_rename_state.rb +108 -0
  25. data/lib/hecks/adapters/driven/postgres_era/lineage/era_integrity.rb +11 -6
  26. data/lib/hecks/adapters/driven/postgres_era/lineage/era_store.rb +63 -5
  27. data/lib/hecks/adapters/driven/postgres_era/lineage/layered_sql.rb +64 -0
  28. data/lib/hecks/adapters/driven/postgres_era/lineage/ledger_reset.rb +150 -0
  29. data/lib/hecks/adapters/driven/postgres_era/lineage/mint_provisioning.rb +85 -0
  30. data/lib/hecks/adapters/driven/postgres_era/lineage/mint_transaction.rb +11 -7
  31. data/lib/hecks/adapters/driven/postgres_era/lineage/provisioning.rb +11 -5
  32. data/lib/hecks/adapters/driven/postgres_era/lineage/split_sql.rb +76 -0
  33. data/lib/hecks/adapters/driven/postgres_era/lineage/sql_templates.rb +9 -0
  34. data/lib/hecks/adapters/driven/postgres_era/lineage.rb +10 -2
  35. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/context.rb +23 -3
  36. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/coverage_check.rb +3 -1
  37. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/disposable_ledger.rb +119 -0
  38. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/era_resolution.rb +219 -0
  39. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/era_resolver.rb +68 -71
  40. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/held_era.rb +68 -0
  41. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/ledger_change.rb +63 -0
  42. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/merge_coordinator.rb +1 -1
  43. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/mint_decision.rb +163 -0
  44. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/mint_request.rb +24 -0
  45. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/minter.rb +59 -21
  46. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/refusals.rb +24 -11
  47. data/lib/hecks/adapters/driven/postgres_era/lineage_manager.rb +40 -6
  48. data/lib/hecks/adapters/driven/postgres_era/shared_tables.rb +32 -0
  49. data/lib/hecks/adapters/driven/postgres_era.adapter +12 -0
  50. data/lib/hecks/adapters/driven/postgres_era.rb +25 -10
  51. data/lib/hecks/adapters/driven/prism.rb +0 -1
  52. data/lib/hecks/adapters/driven/sql_query_builder.rb +1 -0
  53. data/lib/hecks/adapters/driven/sqlite/dispatch_claims.rb +1 -1
  54. data/lib/hecks/adapters/driven/sqlite/name_guard.rb +25 -0
  55. data/lib/hecks/adapters/driven/sqlite/outbox.rb +2 -2
  56. data/lib/hecks/adapters/driven/sqlite/repository.rb +1 -1
  57. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +20 -0
  58. data/lib/hecks/adapters/driven/sqlite.rb +9 -1
  59. data/lib/hecks/adapters/driven/tenant_provisioner.rb +1 -1
  60. data/lib/hecks/adapters/driven/worktree_overlay.rb +117 -0
  61. data/lib/hecks/adapters/driving/cli/shorthand.rb +5 -1
  62. data/lib/hecks/adapters/driving/cli.rb +1 -1
  63. data/lib/hecks/adapters/driving/cli_runner/answers.rb +77 -22
  64. data/lib/hecks/adapters/driving/cli_runner/bang_rule.rb +35 -0
  65. data/lib/hecks/adapters/driving/cli_runner/completion_words.rb +36 -0
  66. data/lib/hecks/adapters/driving/cli_runner/json_words.rb +55 -0
  67. data/lib/hecks/adapters/driving/cli_runner/name_index.rb +63 -0
  68. data/lib/hecks/adapters/driving/cli_runner/read_context.rb +32 -0
  69. data/lib/hecks/adapters/driving/cli_runner/renamed_names.rb +49 -0
  70. data/lib/hecks/adapters/driving/cli_runner/resolution.rb +43 -17
  71. data/lib/hecks/adapters/driving/cli_runner/resume_words.rb +63 -0
  72. data/lib/hecks/adapters/driving/cli_runner/run_reference.rb +66 -0
  73. data/lib/hecks/adapters/driving/cli_runner/run_replay.rb +118 -0
  74. data/lib/hecks/adapters/driving/cli_runner/search_words.rb +35 -0
  75. data/lib/hecks/adapters/driving/cli_runner/settling.rb +6 -5
  76. data/lib/hecks/adapters/driving/cli_runner/streaming.rb +11 -10
  77. data/lib/hecks/adapters/driving/cli_runner/suggestions.rb +1 -1
  78. data/lib/hecks/adapters/driving/cli_runner/tour_words.rb +77 -0
  79. data/lib/hecks/adapters/driving/cli_runner/verb_names.rb +36 -4
  80. data/lib/hecks/adapters/driving/cli_runner/verbosity.rb +75 -0
  81. data/lib/hecks/adapters/driving/cli_runner.rb +106 -7
  82. data/lib/hecks/adapters/driving/context_line.rb +85 -0
  83. data/lib/hecks/adapters/driving/exit_status.rb +31 -0
  84. data/lib/hecks/adapters/driving/launcher_options.rb +7 -5
  85. data/lib/hecks/adapters/driving/mcp/progress_sink.rb +45 -0
  86. data/lib/hecks/adapters/driving/mcp/tool_calls.rb +7 -5
  87. data/lib/hecks/adapters/driving/mcp.rb +25 -5
  88. data/lib/hecks/adapters/driving/mcp_scope/argument_policy.rb +40 -7
  89. data/lib/hecks/adapters/driving/mcp_scope.rb +8 -7
  90. data/lib/hecks/adapters/driving/ruby/aggregate_module/hooks.rb +154 -0
  91. data/lib/hecks/adapters/driving/ruby/aggregate_module.rb +64 -93
  92. data/lib/hecks/adapters/driving/usage_log.rb +146 -0
  93. data/lib/hecks/adapters/driving/write_guard.rb +55 -0
  94. data/lib/hecks/adapters/driving.rb +0 -1
  95. data/lib/hecks/behaviors/dsl.rb +5 -1
  96. data/lib/hecks/behaviors/expectations/boot.rb +0 -1
  97. data/lib/hecks/bluebook/acknowledgements.rb +1 -1
  98. data/lib/hecks/bluebook/aggregate.rb +21 -18
  99. data/lib/hecks/bluebook/assembly/contracts/sagas.rb +2 -0
  100. data/lib/hecks/bluebook/assembly/contracts/structure.rb +18 -13
  101. data/lib/hecks/bluebook/behaviour/aggregate.rb +27 -3
  102. data/lib/hecks/bluebook/command.rb +16 -5
  103. data/lib/hecks/bluebook/declaration.rb +32 -4
  104. data/lib/hecks/bluebook/domain_port.rb +7 -3
  105. data/lib/hecks/bluebook/dsl/aggregate_builder/relationships.rb +0 -2
  106. data/lib/hecks/bluebook/dsl/aggregate_builder/renaming.rb +25 -0
  107. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +3 -0
  108. data/lib/hecks/bluebook/dsl/attribute_collector.rb +0 -1
  109. data/lib/hecks/bluebook/dsl/audience_word.rb +40 -0
  110. data/lib/hecks/bluebook/dsl/bluebook_builder/metadata.rb +56 -0
  111. data/lib/hecks/bluebook/dsl/bluebook_builder/modules.rb +80 -0
  112. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/correlation_keys.rb +51 -0
  113. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +1 -0
  114. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +17 -34
  115. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +8 -0
  116. data/lib/hecks/bluebook/dsl/command_builder.rb +13 -3
  117. data/lib/hecks/bluebook/dsl/entity_builder.rb +0 -2
  118. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +6 -4
  119. data/lib/hecks/bluebook/dsl/kinds.rb +53 -0
  120. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +9 -2
  121. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +25 -15
  122. data/lib/hecks/bluebook/dsl/translation_builder.rb +16 -1
  123. data/lib/hecks/bluebook/dsl/word_gate.rb +1 -1
  124. data/lib/hecks/bluebook/dsl.rb +1 -0
  125. data/lib/hecks/bluebook/meta_validator/judge/identity.rb +9 -4
  126. data/lib/hecks/bluebook/meta_validator/plan/category.rb +10 -2
  127. data/lib/hecks/bluebook/meta_validator/plan/verbs.rb +9 -0
  128. data/lib/hecks/bluebook/meta_validator/plan.rb +2 -1
  129. data/lib/hecks/bluebook/meta_validator/reconstruction/heads.rb +3 -1
  130. data/lib/hecks/bluebook/meta_validator/reconstruction/reading.rb +7 -0
  131. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +7 -2
  132. data/lib/hecks/bluebook/meta_validator/shapes.rb +3 -0
  133. data/lib/hecks/bluebook/meta_validator/syntax_boot/disk_cache.rb +1 -1
  134. data/lib/hecks/bluebook/model_check/aggregate_checks.rb +27 -0
  135. data/lib/hecks/bluebook/model_check/ask_checks.rb +25 -0
  136. data/lib/hecks/bluebook/model_check/reaction_chains.rb +114 -0
  137. data/lib/hecks/bluebook/model_check/reaction_cycles.rb +53 -0
  138. data/lib/hecks/bluebook/model_check/reaction_depth_checks.rb +58 -0
  139. data/lib/hecks/bluebook/model_check/reaction_walk.rb +62 -0
  140. data/lib/hecks/bluebook/model_check.rb +5 -27
  141. data/lib/hecks/bluebook/process_manager.rb +20 -3
  142. data/lib/hecks/bluebook/translation.rb +5 -3
  143. data/lib/hecks/bluebook/translation_plan.rb +9 -2
  144. data/lib/hecks/ci_pipeline/adapters/ci_cache.adapter +7 -0
  145. data/lib/hecks/ci_pipeline/adapters/ci_cache.rb +39 -0
  146. data/lib/hecks/ci_pipeline/adapters/ci_runner.adapter +7 -0
  147. data/lib/hecks/ci_pipeline/adapters/ci_runner.rb +55 -0
  148. data/lib/hecks/ci_pipeline/ci_pipeline.behaviors +322 -0
  149. data/lib/hecks/ci_pipeline/ci_pipeline.bluebook +938 -0
  150. data/lib/hecks/ci_pipeline/ci_pipeline.hecksagon +12 -0
  151. data/lib/hecks/ci_pipeline/ci_pipeline.ports.hecksagon +16 -0
  152. data/lib/hecks/ci_speed/adapters/ci_reader.adapter +7 -0
  153. data/lib/hecks/ci_speed/adapters/ci_reader.rb +141 -0
  154. data/lib/hecks/ci_speed/adapters/local_runner/answer.rb +41 -0
  155. data/lib/hecks/ci_speed/adapters/local_runner/catalog.rb +89 -0
  156. data/lib/hecks/ci_speed/adapters/local_runner/checkouts.rb +89 -0
  157. data/lib/hecks/ci_speed/adapters/local_runner/launcher.rb +69 -0
  158. data/lib/hecks/ci_speed/adapters/local_runner/load_average.rb +38 -0
  159. data/lib/hecks/ci_speed/adapters/local_runner/refs.rb +39 -0
  160. data/lib/hecks/ci_speed/adapters/local_runner/refused.rb +11 -0
  161. data/lib/hecks/ci_speed/adapters/local_runner/verdict.rb +71 -0
  162. data/lib/hecks/ci_speed/adapters/local_runner.adapter +7 -0
  163. data/lib/hecks/ci_speed/adapters/local_runner.rb +168 -0
  164. data/lib/hecks/ci_speed/adapters/replayed_runner.rb +39 -0
  165. data/lib/hecks/ci_speed/ci_speed.behaviors +341 -0
  166. data/lib/hecks/ci_speed/ci_speed.bluebook +1073 -0
  167. data/lib/hecks/ci_speed/ci_speed.hecksagon +11 -0
  168. data/lib/hecks/ci_speed/ci_speed.ports.hecksagon +21 -0
  169. data/lib/hecks/ci_workflows/ci_workflows.behaviors +13 -0
  170. data/lib/hecks/ci_workflows/ci_workflows.bluebook +173 -0
  171. data/lib/hecks/ci_workflows/ci_workflows.hecksagon +15 -0
  172. data/lib/hecks/coder/coder.bluebook +385 -0
  173. data/lib/hecks/coder/coder.hecksagon +12 -0
  174. data/lib/hecks/command_names/check.rb +39 -0
  175. data/lib/hecks/command_names/extractor.rb +67 -0
  176. data/lib/hecks/command_names/index.rb +40 -0
  177. data/lib/hecks/command_names/resolution.rb +81 -0
  178. data/lib/hecks/command_names/scan.rb +54 -0
  179. data/lib/hecks/command_names/sources.rb +73 -0
  180. data/lib/hecks/command_names.rb +26 -0
  181. data/lib/hecks/composer/jobs.rb +1 -1
  182. data/lib/hecks/composer/toolbox.rb +8 -1
  183. data/lib/hecks/corpus/generated_key.rb +205 -0
  184. data/lib/hecks/corpus/listing.rb +34 -0
  185. data/lib/hecks/corpus/rust.rb +27 -11
  186. data/lib/hecks/corpus/sources.rb +2 -1
  187. data/lib/hecks/corpus.rb +37 -42
  188. data/lib/hecks/deprecation.rb +23 -0
  189. data/lib/hecks/doc/reference/readme.rb +1 -1
  190. data/lib/hecks/doc/reference/rendering.rb +2 -2
  191. data/lib/hecks/doc/reference.rb +1 -1
  192. data/lib/hecks/embryonaut_bluebook/manifest.rb +1 -1
  193. data/lib/hecks/fixer/fixer.bluebook +240 -0
  194. data/lib/hecks/fixer/fixer.hecksagon +17 -0
  195. data/lib/hecks/framework_bluebooks.rb +32 -7
  196. data/lib/hecks/fuzzing/concurrent_dispatch/world_copy.rb +1 -0
  197. data/lib/hecks/fuzzing/isolated_boot/postgres_scratch.rb +11 -12
  198. data/lib/hecks/fuzzing/isolated_boot/worlds.rb +23 -5
  199. data/lib/hecks/fuzzing/isolated_boot.rb +1 -1
  200. data/lib/hecks/fuzzing/mutation/checks.rb +3 -3
  201. data/lib/hecks/grammar/translation.bluebook +1 -1
  202. data/lib/hecks/grammar/translation_rules.json +64 -0
  203. data/lib/hecks/hecks/adapter_words.json +7 -0
  204. data/lib/hecks/hecks/adapters/area_overlap.rb +156 -0
  205. data/lib/hecks/hecks/adapters/claude_agents.rb +8 -3
  206. data/lib/hecks/hecks/adapters/codebase/batch_runs.rb +60 -0
  207. data/lib/hecks/hecks/adapters/codebase/changed_specs.rb +52 -0
  208. data/lib/hecks/hecks/adapters/codebase/codemods.rb +3 -2
  209. data/lib/hecks/hecks/adapters/codebase/conformance.rb +14 -1
  210. data/lib/hecks/hecks/adapters/codebase/corpus_queries.rb +53 -0
  211. data/lib/hecks/hecks/adapters/codebase/generated_keys.rb +31 -0
  212. data/lib/hecks/hecks/adapters/codebase/impacted_specs.rb +110 -0
  213. data/lib/hecks/hecks/adapters/codebase/language.rb +2 -2
  214. data/lib/hecks/hecks/adapters/codebase/launcher_text.rb +39 -0
  215. data/lib/hecks/hecks/adapters/codebase/minitest_runs.rb +52 -0
  216. data/lib/hecks/hecks/adapters/codebase/regeneration.rb +51 -8
  217. data/lib/hecks/hecks/adapters/codebase/run_options.rb +35 -0
  218. data/lib/hecks/hecks/adapters/codebase/scripting.rb +39 -0
  219. data/lib/hecks/hecks/adapters/codebase/search.rb +1 -1
  220. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +79 -51
  221. data/lib/hecks/hecks/adapters/codebase/spec_files/report.rb +71 -0
  222. data/lib/hecks/hecks/adapters/codebase/spec_files/shared_run.rb +73 -0
  223. data/lib/hecks/hecks/adapters/codebase/spec_files.rb +95 -0
  224. data/lib/hecks/hecks/adapters/codebase/test_runner.rb +53 -4
  225. data/lib/hecks/hecks/adapters/codebase/test_scope.rb +105 -0
  226. data/lib/hecks/hecks/adapters/codebase/test_suite.rb +48 -34
  227. data/lib/hecks/hecks/adapters/console_capture.rb +1 -1
  228. data/lib/hecks/hecks/adapters/cost_explorer.rb +9 -1
  229. data/lib/hecks/hecks/adapters/dispatching/brief_text.rb +51 -0
  230. data/lib/hecks/hecks/adapters/dispatching/launcher.rb +97 -0
  231. data/lib/hecks/hecks/adapters/dispatching/plan.rb +82 -0
  232. data/lib/hecks/hecks/adapters/dispatching/rows.rb +66 -0
  233. data/lib/hecks/hecks/adapters/dispatching/settings.rb +81 -0
  234. data/lib/hecks/hecks/adapters/dispatching/settler.rb +40 -0
  235. data/lib/hecks/hecks/adapters/dispatching.adapter +7 -0
  236. data/lib/hecks/hecks/adapters/dispatching.rb +93 -0
  237. data/lib/hecks/hecks/adapters/doctor_reading/report.rb +27 -0
  238. data/lib/hecks/hecks/adapters/doctor_reading.adapter +7 -0
  239. data/lib/hecks/hecks/adapters/doctor_reading.rb +161 -0
  240. data/lib/hecks/hecks/adapters/git_checkout.rb +1 -1
  241. data/lib/hecks/hecks/adapters/git_commit.adapter +7 -0
  242. data/lib/hecks/hecks/adapters/git_commit.rb +61 -0
  243. data/lib/hecks/hecks/adapters/git_landing/finisher.rb +82 -0
  244. data/lib/hecks/hecks/adapters/git_landing/foreign_hold.rb +111 -0
  245. data/lib/hecks/hecks/adapters/git_landing/line.rb +171 -0
  246. data/lib/hecks/hecks/adapters/git_landing/lineup.rb +98 -0
  247. data/lib/hecks/hecks/adapters/git_landing/merger.rb +1 -1
  248. data/lib/hecks/hecks/adapters/git_landing/numbering.rb +54 -0
  249. data/lib/hecks/hecks/adapters/git_landing/rivals.rb +59 -0
  250. data/lib/hecks/hecks/adapters/git_landing/ticket.rb +156 -0
  251. data/lib/hecks/hecks/adapters/git_landing/tidy.rb +147 -0
  252. data/lib/hecks/hecks/adapters/git_landing/train.rb +104 -0
  253. data/lib/hecks/hecks/adapters/git_landing/verdict.rb +64 -0
  254. data/lib/hecks/hecks/adapters/git_landing/waiting.rb +120 -0
  255. data/lib/hecks/hecks/adapters/git_landing.rb +106 -20
  256. data/lib/hecks/hecks/adapters/git_main_pull.rb +99 -0
  257. data/lib/hecks/hecks/adapters/git_rehearsal/bench.rb +82 -0
  258. data/lib/hecks/hecks/adapters/git_rehearsal/due.rb +84 -0
  259. data/lib/hecks/hecks/adapters/git_rehearsal/impacted/skips.rb +47 -0
  260. data/lib/hecks/hecks/adapters/git_rehearsal/impacted/spec_run.rb +75 -0
  261. data/lib/hecks/hecks/adapters/git_rehearsal/impacted.rb +181 -0
  262. data/lib/hecks/hecks/adapters/git_rehearsal/line.rb +76 -0
  263. data/lib/hecks/hecks/adapters/git_rehearsal/reading.rb +24 -0
  264. data/lib/hecks/hecks/adapters/git_rehearsal/record.rb +146 -0
  265. data/lib/hecks/hecks/adapters/git_rehearsal/spec_map/corpus.rb +25 -0
  266. data/lib/hecks/hecks/adapters/git_rehearsal/spec_map/needles.rb +61 -0
  267. data/lib/hecks/hecks/adapters/git_rehearsal/spec_map.rb +105 -0
  268. data/lib/hecks/hecks/adapters/git_rehearsal/stages.rb +118 -0
  269. data/lib/hecks/hecks/adapters/git_rehearsal.adapter +7 -0
  270. data/lib/hecks/hecks/adapters/git_rehearsal.rb +141 -0
  271. data/lib/hecks/hecks/adapters/git_train/bench.rb +15 -15
  272. data/lib/hecks/hecks/adapters/git_train/branch_guard.rb +62 -0
  273. data/lib/hecks/hecks/adapters/git_train/carriage.rb +27 -28
  274. data/lib/hecks/hecks/adapters/git_train/launcher_shape.rb +75 -0
  275. data/lib/hecks/hecks/adapters/git_train/mint_step.rb +47 -0
  276. data/lib/hecks/hecks/adapters/git_train/settlement.rb +167 -0
  277. data/lib/hecks/hecks/adapters/git_train.rb +73 -15
  278. data/lib/hecks/hecks/adapters/git_worktree_add.adapter +7 -0
  279. data/lib/hecks/hecks/adapters/git_worktree_add.rb +68 -0
  280. data/lib/hecks/hecks/adapters/github_pull_requests.adapter +7 -0
  281. data/lib/hecks/hecks/adapters/github_pull_requests.rb +109 -0
  282. data/lib/hecks/hecks/adapters/host_http.rb +6 -6
  283. data/lib/hecks/hecks/adapters/in_process_boot.rb +10 -8
  284. data/lib/hecks/hecks/adapters/in_process_operations.rb +11 -17
  285. data/lib/hecks/hecks/adapters/in_process_undo.rb +17 -0
  286. data/lib/hecks/hecks/adapters/journal_store/changes.rb +13 -4
  287. data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +28 -15
  288. data/lib/hecks/hecks/adapters/journal_store/examination.rb +17 -1
  289. data/lib/hecks/hecks/adapters/journal_store/held_domain/renaming.rb +45 -0
  290. data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +31 -5
  291. data/lib/hecks/hecks/adapters/journal_store/held_mint.rb +128 -0
  292. data/lib/hecks/hecks/adapters/journal_store/readings/attestation.rb +1 -1
  293. data/lib/hecks/hecks/adapters/journal_store/readings/boot_status.rb +58 -0
  294. data/lib/hecks/hecks/adapters/journal_store/readings/composition.rb +39 -0
  295. data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/outcomes.rb +76 -0
  296. data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/postgres_reader.rb +44 -0
  297. data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/reader.rb +64 -0
  298. data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/report.rb +102 -0
  299. data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/sqlite_reader.rb +43 -0
  300. data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/stores.rb +80 -0
  301. data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/subscribers.rb +72 -0
  302. data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/summary.rb +39 -0
  303. data/lib/hecks/hecks/adapters/journal_store/readings/event_trace.rb +39 -0
  304. data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/command_line.rb +57 -0
  305. data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/edge_guards.rb +107 -0
  306. data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/inspection.rb +94 -0
  307. data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/plan.rb +66 -0
  308. data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/report.rb +72 -0
  309. data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/store.rb +34 -0
  310. data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/watermark_guards.rb +86 -0
  311. data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/world.rb +66 -0
  312. data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan.rb +36 -0
  313. data/lib/hecks/hecks/adapters/journal_store/readings/projection_audit.rb +64 -0
  314. data/lib/hecks/hecks/adapters/journal_store/readings/scaffolding.rb +3 -3
  315. data/lib/hecks/hecks/adapters/journal_store/readings/sqlite_status/database_reading.rb +113 -0
  316. data/lib/hecks/hecks/adapters/journal_store/readings/sqlite_status/read_only.rb +33 -0
  317. data/lib/hecks/hecks/adapters/journal_store/readings/sqlite_status.rb +32 -0
  318. data/lib/hecks/hecks/adapters/journal_store/readings.rb +13 -1
  319. data/lib/hecks/hecks/adapters/journal_store/reconcile_report.rb +60 -0
  320. data/lib/hecks/hecks/adapters/journal_store/reconciliation.rb +121 -0
  321. data/lib/hecks/hecks/adapters/journal_store/translation_write/shape_edge.rb +59 -0
  322. data/lib/hecks/hecks/adapters/journal_store/translation_write.rb +123 -0
  323. data/lib/hecks/hecks/adapters/journal_store.rb +16 -0
  324. data/lib/hecks/hecks/adapters/landed_work.adapter +7 -0
  325. data/lib/hecks/hecks/adapters/landed_work.rb +52 -0
  326. data/lib/hecks/hecks/adapters/local_files.rb +6 -6
  327. data/lib/hecks/hecks/adapters/process_pool.rb +3 -2
  328. data/lib/hecks/hecks/adapters/push_size.rb +57 -0
  329. data/lib/hecks/hecks/adapters/qa_quiescing.adapter +7 -0
  330. data/lib/hecks/hecks/adapters/qa_quiescing.rb +57 -0
  331. data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +30 -3
  332. data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +63 -3
  333. data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +29 -6
  334. data/lib/hecks/hecks/adapters/rehearsal/subject.rb +11 -5
  335. data/lib/hecks/hecks/adapters/rust_toolchain.rb +10 -1
  336. data/lib/hecks/hecks/adapters/rust_workspace.rb +1 -1
  337. data/lib/hecks/hecks/adapters/settings_reading/disposable_ledger.rb +32 -0
  338. data/lib/hecks/hecks/adapters/settings_reading/push_nudge.rb +26 -0
  339. data/lib/hecks/hecks/adapters/settings_reading/stores.rb +76 -0
  340. data/lib/hecks/hecks/adapters/settings_reading/test_scope.rb +28 -0
  341. data/lib/hecks/hecks/adapters/settings_reading.rb +28 -2
  342. data/lib/hecks/hecks/adapters/started_work.rb +42 -0
  343. data/lib/hecks/hecks/adapters/terminal.rb +12 -10
  344. data/lib/hecks/hecks/adapters/where_reading.adapter +7 -0
  345. data/lib/hecks/hecks/adapters/where_reading.rb +64 -0
  346. data/lib/hecks/hecks/adapters/work_areas.rb +57 -0
  347. data/lib/hecks/hecks/adapters/work_limits.rb +57 -0
  348. data/lib/hecks/hecks/build/build.bluebook +576 -0
  349. data/lib/hecks/hecks/check/check.bluebook +966 -0
  350. data/lib/hecks/hecks/codebase.behaviors +61 -5
  351. data/lib/hecks/hecks/composer.behaviors +1 -1
  352. data/lib/hecks/hecks/context_map.hecksagon +1 -1
  353. data/lib/hecks/hecks/custodian.behaviors +33 -7
  354. data/lib/hecks/hecks/delegate/delegate.bluebook +355 -0
  355. data/lib/hecks/hecks/deliver/deliver.bluebook +945 -0
  356. data/lib/hecks/hecks/deploy.behaviors +1 -1
  357. data/lib/hecks/hecks/evolve/evolve.bluebook +788 -0
  358. data/lib/hecks/hecks/find/find.bluebook +81 -0
  359. data/lib/hecks/hecks/hecks.behaviors +1 -1
  360. data/lib/hecks/hecks/hecks.bluebook +0 -133
  361. data/lib/hecks/hecks/hecks.contextmap +2 -0
  362. data/lib/hecks/hecks/hecks.hecksagon +185 -29
  363. data/lib/hecks/hecks/hecks.world +35 -9
  364. data/lib/hecks/hecks/implicit_moves.json +24 -0
  365. data/lib/hecks/hecks/migrate/migrate.bluebook +1066 -0
  366. data/lib/hecks/hecks/operate/operate.bluebook +695 -0
  367. data/lib/hecks/hecks/quiesce/quiesce.bluebook +117 -0
  368. data/lib/hecks/hecks/rehearse/rehearse.bluebook +133 -0
  369. data/lib/hecks/hecks/release/release.bluebook +916 -0
  370. data/lib/hecks/hecks/serve/serve.bluebook +134 -0
  371. data/lib/hecks/hecks/shapes.json +1 -15
  372. data/lib/hecks/hecks/site.behaviors +1 -1
  373. data/lib/hecks/hecks/start/start.bluebook +194 -0
  374. data/lib/hecks/hecks/test/test.bluebook +638 -0
  375. data/lib/hecks/hecks/tickets.world +14 -0
  376. data/lib/hecks/language/bluebook/aggregate.bluebook +22 -0
  377. data/lib/hecks/language/bluebook/bluebook.bluebook +34 -0
  378. data/lib/hecks/language/bluebook/command.bluebook +35 -0
  379. data/lib/hecks/language/bluebook/process_manager.bluebook +14 -1
  380. data/lib/hecks/language/bluebook/vocabulary.bluebook +174 -54
  381. data/lib/hecks/language/hecksagon/domain_port.bluebook +1 -0
  382. data/lib/hecks/language/hecksagon/port_operation.bluebook +14 -0
  383. data/lib/hecks/language/kinds/run.kind +120 -0
  384. data/lib/hecks/language/oidc.json +10 -0
  385. data/lib/hecks/language/translation/translation_aggregate.bluebook +24 -0
  386. data/lib/hecks/module_folders.rb +31 -0
  387. data/lib/hecks/ports/key_vault.rb +1 -1
  388. data/lib/hecks/ports/persistence/append_only.rb +26 -8
  389. data/lib/hecks/ports/persistence/binding_policy.rb +3 -1
  390. data/lib/hecks/ports/persistence/codec_boundary.rb +1 -1
  391. data/lib/hecks/ports/persistence/mint_door.rb +74 -0
  392. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +6 -5
  393. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +5 -1
  394. data/lib/hecks/ports/persistence/plugins/era/era_status.rb +48 -0
  395. data/lib/hecks/ports/persistence/plugins/era/projected_bluebook.rb +127 -0
  396. data/lib/hecks/ports/persistence/plugins/era/translation/additive_shape.rb +107 -0
  397. data/lib/hecks/ports/persistence/plugins/era/translation/compose.rb +155 -0
  398. data/lib/hecks/ports/persistence/plugins/era/translation/compose_render.rb +79 -0
  399. data/lib/hecks/ports/persistence/plugins/era/translation/edge_files.rb +49 -0
  400. data/lib/hecks/ports/persistence/plugins/era/translation/edge_route.rb +61 -0
  401. data/lib/hecks/ports/persistence/plugins/era/translation/implicit_edge.rb +67 -0
  402. data/lib/hecks/ports/persistence/plugins/era/translation/reconcile.rb +130 -0
  403. data/lib/hecks/ports/persistence/plugins/era/translation/reconcile_files.rb +35 -0
  404. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +13 -5
  405. data/lib/hecks/ports/persistence/plugins/era/translation.rb +5 -0
  406. data/lib/hecks/ports/persistence/plugins/era.rb +1 -0
  407. data/lib/hecks/ports/persistence/repository_factory.rb +11 -3
  408. data/lib/hecks/ports/persistence/stored_name_guard.rb +36 -0
  409. data/lib/hecks/ports/persistence.rb +2 -0
  410. data/lib/hecks/projections/ci_pipeline/table/checks.rb +137 -0
  411. data/lib/hecks/projections/ci_pipeline/table/lookup.rb +59 -0
  412. data/lib/hecks/projections/ci_pipeline/table/refusals.rb +58 -0
  413. data/lib/hecks/projections/ci_pipeline/table/rows.rb +119 -0
  414. data/lib/hecks/projections/ci_pipeline/table/tree.rb +30 -0
  415. data/lib/hecks/projections/ci_pipeline/table/typing.rb +92 -0
  416. data/lib/hecks/projections/ci_pipeline/table.rb +112 -0
  417. data/lib/hecks/projections/ci_pipeline/workflows.rb +162 -0
  418. data/lib/hecks/projections/ci_pipeline/yaml_text.rb +104 -0
  419. data/lib/hecks/projections/deploy/box/hosting.rb +1 -1
  420. data/lib/hecks/projections/deploy/box/roll_recipe.rb +1 -1
  421. data/lib/hecks/projections/deploy/box/roll_scripts.rb +1 -1
  422. data/lib/hecks/projections/deploy/box.rb +1 -1
  423. data/lib/hecks/projections/deploy/templates/fargate/Makefile.tmpl +1 -1
  424. data/lib/hecks/projections/deploy/templates/lambda/Makefile.tmpl +3 -3
  425. data/lib/hecks/projections/undo.rb +46 -0
  426. data/lib/hecks/projections.rb +2 -0
  427. data/lib/hecks/projector/cli_projector/claims.rb +11 -0
  428. data/lib/hecks/projector/cli_projector/command_help.rb +6 -1
  429. data/lib/hecks/projector/cli_projector/command_specs.rb +11 -1
  430. data/lib/hecks/projector/cli_projector/completion.rb +87 -0
  431. data/lib/hecks/projector/cli_projector/everyday.rb +40 -0
  432. data/lib/hecks/projector/cli_projector/json_listing.rb +77 -0
  433. data/lib/hecks/projector/cli_projector/listing.rb +1 -1
  434. data/lib/hecks/projector/cli_projector/modules.rb +39 -0
  435. data/lib/hecks/projector/cli_projector/query_specs.rb +1 -0
  436. data/lib/hecks/projector/cli_projector/renames.rb +58 -0
  437. data/lib/hecks/projector/cli_projector/search.rb +74 -0
  438. data/lib/hecks/projector/cli_projector/usage.rb +23 -6
  439. data/lib/hecks/projector/cli_projector.rb +24 -1
  440. data/lib/hecks/quality_control/adapters/ci_head.adapter +8 -0
  441. data/lib/hecks/quality_control/adapters/image_roll.adapter +9 -0
  442. data/lib/hecks/quality_control/adapters/image_roll.rb +158 -0
  443. data/lib/hecks/quality_control/adapters/main_maintenance.adapter +8 -0
  444. data/lib/hecks/quality_control/adapters/main_maintenance.rb +275 -0
  445. data/lib/hecks/quality_control/adapters/pump_tools.adapter +7 -0
  446. data/lib/hecks/quality_control/adapters/pump_tools.rb +18 -0
  447. data/lib/hecks/quality_control/adapters/push_due_reader.adapter +8 -0
  448. data/lib/hecks/quality_control/adapters/push_due_reader.rb +99 -0
  449. data/lib/hecks/quality_control/adapters/qa_auto_mint.rb +183 -0
  450. data/lib/hecks/quality_control/adapters/qa_era_mint.rb +94 -0
  451. data/lib/hecks/quality_control/adapters/qa_quiesce_marker.rb +126 -0
  452. data/lib/hecks/quality_control/adapters/run_keys.rb +36 -0
  453. data/lib/hecks/quality_control/adapters/run_reader.adapter +7 -0
  454. data/lib/hecks/quality_control/adapters/run_reader.rb +188 -0
  455. data/lib/hecks/quality_control/cli/child.rb +1 -0
  456. data/lib/hecks/quality_control/cli/qa_discover_external_domains/scanning.rb +1 -1
  457. data/lib/hecks/quality_control/cli/qa_domain_novelty.rb +3 -3
  458. data/lib/hecks/quality_control/cli/qa_event_pump/attached.rb +67 -0
  459. data/lib/hecks/quality_control/cli/qa_event_pump/consumer.rb +103 -0
  460. data/lib/hecks/quality_control/cli/qa_event_pump/cursors.rb +60 -0
  461. data/lib/hecks/quality_control/cli/qa_event_pump/reactions.rb +46 -0
  462. data/lib/hecks/quality_control/cli/qa_event_pump/reader.rb +30 -0
  463. data/lib/hecks/quality_control/cli/qa_event_pump.rb +76 -0
  464. data/lib/hecks/quality_control/cli/qa_fix_main_bug/publishing.rb +79 -0
  465. data/lib/hecks/quality_control/cli/qa_fix_main_bug.rb +167 -0
  466. data/lib/hecks/quality_control/cli/qa_generated_domains/evaluation.rb +1 -1
  467. data/lib/hecks/quality_control/cli/qa_generated_domains/promotion.rb +3 -3
  468. data/lib/hecks/quality_control/cli/qa_main_gate/gate_run.rb +52 -0
  469. data/lib/hecks/quality_control/cli/qa_main_gate/logging.rb +70 -0
  470. data/lib/hecks/quality_control/cli/qa_main_gate/shell.rb +47 -0
  471. data/lib/hecks/quality_control/cli/qa_main_gate/sweeping.rb +59 -0
  472. data/lib/hecks/quality_control/cli/qa_main_gate/worktree.rb +62 -0
  473. data/lib/hecks/quality_control/cli/qa_main_gate.rb +135 -0
  474. data/lib/hecks/quality_control/cli/qa_postgres_role.rb +1 -1
  475. data/lib/hecks/quality_control/cli/qa_pr_check/checking.rb +2 -3
  476. data/lib/hecks/quality_control/cli/qa_push_nudge.rb +48 -0
  477. data/lib/hecks/quality_control/cli/qa_seed_angles/seed.rb +1 -1
  478. data/lib/hecks/quality_control/cli/qa_sweep/seat.rb +1 -1
  479. data/lib/hecks/quality_control/cli/qa_tick/event_pump_step.rb +34 -0
  480. data/lib/hecks/quality_control/cli/qa_tick/output.rb +16 -1
  481. data/lib/hecks/quality_control/cli/qa_tick/push_nudge_step.rb +28 -0
  482. data/lib/hecks/quality_control/cli/qa_tick/quiesce_guard.rb +51 -0
  483. data/lib/hecks/quality_control/cli/qa_tick.rb +36 -8
  484. data/lib/hecks/quality_control/quality_control.behaviors +65 -0
  485. data/lib/hecks/quality_control/quality_control.bluebook +1131 -11
  486. data/lib/hecks/quality_control/quality_control.hecksagon +11 -0
  487. data/lib/hecks/quality_control/quality_control.ports.hecksagon +138 -0
  488. data/lib/hecks/query_ir/touchpoints.rb +5 -7
  489. data/lib/hecks/refused.rb +21 -0
  490. data/lib/hecks/release/approval.rb +6 -3
  491. data/lib/hecks/release/conditions.rb +46 -0
  492. data/lib/hecks/release/runner/gem_publisher.rb +4 -4
  493. data/lib/hecks/release/version_commit.rb +48 -0
  494. data/lib/hecks/runtime/command_interpreter/hydration.rb +5 -4
  495. data/lib/hecks/runtime/command_interpreter/persisting.rb +11 -3
  496. data/lib/hecks/runtime/command_interpreter.rb +2 -2
  497. data/lib/hecks/runtime/command_rules/admissibility.rb +0 -2
  498. data/lib/hecks/runtime/command_rules/emission.rb +0 -1
  499. data/lib/hecks/runtime/dependency_planning/expression_reads.rb +0 -1
  500. data/lib/hecks/runtime/dispatcher/planning.rb +24 -0
  501. data/lib/hecks/runtime/dispatcher/routing.rb +6 -0
  502. data/lib/hecks/runtime/dispatcher/value_routing.rb +54 -0
  503. data/lib/hecks/runtime/dispatcher.rb +7 -1
  504. data/lib/hecks/runtime/dry_run_plan.rb +60 -0
  505. data/lib/hecks/runtime/entity_element/location.rb +0 -1
  506. data/lib/hecks/runtime/entity_element/mutations.rb +0 -1
  507. data/lib/hecks/runtime/entity_interpreter/locating.rb +5 -0
  508. data/lib/hecks/runtime/entity_interpreter.rb +0 -1
  509. data/lib/hecks/runtime/errors.rb +26 -0
  510. data/lib/hecks/runtime/instance.rb +0 -1
  511. data/lib/hecks/runtime/invocation_builder.rb +0 -1
  512. data/lib/hecks/runtime/outbox/consumer_runner.rb +9 -1
  513. data/lib/hecks/runtime/outbox/relay.rb +25 -4
  514. data/lib/hecks/runtime/outbox/runnable.rb +3 -1
  515. data/lib/hecks/runtime/outbox.rb +6 -2
  516. data/lib/hecks/runtime/policy_interpreter/fan_out.rb +18 -2
  517. data/lib/hecks/runtime/policy_interpreter.rb +17 -9
  518. data/lib/hecks/runtime/port_operation_interpreter.rb +21 -7
  519. data/lib/hecks/runtime/query_interpreter/arguments.rb +6 -5
  520. data/lib/hecks/runtime/reaction_invocation.rb +0 -1
  521. data/lib/hecks/runtime/refusal_wording.rb +12 -1
  522. data/lib/hecks/runtime/registry/context_maps.rb +1 -5
  523. data/lib/hecks/runtime/registry/declarations.rb +34 -4
  524. data/lib/hecks/runtime/registry/hecksagon_rules.rb +15 -1
  525. data/lib/hecks/runtime/registry/runtime_state.rb +10 -0
  526. data/lib/hecks/runtime/registry/world_defaults.rb +24 -2
  527. data/lib/hecks/runtime/registry/world_names.rb +99 -0
  528. data/lib/hecks/runtime/registry.rb +33 -27
  529. data/lib/hecks/runtime/saga_interpreter/compensation.rb +10 -0
  530. data/lib/hecks/runtime/saga_interpreter/correlation.rb +29 -3
  531. data/lib/hecks/runtime/saga_interpreter/delivery.rb +0 -1
  532. data/lib/hecks/runtime/saga_interpreter/lifecycle.rb +1 -1
  533. data/lib/hecks/runtime/saga_interpreter.rb +46 -2
  534. data/lib/hecks/runtime/value/coercion.rb +36 -8
  535. data/lib/hecks/rust_build/conformance.rb +16 -5
  536. data/lib/hecks/rust_build/parser.rb +25 -0
  537. data/lib/hecks/rust_build/project_rust/codegen_run.rb +45 -12
  538. data/lib/hecks/rust_build/project_rust/declared.rb +80 -0
  539. data/lib/hecks/rust_build/project_rust/features_header.rb +22 -0
  540. data/lib/hecks/rust_build/project_rust/manifest.rb +96 -0
  541. data/lib/hecks/rust_build/project_rust/meta.rb +36 -0
  542. data/lib/hecks/rust_build/project_rust.rb +28 -90
  543. data/lib/hecks/rust_build/write_if_changed.rb +19 -4
  544. data/lib/hecks/rust_build.rb +2 -1
  545. data/lib/hecks/rust_format.rb +32 -2
  546. data/lib/hecks/secrets.rb +111 -0
  547. data/lib/hecks/shape_ledger/edge_hygiene.rb +170 -0
  548. data/lib/hecks/shape_ledger/scope.rb +51 -0
  549. data/lib/hecks/shape_ledger/status.rb +89 -0
  550. data/lib/hecks/shape_ledger.rb +78 -33
  551. data/lib/hecks/site/bluebook/site.bluebook +1 -0
  552. data/lib/hecks/storehouse/audit.rb +34 -4
  553. data/lib/hecks/storehouse/callers.rb +8 -0
  554. data/lib/hecks/storehouse/dispatching.rb +14 -3
  555. data/lib/hecks/storehouse/identity.rb +41 -0
  556. data/lib/hecks/storehouse/querying.rb +7 -7
  557. data/lib/hecks/storehouse.rb +28 -10
  558. data/lib/hecks/tickets/bluebook/adapters/work_areas.adapter +7 -0
  559. data/lib/hecks/tickets/bluebook/adapters/work_limits.adapter +7 -0
  560. data/lib/hecks/tickets/bluebook/tickets.behaviors +70 -0
  561. data/lib/hecks/tickets/bluebook/tickets.bluebook +323 -28
  562. data/lib/hecks/tickets/bluebook/tickets.hecksagon +7 -0
  563. data/lib/hecks/tickets/bluebook/tickets.ports.hecksagon +17 -0
  564. data/lib/hecks/{cli → tools}/behaviors.rb +1 -1
  565. data/lib/hecks/{cli → tools}/canonicalise.rb +1 -1
  566. data/lib/hecks/{cli → tools}/check_engine_agreement.rb +1 -1
  567. data/lib/hecks/{cli → tools}/check_era.rb +6 -6
  568. data/lib/hecks/tools/ci_gates.rb +8 -2
  569. data/lib/hecks/tools/ci_workflows.rb +98 -0
  570. data/lib/hecks/{cli → tools}/console.rb +1 -1
  571. data/lib/hecks/{cli → tools}/deploy_template_diff.rb +1 -1
  572. data/lib/hecks/{cli → tools}/doc_coverage.rb +2 -2
  573. data/lib/hecks/{cli → tools}/document.rb +1 -1
  574. data/lib/hecks/{cli → tools}/domain_stub.rb +1 -1
  575. data/lib/hecks/{cli → tools}/domain_writer.rb +1 -1
  576. data/lib/hecks/tools/downstream_model_check.rb +122 -0
  577. data/lib/hecks/tools/evolve_run.rb +2 -2
  578. data/lib/hecks/{cli → tools}/expected_era.rb +1 -1
  579. data/lib/hecks/{cli → tools}/expression_projection.rb +1 -1
  580. data/lib/hecks/{cli → tools}/follow.rb +1 -1
  581. data/lib/hecks/tools/fuzz_sweep/options.rb +40 -5
  582. data/lib/hecks/tools/fuzz_sweep/pool.rb +12 -1
  583. data/lib/hecks/tools/fuzz_sweep/shards.rb +155 -0
  584. data/lib/hecks/tools/fuzz_sweep.rb +11 -5
  585. data/lib/hecks/tools/gate.rb +38 -29
  586. data/lib/hecks/tools/gate_memo/local_files.rb +33 -0
  587. data/lib/hecks/tools/gate_memo/rehearsal.rb +54 -0
  588. data/lib/hecks/tools/gate_memo/ruby_files.rb +152 -0
  589. data/lib/hecks/tools/gate_memo/session.rb +114 -0
  590. data/lib/hecks/tools/gate_memo/standalone.rb +67 -0
  591. data/lib/hecks/tools/gate_memo.rb +196 -0
  592. data/lib/hecks/tools/gate_report.rb +56 -0
  593. data/lib/hecks/{cli → tools}/history.rb +1 -1
  594. data/lib/hecks/{cli → tools}/interview_agent.rb +1 -1
  595. data/lib/hecks/{cli → tools}/interview_aggregate.rb +1 -1
  596. data/lib/hecks/{cli → tools}/interview_draft.rb +1 -1
  597. data/lib/hecks/{cli → tools}/interview_record.rb +1 -1
  598. data/lib/hecks/{cli → tools}/interview_run.rb +2 -2
  599. data/lib/hecks/{cli → tools}/interview_session/agent_state.rb +1 -1
  600. data/lib/hecks/{cli → tools}/interview_session/catalog.rb +1 -1
  601. data/lib/hecks/{cli → tools}/interview_session/manual_findings.rb +1 -1
  602. data/lib/hecks/{cli → tools}/interview_session/proposals.rb +1 -1
  603. data/lib/hecks/{cli → tools}/interview_session.rb +2 -2
  604. data/lib/hecks/{cli → tools}/interview_shape/lifecycle.rb +1 -1
  605. data/lib/hecks/{cli → tools}/interview_shape.rb +1 -1
  606. data/lib/hecks/{cli → tools}/ir.rb +1 -1
  607. data/lib/hecks/tools/lanes/files.rb +1 -1
  608. data/lib/hecks/tools/lanes/watch_workflow.rb +6 -8
  609. data/lib/hecks/tools/lanes/workflow.rb +1 -1
  610. data/lib/hecks/tools/lanes.rb +1 -1
  611. data/lib/hecks/{cli → tools}/mcp.rb +1 -1
  612. data/lib/hecks/{cli → tools}/model_check/examination.rb +1 -1
  613. data/lib/hecks/{cli → tools}/model_check/sources.rb +1 -1
  614. data/lib/hecks/{cli → tools}/model_check.rb +1 -1
  615. data/lib/hecks/tools/move_disables/reasons.rb +35 -0
  616. data/lib/hecks/tools/move_disables.rb +211 -0
  617. data/lib/hecks/tools/move_former_name.rb +105 -0
  618. data/lib/hecks/{cli → tools}/pattern_cases.rb +1 -1
  619. data/lib/hecks/{cli → tools}/persistence_notice.rb +1 -1
  620. data/lib/hecks/{cli → tools}/present.rb +1 -1
  621. data/lib/hecks/{cli → tools}/project_cli/launcher_source.rb +32 -22
  622. data/lib/hecks/{cli → tools}/project_cli/templates.rb +22 -15
  623. data/lib/hecks/{cli → tools}/project_cli.rb +11 -7
  624. data/lib/hecks/{cli → tools}/project_diagrams.rb +3 -3
  625. data/lib/hecks/{cli → tools}/project_glossary.rb +2 -2
  626. data/lib/hecks/{cli → tools}/reference.rb +2 -2
  627. data/lib/hecks/{cli → tools}/refresh_projections.rb +1 -1
  628. data/lib/hecks/{cli → tools}/refresh_rspec_runtime_baseline.rb +1 -1
  629. data/lib/hecks/tools/regeneration_run/pool.rb +148 -0
  630. data/lib/hecks/tools/regeneration_run/scratch.rb +76 -0
  631. data/lib/hecks/tools/regeneration_run.rb +125 -103
  632. data/lib/hecks/{cli → tools}/release.rb +1 -1
  633. data/lib/hecks/{cli → tools}/release_gem.rb +1 -1
  634. data/lib/hecks/{cli → tools}/rspec_io_parallel_files.rb +1 -1
  635. data/lib/hecks/{cli → tools}/rspec_shard_files.rb +1 -1
  636. data/lib/hecks/{cli → tools}/run.rb +9 -2
  637. data/lib/hecks/{cli → tools}/run_expectations.rb +1 -1
  638. data/lib/hecks/{cli → tools}/seed_semantics_corpus.rb +4 -4
  639. data/lib/hecks/{cli → tools}/serve_query_ir_mcp.rb +1 -1
  640. data/lib/hecks/{cli → tools}/shape.rb +3 -2
  641. data/lib/hecks/{cli → tools}/smoke_http/checks.rb +1 -1
  642. data/lib/hecks/{cli → tools}/smoke_http/settings.rb +1 -1
  643. data/lib/hecks/{cli → tools}/smoke_http/transport.rb +1 -1
  644. data/lib/hecks/{cli → tools}/smoke_http.rb +1 -1
  645. data/lib/hecks/{cli → tools}/smoke_test.rb +1 -1
  646. data/lib/hecks/{cli → tools}/spec_example.rb +1 -1
  647. data/lib/hecks/tools/stage_progress.rb +54 -0
  648. data/lib/hecks/{cli → tools}/statements.rb +1 -1
  649. data/lib/hecks/{cli → tools}/stores.rb +1 -1
  650. data/lib/hecks/{cli → tools}/stress_concurrency_specs/reporting.rb +1 -1
  651. data/lib/hecks/{cli → tools}/stress_concurrency_specs/usage.rb +4 -4
  652. data/lib/hecks/{cli → tools}/stress_concurrency_specs.rb +3 -3
  653. data/lib/hecks/tools/tenant_provisioning.rb +23 -0
  654. data/lib/hecks/tools/tools_doc.rb +10 -8
  655. data/lib/hecks/tools/translation_audit/edges.rb +21 -16
  656. data/lib/hecks/tools/translation_audit/verdicts.rb +1 -1
  657. data/lib/hecks/tools/translation_audit.rb +1 -1
  658. data/lib/hecks/tools/translation_scaffold.rb +1 -1
  659. data/lib/hecks/tools.rb +4 -1
  660. data/lib/hecks/version.rb +1 -1
  661. data/lib/hecks/vocabulary.rb +61 -31
  662. data/lib/hecks/workflow_pagination/judge.rb +48 -0
  663. data/lib/hecks/workflow_pagination/statement.rb +50 -0
  664. data/lib/hecks/workflow_pagination.rb +58 -0
  665. data/lib/hecks.rb +2 -0
  666. data/lib/rubocop/cop/hecks/bare_event_and_query_names.rb +58 -0
  667. data/lib/rubocop/cop/hecks/correlates_by_aggregate.rb +39 -0
  668. data/lib/rubocop/cop/hecks/creating_command_sets.rb +75 -0
  669. data/lib/rubocop/cop/hecks/invariant_expression_language.rb +47 -0
  670. data/lib/rubocop/cop/hecks/lifecycle_needs_attribute.rb +58 -0
  671. data/lib/rubocop/cop/hecks/no_id_string_attribute.rb +49 -0
  672. data/lib/rubocop/cop/hecks/one_terminal_mark_per_lifecycle.rb +47 -0
  673. data/lib/rubocop/cop/hecks/value_object_shorthand.rb +56 -0
  674. data/prebuilt/13c04bdc5970295b310f0a980d397ba722232c8da99cc1a0cdb383249921e871.marshal +0 -0
  675. data/prebuilt/e3857b77f47d9ecc1787b2e1f5136364f3cb3444ef4aea5479f3b98167affea2.marshal +0 -0
  676. data/qa/settings.yml +23 -0
  677. data/rust/.gitignore +0 -6
  678. data/rust/Cargo.toml +2 -1
  679. data/rust/build/Cargo.toml +1 -1
  680. data/rust/build/src/binding_pass.rs +57 -4
  681. data/rust/build/src/pipeline.rs +6 -4
  682. data/rust/build/src/resolve.rs +1 -0
  683. data/rust/codegen/Cargo.toml +1 -1
  684. data/rust/codegen/src/domain_generator.rs +3 -0
  685. data/rust/codegen/src/json_codec.rs +182 -23
  686. data/rust/codegen/src/one_of_references.rs +1 -0
  687. data/rust/codegen/src/queries.rs +20 -2
  688. data/rust/codegen/src/registry.rs +21 -5
  689. data/rust/expr/Cargo.toml +1 -1
  690. data/rust/host/Cargo.toml +1 -1
  691. data/rust/host/HECKS_RELEASE +1 -1
  692. data/rust/host/core/Cargo.toml +1 -1
  693. data/rust/host/src/bin/mint_harness.rs +1 -0
  694. data/rust/host/src/boot.rs +1 -0
  695. data/rust/host/src/store/mint.rs +40 -6
  696. data/rust/lsp/Cargo.toml +1 -1
  697. data/rust/parser/Cargo.toml +1 -1
  698. data/rust/parser/src/build/correlation_keys.rs +125 -0
  699. data/rust/parser/src/build/mod.rs +1 -0
  700. data/rust/parser/src/emit.rs +55 -3
  701. data/rust/parser/src/ir.rs +33 -1
  702. data/rust/parser/src/keywords.rs +289 -1
  703. data/rust/parser/src/main.rs +2 -0
  704. data/rust/parser/src/parse/aggregate.rs +10 -1
  705. data/rust/parser/src/parse/audience.rs +65 -0
  706. data/rust/parser/src/parse/command.rs +14 -0
  707. data/rust/parser/src/parse/declaration.rs +97 -0
  708. data/rust/parser/src/parse/domain_port.rs +6 -0
  709. data/rust/parser/src/parse/mod.rs +1 -0
  710. data/rust/parser/src/parse/process_manager.rs +17 -10
  711. data/rust/parser/src/parse/world.rs +12 -8
  712. data/rust/parser/src/render_ruby.rs +9 -0
  713. data/rust/parser/src/sentence/format.rs +3 -0
  714. data/rust/parser/src/sentence/process.rs +6 -2
  715. data/rust/src/exemplar/json.rs +17 -0
  716. data/rust/src/kernel/cli.rs +27 -3
  717. data/rust/src/kernel/dispatch.rs +4 -2
  718. data/rust/src/kernel/json.rs +62 -0
  719. data/rust/src/kernel/orchestrate.rs +28 -3
  720. data/rust/src/kernel/orchestrate_guard_tests.rs +12 -12
  721. data/rust/web/Cargo.toml +1 -1
  722. metadata +389 -96
  723. data/lib/hecks/cli/command_table.rb +0 -72
  724. data/lib/hecks/cli/launcher_words.rb +0 -168
  725. data/lib/hecks/cli.rb +0 -133
  726. data/lib/hecks/hecks/agents.bluebook +0 -246
  727. data/lib/hecks/hecks/codebase.bluebook +0 -3209
  728. data/lib/hecks/hecks/custodian.bluebook +0 -1300
  729. data/lib/hecks/hecks/ledger.bluebook +0 -190
  730. data/lib/hecks/hecks/rehearsal.bluebook +0 -437
  731. data/lib/hecks/hecks/tooling.bluebook +0 -1234
  732. data/lib/hecks/hecks/translations/2-148748.bluebook +0 -2
  733. data/lib/hecks/hecks/translations/2-15a2e4.bluebook +0 -2
  734. data/lib/hecks/hecks/translations/2-1b447c.bluebook +0 -5
  735. data/lib/hecks/hecks/translations/2-240474.bluebook +0 -2
  736. data/lib/hecks/hecks/translations/2-44446d.bluebook +0 -2
  737. data/lib/hecks/hecks/translations/2-46ca26.bluebook +0 -2
  738. data/lib/hecks/hecks/translations/2-473eab.bluebook +0 -2
  739. data/lib/hecks/hecks/translations/2-75cc28.bluebook +0 -2
  740. data/lib/hecks/hecks/translations/2-781c07.bluebook +0 -2
  741. data/lib/hecks/hecks/translations/2-84c18b.bluebook +0 -2
  742. data/lib/hecks/hecks/translations/2-aa5fff.bluebook +0 -2
  743. data/lib/hecks/hecks/translations/2-bfdb48.bluebook +0 -2
  744. data/lib/hecks/hecks/translations/2-cd96d8.bluebook +0 -10
  745. data/lib/hecks/hecks/translations/2-d43f71.bluebook +0 -2
  746. data/lib/hecks/hecks/translations/2-eea577.bluebook +0 -2
  747. data/lib/hecks/hecks/translations/2-f53248.bluebook +0 -2
  748. data/lib/hecks/hecks/translations/2-fbb677.bluebook +0 -2
  749. data/lib/hecks/hecks/translations/2-ffdf12.bluebook +0 -6
  750. data/lib/hecks/quality_control/adapters/clearance_recorder.rb +0 -36
  751. data/lib/hecks/quality_control/adapters/github_ci_webhook.rb +0 -56
  752. data/prebuilt/31cf81e12f16236ad26249be590cfea3799c9db67432e6a4ef54b801c355b6ae.marshal +0 -0
  753. data/prebuilt/68ecb2bd3abb1a221007db4f2b0375834c13aaccac8f2b99778e8517f11aa113.marshal +0 -0
  754. data/prebuilt/verdicts-0328c2403383837d780ad5ed81d1da351c56436b894cf9015b5d56b8f1f1bf10.json +0 -1
@@ -4,7 +4,7 @@ require_relative "../../hecks"
4
4
  require_relative "../doc/reference"
5
5
 
6
6
  module Hecks
7
- module CLI
7
+ module Tools
8
8
  # The command behind `hecks measure_doc_coverage`: refuses unless every live word in
9
9
  # `docs/implemented/reference/` has prose and a running example. It checks that an example
10
10
  # exists; `spec/reference_doctest_spec.rb` checks that it passes. `ruby skip` fences and
@@ -24,7 +24,7 @@ module Hecks
24
24
  return 0 if clean
25
25
 
26
26
  err.puts "docs/implemented/reference/ is behind the language. " \
27
- "Regenerate with hecks language_run.project_reference to see the words in place."
27
+ "Regenerate with hecks language_run.project_reference! to see the words in place."
28
28
  1
29
29
  end
30
30
  end
@@ -1,7 +1,7 @@
1
1
  require_relative "../../hecks"
2
2
 
3
3
  module Hecks
4
- module CLI
4
+ module Tools
5
5
  # The command behind `hecks docs` and `hecks narrate`:
6
6
  # one domain's document, projected from its own bluebook, to stdout.
7
7
  #
@@ -1,7 +1,7 @@
1
1
  require_relative "../naming"
2
2
 
3
3
  module Hecks
4
- module CLI
4
+ module Tools
5
5
  # The files `hecks init` writes: a stub domain that boots as it stands and that the author
6
6
  # then edits into a real one (ADR 0087).
7
7
  #
@@ -1,7 +1,7 @@
1
1
  require "fileutils"
2
2
 
3
3
  module Hecks
4
- module CLI
4
+ module Tools
5
5
  # Writes the files `hecks init` and `hecks interview` produce, never replacing one (ADR 0087).
6
6
  #
7
7
  # Every target is checked before the first is written, so a refusal leaves nothing half-written.
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+
5
+ module Hecks
6
+ module Tools
7
+ # The model check a domain repository runs in its CI, generated from Hecks' own definition of it:
8
+ # the client profile, over the domain's bluebooks, from the hecks the Gemfile pins.
9
+ #
10
+ # A downstream repository commits the file this writes (`.github/workflows/model-check.yml`) and
11
+ # never edits it; a change to the check is made here, and every repository regenerates from it.
12
+ #
13
+ # ruby lib/hecks/tools/downstream_model_check.rb <checkout> <domain-directory> [--check]
14
+ #
15
+ # With `--check` nothing is written: the tool answers 1 when the committed file differs.
16
+ module DownstreamModelCheck
17
+ # Where the workflow lives under the checkout.
18
+ FILE = ".github/workflows/model-check.yml"
19
+
20
+ # The hecks profile the check runs under (Hecks' client profile, which refuses what the
21
+ # client cannot answer for).
22
+ PROFILE = "client"
23
+
24
+ # The bluebook directory the check reads, relative to the domain directory.
25
+ DOMAINS = "bluebook"
26
+
27
+ # The Ruby the check runs on, matching the domain repositories' CI.
28
+ RUBY_VERSION = "3.3"
29
+
30
+ BANNER = "# Generated by hecks lib/hecks/tools/downstream_model_check.rb from Hecks' client-profile model " \
31
+ "check. Do not edit; change the generator in hecks."
32
+
33
+ module_function
34
+
35
+ # @param domain_directory [String] the directory the domain's Gemfile and bluebooks live in,
36
+ # relative to the checkout (`.` when they are at the root)
37
+ # @return [String] the workflow's text, ending in a newline
38
+ def workflow(domain_directory:)
39
+ <<~YAML
40
+ #{BANNER}
41
+
42
+ name: Model check
43
+
44
+ on:
45
+ pull_request:
46
+ push:
47
+ branches: [main]
48
+ workflow_dispatch: {}
49
+
50
+ permissions:
51
+ contents: read
52
+
53
+ jobs:
54
+ model-check:
55
+ runs-on: ubuntu-24.04
56
+ timeout-minutes: 15
57
+ steps:
58
+ - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
59
+
60
+ - uses: ruby/setup-ruby@14594264cd68ce8a2345dd349bc3d138a4ef85c8 # v1
61
+ with:
62
+ ruby-version: "#{RUBY_VERSION}"
63
+ bundler-cache: false
64
+
65
+ # The generator ships inside the pinned hecks gem, so the file is checked against the
66
+ # same generator that a bump brings in: a hand edit, or a stale file after a bump, fails here.
67
+ - name: Generated workflow is current
68
+ working-directory: #{domain_directory}
69
+ run: |
70
+ bundle install --quiet --jobs 4
71
+ ruby "$(bundle info hecks --path)/lib/hecks/tools/downstream_model_check.rb" "$GITHUB_WORKSPACE" #{domain_directory} --check
72
+
73
+ - name: Model check (hecks #{PROFILE} profile)
74
+ working-directory: #{domain_directory}
75
+ env:
76
+ HECKS_ENVIRONMENT: memory
77
+ run: |
78
+ bundle install --quiet --jobs 4
79
+ bundle exec hecks model_check_run.model_check! profile=#{PROFILE} domains=#{DOMAINS} --wait
80
+ YAML
81
+ end
82
+
83
+ # Writes the workflow under `root`, or with `check` compares it and reports whether it is current.
84
+ #
85
+ # @param root [String] the checkout
86
+ # @param domain_directory [String] see {workflow}
87
+ # @param check [Boolean] compare without writing
88
+ # @return [Integer] 0 when written or current, 1 when `check` finds the file out of date
89
+ def main(root:, domain_directory:, check: false)
90
+ path = File.join(root, FILE)
91
+ text = workflow(domain_directory: domain_directory)
92
+ current = File.file?(path) && File.read(path) == text
93
+ return check_report(current, path) if check
94
+ return puts("#{FILE}: already current") && 0 if current
95
+
96
+ FileUtils.mkdir_p(File.dirname(path))
97
+ File.write(path, text)
98
+ puts "wrote #{FILE}"
99
+ 0
100
+ end
101
+
102
+ # @return [Integer] 0 when the file is current, else 1 with the reason on stderr
103
+ def check_report(current, path)
104
+ if current
105
+ puts "#{FILE}: current"
106
+ return 0
107
+ end
108
+
109
+ warn "#{FILE}: out of date at #{path} (regenerate with downstream_model_check.rb)"
110
+ 1
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ if $PROGRAM_NAME == __FILE__
117
+ root, domain_directory, flag = ARGV
118
+ abort "usage: downstream_model_check.rb <checkout> <domain-directory> [--check]" unless root && domain_directory
119
+
120
+ exit Hecks::Tools::DownstreamModelCheck.main(root: root, domain_directory: domain_directory,
121
+ check: flag == "--check")
122
+ end
@@ -103,7 +103,7 @@ module Hecks
103
103
 
104
104
  # @return [Boolean] true if the block ran and the gates held; false after restoring the
105
105
  # snapshots when a gate refused
106
- def edit_and_gate(word, context, evolve, root, &) # rubocop:disable Naming/PredicateMethod -- the edit's verdict
106
+ def edit_and_gate(word, context, evolve, root, &)
107
107
  paths = evolve.syntax_paths + [File.join(root, "spec/golden/ir/Bluebook.json")]
108
108
  snapshots = paths.to_h { |path| [path, File.read(path)] }
109
109
  evolve.restore_on_raise(paths, &)
@@ -124,7 +124,7 @@ module Hecks
124
124
  end
125
125
 
126
126
  # @return [true]
127
- def status(evolve) # rubocop:disable Naming/PredicateMethod -- the command always succeeds
127
+ def status(evolve)
128
128
  rows = evolve.keyword_rows
129
129
  moving = rows.reject { |row| row[:status] == "admitted" }
130
130
  renamed = rows.reject { |row| row[:was].to_s.empty? }
@@ -3,7 +3,7 @@ require "net/http"
3
3
  require "uri"
4
4
 
5
5
  module Hecks
6
- module CLI
6
+ module Tools
7
7
  # Compares a running host's reported era (`GET /version`) against an
8
8
  # allow-list file of eras that should be running, for use after a roll. It is the HTTP side of
9
9
  # `hecks check_era`, which the `HostHttp` adapter runs; the runtime never calls it.
@@ -3,7 +3,7 @@
3
3
  require_relative "../projection_files"
4
4
 
5
5
  module Hecks
6
- module CLI
6
+ module Tools
7
7
  # The command behind `hecks project_expression_tables`: projects the expression machinery's
8
8
  # operator and normalisation tables from the grammar bluebook into
9
9
  # `lib/hecks/bluebook/expression/projection.json`.
@@ -5,7 +5,7 @@ require "optparse"
5
5
  require_relative "../../hecks"
6
6
 
7
7
  module Hecks
8
- module CLI
8
+ module Tools
9
9
  # The command behind `hecks follow`: live-tails a domain's persisted event log as JSON lines.
10
10
  #
11
11
  # Events are one table per domain, so this polls the first repository that answers `:events`
@@ -9,21 +9,24 @@ module Hecks
9
9
  # The command line of a sweep: its options, and what the chosen adapter needs.
10
10
  module Options
11
11
  # The options that take a whole number, and the key each fills.
12
- INTEGER_OPTIONS = { "--seeds" => :seeds, "--steps" => :steps, "--workers" => :workers }.freeze
12
+ INTEGER_OPTIONS = { "--seeds" => :seeds, "--steps" => :steps, "--workers" => :workers,
13
+ "--list-shards" => :list_shards }.freeze
13
14
 
14
15
  # The most children a Postgres sweep runs at once unless `--workers` says otherwise.
15
16
  POSTGRES_WORKERS = 4
16
17
 
17
18
  # @param args [Array<String>] the command line, consumed
18
- # @return [Hash, nil] `seeds`, `steps`, `workers`, `adapter`, `persist` and `domain`; nil
19
- # after a refusal
19
+ # @return [Hash, nil] `seeds`, `steps`, `workers`, `adapter`, `persist`, `domain`, `shard`
20
+ # (the shard's number and the shard count) and `list_shards` (how many shards to list);
21
+ # nil after a refusal
20
22
  def parse(args)
21
- options = { seeds: 20, steps: 30, workers: nil, adapter: :memory, domain: nil, persist: false }
23
+ options = { seeds: 20, steps: 30, workers: nil, adapter: :memory, domain: nil, persist: false,
24
+ shard: nil, list_shards: nil }
22
25
  until args.empty?
23
26
  apply_option(options, args.shift, args)
24
27
  return nil unless adapter_known?(options[:adapter])
25
28
  end
26
- options
29
+ settle_shards(options)
27
30
  end
28
31
 
29
32
  # Records one command-line word in `options`, taking its value from `rest` when it has one.
@@ -31,10 +34,42 @@ module Hecks
31
34
  if (key = INTEGER_OPTIONS[arg]) then options[key] = Integer(rest.shift)
32
35
  elsif arg == "--persist-regressions" then options[:persist] = true
33
36
  elsif arg == "--adapter" then options[:adapter] = adapter_name(rest.shift)
37
+ elsif arg == "--shard" then options[:shard] = rest.shift.to_s
34
38
  else options[:domain] = arg
35
39
  end
36
40
  end
37
41
 
42
+ # Turns `--shard I/N` into the pair it names, and refuses the shard options that do not fit
43
+ # together or with what they split.
44
+ #
45
+ # @param options [Hash] the options as typed
46
+ # @return [Hash, nil] the options with `shard` parsed, or nil after a refusal
47
+ def settle_shards(options)
48
+ return options if options.values_at(:shard, :list_shards).none?
49
+ return nil unless shard_choice_clear?(options)
50
+
51
+ options[:shard] &&= Shards.parse_shard(options[:shard])
52
+ options.values_at(:shard, :list_shards).any? ? options : nil
53
+ end
54
+
55
+ # @return [Boolean] whether the shard options name one split of the corpus; warns why not
56
+ def shard_choice_clear?(options)
57
+ reason = shard_conflict(options)
58
+ warn reason if reason
59
+ reason.nil?
60
+ end
61
+
62
+ # @return [String, nil] why the shard options cannot be used together, or nil when they can
63
+ def shard_conflict(options)
64
+ if options[:shard] && options[:list_shards]
65
+ "--shard sweeps one shard and --list-shards lists them all; use one"
66
+ elsif options[:domain]
67
+ "shards split every domain of the checkout, so no domain can be named"
68
+ elsif options[:list_shards] && !options[:list_shards].positive?
69
+ "--list-shards takes a shard count of 1 or more"
70
+ end
71
+ end
72
+
38
73
  # @param word [String, nil] the word after `--adapter`
39
74
  # @return [Symbol] the adapter it names, lowercased
40
75
  def adapter_name(word) = word.to_s.downcase.to_sym
@@ -16,16 +16,27 @@ module Hecks
16
16
  # @yieldparam domain [String] a domain directory
17
17
  # @yieldreturn [Boolean] whether it was clean
18
18
  # @return [Boolean] true if every domain's child exited 0, false if any exited non-zero
19
- def fuzz_in_pool(domains, workers, &) # rubocop:disable Naming/PredicateMethod -- the sweep's verdict
19
+ def fuzz_in_pool(domains, workers, &)
20
20
  results = run_children(domains, workers, &)
21
21
  slowest = results.max_by(5) { |_, (_, _, seconds)| seconds }
22
22
  .map { |index, (_, _, seconds)| "#{File.basename(domains[index])} #{seconds.round}s" }
23
23
  warn "slowest domains: #{slowest.join(", ")}"
24
+ warn "domain seconds: #{seconds_by_domain(domains, results)}"
24
25
  replay_outputs(results).all?
25
26
  end
26
27
 
27
28
  private
28
29
 
30
+ # Every domain's wall seconds, so a cost table can be read off one run's log.
31
+ #
32
+ # @return [String] `directory seconds` for each domain, in domain order, the directory
33
+ # relative to the working directory when it lies under it
34
+ def seconds_by_domain(domains, results)
35
+ results.sort.map do |index, (_, _, seconds)|
36
+ "#{domains[index].delete_prefix("#{Dir.pwd}/")} #{seconds.round(1)}"
37
+ end.join(", ")
38
+ end
39
+
29
40
  # @return [Hash{Integer => Array}] by domain index: whether the child succeeded, its output
30
41
  # file, and its seconds
31
42
  def run_children(domains, workers, &)
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../tools"
4
+
5
+ module Hecks
6
+ module Tools
7
+ module FuzzSweep
8
+ # Splits the domains of a sweep into `n` shards, so `n` jobs together sweep every domain once.
9
+ #
10
+ # The split is a pure function of the domain list, `n` and the committed cost table: no clock,
11
+ # no discovery order and no run history reach it, so every job computes the same partition.
12
+ # Domains are dealt, dearest first, to whichever shard holds the least cost so far, which
13
+ # keeps the shards near one another in cost; a domain the table does not name costs the
14
+ # table's median, and an absent table costs every domain the same.
15
+ module Shards
16
+ # What `--shard` takes: the shard's number, a slash, and how many shards there are.
17
+ SHARD_WORD = %r{\A([1-9]\d*)/([1-9]\d*)\z}
18
+
19
+ # Where the committed cost table is read from, relative to the checkout: one
20
+ # `directory:seconds` line for each domain, `#` lines being notes.
21
+ COST_TABLE = "spec/fuzzing/fuzz_runtime_baseline.log"
22
+
23
+ # What a domain costs when no table says: every domain alike.
24
+ FLAT_COST = 1.0
25
+
26
+ # A split that left a domain out or put one in two shards.
27
+ class NotAPartition < StandardError; end
28
+
29
+ module_function
30
+
31
+ # @param word [String, nil] the word after `--shard`
32
+ # @return [Array(Integer, Integer), nil] the shard's number and the shard count, or nil when
33
+ # the word is not `I/N` with `I` between 1 and `N`; warns what is accepted
34
+ def parse_shard(word)
35
+ match = SHARD_WORD.match(word.to_s)
36
+ return [match[1].to_i, match[2].to_i] if match && match[1].to_i <= match[2].to_i
37
+
38
+ warn "unknown --shard #{word.inspect} — use I/N with I from 1 to N, such as 1/2, 2/2 or 3/4"
39
+ nil
40
+ end
41
+
42
+ # @param domains [Array<String>] the domain directories, each unique
43
+ # @param shards [Integer] how many shards to split into
44
+ # @param costs [Hash{String => Float}] each domain directory's cost, perhaps lacking some
45
+ # @return [Array<Array<String>>] the domains of each shard, each in the order `domains` has
46
+ def partition(domains, shards, costs = {})
47
+ price = prices(domains, costs)
48
+ order = domains.each_index.sort_by { |index| [-price[index], domains[index]] }
49
+ deal(order, price, shards).map { |indexes| indexes.sort.map { |index| domains[index] } }
50
+ end
51
+
52
+ # @param order [Array<Integer>] the domain positions, in the order to deal them
53
+ # @param price [Array<Float>] each position's cost
54
+ # @param shards [Integer] how many shards to deal into
55
+ # @return [Array<Array<Integer>>] the positions each shard took, each to the lightest shard
56
+ # so far, the lowest numbered when they tie
57
+ def deal(order, price, shards)
58
+ loads = Array.new(shards, 0.0)
59
+ order.each_with_object(Array.new(shards) { [] }) do |index, taken|
60
+ lightest = loads.each_index.min_by { |shard| [loads[shard], shard] }
61
+ loads[lightest] += price[index]
62
+ taken[lightest] << index
63
+ end
64
+ end
65
+
66
+ # @param domains [Array<String>] the domain directories
67
+ # @param number [Integer] the shard wanted, from 1
68
+ # @param shards [Integer] how many shards there are
69
+ # @param costs [Hash{String => Float}] each domain directory's cost
70
+ # @return [Array<String>] the domains that shard sweeps
71
+ def select(domains, number, shards, costs = {}) = partition(domains, shards, costs).fetch(number - 1)
72
+
73
+ # The assignment as data: one line for each domain, `I/N<TAB>cost<TAB>directory`, shard by
74
+ # shard. A split that left a domain out or put one in two shards is refused, since a job
75
+ # that reads this list would then trust a sweep that covers less than it says.
76
+ #
77
+ # @param domains [Array<String>] the domain directories
78
+ # @param shards [Integer] how many shards to split into
79
+ # @param costs [Hash{String => Float}] each domain directory's cost
80
+ # @param root [String] the checkout the directories are shown relative to
81
+ # @return [Array<String>] the lines
82
+ # @raise [NotAPartition] when the shards are not a partition of `domains`
83
+ def listing(domains, shards, costs, root)
84
+ split = partition(domains, shards, costs)
85
+ raise NotAPartition, "the shards are not a partition of the domains" unless split.flatten.sort == domains.sort
86
+
87
+ price = domains.zip(prices(domains, costs)).to_h
88
+ split.each_with_index.flat_map do |members, index|
89
+ members.map { |domain| row("#{index + 1}/#{shards}", price[domain], domain.delete_prefix("#{root}/")) }
90
+ end
91
+ end
92
+
93
+ def row(shard, price, directory) = "#{shard}\t#{price.round(1)}\t#{directory}"
94
+
95
+ # Every discoverable domain, or the share of them `--shard I/N` asks for, which a pool
96
+ # starts dearest first so the longest domain is never the last one left running.
97
+ #
98
+ # @param options [Hash] the parsed options: `shard`, perhaps
99
+ # @param root [String] the checkout
100
+ # @return [Array<String>] the domain directories this sweep covers
101
+ def domains_for(options, root)
102
+ domains = Hecks::Corpus.fuzzable_domains(root)
103
+ return domains unless options[:shard]
104
+
105
+ number, shards = options[:shard]
106
+ puts "shard #{number}/#{shards}"
107
+ costs = costs_from(root)
108
+ dearest_first(select(domains, number, shards, costs), costs)
109
+ end
110
+
111
+ # @param domains [Array<String>] the domain directories
112
+ # @param costs [Hash{String => Float}] each domain directory's cost
113
+ # @return [Array<String>] the same domains, dearest first, ties in path order
114
+ def dearest_first(domains, costs)
115
+ price = domains.zip(prices(domains, costs)).to_h
116
+ domains.sort_by { |domain| [-price[domain], domain] }
117
+ end
118
+
119
+ # Prints the listing of every domain of the checkout, and sweeps nothing.
120
+ #
121
+ # @param shards [Integer] how many shards to split into
122
+ # @param root [String] the checkout
123
+ # @return [Integer] 0 once printed
124
+ def print_listing(shards, root)
125
+ puts listing(Hecks::Corpus.fuzzable_domains(root), shards, costs_from(root), root)
126
+ 0
127
+ end
128
+
129
+ # @param root [String] the checkout
130
+ # @return [Hash{String => Float}] seconds by absolute domain directory, from the committed
131
+ # table; empty when the table is absent
132
+ def costs_from(root)
133
+ path = File.join(root, COST_TABLE)
134
+ return {} unless File.file?(path)
135
+
136
+ File.readlines(path, chomp: true).reject { |line| line.strip.empty? || line.start_with?("#") }
137
+ .to_h { |line| line.split(":", 2).then { |dir, seconds| [File.join(root, dir), Float(seconds)] } }
138
+ end
139
+
140
+ # @return [Array<Float>] each domain's cost, a missing one at the table's median
141
+ def prices(domains, costs)
142
+ fallback = median(costs.values) || FLAT_COST
143
+ domains.map { |domain| costs.fetch(domain, fallback) }
144
+ end
145
+
146
+ def median(values)
147
+ return nil if values.empty?
148
+
149
+ ranked = values.sort
150
+ (ranked[(ranked.size - 1) / 2] + ranked[ranked.size / 2]) / 2.0
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
@@ -8,6 +8,7 @@ require_relative "fuzz_sweep/options"
8
8
  require_relative "fuzz_sweep/findings"
9
9
  require_relative "fuzz_sweep/reporting"
10
10
  require_relative "fuzz_sweep/pool"
11
+ require_relative "fuzz_sweep/shards"
11
12
 
12
13
  module Hecks
13
14
  module Tools
@@ -15,11 +16,13 @@ module Hecks
15
16
  # properties, then shrinks each distinct finding and saves it under `tmp/fuzz-failures/`.
16
17
  #
17
18
  # hecks fuzz [domain] [--seeds N] [--steps N] [--workers N] [--adapter memory|sqlite|postgres]
18
- # [--persist-regressions]
19
+ # [--persist-regressions] [--shard I/N | --list-shards N]
19
20
  #
20
21
  # The sweep is single-runtime on purpose; the cross-runtime differential is
21
22
  # `spec/rust_conformance_fuzz_spec.rb`. Without a domain it sweeps every domain
22
- # `Hecks::Corpus.fuzzable_domains` finds, one forked child per core.
23
+ # `Hecks::Corpus.fuzzable_domains` finds, one forked child per core. `--shard I/N` sweeps only
24
+ # shard I of N (see `Shards`), so N jobs together sweep every domain once; `--list-shards N`
25
+ # prints which shard each domain falls in and sweeps nothing.
23
26
  module FuzzSweep
24
27
  # How many seeds are drawn for each seed that must execute.
25
28
  DRAW_LIMIT = 5
@@ -57,13 +60,16 @@ module Hecks
57
60
  # argument is refused
58
61
  def main(argv, root: Tools::ROOT)
59
62
  require "hecks"
60
- require "hecks/fuzzing"
61
63
  options = parse(argv.dup)
62
- return 1 unless options && adapter_ready?(options)
64
+ return 1 unless options
65
+ return Shards.print_listing(options[:list_shards], root) if options[:list_shards]
66
+
67
+ require "hecks/fuzzing"
68
+ return 1 unless adapter_ready?(options)
63
69
 
64
70
  # Every discoverable domain, so one added anywhere is swept without a list. Paths the sweep
65
71
  # cannot boot are routed by Hecks::Corpus::ROUTES to the check that owns them.
66
- domains = options[:domain] ? [options[:domain]] : Hecks::Corpus.fuzzable_domains(root)
72
+ domains = options[:domain] ? [options[:domain]] : Shards.domains_for(options, root)
67
73
  ok = sweep(domains, worker_count(options), options, root)
68
74
 
69
75
  puts(ok ? "CLEAN — no generated sequence broke a property or the interpreter." : "FUZZ FOUND SOMETHING.")
@@ -3,6 +3,9 @@
3
3
  require "open3"
4
4
  require "hecks/vocabulary"
5
5
  require_relative "../tools"
6
+ require_relative "stage_progress"
7
+ require_relative "gate_memo/session"
8
+ require_relative "gate_report"
6
9
 
7
10
  module Hecks
8
11
  module Tools
@@ -14,10 +17,17 @@ module Hecks
14
17
  # hecks gate --list # the stages and their checks
15
18
  #
16
19
  # A green stage prints which checks passed; a red one prints each failing check's whole output
17
- # and what its failure means, and answers status 1.
20
+ # and what its failure means, and answers status 1. A check whose inputs are unchanged since its
21
+ # last green run is not run: it is printed as skipped, and the verdict counts it as skipped, not
22
+ # passed (see GateMemo).
18
23
  module Gate
19
24
  USAGE = "usage: hecks gate <stage> [only=a,b] | hecks gate --list"
20
25
 
26
+ # RuboCop keys its cache by file content, so one directory shared by every worktree lets each
27
+ # reuse the others' results. It is set here, in the gate's child environment, and not in
28
+ # .rubocop.yml, so CI and a bare `rubocop` keep the defaults (ADR 0091).
29
+ RUBOCOP_CACHE_ROOT = File.join(Dir.home, ".cache", "hecks", "rubocop")
30
+
21
31
  module_function
22
32
 
23
33
  # @return [Hash{String => Hash}] each stage by name: its `env` and its `checks`, in order,
@@ -45,8 +55,9 @@ module Hecks
45
55
 
46
56
  # @param argv [Array<String>] a stage name and optionally `only=a,b`, or `--list`
47
57
  # @param root [String] the checkout the checks run in
58
+ # @param progress [StageProgress] where each check's start and end lines go
48
59
  # @return [Integer] 0 when every check passed, 1 when one failed, 2 on bad usage
49
- def main(argv, root: Tools::ROOT, **)
60
+ def main(argv, root: Tools::ROOT, progress: StageProgress.new, **)
50
61
  stages = self.stages
51
62
  return list(stages) if argv == ["--list"]
52
63
 
@@ -57,7 +68,17 @@ module Hecks
57
68
  checks = select(stage.fetch("checks"), only)
58
69
  return unknown(stage, only) if checks.nil?
59
70
 
60
- report(name, checks, run_all(stage, checks, root))
71
+ run_planned(name, stage, checks, root, progress)
72
+ end
73
+
74
+ # Runs the checks the memo does not skip, records the green ones, and reports the stage.
75
+ #
76
+ # @return [Integer] the verdict of `report`
77
+ def run_planned(name, stage, checks, root, progress)
78
+ session = GateMemo::Session.new(root: root, env: stage.fetch("env", {}))
79
+ plan = session.plan(checks)
80
+ results = run_all(stage, plan.run, root, progress)
81
+ GateReport.report(name, plan, results, session.record(results))
61
82
  end
62
83
 
63
84
  # @param argv [Array<String>] the command line words
@@ -83,18 +104,30 @@ module Hecks
83
104
  # @param stage [Hash] the stage: its `env` and `checks`
84
105
  # @param checks [Array<Hash>] the checks to run
85
106
  # @param root [String] the directory they run in
107
+ # @param progress [StageProgress] told when each check starts and ends
86
108
  # @return [Hash{String => Array}] each check's id, and its output and whether it passed
87
- def run_all(stage, checks, root)
109
+ def run_all(stage, checks, root, progress)
88
110
  env = stage.fetch("env", {}).reject { |key, _| ENV.key?(key) }
89
111
  threads = checks.map do |check|
90
- Thread.new { [check["id"], run_one(check, env, root)] }
112
+ progress.start(check["id"])
113
+ Thread.new { [check["id"], run_timed(check, env, root, progress)] }
91
114
  end
92
115
  threads.to_h(&:value)
93
116
  end
94
117
 
118
+ # @return [Array(String, Boolean)] what the check printed, and whether it exited 0; the end
119
+ # line is written as the check finishes
120
+ def run_timed(check, env, root, progress)
121
+ started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
122
+ output, passed = run_one(check, env, root)
123
+ progress.finish(check["id"], passed, Process.clock_gettime(Process::CLOCK_MONOTONIC) - started)
124
+ [output, passed]
125
+ end
126
+
95
127
  # @return [Array(String, Boolean)] what the check printed, and whether it exited 0
96
128
  def run_one(check, env, root)
97
129
  argv = check.fetch("run").map { |word| word.to_s.sub("{workers}", workers.to_s) }
130
+ env = { "RUBOCOP_CACHE_ROOT" => RUBOCOP_CACHE_ROOT }.merge(ENV.slice("RUBOCOP_CACHE_ROOT")).merge(env)
98
131
  output, status = Open3.capture2e(env, *argv, chdir: root)
99
132
  [output, status.success?]
100
133
  rescue SystemCallError => e
@@ -108,30 +141,6 @@ module Hecks
108
141
  [Etc.nprocessors / 2, 1].max
109
142
  end
110
143
 
111
- # @param name [String] the stage name
112
- # @param checks [Array<Hash>] the checks that ran
113
- # @param results [Hash{String => Array}] each check's output and whether it passed
114
- # @return [Integer] 0 when every check passed, 1 otherwise
115
- def report(name, checks, results)
116
- failed = checks.reject { |check| results.fetch(check["id"]).last }
117
- failed.each { |check| print_failure(name, check, results.fetch(check["id"]).first) }
118
- return summary(name, "red", failed, 1) unless failed.empty?
119
-
120
- summary(name, "green", checks, 0)
121
- end
122
-
123
- # @return [Integer] the given status, after printing the stage's verdict and its check ids
124
- def summary(name, verdict, checks, status)
125
- puts "[gate #{name}] #{verdict}: #{checks.map { |check| check["id"] }.join(", ")}"
126
- status
127
- end
128
-
129
- # @return [void] prints a failing check's output and what its failure means
130
- def print_failure(name, check, output)
131
- puts "\n[gate #{name}] #{check["title"]}\n\n#{output}"
132
- puts "\n[gate #{name}] BLOCKED: #{check["blocked"]}\n"
133
- end
134
-
135
144
  def list(stages)
136
145
  stages.each do |name, stage|
137
146
  puts "#{name}: #{stage.fetch("checks").map { |check| check["id"] }.join(", ")}"