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,119 @@
1
+ require_relative "../lineage"
2
+ require_relative "../../../../ports/persistence/plugins/era/storage_shape"
3
+ require_relative "ledger_change"
4
+
5
+ module Hecks
6
+ module Adapters
7
+ class PostgresEra
8
+ module LineageManager
9
+ # The era check of a world that declares `disposable_ledger true` in its PostgresEra
10
+ # `persisted_by` block: a tooling ledger whose rows are worth keeping, not worth an era.
11
+ #
12
+ # Its boot never refuses as unminted or superseded and needs no edge, mint or rehearsal. It
13
+ # holds the first era itself, and on a shape change it re-holds the latest era at the
14
+ # booting shape: an added aggregate gets a head, a widened one keeps its rows as they are,
15
+ # and a replaced one has its rows moved to a dated archive table and starts empty. It names
16
+ # each archive on stderr. A world without the setting never reaches this module.
17
+ module DisposableLedger
18
+ module_function
19
+
20
+ # Whether a world's persistence settings declare the ledger disposable, spelled as a
21
+ # boolean or as the word. Unset is false, so every other world keeps its era guard.
22
+ #
23
+ # @param settings [Hash{Symbol, String => Object}] the persistence settings of the boot
24
+ # @return [Boolean]
25
+ def declared?(settings) = [true, "true"].include?(PostgresEra.setting(settings, :disposable_ledger, default: false))
26
+
27
+ # Brings the ledger to the booting shape and records the era the boot reads.
28
+ #
29
+ # @param registry [Runtime::Registry] the booting registry; `resolved_eras` is set
30
+ # @param bluebook [Bluebook::Declaration] the bluebook booting now
31
+ # @param current_text [String] its source text, held as the era's text
32
+ # @param settings [Hash{Symbol, String => Object}] the persistence settings of the boot
33
+ # @return [nil] a disposable ledger's boot mints no era
34
+ # @raise [Runtime::WiringError] if the connection is refused or its role is unfenced
35
+ #
36
+ # The world's `stored_as` names are applied first: the boot gates run before the wiring
37
+ # check that applies them, and a re-hold provisions heads under the storage names.
38
+ def check!(registry:, bluebook:, current_text:, settings:)
39
+ registry.apply_world_names!
40
+ db = PostgresEra.connect_for(bluebook.name, settings, provision: true)
41
+ lineage = open_lineage(db, bluebook, settings)
42
+ registry.resolved_eras[bluebook.name] = locked(db, bluebook.name) { reconcile!(lineage, bluebook, current_text) }
43
+ grant!(lineage, bluebook, settings, registry.resolved_eras[bluebook.name])
44
+ nil
45
+ ensure
46
+ db&.close
47
+ end
48
+
49
+ # The ordinal the ledger holds once it is at the booting shape.
50
+ #
51
+ # @return [Integer]
52
+ def reconcile!(lineage, bluebook, text)
53
+ latest = lineage.eras.last
54
+ return hold_first!(lineage, bluebook, text) unless latest
55
+
56
+ change = LedgerChange.between(held_projection(latest), bluebook)
57
+ apply!(lineage, bluebook, text, change, latest[:ordinal]) if change.changed?
58
+ latest[:ordinal]
59
+ end
60
+
61
+ # Archives every replaced aggregate, gives each added or replaced one a head over the
62
+ # latest era alone, provisions the read side of every aggregate, and re-holds the era.
63
+ def apply!(lineage, bluebook, text, change, ordinal)
64
+ stamp = Time.now.utc.strftime("%Y%m%d%H%M%S")
65
+ change.replaced.each { |name| archive!(lineage, bluebook, change.storage_name(bluebook, name), stamp) }
66
+ fresh_heads!(lineage, bluebook, change, ordinal)
67
+ bluebook.aggregates.each { |aggregate| lineage.provision_read_side!(aggregate, ordinal) }
68
+ lineage.rehold!(ordinal, text, Runtime::StorageShape.project(bluebook), Runtime::StorageShape.mint_hash(bluebook))
69
+ end
70
+
71
+ # Gives every added or replaced aggregate the booting bluebook declares a head over the
72
+ # latest era alone: none of its rows are in an earlier era.
73
+ def fresh_heads!(lineage, bluebook, change, ordinal)
74
+ (change.added + change.replaced).filter_map { |name| bluebook.aggregate(name) }
75
+ .each { |aggregate| lineage.fresh_head!(aggregate.storage_name, ordinal) }
76
+ end
77
+
78
+ def archive!(lineage, bluebook, storage_name, stamp)
79
+ archive, count = lineage.archive_aggregate!(storage_name, stamp)
80
+ warn "[hecks] #{bluebook.name}: the shape of #{storage_name} changed in a way its rows do not fit; " \
81
+ "archived #{count} journal row#{"s" unless count == 1} to #{archive} and started it empty " \
82
+ "(disposable_ledger)"
83
+ end
84
+
85
+ def hold_first!(lineage, bluebook, text)
86
+ lineage.hold_first!(text, projection: Runtime::StorageShape.project(bluebook))
87
+ lineage.provision_first_era!(bluebook.aggregates)
88
+ 1
89
+ end
90
+
91
+ # The storage shape the latest era holds: the projection its hold recorded, else the
92
+ # shape its held text declares.
93
+ def held_projection(era) = era[:projection] || Runtime::StorageShape.project(LineageManager.shadow(era[:held_text]))
94
+
95
+ def open_lineage(db, bluebook, settings)
96
+ lineage = Lineage.new(db, bluebook.name, formerly_known_as: bluebook.formerly_known_as)
97
+ lineage.check_fence_applies!(allow_superuser: PostgresEra.setting(settings, :allow_superuser, default: false))
98
+ lineage.ensure_base!
99
+ lineage
100
+ end
101
+
102
+ # Holds the domain's era lock, the one a mint takes, for the whole reconcile, so two boots
103
+ # of a changed shape archive once.
104
+ def locked(db, domain)
105
+ db.exec_params("SELECT pg_advisory_lock(hashtext('hecks_eras:' || $1))", [domain])
106
+ yield
107
+ ensure
108
+ db.exec_params("SELECT pg_advisory_unlock(hashtext('hecks_eras:' || $1))", [domain])
109
+ end
110
+
111
+ def grant!(lineage, bluebook, settings, ordinal)
112
+ role = PostgresEra.setting(settings, :role)
113
+ lineage.grant_role!(role.to_s, aggregates: bluebook.aggregates, era: ordinal) unless role.to_s.empty?
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,219 @@
1
+ require_relative "../../../../ports/persistence/plugins/era/storage_shape"
2
+ require_relative "mint_decision"
3
+ require_relative "../lineage"
4
+
5
+ module Hecks
6
+ module Adapters
7
+ class PostgresEra
8
+ module LineageManager
9
+ # The one answer to which era a bluebook's shape resolves to, given the eras its database
10
+ # holds: the boot records it, the boot status reports it, and the rehearsal and the mint
11
+ # read the held era and the match from it. It writes nothing. A status is one of:
12
+ #
13
+ # - `unheld`: the database holds no era for the bluebook.
14
+ # - `current`: the latest held era holds the current shape.
15
+ # - `superseded`: an older held era holds the current shape; a later one superseded it.
16
+ # - `behind`: no era holds the current shape, and a mint from the latest one would go ahead
17
+ # on its translation edge or recorded implicit move.
18
+ # - `unminted`: no era holds the current shape, and no mint from the latest one would go
19
+ # ahead.
20
+ module EraResolution
21
+ # What a resolution concludes. `resolved` is the era the boot reads (nil when `unheld`),
22
+ # `latest` the latest held era, `verdict` the mint decision from the latest era (nil
23
+ # unless the shape is `behind` or `unminted`), and `held_count` how many eras are held.
24
+ Answer = Data.define(:status, :resolved, :latest, :current_label, :verdict, :held_count,
25
+ :breaking) do
26
+ # @return [Boolean] whether the shape is unminted because the move is breaking and no
27
+ # translation edge covers it, so a boot reads nothing under it and refuses
28
+ def breaking? = breaking == true
29
+
30
+ # @return [Boolean] whether a held era holds the current shape
31
+ def matched? = %w[current superseded].include?(status)
32
+
33
+ # The label of the era the boot reads. An era no edge has left yet has no stored name,
34
+ # so its label is worked out in memory, as its mint would name it; naming is a write,
35
+ # so a resolution never stores it.
36
+ #
37
+ # @return [String, nil] the label; nil when no era is held
38
+ # @raise [Runtime::WiringError] when an unnamed era stores no shape (`refuse_unshaped!`)
39
+ def held_label
40
+ stored = resolved&.fetch(:label, nil)
41
+ return stored if stored || resolved.nil?
42
+
43
+ LineageManager.held_hash(resolved)[0, Runtime::StorageShape::LABEL_LENGTH]
44
+ end
45
+
46
+ # @return [Integer, nil] the ordinal of the era the boot reads
47
+ def ordinal = resolved&.fetch(:ordinal, nil)
48
+
49
+ # @return [String, nil] why the status is what it is; nil for `current`
50
+ def reason = Reasons.of(self)
51
+
52
+ # @return [Boolean] whether a mint from the latest era would go ahead on a recorded
53
+ # implicit move rather than a written translation edge
54
+ def implicit? = verdict&.source == "implicit"
55
+
56
+ # @return [String] the mint decision from the latest era and why, as `verdict: reason`
57
+ def decision = "#{verdict.verdict}: #{verdict.reason}"
58
+
59
+ # @return [Hash, nil] the implicit move a `behind` shape would mint on, or nil
60
+ def move
61
+ return unless status == "behind" && implicit?
62
+
63
+ { "from" => held_label, "to" => current_label }
64
+ end
65
+
66
+ # @return [String, nil] a sentence when the read era's stored hash is not the hash
67
+ # of its stored projection (a boot still reads it); nil when they agree or the era
68
+ # stores only one of them
69
+ def warning
70
+ recomputed = projection_hash
71
+ return if recomputed.nil? || recomputed == resolved[:hash]
72
+
73
+ "era #{ordinal} stores hash #{resolved[:hash][0, 6]} but its stored projection hashes to #{recomputed[0, 6]}"
74
+ end
75
+
76
+ # @return [String, nil] the hash of the read era's stored projection; nil when it stores
77
+ # no hash or no projection
78
+ def projection_hash
79
+ return unless resolved && resolved[:hash] && resolved[:projection]
80
+
81
+ Runtime::ProjectedBluebook.new(resolved[:projection]).mint_hash
82
+ end
83
+
84
+ # @return [Hash] the answer as a status row, string-keyed for a JSON document; `warning`
85
+ # appears only when `warning` has something to say
86
+ def to_row
87
+ row = { "status" => status, "held" => held_label, "current" => current_label, "ordinal" => ordinal,
88
+ "move" => move, "reason" => reason, "source" => "database" }
89
+ (note = warning) ? row.merge("warning" => note) : row
90
+ end
91
+ end
92
+
93
+ module_function
94
+
95
+ # Resolves the current shape of a bluebook against the eras its database holds.
96
+ #
97
+ # @param eras [Array<Hash>] the held eras, oldest first, as `Lineage#eras` answers them
98
+ # @param bluebook [Bluebook::Declaration] the bluebook as it stands
99
+ # @param translations [Array<Object>] the translation edges the registry loaded
100
+ # @param directory [String, nil] the domain's bluebook directory, where moves are recorded
101
+ # @yieldreturn [Integer] the ledger watermark, read only when no held era matches
102
+ # @return [Answer]
103
+ def resolve(eras:, bluebook:, translations:, directory:)
104
+ facts = { latest: eras.last, current_label: MintDecision.label_of(bluebook), held_count: eras.size }
105
+ return answer("unheld", nil, facts) if eras.empty?
106
+
107
+ matched = matching_era(eras, bluebook)
108
+ return answer(matched.equal?(eras.last) ? "current" : "superseded", matched, facts) if matched
109
+
110
+ unmatched(facts, bluebook, translations, directory, yield)
111
+ end
112
+
113
+ # Resolves a bluebook against the eras its own database holds, over a connection that
114
+ # can only read: nothing is provisioned, and the session refuses any write.
115
+ #
116
+ # @param bluebook [Bluebook::Declaration] the bluebook as it stands
117
+ # @param settings [Hash] the persistence settings its world declares; needs `database`
118
+ # @param translations [Array<Object>] the translation edges the registry loaded
119
+ # @param directory [String, nil] the domain's bluebook directory
120
+ # @return [Answer]
121
+ # @raise [Runtime::WiringError] if the connection is refused or a held text fails its
122
+ # integrity check
123
+ def read(bluebook:, settings:, translations:, directory: nil)
124
+ db = PostgresEra.connect_for(bluebook.name, settings, provision: false)
125
+ db.exec("SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY")
126
+ lineage = Lineage.new(db, bluebook.name, formerly_known_as: bluebook.formerly_known_as, reading: true)
127
+ resolve(eras: lineage.eras, bluebook: bluebook, translations: translations, directory: directory) do
128
+ lineage.last_ordinal
129
+ end
130
+ ensure
131
+ db&.close
132
+ end
133
+
134
+ # The held era whose shape is the bluebook's: the latest when it holds it, else the
135
+ # oldest that does; nil when none holds it.
136
+ #
137
+ # @param eras [Array<Hash>] the held eras, oldest first
138
+ # @param bluebook [Bluebook::Declaration] the bluebook as it stands
139
+ # @return [Hash, nil] the matching era
140
+ def matching_era(eras, bluebook)
141
+ current = Runtime::StorageShape.project(bluebook)
142
+ [eras.last, *eras].find { |era| held_shape_of(era, bluebook) == current }
143
+ end
144
+
145
+ # An era's storage shape, with a prior name normalized to the bluebook's. Held text is a
146
+ # frozen record and keeps the prior name; only the in-memory shape is renamed, and only on
147
+ # an exact match with the declared prior name.
148
+ #
149
+ # @param era [Hash] a held era
150
+ # @param bluebook [Bluebook::Declaration] the bluebook as it stands
151
+ # @return [Hash] the storage shape the era holds
152
+ def held_shape_of(era, bluebook)
153
+ shape = LineageManager.held_shape(era)
154
+ prior = bluebook.formerly_known_as
155
+ prior && shape["name"] == prior ? shape.merge("name" => bluebook.name) : shape
156
+ end
157
+
158
+ # @return [Answer] a `behind` or `unminted` answer, by the latest era's mint decision
159
+ def unmatched(facts, bluebook, translations, directory, watermark)
160
+ verdict = MintDecision.decide_latest(facts[:latest], bluebook: bluebook, translations: translations,
161
+ directory: directory, watermark: watermark)
162
+ status = verdict.verdict == "would_mint" ? "behind" : "unminted"
163
+ breaking = MintDecision.breaking?(facts[:latest], bluebook, verdict, translations)
164
+ answer(status, facts[:latest], facts, verdict, breaking: breaking)
165
+ end
166
+
167
+ # @return [Answer]
168
+ def answer(status, resolved, facts, verdict = nil, breaking: false)
169
+ Answer.new(status: status, resolved: resolved, verdict: verdict, breaking: breaking, **facts)
170
+ end
171
+
172
+ # The sentence a status row gives for its status.
173
+ module Reasons
174
+ REMEDY = "write an edge (`era.scaffold_translation`) and mint it with the release mint".freeze
175
+
176
+ module_function
177
+
178
+ # @param answer [Answer] the resolution
179
+ # @return [String, nil] the reason; nil for `current`
180
+ def of(answer)
181
+ case answer.status
182
+ when "unheld" then "no era is held; the mint entry point holds the first"
183
+ when "superseded" then superseded(answer)
184
+ when "behind" then behind(answer)
185
+ when "unminted" then unminted(answer)
186
+ end
187
+ end
188
+
189
+ # @return [String]
190
+ def superseded(answer)
191
+ "era #{answer.ordinal} holds the current shape #{answer.current_label}; era " \
192
+ "#{answer.latest[:ordinal]} (#{answer.latest[:label]}) superseded it"
193
+ end
194
+
195
+ # @return [String]
196
+ def behind(answer)
197
+ "no era holds #{answer.current_label}; a mint from era #{answer.ordinal} (#{answer.held_label}) " \
198
+ "would go ahead on its #{answer.implicit? ? "recorded implicit move" : "translation edge"}"
199
+ end
200
+
201
+ # @return [String] the refusal a boot over a breaking move gives
202
+ def breaking(answer)
203
+ "no translation edge covers it: #{answer.current_label} is not an additive move from " \
204
+ "era #{answer.ordinal} (#{answer.held_label}); #{REMEDY}"
205
+ end
206
+
207
+ # @return [String]
208
+ def unminted(answer)
209
+ return breaking(answer) if answer.breaking?
210
+
211
+ "no era holds #{answer.current_label}; a mint from era #{answer.ordinal} (#{answer.held_label}) " \
212
+ "is #{answer.decision}"
213
+ end
214
+ end
215
+ end
216
+ end
217
+ end
218
+ end
219
+ end
@@ -1,114 +1,111 @@
1
1
  require_relative "../lineage"
