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
@@ -1,6 +1,9 @@
1
1
  require_relative "../../../../naming"
2
2
  require_relative "../../../../ports/persistence/plugins/era/translation/rule_compiler"
3
3
  require_relative "sql_templates"
4
+ require_relative "split_sql"
5
+ require_relative "layered_sql"
6
+ require_relative "direct_sql"
4
7
 
5
8
  module Hecks
6
9
  module Adapters
@@ -11,6 +14,9 @@ module Hecks
11
14
  # over them in mint order.
12
15
  module ChainSql
13
16
  include SqlTemplates
17
+ include SplitSql
18
+ include LayeredSql
19
+ include DirectSql
14
20
 
15
21
  # Reduces the tail before chaining edges over it, not after: every
16
22
  # reader already reduces to latest-per-id, so translating a
@@ -23,29 +29,12 @@ module Hecks
23
29
  "FROM (#{tail}) tail_entries ORDER BY aggregate_id, ordinal DESC"
24
30
  end
25
31
 
26
- # Builds era N by layering its own last edge onto era N-1's existing
27
- # matview, instead of re-deriving from raw history; returns nil
28
- # (falling back to chain_sql) whenever that shortcut can't honestly
29
- # apply. `edges.size != era - 1` guards names_by_era's own
30
- # assumption that `edges` is the full chain reaching `era` — a
31
- # shorter chain here would index names[:storage] out of bounds
32
- # rather than safely falling back.
33
- def layered_chain_sql(aggregate, era, edges)
34
- return nil unless layerable?(era, edges)
35
-
36
- held = eras
37
- prior_view = existing_prior_view(aggregate, era, held)
38
- return nil unless prior_view
39
-
40
- layered_sql(aggregate, era, edges, held, prior_view)
41
- end
42
-
43
32
  # Chains the original edges in mint order rather than flattening them
44
33
  # into one rule set: edge 1 renaming A→B then edge 2 renaming C→A
45
34
  # has no single phase order that applies both correctly.
46
35
  def chain_sql(aggregate, era, edges)
47
36
  names = names_by_era(aggregate, edges)
48
- tail = latest_per_id(ancestor_tail_sql(names, era))
37
+ tail = latest_per_id(target_tail_sql(names, era, edges))
49
38
  chain = edges.each_with_index.map { |edge, index| edge_step_sql(edge, index, names) }
50
39
  format(CHAIN_SQL, tail: tail, chain: chain.join(",\n"), last: edges.size)
51
40
  end
@@ -67,6 +56,15 @@ module Hecks
67
56
  latest_of("SELECT ordinal, era, aggregate_id, operation, state FROM (#{tail}) tail_rows")
68
57
  end
69
58
 
59
+ # The aggregate's head as the chain's last edge receives it: every edge but the last,
60
+ # read under the name the held era gives the aggregate. The last edge's `was` is that
61
+ # name, so a renamed aggregate's held rows are found, as `ancestor_latest` finds them.
62
+ # Needs at least two edges; with one, `ancestor_latest` is the whole answer.
63
+ def received_latest(aggregate, era, edges)
64
+ held_name = edges.last[:translation].for_aggregate(aggregate.name)&.was || aggregate.name
65
+ latest_of(chain_sql(Struct.new(:name).new(held_name), era, edges[0..-2]))
66
+ end
67
+
70
68
  # Reduces to newest entry per aggregate id, dropping any id whose
71
69
  # newest entry is a delete rather than returning it with a nil state.
72
70
  def latest_of(sql)
@@ -78,10 +76,16 @@ module Hecks
78
76
  # (translated_latest) can never approve a branch the real mint
79
77
  # doesn't take. `full:` mirrors compile_head!'s own default;
80
78
  # tail_merge is the one caller that forces it true.
81
- def head_body_sql(aggregate, era, edges, full: false)
82
- return chain_sql(aggregate, era, edges) if full
79
+ def head_body_sql(aggregate, era, edges, full: false, direct: direct_translation?(edges))
80
+ return walked_sql(aggregate, era, edges, direct) if full
83
81
 
