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
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "git"
4
+ require_relative "console_capture"
5
+ require_relative "red_flag_marker"
6
+ require_relative "red_flag_report"
7
+ require_relative "red_flag_survey"
8
+
9
+ module Hecks
10
+ module Adapters
11
+ # The `Flagging` port's adapter for `RedFlag`: raises, notes, checks in to and lowers the red
12
+ # flag of a repository, reads its status, and surveys the latest CI run of main to raise or
13
+ # lower it. The flag is `RedFlagMarker`'s file; nothing else is touched.
14
+ class RedFlagging
15
+ # The CI run id in a report such as `abc: CI run 12 failure; failed jobs: a, b`.
16
+ RUN_ID = /run (\d+)/
17
+ # The failed jobs in that report.
18
+ JOBS = /failed jobs: (.*)\z/
19
+
20
+ # @param aggregate [Object, nil] unused
21
+ # @param settings [Hash] unused
22
+ # @param root [String, nil] unused
23
+ # @param tune [Hash] `checkout:` the repository, `clock:` and `runner:` (for a spec)
24
+ def initialize(aggregate: nil, settings: {}, root: nil, **tune)
25
+ @tune = tune
26
+ end
27
+
28
+ # @param held [Hash] the `RedFlag` record: `commit`, `detail`, `rows`, `checkout`
29
+ # @return [Hash{Symbol => Hash}] `outcome:` (`raised` or `noted`) and the flag's facts
30
+ def hoist(**held)
31
+ detail = pick(held, :detail, :report).to_s
32
+ facts = { ci_run: detail[RUN_ID, 1], jobs: detail[JOBS, 1], rows: plain(held[:rows]) }
33
+ state, flag = marker(held).raise!(commit: pick(held, :commit, :landed).to_s, run: facts)
34
+ answer(state.to_s, flag, facts)
35
+ end
36
+
37
+ # @param held [Hash] the `RedFlag` record: `session`, `taking`, `checkout`
38
+ # @return [Hash{Symbol => Hash}] `outcome:` (`checked_in`) and the flag's facts
39
+ # @raise [ConsoleCapture::Failure] when no flag stands
40
+ def sign_in(**held)
41
+ flag = marker(held).check_in!(session: plain(held[:session]).to_s, taking: plain(held[:taking]).to_s)
42
+ raise ConsoleCapture::Failure, "no red flag stands: nothing to check in to" unless flag
43
+
44
+ answer("checked_in", flag, session: plain(held[:session]), taking: plain(held[:taking]))
45
+ end
46
+
47
+ # @param held [Hash] the `RedFlag` record: `checkout`
48
+ # @return [Hash{Symbol => Hash}] `outcome:` (`cleared`, or `idle` when none stood)
49
+ def lower(**held)
50
+ flag = marker(held).clear!
51
+ flag ? answer("cleared", flag, {}) : { outcome: { value: "idle" }, output: { value: "no red flag was standing" } }
52
+ end
53
+
54
+ # Reads the latest completed CI run of main and raises or lowers the flag to match it.
55
+ #
56
+ # @param held [Hash] the `RedFlag` record: `checkout`
57
+ # @return [Hash{Symbol => Hash}] `outcome:` as {#hoist} and {#lower}, or `idle` while a run is
58
+ # still going
59
+ def survey(**held)
60
+ seen = RedFlagSurvey.new(runner: @tune[:runner], checkout: checkout(held)).latest
61
+ return hoist(commit: seen[:commit], detail: seen[:detail]) if seen[:verdict] == :red
62
+ return lower(**held) if seen[:verdict] == :green
63
+
64
+ { outcome: { value: "idle" }, output: { value: seen[:detail] } }
65
+ end
66
+
67
+ # @param held [Hash] `checkout`
68
+ # @return [Hash{Symbol => String}] `text:` the flag in words, or that none stands
69
+ def status(**held)
70
+ flag = marker(held).read
71
+ { text: flag ? "#{RedFlagReport.new(flag).text}\n" : "main is not flagged\n" }
72
+ end
73
+
74
+ private
75
+
76
+ def answer(outcome, flag, facts)
77
+ { outcome: { value: outcome }, flag: { value: flag["id"] }, commit: { value: flag["commit"].to_s },
78
+ ci_run: { value: facts[:ci_run].to_s }, jobs: { value: facts[:jobs].to_s }, rows: { value: facts[:rows].to_s },
79
+ session: { value: facts[:session].to_s }, taking: { value: facts[:taking].to_s } }
80
+ end
81
+
82
+ def marker(held) = RedFlagMarker.new(git_dir(held), **@tune.slice(:clock))
83
+
84
+ def git_dir(held)
85
+ ok = Git.new.capture("rev-parse", "--path-format=absolute", "--git-common-dir", chdir: checkout(held))
86
+ raise ConsoleCapture::Failure, "#{checkout(held)} is not a repository" unless ok.ok?
87
+
88
+ ok.out.strip
89
+ end
90
+
91
+ def checkout(held) = plain(held[:checkout]) || @tune[:checkout] || Dir.pwd
92
+
93
+ def pick(held, *keys) = plain(held[keys.find { |key| held.key?(key) }])
94
+
95
+ def plain(argument) = argument.is_a?(Hash) ? argument[:value] : argument
96
+ end
97
+ end
98
+ end
@@ -82,7 +82,12 @@ module Hecks
82
82
  def answer(edge, report, refused, unchecked)