2
2
  require_relative "../../../../ports/persistence/plugins/era/storage_shape"
3
3
  require_relative "context"
4
+ require_relative "mint_request"
5
+ require_relative "mint_decision"
6
+ require_relative "era_resolution"
7
+ require_relative "disposable_ledger"
4
8
  require_relative "../../../../indifferent_key"
5
9
 
6
10
  module Hecks
7
11
  module Adapters
8
12
  class PostgresEra
9
13
  module LineageManager
10
- # Boot-time era resolution: first boot holds era 1, a quiet reboot changes nothing,
11
- # a superseded shape boots read-only, an unheld shape goes to the minter.
14
+ # Boot-time era resolution. A boot reads: it reports the era its shape resolves to, and
15
+ # writes nothing. The mint entry point (a settings `mint_request`) is the only path that
16
+ # provisions the lineage or mints an era.
12
17
  module EraResolver
13
18
  # Whether this copy may mint when its world says nothing about it. False, so a copy that
14
- # was not told refuses, and only the copy that owns a database moves it.
19
+ # was not told refuses, and only the copy that owns a database moves it. A mint also needs
20
+ # the mint entry point's request (`mint_request` in the settings): a boot never mints.
15
21
  MINT_BY_DEFAULT = false
16
22
 
17
23
  # Resolves which era this boot is and records it on the registry.