84
- layered_chain_sql(aggregate, era, edges) || chain_sql(aggregate, era, edges)
82
+ layered_chain_sql(aggregate, era, edges) || walked_sql(aggregate, era, edges, direct)
83
+ end
84
+
85
+ # The whole chain from the journal: one composed step when `direct` and the chain composes,
86
+ # otherwise one link per era.
87
+ def walked_sql(aggregate, era, edges, direct)
88
+ (direct && direct_chain_sql(aggregate, era, edges)) || chain_sql(aggregate, era, edges)
85
89
  end
86
90
 
87
91
  # Walks `was:` chains backward from the current name, so each
@@ -109,34 +113,10 @@ module Hecks
109
113
 
110
114
  private
111
115
 
112
- def layerable?(era, edges) = era >= 3 && edges.size >= 2 && edges.size == era - 1
113
-
114
- # The previous era's matview, when it is labelled and already built.
115
- def existing_prior_view(aggregate, era, held)
116
- prior = held.find { |candidate| candidate[:ordinal] == era - 1 }
117
- return nil unless prior && prior[:label]
118
-
119
- view = matview(aggregate.storage_name, era - 1, prior[:label])
120
- view if view_exists?(view)
121
- end
122
-
123
- def layered_sql(aggregate, era, edges, held, prior_view)
124
- names = names_by_era(aggregate, edges)
125
- declared = edges.last[:translation].for_aggregate(names[:current][edges.size])
126
- tokens = layered_tokens(prior_view, names, era, declared)
127
- format(LAYERED_SQL, tokens.merge(cut: cut_clause(held, era)))
128
- end
129
-
130
- def layered_tokens(prior_view, names, era, declared)
131
- { prior_view: quote(prior_view), journal: quoted_journal, prior_era: era - 1,
132
- aggregate: text_literal(names[:storage][era - 2]),
133
- id_column: id_column_for(declared, "operation = 'save'"), expression: rules_expression(declared) }
134
- end
135
-
136
- # Cuts the previous era's rows at the watermark recorded when this era was minted.
137
- def cut_clause(held, era)
138
- cut = held.find { |candidate| candidate[:ordinal] == era }&.dig(:watermark)
139
- " AND ordinal <= #{cut}" if cut
116
+ # A split target's tail is its own journal rows plus the rows its split fans out of the
117
+ # source.
118
+ def target_tail_sql(names, era, edges)
119
+ [ancestor_tail_sql(names, era), split_tail_sql(names, edges)].compact.reject(&:empty?).join(" UNION ALL ")
140
120
  end
141
121
 
142
122
  # One link of the chain: its edge's rules over the previous link. A link is `MATERIALIZED`