83
83
  rows, total = Rows.from(refused)
84
84
  edge_facts(edge).merge(refused_rows: rows, refusal_total: plain(total), parity: report.parity,
85
- unchecked: unchecked).merge(report.facts)
85
+ unchecked: unchecked).merge(report.facts).merge(record_facts)
86
+ end
87
+
88
+ # The domain and the ledger watermark the copy held: what a mint's record of this rehearsal names.
89
+ def record_facts
90
+ { rehearsed_domain: @subject.bluebook_name, watermark: @store.watermark(@subject) }
86
91
  end
87
92
 
88
93
  def flag(value) = plain(value)
@@ -84,7 +84,8 @@ module Hecks
84
84
 
85
85
  private
86
86
 
87
- # A split target's "before": its source's held records, fanned out one per key the split names,
87
+ # A split target's "before": its source's held records, fanned out one per key the split
88
+ # names,
88
89
  # carrying the split's fields (ADR 0166). Nil when the aggregate is not a split target.
89
90
  def split_before(edge, aggregate)
90
91
  source, split = split_source_for(edge, aggregate)
@@ -100,7 +101,8 @@ module Hecks
100
101
  []
101
102
  end
102
103
 
103
- # One target record per source record that carries the split's key, holding the split's fields.
104
+ # One target record per source record that carries the split's key, holding the split's
105
+ # fields.
104
106
  def fanned_out(held, split)
105
107
  fields = split.fields.map(&:to_s)
106
108
  held.filter_map do |_id, state|
@@ -120,7 +122,8 @@ module Hecks
120
122
  held.filter_map { |_id, state| fanned_row(state, split.keyed_by.to_s, fields) }.to_h
121
123
  end
122
124
 
123
- # One target row keyed by the held record's key, a value object's `value` member when it has one.
125
+ # One target row keyed by the held record's key, a value object's `value` member when it has
126
+ # one.
124
127
  def fanned_row(state, key, fields)
125
128
  value = state[key]
126
129
  value = value["value"] if value.is_a?(Hash)
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hecks
4
+ module Adapters
5
+ class SettingsReading
6
+ # The `event tap` row of `hecks config`: where the runtime's event tap writes, and whether
7
+ # the `HECKS_EVENT_TAP` variable, the world or neither says.
8
+ module EventTapRow
9
+ # @param directory [String] the domain directory
10
+ # @return [Array(String, String, String)] the row's name, value and source
11
+ def event_tap(directory)
12
+ declared = declared_event_log(directory)
13
+ return ["event tap", "none declared", "default"] if declared.empty?
14
+ return ["event tap", "off", "world"] if declared[:path].to_s.empty?
15
+
16
+ override = Assembly.event_tap_override
17
+ source = override ? "variable #{Assembly::EVENT_TAP_VARIABLE}" : "world"
18
+ ["event tap", Runtime::EventTap.path_for(declared, override) || "off", source]
19
+ rescue StandardError => e
20
+ ["event tap", "could not be read", first_line(e.message)]
21
+ end
22
+
23
+ private
24
+
25
+ # The Hecks world's `event_log` settings, empty when it declares none.
26
+ def declared_event_log(directory)
27
+ world = Hecks.describe(directory).registry.world("Hecks")
28
+ world&.for_verb(Runtime::EventTap::VERB).to_h
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -7,6 +7,7 @@ require_relative "settings_reading/stores"
7
7
  require_relative "settings_reading/disposable_ledger"
8
8
  require_relative "settings_reading/push_nudge"
9
9
  require_relative "settings_reading/test_scope"
10
+ require_relative "settings_reading/event_tap_row"
10
11
  require_relative "work_limits"
11
12
 
12
13
  module Hecks
@@ -30,6 +31,7 @@ module Hecks
30
31
  include DisposableLedger