18
24
  #
25
+ # A boot with no mint request opens its lineage for reading and never provisions, holds,
26
+ # mints or grants. A boot with a request provisions the lineage first, then mints if the
27
+ # shape drifted.
28
+ #
19
29
  # @return [Integer, nil] the ordinal minted, or nil when this boot minted nothing
20
30
  # @raise [Runtime::WiringError] if the connection is refused, a held text fails its
21
- # integrity check, or the shape is unheld and `mint!` refuses
31
+ # integrity check, no era is held, or the shape is unheld and `mint!` refuses
22
32
  def check!(registry:, bluebook:, current_text:, settings:, directory: nil)
23
- db = PostgresEra.connect_for(bluebook.name, settings)
24
- lineage = Lineage.new(db, bluebook.name, formerly_known_as: bluebook.formerly_known_as)
25
- # First, before ensure_base!: the earliest point the connection's role is known,
26
- # so a superuser connection refuses here by default.
27
- lineage.check_fence_applies!(allow_superuser: PostgresEra.setting(settings, :allow_superuser, default: false))
28
- lineage.ensure_base!
29
- # key? first, never `||`: a stored `false` must not read as an absent key.
30
- role = IndifferentKey.read(settings, :role)
33
+ # A world that declares its ledger disposable has no era guard: see `DisposableLedger`.
34
+ return DisposableLedger.check!(registry:, bluebook:, current_text:, settings:) if DisposableLedger.declared?(settings)
35
+
36
+ request = MintRequest.from(settings)
37
+ db = PostgresEra.connect_for(bluebook.name, settings, provision: !request.nil?)
38
+ lineage = open_lineage(db, bluebook, settings, request)
31
39
  resolve_era(Context.new(registry: registry, bluebook: bluebook, current_text: current_text,
32
- lineage: lineage, role: role, directory: directory,
33
- mint_permitted: mint_permitted?(settings)))
40
+ lineage: lineage, role: IndifferentKey.read(settings, :role),
41
+ directory: directory, mint_permitted: mint_permitted?(settings),
42
+ mint_request: request))
34
43
  ensure
