hecks 7.0.0 → 7.1.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 (257) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hecks/adapters/driven/postgres_era/lineage/chain_sql.rb +2 -1
  3. data/lib/hecks/adapters/driven/postgres_era/lineage/provisioning.rb +4 -2
  4. data/lib/hecks/adapters/driven/postgres_era/lineage/split_sql.rb +16 -8
  5. data/lib/hecks/adapters/driven/postgres_era/lineage/transform_sql.rb +7 -3
  6. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/era_resolver.rb +2 -1
  7. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/mint_request.rb +2 -1
  8. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/refusals.rb +2 -1
  9. data/lib/hecks/adapters/driving/cli_runner/bang_rule.rb +3 -0
  10. data/lib/hecks/adapters/driving/cli_runner/completion_words.rb +2 -0
  11. data/lib/hecks/adapters/driving/cli_runner/read_context.rb +6 -3
  12. data/lib/hecks/adapters/driving/cli_runner/renamed_names.rb +2 -1
  13. data/lib/hecks/adapters/driving/cli_runner/resolution.rb +2 -2
  14. data/lib/hecks/adapters/driving/cli_runner/resume_words.rb +1 -0
  15. data/lib/hecks/adapters/driving/cli_runner/run_reference.rb +6 -3
  16. data/lib/hecks/adapters/driving/cli_runner/search_words.rb +2 -0
  17. data/lib/hecks/adapters/driving/cli_runner/tour_words.rb +1 -0
  18. data/lib/hecks/adapters/driving/cli_runner/verb_names.rb +2 -1
  19. data/lib/hecks/adapters/driving/cli_runner/verbosity.rb +2 -0
  20. data/lib/hecks/adapters/driving/cli_runner/word_help.rb +40 -0
  21. data/lib/hecks/adapters/driving/cli_runner.rb +2 -0
  22. data/lib/hecks/adapters/driving/mcp/progress_sink.rb +4 -2
  23. data/lib/hecks/adapters/driving/mcp.rb +2 -1
  24. data/lib/hecks/adapters/driving/mcp_scope/argument_policy.rb +2 -1
  25. data/lib/hecks/adapters/driving/ruby/aggregate_module/hooks.rb +28 -14
  26. data/lib/hecks/adapters/driving/ruby/aggregate_module.rb +4 -2
  27. data/lib/hecks/assembly.rb +18 -4
  28. data/lib/hecks/behaviors/expectations/adapter_selection.rb +82 -0
  29. data/lib/hecks/behaviors/expectations/boot.rb +12 -5
  30. data/lib/hecks/bluebook/acknowledgements.rb +1 -1
  31. data/lib/hecks/bluebook/command.rb +3 -2
  32. data/lib/hecks/bluebook/declaration.rb +2 -1
  33. data/lib/hecks/bluebook/dsl/adapter_builder.rb +9 -1
  34. data/lib/hecks/bluebook/dsl/bluebook_builder/metadata.rb +6 -4
  35. data/lib/hecks/bluebook/dsl/bluebook_builder/modules.rb +6 -3
  36. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/correlation_keys.rb +2 -1
  37. data/lib/hecks/bluebook/dsl/command_builder/folded_sets.rb +4 -2
  38. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +2 -1
  39. data/lib/hecks/bluebook/hecksagon.rb +6 -3
  40. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +9 -2
  41. data/lib/hecks/bluebook/meta_validator.rb +1 -1
  42. data/lib/hecks/bluebook/model_check/aggregate_checks.rb +2 -1
  43. data/lib/hecks/bluebook/model_check/classification_checks.rb +51 -0
  44. data/lib/hecks/bluebook/model_check/context_checks.rb +2 -0
  45. data/lib/hecks/bluebook/model_check/redundant_relationships.rb +2 -1
  46. data/lib/hecks/ci_speed/adapters/shard_partitioner.adapter +7 -0
  47. data/lib/hecks/ci_speed/adapters/shard_partitioner.rb +66 -0
  48. data/lib/hecks/ci_speed/adapters/timings_source.adapter +7 -0
  49. data/lib/hecks/ci_speed/adapters/timings_source.rb +68 -0
  50. data/lib/hecks/ci_speed/ci_speed.bluebook +111 -0
  51. data/lib/hecks/ci_speed/ci_speed.ports.hecksagon +22 -0
  52. data/lib/hecks/ci_workflows/ci_workflows.bluebook +50 -6
  53. data/lib/hecks/coder/coder.bluebook +3 -1
  54. data/lib/hecks/command_names/index.rb +1 -1
  55. data/lib/hecks/command_names/resolution.rb +15 -5
  56. data/lib/hecks/composer/toolbox.rb +2 -1
  57. data/lib/hecks/corpus.rb +4 -0
  58. data/lib/hecks/doc/reference/readme.rb +1 -1
  59. data/lib/hecks/framework_bluebooks.rb +3 -2
  60. data/lib/hecks/fuzzing/differential.rb +1 -1
  61. data/lib/hecks/fuzzing/era_boundary.rb +1 -1
  62. data/lib/hecks/fuzzing/isolated_boot/postgres_scratch.rb +1 -1
  63. data/lib/hecks/fuzzing/persistence_parity.rb +1 -1
  64. data/lib/hecks/fuzzing/target_capabilities.rb +1 -1
  65. data/lib/hecks/hecks/adapters/ci_runs.adapter +7 -0
  66. data/lib/hecks/hecks/adapters/ci_runs.rb +80 -0
  67. data/lib/hecks/hecks/adapters/claude_agent_list.rb +43 -0
  68. data/lib/hecks/hecks/adapters/claude_agents.rb +50 -19
  69. data/lib/hecks/hecks/adapters/claude_jobs.rb +36 -0
  70. data/lib/hecks/hecks/adapters/codebase/corpus_queries.rb +2 -1
  71. data/lib/hecks/hecks/adapters/codebase/minitest_runs.rb +4 -2
  72. data/lib/hecks/hecks/adapters/codebase/run_options.rb +2 -1
  73. data/lib/hecks/hecks/adapters/codebase/test_suite.rb +3 -19
  74. data/lib/hecks/hecks/adapters/dispatching/brief_text.rb +5 -3
  75. data/lib/hecks/hecks/adapters/dispatching/launcher.rb +2 -1
  76. data/lib/hecks/hecks/adapters/dispatching/plan.rb +8 -4
  77. data/lib/hecks/hecks/adapters/dispatching/settings.rb +2 -1
  78. data/lib/hecks/hecks/adapters/dispatching/settler.rb +4 -2
  79. data/lib/hecks/hecks/adapters/dispatching.rb +6 -3
  80. data/lib/hecks/hecks/adapters/doctor_reading.rb +4 -2
  81. data/lib/hecks/hecks/adapters/git_landing/carried.rb +42 -0
  82. data/lib/hecks/hecks/adapters/git_landing/finisher.rb +2 -1
  83. data/lib/hecks/hecks/adapters/git_landing/flagged.rb +32 -0
  84. data/lib/hecks/hecks/adapters/git_landing/tidy.rb +27 -14
  85. data/lib/hecks/hecks/adapters/git_landing.rb +30 -13
  86. data/lib/hecks/hecks/adapters/git_leftovers.adapter +7 -0
  87. data/lib/hecks/hecks/adapters/git_leftovers.rb +49 -0
  88. data/lib/hecks/hecks/adapters/git_main_pull.rb +2 -2
  89. data/lib/hecks/hecks/adapters/git_train.rb +9 -5
  90. data/lib/hecks/hecks/adapters/git_worktree_add.rb +8 -4
  91. data/lib/hecks/hecks/adapters/github_pull_requests.rb +2 -1
  92. data/lib/hecks/hecks/adapters/in_process_operations.rb +2 -1
  93. data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +1 -1
  94. data/lib/hecks/hecks/adapters/journal_store/readings/composition.rb +2 -1
  95. data/lib/hecks/hecks/adapters/journal_store/translation_write/shape_edge.rb +4 -2
  96. data/lib/hecks/hecks/adapters/journal_store/translation_write.rb +4 -2
  97. data/lib/hecks/hecks/adapters/landed_branches.rb +38 -0
  98. data/lib/hecks/hecks/adapters/main_boarding.adapter +7 -0
  99. data/lib/hecks/hecks/adapters/main_boarding.rb +85 -0
  100. data/lib/hecks/hecks/adapters/main_integration/checks.rb +63 -0
  101. data/lib/hecks/hecks/adapters/main_integration/command_runner.rb +32 -0
  102. data/lib/hecks/hecks/adapters/main_integration/range_guard.rb +49 -0
  103. data/lib/hecks/hecks/adapters/main_integration/repository.rb +83 -0
  104. data/lib/hecks/hecks/adapters/main_integration/runs.rb +51 -0
  105. data/lib/hecks/hecks/adapters/main_integration/train.rb +74 -0
  106. data/lib/hecks/hecks/adapters/main_integration.adapter +7 -0
  107. data/lib/hecks/hecks/adapters/main_integration.rb +77 -0
  108. data/lib/hecks/hecks/adapters/main_verification.adapter +7 -0
  109. data/lib/hecks/hecks/adapters/main_verification.rb +89 -0
  110. data/lib/hecks/hecks/adapters/push_recorder.adapter +7 -0
  111. data/lib/hecks/hecks/adapters/push_recorder.rb +78 -0
  112. data/lib/hecks/hecks/adapters/red_flag_guard.rb +62 -0
  113. data/lib/hecks/hecks/adapters/red_flag_marker.rb +101 -0
  114. data/lib/hecks/hecks/adapters/red_flag_report.rb +59 -0
  115. data/lib/hecks/hecks/adapters/red_flag_survey.rb +65 -0
  116. data/lib/hecks/hecks/adapters/red_flagging.adapter +7 -0
  117. data/lib/hecks/hecks/adapters/red_flagging.rb +98 -0
  118. data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +6 -1
  119. data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +6 -3
  120. data/lib/hecks/hecks/adapters/settings_reading/event_tap_row.rb +33 -0
  121. data/lib/hecks/hecks/adapters/settings_reading.rb +3 -1
  122. data/lib/hecks/hecks/codebase.behaviors +0 -6
  123. data/lib/hecks/hecks/custodian.behaviors +22 -1
  124. data/lib/hecks/hecks/delegate/delegate.bluebook +128 -15
  125. data/lib/hecks/hecks/deliver/deliver.bluebook +450 -2
  126. data/lib/hecks/hecks/environments/memory.world +5 -0
  127. data/lib/hecks/hecks/hecks.contextmap +8 -0
  128. data/lib/hecks/hecks/hecks.hecksagon +106 -6
  129. data/lib/hecks/hecks/hecks.world +9 -1
  130. data/lib/hecks/hecks/migrate/migrate.bluebook +6 -1
  131. data/lib/hecks/hecks/redflag/redflag.bluebook +249 -0
  132. data/lib/hecks/hecks/test/test.bluebook +0 -19
  133. data/lib/hecks/language/adapter.bluebook +13 -0
  134. data/lib/hecks/language/bluebook/vocabulary.bluebook +8 -9
  135. data/lib/hecks/language/sentence/sentence.bluebook +2 -0
  136. data/lib/hecks/pipeline/pipeline.bluebook +251 -0
  137. data/lib/hecks/pipeline/pipeline.hecksagon +17 -0
  138. data/lib/hecks/pipeline_board/adapters/cache.rb +43 -0
  139. data/lib/hecks/pipeline_board/adapters/pipeline_fill.rb +56 -0
  140. data/lib/hecks/pipeline_board/adapters/pipeline_git.rb +82 -0
  141. data/lib/hecks/pipeline_board/adapters/pipeline_github/runs.rb +66 -0
  142. data/lib/hecks/pipeline_board/adapters/pipeline_github.adapter +7 -0
  143. data/lib/hecks/pipeline_board/adapters/pipeline_github.rb +70 -0
  144. data/lib/hecks/pipeline_board/adapters/pipeline_jobs.adapter +7 -0
  145. data/lib/hecks/pipeline_board/adapters/pipeline_jobs.rb +87 -0
  146. data/lib/hecks/pipeline_board/adapters/pipeline_journal/chain.rb +40 -0
  147. data/lib/hecks/pipeline_board/adapters/pipeline_journal/tap.rb +75 -0
  148. data/lib/hecks/pipeline_board/adapters/pipeline_journal.adapter +7 -0
  149. data/lib/hecks/pipeline_board/adapters/pipeline_journal.rb +83 -0
  150. data/lib/hecks/pipeline_board/adapters/pipeline_stages/ci_tiles.rb +85 -0
  151. data/lib/hecks/pipeline_board/adapters/pipeline_stages/fanout.rb +73 -0
  152. data/lib/hecks/pipeline_board/adapters/pipeline_stages/gather.rb +57 -0
  153. data/lib/hecks/pipeline_board/adapters/pipeline_stages/metrics.rb +36 -0
  154. data/lib/hecks/pipeline_board/adapters/pipeline_stages/strip.rb +102 -0
  155. data/lib/hecks/pipeline_board/adapters/pipeline_stages/tile_kit.rb +57 -0
  156. data/lib/hecks/pipeline_board/adapters/pipeline_stages/work_tiles.rb +59 -0
  157. data/lib/hecks/pipeline_board/adapters/pipeline_stages.adapter +7 -0
  158. data/lib/hecks/pipeline_board/adapters/pipeline_stages.rb +84 -0
  159. data/lib/hecks/pipeline_board/adapters/pipeline_tickets.adapter +7 -0
  160. data/lib/hecks/pipeline_board/adapters/pipeline_tickets.rb +77 -0
  161. data/lib/hecks/pipeline_board/adapters/pipeline_web/page.html +292 -0
  162. data/lib/hecks/pipeline_board/adapters/pipeline_web/sequence.html +190 -0
  163. data/lib/hecks/pipeline_board/adapters/pipeline_web/server.rb +105 -0
  164. data/lib/hecks/pipeline_board/adapters/pipeline_web/snapshot.rb +57 -0
  165. data/lib/hecks/pipeline_board/adapters/pipeline_web/stream.rb +59 -0
  166. data/lib/hecks/pipeline_board/adapters/pipeline_web/wiring.html +536 -0
  167. data/lib/hecks/pipeline_board/adapters/pipeline_web.adapter +7 -0
  168. data/lib/hecks/pipeline_board/adapters/pipeline_web.rb +80 -0
  169. data/lib/hecks/pipeline_board/adapters/pipeline_wiring/details.rb +85 -0
  170. data/lib/hecks/pipeline_board/adapters/pipeline_wiring/graph.rb +120 -0
  171. data/lib/hecks/pipeline_board/adapters/pipeline_wiring.adapter +7 -0
  172. data/lib/hecks/pipeline_board/adapters/pipeline_wiring.rb +36 -0
  173. data/lib/hecks/pipeline_board/adapters/probe.rb +48 -0
  174. data/lib/hecks/pipeline_board/pipeline_board.bluebook +144 -0
  175. data/lib/hecks/pipeline_board/pipeline_board.hecksagon +12 -0
  176. data/lib/hecks/pipeline_board/pipeline_board.ports.hecksagon +47 -0
  177. data/lib/hecks/ports/persistence/binding_policy.rb +2 -1
  178. data/lib/hecks/ports/persistence/plugins/era/translation/compose.rb +4 -3
  179. data/lib/hecks/ports/persistence/repository_factory.rb +2 -1
  180. data/lib/hecks/projections/ci_pipeline/table.rb +2 -1
  181. data/lib/hecks/projections/deploy/lambda/stack/recipes.rb +2 -2
  182. data/lib/hecks/projections/undo.rb +2 -1
  183. data/lib/hecks/projector/cli_projector/command_help.rb +5 -4
  184. data/lib/hecks/projector/cli_projector/renames.rb +4 -2
  185. data/lib/hecks/projector/cli_projector/usage.rb +4 -3
  186. data/lib/hecks/projector/exporter/adapters.rb +3 -2
  187. data/lib/hecks/projector/narrate_projector/use_cases.rb +74 -0
  188. data/lib/hecks/projector/narrate_projector.rb +10 -3
  189. data/lib/hecks/quality_control/adapters/main_maintenance.rb +9 -5
  190. data/lib/hecks/quality_control/adapters/qa_auto_mint.rb +21 -2
  191. data/lib/hecks/quality_control/cli/qa_concurrency_racer.rb +1 -1
  192. data/lib/hecks/quality_control/cli/qa_domain_novelty.rb +2 -1
  193. data/lib/hecks/quality_control/cli/qa_fix_main_bug/publishing.rb +8 -4
  194. data/lib/hecks/quality_control/cli/qa_fix_main_bug.rb +10 -8
  195. data/lib/hecks/quality_control/cli/qa_generated_domains/promotion.rb +2 -2
  196. data/lib/hecks/quality_control/cli/qa_main_gate/gate_run.rb +6 -3
  197. data/lib/hecks/quality_control/cli/qa_main_gate/logging.rb +8 -4
  198. data/lib/hecks/quality_control/cli/qa_main_gate/shell.rb +4 -2
  199. data/lib/hecks/quality_control/cli/qa_main_gate/sweeping.rb +8 -4
  200. data/lib/hecks/quality_control/cli/qa_main_gate/worktree.rb +3 -2
  201. data/lib/hecks/quality_control/cli/qa_main_gate.rb +10 -5
  202. data/lib/hecks/quality_control/cli/qa_sweep/all_mode/children.rb +1 -1
  203. data/lib/hecks/quality_control/cli/qa_sweep/checks.rb +1 -1
  204. data/lib/hecks/quality_control/cli/qa_sweep.rb +1 -1
  205. data/lib/hecks/quality_control/cli/qa_tick.rb +6 -3
  206. data/lib/hecks/quality_control/quality_control.bluebook +11 -11
  207. data/lib/hecks/quality_control/quality_control.ports.hecksagon +6 -3
  208. data/lib/hecks/query_ir/touchpoints.rb +2 -1
  209. data/lib/hecks/refused.rb +2 -1
  210. data/lib/hecks/release/approval.rb +4 -2
  211. data/lib/hecks/runtime/event_log.rb +27 -0
  212. data/lib/hecks/runtime/event_tap.rb +106 -0
  213. data/lib/hecks/runtime/loader/boot_steps.rb +1 -1
  214. data/lib/hecks/runtime/loader.rb +35 -19
  215. data/lib/hecks/runtime/registry/durability_warnings.rb +12 -5
  216. data/lib/hecks/runtime/registry/runtime_state.rb +4 -1
  217. data/lib/hecks/runtime/registry/saga_anchor.rb +54 -0
  218. data/lib/hecks/runtime/registry/saga_durability_rule.rb +66 -0
  219. data/lib/hecks/runtime/registry/saga_persistence.rb +58 -12
  220. data/lib/hecks/runtime/registry/verification.rb +7 -3
  221. data/lib/hecks/runtime/saga_interpreter/lifecycle.rb +2 -1
  222. data/lib/hecks/runtime/saga_interpreter.rb +1 -1
  223. data/lib/hecks/rust_format.rb +2 -1
  224. data/lib/hecks/shape_ledger/edge_hygiene.rb +10 -5
  225. data/lib/hecks/shape_ledger.rb +1 -1
  226. data/lib/hecks/storehouse/audit.rb +2 -1
  227. data/lib/hecks/storehouse/identity.rb +6 -3
  228. data/lib/hecks/storehouse.rb +2 -1
  229. data/lib/hecks/tickets/bluebook/tickets.bluebook +5 -4
  230. data/lib/hecks/tools/ci_workflows.rb +2 -1
  231. data/lib/hecks/tools/document.rb +12 -1
  232. data/lib/hecks/tools/downstream_model_check.rb +4 -2
  233. data/lib/hecks/tools/fuzz_sweep/shards.rb +9 -30
  234. data/lib/hecks/tools/model_check/examination.rb +25 -2
  235. data/lib/hecks/tools/move_disables.rb +4 -2
  236. data/lib/hecks/tools/regeneration_run/scratch.rb +2 -1
  237. data/lib/hecks/tools/rspec_shard_files.rb +12 -15
  238. data/lib/hecks/tools/sequence_script.rb +1 -1
  239. data/lib/hecks/tools/shard_planner.rb +65 -0
  240. data/lib/hecks/tools/stage_progress.rb +2 -1
  241. data/lib/hecks/tools/stress_concurrency_specs.rb +2 -1
  242. data/lib/hecks/version.rb +1 -1
  243. data/lib/hecks/vocabulary.rb +8 -9
  244. data/lib/hecks.rb +5 -2
  245. data/prebuilt/{13c04bdc5970295b310f0a980d397ba722232c8da99cc1a0cdb383249921e871.marshal → 4b81646f769ae5b1e7457f9b11c48d8bc1093e96a1dd6fb61864065121ca6f01.marshal} +0 -0
  246. data/prebuilt/{e3857b77f47d9ecc1787b2e1f5136364f3cb3444ef4aea5479f3b98167affea2.marshal → 681890632e3f4a99646623dcae44af36b1bad66c515f5abf67a2460372b9c6fd.marshal} +0 -0
  247. data/rust/host/HECKS_RELEASE +1 -1
  248. data/rust/parser/src/emit.rs +5 -1
  249. data/rust/parser/src/ir.rs +2 -0
  250. data/rust/parser/src/keywords.rs +12 -0
  251. data/rust/parser/src/main.rs +1 -0
  252. data/rust/parser/src/parse/adapter.rs +1 -0
  253. data/rust/parser/src/phrases.rs +16 -0
  254. data/rust/parser/src/sentence/examples.rs +93 -0
  255. data/rust/parser/src/sentence/mod.rs +1 -0
  256. metadata +89 -5
  257. data/lib/hecks/tools/refresh_rspec_runtime_baseline.rb +0 -166
