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,111 @@
1
+ require "delegate"
2
+
3
+ module Hecks
4
+ # The values a world hands an adapter under a `secret` declaration, held by name so any text a
5
+ # launcher prints can be scrubbed of them: a refusal, a boot note, a settings line, a result or
6
+ # an exception's message. The adapter that declared the secret gets the value; whatever a person
7
+ # or a log sees gets `«secret:NAME»`. Redaction follows the declaration, not a guess at a
8
+ # value's shape, with one addition: the password inside a `database` URL, which the URL
9
+ # carries without any adapter declaring it.
10
+ module Secrets
11
+ MASK = "«secret:%s»".freeze
12
+ PASSWORD = %r{://[^:/@\s]+:([^@\s]+)@}
13
+
14
+ @held = {}
15
+ @mutex = Mutex.new
16
+
17
+ module_function
18
+
19
+ # Holds one secret value under its name. An empty or non-String value is nothing to hide.
20
+ #
21
+ # @param name [#to_s] the declared name
22
+ # @param value [Object] the value the world supplied
23
+ # @return [void]
24
+ def register(name, value)
25
+ return unless value.is_a?(String) && !value.empty?
26
+
27
+ @mutex.synchronize { @held[value] = name.to_s }
28
+ end
29
+
30
+ # Holds every secret an adapter declares out of the settings it is built with, and the
31
+ # password of its `database` URL when the URL carries one.
32
+ #
33
+ # @param adapter [Bluebook::Adapter, nil] the adapter's declaration; nil holds nothing
34
+ # @param settings [Hash] the settings the world supplied for it
35
+ # @return [void]
36
+ def register_settings(adapter, settings)
37
+ Array(adapter&.secrets).each { |secret| register(secret, settings[secret.to_sym]) }
38
+ register_database(settings[:database])
39
+ end
40
+
41
+ # Holds the password a `scheme://user:password@host` URL carries, under a fixed name.
42
+ #
43
+ # @param url [Object] the database setting; anything but a URL with a password holds nothing
44
+ # @return [void]
45
+ def register_database(url)
46
+ return unless url.is_a?(String) && (found = url.match(PASSWORD))
47
+
48
+ register("database password", found[1])
49
+ end
50
+
51
+ # Replaces every held value in the text with its mask, longest value first so a value that
52
+ # contains another is masked whole.
53
+ #
54
+ # @param text [Object] what is about to be printed; a non-String is returned as it is
55
+ # @return [Object] the text with each held value masked
56
+ def redact(text)
57
+ return text unless text.is_a?(String)
58
+
59
+ held = @mutex.synchronize { @held.dup }
60
+ held.keys.sort_by { |value| -value.length }
61
+ .reduce(text) { |scrubbed, value| scrubbed.gsub(value) { format(MASK, held[value]) } }
62
+ end
63
+
64
+ # Forgets every held value, for a process that boots another domain.
65
+ # @return [void]
66
+ def clear! = @mutex.synchronize { @held.clear }
67
+
68
+ # Whether any value is held.
69
+ # @return [Boolean]
70
+ def any? = @mutex.synchronize { !@held.empty? }
71
+
72
+ # The stream, writing through `redact`; a stream already guarded is returned as it is.
73
+ #
74
+ # @param io [IO, #write] the stream to guard
75
+ # @return [Guarded] the guarded stream
76
+ def guarded(io) = io.is_a?(Guarded) ? io : Guarded.new(io)
77
+
78
+ # Guards the process's own output streams, so a `puts`, `warn` or `abort` anywhere in it
79
+ # prints no held value. A launcher calls it once, right after it loads the gem.
80
+ #
81
+ # @return [void]
82
+ def guard!
83
+ $stdout = guarded($stdout)
84
+ $stderr = guarded($stderr)
85
+ end
86
+
87
+ # A stream whose every write passes through `Secrets.redact`. A value split across two
88
+ # writes is not caught, which the one-call writes `puts`, `warn` and `abort` make never
89
+ # happens.
90
+ class Guarded < SimpleDelegator
91
+ # @param parts [Array<#to_s>] what is written
92
+ # @return [Integer] the bytes written
93
+ def write(*parts) = __getobj__.write(*parts.map { |part| Secrets.redact(part.to_s) })
94
+
95
+ # @param parts [Array<#to_s>] the lines; an Array prints one line per element
96
+ # @return [nil]
97
+ def puts(*parts) = __getobj__.puts(*parts.flatten.map { |part| Secrets.redact(part.to_s) })
98
+
99
+ # @param parts [Array<#to_s>] what is printed
100
+ # @return [nil]
101
+ def print(*parts) = __getobj__.print(*parts.map { |part| Secrets.redact(part.to_s) })
102
+
103
+ # @param part [#to_s] what is appended
104
+ # @return [Guarded] self
105
+ def <<(part)
106
+ write(part)
107
+ self
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,170 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Hecks
6
+ module ShapeLedger
7
+ # Finds the translation edges that no longer earn their file; it deletes nothing. The graph is
8
+ # a bluebook's edge files plus its recorded implicit moves, aimed at the current shape. A file is:
9
+ #
10
+ # - a **self-loop** when its `from` equals its `to`.
11
+ # - a **fork** branch when two edges leave a label and both reach the current shape; the branch
12
+ # with the fewest steps is kept.
13
+ # - an **orphan** when it lies on no path from a held era (the ledger label, an implicit move's
14
+ # start, or the start of a chain) to the current shape. A sole route is never flagged.
15
+ #
16
+ # @example
17
+ # EdgeHygiene.findings(dir, ledger: {}, shapes: { "Shop" => "bbbbbb1" }, moves: {})
18
+ module EdgeHygiene
19
+ # One edge: the file that declares it (nil for a recorded implicit move) and its two labels.
20
+ Edge = Struct.new(:path, :from, :to)
21
+
22
+ # The list beside the ledger of stale edges already known, each a finding key. A listed edge is
23
+ # reported as a warning and does not fail the check; any other stale edge fails it.
24
+ KNOWN_FILE = "known_stale_edges.json"
25
+
26
+ # One problem: its kind, the bluebook, the file to remove, a stable key naming the edge, and what
27
+ # to do.
28
+ Finding = Struct.new(:kind, :domain, :path, :key, :message, keyword_init: true)
29
+
30
+ module_function
31
+
32
+ # Raises on each stale edge the known list does not name, and warns of those it does.
33
+ #
34
+ # @param directory [String] the domain directory holding `translations/`
35
+ # @return [String] a warning line for each known stale edge, or empty
36
+ # @raise [ShapeLedger::Failure] naming each other stale edge and the `git rm` that clears it
37
+ def report(directory, **world)
38
+ known = known_stale(File.join(directory, KNOWN_FILE))
39
+ listed, fresh = findings(directory, **world).partition { |finding| known.include?(finding.key) }
40
+ raise Failure, lines(fresh).join("\n") unless fresh.empty?
41
+
42
+ listed.map { |finding| "\nwarning, known stale edge (#{KNOWN_FILE}) #{lines([finding]).first}" }.join
43
+ end
44
+
45
+ # @return [Array<String>] the finding keys of the known stale edges, empty when there is no list
46
+ def known_stale(path) = File.exist?(path) ? JSON.parse(File.read(path)) : []
47
+
48
+ # @param directory [String] the domain directory holding `translations/`
49
+ # @param ledger [Hash{String => String}] the committed shape ledger
50
+ # @param shapes [Hash{String => String}] each bluebook's current storage-shape hash
51
+ # @param moves [Hash{String => Array<Hash>}] the recorded implicit moves
52
+ # @return [Array<Finding>] every stale edge file, by bluebook
53
+ def findings(directory, ledger:, shapes:, moves:)
54
+ files = Translation::EdgeFiles.scan(directory)
55
+ shapes.flat_map do |name, hash|
56
+ recorded = moves.fetch(name, [])
57
+ domain_findings(name, edges_of(files, name) + implicit_edges(recorded), ShapeLedger.label(hash),
58
+ held_labels(ledger[name], recorded))
59
+ end
60
+ end
61
+
62
+ def edges_of(files, name)
63
+ files.select { |found| found.domain == name }.map { |f| Edge.new(f.path, f.from, f.to) }
64
+ end
65
+
66
+ # @return [Array<String>] one report line per finding, each telling what to do
67
+ def lines(findings) = findings.map { |finding| "#{finding.domain}: #{finding.message}" }
68
+
69
+ # @return [String] the stable name of the edge a finding is about, as `kind:Bluebook:from>to`
70
+ def key(kind, name, edge) = "#{kind}:#{name}:#{edge.from}>#{edge.to}"
71
+
72
+ def implicit_edges(moves) = moves.map { |move| Edge.new(nil, move["from"], move["to"]) }
73
+
74
+ def held_labels(held, moves)
75
+ [(ShapeLedger.label(held) if held), *moves.map { |move| move["from"] }].compact
76
+ end
77
+
78
+ def domain_findings(name, all, current, held)
79
+ loops, graph = all.partition { |edge| edge.from == edge.to && edge.path }
80
+ forks = fork_findings(name, graph, current)
81
+ route = held + entry_labels(graph)
82
+ loops.map { |edge| loop_finding(name, edge) } + forks +
83
+ orphan_findings(name, graph, current, route, forks.map(&:path))
84
+ end
85
+
86
+ def loop_finding(name, edge)
87
+ Finding.new(kind: :self_loop, domain: name, path: edge.path, key: key(:self_loop, name, edge),
88
+ message: "self-loop edge #{edge.from} -> #{edge.to} in #{edge.path}; remove it with " \
89
+ "`git rm #{edge.path}`")
90
+ end
91
+
92
+ def fork_findings(name, graph, current)
93
+ graph.group_by(&:from).flat_map do |label, leaving|
94
+ live = leaving.select { |edge| leads_to?(edge.to, current, graph) }
95
+ live.size > 1 ? fork_branch_findings(name, label, live, graph, current) : []
96
+ end
97
+ end
98
+
99
+ def fork_branch_findings(name, label, live, graph, current)
100
+ keep = live.min_by { |edge| [steps(edge.to, current, graph), edge.path ? 0 : 1] }
101
+ (live - [keep]).map do |branch|
102
+ files = branch_files(branch, keep, graph)
103
+ Finding.new(kind: :fork, domain: name, path: files.first, key: key(:fork, name, branch),
104
+ message: fork_message(label, live, keep, branch, files))
105
+ end
106
+ end
107
+
108
+ def fork_message(label, live, keep, branch, files)
109
+ leaving = live.map { |edge| "#{edge.from} -> #{edge.to}" }.join(", ")
110
+ "fork at #{label}: #{live.size} edges leave it and each reaches the current shape (#{leaving}); " \
111
+ "keep #{keep.from} -> #{keep.to} and #{removal(branch, files)}, or compose the branch into one edge"
112
+ end
113
+
114
+ def removal(branch, files)
115
+ return "remove the recorded implicit move #{branch.from} -> #{branch.to} from implicit_moves.json" if files.empty?
116
+
117
+ "remove the branch with `git rm #{files.join(" ")}`"
118
+ end
119
+
120
+ # The files only the branch carries: edges leaving a label the branch reaches that the kept
121
+ # route does not also reach.
122
+ def branch_files(branch, keep, graph)
123
+ shared = reached_from(keep.to, graph)
124
+ own = reached_from(branch.to, graph) - shared
125
+ [branch, *graph.select { |edge| own.include?(edge.from) }].filter_map(&:path).uniq
126
+ end
127
+
128
+ def orphan_findings(name, graph, current, starts, flagged)
129
+ carried = starts.flat_map { |label| reached_from(label, graph) }
130
+ graph.select(&:path).reject { |edge| flagged.include?(edge.path) || carried_to?(edge, carried, current, graph) }
131
+ .map { |edge| orphan_finding(name, edge, current) }
132
+ end
133
+
134
+ def carried_to?(edge, carried, current, graph) = carried.include?(edge.from) && leads_to?(edge.to, current, graph)
135
+
136
+ def orphan_finding(name, edge, current)
137
+ Finding.new(kind: :orphan, domain: name, path: edge.path, key: key(:orphan, name, edge),
138
+ message: "orphan edge #{edge.from} -> #{edge.to} in #{edge.path} lies on no route from a " \
139
+ "held era to the current shape #{current}; remove it with `git rm #{edge.path}`")
140
+ end
141
+
142
+ # @return [Array<String>] labels no edge leads into: the first shape of each chain
143
+ def entry_labels(graph) = graph.map(&:from).uniq - graph.map(&:to)
144
+
145
+ def leads_to?(label, current, graph) = label == current || reached_from(label, graph).include?(current)
146
+
147
+ # @return [Array<String>] every label a chain of edges leads to from one label, itself included
148
+ def reached_from(label, graph)
149
+ seen = [label]
150
+ seen.each { |at| graph.each { |edge| seen << edge.to if edge.from == at && !seen.include?(edge.to) } }
151
+ seen
152
+ end
153
+
154
+ # @return [Integer, Float] the fewest edges from one label to another, infinite when none
155
+ def steps(label, current, graph)
156
+ distance = { label => 0 }
157
+ graph.size.times { widen(distance, graph) }
158
+ distance.fetch(current, Float::INFINITY)
159
+ end
160
+
161
+ def widen(distance, graph)
162
+ graph.each do |edge|
163
+ next unless distance.key?(edge.from)
164
+
165
+ distance[edge.to] = [distance[edge.to], distance[edge.from] + 1].compact.min
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hecks
4
+ module ShapeLedger
5
+ # Which bluebooks the ledger holds. A bluebook is held when its committed persistence is an
6
+ # era-managed adapter, or when it is a framework member such as Governance, which clients
7
+ # persist. Every other bluebook is tooling, Memory by its committed world (ADR 0136), with no
8
+ # database whose eras a shape change could strand.
9
+ module Scope
10
+ module_function
11
+
12
+ # @param directory [String] the domain directory
13
+ # @return [Runtime::Registry] its bluebooks as committed: no local or environment overlay
14
+ def committed_registry(directory)
15
+ Adapters::FolderOverlay.committed_only { Hecks.describe(directory, environment: nil).registry }
16
+ end
17
+
18
+ # @param registry [Runtime::Registry] the committed registry
19
+ # @return [Array(Array<String>, Hash{String => String})] the names the ledger holds, and each
20
+ # skipped bluebook's name mapped to its committed adapter
21
+ def of(registry)
22
+ held, skipped = registry.bluebooks.partition { |name, bluebook| held?(registry, name, bluebook) }
23
+ [held.map(&:first), skipped.to_h { |name, bluebook| [name, adapter_of(registry, name, bluebook)] }]
24
+ end
25
+
26
+ # @return [Array(Hash, Hash, Hash)] the shapes and the ledger entries of the bluebooks held,
27
+ # and the bluebooks skipped
28
+ def view(registry, path)
29
+ skipped = of(registry).last
30
+ [ShapeLedger.shapes_of(registry), ShapeLedger.read(path)].map { |held| held.except(*skipped.keys) } << skipped
31
+ end
32
+
33
+ # @return [String] the line naming the skipped bluebooks, empty when there are none
34
+ def note(skipped)
35
+ return "" if skipped.empty?
36
+
37
+ "\nskipped #{skipped.size} tooling bluebooks (committed persistence: #{skipped.values.uniq.join(", ")}): " \
38
+ "#{skipped.keys.join(", ")}"
39
+ end
40
+
41
+ def held?(registry, name, bluebook)
42
+ Framework.members.key?(name) || Runtime::EraCheck.lineage_capable?(registry, adapter_of(registry, name, bluebook))
43
+ end
44
+
45
+ def adapter_of(registry, name, bluebook)
46
+ first = bluebook.aggregates.first
47
+ first ? Runtime::EraCheck.adapter_for(registry, name, first) : Ports::Persistence::DEFAULT_ADAPTER
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hecks
4
+ module ShapeLedger
5
+ # The ledger status of each bluebook under a directory, read from the committed ledger and its
6
+ # edges, never from a database. A status is one of:
7
+ #
8
+ # - `current`: the ledger holds the shape the bluebook now has.
9
+ # - `behind`: the shape the ledger holds leads to the current one. `move` names the implicit
10
+ # move the ledger records for that step, or is nil when no such move is recorded.
11
+ # - `superseded`: the current shape is an older one than the ledger holds.
12
+ # - `unminted`: the ledger has no entry for the bluebook, or no chain of edges or recorded moves
13
+ # leads from what it holds to the current shape.
14
+ #
15
+ # @example
16
+ # Status.of("lib/hecks/hecks")["bluebooks"]["Governance"]["status"] # => "current"
17
+ module Status
18
+ module_function
19
+
20
+ # @param directory [String] the domain directory whose bluebooks the ledger records
21
+ # @param registry [Runtime::Registry] the domain's loaded registry; loaded here when not given
22
+ # @return [Hash] `domain`, and `bluebooks`: each bluebook's status row, by name
23
+ def of(directory, registry: Hecks.describe(directory).registry)
24
+ path = File.join(directory, FILE)
25
+ ledger = ShapeLedger.read(path)
26
+ moves = ShapeLedger.implicit_moves(ShapeLedger.implicit_path(path))
27
+ edges = ShapeLedger.with_implicit(ShapeLedger.edges_of(registry), moves)
28
+ bluebooks = registry.bluebooks.slice(*Scope.of(Scope.committed_registry(directory)).first).to_h do |name, bluebook|
29
+ [name, classify(name, ledger[name], Runtime::StorageShape.mint_hash(bluebook), moves, edges)]
30
+ end
31
+ { "domain" => directory, "bluebooks" => bluebooks }
32
+ end
33
+
34
+ # @param name [String] the bluebook's name
35
+ # @param held [String, nil] the storage-shape hash the ledger holds, nil when it has none
36
+ # @param now [String] the storage-shape hash of the bluebook as it is now
37
+ # @param moves [Hash{String => Array<Hash>}] each bluebook's recorded implicit moves
38
+ # @param edges [Hash{String => Array<Array(String, String)>}] each bluebook's edges and moves
39
+ # @return [Hash] the bluebook's status row
40
+ def classify(name, held, now, moves, edges)
41
+ return unminted(name, now) unless held
42
+
43
+ held_label = ShapeLedger.label(held)
44
+ now_label = ShapeLedger.label(now)
45
+ return row("current", held_label, now_label) if held_label == now_label
46
+
47
+ ahead_of(held_label, now_label, moves.fetch(name, []), edges.fetch(name, []))
48
+ end
49
+
50
+ # @return [Hash] a row for a bluebook the ledger holds no shape for
51
+ def unminted(name, now)
52
+ row("unminted", nil, ShapeLedger.label(now), reason: "the ledger holds no shape for #{name}")
53
+ end
54
+
55
+ # Places the current shape against the held one when they differ.
56
+ #
57
+ # @return [Hash] a `superseded`, `behind` or `unminted` row
58
+ def ahead_of(held, now, recorded, edges)
59
+ return superseded(held, now) if ShapeLedger.reachable?(now, held, edges)
60
+ return behind(held, now, recorded) if ShapeLedger.reachable?(held, now, edges)
61
+
62
+ row("unminted", held, now, reason: "no translation edge or recorded implicit move leads from #{held} to #{now}")
63
+ end
64
+
65
+ # @return [Hash] a row for a shape older than the one the ledger holds
66
+ def superseded(held, now)
67
+ row("superseded", held, now, reason: "the current shape #{now} is older than the ledger's #{held}")
68
+ end
69
+
70
+ # @param held [String] the label the ledger holds
71
+ # @param now [String] the label the bluebook now has
72
+ # @param recorded [Array<Hash>] the bluebook's recorded implicit moves
73
+ # @return [Hash] a row for a shape one recorded move or edge ahead of the ledger
74
+ def behind(held, now, recorded)
75
+ move = recorded.find { |entry| entry["from"] == held && entry["to"] == now }
76
+ return row("behind", held, now, move: move) if move
77
+
78
+ row("behind", held, now, reason: "no implicit move is recorded from #{held} to #{now}")
79
+ end
80
+
81
+ # @return [Hash] one bluebook's status, with every field present; `source` says it was read
82
+ # from the ledger
83
+ def row(status, held, now, move: nil, reason: nil)
84
+ { "status" => status, "held" => held, "current" => now, "move" => move, "reason" => reason,
85
+ "source" => "ledger" }
86
+ end
87
+ end
88
+ end
89
+ end
@@ -1,6 +1,9 @@
1
- require "fileutils"
2
1
  require "json"
3
2
  require "hecks"
3
+ require_relative "ports/persistence/plugins/era/era_check"
4
+ require_relative "shape_ledger/scope"
5
+ require_relative "shape_ledger/status"
6
+ require_relative "shape_ledger/edge_hygiene"
4
7
 
5
8
  module Hecks
6
9
  # The committed record of each bluebook's storage shape, held against the shapes the bluebooks
@@ -10,17 +13,24 @@ module Hecks
10
13
  # that at a boot only runs against a real database, which specs and gates do not use, so this one
11
14
  # reads nothing but the loaded bluebooks and the committed edges: it needs no database.
12
15
  #
16
+ # It holds only a bluebook whose committed persistence is an era adapter, or a framework member
17
+ # that clients persist (see `Scope`); tooling is Memory and needs no entry.
18
+ #
13
19
  # @example
14
- # ShapeLedger.check("lib/hecks/hecks") # => "15 bluebook shapes, each the one the ledger holds"
20
+ # ShapeLedger.check("lib/hecks/hecks") # => "1 bluebook shapes, each ..."
15
21
  module ShapeLedger
16
22
  # The ledger file, beside the bluebooks it records.
17
23
  FILE = "shapes.json".freeze
18
24
 
25
+ # The record beside the ledger of each implicit move: a shape change that carried no edge file
26
+ # and was settled with `--confirm --additive`. Each bluebook maps to its `{from, to}` moves.
27
+ IMPLICIT_FILE = "implicit_moves.json".freeze
28
+
19
29
  # The length of the label a translation edge names a shape by.
20
30
  LABEL = 6
21
31
 
22
32
  # What a problem line tells the reader to do.
23
- REWRITE = "rewrite the ledger with `hecks conformance_run.check_shape_ledger --confirm --wait`".freeze
33
+ REWRITE = "rewrite the ledger with `hecks conformance_run.check_shape_ledger! --confirm --wait`".freeze
24
34
 
25
35
  # Raised with every problem found, or when the ledger cannot be rewritten.
26
36
  class Failure < StandardError; end
@@ -31,17 +41,22 @@ module Hecks
31
41
  #
32
42
  # @param directory [String] the domain directory whose bluebooks the ledger records
33
43
  # @param confirm [Boolean] rewrite the ledger when every moved shape has an edge
34
- # @param additive [Boolean] with `confirm`, write the empty edge for each shape that moved with
35
- # none, on the operator's word that the change only adds; the mint audits every edge against
36
- # the records it migrates and refuses one that would lose data
44
+ # @param additive [Boolean] with `confirm`, move the ledger for each shape that moved with no
45
+ # edge, on the operator's word that the change only adds. No edge file is written: the boot
46
+ # and the mint treat such a move as an implicit empty edge, and the mint refuses any change
47
+ # the scaffold does not find to be additive
37
48
  # @return [String] the clean verdict, or what was written
38
49
  # @raise [Failure] with each problem
39
50
  def check(directory, confirm: false, additive: false)
40
- registry = Hecks.describe(directory).registry
41
- shapes = shapes_of(registry)
42
- edges = edges_of(registry)
51
+ registry = Scope.committed_registry(directory)
43
52
  path = File.join(directory, FILE)
44
- confirm ? rewrite(path, shapes, edges, directory, additive) : verdict(path, shapes, edges)
53
+ shapes, ledger, skipped = Scope.view(registry, path)
54
+ moves = implicit_moves(implicit_path(path))
55
+ edges = with_implicit(edges_of(registry), moves)
56
+ return rewrite(path, ledger, shapes, edges, additive) if confirm
57
+
58
+ verdict(ledger, shapes, edges) + Scope.note(skipped) +
59
+ EdgeHygiene.report(directory, ledger: ledger, shapes: shapes, moves: moves)
45
60
  end
46
61
 
47
62
  # @param registry [Runtime::Registry] the loaded bluebooks
@@ -58,6 +73,18 @@ module Hecks
58
73
 
59
74
  def pairs(edges) = edges.map { |edge| [edge.from, edge.to] }
60
75
 
76
+ # @return [Hash{String => Array<Array(String, String)>}] `edges` with each recorded implicit
77
+ # move added as an edge: the boot and the mint read a recorded move as an empty edge
78
+ def with_implicit(edges, record)
79
+ record.each_with_object(edges.dup) do |(name, moves), merged|
80
+ merged[name] = merged.fetch(name, []) + pairs_of(moves)
81
+ end
82
+ end
83
+
84
+ # @param moves [Array<Hash>] recorded `{"from" => ..., "to" => ...}` moves
85
+ # @return [Array<Array(String, String)>] the moves as `[from, to]`
86
+ def pairs_of(moves) = moves.map { |move| [move["from"], move["to"]] }
87
+
61
88
  def label(hash) = hash.to_s[0, LABEL]
62
89
 
63
90
  # @return [Boolean] whether a chain of edges leads from one label to another
@@ -74,8 +101,8 @@ module Hecks
74
101
  fresh
75
102
  end
76
103
 
77
- def verdict(path, shapes, edges)
78
- problems = problems(read(path), shapes, edges)
104
+ def verdict(ledger, shapes, edges)
105
+ problems = problems(ledger, shapes, edges)
79
106
  raise Failure, problems.join("\n") unless problems.empty?
80
107
 
81
108
  "#{shapes.size} bluebook shapes, each the one the ledger holds"
@@ -100,25 +127,49 @@ module Hecks
100
127
  # What to commit: the empty edge covers a change that only adds, and a change that renames or
101
128
  # reshapes data needs the rules the scaffold writes from the database that holds the eras.
102
129
  def edge_advice(name, from, now)
103
- edge = %(Hecks.data_translation "#{name}", from: "#{from}", to: "#{now}" do end)
104
- "for a change that only adds, commit this edge under translations/ (as 2-#{now}.bluebook): #{edge}. " \
105
- "Data that is renamed or reshaped needs rules: `hecks query era.scaffold_translation " \
106
- "domain.value=<directory>` writes them, reading the eras from the database the domain's " \
107
- "own world names. A change that only adds is settled in one step " \
108
- "by `hecks conformance_run.check_shape_ledger --confirm --additive --wait`"
130
+ "#{name} moved with no edge. A change that only adds needs no edge file: settle it in one step " \
131
+ "with `hecks conformance_run.check_shape_ledger! --confirm --additive --wait`, which the boot and " \
132
+ "the mint treat as an implicit empty edge from #{from} to #{now}. Data that is renamed or reshaped " \
133
+ "needs rules: `hecks query era.scaffold_translation domain.value=<directory>` writes them, reading " \
134
+ "the eras from the database the domain's own world names."
109
135
  end
110
136
 
111
- def rewrite(path, shapes, edges, directory, additive)
112
- ledger = read(path)
113
- written = additive ? write_edges(directory, uncovered_moves(ledger, shapes, edges)) : []
137
+ def rewrite(path, ledger, shapes, edges, additive)
138
+ implicit = additive ? uncovered_moves(ledger, shapes, edges) : []
114
139
  uncovered = additive ? [] : problems(ledger, shapes, edges).reject { |line| line.include?(REWRITE) }
115
140
  raise Failure, uncovered.join("\n") unless uncovered.empty?
116
141
 
117
142
  File.write(path, "#{JSON.pretty_generate(shapes.sort.to_h)}\n")
118
- "wrote #{shapes.size} bluebook shapes to #{path}#{edges_note(written)}"
143
+ record_implicit(implicit_path(path), implicit)
144
+ "wrote #{shapes.size} bluebook shapes to #{path}#{implicit_note(implicit)}"
119
145
  end
120
146
 
121
- def edges_note(written) = written.empty? ? "" : " and #{written.size} empty edge(s): #{written.join(", ")}"
147
+ # Appends each implicit move to the record, once: a move the record already holds is skipped.
148
+ #
149
+ # @param path [String] the record file beside the ledger
150
+ # @param moves [Array<Array(String, String, String)>] `[bluebook, from, to]` for each move
151
+ # @return [void]
152
+ def record_implicit(path, moves)
153
+ return if moves.empty?
154
+
155
+ record = implicit_moves(path)
156
+ moves.each { |name, from, now| append_move(record, name, { "from" => from, "to" => now }) }
157
+ File.write(path, "#{JSON.pretty_generate(record.sort.to_h)}\n")
158
+ end
159
+
160
+ def append_move(record, name, move)
161
+ entries = record.fetch(name, [])
162
+ record[name] = entries.include?(move) ? entries : entries + [move]
163
+ end
164
+
165
+ def implicit_path(path) = File.join(File.dirname(path), IMPLICIT_FILE)
166
+
167
+ def implicit_note(moves)
168
+ return "" if moves.empty?
169
+
170
+ named = moves.map { |name, from, now| "#{name} #{from} -> #{now}" }
171
+ " and #{moves.size} implicit move(s), no edge file: #{named.join(", ")}"
172
+ end
122
173
 
123
174
  # @return [Array<Array(String, String, String)>] `[bluebook, from, to]` for each shape that
124
175
  # moved
@@ -133,17 +184,11 @@ module Hecks
133
184
  end
134
185
  end
135
186
 
136
- # @return [Array<String>] the paths of the empty edges written
137
- def write_edges(directory, moves)
138
- FileUtils.mkdir_p(File.join(directory, "translations"))
139
- moves.map do |name, from, now|
140
- path = File.join(directory, "translations", "2-#{now}.bluebook")
141
- File.write(path, %(Hecks.data_translation "#{name}", from: "#{from}", to: "#{now}" do\nend\n))
142
- path
143
- end
144
- end
145
-
146
187
  # @return [Hash{String => String}] the ledger, empty when there is none yet
147
188
  def read(path) = File.exist?(path) ? JSON.parse(File.read(path)) : {}
189
+
190
+ # @param path [String] the implicit-move record
191
+ # @return [Hash{String => Array<Hash>}] each bluebook's recorded moves, empty when there is none
192
+ def implicit_moves(path) = File.exist?(path) ? JSON.parse(File.read(path)) : {}
148
193
  end
149
194
  end
@@ -23,6 +23,7 @@ Hecks.bluebook "Site" do
23
23
 
24
24
  aggregate "Route" do
25
25
  description "The shape of one row of a site's route table: where a path is served from, who may reach it, how it is cached, and where it appears in the navigation."
26
+ acknowledges :no_command_surface, "a route row is declared data: the table is what a site's bluebook says, and no command changes a row"
26
27
 
27
28
  identified_by :path
28
29
 
@@ -4,11 +4,18 @@ module Hecks
4
4
  # reading it back as a tail or as the events it announced. Extended onto `Storehouse`.
5
5
  module Audit
6
6
  # The caller-supplied fields of an audit line, in the order the line spells them.
7
- AUDIT_FIELDS = %i[verb summary source role actor_id].freeze
7
+ AUDIT_FIELDS = %i[verb summary source role actor_id surface].freeze
8
8
 
9
9
  # A character a log file's name may not carry.
10
10
  UNSAFE_NAME_CHARACTER = /[^A-Za-z0-9_-]/
11
11
 
12
+ # The boundary between a lowercase or digit character and an uppercase one, where a
13
+ # CamelCase word takes an underscore.
14
+ CAMEL_CASE_BOUNDARY = /([a-z\d])([A-Z])/
15
+
16
+ # The last segment of a qualified constant name, such as `TypeMismatch` in `Runtime::TypeMismatch`.
17
+ UNQUALIFIED_NAME = /[^:]+\z/
18
+
12
19
  # Kept under Hecks::CacheDir, not the domain directory (part of the domain's
13
20
  # own state) or the gem directory (read-only once installed).
14
21
  # :nodoc:
@@ -27,14 +34,37 @@ module Hecks
27
34
  def record!(domain_name, tool:, outcome:, **entry)
28
35
  return unless domain_name
29
36
 
30
- line = { time: Time.now.utc.iso8601, tool: tool }.merge(AUDIT_FIELDS.to_h { |field| [field, entry[field]] })
31
- .merge(outcome.slice(:ok, :id, :error, :events)).compact
32
37
  FileUtils.mkdir_p(log_root)
33
- File.open(log_path(domain_name), "a") { |f| f.puts(JSON.generate(line)) }
38
+ File.open(log_path(domain_name), "a") { |f| f.puts(JSON.generate(audit_line(tool, outcome, entry))) }
34
39
  rescue StandardError
35
40
  nil
36
41
  end
37
42
 
43
+ # One audit line as a hash: when, the tool, the caller's fields, the outcome's fields, then
44
+ # the call's duration and refusal kind, with absent values left out.
45
+ # :nodoc:
46
+ def audit_line(tool, outcome, entry)
47
+ fields = AUDIT_FIELDS.to_h { |field| [field, entry[field]] }
48
+ { time: Time.now.utc.iso8601, tool: tool, **fields, **outcome.slice(:ok, :id, :error, :events),
49
+ **entry.slice(:duration_ms, :refusal) }.compact
50
+ end
51
+
52
+ # Whole milliseconds since `started`, a CLOCK_MONOTONIC reading.
53
+ # :nodoc:
54
+ def elapsed_ms(started)
55
+ ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - started) * 1000).round
56
+ end
57
+
58
+ # The refusal's class as a snake_case kind, such as `type_mismatch`.
59
+ # :nodoc:
60
+ def refusal_kind(error)
61
+ snake_case(error.class.name[UNQUALIFIED_NAME])
62
+ end
63
+
64
+ # The word with an underscore at each CamelCase boundary, in lowercase.
65
+ # :nodoc:
66
+ def snake_case(word) = word.gsub(CAMEL_CASE_BOUNDARY, '\\1_\\2').downcase
67
+
38
68
  # Tails this bus's own dispatch/query/state audit log — a pull-based
39
69
  # substitute for a push subscription, since a stdio server answers one
40
70
  # request at a time with no channel to push through.