@@ -0,0 +1,73 @@
1
+ require_relative "../../../../ports/persistence/plugins/era/translation/compose"
2
+ require_relative "sql_templates"
3
+ require_relative "direct_verified"
4
+
5
+ module Hecks
6
+ module Adapters
7
+ class PostgresEra
8
+ class Lineage
9
+ # The head read in one step. Each held era's rows take the translation composed from that
10
+ # era's edge to the last, instead of one link per later era. A chain that does not compose
11
+ # exactly (two steps ruling one aggregate, a rekey or a split) has no direct form and
12
+ # reads through `ChainSql#chain_sql` instead.
13
+ module DirectSql
14
+ include SqlTemplates
15
+
16
+ # Overrides the default of reading directly where the chain is verified.
17
+ #
18
+ # @return [Boolean, nil] true or false to force a path; nil to follow `DirectVerified`
19
+ attr_writer :direct_translation
20
+
21
+ # @param edges [Array<Hash>] the chain, one `{ translation: edge }` per era
22
+ # @return [Boolean] whether the head should read through the composed step
23
+ def direct_translation?(edges)
24
+ return @direct_translation unless @direct_translation.nil?
25
+
26
+ DirectVerified.proven?(edges.map { |edge| edge[:translation] })
27
+ end
28
+
29
+ # The head SQL as one step, or nil when the chain cannot be composed exactly.
30
+ #
31
+ # @param aggregate [#name, #storage_name] the aggregate whose head is read
32
+ # @param era [Integer] the era the head is read for
33
+ # @param edges [Array<Hash>] the chain reaching `era`
34
+ # @return [String, nil] the SQL; nil when a link needs the chain
35
+ def direct_chain_sql(aggregate, era, edges)
36
+ names = names_by_era(aggregate, edges)
37
+ composed = composed_by_era(aggregate, edges)
38
+ return unless composed
39
+
40
+ tail = latest_per_id(target_tail_sql(names, era, edges))
41
+ format(DIRECT_SQL, tail: tail, state: direct_state_sql(composed))
42
+ end
43
+
44
+ private
45
+
46
+ # The aggregate's rules from each held era to the last, keyed by that era; nil when any
47
+ # suffix of the chain refuses to compose.
48
+ def composed_by_era(aggregate, edges)
49
+ translations = edges.map { |edge| edge[:translation] }
50
+ (1..translations.size).to_h do |held|
51
+ composed = Translation::Compose.compose(**compose_arguments(translations.drop(held - 1)))
52
+ [held, composed.for_aggregate(aggregate.name)]
53
+ end
54
+ rescue Runtime::WiringError
55
+ nil
56
+ end
57
+
58
+ def compose_arguments(chain)
59
+ { domain: chain.first.domain, from: chain.first.from, to: chain.last.to, chain: chain }
60
+ end
61
+
62
+ # `state` itself when no era's rows change, else one case per era that does.
63
+ def direct_state_sql(composed)
64
+ cases = composed.filter_map do |held, declared|
65
+ "WHEN era = #{held} AND operation = 'save' THEN #{rules_expression(declared)}" if declared
66
+ end
67
+ cases.empty? ? "state" : "CASE #{cases.join(" ")} ELSE state END"
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,10 @@
1
+ {
2
+ "chains": [
3
+ {
4
+ "domain": "Pizzas",
5
+ "from": "e3f3d7",
6
+ "to": "77625c",
7
+ "fingerprint": "cddf5d0e5867f849eff61a9cce6751bbfd221f173a9573673fac3963fb21af26"
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,65 @@
1
+ require "json"
2
+ require "digest"
3
+
4
+ module Hecks
5
+ module Adapters
6
+ class PostgresEra
7
+ class Lineage
8
+ # The edge chains whose composed (direct) translation `direct_translation_spec` proves
9
+ # equal to their one-link-per-era chain, kept as fingerprints in `direct_verified.json`.
10
+ #
11
+ # A fingerprint is the bluebook name and, for each edge in order, its origin, its target
12
+ # and a digest of its rules. A client chain that shares a name with a proven one but
13
+ # differs in any edge reads through the chain. A stale file only costs the direct path.
14
+ #
15
+ # Rewrite the file after a committed edge changes by running the generator spec:
16
+ #
17
+ # HECKS_WRITE_DIRECT_VERIFIED=1 exe/hecks test_suite_run.run_spec_example! \
18
+ # file.value=spec/adapters/driven/postgres_era/direct_verified_spec.rb \
19
+ # "example.value= " --wait
20
+ module DirectVerified
21
+ PATH = File.join(__dir__, "direct_verified.json").freeze
22
+
23
+ module_function
24
+
25
+ # @param translations [Array<Bluebook::Translation>] a chain of edges, oldest first
26
+ # @return [String] the chain's fingerprint, SHA-256 hex
27
+ def fingerprint(translations)
28
+ edges = translations.map { |edge| [edge.from.to_s, edge.to.to_s, rules_digest(edge)] }
29
+ Digest::SHA256.hexdigest(JSON.generate([translations.first&.domain.to_s, edges]))
30
+ end
31
+
32
+ # @param translations [Array<Bluebook::Translation>] a chain of edges, oldest first
33
+ # @return [Boolean] whether this exact chain is one the spec proved
34
+ def proven?(translations)
35
+ return false if translations.empty? || translations.any?(&:nil?)
36
+
37
+ proven.include?(fingerprint(translations))
38
+ rescue StandardError
39
+ false
40
+ end
41
+
42
+ # @return [Array<String>] the proven fingerprints in the committed file; empty if it is
43
+ # missing or unreadable
44
+ def proven
45
+ JSON.parse(File.read(PATH)).fetch("chains", []).map { |chain| chain["fingerprint"] }
46
+ rescue SystemCallError, JSON::ParserError
47
+ []
48
+ end
49
+
50
+ # @param chains [Array<Array>] each `[domain, start label, final label, edges]` proved
51
+ # @return [String] the file's JSON text for those chains
52
+ def render(chains)
53
+ rows = chains.map do |domain, start, final, edges|
54
+ { "domain" => domain, "from" => start.to_s, "to" => final.to_s, "fingerprint" => fingerprint(edges) }
55
+ end
56
+ ordered = rows.uniq.sort_by { |row| row.values_at("domain", "from", "to", "fingerprint") }
57
+ "#{JSON.pretty_generate("chains" => ordered)}\n"
58
+ end
59
+
60
+ def rules_digest(edge) = Digest::SHA256.hexdigest(JSON.generate(edge.to_plan))
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,25 +1,32 @@
1
1
  require_relative "../../../../naming"
2
2
  require_relative "../../../../runtime/errors"
3
+ require_relative "domain_rename_state"
3
4
 
4
5
  module Hecks
5
6
  module Adapters
6
7
  class PostgresEra
7
8
  class Lineage
8
9
  # The domain rename bridge: moves a domain's journal, sequence, partitions and lineage
9
- # rows from its `formerly_known_as` name to the current one.
10
+ # rows from its `formerly_known_as` name to the current one. Only the rename entry point
11
+ # (`Era.RenameDomain`) moves them; a boot or a provisioning that finds the move pending
12
+ # refuses by name (`refuse_pending_rename!`), so no history starts fresh beside the old.
10
13
  module DomainRename
11
- # Moves a domain's journal, sequence, partitions and lineage rows from
12
- # `formerly_known_as` to the current name, in one transaction.
14
+ include DomainRenameState
15
+
16
+ # Moves a domain's journal, sequence, partitions, lineage rows, heads and field caches
17
+ # from `formerly_known_as` to the current name, in one transaction.
13
18
  #
14
- # Runs before `provisioner?` and the CREATE TABLE calls in `ensure_base!`; a no-op when
15
- # `hecks_eras` is missing, the new name has rows, or the former name has none.
16
- # Locks go in a fixed order (old before new, eras before ordinal) so nothing deadlocks.
19
+ # A no-op when `hecks_eras` is missing, the new name has rows, or the former name has
20
+ # none. Locks go in a fixed order (old before new, eras before ordinal).
17
21
  #
18
- # @return [void]
22
+ # @param storage_names [Array<String>] the storage names of the domain's aggregates
23
+ # @param cache_fields [Hash{String => Array<String>}] each aggregate's storage name mapped
24
+ # to the where-fields its queries cache, whose tables hash the domain
25
+ # @return [Integer] how many eras moved; 0 when nothing was pending
19
26
  # @raise [Runtime::WiringError] if the locks are not granted within 10s or Postgres
20
27
  # refuses a statement (prechecks included); both roll back first
21
- def rename_domain!
22
- rename_in_transaction! if rename_pending?
28
+ def rename_domain!(storage_names: [], cache_fields: {})
29
+ rename_pending? ? rename_in_transaction!(storage_names, cache_fields) : 0
23
30
  rescue PG::LockNotAvailable
24
31
  rollback_quietly
25
32
  raise Runtime::WiringError,
@@ -30,31 +37,71 @@ module Hecks
30
37
  raise Runtime::WiringError, "cannot rename #{@formerly_known_as} to #{@domain}: #{e.message.strip}"
31
38
  end
32
39
 
33
- private
34
-
40
+ # Tells whether the history sits under the former name while the current name holds none.
41
+ #
42
+ # @return [Boolean] false with no prior name, no eras table, eras under the current name,
43
+ # or none under the former name
44
+ # @raise [PG::Error] if the lookup fails
35
45
  def rename_pending?
46
+ return false unless @formerly_known_as
36
47
  return false unless @db.exec_params("SELECT to_regclass($1)", ["hecks_eras"])[0]["to_regclass"]
37
48
  return false if domain_rows?(@domain)
38
49
 
39
50
  domain_rows?(@formerly_known_as)
40
51
  end
41
52
 
53
+ # Refuses, by name, to go on while the rename is pending.
54
+ #
55
+ # @return [void]
56
+ # @raise [Runtime::WiringError] if `rename_pending?`
57
+ def refuse_pending_rename!
58
+ return unless rename_pending?
59
+
60
+ raise Runtime::WiringError,
61
+ "cannot boot #{@domain}: its history is held under the former name #{@formerly_known_as} " \
62
+ "and #{@domain} holds none; rename the domain with era.rename_domain first " \
63
+ "(`hecks era.rename_domain! domain=<domain directory> --confirm`)"
64
+ end
65
+
66
+ private
67
+
42
68
  def domain_rows?(domain)
43
69
  @db.exec_params("SELECT 1 FROM hecks_eras WHERE domain = $1 LIMIT 1", [domain]).ntuples.positive?
44
70
  end
45
71
 
46
- def rename_in_transaction!
47
- old_journal = "hecks_journal_#{Naming.snake(@formerly_known_as)}"
48
- ordinals = @db.exec_params(
49
- "SELECT ordinal FROM hecks_eras WHERE domain = $1 ORDER BY ordinal", [@formerly_known_as]
50
- ).map { |row| row["ordinal"].to_i }
51
-
72
+ # The locks come first and the eras are read after them, so a mint that lands before the
73
+ # locks are granted has its partition renamed with the rest; the pending check is made
74
+ # again under the locks for a rename that finished while this one waited.
75
+ def rename_in_transaction!(storage_names, cache_fields)
52
76
  @db.exec("BEGIN")
53
77
  @db.exec("SET LOCAL lock_timeout = '10s'")
54
78
  rename_lock_keys.each { |key| @db.exec_params("SELECT pg_advisory_xact_lock(hashtext($1))", [key]) }
55
- rename_relations!(old_journal, ordinals)
56
- rename_rows!
79
+ return finish_without_moving unless rename_pending?
80
+
81
+ eras = former_eras
82
+ move_everything!(eras, storage_names, cache_fields)
57
83
  @db.exec("COMMIT")
84
+ eras.size
85
+ end
86
+
87
+ def finish_without_moving
88
+ @db.exec("ROLLBACK")
89
+ 0
90
+ end
91
+
92
+ def move_everything!(eras, storage_names, cache_fields)
93
+ rename_relations!("hecks_journal_#{Naming.snake(@formerly_known_as)}", eras.map(&:first))
94
+ rename_heads!(storage_names, eras)
95
+ rename_catalogue_relations!
96
+ rename_field_caches!(cache_fields, eras.map(&:first))
97
+ rename_rows!
98
+ end
99
+
100
+ # The former name's eras as [ordinal, label] pairs, oldest first.
101
+ def former_eras
102
+ @db.exec_params(
103
+ "SELECT ordinal, label FROM hecks_eras WHERE domain = $1 ORDER BY ordinal", [@formerly_known_as]
104
+ ).map { |row| [row["ordinal"].to_i, row["label"]] }
58
105
  end
59
106
 
60
107
  def rename_lock_keys
@@ -75,14 +122,19 @@ module Hecks
75
122
  end
76
123
  end
77
124
 
78
- def rename_rows!
79
- %w[hecks_eras hecks_era_texts].each do |table|
80
- @db.exec_params("UPDATE #{table} SET domain = $1 WHERE domain = $2", [@domain, @formerly_known_as])
125
+ # Renames each aggregate's head view, per-era snapshot tables and translated-tail views;
126
+ # ALTER TABLE renames a view as it does a table, and the view keeps its snapshot by oid.
127
+ def rename_heads!(storage_names, eras)
128
+ former = self.class.new(@db, @formerly_known_as)
129
+ storage_names.each do |name|
130
+ rename_relation!(former.head_view(name), head_view(name))
131
+ eras.each { |ordinal, label| rename_era_heads!(former, name, ordinal, label) }
81
132
  end
82
- # hecks_attestations is created lazily on first reattest!, so it may not exist.
83
- return unless @db.exec_params("SELECT to_regclass($1)", ["hecks_attestations"])[0]["to_regclass"]
133
+ end
84
134
 
85
- @db.exec_params("UPDATE hecks_attestations SET domain = $1 WHERE domain = $2", [@domain, @formerly_known_as])
135
+ def rename_era_heads!(former, name, ordinal, label)
136
+ rename_relation!(former.head_snapshot(name, ordinal), head_snapshot(name, ordinal))
137
+ rename_relation!(former.matview(name, ordinal, label), matview(name, ordinal, label)) if label
86
138
  end
87
139
  end
88
140
  end
@@ -0,0 +1,108 @@
1
+ module Hecks
2
+ module Adapters
3
+ class PostgresEra
4
+ class Lineage
5
+ # What a domain rename moves besides the journal: every table keyed by the domain's name,
6
+ # the head objects found in the catalogue (not by the aggregates the bluebook lists now, so
7
+ # an aggregate since removed moves too), the field caches whose names hash the domain, and
8
+ # the backfill cursors that name the renamed relations.
9
+ module DomainRenameState
10
+ # Tables with a `domain` column, moved by name when they exist.
11
+ DOMAIN_TABLES = %w[hecks_eras hecks_era_texts hecks_attestations hecks_ledger_archives
12
+ hecks_saga_instances].freeze
13
+
14
+ # The suffixes of the relations a domain's heads and archives are named with.
15
+ HEAD_SUFFIX = /\A(?<storage>.+?)_(?<kind>head|head_snapshot_\d+|lineage_\d+_[^_]+|archived_\d+)\z/
16
+
17
+ private
18
+
19
+ def rename_rows!
20
+ DOMAIN_TABLES.each do |table|
21
+ next unless relation?(table)
22
+
23
+ @db.exec_params("UPDATE #{table} SET domain = $1 WHERE domain = $2", [@domain, @formerly_known_as])
24
+ end
25
+ rename_archive_names!
26
+ end
27
+
28
+ # An archive table moved with its domain prefix, so its list says where it is now.
29
+ def rename_archive_names!
30
+ return unless relation?("hecks_ledger_archives")
31
+
32
+ @db.exec_params(
33
+ "UPDATE hecks_ledger_archives SET archive = $3 || substr(archive, $4) " \
34
+ "WHERE domain = $1 AND left(archive, $4 - 1) = $2",
35
+ [@domain, "#{former_prefix}_", "#{current_prefix}_", former_prefix.length + 2]
36
+ )
37
+ end
38
+
39
+ def relation?(name) = !@db.exec_params("SELECT to_regclass($1)", [quote(name)])[0]["to_regclass"].nil?
40
+
41
+ def former_prefix = Naming.snake(@formerly_known_as)
42
+
43
+ def current_prefix = Naming.snake(@domain)
44
+
45
+ # Renames each head, snapshot, matview and archive the catalogue holds under the former
46
+ # prefix, leaving alone those that belong to another domain whose name extends it.
47
+ def rename_catalogue_relations!
48
+ catalogue_relations.each do |name|
49
+ rename_relation!(name, name.sub(/\A#{Regexp.escape(former_prefix)}_/, "#{current_prefix}_"))
50
+ end
51
+ end
52
+
53
+ def catalogue_relations
54
+ others = other_prefixes
55
+ names = @db.exec_params(
56
+ "SELECT relname FROM pg_class WHERE relkind IN ('r', 'v', 'm') AND relname LIKE $1 " \
57
+ "AND pg_table_is_visible(oid) ORDER BY relname", ["#{former_prefix.gsub("_", "\\_")}\\_%"]
58
+ ).map { |row| row["relname"] }
59
+ names.select { |name| head_name?(name) && others.none? { |other| name.start_with?("#{other}_") } }
60
+ end
61
+
62
+ def head_name?(name) = name.delete_prefix("#{former_prefix}_").match?(HEAD_SUFFIX)
63
+
64
+ # Prefixes of other held domains that extend the former name and so would match it.
65
+ def other_prefixes
66
+ held = @db.exec_params("SELECT DISTINCT domain FROM hecks_eras WHERE domain NOT IN ($1, $2)",
67
+ [@formerly_known_as, @domain]).map { |row| Naming.snake(row["domain"]) }
68
+ held.select { |other| other.start_with?("#{former_prefix}_") }
69
+ end
70
+
71
+ # The field caches for each cached field of each aggregate, under every held era.
72
+ def rename_field_caches!(cache_fields, ordinals)
73
+ former = self.class.new(@db, @formerly_known_as)
74
+ cache_fields.each do |storage, fields|
75
+ ordinals.product(fields) do |ordinal, field|
76
+ rename_cache!(former.field_cache(storage, ordinal, field), field_cache(storage, ordinal, field))
77
+ end
78
+ end
79
+ end
80
+
81
+ def rename_cache!(from, to)
82
+ rename_relation!(from, to)
83
+ end
84
+
85
+ # Renames a table, view or materialized view, the indexes named after it and its cursor.
86
+ def rename_relation!(from, to)
87
+ return unless relation?(from)
88
+
89
+ @db.exec("ALTER TABLE #{quote(from)} RENAME TO #{quote(to)}")
90
+ rename_progress!(from, to)
91
+ %w[_value_idx _reduce_idx _pkey].each { |suffix| rename_index!("#{from}#{suffix}", "#{to}#{suffix}") }
92
+ end
93
+
94
+ def rename_index!(from, to)
95
+ @db.exec("ALTER INDEX #{quote(from)} RENAME TO #{quote(to)}") if relation?(from)
96
+ end
97
+
98
+ # The backfill cursor follows its relation, so a renamed snapshot is not filled afresh.
99
+ def rename_progress!(from, to)
100
+ return unless relation?("hecks_backfill_progress")
101
+
102
+ @db.exec_params("UPDATE hecks_backfill_progress SET target = $2 WHERE target = $1", [from, to])
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -1,4 +1,5 @@
1
1
  require_relative "../../../../ports/persistence/plugins/era/era_tamper"
2
+ require_relative "../../../../ports/persistence/plugins/era/projected_bluebook"
2
3
  require_relative "../../../../runtime/registry"
3
4
 
4
5
  module Hecks
@@ -45,23 +46,27 @@ module Hecks
45
46
  end
46
47
 
47
48
  # A row with no stored digest yet is backfilled, not refused; only a mismatch raises.
48
- def verify_integrity!(ordinal, text, stored_digest, stored_projection_json)
49
+ # `stored_hash` is the era's name, which a missing projection is backfilled against.
50
+ def verify_integrity!(ordinal, text, stored_digest, stored_projection_json, stored_hash = nil)
49
51
  digest = Digest::SHA256.hexdigest(text)
50
52
  store_missing_digest!(ordinal, digest) if stored_digest.nil?
51
53
  unless stored_digest.nil? || stored_digest == digest
52
54
  raise Runtime::WiringError, Runtime::EraTamper.refusal(domain: @domain, ordinal: ordinal)
53
55
  end
54
56
 
55
- backfill_frozen_facts!(ordinal, text, stored_projection_json)
57
+ backfill_frozen_facts!(ordinal, text, stored_projection_json, stored_hash)
56
58
  end
57
59
 
58
- # Call only on already-verified text; this would bless an unverified edit.
59
- def backfill_frozen_facts!(ordinal, text, stored_projection_json)
60
+ # Call only on already-verified text; this would bless an unverified edit. A missing
61
+ # projection is filled from the text only when the text hashes to the era's stored hash,
62
+ # so it is the whole domain the era was minted from; a domain split across files whose
63
+ # top-level file alone was held stays without one, and reading its shape refuses by name.
64
+ def backfill_frozen_facts!(ordinal, text, stored_projection_json, stored_hash = nil)
60
65
  archive_text!(ordinal, text)
61
- return if stored_projection_json
66
+ return if stored_projection_json || stored_hash.nil?
62
67
 
63
68
  projection = Runtime::EraTamper.project(text)
64
- return unless projection
69
+ return unless projection && Runtime::ProjectedBluebook.new(projection).mint_hash == stored_hash
65
70
 
66
71
  @db.exec_params(
67
72
  "UPDATE hecks_eras SET held_projection = $3::text::jsonb " \
@@ -9,19 +9,66 @@ module Hecks
9
9
  # CRUD over `hecks_eras`/`hecks_era_texts`: era text
10
10
  # integrity, archiving, and once-only era naming.
11
11
  module EraStore
12
- # Verifies each held text's digest before returning it; a missing digest is backfilled.
12
+ # Verifies each held text's digest before returning it; a missing digest is backfilled. A
13
+ # reading lineage verifies without writing.
13
14
  def eras
15
+ return eras_unrecorded if @reading
16
+
14
17
  @db.exec_params(
15
- "SELECT ordinal, hash, label, held_text, watermark, held_digest, held_projection::text " \
18
+ "SELECT ordinal, hash, label, held_text, watermark, held_digest, held_projection::text, edge::text " \
16
19
  "FROM hecks_eras WHERE domain = $1 ORDER BY ordinal",
17
20
  [@domain]
18
21
  ).map do |row|
19
- verify_integrity!(row["ordinal"].to_i, row["held_text"], row["held_digest"], row["held_projection"])
20
- { ordinal: row["ordinal"].to_i, hash: row["hash"], label: row["label"],
21
- held_text: row["held_text"], watermark: row["watermark"]&.to_i }
22
+ verify_integrity!(row["ordinal"].to_i, row["held_text"], row["held_digest"], row["held_projection"],
23
+ row["hash"])
24
+ era_from(row)
22
25
  end
23
26
  end
24
27
 
28
+ # The held eras as stored, each text checked against its digest in memory. Unlike `eras`,
29
+ # nothing is archived or backfilled, so a reading works under a role that may only select.
30
+ #
31
+ # @return [Array<Hash>] the eras, as `eras` answers them
32
+ # @raise [Runtime::WiringError] when a held text does not match its digest
33
+ def eras_unrecorded
34
+ return [] unless table_exists?("hecks_eras")
35
+
36
+ @db.exec_params(
37
+ "SELECT ordinal, hash, label, held_text, watermark, held_digest, held_projection::text, edge::text " \
38
+ "FROM hecks_eras WHERE domain = $1 ORDER BY ordinal",
39
+ [@domain]
40
+ ).map { |row| unrecorded_era(row) }
41
+ end
42
+
43
+ def unrecorded_era(row)
44
+ ordinal = row["ordinal"].to_i
45
+ stored = row["held_digest"]
46
+ if stored && stored != Digest::SHA256.hexdigest(row["held_text"])
47
+ raise Runtime::WiringError, Runtime::EraTamper.refusal(domain: @domain, ordinal: ordinal)
48
+ end
49
+
50
+ era_from(row)
51
+ end
52
+
53
+ # @return [Hash] the era a row holds, as `eras` answers it
54
+ def era_from(row)
55
+ { ordinal: row["ordinal"].to_i, hash: row["hash"], label: row["label"], held_text: row["held_text"],
56
+ watermark: row["watermark"]&.to_i, edge: parsed_edge(row["edge"]),
57
+ projection: parsed_projection(row["held_projection"]) }
58
+ end
59
+
60
+ # @return [Hash, nil] the storage-shape projection the era's mint recorded, string-keyed
61
+ # as `StorageShape.project` answers it, or nil for an era minted without one
62
+ def parsed_projection(text)
63
+ text && JSON.parse(text)
64
+ end
65
+
66
+ # @return [Hash, nil] the edge column as symbol-keyed JSON, or nil for an era a written
67
+ # edge reached
68
+ def parsed_edge(text)
69
+ text && JSON.parse(text, symbolize_names: true)
70
+ end
71
+
25
72
  def current_era
26
73
  held = eras
27
74
  held.empty? ? 1 : held.last[:ordinal]
@@ -60,6 +107,17 @@ module Hecks
60
107
  def last_ordinal
61
108
  @db.exec("SELECT COALESCE(max(ordinal), 0) AS o FROM #{quoted_journal}")[0]["o"].to_i
62
109
  end
110
+
111
+ # How many journal rows above an ordinal belong to one record of one aggregate.
112
+ #
113
+ # @param ordinal [Integer] the ordinal the rows must exceed
114
+ # @param aggregate [String] the aggregate's storage name
115
+ # @param id [String] the record's id
116
+ # @return [Integer]
117
+ def rows_of_after(ordinal, aggregate, id)
118
+ sql = "SELECT count(*) AS n FROM #{quoted_journal} WHERE ordinal > $1 AND aggregate = $2 AND aggregate_id = $3"
119
+ @db.exec_params(sql, [ordinal, aggregate, id])[0]["n"].to_i
120
+ end
63
121
  end
64
122
  end
65
123
  end