@@ -1898,6 +1898,18 @@ pub static KEYWORDS: &[KeywordRow] = &[
1898
1898
  resolves_via: "",
1899
1899
  disambiguator: "",
1900
1900
  },
1901
+ KeywordRow {
1902
+ word: "stays_inside",
1903
+ context: "Adapter",
1904
+ body: "none",
1905
+ inner: "",
1906
+ opens: "",
1907
+ fills: "stays_inside",
1908
+ status: "admitted",
1909
+ was: "",
1910
+ resolves_via: "",
1911
+ disambiguator: "",
1912
+ },
1901
1913
  KeywordRow {
1902
1914
  word: "data_translation",
1903
1915
  context: "File",
@@ -344,6 +344,7 @@ const COVERED_PAIRS: &[(&str, &str)] = &[
344
344
  ("answers_query", "Adapter"),
345
345
  ("runs", "Adapter"),
346
346
  ("provides", "Adapter"),
347
+ ("stays_inside", "Adapter"),
347
348
  ("world", "File"),
348
349
  ("realm", "World"),
349
350
  ("latest", "World"),
@@ -114,6 +114,7 @@ fn parse_body(
114
114
  1,
115
115
  )?);
116
116
  }
117
+ "stays_inside" => adapter.stays_inside = true,
117
118
  // A repeat replaces the earlier value, as the Ruby builder's setter does.