35
44
  db&.close
36
45
  end
37
46
 
38
47
  private
39
48
 
49
+ # Opens the lineage: for reading on a boot, and provisioned first on a mint request. Either
50
+ # refuses, by name, while the domain's history still sits under its former name.
51
+ def open_lineage(db, bluebook, settings, request)
52
+ lineage = Lineage.new(db, bluebook.name, formerly_known_as: bluebook.formerly_known_as,
53
+ reading: request.nil?)
54
+ # First, before anything else: the earliest point the connection's role is known,
55
+ # so a superuser connection refuses here by default.
56
+ lineage.check_fence_applies!(allow_superuser: PostgresEra.setting(settings, :allow_superuser, default: false))
57
+ request ? lineage.ensure_base! : lineage.refuse_pending_rename!
58
+ lineage
59
+ end
60
+
40
61
  # Whether the world allows minting, spelled as a boolean or as the word.
41
62
  def mint_permitted?(settings)
42
63
  [true, "true"].include?(PostgresEra.setting(settings, :mint_eras, default: MINT_BY_DEFAULT))
43
64
  end
44
65
 
66
+ # Resolves the boot's shape through `EraResolution`, the answer every era tool reads, and
67
+ # records it on the registry; a mint request on a shape no era holds mints instead.
45
68
  def resolve_era(context)