31
32
  include PushNudge
32
33
  include TestScope
34
+ include EventTapRow
33
35
 
34
36
  # The first line of a message.
35
37
  FIRST_LINE = /\A[^\n]*/
@@ -62,7 +64,7 @@ module Hecks
62
64
  schema, project_environment,
63
65
  variable_or_default("cache directory", "HECKS_CACHE_DIR", usage_cache_directory),
64
66
  variable_or_default("rust workspace", "HECKS_RUST_DIR", RustBuild.rust_dir),
65
- registry_source(directory), *stores(directory)]
67
+ event_tap(directory), registry_source(directory), *stores(directory)]
66
68
  end
67
69
 
68
70
  def line_of(name, value, source) = "#{name}: #{value} (#{source})"
@@ -207,12 +207,6 @@ Hecks.behaviors "Codebase" do
207
207
  expect emits: %w[ClearCachesRequested TestSuiteExamined TestSuiteAccepted TestSuiteAnswered TestSuiteCompleted]
208
208
  end
209
209
 
210
- test "RefreshRuntimeBaseline refuses a run that is not a number" do
211
- tests "RefreshRuntimeBaseline", on: "TestSuiteRun"
212
- input from_run: { value: "latest" }, run: { value: "baseline" }
213
- expect refused: "must match"
214
- end
215
-
216
210
  test "RegenerateLegacyFixtures reports what it would rewrite, and rewrites nothing, without confirm" do
217
211
  tests "RegenerateLegacyFixtures", on: "TestSuiteRun"
218
212
  input run: { value: "fixtures" }
@@ -66,7 +66,7 @@ Hecks.behaviors "Custodian" do
66
66
  test "Board keeps a checkout that is not there as a faulted push" do
67
67
  tests "Board", on: "SendTrain"
68
68
  input run: { value: "evening" }, checkout: { value: "/tmp/hecks-behaviors-absent/checkout" }, confirm: { value: true }
69
- expect emits: %w[BoardingRequested TrainRefused PushFaulted]
69
+ expect emits: %w[BoardingRequested TrainRefused PushFaulted OutcomeUnrecorded]
70
70
  end
71
71
 
72
72
  test "Land keeps a checkout that is not there as a refused landing" do
@@ -101,6 +101,27 @@ Hecks.behaviors "Custodian" do
101
101
  expect emits: %w[RetirementRequested RemovalRefused RetirementDeclined]
102
102
  end
103
103
 
104
+ test "Discard keeps a checkout that is not there as a spared worktree" do
105
+ tests "Discard", on: "Landing"
106
+ input run: { value: "evening" }, checkout: { value: "/tmp/hecks-behaviors-absent/checkout" },
107
+ reason: { value: "superseded" }, confirm: { value: true }
108
+ expect emits: %w[DiscardRequested DiscardRefused DiscardDeclined]
109
+ end
110
+
111
+ test "Discard needs confirm" do
112
+ tests "Discard", on: "Landing"
113
+ input run: { value: "evening" }, checkout: { value: "/tmp/hecks-behaviors-absent/checkout" },
114
+ reason: { value: "superseded" }
115
+ expect refused: "discards the branch's commits that main lacks needs --confirm; without it this only reports"
116
+ end
117
+
118
+ test "Discard needs a reason" do
119
+ tests "Discard", on: "Landing"
120
+ input run: { value: "evening" }, checkout: { value: "/tmp/hecks-behaviors-absent/checkout" },
121
+ reason: { value: "" }, confirm: { value: true }
122
+ expect refused: "a discard says why"
123
+ end
124
+
104
125
  test "Dispatch keeps a directory that is not there as a declined run" do
105
126
  tests "Dispatch", on: "AgentRun"
106
127
  input run: { value: "evening" }, repo: { value: "/tmp/hecks-behaviors-absent/repo" }, brief: { value: "say ok" }
@@ -7,9 +7,10 @@ Hecks.bluebook "Hecks" do
7
7
  # the givens and reactions below.
8
8
  #
9
9
  # The bluebook decides everything: a brief must say something, an agent name cannot be mistaken
10
- # for a flag, the directory must be one the `Agents` port finds, and a session that reports
11
- # `done` or `stopped` has finished while `busy` and `idle` have not. The port only looks and
12
- # starts: it finds a directory, starts the session, and reports the state the session is in.
10
+ # for a flag, the directory must be one the `Agents` port finds and the agent one it lists, and a
11
+ # job that reports `done`, `stopped` or `failed` has ended while `working` and `blocked` have
12
+ # not. The port only looks and starts: it finds the directory and the agents, starts the
13
+ # session, and reports the state its job is in.
13
14
 