118
119
  "runs" => {
119
120
  adapter.runs = Some(super::positional_text(
@@ -1111,6 +1111,22 @@ pub static PHRASES: &[PhraseRow] = &[
1111
1111
  canonical: "yes",
1112
1112
  status: "admitted",
1113
1113
  },
1114
+ PhraseRow {
1115
+ id: "examples_goal",
1116
+ context: "Examples",
1117
+ pattern: "Goal: {goal:rest}",
1118
+ opens: "Goal",
1119
+ canonical: "yes",
1120
+ status: "admitted",
1121
+ },
1122
+ PhraseRow {
1123
+ id: "goal_example",
1124
+ context: "Goal",
1125
+ pattern: "Example: {title:rest}",
1126
+ opens: "Example",
1127
+ canonical: "yes",
1128
+ status: "admitted",
1129
+ },
1114
1130
  PhraseRow {
1115
1131
  id: "person_role",
1116
1132
  context: "Background",
@@ -148,6 +148,7 @@ impl Parser<'_> {
148
148
  match m.id {
149
149
  "background" => self.background(&m, &mut people)?,
150
150
  "example" => tests.extend(self.example(&m, declaration, &people)?),
151
+ "examples_goal" => tests.extend(self.goal(&m, declaration, &people)?),
151
152
  _ => return Err(self.unmeant(&m)),
152
153
  }
153
154
  }
@@ -177,6 +178,67 @@ impl Parser<'_> {
177
178
  Ok(())
178
179
  }
179
180
 
181
+ /// Reads one `Goal:` and the examples under it (ADR 0173). The goal names a `Background:`
182
+ /// person who pursues it; the first example is its main scenario and the others its
183
+ /// extensions. The tests compile as any example does; a goal adds only its two checks.
184
+ fn goal(
185
+ &mut self,
186
+ header: &Matched,
187
+ declaration: &ir::Bluebook,
188
+ people: &[Person],
189
+ ) -> ParseResult<Vec<TestOut>> {
190
+ let goal = header.text_of("goal");
191
+ if !people.iter().any(|p| pursues(&goal, &p.name)) {
192
+ let names: Vec<&str> = people.iter().map(|p| p.name.as_str()).collect();
193
+ return Err(self.error(
194
+ header.line,
195
+ header.col_of("goal"),
196
+ format!(
197
+ "a goal names the person who pursues it, one of the Background people ({})",
198
+ names.join(", ")
199
+ ),
200
+ ));
201
+ }
202
+ let mut scenarios: Vec<(usize, Vec<TestOut>)> = Vec::new();
203
+ let mut scope = Scope::inside(header.indent);
204
+ while let Some(step) = self.step(&mut scope, "Goal", false)? {
205
+ let Step::Phrase(m) = step else { continue };
206
+ match m.id {
207
+ "goal_example" => scenarios.push((m.line, self.example(&m, declaration, people)?)),
208
+ _ => return Err(self.unmeant(&m)),
209
+ }
210
+ }
211
+ let Some((main_line, main)) = scenarios.first() else {
212
+ return Err(self.error(header.line, 1, "this goal holds no `Example:`"));
213
+ };
214
+ if main.iter().any(|test| test.expect.refused.is_some()) {
215
+ return Err(self.error(
216
+ *main_line,
217
+ 1,
218
+ format!(
219
+ "goal_without_success: the main scenario of '{goal}' ends in a refusal, so nothing under the goal reaches it"
220
+ ),
221
+ ));
222
+ }
223
+ let path = main_path(main);
224
+ for (line, extension) in scenarios.iter().skip(1) {
225
+ if let Some(off) = extension
226
+ .iter()
227
+ .find(|test| !path.contains(&tested_verb(test)))
228
+ {
229
+ return Err(self.error(
230
+ *line,
231
+ 1,
232
+ format!(
233
+ "extension_off_path: this extension of '{goal}' tries {}, which its main scenario never does",
234
+ tested_verb(off)
235
+ ),
236
+ ));
237
+ }
238
+ }
239
+ Ok(scenarios.into_iter().flat_map(|(_, tests)| tests).collect())
240
+ }
241
+
180
242
  /// Reads one `Example:` and compiles it, once per row of its `Where:` table.
181
243
  fn example(
182
244
  &mut self,
@@ -779,6 +841,37 @@ impl Parser<'_> {
779
841
 
780
842
  /// Every event a command announces, including those announced by commands that policies run in
781
843
  /// answer to its events, and so on until nothing new follows.
844
+ /// Whether a goal's sentence begins with the person's name.
845
+ fn pursues(goal: &str, person: &str) -> bool {
846
+ goal.len() > person.len()
847
+ && goal[..person.len()].eq_ignore_ascii_case(person)
848
+ && goal[person.len()..].starts_with(' ')
849
+ }
850
+
851
+ /// A test's tested command as `Aggregate.Command`.
852
+ fn tested_verb(test: &TestOut) -> String {
853
+ format!("{}.{}", test.aggregate, test.command)
854
+ }
855
+
856
+ /// Every command a main scenario calls, its setups and its tested step, as `Aggregate.Command`.
857
+ fn main_path(main: &[TestOut]) -> Vec<String> {
858
+ main.iter()
859
+ .flat_map(|test| {
860
+ test.setups
861
+ .iter()
862
+ .map(|setup| {
863
+ setup
864
+ .verb
865
+ .rsplit("::")
866
+ .next()
867
+ .unwrap_or(&setup.verb)
868
+ .to_string()
869
+ })
870
+ .chain(std::iter::once(tested_verb(test)))
871
+ })
872
+ .collect()
873
+ }
874
+
782
875
  fn announced_by(declaration: &ir::Bluebook, command: &ir::Command) -> Vec<String> {
783
876
  let mut events: Vec<String> = command.emits.clone();
784
877
  let mut grew = true;
@@ -250,6 +250,7 @@ fn context_label(context: &str) -> &'static str {
250
250
  "ExamplesFile" => "the top of an examples file",
251
251
  "Examples" => "an examples file",
252
252
  "Background" => "a background",
253
+ "Goal" => "a goal",
253
254
  "Example" => "an example",
254
255
  "Step" => "a step",
255
256
  "Outcome" => "an outcome",
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hecks
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.0.0
4
+ version: 7.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Young
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-11-02 00:00:00.000000000 Z
11
+ date: 2026-11-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: prism
@@ -211,6 +211,7 @@ files:
211
211
  - lib/hecks/adapters/driving/cli_runner/tour_words.rb
212
212
  - lib/hecks/adapters/driving/cli_runner/verb_names.rb
213
213
  - lib/hecks/adapters/driving/cli_runner/verbosity.rb
214
+ - lib/hecks/adapters/driving/cli_runner/word_help.rb
214
215
  - lib/hecks/adapters/driving/command_request.rb
215
216
  - lib/hecks/adapters/driving/context_line.rb
216
217
  - lib/hecks/adapters/driving/exit_status.rb
@@ -246,6 +247,7 @@ files:
246
247
  - lib/hecks/behaviors.rb
247
248
  - lib/hecks/behaviors/dsl.rb
248
249
  - lib/hecks/behaviors/expectations.rb
250
+ - lib/hecks/behaviors/expectations/adapter_selection.rb
249
251
  - lib/hecks/behaviors/expectations/boot.rb
250
252
  - lib/hecks/behaviors/expectations/checks.rb
251
253
  - lib/hecks/behaviors/expectations/dispatching.rb
@@ -421,6 +423,7 @@ files:
421
423
  - lib/hecks/bluebook/model_check.rb
422
424
  - lib/hecks/bluebook/model_check/aggregate_checks.rb
423
425
  - lib/hecks/bluebook/model_check/ask_checks.rb
426
+ - lib/hecks/bluebook/model_check/classification_checks.rb
424
427
  - lib/hecks/bluebook/model_check/client_profile.rb
425
428
  - lib/hecks/bluebook/model_check/context_checks.rb
426
429
  - lib/hecks/bluebook/model_check/context_map_checks.rb
@@ -492,6 +495,10 @@ files:
492
495
  - lib/hecks/ci_speed/adapters/local_runner/refused.rb
493
496
  - lib/hecks/ci_speed/adapters/local_runner/verdict.rb
494
497
  - lib/hecks/ci_speed/adapters/replayed_runner.rb
498
+ - lib/hecks/ci_speed/adapters/shard_partitioner.adapter
499
+ - lib/hecks/ci_speed/adapters/shard_partitioner.rb
500
+ - lib/hecks/ci_speed/adapters/timings_source.adapter
501
+ - lib/hecks/ci_speed/adapters/timings_source.rb
495
502
  - lib/hecks/ci_speed/ci_speed.behaviors
496
503
  - lib/hecks/ci_speed/ci_speed.bluebook
497
504
  - lib/hecks/ci_speed/ci_speed.hecksagon
@@ -755,8 +762,12 @@ files:
755
762
  - lib/hecks/hecks/adapter_words.json
756
763
  - lib/hecks/hecks/adapters/area_overlap.rb
757
764
  - lib/hecks/hecks/adapters/bluebook_report.rb
765
+ - lib/hecks/hecks/adapters/ci_runs.adapter
766
+ - lib/hecks/hecks/adapters/ci_runs.rb
767
+ - lib/hecks/hecks/adapters/claude_agent_list.rb
758
768
  - lib/hecks/hecks/adapters/claude_agents.adapter
759
769
  - lib/hecks/hecks/adapters/claude_agents.rb
770
+ - lib/hecks/hecks/adapters/claude_jobs.rb
760
771
  - lib/hecks/hecks/adapters/claude_transcript.rb
761
772
  - lib/hecks/hecks/adapters/codebase/batch_runs.rb
762
773
  - lib/hecks/hecks/adapters/codebase/cache_clearing.rb
@@ -843,7 +854,9 @@ files:
843
854
  - lib/hecks/hecks/adapters/git_commit.rb
844
855
  - lib/hecks/hecks/adapters/git_landing.adapter
845
856
  - lib/hecks/hecks/adapters/git_landing.rb
857
+ - lib/hecks/hecks/adapters/git_landing/carried.rb
846
858
  - lib/hecks/hecks/adapters/git_landing/finisher.rb
859
+ - lib/hecks/hecks/adapters/git_landing/flagged.rb
847
860
  - lib/hecks/hecks/adapters/git_landing/foreign_hold.rb
848
861
  - lib/hecks/hecks/adapters/git_landing/line.rb
849
862
  - lib/hecks/hecks/adapters/git_landing/lineup.rb
@@ -857,6 +870,8 @@ files:
857
870
  - lib/hecks/hecks/adapters/git_landing/waiting.rb
858
871
  - lib/hecks/hecks/adapters/git_launcher.adapter
859
872
  - lib/hecks/hecks/adapters/git_launcher.rb
873
+ - lib/hecks/hecks/adapters/git_leftovers.adapter
874
+ - lib/hecks/hecks/adapters/git_leftovers.rb
860
875
  - lib/hecks/hecks/adapters/git_main_pull.rb
861
876
  - lib/hecks/hecks/adapters/git_refs.rb
862
877
  - lib/hecks/hecks/adapters/git_rehearsal.adapter
@@ -938,6 +953,7 @@ files:
938
953
  - lib/hecks/hecks/adapters/journal_store/reconciliation.rb
939
954
  - lib/hecks/hecks/adapters/journal_store/translation_write.rb
940
955
  - lib/hecks/hecks/adapters/journal_store/translation_write/shape_edge.rb
956
+ - lib/hecks/hecks/adapters/landed_branches.rb
941
957
  - lib/hecks/hecks/adapters/landed_work.adapter
942
958
  - lib/hecks/hecks/adapters/landed_work.rb
943
959
  - lib/hecks/hecks/adapters/ledger.adapter
@@ -946,15 +962,35 @@ files:
946
962
  - lib/hecks/hecks/adapters/ledger/journal.rb
947
963
  - lib/hecks/hecks/adapters/local_files.adapter
948
964
  - lib/hecks/hecks/adapters/local_files.rb
965
+ - lib/hecks/hecks/adapters/main_boarding.adapter
966
+ - lib/hecks/hecks/adapters/main_boarding.rb
967
+ - lib/hecks/hecks/adapters/main_integration.adapter
968
+ - lib/hecks/hecks/adapters/main_integration.rb
969
+ - lib/hecks/hecks/adapters/main_integration/checks.rb
970
+ - lib/hecks/hecks/adapters/main_integration/command_runner.rb
971
+ - lib/hecks/hecks/adapters/main_integration/range_guard.rb
972
+ - lib/hecks/hecks/adapters/main_integration/repository.rb
973
+ - lib/hecks/hecks/adapters/main_integration/runs.rb
974
+ - lib/hecks/hecks/adapters/main_integration/train.rb
975
+ - lib/hecks/hecks/adapters/main_verification.adapter
976
+ - lib/hecks/hecks/adapters/main_verification.rb
949
977
  - lib/hecks/hecks/adapters/pg_admin.adapter
950
978
  - lib/hecks/hecks/adapters/pg_admin.rb
951
979
  - lib/hecks/hecks/adapters/pg_admin/ownership.rb
952
980
  - lib/hecks/hecks/adapters/process_pool.adapter
953
981
  - lib/hecks/hecks/adapters/process_pool.rb
982
+ - lib/hecks/hecks/adapters/push_recorder.adapter
983
+ - lib/hecks/hecks/adapters/push_recorder.rb
954
984
  - lib/hecks/hecks/adapters/push_size.rb
955
985
  - lib/hecks/hecks/adapters/qa_quiescing.adapter
956
986
  - lib/hecks/hecks/adapters/qa_quiescing.rb
957
987
  - lib/hecks/hecks/adapters/rails_app_files.rb
988
+ - lib/hecks/hecks/adapters/red_flag_guard.rb
989
+ - lib/hecks/hecks/adapters/red_flag_marker.rb
990
+ - lib/hecks/hecks/adapters/red_flag_report.rb
991
+ - lib/hecks/hecks/adapters/red_flag_survey.rb
992
+ - lib/hecks/hecks/adapters/red_flagging.adapter
993
+ - lib/hecks/hecks/adapters/red_flagging.rb
958
994
  - lib/hecks/hecks/adapters/rehearsal.rb
959
995
  - lib/hecks/hecks/adapters/rehearsal/audit_report.rb
960
996
  - lib/hecks/hecks/adapters/rehearsal/census.rb
@@ -976,6 +1012,7 @@ files:
976
1012
  - lib/hecks/hecks/adapters/settings_reading.adapter
977
1013
  - lib/hecks/hecks/adapters/settings_reading.rb
978
1014
  - lib/hecks/hecks/adapters/settings_reading/disposable_ledger.rb
1015
+ - lib/hecks/hecks/adapters/settings_reading/event_tap_row.rb
979
1016
  - lib/hecks/hecks/adapters/settings_reading/push_nudge.rb
980
1017
  - lib/hecks/hecks/adapters/settings_reading/stores.rb
981
1018
  - lib/hecks/hecks/adapters/settings_reading/test_scope.rb
@@ -1015,6 +1052,7 @@ files:
1015
1052
  - lib/hecks/hecks/migrate/migrate.bluebook
1016
1053
  - lib/hecks/hecks/operate/operate.bluebook
1017
1054
  - lib/hecks/hecks/quiesce/quiesce.bluebook
1055
+ - lib/hecks/hecks/redflag/redflag.bluebook
1018
1056
  - lib/hecks/hecks/rehearse/rehearse.bluebook
1019
1057
  - lib/hecks/hecks/release/release.bluebook
1020
1058
  - lib/hecks/hecks/serve/serve.bluebook
@@ -1065,6 +1103,47 @@ files:
1065
1103
  - lib/hecks/persistence_legacy_fixture.rb
1066
1104
  - lib/hecks/persistence_legacy_fixture/postgres_stores.rb
1067
1105
  - lib/hecks/persistence_legacy_fixture/regenerate.rb
1106
+ - lib/hecks/pipeline/pipeline.bluebook
1107
+ - lib/hecks/pipeline/pipeline.hecksagon
1108
+ - lib/hecks/pipeline_board/adapters/cache.rb
1109
+ - lib/hecks/pipeline_board/adapters/pipeline_fill.rb
1110
+ - lib/hecks/pipeline_board/adapters/pipeline_git.rb
1111
+ - lib/hecks/pipeline_board/adapters/pipeline_github.adapter
1112
+ - lib/hecks/pipeline_board/adapters/pipeline_github.rb
1113
+ - lib/hecks/pipeline_board/adapters/pipeline_github/runs.rb
1114
+ - lib/hecks/pipeline_board/adapters/pipeline_jobs.adapter
1115
+ - lib/hecks/pipeline_board/adapters/pipeline_jobs.rb
1116
+ - lib/hecks/pipeline_board/adapters/pipeline_journal.adapter
1117
+ - lib/hecks/pipeline_board/adapters/pipeline_journal.rb
1118
+ - lib/hecks/pipeline_board/adapters/pipeline_journal/chain.rb
1119
+ - lib/hecks/pipeline_board/adapters/pipeline_journal/tap.rb
1120
+ - lib/hecks/pipeline_board/adapters/pipeline_stages.adapter
1121
+ - lib/hecks/pipeline_board/adapters/pipeline_stages.rb
1122
+ - lib/hecks/pipeline_board/adapters/pipeline_stages/ci_tiles.rb
1123
+ - lib/hecks/pipeline_board/adapters/pipeline_stages/fanout.rb
1124
+ - lib/hecks/pipeline_board/adapters/pipeline_stages/gather.rb
1125
+ - lib/hecks/pipeline_board/adapters/pipeline_stages/metrics.rb
1126
+ - lib/hecks/pipeline_board/adapters/pipeline_stages/strip.rb
1127
+ - lib/hecks/pipeline_board/adapters/pipeline_stages/tile_kit.rb
1128
+ - lib/hecks/pipeline_board/adapters/pipeline_stages/work_tiles.rb
1129
+ - lib/hecks/pipeline_board/adapters/pipeline_tickets.adapter
1130
+ - lib/hecks/pipeline_board/adapters/pipeline_tickets.rb
1131
+ - lib/hecks/pipeline_board/adapters/pipeline_web.adapter
1132
+ - lib/hecks/pipeline_board/adapters/pipeline_web.rb
1133
+ - lib/hecks/pipeline_board/adapters/pipeline_web/page.html
1134
+ - lib/hecks/pipeline_board/adapters/pipeline_web/sequence.html
1135
+ - lib/hecks/pipeline_board/adapters/pipeline_web/server.rb
1136
+ - lib/hecks/pipeline_board/adapters/pipeline_web/snapshot.rb
1137
+ - lib/hecks/pipeline_board/adapters/pipeline_web/stream.rb
1138
+ - lib/hecks/pipeline_board/adapters/pipeline_web/wiring.html
1139
+ - lib/hecks/pipeline_board/adapters/pipeline_wiring.adapter
1140
+ - lib/hecks/pipeline_board/adapters/pipeline_wiring.rb
1141
+ - lib/hecks/pipeline_board/adapters/pipeline_wiring/details.rb
1142
+ - lib/hecks/pipeline_board/adapters/pipeline_wiring/graph.rb
1143
+ - lib/hecks/pipeline_board/adapters/probe.rb
1144
+ - lib/hecks/pipeline_board/pipeline_board.bluebook
1145
+ - lib/hecks/pipeline_board/pipeline_board.hecksagon
1146
+ - lib/hecks/pipeline_board/pipeline_board.ports.hecksagon
1068
1147
  - lib/hecks/ports.rb
1069
1148
  - lib/hecks/ports/access_control.port
1070
1149
  - lib/hecks/ports/access_control.rb
@@ -1590,6 +1669,7 @@ files:
1590
1669
  - lib/hecks/projector/narrate_projector.rb
1591
1670
  - lib/hecks/projector/narrate_projector/commands.rb
1592
1671
  - lib/hecks/projector/narrate_projector/reactions.rb
1672
+ - lib/hecks/projector/narrate_projector/use_cases.rb
1593
1673
  - lib/hecks/projector/target.rb
1594
1674
  - lib/hecks/quality_control/adapters/agent.adapter
1595
1675
  - lib/hecks/quality_control/adapters/agent.rb
@@ -1814,6 +1894,8 @@ files:
1814
1894
  - lib/hecks/runtime/entity_interpreter/saving.rb
1815
1895
  - lib/hecks/runtime/errors.rb
1816
1896
  - lib/hecks/runtime/event.rb
1897
+ - lib/hecks/runtime/event_log.rb
1898
+ - lib/hecks/runtime/event_tap.rb
1817
1899
  - lib/hecks/runtime/identity.rb
1818
1900
  - lib/hecks/runtime/instance.rb
1819
1901
  - lib/hecks/runtime/interpreting.rb
@@ -1866,6 +1948,8 @@ files:
1866
1948
  - lib/hecks/runtime/registry/query_verification.rb
1867
1949
  - lib/hecks/runtime/registry/read_repository.rb
1868
1950
  - lib/hecks/runtime/registry/runtime_state.rb
1951
+ - lib/hecks/runtime/registry/saga_anchor.rb
1952
+ - lib/hecks/runtime/registry/saga_durability_rule.rb
1869
1953
  - lib/hecks/runtime/registry/saga_persistence.rb
1870
1954
  - lib/hecks/runtime/registry/verification.rb
1871
1955
  - lib/hecks/runtime/registry/world_defaults.rb
@@ -2070,7 +2154,6 @@ files:
2070
2154
  - lib/hecks/tools/query_ir_run.rb
2071
2155
  - lib/hecks/tools/reference.rb
2072
2156
  - lib/hecks/tools/refresh_projections.rb
2073
- - lib/hecks/tools/refresh_rspec_runtime_baseline.rb
2074
2157
  - lib/hecks/tools/regeneration_run.rb
2075
2158
  - lib/hecks/tools/regeneration_run/pool.rb
2076
2159
  - lib/hecks/tools/regeneration_run/scratch.rb
@@ -2094,6 +2177,7 @@ files:
2094
2177
  - lib/hecks/tools/sequence_script.rb
2095
2178
  - lib/hecks/tools/serve_query_ir_mcp.rb
2096
2179
  - lib/hecks/tools/shape.rb
2180
+ - lib/hecks/tools/shard_planner.rb
2097
2181
  - lib/hecks/tools/site_routes.rb
2098
2182
  - lib/hecks/tools/site_routes/cli.rb
2099
2183
  - lib/hecks/tools/site_routes/editor_files.rb
@@ -2152,8 +2236,8 @@ files:
2152
2236
  - lib/rubocop/cop/hecks/unnamed_pipeline.rb
2153
2237
  - lib/rubocop/cop/hecks/untyped_raise.rb
2154
2238
  - lib/rubocop/cop/hecks/value_object_shorthand.rb
2155
- - prebuilt/13c04bdc5970295b310f0a980d397ba722232c8da99cc1a0cdb383249921e871.marshal
2156
- - prebuilt/e3857b77f47d9ecc1787b2e1f5136364f3cb3444ef4aea5479f3b98167affea2.marshal
2239
+ - prebuilt/4b81646f769ae5b1e7457f9b11c48d8bc1093e96a1dd6fb61864065121ca6f01.marshal
2240
+ - prebuilt/681890632e3f4a99646623dcae44af36b1bad66c515f5abf67a2460372b9c6fd.marshal
2157
2241
  - qa/settings.yml
2158
2242
  - rust/.cargo/config.toml
2159
2243
  - rust/.cargo/mutants.toml
@@ -1,166 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "fileutils"
4
- require "json"
5
- require "open3"
6
- require "yaml"
7
-
8
- module Hecks
9
- module Tools
10
- # The command behind `hecks refresh_runtime_baseline`: regenerates the committed runtime
11
- # baselines the sharded CI jobs group from. It prefers one CI run's timings; a local run is
12
- # the fallback.
13
- #
14
- # hecks refresh_runtime_baseline --from-run <run-id> # preferred: one CI run's timings
15
- # hecks refresh_runtime_baseline [workers] # fallback: this machine only
16
- #
17
- # `--from-run` reads the runtime-log artifacts of a successful run and writes a family only
18
- # when every leg uploaded one; a partial set would skew the split. Runner seconds differ from a
19
- # laptop's, so a local baseline misses subprocess-heavy specs. The local mode refuses a red run.
20
- module RefreshRspecRuntimeBaseline
21
- REPO = "heckslabs/hecks"
22
- USAGE = "usage: hecks refresh_runtime_baseline --from-run <run-id> | [workers]"
23
-
24
- # Only `spec/..._spec.rb:<seconds>` lines; RSpec's "Run options" line must not be copied
25
- # into a committed file. Seconds can arrive in exponent form (`9.69e-05`).
26
- RUNTIME_LINE = %r{\A(?<file>spec/\S+_spec\.rb):(?<seconds>\d+(?:\.\d+)?(?:e-?\d+)?)\z}
27
-
28
- # What a `--from-run` refresh works with: the checkout, the CI run, where its artifacts
29
- # download to, and where progress goes.
30
- Download = Struct.new(:root, :run_id, :dir, :out)
31
-
32
- module_function
33
-
34
- # Refreshes the baselines from a CI run or a local run.
35
- #
36
- # @param argv [Array<String>] `--from-run RUN_ID`, or optionally the local worker count
37
- # @param root [String] the checkout whose `.github/` baselines are rewritten
38
- # @param out [IO] where what was written goes
39
- # @return [Integer] the exit status, 0 once written
40
- # @raise [SystemExit] when the run is unreadable or unsuccessful, the local suite is red, or
41
- # no artifact family is complete
42
- def call(argv, root:, out: $stdout)
43
- if argv.first == "--from-run"
44
- from_run(root, argv.fetch(1) { abort USAGE }, out)
45
- else
46
- from_local(root, argv.fetch(0) { abort USAGE }, out)
47
- end
48
- 0
49
- end
50
-
51
- # Reads the leg counts from the workflow matrix so a resize cannot leave a stale count here.
52
- #
53
- # @param root [String] the checkout
54
- # @return [Hash{String => Hash}] each artifact family's leg count and baseline file
55
- def families(root)
56
- {
57
- "rspec-runtime-group" => { legs: matrix_legs(root, "ci-rspec.yml", "rspec_shard"),
58
- baseline: ".github/rspec_runtime_baseline.log" },
59
- "postgres-io-runtime-group" => { legs: matrix_legs(root, "ci-postgres-io-parallel.yml",
60
- "rspec_postgres_io_parallel_shard"),
61
- baseline: ".github/postgres_io_runtime_baseline.log" }
62
- }
63
- end
64
-
65
- # @param root [String] the checkout
66
- # @param workflow [String] the workflow file's name
67
- # @param job [String] the job whose matrix is counted
68
- # @return [Integer] how many groups the job's matrix has
69
- def matrix_legs(root, workflow, job)
70
- YAML.load_file(File.join(root, ".github/workflows", workflow)).dig("jobs", job, "strategy", "matrix", "group").size
71
- end
72
-
73
- # @param paths [Array<String>] runtime logs
74
- # @return [Array<String>] one sorted `file:seconds` line per spec file
75
- def baseline_lines(paths)
76
- seconds_by_file = paths.flat_map { |path| File.readlines(path) }.filter_map do |line|
77
- match = RUNTIME_LINE.match(line.strip) or next
78
- [match[:file], Float(match[:seconds])]
79
- end.to_h
80
- seconds_by_file.map { |file, seconds| format("%<file>s:%<seconds>.2f", file: file, seconds: seconds) }.sort
81
- end
82
-
83
- # @param root [String] the checkout
84
- # @param relative [String] the baseline's path under the checkout
85
- # @param lines [Array<String>] the baseline's lines
86
- # @param out [IO] where the note goes
87
- # @return [void]
88
- def write_baseline(root, relative, lines, out)
89
- File.write(File.join(root, relative), "#{lines.join("\n")}\n")
90
- out.puts "wrote #{relative}: #{lines.size} spec files"
91
- end
92
-
93
- # @param run_id [String] the CI run's id
94
- # @return [void]
95
- # @raise [SystemExit] when `gh` cannot read the run or it did not succeed
96
- def refuse_unless_successful!(run_id)
97
- out, status = Open3.capture2("gh", "run", "view", run_id, "-R", REPO, "--json", "conclusion")
98
- abort "hecks refresh_runtime_baseline: could not read run #{run_id}" unless status.success?
99
-
100
- conclusion = JSON.parse(out)["conclusion"]
101
- return if conclusion == "success"
102
-
103
- abort "hecks refresh_runtime_baseline: run #{run_id} concluded #{conclusion.inspect} — " \
104
- "refusing to write a baseline from anything but a successful run"
105
- end
106
-
107
- # @param root [String] the checkout
108
- # @param run_id [String] the CI run's id
109
- # @param out [IO] where what was written goes
110
- # @return [void]
111
- # @raise [SystemExit] when no family of artifacts is complete
112
- def from_run(root, run_id, out)
113
- refuse_unless_successful!(run_id)
114
- dir = File.join(root, "tmp/ci-runtime-#{run_id}")
115
- FileUtils.rm_rf(dir)
116
-
117
- download = Download.new(root, run_id, dir, out)
118
- written = families(root).count { |prefix, family| refresh_family(download, prefix, family) }
119
- return unless written.zero?
120
-
121
- abort "hecks refresh_runtime_baseline: run #{run_id} had no complete artifact family — nothing written"
122
- end
123
-
124
- # Downloads one artifact family and writes its baseline when every leg uploaded a log.
125
- #
126
- # @api private
127
- # @return [Boolean] whether the family was complete and written
128
- def refresh_family(download, prefix, family)
129
- logs = download_logs(download, prefix)
130
- legs = logs.map { |path| File.basename(File.dirname(path)) }.uniq.size
131
- complete = legs == family[:legs]
132
- if complete
133
- write_baseline(download.root, family[:baseline], baseline_lines(logs), download.out)
134
- else
135
- warn "skipping #{family[:baseline]}: run #{download.run_id} has #{legs} of #{family[:legs]} #{prefix}-* artifacts"
136
- end
137
- complete
138
- end
139
-
140
- # @api private
141
- def download_logs(download, prefix)
142
- system("gh", "run", "download", download.run_id, "-R", REPO, "-p", "#{prefix}-*", "-D", download.dir,
143
- out: File::NULL, err: File::NULL)
144
- Dir[File.join(download.dir, "#{prefix}-*", "*.log")]
145
- end
146
-
147
- # @param root [String] the checkout
148
- # @param workers [String] how many `parallel_rspec` workers to time with
149
- # @param out [IO] where what was written goes
150
- # @return [void]
151
- # @raise [SystemExit] when the local suite is red
152
- def from_local(root, workers, out)
153
- raw = "tmp/rspec_runtime_baseline.raw.log"
154
- FileUtils.mkdir_p(File.join(root, "tmp"))
155
- FileUtils.rm_f(File.join(root, raw))
156
-
157
- formatters = "--tag ~io --tag ~fuzzing --format progress --format ParallelTests::RSpec::RuntimeLogger --out #{raw}"
158
- green = system("bundle", "exec", "parallel_rspec", "spec", "-n", workers, "-o", formatters, chdir: root)
159
- abort "hecks refresh_runtime_baseline: the spec run failed — not writing a baseline from a red suite" unless green
160
-
161
- write_baseline(root, families(root).fetch("rspec-runtime-group")[:baseline],
162
- baseline_lines([File.join(root, raw)]), out)
163
- end
164
- end
165
- end
166
- end