46
- held = context.held_eras
47
- return hold_first_era(context) if held.empty?
48
-
49
- resolve_held(context, held)
50
- end
51
-
52
- # Some era is held: the latest, a superseded one, or none matches and a new one is minted.
53
- def resolve_held(context, held)
54
- shapes = held_shapes(context.bluebook, held)
55
- latest, latest_shape = shapes.last
56
- current_shape = context.current_shape
57
- return resolve_latest(context, latest) if latest_shape == current_shape
58
-
59
- matched, = shapes.find { |_, shape| shape == current_shape }
60
- return resolve_superseded(context, matched, latest) if matched
61
-
62
- context.record_resolved(mint!(context.with(latest: latest)))
63
- end
64
-
65
- def hold_first_era(context)
66
- context.lineage.hold_first!(context.current_text, projection: context.current_shape)
67
- ensure_first_heads!(context)
68
- grant_role!(context, 1)
69
- nil
70
- end
69
+ answer = EraResolution.resolve(eras: context.held_eras, bluebook: context.bluebook,
70
+ translations: context.translations, directory: context.directory) do
71
+ context.watermark
72
+ end
73
+ return refuse_unheld!(context) if answer.status == "unheld"
74
+ return context.record_resolved(mint!(context.with(latest: answer.latest))) if minting?(answer, context)
71
75
 