14
15
  aggregate "AgentRun" do
15
16
  description "One brief handed to a background Claude session: started and tracked, or refused with the reason."
@@ -22,6 +23,7 @@ Hecks.bluebook "Hecks" do
22
23
  attribute :condition, Condition, optional: true
23
24
  attribute :refusal, Report, optional: true
24
25
  attribute :tidied_at, Moment, optional: true
26
+ attribute :detail, Report, optional: true
25
27
  identified_by :run
26
28
 
27
29
  value_object "RunKey" do
@@ -61,11 +63,18 @@ Hecks.bluebook "Hecks" do
61
63
  attribute :value, String
62
64
  end
63
65
 
66
+ # The directory a session works in, written with its trailing slash, so a watch that filters
67
+ # on a checkout's directory matches the checkout and every worktree under it.
68
+ value_object "Checkout" do
69
+ attribute :value, String
70
+ end
71
+
64
72
  lifecycle :status, default: "requested" do
65
73
  mark :failure, "refused"
66
74
  transition "Begin" => "started", from: "requested"
67
75
  transition "Decline" => "refused", from: "requested"
68
76
  transition "Finish" => "finished", from: "started"
77
+ transition "Fail" => "finished", from: "started"
69
78
  transition "Tidied" => "tidied", from: "finished"
70
79
  mark :terminal, "tidied", "refused"
71
80
  end
@@ -107,24 +116,63 @@ Hecks.bluebook "Hecks" do
107
116
  emits AgentRunDeclined
108
117
  end
109
118
 
119
+ # A check reads the job's own state and records a change as one of the four session events
120
+ # below; a state that did not change records nothing.
110
121
  command "Check", from: "started" do
111
122
  role "Operator"
112
- goal "Ask whether a started session is still working"
123
+ goal "Read the state a started session's job is in, and record it when it changed"
113
124
 
114
125
  reference_to AgentRun
115
126
 
116
127
  emits AgentCheckRequested
117
128
  end
118
129
 
130
+ command "Work", from: "started" do
131
+ role "System"
132
+ goal "Record that the session is working"
133
+
134
+ reference_to AgentRun
135
+ attribute :condition, Condition, sets: true
136
+ attribute :checkout, Checkout
137
+
138
+ emits SessionWorking
139
+ end
140
+
141
+ command "Block", from: "started" do
142
+ role "System"
143
+ goal "Record that the session waits on a person, and the question it waits on"
144
+
145
+ reference_to AgentRun
146
+ attribute :condition, Condition, sets: true
147
+ attribute :detail, Report, sets: true
148
+ attribute :checkout, Checkout
149
+
150
+ emits SessionBlocked
151
+ end
152
+
153
+ # `done` and `stopped` are a session's two ways of ending well; `failed` is `Fail`.
119
154
  command "Finish" do
120
155
  role "System"
121
156
  goal "Record that the session has ended, and how it ended"
122
157
 
123
158
  reference_to AgentRun
124
159
  attribute :condition, Condition, sets: true
160
+ attribute :detail, Report, sets: true
161
+ attribute :checkout, Checkout
125
162
 
163
+ emits SessionFinished
164
+ end
165
+
166
+ command "Fail" do
167
+ role "System"
168
+ goal "Record that the session failed, and what it said"
126
169
 
127
- emits AgentRunFinished
170
+ reference_to AgentRun
171
+ attribute :condition, Condition, sets: true
172
+ attribute :detail, Report, sets: true
173
+ attribute :checkout, Checkout
174
+
175
+ emits SessionFailed
128
176
  end
129
177
 
130
178
  # A finished run's session is removed only when it ended `done`; a session still `working`,
@@ -181,6 +229,28 @@ Hecks.bluebook "Hecks" do
181
229
  end
182
230
  end
183
231
 
232
+ # One check of every run still started, for a driver that watches sessions. The launcher also
233
+ # answers it as `agent_run.check_all!`.
234
+ aggregate "SessionSweep" do
235
+ description "One check of every started run's session: each change of state is recorded on its run."
236
+
237
+ attribute :run, SweepKey
238
+ identified_by :run
239
+
240
+ value_object "SweepKey" do
241
+ attribute :value, String, matches: :present
242
+ end
243
+
244
+ command "CheckAll" do
245
+ role "Operator"
246
+ goal "Check every run whose session was started and is not known to have ended"
247
+
248
+ attribute :run, SweepKey, sets: true
249
+
250
+ emits SweepRequested
251
+ end
252
+ end
253
+
184
254
  # Filling the Work queue is one run too: the Work rows that may start a session now are chosen
