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
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hecks
4
+ module Tools
5
+ module GateMemo
6
+ # The gitignored files a copy keeps for itself, which decide where a booted domain stores and
7
+ # so what a check that boots it sees (`FolderOverlay`): local worlds and hecksagons beside a
8
+ # domain or in the working directory, and the `local` environment. Git ignores them, so the
9
+ # memo's `owned` list leaves them out; a check that boots a domain reads them from disk, or a
10
+ # green on one copy's database would be reused on a copy that names another.
11
+ module LocalFiles
12
+ # Globs under the checkout.
13
+ GLOBS = %w[lib/**/*.local.hecksagon lib/**/*.local.world examples/**/*.local.hecksagon
14
+ examples/**/*.local.world qa/**/*.local.hecksagon qa/**/*.local.world
15
+ *.local.hecksagon *.local.world environments/local.hecksagon
16
+ environments/local.world].freeze
17
+
18
+ # The checks that boot a domain, so read its local files.
19
+ BOOTING = %w[rspec fuzzing query_agreement engine_agreement model_check].freeze
20
+
21
+ module_function
22
+
23
+ # @param root [String] the checkout
24
+ # @return [Array<String>] the local world and hecksagon files on disk, git-ignored or not,
25
+ # relative to the checkout and sorted
26
+ def files(root)
27
+ found = GLOBS.flat_map { |glob| Dir.glob(glob, base: root) }
28
+ found.uniq.select { |rel| File.file?(File.join(root, rel)) }.sort
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "time"
4
+ require_relative "../gate_memo"
5
+
6
+ module Hecks
7
+ module Tools
8
+ module GateMemo
9
+ # Whether a mint rehearsal's copy of a ledger can be reused. Only a rehearsal that said
10
+ # `would_mint`
11
+ # is kept, and it is reused only while the ledger's watermark and its translation edge are the
12
+ # ones
13
+ # it read. Reuse never skips the mint's own check: `moved?` compares the live watermark, and
14
+ # the
15
+ # PostgresEra lineage manager refuses a mint whose watermark moved.
16
+ module Rehearsal
17
+ # The one verdict a mint may follow.
18
+ VERDICT = "would_mint"
19
+
20
+ module_function
21
+
22
+ # @param domain [String] the domain the rehearsal was for
23
+ # @return [String] the memo key of the domain's rehearsal
24
+ def key(domain) = "mint_rehearsal:#{domain}"
25
+
26
+ # @return [Hash, nil] the entry to keep for a rehearsal, or nil when its verdict is not
27
+ # would_mint
28
+ def entry(verdict:, watermark:, edge:)
29
+ return nil unless verdict == VERDICT
30
+
31
+ { "verdict" => verdict, "watermark" => watermark, "edge" => edge, "at" => Time.now.utc.iso8601 }
32
+ end
33
+
34
+ # @return [Boolean] whether the memo's rehearsal of the domain said would_mint on this
35
+ # watermark
36
+ # and this edge; false when the edge is unknown
37
+ def reusable?(memo, domain, watermark:, edge:)
38
+ entry = memo[key(domain)]
39
+ !edge.nil? && entry.is_a?(Hash) && entry["verdict"] == VERDICT &&
40
+ entry["watermark"] == watermark && entry["edge"] == edge
41
+ end
42
+
43
+ # @return [Boolean] whether the live watermark differs from the one the rehearsal read, so a
44
+ # mint needs a new rehearsal
45
+ def moved?(entry, live_watermark) = entry["watermark"] != live_watermark
46
+
47
+ # @return [void] keeps a would_mint entry for the domain, and forgets any other verdict
48
+ def remember(memo, domain, entry)
49
+ entry ? memo[key(domain)] = entry : memo.delete(key(domain))
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+ require "pathname"
5
+ require "yaml"
6
+ require_relative "../gate_memo"
7
+
8
+ module Hecks
9
+ module Tools
10
+ module GateMemo
11
+ # Decides how much of RuboCop a gate run needs. The green run's memo holds the hash of every
12
+ # Ruby
13
+ # file and of the configuration, and the run is skipped only when the files and their hashes
14
+ # are
15
+ # identical to it. Otherwise RuboCop lints the changed files and the files that require them,
16
+ # and
17
+ # a full run is made whenever that set cannot be worked out.
18
+ module RubyFiles
19
+ # Files whose change invalidates every verdict: the configuration and the local cops it
20
+ # loads.
21
+ CONFIG = %w[.rubocop.yml .rubocop_todo.yml Gemfile.lock lib/rubocop/cop/**/*.rb].freeze
22
+
23
+ # A `require` or `require_relative` line, and the path it names.
24
+ REQUIRE = /^\s*require(_relative)?\s+["']([^"']+)["']/
25
+
26
+ # Directories that hold Ruby the checkout does not own, or copies of it.
27
+ SKIPPED_DIRS = %w[.claude .git rust vendor node_modules tmp].freeze
28
+
29
+ module_function
30
+
31
+ # @param root [String] the checkout
32
+ # @return [Hash{String => String}] each Ruby file's path and sha256
33
+ def hashes(root)
34
+ listed(root).to_h { |rel| [rel, GateMemo.sha(root, rel)] }
35
+ end
36
+
37
+ # @return [Array<String>] the Ruby files of the checkout, sorted, the same set whether
38
+ # or not the checkout is a git repository
39
+ def listed(root)
40
+ listing(root).reject { |rel| rel.split("/").intersect?(SKIPPED_DIRS) }.sort
41
+ end
42
+
43
+ # @return [Array<String>] the Ruby files git lists, or the ones on disk when git does not
44
+ def listing(root)
45
+ out, status = Open3.capture2("git", "-C", root, "ls-files", "-co", "--exclude-standard", "--", "*.rb",
46
+ err: File::NULL)
47
+ return out.split("\n") if status.success?
48
+
49
+ Dir.glob("**/*.rb", base: root)
50
+ end
51
+
52
+ # @param root [String] the checkout
53
+ # @param entry [Hash, nil] the memo's entry for rubocop: `inputs` (the config's digest) and
54
+ # `files`
55
+ # @param now [Hash{String => String}] the checkout's Ruby files and their hashes, as they
56
+ # are now
57
+ # @return [Array(Symbol, Array<String>, String)] the mode (`skip`, `incremental` or `full`),
58
+ # the
59
+ # files to lint when incremental, and why
60
+ def plan(root, entry, now)
61
+ return [:full, [], "no green run is recorded"] unless entry.is_a?(Hash) && entry["files"].is_a?(Hash)
62
+ return [:full, [], "the configuration or a local cop changed"] unless entry["inputs"] == config(root)
63
+ return [:skip, [], "the Ruby files are unchanged"] if now == entry["files"]
64
+
65
+ incremental(root, entry["files"], now)
66
+ rescue SystemCallError, EncodingError, ArgumentError => e
67
+ [:full, [], "the Ruby files could not be read (#{e.message})"]
68
+ end
69
+
70
+ # @return [String] the digest of the configuration and the local cops
71
+ def config(root) = GateMemo.fingerprint(root, CONFIG)
72
+
73
+ # @return [Array(Symbol, Array<String>, String)] the files that changed, or were removed,
74
+ # and the
75
+ # files that require them, less the ones `AllCops` Exclude names; skipped when only
76
+ # excluded files changed, and a full run when a removal leaves nothing to lint
77
+ def incremental(root, was, now)
78
+ changed = now.keys.reject { |rel| was[rel] == now[rel] }
79
+ removed = was.keys - now.keys
80
+ set = lintable(root, closure(root, changed + removed, now.keys))
81
+ return nothing_to_lint(removed) if set.empty?
82
+
83
+ [:incremental, set, "#{set.size} of #{now.size} Ruby files: changed, removed or requiring them"]
84
+ end
85
+
86
+ # @return [Array(Symbol, Array<String>, String)] a full run when a removal left the set
87
+ # empty, since an empty file list would make RuboCop lint the whole tree; otherwise a
88
+ # skip, because only excluded files changed
89
+ def nothing_to_lint(removed)
90
+ return [:full, [], "removed files leave nothing to lint"] if removed.any?
91
+
92
+ [:skip, [], "only files the configuration excludes changed"]
93
+ end
94
+
95
+ # @return [Array<String>] the files that `AllCops` Exclude does not name, as RuboCop
96
+ # applies it to a full run; an explicit file list would otherwise bypass it
97
+ def lintable(root, files)
98
+ globs = excludes(root)
99
+ files.reject { |rel| globs.any? { |glob| File.fnmatch?(glob, rel, File::FNM_PATHNAME) } }
100
+ end
101
+
102
+ # @return [Array<String>] the `AllCops` Exclude globs of the checkout's `.rubocop.yml`; none
103
+ # when the file is absent
104
+ def excludes(root)
105
+ path = File.join(root, ".rubocop.yml")
106
+ return [] unless File.file?(path)
107
+
108
+ Array(YAML.safe_load_file(path, permitted_classes: [Symbol]).dig("AllCops", "Exclude"))
109
+ end
110
+
111
+ # @return [Array<String>] the seeds and every file that requires one, through
112
+ # require_relative
113
+ # and require "hecks/..." lines, among the files now present
114
+ def closure(root, seeds, present)
115
+ reverse = requirers(root, present)
116
+ queue = seeds.dup
117
+ seen = queue.to_set
118
+ queue.shift.then { |rel| (reverse[rel] || []).each { |req| queue << req if seen.add?(req) } } until queue.empty?
119
+ seen.select { |rel| present.include?(rel) }.sort
120
+ end
121
+
122
+ # @return [Hash{String => Array<String>}] each file's requirers, keyed by the file it
123
+ # requires
124
+ def requirers(root, present)
125
+ present.each_with_object(Hash.new { |h, k| h[k] = [] }) do |rel, reverse|
126
+ requires(root, rel).each { |target| reverse[target] << rel }
127
+ end
128
+ end
129
+
130
+ # @return [Array<String>] the Ruby files that `rel` requires inside the checkout
131
+ def requires(root, rel)
132
+ source = File.read(File.join(root, rel))
133
+ source.scan(REQUIRE).filter_map do |relative, name|
134
+ relative ? resolve_relative(rel, name) : resolve_library(name)
135
+ end
136
+ end
137
+
138
+ # @return [String] the path a `require_relative` names, from the file that names it
139
+ def resolve_relative(rel, name)
140
+ target = name.end_with?(".rb") ? name : "#{name}.rb"
141
+ Pathname.new(File.join(File.dirname(rel), target)).cleanpath.to_s
142
+ end
143
+
144
+ # @return [String, nil] the library file a `require "hecks/x"` names under lib/; nil for a
145
+ # gem
146
+ def resolve_library(name)
147
+ "lib/#{name}.rb" if name.start_with?("hecks/")
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../gate_memo"
4
+ require_relative "ruby_files"
5
+
6
+ module Hecks
7
+ module Tools
8
+ module GateMemo
9
+ # One stage's use of the memo: which checks a run skips, and what its green results record.
10
+ #
11
+ # A run calls `plan` before it starts and `record` once every check has answered. A check that
12
+ # passed is recorded with the inputs it ran on; a check that failed is forgotten, so it runs
13
+ # again.
14
+ class Session
15
+ # What a run does with the checks it was given.
16
+ #
17
+ # @!attribute [r] run
18
+ # @return [Array<Hash>] the checks to run; rubocop with the files it lints when
19
+ # incremental
20
+ # @!attribute [r] skipped
21
+ # @return [Array(Hash, String)] each skipped check and when its green run was recorded
22
+ # @!attribute [r] notes
23
+ # @return [Array<String>] what the memo could not do, and how each planned check runs
24
+ Plan = Struct.new(:run, :skipped, :notes)
25
+
26
+ # @param root [String] the checkout
27
+ # @param env [Hash{String => String}] the environment the stage sets for its checks
28
+ # @param file [String] the memo's file
29
+ def initialize(root:, env:, file: GateMemo.path)
30
+ @root = root
31
+ @env = env
32
+ @file = file
33
+ @ran = []
34
+ @pending = {}
35
+ @notes = []
36
+ end
37
+
38
+ # @param checks [Array<Hash>] the checks a run was asked for
39
+ # @return [Plan] the checks to run and the ones skipped; every check runs when the memo is
40
+ # unusable
41
+ def plan(checks)
42
+ @memo, problem = GateMemo.read(@file)
43
+ @readable = problem.nil?
44
+ @memo ||= {}
45
+ @notes << "gate memo unusable (#{problem}): every check runs, and nothing is recorded" unless @readable
46
+ result = Plan.new([], [], @notes)
47
+ checks.each { |check| check["id"] == "rubocop" ? rubocop(check, result) : generic(check, result) }
48
+ result
49
+ end
50
+
51
+ # @param results [Hash{String => Array}] each check that ran: its output, and whether it
52
+ # passed
53
+ # @return [Array<String>] what could not be recorded; empty when the memo was written
54
+ def record(results)
55
+ return [] unless @readable
56
+
57
+ @ran.each { |id| update(id, results.fetch(id).last) }
58
+ error = GateMemo.write(@memo, @file)
59
+ error ? ["gate memo not written (#{error}): the next run runs these checks in full"] : []
60
+ end
61
+
62
+ private
63
+
64
+ # @return [void] a check with declared inputs is skipped when its inputs hash as its green
65
+ # run did
66
+ def generic(check, result)
67
+ tree = GateMemo.tree(@root, check["id"])
68
+ inputs = GateMemo.digest(@root, check, @env, tree)
69
+ entry = @memo[check["id"]]
70
+ return result.skipped << [check, entry["at"]] if GateMemo.reusable?(entry, inputs)
71
+
72
+ run(check, result, inputs ? GateMemo.green(inputs, tree) : nil)
73
+ end
74
+
75
+ # @return [void] RuboCop runs on the files its green run did not cover, or on all of them
76
+ def rubocop(check, result)
77
+ entry = @memo["rubocop"]
78
+ now = RubyFiles.hashes(@root)
79
+ mode, files, why = RubyFiles.plan(@root, entry, now)
80
+ return result.skipped << [check, entry["at"]] if mode == :skip
81
+
82
+ result.notes << "rubocop #{mode}: #{why}"
83
+ run(rubocop_check(check, mode, files), result, rubocop_entry(now))
84
+ end
85
+
86
+ # @return [Hash] the check, with the files it lints appended when the run is incremental
87
+ def rubocop_check(check, mode, files)
88
+ mode == :incremental ? check.merge("run" => check["run"] + files) : check
89
+ end
90
+
91
+ # @return [Hash] the green entry a passing RuboCop run records: its config and every Ruby
92
+ # file
93
+ def rubocop_entry(now) = GateMemo.green(RubyFiles.config(@root)).merge("files" => now)
94
+
95
+ # @return [void] the check joins the run; its green entry is kept when it passes
96
+ def run(check, result, entry)
97
+ result.run << check
98
+ @ran << check["id"]
99
+ @pending[check["id"]] = entry
100
+ end
101
+
102
+ # @return [void] keeps a passed check's entry, and forgets a check that did not pass
103
+ def update(id, passed)
104
+ entry = @pending[id]
105
+ if passed && entry
106
+ @memo[id] = entry
107
+ else
108
+ @memo.delete(id)
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../gate_memo"
4
+
5
+ module Hecks
6
+ module Tools
7
+ module GateMemo
8
+ # The gate's memo, read by a check that is also run by hand. A green run of the check, in the
9
+ # gate or alone, is kept with the digest of the files it read (`tree`); the check is skipped
10
+ # when it is asked again on that same tree. Only a check that changes nothing asks: a run that
11
+ # confirms, rewrites or names a part of the corpus never skips.
12
+ module Standalone
13
+ module_function
14
+
15
+ # @param id [String] a check id
16
+ # @return [String] the memo key a run by hand keeps its own green result under, so it never
17
+ # replaces the gate's entry for the check
18
+ def key(id) = "standalone:#{id}"
19
+
20
+ # @param root [String] the checkout
21
+ # @param id [String] a check id with declared inputs in `GateMemo::INPUTS`
22
+ # @param file [String] the memo's file
23
+ # @return [String, nil] when the gate or a run by hand last passed the check on exactly
24
+ # these files; nil to run it
25
+ def passed_at(root, id, file: GateMemo.path, tree: GateMemo.tree(root, id))
26
+ memo, problem = GateMemo.read(file)
27
+ return nil if problem || tree.nil?
28
+
29
+ entries = [memo[id], memo[key(id)]].select { |e| e.is_a?(Hash) && e["result"] == "green" }
30
+ entries.find { |entry| entry["tree"] == tree }&.fetch("at")
31
+ end
32
+
33
+ # @param root [String] the checkout
34
+ # @param id [String] a check id with declared inputs
35
+ # @param file [String] the memo's file
36
+ # @return [void] keeps a green entry for the check on the files it read now; an unreadable
37
+ # memo is left as it is
38
+ def record(root, id, file: GateMemo.path, tree: GateMemo.tree(root, id))
39
+ memo, problem = GateMemo.read(file)
40
+ return if problem || tree.nil?
41
+
42
+ memo[key(id)] = GateMemo.green(tree, tree)
43
+ GateMemo.write(memo, file)
44
+ end
45
+
46
+ # Runs the block unless the check already passed on these files, and keeps its pass. The
47
+ # files are read before the block runs, so an edit made while it runs is not credited to it.
48
+ #
49
+ # @param root [String] the checkout
50
+ # @param id [String] a check id with declared inputs
51
+ # @param file [String] the memo's file
52
+ # @return [String, Object] the skip line, or what the block returned when it did not raise
53
+ def guard(root, id, file: GateMemo.path)
54
+ tree = GateMemo.tree(root, id)
55
+ at = passed_at(root, id, file: file, tree: tree)
56
+ return skipped_line(at) if at
57
+
58
+ yield.tap { record(root, id, file: file, tree: tree) }
59
+ end
60
+
61
+ # @param at [String] when the green run was recorded
62
+ # @return [String] what a skipped check says
63
+ def skipped_line(at) = "skipped as unchanged since #{at}"
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,196 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest"
4
+ require "fileutils"
5
+ require "json"
6
+ require "open3"
7
+ require "time"
8
+ require_relative "gate_memo/local_files"
9
+
10
+ module Hecks
11
+ module Tools
12
+ # The gate's memo of green runs, kept outside the checkout. A check whose inputs hash the same
13
+ # as
14
+ # the inputs of its last green run is reported as skipped, never as passed. The inputs of a
15
+ # check
16
+ # are the files it reads (the globs in `INPUTS`, hashed by content), together with the command
17
+ # and
18
+ # the environment it runs under. A red result is never kept, so a red check runs again next
19
+ # time.
20
+ #
21
+ # The rule itself is declared in the Check bluebook beside `GateRun`, in
22
+ # lib/hecks/hecks/check/check.bluebook.
23
+ module GateMemo
24
+ # Build output is never an input: a Rust target directory is large, and its tool rebuilds
25
+ # it (`target` and `node_modules`).
26
+ BUILD_DIRS = %w[target node_modules .git].freeze
27
+
28
+ # Where the memo is kept, unless `HECKS_GATE_MEMO` names another file.
29
+ DEFAULT_PATH = File.join(Dir.home, ".claude", "hecks-dev", "gate_memo.json")
30
+
31
+ # The files each check reads, as globs under the checkout. A glob is wide where the check
32
+ # reads a whole tree: a change to any file a check could read reruns it, and a check is
33
+ # skipped only when none of them moved. A check with no entry here is never skipped.
34
+ SHAPE = %w[lib/**/*.rb lib/**/*.bluebook lib/**/*.hecksagon lib/**/*.world lib/**/*.json
35
+ lib/**/*.adapter translations/**/*].freeze
36
+ STYLE = %w[lib/**/*.rb spec/**/*.rb docs/COMMENT_STYLE_GUIDE.md].freeze
37
+ SPEC = %w[spec/spec_helper.rb spec/support/**/*.rb].freeze
38
+ LIBRARY = %w[lib/**/*].freeze
39
+ SOURCE = %w[lib/**/* spec/**/* examples/**/* qa/**/* exe/**/* bin/**/* docs/**/* rust/**/* .github/**/*
40
+ Gemfile Gemfile.lock *.gemspec .rubocop.yml .rubocop_todo.yml].freeze
41
+ RUST = %w[rust/**/*.rs rust/**/Cargo.toml rust/**/Cargo.lock rust/rust-toolchain.toml
42
+ rust-toolchain.toml].freeze
43
+ MODEL = %w[lib/**/*.rb lib/**/*.bluebook lib/**/*.hecksagon lib/**/*.world examples/**/*].freeze
44
+
45
+ # What the code, the rust tree and the bundle hold: everything the whole-tree checks read
46
+ # except prose, workflows and lint settings.
47
+ CODE = %w[lib/**/* spec/**/* examples/**/* exe/**/* bin/**/* rust/**/* Gemfile Gemfile.lock *.gemspec].freeze
48
+ # `spec/fuzzing` runs the specs and the corpus against the library and the rust tree
49
+ # (`rust/` is read for the target capabilities), and boots the stress domains, the QA bluebook
50
+ # and the settings under `qa/`. No spec in it reads `docs/` or `.github/`.
51
+ FUZZING = CODE + %w[qa/**/*]
52
+ # The reference-docs check reads the words of the language (`lib/`) against the pages of
53
+ # `docs/implemented/reference/` and nothing else.
54
+ DOC_COVERAGE = %w[lib/**/* docs/implemented/reference/**/* Gemfile Gemfile.lock *.gemspec].freeze
55
+ # Regenerating the corpus reads its bluebooks, hecksagons and worlds (`lib/`, `examples/`,
56
+ # `qa/`, `packages/`, the fixtures and corpus specs the corpus routes name), the generator
57
+ # (`lib/`, `rust/codegen`) and the committed output (`rust/`). It reads no prose.
58
+ CODEGEN = %w[lib/**/* examples/**/* qa/**/* packages/**/* spec/fixtures/**/* spec/corpus/**/* exe/**/*
59
+ bin/**/* rust/**/* Gemfile Gemfile.lock *.gemspec].freeze
60
+
61
+ INPUTS = {
62
+ "shape_ledger" => SHAPE,
63
+ "comment_style" => STYLE,
64
+ "comment_blocks" => STYLE + %w[examples/**/*.rb],
65
+ "secret_output" => LIBRARY + SPEC + %w[spec/secret_output_spec.rb],
66
+ "adapter_words" => LIBRARY + SPEC + %w[spec/adapter_words_spec.rb],
67
+ "rspec" => SOURCE,
68
+ "fuzzing" => FUZZING,
69
+ "query_agreement" => SOURCE,
70
+ "engine_agreement" => SOURCE,
71
+ "model_check" => MODEL,
72
+ "doc_coverage" => DOC_COVERAGE,
73
+ "codegen_drift" => CODEGEN,
74
+ "rust_fmt" => RUST
75
+ }.freeze
76
+
77
+ # The sha of each file this process has hashed, by path, modification time, size and inode, so
78
+ # the checks of one run that read the same tree hash each file once.
79
+ SHAS = {} # rubocop:disable Style/MutableConstant
80
+
81
+ module_function
82
+
83
+ # @return [String] the memo's file: `HECKS_GATE_MEMO` when set, else `DEFAULT_PATH`
84
+ def path = ENV.fetch("HECKS_GATE_MEMO", DEFAULT_PATH)
85
+
86
+ # @param file [String] the memo's file
87
+ # @return [Array(Hash, String)] the memo and nil; nil and why it could not be read. A missing
88
+ # file is an empty memo, not a problem.
89
+ def read(file = path)
90
+ return [{}, nil] unless File.exist?(file)
91
+
92
+ memo = JSON.parse(File.read(file))
93
+ memo.is_a?(Hash) ? [memo, nil] : [nil, "#{file} does not hold a JSON object"]
94
+ rescue SystemCallError, JSON::ParserError => e
95
+ [nil, "#{file}: #{e.message}"]
96
+ end
97
+
98
+ # @param memo [Hash] the memo to keep
99
+ # @param file [String] the memo's file
100
+ # @return [String, nil] nil when it was written, else why it could not be
101
+ def write(memo, file = path)
102
+ FileUtils.mkdir_p(File.dirname(file))
103
+ File.write(file, "#{JSON.pretty_generate(memo)}\n")
104
+ nil
105
+ rescue SystemCallError => e
106
+ "#{file}: #{e.message}"
107
+ end
108
+
109
+ # The files a checkout owns: the ones git tracks and the ones it would add. A file git
110
+ # ignores (a run's data directory, a build's output, a log) is never an input: a check's own
111
+ # run rewrites those, so hashing them made the next run's inputs differ from the last.
112
+ #
113
+ # @param root [String] the checkout
114
+ # @return [Array<String>, nil] its files relative to the checkout; nil when `root` is not a
115
+ # git checkout
116
+ def owned(root)
117
+ out, status = Open3.capture2("git", "-C", root, "ls-files", "-co", "--exclude-standard", "-z",
118
+ err: File::NULL)
119
+ status.success? ? out.split("\0") : nil
120
+ rescue SystemCallError
121
+ nil
122
+ end
123
+
124
+ # @param root [String] the checkout
125
+ # @param globs [Array<String>] globs under the checkout
126
+ # @return [Array<String>] the regular files the globs name, relative to the checkout, sorted;
127
+ # in a git checkout only the files it owns
128
+ def files(root, globs)
129
+ own = owned(root)
130
+ matched = globs.flat_map { |glob| Dir.glob(glob, base: root) }.uniq
131
+ matched &= own if own
132
+ matched.reject { |rel| rel.split("/").intersect?(BUILD_DIRS) }
133
+ .select { |rel| File.file?(File.join(root, rel)) }.sort
134
+ end
135
+
136
+ # @param root [String] the checkout
137
+ # @param rel [String] a file relative to the checkout
138
+ # @return [String] the sha256 of the file's content
139
+ def sha(root, rel)
140
+ path = File.join(root, rel)
141
+ stat = File.stat(path)
142
+ key = [path, stat.mtime.to_r, stat.size, stat.ino]
143
+ SHAS.fetch(key) { SHAS[key] = Digest::SHA256.file(path).hexdigest }
144
+ end
145
+
146
+ # @param root [String] the checkout
147
+ # @param globs [Array<String>] the files to fingerprint
148
+ # @param local [Boolean] whether the checkout's local worlds are read too
149
+ # @return [String] one digest of each file's path and content, in path order
150
+ def fingerprint(root, globs, local: false)
151
+ rows = files(root, globs).map { |rel| "#{rel} #{sha(root, rel)}" }
152
+ rows += LocalFiles.files(root).map { |rel| "#{rel} #{sha(root, rel)}" } if local
153
+ Digest::SHA256.hexdigest(rows.join("\n"))
154
+ end
155
+
156
+ # @param root [String] the checkout
157
+ # @param check [Hash] a check: its `id` and its `run` words
158
+ # @param env [Hash{String => String}] the environment the stage sets for the check
159
+ # @return [String, nil] the digest of the check's command, environment and inputs; nil when
160
+ # the
161
+ # check has no declared inputs, or one of them cannot be read, so it is never skipped
162
+ def digest(root, check, env, tree = tree(root, check["id"]))
163
+ return nil unless tree
164
+
165
+ words = ["run #{check["run"].join(" ")}", "env #{env.sort.map { |pair| pair.join("=") }.join(" ")}"]
166
+ Digest::SHA256.hexdigest("#{words.join("\n")}\n#{tree}")
167
+ end
168
+
169
+ # @param root [String] the checkout
170
+ # @param id [String] a check's id
171
+ # @return [String, nil] the digest of the files the check reads, whatever command runs it;
172
+ # nil when it has no declared inputs or one of them cannot be read
173
+ def tree(root, id)
174
+ globs = INPUTS[id]
175
+ globs && fingerprint(root, globs, local: LocalFiles::BOOTING.include?(id))
176
+ rescue SystemCallError
177
+ nil
178
+ end
179
+
180
+ # @param entry [Hash, nil] the memo's entry for a check
181
+ # @param inputs [String, nil] the check's digest now
182
+ # @return [Boolean] whether the entry is a green run on exactly these inputs
183
+ def reusable?(entry, inputs)
184
+ !inputs.nil? && entry.is_a?(Hash) && entry["result"] == "green" && entry["inputs"] == inputs
185
+ end
186
+
187
+ # @param inputs [String] the digest of the command, the environment and the files
188
+ # @param tree [String, nil] the digest of the files alone, which a standalone run of the same
189
+ # check compares with
190
+ # @return [Hash] a green entry for a check that ran on these inputs
191
+ def green(inputs, tree = nil)
192
+ { "result" => "green", "at" => Time.now.utc.iso8601, "inputs" => inputs }.merge(tree ? { "tree" => tree } : {})
193
+ end
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hecks
4
+ module Tools
5
+ # Prints a gate run's verdict: the notes the memo left, the checks it skipped as unchanged, the
6
+ # output and meaning of each red check, and the summary line. A skipped check is printed as
7
+ # skipped, never as passed, and the verdict counts only the checks that ran.
8
+ module GateReport
9
+ module_function
10
+
11
+ # @param name [String] the stage name
12
+ # @param plan [GateMemo::Session::Plan] the checks that ran and the ones skipped as unchanged
13
+ # @param results [Hash{String => Array}] each check's output and whether it passed
14
+ # @param notes [Array<String>] what the memo could not record
15
+ # @return [Integer] 0 when every check that ran passed, 1 otherwise
16
+ def report(name, plan, results, notes = [])
17
+ (plan.notes + notes).each { |note| puts "[gate #{name}] #{note}" }
18
+ plan.skipped.each { |check, since| print_skipped(name, check, since) }
19
+ failed = print_failures(name, plan.run, results)
20
+ return summary(name, "red", failed, plan.skipped, 1) unless failed.empty?
21
+
22
+ summary(name, "green", plan.run, plan.skipped, 0)
23
+ end
24
+
25
+ # @return [Array<Hash>] the checks that did not pass, after printing each one's output and
26
+ # meaning
27
+ def print_failures(name, checks, results)
28
+ checks.reject { |check| results.fetch(check["id"]).last }.each do |check|
29
+ print_failure(name, check, results.fetch(check["id"]).first)
30
+ end
31
+ end
32
+
33
+ # @return [void] prints a check the memo skipped, as skipped and never as passed
34
+ def print_skipped(name, check, since)
35
+ puts "[gate #{name}] #{check["id"]}: unchanged, skipped (green as of #{since})"
36
+ end
37
+
38
+ # @return [Integer] the given status, after printing the stage's verdict, the checks that ran
39
+ # and
40
+ # the checks skipped as unchanged
41
+ def summary(name, verdict, checks, skipped, status)
42
+ ran = checks.map { |check| check["id"] }
43
+ line = "[gate #{name}] #{verdict}: #{ran.empty? ? "nothing ran" : ran.join(", ")}"
44
+ line += "; skipped as unchanged: #{skipped.map { |check, _| check["id"] }.join(", ")}" if skipped.any?
45
+ puts line
46
+ status
47
+ end
48
+
49
+ # @return [void] prints a failing check's output and what its failure means
50
+ def print_failure(name, check, output)
51
+ puts "\n[gate #{name}] #{check["title"]}\n\n#{output}"
52
+ puts "\n[gate #{name}] BLOCKED: #{check["blocked"]}\n"
53
+ end
54
+ end
55
+ end
56
+ end