72
- # The latest held era is the current shape: nothing to mint.
73
- def resolve_latest(context, latest)
74
- ensure_first_heads!(context) if latest[:ordinal] == 1
75
- grant_role!(context, latest[:ordinal])
76
- context.record_resolved(latest[:ordinal])
77
- nil
78
- end
76
+ return refuse_breaking!(context, answer) if answer.breaking?
79
77
 
80
- # A superseded era (an old checkout) may read but not write: never advance_era!
81
- # here, since the fence is already past this ordinal.
82
- def resolve_superseded(context, matched, latest)
83
- grant_role!(context, matched[:ordinal])
84
- context.record_resolved(matched[:ordinal])
85
- # The adapter refuses appends on this, even where the RLS fence cannot bind.
86
- context.record_superseded(latest[:ordinal])
87
- nil
78
+ record(context, answer)
88
79
  end
89
80
 
90
- def ensure_first_heads!(context)
91
- lineage = context.lineage
92
- context.aggregates.each { |aggregate| lineage.ensure_first_head!(aggregate.storage_name) }
81
+ # A breaking move no edge covers: the boot reads nothing and writes nothing. This stands
82
+ # in until the commit-time gate (ADR 0168) keeps such a move off main.
83
+ def refuse_breaking!(context, answer)
84
+ raise Runtime::WiringError, "cannot boot #{context.domain_name}: #{answer.reason}"
93
85
  end
94
86
 
95
- def grant_role!(context, ordinal)
96
- return unless context.role
87
+ # Whether this check mints: a mint request, on a shape no held era holds.
88
+ def minting?(answer, context) = context.mint_request && !answer.matched?
97
89
 