185
255
  # from the queue, the machine's load and the freeze, and without `confirm` that is all it does.
186
256
  # The choosing and the starting are the port's; whether a start is allowed stays with `Work`.
@@ -292,16 +362,33 @@ Hecks.bluebook "Hecks" do
292
362
  ask :locate, with: { run: :run }
293
363
  end
294
364
 
295
- policy "StartWhenTheRepoIsThere" do
365
+ # Locating the directory also says whether its `claude` lists the named agent, so a name it
366
+ # does not know is refused before a session is started; with no agent named, the session runs
367
+ # `claude`'s default. One look, not two: the reactions after a start stay within the depth limit.
368
+ policy "StartWhenTheAgentIsListed" do
296
369
  on "AgentRun.RepoLocated"
370
+ where { listed != false }
297
371
  ask :start, with: { run: :run }
298
372
  end
299
373
 
374
+ policy "RecordTheUnlistedAgent" do
375
+ on "AgentRun.RepoLocated"
376
+ where { listed == false }
377
+ trigger AgentRun::Decline, with: { refusal: :note }
378
+ end
379
+
300
380
  policy "RecordTheSession" do
301
381
  on "AgentRun.AgentStarted"
302
382
  trigger AgentRun::Begin, with: { session: :session }
303
383
  end
304
384
 
385
+ # A session can die within seconds of its start (an agent that fails to load). Its job is read
386
+ # once the port has let it settle, so a dead session is recorded failed, never left started.
387
+ policy "ReadTheNewSession" do
388
+ on "AgentRun.AgentRunStarted"
389
+ ask :await_job, with: { run: :run }
390
+ end
391
+
305
392
  policy "RecordTheMissingRepo" do
306
393
  on "AgentRun.RepoMissing"
307
394
  trigger AgentRun::Decline, with: { refusal: :refusal }
@@ -312,9 +399,42 @@ Hecks.bluebook "Hecks" do
312
399
  trigger AgentRun::Decline, with: { refusal: :refusal }
313
400
  end
314
401
 
315
- policy "AskTheSession" do
402
+ policy "ReadTheJob" do
316
403
  on "AgentRun.AgentCheckRequested"
317
- ask :examine, with: { run: :run }
404
+ ask :read_job, with: { run: :run }
405
+ end
406
+
407
+ # Each check is addressed by the row's key alone: the sweep's own `run` is not passed on.
408
+ policy "CheckEveryStartedRun" do
409
+ on "SessionSweep.SweepRequested"
410
+ for_each "AgentRun.Running"
411
+ trigger AgentRun::Check, with: { agent_run: :agent_run }
412
+ end
413
+
414
+ # A job's state becomes an event only when it differs from the state the run last recorded
415
+ # (`previous`), so a check that finds nothing new records nothing.
416
+ policy "RecordTheWorkingSession" do
417
+ on "AgentRun.JobRead"
418
+ where { state == "working" && previous != state }
419
+ trigger AgentRun::Work, with: { condition: :state, checkout: :checkout }
420
+ end
421
+
422
+ policy "RecordTheBlockedSession" do
423
+ on "AgentRun.JobRead"
424
+ where { state == "blocked" && previous != state }
425
+ trigger AgentRun::Block, with: { condition: :state, detail: :detail, checkout: :checkout }
426
+ end
427
+
428
+ policy "RecordTheEndedSession" do
429
+ on "AgentRun.JobRead"
430
+ where { (state == "done" || state == "stopped") && previous != state }
431
+ trigger AgentRun::Finish, with: { condition: :state, detail: :detail, checkout: :checkout }
432
+ end
433
+
434
+ policy "RecordTheFailedSession" do
435
+ on "AgentRun.JobRead"
436
+ where { state == "failed" && previous != state }
437
+ trigger AgentRun::Fail, with: { condition: :state, detail: :detail, checkout: :checkout }
318
438
  end
319
439
 
320
440
  # The session is looked at before it is removed; the port says whether a person took it over.
@@ -344,12 +464,5 @@ Hecks.bluebook "Hecks" do
344
464
  on "AgentRun.RemoveRefused"
345
465
  trigger AgentRun::Retain, with: { refusal: :refusal }
346
466
  end
347
-
348
- # `done` and `stopped` are a session's two ways of ending; `busy` and `idle` are not.
349
- policy "FinishWhenTheSessionEnded" do
350
- on "AgentRun.SessionChecked"
351
- where { condition == "done" || condition == "stopped" }
352
- trigger AgentRun::Finish, with: { condition: :condition }
353
- end
354
467
  end
355
468
  end