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,183 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "qa_era_mint"
4
+ require_relative "../../adapters/driven/postgres_era/lineage_manager/mint_decision"
5
+
6
+ module Hecks
7
+ module Adapters
8
+ # The auto-mint rule the Maintenance mint enforces. A mint runs without the owner's confirm only
9
+ # when
10
+ # the edge leaving the held era is empty, every change in the shape diff is additive, and the
11
+ # rehearsal
12
+ # refused nothing. The rule is declared as `AutoMintAdditive` in quality_control.bluebook; this
13
+ # module
14
+ # is its check. The stranded-marker and watermark guards stay in `MainMaintenance#mint`.
15
+ module QaAutoMint
16
+ # The edge file's opening line and its closing line, with nothing between them when it is
17
+ # empty.
18
+ EDGE_HEADER = /\AHecks\.data_translation\s+"[^"]*",\s+from:\s+"[^"]*",\s+to:\s+"[^"]*"\s+do\z/
19
+
20
+ module_function
21
+
22
+ # The facts the rule reads from a ledger: its live watermark, the edge leaving its held era
23
+ # and that edge's text, and the verdict the shared mint decision reaches for the held era.
24
+ #
25
+ # @param ledger [String] the bluebook directory of the ledger
26
+ # @return [Hash] `watermark`, `from`, `to`, `edge_text`, `verdict`, `held` and `current`
27
+ def facts(ledger)
28
+ held = JournalStore::HeldDomain.open(ledger, mode: :translated)
29
+ registry = held.registry
30
+ latest, watermark = latest_era(held)
31
+ verdict = PostgresEra::LineageManager::MintDecision.decide_latest(
32
+ latest, bluebook: held.bluebook, translations: registry.translations, directory: ledger, watermark: watermark
33
+ )
34
+ { watermark: watermark, held: held_bluebook(latest), current: held.bluebook, verdict: verdict }
35
+ .merge(edge_facts(held, ledger, latest[:label]))
36
+ end
37
+
38
+ # @return [Array(Hash, Integer)] the latest held era and the live watermark, read from its
39
+ # database
40
+ def latest_era(held)
41
+ db = PostgresEra.connect_for(held.domain_name, held.settings)
42
+ lineage = PostgresEra::Lineage.new(db, held.domain_name)
43
+ [lineage.eras.last, lineage.last_ordinal]
44
+ ensure
45
+ db&.close
46
+ end
47
+
48
+ # @return [Runtime::ProjectedBluebook] the held era's stored storage shape, as a bluebook view
49
+ def held_bluebook(latest) = PostgresEra::LineageManager.held_bluebook(latest)
50
+
51
+ # The bluebook directory a mint is for: the one the request names, or the QualityControl
52
+ # ledger.
53
+ #
54
+ # @return [String]
55
+ def ledger_for(root, requested) = requested || File.join(root, QaEraMint::LEDGER)
56
+
57
+ # The ledger a mint is for, refused by name when its world declares it disposable: its boot
58
+ # holds every shape change itself, so there is no era to mint.
59
+ #
60
+ # @return [String] the bluebook directory
61
+ # @raise [Runtime::WiringError] if the ledger is disposable
62
+ def mintable_ledger_for(root, requested)
63
+ ledger = ledger_for(root, requested)
64
+ raise Runtime::WiringError, QaEraMint::DISPOSABLE if QaEraMint.disposable?(ledger)
65
+
66
+ ledger
67
+ end
68
+
69
+ # The journal rows the run's own `Maintenance` record wrote above the rehearsed watermark: the
70
+ # mint command saves that record before its entry point runs, so it is the one change the
71
+ # rehearsal could not have read.
72
+ #
73
+ # @param ledger [String] the bluebook directory of the ledger
74
+ # @param run [String] the run key, which is the record's id
75
+ # @param rehearsed [Integer] the watermark the rehearsal read
76
+ # @return [Integer]
77
+ def own_rows_after(ledger, run, rehearsed)
78
+ held = JournalStore::HeldDomain.open(ledger, mode: :translated)
79
+ db = PostgresEra.connect_for(held.domain_name, held.settings)
80
+ PostgresEra::Lineage.new(db, held.domain_name).rows_of_after(rehearsed, "maintenance", run)
81
+ ensure
82
+ db&.close
83
+ end
84
+
85
+ # Why a rehearsed watermark is stale at the live one: the ledger moved by something other than
86
+ # the run's own record, which `commands` counts in the journal now.
87
+ #
88
+ # @param commands [#own_rows_after] reads the journal
89
+ # @param ledger [String] the bluebook directory of the ledger
90
+ # @param run [String] the run key
91
+ # @param rehearsed [Integer] the watermark the rehearsal read
92
+ # @param live [Integer] the live watermark
93
+ # @return [String, nil]
94
+ def stale_for(commands, ledger, run, rehearsed, live)
95
+ stale_watermark(rehearsed, live, own_rows: commands.own_rows_after(ledger, run, rehearsed))
96
+ end
97
+
98
+ # A watermark is stale when the ledger moved by anything but the run's own record.
99
+ #
100
+ # @param own_rows [Integer] the rows that record wrote above the rehearsed watermark
101
+ # @return [String, nil] the reason the live watermark moved from the rehearsed one, or nil
102
+ def stale_watermark(rehearsed, live, own_rows: 0)
103
+ return if rehearsed == live - own_rows
104
+
105
+ "the ledger watermark moved from #{rehearsed} to #{live} since the rehearsal; rehearse again"
106
+ end
107
+
108
+ # Why a mint waits for the owner: never when confirmed, else the reason the rule holds it.
109
+ #
110
+ # @param confirm [Boolean, nil] the owner's confirmation
111
+ # @return [String, nil]
112
+ def hold_reason(facts, confirm, refusal_total)
113
+ held_reason(facts, refusal_total: refusal_total) unless confirm == true
114
+ end
115
+
116
+ # Why the auto-mint rule holds a mint for the owner, or nil when it lets the mint run
117
+ # unconfirmed. With no edge file, the implicit empty edge stands in for an empty edge when
118
+ # the shape change qualifies for one.
119
+ #
120
+ # @param facts [Hash] what `facts` read from the ledger
121
+ # @param refusal_total [Integer, nil] the rehearsal's refusal count
122
+ # @return [String, nil] the reason the owner must approve, or nil when the rule holds
123
+ def held_reason(facts, refusal_total:)
124
+ edge_reason(facts) || additive_reason(facts) || rehearsal_reason(refusal_total)
125
+ end
126
+
127
+ # @return [String, nil] why the edge leaving the held era does not stand in for an empty one
128
+ def edge_reason(facts)
129
+ return "no translation edge leaves #{facts[:from]}" if facts[:edge_text].nil? && !implicit?(facts)
130
+ return "the edge from #{facts[:from]} is not empty" if facts[:edge_text] && !empty_edge?(facts[:edge_text])
131
+
132
+ nil
133
+ end
134
+
135
+ # @return [String, nil] why the shared decision does not take the shape change as additive
136
+ def additive_reason(facts)
137
+ case facts[:verdict].reason
138
+ when :non_qualifying then "the shape change is not additive"
139
+ when :unrecorded then "the move from #{facts[:from]} to #{facts[:to]} is not recorded"
140
+ end
141
+ end
142
+
143
+ # @return [String, nil] why the rehearsal refused rows
144
+ def rehearsal_reason(refusal_total)
145
+ return if refusal_total.is_a?(Integer) && refusal_total.zero?
146
+
147
+ "the rehearsal refused #{refusal_total.inspect} rows"
148
+ end
149
+
150
+ # Whether an edge file declares nothing: its header and its `end`, with no line between them.
151
+ #
152
+ # @param text [String] the edge file's text
153
+ # @return [Boolean]
154
+ def empty_edge?(text)
155
+ body = text.lines.map(&:strip).reject { |line| line.empty? || line.start_with?("#") }
156
+ body.size == 2 && EDGE_HEADER.match?(body.first) && body.last == "end"
157
+ end
158
+
159
+ # Whether no edge file is needed: the shared decision takes the implicit empty edge.
160
+ #
161
+ # @return [Boolean]
162
+ def implicit?(facts) = facts[:verdict].verdict == "would_mint" && facts[:verdict].source == "implicit"
163
+
164
+ # @return [Hash] the edge leaving the held era's label for the current shape's label, and its
165
+ # text
166
+ def edge_facts(held, ledger, from)
167
+ to = Runtime::StorageShape.mint_hash(held.bluebook)[0, Runtime::StorageShape::LABEL_LENGTH]
168
+ { from: from, to: to, edge_text: edge_text(File.join(ledger, "translations"), from, to) }
169
+ end
170
+
171
+ # The text of the edge that leads from `from` to `to`, found by its header.
172
+ #
173
+ # @return [String, nil]
174
+ def edge_text(dir, from, to)
175
+ header = %(from: "#{from}", to: "#{to}")
176
+ Dir.glob(File.join(dir, "*.bluebook")).map { |path| File.read(path) }.find { |text| header_of(text).include?(header) }
177
+ end
178
+
179
+ # @return [String] the first line of an edge file, where its source and target are named
180
+ def header_of(text) = text.lines.first.to_s
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../hecks/adapters/journal_store/held_domain"
4
+
5
+ module Hecks
6
+ module Adapters
7
+ # The mint of the QualityControl ledger's next era, run by the Maintenance mint. The era check
8
+ # of the ledger the QA world names runs once with the mint command's request. The world keeps
9
+ # `mint_eras false`; this one check's settings carry `mint_eras true` and the request, which is
10
+ # the one-mint permission the world's comment describes.
11
+ module QaEraMint
12
+ # The ledger's bluebook directory, under the repository root.
13
+ LEDGER = "qa/bluebook"
14
+
15
+ # What a mint of a disposable ledger answers: its boot holds every shape change itself.
16
+ DISPOSABLE = "disposable, no mint needed: the ledger's world declares disposable_ledger true, " \
17
+ "so its boot brings the shape up to date without an era"
18
+
19
+ module_function
20
+
21
+ # Whether a ledger's world declares `disposable_ledger true`, so it needs no mint.
22
+ #
23
+ # @param ledger [String, JournalStore::HeldDomain] the bluebook directory, or the opened one
24
+ # @return [Boolean]
25
+ def disposable?(ledger)
26
+ held = ledger.is_a?(String) ? JournalStore::HeldDomain.open(ledger, mode: :translated) : ledger
27
+ PostgresEra::LineageManager::DisposableLedger.declared?(held.settings)
28
+ end
29
+
30
+ # Mints the next era of the ledger, or refuses by name.
31
+ #
32
+ # @param root [String] the repository root holding `qa/bluebook`
33
+ # @param domain [String] the domain the mint is for; the ledger must hold it
34
+ # @param era [Integer] the ordinal the mint is for; it must be the era the ledger would mint
35
+ # next
36
+ # @param request [Hash] the mint command's request: `confirm` (a boolean), `verdict` (the
37
+ # rehearsal's verdict) and `watermark` (the ledger watermark the rehearsal read)
38
+ # @param ledger [String, nil] the bluebook directory of the ledger; the QualityControl ledger
39
+ # under `root` when nil
40
+ # @return [Array(String, Boolean)] the minted era's label or the refusal, and whether it
41
+ # minted
42
+ def call(root, domain:, era:, request:, ledger: nil)
43
+ held = JournalStore::HeldDomain.open(ledger || File.join(root, LEDGER), mode: :translated)
44
+ return [DISPOSABLE, false] if disposable?(held)
45
+
46
+ settings = held.settings.merge(mint_eras: true, mint_request: request)
47
+ refuse_wrong_mint!(held, settings, domain, era)
48
+ mint!(held, settings) ? [Runtime::StorageShape.mint_label(held.bluebook), true] : [unminted, false]
49
+ rescue Runtime::WiringError, Runtime::NotFound => e
50
+ [e.message, false]
51
+ end
52
+
53
+ # Refuses a mint that names another domain, or an era other than the one the ledger would mint
54
+ # next.
55
+ #
56
+ # @return [void]
57
+ # @raise [Runtime::WiringError] naming the mismatch
58
+ def refuse_wrong_mint!(held, settings, domain, era)
59
+ name = held.domain_name
60
+ raise Runtime::WiringError, "the ledger holds #{name}, not #{domain}" unless name == domain
61
+
62
+ next_era = next_era_of(settings, name)
63
+ return if next_era == era
64
+
65
+ raise Runtime::WiringError, "the mint names era #{era}, but the ledger's next era is #{next_era}"
66
+ end
67
+
68
+ # @return [Integer] the ordinal the ledger would mint next: one past its latest held era
69
+ def next_era_of(settings, name)
70
+ db = PostgresEra.connect_for(name, settings)
71
+ latest = PostgresEra::Lineage.new(db, name).eras.last
72
+ latest ? latest[:ordinal] + 1 : 1
73
+ ensure
74
+ db&.close
75
+ end
76
+
77
+ # Runs the era check with the request. It mints when the shape drifted, and answers false when
78
+ # the shape is already the ledger's current one.
79
+ #
80
+ # @return [Integer, nil] the ordinal minted, or nil when nothing minted
81
+ def mint!(held, settings)
82
+ text = Runtime::EraCheck.source_text_for(held.bluebook, held.directory, registry: held.registry) ||
83
+ Runtime::EraCheck.refuse_missing_source!(held.bluebook, held.directory)
84
+ held.registry.adapter_class(held.adapter_name).era_check!(
85
+ registry: held.registry, bluebook: held.bluebook, current_text: text, settings: settings,
86
+ directory: held.directory
87
+ )
88
+ end
89
+
90
+ # @return [String] the refusal for a mint that found nothing to mint
91
+ def unminted = "the ledger already holds the current shape; no era was minted"
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "time"
5
+ require "fileutils"
6
+
7
+ module Hecks
8
+ module Adapters
9
+ # The marker file that quiets QA for a mint window. While a fresh marker exists, `hecks
10
+ # quality_control sweep.tick` refuses to start any step (it prints `quiesced: skipped
11
+ # <step>` and exits 0), so nothing writes to the QA ledger and its watermark holds still
12
+ # between a rehearsal and the mint. The tick reads the marker before each step, so a tick
13
+ # already running notices it too.
14
+ #
15
+ # The marker is one JSON file beside the stranded-mint record (`~/.claude/hecks-dev/quiesce`,
16
+ # `HECKS_QUIESCE_STATE` moves it) holding its owner, reason and time. A marker older than the
17
+ # staleness window (two hours) is ignored and reported, so a crashed mint cannot freeze QA
18
+ # for good. It touches no ledger and no git state, and the mint itself never requires it.
19
+ class QaQuiesceMarker
20
+ # Raised when a hold or release is refused; the message is the reason.
21
+ class Refused < RuntimeError; end
22
+
23
+ # Where the marker lives.
24
+ PATH = ENV.fetch("HECKS_QUIESCE_STATE", File.expand_path("~/.claude/hecks-dev/quiesce")).freeze
25
+
26
+ # How long a marker holds before it counts as left behind by a crash.
27
+ STALE_AFTER = Integer(ENV.fetch("HECKS_QUIESCE_STALE_SECONDS", 2 * 60 * 60))
28
+
29
+ # What a marker says: who holds the window, why and since when.
30
+ Hold = Struct.new(:owner, :reason, :held_at, keyword_init: true)
31
+
32
+ # @param path [String] the marker file
33
+ # @param stale_after [Integer] seconds after which a marker is ignored
34
+ # @param clock [#call] answers the current Time
35
+ def initialize(path: PATH, stale_after: STALE_AFTER, clock: -> { Time.now })
36
+ @path = path
37
+ @stale_after = stale_after
38
+ @clock = clock
39
+ end
40
+
41
+ # @return [Hold, nil] the marker's content, fresh or stale, or nil when there is none
42
+ def read
43
+ return unless File.exist?(@path)
44
+
45
+ data = JSON.parse(File.read(@path))
46
+ Hold.new(owner: data["owner"].to_s, reason: data["reason"].to_s, held_at: Time.iso8601(data["held_at"]))
47
+ rescue JSON::ParserError, ArgumentError, TypeError
48
+ Hold.new(owner: "unknown", reason: "unreadable marker", held_at: File.mtime(@path))
49
+ end
50
+
51
+ # @return [Hold, nil] the marker when it is present and not stale
52
+ def active
53
+ hold = read
54
+ hold if hold && !stale?(hold)
55
+ end
56
+
57
+ # @return [Hold, nil] the marker when it is present and past the staleness window
58
+ def stale
59
+ hold = read
60
+ hold if hold && stale?(hold)
61
+ end
62
+
63
+ # Writes the marker. A fresh marker of another owner is refused unless forced; the same
64
+ # owner's hold is refreshed, and a stale one is replaced.
65
+ #
66
+ # @return [Hold] what was written
67
+ # @raise [Refused] when another owner holds a fresh marker and `force` is false
68
+ def hold!(owner:, reason:, force: false)
69
+ current = active
70
+ if current && current.owner != owner && !force
71
+ raise Refused, "already held by #{current.owner} (#{describe(current)}); release it, or hold with force"
72
+ end
73
+
74
+ write(Hold.new(owner: owner, reason: reason, held_at: @clock.call))
75
+ end
76
+
77
+ # Removes the marker. Another owner's fresh marker stays unless forced.
78
+ #
79
+ # @return [Hold, nil] the marker removed, or nil when there was none
80
+ # @raise [Refused] when another owner holds a fresh marker and `force` is false
81
+ def release!(owner:, force: false)
82
+ current = read
83
+ return unless current
84
+ if current.owner != owner && !force && !stale?(current)
85
+ raise Refused, "held by #{current.owner} (#{describe(current)}), not #{owner}; release with force to remove it"
86
+ end
87
+
88
+ FileUtils.rm_f(@path)
89
+ current
90
+ end
91
+
92
+ # @return [String] a sentence on the marker: owner, reason and age, or that QA is not quiesced
93
+ def status
94
+ hold = read
95
+ return "not quiesced: no marker at #{@path}" unless hold
96
+
97
+ "#{stale?(hold) ? "stale (ignored)" : "quiesced"}: #{describe(hold)}"
98
+ end
99
+
100
+ # @return [String] "owner, reason, held 12 minutes ago"
101
+ def describe(hold)
102
+ "owner #{hold.owner}, reason #{hold.reason.inspect}, held #{age(hold)} ago (since #{hold.held_at.utc.iso8601})"
103
+ end
104
+
105
+ private
106
+
107
+ def stale?(hold) = (@clock.call - hold.held_at) > @stale_after
108
+
109
+ def age(hold)
110
+ seconds = (@clock.call - hold.held_at).to_i
111
+ return "#{seconds}s" if seconds < 120
112
+ return "#{seconds / 60}m" if seconds < 7200
113
+
114
+ "#{seconds / 3600}h"
115
+ end
116
+
117
+ def write(hold)
118
+ FileUtils.mkdir_p(File.dirname(@path))
119
+ temp = "#{@path}.#{Process.pid}"
120
+ File.write(temp, JSON.generate(owner: hold.owner, reason: hold.reason, held_at: hold.held_at.utc.iso8601))
121
+ File.rename(temp, @path)
122
+ hold
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest"
4
+
5
+ module Hecks
6
+ module Adapters
7
+ # The keys of the `RedSpec` rows a run reader files. A key is a readable slug of what is red
8
+ # and a digest of the exact text, so two spec paths never share a key: separators stay distinct
9
+ # (`__`), and paths differing only in case or punctuation differ in the digest.
10
+ module RunKeys
11
+ # The `.rb` ending of a spec path, left off its key.
12
+ RUBY_SUFFIX = ".rb"
13
+
14
+ # Runs of characters a Work key does not hold.
15
+ KEY_BREAK = /[^a-zA-Z0-9_]+/
16
+
17
+ # Hex digits of the exact text's digest a key ends with.
18
+ DIGEST_LENGTH = 10
19
+
20
+ module_function
21
+
22
+ # @param path [String] a spec path such as `spec/a/b_spec.rb`
23
+ # @return [String] `red-<readable slug>-<digest>`
24
+ def spec(path) = build(path.delete_suffix(RUBY_SUFFIX).gsub("/", "__"), path)
25
+
26
+ # @param job [String] the name of a spec job that failed with no failing example in its log
27
+ # @return [String] `red-job-<readable slug>-<digest>`
28
+ def job(job) = build("job-#{job}", job)
29
+
30
+ # @return [String] the key for a readable slug and the exact text it stands for
31
+ def build(slug, exact)
32
+ "red-#{slug.gsub(KEY_BREAK, "-").downcase}-#{Digest::SHA256.hexdigest(exact)[0, DIGEST_LENGTH]}"
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,7 @@
1
+ # Binds the `Reader` port to `RunReader`, which reads a CI run of main for the spec files it failed.
2
+ # Required by name, not relative, so a copy of this directory shares the one implementation loaded.
3
+ require "hecks/quality_control/adapters/run_reader"
4
+
5
+ Hecks.adapter "RunReader" do
6
+ port "Reader"
7
+ end
@@ -0,0 +1,188 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "open3"
5
+ require "time"
6
+ require "hecks/quality_control/adapters/run_keys"
7
+ require "hecks/runtime/asking"
8
+ require "hecks/runtime/errors"
9
+ require "hecks/runtime/row_sweep"
10
+ require "hecks/runtime/dispatcher"
11
+
12
+ module Hecks
13
+ module Adapters
14
+ # The `Reader` port's adapter: reads one CI run of main (`gh run view`, job logs) for the spec
15
+ # files it failed, and reports each to `RedSpec` through the boot whose `MainRun` asked. A file
16
+ # already red is updated, never opened twice. It answers the commit and whether the run settled,
17
+ # which the bluebook's policies use to clear the files the run did not fail.
18
+ #
19
+ # `gh` is the one thing it reaches for besides the clock; a spec stubs `Open3` with a fake run.
20
+ class RunReader
21
+ # A line of RSpec's "Failed examples:" summary as a job log prints it: the file, then the
22
+ # example's description.
23
+ # The path is read up to its `.rb` and the location that follows (`:12` or `[1:2]`), so a
24
+ # path with a space in it still matches; RSpec may quote it or escape the space.
25
+ FAILED_EXAMPLE = %r{rspec ['"]?(\./spec/.+?\.rb)(?:\[[^\]]*\]|:\d+)['"]?\s*(?:#\s*(.*))?$}
26
+
27
+ # Where a path's escaped space is unescaped.
28
+ ESCAPED_SPACE = "\\ "
29
+
30
+ # A job whose name says it runs specs.
31
+ SPEC_JOB = /spec/i
32
+
33
+ # The one conclusion that clears a red spec: the job ran its specs and they passed.
34
+ PASSED = "success"
35
+
36
+ # The branch and the event whose runs count: a push to main.
37
+ BRANCH = "main"
38
+ EVENT = "push"
39
+
40
+ # Job conclusions that are no verdict on the specs: the job did not run to an end.
41
+ UNFINISHED = [nil, "", "cancelled", "skipped", "neutral", "stale", "action_required"].freeze
42
+
43
+ # What `gh run view --json` is asked for.
44
+ JSON_FIELDS = "headSha,headBranch,event,createdAt,conclusion,jobs"
45
+
46
+ # Longest text the reader keeps of a failure line or the example list.
47
+ SHORT = 200
48
+
49
+ # @param aggregate [Object, nil] unused
50
+ # @param settings [Hash] unused
51
+ # @param root [String, nil] the checkout `gh` runs in; this repository when nil
52
+ def initialize(aggregate: nil, settings: {}, root: nil)
53
+ @root = root || File.expand_path("../../../..", __dir__)
54
+ end
55
+
56
+ # @param path [String] a spec path such as `spec/a/b_spec.rb`
57
+ # @return [String] the key of its row; see `RunKeys`
58
+ def self.key_for(path) = RunKeys.spec(path)
59
+
60
+ # @param run [Hash, String] the GitHub run id
61
+ # @param repo [Hash, String] `owner/name`
62
+ # @return [Hash] `commit:` and `settled:` ("yes" only when every spec job ended `success`)
63
+ # @raise [Runtime::GivenNotMet] when the run is not a push to main
64
+ def read(run:, repo:, **)
65
+ id = named(run)
66
+ view = JSON.parse(gh("run", "view", id, "--repo", named(repo), "--json", JSON_FIELDS))
67
+ refuse_unless_main!(view, id)
68
+ seen = { run: id, commit: view.fetch("headSha") }
69
+ file_failures(failures_in(gh("run", "view", id, "--repo", named(repo), "--log-failed")), view, seen)
70
+ { commit: { value: seen[:commit] }, settled: { value: green?(view, id) ? "yes" : "no" } }
71
+ end
72
+
73
+ private
74
+
75
+ def green?(view, id) = settled?(view) && !behind_a_row?(id)
76
+
77
+ # Reports each failing file; a failed job whose log named none is reported as a row
78
+ # of its own.
79
+ def file_failures(failures, view, seen)
80
+ failures.each { |path, examples| report(RunKeys.spec(path), path, examples, seen) }
81
+ report_unparsed(view, failures, seen)
82
+ end
83
+
84
+ def named(value) = value.is_a?(Hash) ? value.values.first.to_s : value.to_s
85
+
86
+ # Only a push to main is read: another branch's run, or a pull request's, says nothing
87
+ # of main.
88
+ def refuse_unless_main!(view, id)
89
+ return if view["headBranch"] == BRANCH && view["event"] == EVENT
90
+
91
+ raise Runtime::GivenNotMet, "run #{id} is #{view["event"].inspect} on #{view["headBranch"].inspect}, " \
92
+ "not a #{EVENT} to #{BRANCH}; only main's own runs are read"
93
+ end
94
+
95
+ def spec_jobs(view) = view.fetch("jobs", []).select { |job| job["name"].to_s.match?(SPEC_JOB) }
96
+
97
+ # Every job that runs specs ended `success`, and at least one did. A failed job with no
98
+ # failure parsed from its log is not green: the run clears nothing.
99
+ def settled?(view)
100
+ jobs = spec_jobs(view)
101
+ jobs.any? && jobs.all? { |job| job["conclusion"] == PASSED }
102
+ end
103
+
104
+ # A spec job that failed (or timed out) while the log named no failing example is reported as
105
+ # a row of its own, so the failure stays visible and open.
106
+ def report_unparsed(view, failures, seen)
107
+ return unless failures.empty?
108
+
109
+ spec_jobs(view).reject { |job| UNFINISHED.include?(job["conclusion"]) || job["conclusion"] == PASSED }.each do |job|
110
+ name = job["name"].to_s
111
+ report(RunKeys.job(name), "job #{name}", ["#{name} #{job["conclusion"]}: no failing example in its log"], seen,
112
+ title: "spec job #{name} failed on main with no failing example parsed")
113
+ end
114
+ end
115
+
116
+ # @return [Hash{String => Array<String>}] each failing spec file to its failing example texts
117
+ def failures_in(log)
118
+ log.each_line(chomp: true).with_object(Hash.new { |h, k| h[k] = [] }) do |line, found|
119
+ match = FAILED_EXAMPLE.match(line)
120
+ found[match[1].delete_prefix("./").gsub(ESCAPED_SPACE, " ")] << match[2].to_s.strip if match
121
+ end
122
+ end
123
+
124
+ def report(key, path, examples, seen, title: "#{path} is red on main")
125
+ facts = facts_of(examples, seen[:run], seen[:commit])
126
+ row = row_for(key)
127
+ if row
128
+ dispatch("QualityControl::RedSpec.Update", facts.merge(to: key)) unless older?(seen[:run], row)
129
+ else
130
+ open_or_reopen(key, path, title, facts)
131
+ end
132
+ end
133
+
134
+ # A file never seen red is opened; one cleared earlier already has its row, which reopens.
135
+ def open_or_reopen(key, path, title, facts)
136
+ facts = facts.merge(title: title, since: Time.now.utc.strftime("%Y-%m-%d"))
137
+ dispatch("QualityControl::RedSpec.Open", facts.merge(spec: key, path: path))
138
+ rescue Runtime::AlreadyExists
139
+ dispatch("QualityControl::RedSpec.Reopen", facts.merge(to: key))
140
+ end
141
+
142
+ # A run older than the one the row last saw does not overwrite what the row knows.
143
+ def older?(run, row) = run.to_i < last_run_of(row)
144
+
145
+ def last_run_of(row)
146
+ value = row[:last_run]
147
+ (value.respond_to?(:value) ? value.value : value).to_s.to_i
148
+ end
149
+
150
+ # Whether some red row was last seen by a newer run than this one: GitHub run ids grow, so a
151
+ # run behind a row says nothing of the file's state now and clears nothing.
152
+ def behind_a_row?(run) = open_rows.any? { |row| last_run_of(row) > run.to_i }
153
+
154
+ def facts_of(examples, run, commit)
155
+ count = examples.size
156
+ { failures: count, first_failure: short(examples.first.to_s), examples: short(examples.first(3).join(" | ")),
157
+ last_run: run, commit: commit,
158
+ detail: short("#{count} failing example#{"s" unless count == 1}; first: #{examples.first}; run #{run}; sha #{commit}") }
159
+ end
160
+
161
+ def short(text) = text.length > SHORT ? "#{text[0, SHORT - 3]}..." : text
162
+
163
+ # The spec's row while it is red: a red row is updated, a green or absent one is opened.
164
+ def row_for(key) = open_rows.find { |row| row[:id].to_s == key }
165
+
166
+ def open_rows
167
+ sweep = Runtime::RowSweep.new(registry)
168
+ aggregate = sweep.query_aggregate("QualityControl", "RedSpec", verb: "RedSpec.OnMain")
169
+ sweep.rows("QualityControl", aggregate, "OnMain", {})
170
+ end
171
+
172
+ def dispatch(verb, args) = Runtime::Dispatcher.new(registry).dispatch_flat(verb, args)
173
+
174
+ def registry
175
+ Runtime::Asking.registry or raise Runtime::GivenNotMet, "a run is read only when its MainRun asks"
176
+ end
177
+
178
+ def gh(*args)
179
+ out, err, status = Open3.capture3("gh", *args, chdir: @root)
180
+ raise Runtime::GivenNotMet, "gh #{args.first(3).join(" ")} failed: #{err.lines.first&.strip}" unless status.success?
181
+
182
+ out
183
+ rescue Errno::ENOENT
184
+ raise Runtime::GivenNotMet, "gh is not installed or not on PATH"
185
+ end
186
+ end
187
+ end
188
+ end
@@ -12,6 +12,7 @@ module Hecks
12
12
  # `hecks/quality_control/cli/<command>`.
13
13
  COMMANDS = {
14
14
  "qa_tick" => ["QaTick", :argv_root],
15
+ "qa_event_pump" => ["QaEventPump", :argv_root],
15
16
  "qa_sweep" => ["QaSweep", :argv_root],
16
17
  "qa_pr_check" => ["QaPrCheck", :argv_root],
17
18
  "qa_generated_domains" => ["QaGeneratedDomains", :argv_root],
@@ -86,7 +86,7 @@ module Hecks
86
86
  "pass --known-path <path> ... to run without the ledger", usage: false
87
87
  end
88
88
 
89
- JSON.parse(out).filter_map { |row| row.dig("path", "value") }
89
+ JSON.parse(out).filter_map { |row| row["path"] }
90
90
  end
91
91
 
92
92
  # The sibling directories under `projects_dir`, leaving out this repo (already fully covered