98
- lineage = context.lineage
99
- lineage.grant_role!(context.role, aggregates: context.aggregates, era: ordinal)
90
+ # No era is held: the first era is held by the mint entry point, never by a boot.
91
+ def refuse_unheld!(context)
92
+ raise Runtime::WiringError,
93
+ "cannot boot #{context.domain_name}: no era held; run the mint entry point " \
94
+ "(`hecks era.hold_first! <domain directory> --confirm`) to hold the first era"
100
95
  end
101
96
 
102
- # Each held era with its storage shape. Held text is a frozen record and keeps the
103
- # prior name; only the in-memory shape is normalized, and only on an exact match with
104
- # the declared prior name.
105
- def held_shapes(bluebook, held)
106
- shapes = held.map { |era| [era, Runtime::StorageShape.project(shadow(era[:held_text]))] }
107
- return shapes unless bluebook.formerly_known_as
108
-
109
- shapes.map do |era, shape|
110
- [era, shape["name"] == bluebook.formerly_known_as ? shape.merge("name" => bluebook.name) : shape]
111
- end
97
+ # Records what the resolution answered and writes nothing. A superseded era (an old
98
+ # checkout) reads and never writes: the adapter refuses appends on it, even where the RLS
99
+ # fence cannot bind. A shape no era holds reads the latest held era, and is behind when a
100
+ # mint from it would go ahead; the mint entry point runs that mint.
101
+ def record(context, answer)
102
+ context.record_resolved(answer.ordinal)
103
+ latest = answer.latest[:ordinal]
104
+ status = answer.status
105
+ context.record_superseded(latest) if status == "superseded"
106
+ context.record_unminted(latest) if %w[unminted behind].include?(status)
107
+ context.record_behind(latest) if status == "behind"
108
+ nil
112
109
  end
113
110
  end
114
111
  end
@@ -0,0 +1,68 @@
1
+ require_relative "../../../../ports/persistence/plugins/era/storage_shape"
2
+ require_relative "../../../../ports/persistence/plugins/era/projected_bluebook"
3
+ require_relative "../../../../runtime/errors"
4
+
5
+ module Hecks
6
+ module Adapters
7
+ class PostgresEra
8
+ module LineageManager
9
+ # What a held era is: the storage shape its mint recorded, and the hash of that shape.
10
+ #
11
+ # Held text is kept for audit and decides nothing. An era minted without a stored shape is
12
+ # read from its held text only when that text is the whole domain, which it is when it
13
+ # hashes to the era's stored hash; otherwise the era is refused by name. LineageManager
14
+ # extends it, so each method is called on LineageManager.
15
+ module HeldEra
16
+ # @param era [Hash] an era as `Lineage#eras` answers it: `projection` when its mint
17
+ # recorded one, `hash` once it is named, and `held_text`
18
+ # @return [Hash] the storage shape the era holds
19
+ # @raise [Runtime::WiringError] naming the era, when it stores no shape and its held text
20
+ # does not hash to its stored hash
21
+ def held_shape(era) = era[:projection] || verified_text_shape(era) || refuse_unshaped!(era)
22
+
23
+ # The held era as a bluebook view over its storage shape, for a diff, the additive rule or
24
+ # the coverage check.
25
+ #
26
+ # @param era [Hash] an era as `Lineage#eras` answers it
27
+ # @return [Runtime::ProjectedBluebook]
28
+ # @raise [Runtime::WiringError] as `held_shape`
29
+ def held_bluebook(era) = Runtime::ProjectedBluebook.new(held_shape(era))
30
+
31
+ # The era's hash: the one it stores once named, else the hash of the shape it holds.
32
+ #
33
+ # @param era [Hash] an era as `Lineage#eras` answers it
34
+ # @return [String] 64 lowercase hex characters
35
+ def held_hash(era) = era[:hash] || held_bluebook(era).mint_hash
36
+
37
+ # The shape an era's held text declares, when the text hashes to the era's stored hash and
38
+ # so is the whole domain the era was minted from; nil otherwise.
39
+ #
40
+ # @param era [Hash] an era with no stored shape
41
+ # @return [Hash, nil]
42
+ def verified_text_shape(era)
43
+ return unless era[:hash] && era[:held_text]
44
+
45
+ declared = shadow(era[:held_text])
46
+ Runtime::StorageShape.project(declared) if declared && Runtime::StorageShape.mint_hash(declared) == era[:hash]
47
+ rescue StandardError, SyntaxError
48
+ nil
49
+ end
50
+
51
+ # @raise [Runtime::WiringError] always, naming the era and why its shape is unknown
52
+ def refuse_unshaped!(era)
53
+ raise Runtime::WiringError,
54
+ "era #{era[:ordinal]} (#{era[:label] || "unnamed"}) stores no storage shape, and its held " \
55
+ "text #{unverified_reason(era)}, so it is not the whole domain the era was minted from"
56
+ end
57
+
58
+ # @return [String] why an era's held text cannot stand in for its missing shape
59
+ def unverified_reason(era)
60
+ return "has no stored hash to check it against" unless era[:hash]
61
+
62
+ "does not hash to its stored hash"
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,63 @@
1
+ require_relative "../../../../ports/persistence/plugins/era/storage_shape"
2
+ require_relative "../../../../naming"
3
+ require_relative "../../../../ports/persistence/plugins/era/translation/additive_shape"
4
+
5
+ module Hecks
6
+ module Adapters
7
+ class PostgresEra
8
+ module LineageManager
9
+ # The change between the storage shape a disposable ledger holds and the shape it boots
10
+ # with, aggregate by aggregate. `added` names aggregates the held shape did not have,
11
+ # `widened` those that only gained optional attributes, and `replaced` those whose stored
12
+ # rows do not fit the booting shape: removed, or with an attribute renamed, dropped,
13
+ # retyped, switched between list and scalar, or a changed identity or lifecycle field. It
14
+ # reads and writes nothing.
15
+ LedgerChange = Data.define(:added, :widened, :replaced) do
16
+ # @param held_projection [Hash] the held era's storage shape, as `StorageShape.project`
17
+ # answers it
18
+ # @param bluebook [Bluebook::Declaration] the bluebook booting now
19
+ # @return [LedgerChange]
20
+ def self.between(held_projection, bluebook)
21
+ held = by_name(held_projection)
22
+ current = by_name(Runtime::StorageShape.project(bluebook))
23
+ widened, broken = changed_in_both(held, current).partition do |name|
24
+ widened?(held[name], current[name], bluebook.aggregate(name))
25
+ end
26
+ new(added: missing(held, current), widened: widened, replaced: broken + missing(current, held))
27
+ end
28
+
29
+ # @return [Hash{String => Hash}] each projected aggregate by its name
30
+ def self.by_name(projection) = projection.fetch("aggregates", []).to_h { |aggregate| [aggregate["name"], aggregate] }
31
+
32
+ # @return [Array<String>] the aggregates `to` has and `from` does not
33
+ def self.missing(from, to) = to.keys - from.keys
34
+
35
+ # @return [Array<String>] the aggregates both shapes have, whose shapes differ
36
+ def self.changed_in_both(held, current) = (held.keys & current.keys).reject { |name| held[name] == current[name] }
37
+
38
+ # Whether an aggregate kept its identity, lifecycle field and every attribute as it was
39
+ # held, list or scalar included, and gained only attributes it declares optional
40
+ # (`Translation::AdditiveShape`). An attribute that became required is not seen: the
41
+ # held projection does not record `optional`.
42
+ #
43
+ # @return [Boolean]
44
+ def self.widened?(held, current, aggregate)
45
+ Translation::AdditiveShape.widened?(held, current, optional: Translation::AdditiveShape.optional_in(aggregate))
46
+ end
47
+ private_class_method :by_name, :changed_in_both, :widened?, :missing
48
+
49
+ # @return [Boolean] whether the held shape and the booting shape differ at all
50
+ def changed? = !(added.empty? && widened.empty? && replaced.empty?)
51
+
52
+ # The storage name of a replaced aggregate: the booting bluebook's own, else the name the
53
+ # held shape gave it, snake-cased the way an aggregate names its storage.
54
+ #
55
+ # @param bluebook [Bluebook::Declaration] the bluebook booting now
56
+ # @param name [String] a name from `replaced`
57
+ # @return [String]
58
+ def storage_name(bluebook, name) = bluebook.aggregate(name)&.storage_name || Naming.snake(name)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end