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,107 @@
1
+ require_relative "../storage_shape"
2
+ require_relative "../projected_bluebook"
3
+
4
+ module Hecks
5
+ module Translation
6
+ # The one rule for whether a change to an aggregate's storage shape only adds: its identity and
7
+ # lifecycle field are as they were, every attribute it held is held exactly as before (same
8
+ # name, same list or scalar form, same type with the same members), and each attribute it gained
9
+ # is optional. Anything else is breaking, and a caller that cannot tell treats it as breaking.
10
+ #
11
+ # The implicit edge and the disposable ledger both ask it, so the two never disagree.
12
+ #
13
+ # **Known limits.** A stored projection leaves out `optional` and a lifecycle's states, so an
14
+ # attribute that became required, or a state that was removed, is seen only when the held side
15
+ # is a full declaration; between two projections it cannot be told from an unchanged shape.
16
+ module AdditiveShape
17
+ # The parts of a projected aggregate besides its attributes that its stored rows depend on.
18
+ KEYS = %w[identity lifecycle_field].freeze
19
+
20
+ module_function
21
+
22
+ # @param held [Hash] the held aggregate's projection, as `StorageShape.project` builds it
23
+ # @param current [Hash] the booting aggregate's projection
24
+ # @param optional [#call] answers, given a name, whether the booting aggregate declares
25
+ # that attribute optional
26
+ # @param was_optional [#call, nil] answers whether the held aggregate declared an attribute
27
+ # optional, or nil when the held side is a projection that does not say
28
+ # @return [Boolean] whether `current` only adds to `held`
29
+ def widened?(held, current, optional:, was_optional: nil)
30
+ return false unless held.values_at(*KEYS) == current.values_at(*KEYS)
31
+ return false unless (held["attributes"] - current["attributes"]).empty?
32
+ return false if tightened?(held, optional, was_optional)
33
+
34
+ (names(current) - names(held)).all? { |name| optional.call(name) }
35
+ end
36
+
37
+ # Whether an attribute the held declaration marked optional is required now.
38
+ #
39
+ # @return [Boolean]
40
+ def tightened?(held, optional, was_optional)
41
+ return false unless was_optional
42
+
43
+ names(held).any? { |name| was_optional.call(name) && !optional.call(name) }
44
+ end
45
+
46
+ # @return [Array<String>] the attribute names of a projected aggregate
47
+ def names(projected) = projected["attributes"].map { |attribute| attribute["name"] }
48
+
49
+ # Whether every aggregate present in both shapes only adds. A shared aggregate that is
50
+ # unchanged counts; one the current side lacks or the held side lacks is not this check's
51
+ # business (the scaffold diff names renames, retirements and new aggregates).
52
+ #
53
+ # @param held [Hash, Runtime::ProjectedBluebook, Bluebook::Declaration] the held side
54
+ # @param current [Bluebook::Declaration] the booting bluebook
55
+ # @return [Boolean]
56
+ def additive?(held, current)
57
+ held_by = by_name(shape_of(held))
58
+ by_name(Runtime::StorageShape.project(current)).all? do |name, projected|
59
+ held_by[name].nil? || aggregate_additive?(held, held_by[name], projected, current.aggregate(name))
60
+ end
61
+ end
62
+
63
+ # @return [Boolean] whether one aggregate present in both shapes only adds
64
+ def aggregate_additive?(held, before, after, aggregate)
65
+ states_kept?(held, aggregate) &&
66
+ widened?(before, after, optional: optional_in(aggregate), was_optional: was_optional_in(held, aggregate.name))
67
+ end
68
+
69
+ # Whether every lifecycle state the held declaration had is still declared. A held side that
70
+ # is a projection records no states, so it answers true.
71
+ #
72
+ # @return [Boolean]
73
+ def states_kept?(held, aggregate)
74
+ return true if held.is_a?(Hash) || held.is_a?(Runtime::ProjectedBluebook)
75
+
76
+ before = states_of(held.aggregate(aggregate.name))
77
+ before.nil? || ((before - Array(states_of(aggregate))).empty? && !states_of(aggregate).nil?)
78
+ end
79
+
80
+ # @return [Array<String>, nil] the lifecycle states an aggregate declares; nil without one
81
+ def states_of(aggregate)
82
+ lifecycle = aggregate&.lifecycle
83
+ lifecycle&.states
84
+ end
85
+
86
+ # @return [Hash] the storage shape of any held side
87
+ def shape_of(held)
88
+ held = Runtime::ProjectedBluebook.wrap(held)
89
+ held.is_a?(Runtime::ProjectedBluebook) ? held.projection : Runtime::StorageShape.project(held)
90
+ end
91
+
92
+ # @return [Hash{String => Hash}] each projected aggregate by name
93
+ def by_name(shape) = shape["aggregates"].to_h { |aggregate| [aggregate["name"], aggregate] }
94
+
95
+ # @return [#call] whether the aggregate declares the named attribute optional
96
+ def optional_in(aggregate) = ->(name) { aggregate&.attribute(name)&.optional? || false }
97
+
98
+ # @return [#call, nil] whether the held declaration's aggregate declared the named attribute
99
+ # optional; nil for a projection, which does not record it
100
+ def was_optional_in(held, name)
101
+ return nil if held.is_a?(Hash) || held.is_a?(Runtime::ProjectedBluebook)
102
+
103
+ optional_in(held.aggregate(name))
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../../../../bluebook/translation"
4
+ require_relative "compose_render"
5
+
6
+ module Hecks
7
+ module Translation
8
+ # Composes the chain of translation edges from a held era to the current shape into the one
9
+ # edge that leads straight there. Rendering the edge as `.bluebook` text is `ComposeRender`.
10
+ #
11
+ # Composition is exact only where the chain is unambiguous. An aggregate's field rules (renames,
12
+ # drops, derives and the rest) may appear in one step of the chain; two steps' rules on the same
13
+ # aggregate would need their order and names reconciled, and that is a decision for a person.
14
+ # Aggregate names are followed through every step, so a rename in any step survives into the
15
+ # composed edge as `was:`.
16
+ module Compose
17
+ # The field-level rule lists a `Bluebook::TranslationAggregate` carries.
18
+ FIELD_RULES = %i[renames moves converts drops retypes derives rekeys backfills splits].freeze
19
+
20
+ # The rules no rendering covers yet; a chain that carries one is refused, not guessed at.
21
+ UNRENDERABLE = %i[rekeys splits].freeze
22
+
23
+ module_function
24
+
25
+ # Walks the edges that lead one shape label to the next until the current shape.
26
+ #
27
+ # @param translations [Array<Bluebook::Translation>] every loaded edge, any domain
28
+ # @param domain [String] the domain whose edges count
29
+ # @param from [String] the held era's shape label
30
+ # @param to [String] the current shape label
31
+ # A held era whose label is the current one but whose projection differs has no chain to walk:
32
+ # its one step is the edge from that label to itself, when such an edge exists.
33
+ #
34
+ # @return [Array<Bluebook::Translation>] the edges in order; empty when none leads `from` to `to`
35
+ # and `from` is `to`
36
+ # @raise [Runtime::WiringError] when a step has no edge, has two, or the walk loops
37
+ def chain(translations, domain:, from:, to:)
38
+ return self_edges(translations, domain, from) if from == to
39
+
40
+ steps = []
41
+ label = from
42
+ until label == to
43
+ step = next_step(translations, domain, label, to)
44
+ raise_loop!(domain, step.to) if [from, *steps.map(&:to)].include?(step.to)
45
+ steps << step
46
+ label = step.to
47
+ end
48
+ steps
49
+ end
50
+
51
+ # @return [Bluebook::Translation] the one edge from the first step's origin to the last step's target
52
+ # @raise [Runtime::WiringError] when a step's rules cannot be composed into one edge
53
+ def compose(domain:, from:, to:, chain:)
54
+ refuse_unrenderable!(chain)
55
+ origins, retired, ruled = track_chain(chain)
56
+ Bluebook::Translation.new(domain: domain, from: from, to: to,
57
+ aggregates: composed_aggregates(origins, ruled),
58
+ retired: retired.reject { |name| origins.key?(name) })
59
+ end
60
+
61
+ def self_edges(translations, domain, label)
62
+ translations.select { |edge| edge.domain == domain && edge.from == label && edge.to == label }
63
+ end
64
+
65
+ # @return [Bluebook::Translation] the step leaving `label`, the only one that does
66
+ # @raise [Runtime::WiringError] when none leaves it or two do
67
+ def next_step(translations, domain, label, to)
68
+ leaving = translations.select { |edge| edge.domain == domain && edge.from == label }
69
+ refuse_broken!(domain, label, to) if leaving.empty?
70
+ refuse_forked!(domain, label, leaving) if leaving.size > 1
71
+ leaving.first
72
+ end
73
+
74
+ def refuse_broken!(domain, label, to)
75
+ raise Runtime::WiringError,
76
+ "cannot compose #{domain}: the edge chain is broken at #{label} — no translation leads " \
77
+ "#{label} onward to #{to}; restore translations/ or write the missing edge"
78
+ end
79
+
80
+ def refuse_forked!(domain, label, leaving)
81
+ raise Runtime::WiringError,
82
+ "cannot compose #{domain}: #{leaving.size} translations leave #{label} " \
83
+ "(#{leaving.map(&:to).join(", ")}); one must go before the chain can compose"
84
+ end
85
+
86
+ def raise_loop!(domain, label)
87
+ raise Runtime::WiringError, "cannot compose #{domain}: the edge chain loops back to #{label}"
88
+ end
89
+
90
+ def refuse_unrenderable!(chain)
91
+ chain.each do |step|
92
+ step.aggregates.each do |aggregate|
93
+ next if UNRENDERABLE.all? { |rule| aggregate.public_send(rule).empty? }
94
+
95
+ raise Runtime::WiringError,
96
+ "cannot compose #{step.domain}: #{aggregate.name} carries a rekey or split, which " \
97
+ "composes by hand; write the composed edge yourself"
98
+ end
99
+ end
100
+ end
101
+
102
+ # Follows each aggregate from its held name to its current one, and finds the one step that
103
+ # carries field rules for each origin.
104
+ #
105
+ # @return [Array(Hash, Array<String>, Hash)] origins keyed by current name, every retired name,
106
+ # and the aggregate carrying field rules for each origin
107
+ def track_chain(chain)
108
+ state = { origins: {}, retired: [], ruled: {} }
109
+ chain.each_with_index { |step, index| track_step(state, step, index) }
110
+ [state[:origins], state[:retired].uniq, state[:ruled].transform_values(&:last)]
111
+ end
112
+
113
+ def track_step(state, step, index)
114
+ step.aggregates.each { |aggregate| track_aggregate(state, step.domain, aggregate, index) }
115
+ state[:retired].concat(step.retired)
116
+ end
117
+
118
+ def track_aggregate(state, domain, aggregate, index)
119
+ origin = track(state[:origins], aggregate)
120
+ return if FIELD_RULES.all? { |rule| aggregate.public_send(rule).empty? }
121
+
122
+ refuse_two_steps!(domain, aggregate.name, state[:ruled][origin], index) if state[:ruled][origin]
123
+ state[:ruled][origin] = [index, aggregate]
124
+ end
125
+
126
+ # Records an aggregate under its current name, keeping the name it was held under.
127
+ def track(origins, aggregate)
128
+ previous = aggregate.was || aggregate.name
129
+ origin = origins.delete(previous) || previous
130
+ origins[aggregate.name] = origin
131
+ end
132
+
133
+ # Builds the composed aggregates: one per aggregate that was renamed or carries field rules.
134
+ def composed_aggregates(origins, ruled)
135
+ origins.filter_map do |name, origin|
136
+ source = ruled[origin]
137
+ next if source.nil? && origin == name
138
+
139
+ composed_aggregate(name, origin, source)
140
+ end
141
+ end
142
+
143
+ def refuse_two_steps!(domain, name, earlier, index)
144
+ raise Runtime::WiringError,
145
+ "cannot compose #{domain}: #{name} has field rules in steps #{earlier.first + 1} and " \
146
+ "#{index + 1} of the chain; compose those by hand"
147
+ end
148
+
149
+ def composed_aggregate(name, origin, source)
150
+ rules = source ? FIELD_RULES.to_h { |rule| [rule, source.public_send(rule)] } : {}
151
+ Bluebook::TranslationAggregate.new(name: name, was: (origin == name ? nil : origin), **rules)
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hecks
4
+ module Translation
5
+ # Renders a composed edge as `.bluebook` text, the form the era gate and the audit read back.
6
+ module ComposeRender
7
+ module_function
8
+
9
+ # The `.bluebook` text of one edge, with a comment naming the chain it composes.
10
+ #
11
+ # @param edge [Bluebook::Translation] the composed edge
12
+ # @param labels [Array<String>] the shape labels the chain passes through, in order
13
+ # @return [String] the text, ending in a newline
14
+ def render(edge, labels: [])
15
+ lines = [*comment_lines(labels), header(edge)]
16
+ edge.aggregates.each { |aggregate| lines.concat(aggregate_lines(aggregate)) }
17
+ edge.retired.each { |name| lines << " retired #{name.inspect}" }
18
+ lines << "end"
19
+ "#{lines.join("\n")}\n"
20
+ end
21
+
22
+ def header(edge)
23
+ "Hecks.data_translation #{edge.domain.inspect}, from: #{edge.from.inspect}, to: #{edge.to.inspect} do"
24
+ end
25
+
26
+ def comment_lines(labels)
27
+ return [] if labels.empty?
28
+
29
+ ["# Composed from the chain #{labels.join(" -> ")}: the rules of each step, carried into one edge."]
30
+ end
31
+
32
+ def aggregate_lines(aggregate)
33
+ header = " aggregate #{aggregate.name.inspect}"
34
+ header += ", was: #{aggregate.was.inspect}" if aggregate.was
35
+ ["#{header} do", *rule_lines(aggregate).map { |line| " #{line}" }, " end"]
36
+ end
37
+
38
+ # Each field rule as the line that declares it, in the order the bluebook reads.
39
+ def rule_lines(aggregate)
40
+ renames(aggregate) + moves(aggregate) + converts(aggregate) + drops(aggregate) +
41
+ retypes(aggregate) + derives(aggregate) + backfills(aggregate)
42
+ end
43
+
44
+ def renames(aggregate)
45
+ aggregate.renames.map { |old_name, new_name| "rename #{symbol(old_name)}, to: #{symbol(new_name)}" }
46
+ end
47
+
48
+ def moves(aggregate)
49
+ aggregate.moves.map { |move| "move #{move.from.inspect}, to: #{move.to.inspect}" }
50
+ end
51
+
52
+ def converts(aggregate)
53
+ aggregate.converts.map do |convert|
54
+ "convert #{convert.from.inspect}, to: #{convert.to.inspect}, values: #{convert.values.inspect}"
55
+ end
56
+ end
57
+
58
+ def drops(aggregate) = aggregate.drops.map { |path| "drop #{path.to_s.inspect}" }
59
+
60
+ def retypes(aggregate)
61
+ aggregate.retypes.map { |retype| "retype #{retype.from.inspect}, to: #{retype.to.inspect}" }
62
+ end
63
+
64
+ def derives(aggregate)
65
+ aggregate.derives.map do |derive|
66
+ "derive #{symbol(derive.to)}, as: #{derive.expression.to_s.inspect}"
67
+ end
68
+ end
69
+
70
+ def backfills(aggregate)
71
+ aggregate.backfills.map do |backfill|
72
+ "backfill #{symbol(backfill.name)}, default: #{backfill.default.inspect}"
73
+ end
74
+ end
75
+
76
+ def symbol(name) = ":#{name}"
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hecks
4
+ module Translation
5
+ # The edge files of a domain's `translations/` directory, read by their headers.
6
+ #
7
+ # A header names the domain and the two shape labels an edge leads between, so a file is found
8
+ # without loading it: the writer needs to know which files a chain uses before it changes any.
9
+ module EdgeFiles
10
+ # One edge file: where it is and the header it declares.
11
+ Found = Struct.new(:path, :domain, :from, :to, keyword_init: true)
12
+
13
+ HEADER = /^Hecks\.data_translation\s+"(?<domain>[^"]+)",\s*from:\s*"(?<from>[^"]+)",\s*to:\s*"(?<to>[^"]+)"/
14
+
15
+ module_function
16
+
17
+ # @param directory [String] the domain's bluebook directory
18
+ # @return [Array<Found>] every `translations/*.bluebook` file whose header parses, by path
19
+ def scan(directory)
20
+ Dir[File.join(directory, "translations", "*.bluebook")].filter_map do |path|
21
+ match = HEADER.match(File.read(path))
22
+ Found.new(path: path, domain: match[:domain], from: match[:from], to: match[:to]) if match
23
+ end
24
+ end
25
+
26
+ # The files of the edges a composed chain replaces: each leads to a shape the chain passes
27
+ # through before its last step, so only the chain used it.
28
+ #
29
+ # @param directory [String] the domain's bluebook directory
30
+ # @param domain [String] the domain whose edges count
31
+ # @param chain [Array<Bluebook::Translation>] the chain being composed, in order
32
+ # @return [Array<String>] the paths to delete
33
+ def stale(directory, domain:, chain:)
34
+ intermediates = chain[0...-1].map(&:to)
35
+ scan(directory).filter_map do |found|
36
+ found.path if found.domain == domain && intermediates.include?(found.to)
37
+ end
38
+ end
39
+
40
+ # @param directory [String] the domain's bluebook directory
41
+ # @param from [String] the shape label the composed edge leaves
42
+ # @param to [String] the shape label the composed edge reaches
43
+ # @return [String] the path the composed edge is written to
44
+ def path_for(directory, from:, to:)
45
+ File.join(directory, "translations", "2-#{from}-to-#{to}.bluebook")
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hecks
4
+ module Translation
5
+ # Finds the run of translation edges that leads one shape label to another. A run is found by
6
+ # search rather than by walking every label's edges, so an edge that leaves a label off the run
7
+ # (a fork branch) does not stop the route.
8
+ module EdgeRoute
9
+ module_function
10
+
11
+ # The shortest run of edges from one label to another.
12
+ #
13
+ # @return [Array<Bluebook::Translation>] the edges in order; when `from` is `to`, the self
14
+ # edge if one exists, else empty
15
+ # @raise [Runtime::WiringError] when no run of edges leads `from` to `to`
16
+ def route(edges, from, to)
17
+ return edges.select { |edge| edge.from == from && edge.to == from }.first(1) if from == to
18
+
19
+ walk(edges, from, to).fetch(to) { raise Runtime::WiringError, "no translation edge leads #{from} to #{to}" }
20
+ end
21
+
22
+ # Breadth-first over labels, keeping the first run found to each one.
23
+ #
24
+ # @return [Hash{String => Array<Bluebook::Translation>}] the run to each reached label
25
+ def walk(edges, from, to)
26
+ paths = { from => [] }
27
+ frontier = [from]
28
+ until frontier.empty? || paths.key?(to)
29
+ label = frontier.shift
30
+ extend_runs(paths, frontier, edges, label)
31
+ end
32
+ paths
33
+ end
34
+
35
+ def extend_runs(paths, frontier, edges, label)
36
+ edges.each do |edge|
37
+ next unless edge.from == label && !paths.key?(edge.to)
38
+
39
+ paths[edge.to] = [*paths[label], edge]
40
+ frontier << edge.to
41
+ end
42
+ end
43
+
44
+ # @return [Boolean] whether a run of edges leads from one label to another
45
+ def reachable?(from, to, edges) = reached(from, edges).include?(to)
46
+
47
+ # @return [Array<String>] every label reachable from `from`, `from` included
48
+ def reached(from, edges)
49
+ seen = [from]
50
+ frontier = [from]
51
+ until frontier.empty?
52
+ label = frontier.shift
53
+ successors = edges.filter_map { |edge| edge.to if edge.from == label && !seen.include?(edge.to) }
54
+ seen.concat(successors)
55
+ frontier.concat(successors)
56
+ end
57
+ seen
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,67 @@
1
+ require_relative "../projected_bluebook"
2
+ require_relative "additive_shape"
3
+
4
+ module Hecks
5
+ module Translation
6
+ # The empty edge a shape change needs when no edge file is written.
7
+ #
8
+ # A change that scaffolds to no rule, no unresolved line and no retirement, and whose storage
9
+ # change only adds, carries every row unchanged, so no file has to say so. The boot and the mint
10
+ # treat it as an empty edge from the held shape to the current one, and the journal records the
11
+ # era it minted as implicit. Every other change still needs a real edge file.
12
+ class ImplicitEdge
13
+ # What the journal stores for an era minted under an implicit edge. A later boot reads
14
+ # it back, so an audit can tell the edge was never written by hand.
15
+ RECORD = { kind: "implicit", scaffolded: true }.freeze
16
+
17
+ attr_reader :domain, :from, :to
18
+
19
+ # @param domain [String] the domain the edge belongs to
20
+ # @param from [String] the shape label the edge leaves
21
+ # @param to [String] the shape label the edge reaches
22
+ def initialize(domain:, from:, to:)
23
+ @domain = domain
24
+ @from = from
25
+ @to = to
26
+ end
27
+
28
+ # An implicit edge declares no aggregate, so it renames, moves, converts and drops nothing.
29
+ #
30
+ # @return [Array] always empty
31
+ def aggregates = []
32
+
33
+ # An implicit edge retires no aggregate, as a written edge's `retired` lists none it drops.
34
+ #
35
+ # @return [Array] always empty
36
+ def retired = []
37
+
38
+ # @param _name [String, Symbol] an aggregate name
39
+ # @return [nil] an implicit edge has no rules for any aggregate
40
+ def for_aggregate(_name) = nil
41
+
42
+ # @return [Boolean] always true
43
+ def implicit? = true
44
+
45
+ # The era record a mint writes for this edge, keyed by the label of the era it reaches.
46
+ #
47
+ # @return [Hash] `label` and `edge`, as the journal stores them
48
+ def record = { label: to, edge: RECORD }
49
+
50
+ # Whether a storage change needs no edge file: the scaffold finds no rule, no retirement and
51
+ # no unresolved name, and every aggregate both shapes have only adds: same identity and
52
+ # lifecycle field, every held attribute held exactly as before (list or scalar included),
53
+ # and every attribute gained optional. Anything this cannot tell is breaking.
54
+ #
55
+ # @param diff [Hash] the scaffold's diff of the held shape against the current one
56
+ # @param held [Hash, Runtime::ProjectedBluebook, Bluebook::Declaration] the held era's storage
57
+ # shape, a view over it, or a declared bluebook
58
+ # @param current [Bluebook::Declaration] the current bluebook, which says which of its
59
+ # attributes are optional
60
+ # @return [Boolean]
61
+ def self.qualifies?(diff, held, current)
62
+ diff[:aggregates].empty? && diff[:retired].empty? && diff[:unclaimed].empty? &&
63
+ AdditiveShape.additive?(held, current)
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "compose"
4
+ require_relative "edge_route"
5
+ require_relative "compose_render"
6
+ require_relative "edge_files"
7
+
8
+ module Hecks
9
+ module Translation
10
+ # Plans `Era.Reconcile` for one domain: the one edge that leads its latest held era to the
11
+ # current shape, the edge files that edge supersedes, and the named reason a plan is refused.
12
+ #
13
+ # Planning reads the held labels, the loaded edges and the edge files, and writes nothing;
14
+ # `ReconcileFiles` carries out an admitted plan.
15
+ module Reconcile
16
+ # What a plan is made from: the domain's name, its held eras (`ordinal:` and `label:`, in
17
+ # order), the current shape's label, its loaded edges, the edge files of its translations
18
+ # directory and the directory a composed edge is written under.
19
+ Scope = Struct.new(:domain, :held, :current, :edges, :files, :directory, keyword_init: true)
20
+
21
+ # One domain's plan. `status` is `:no_era`, `:clean`, `:write` or `:refused`; `latest` is the
22
+ # latest held era (`ordinal:`, `label:`); `edge` is the composed edge when one is needed, and
23
+ # `removals` the edge files the plan deletes; `refusal` names why a plan is refused.
24
+ Plan = Struct.new(:domain, :status, :latest, :current, :labels, :edge, :write_path, :text,
25
+ :removals, :mints, :refusal, keyword_init: true)
26
+
27
+ module_function
28
+
29
+ # @param scope [Scope] what the plan is made from
30
+ # @return [Plan] the plan; `status` is `:refused` with `refusal` naming the reason
31
+ def plan(scope)
32
+ return Plan.new(domain: scope.domain, status: :no_era, current: scope.current) if scope.held.empty?
33
+
34
+ latest = scope.held.last
35
+ decide(scope, latest, composition(scope, latest))
36
+ rescue Runtime::WiringError => e
37
+ Plan.new(domain: scope.domain, status: :refused, latest: scope.held.last, current: scope.current,
38
+ refusal: e.message)
39
+ end
40
+
41
+ # @return [Hash] `chain:` the edges from the latest era, `edge:` the composed edge when the
42
+ # chain has more than one step, and `kept:` the edge that already leads to the current shape
43
+ def composition(scope, latest)
44
+ chain = EdgeRoute.route(scope.edges, latest[:label], scope.current)
45
+ return { chain: chain, edge: nil, kept: chain.first } if chain.size <= 1
46
+
47
+ edge = Compose.compose(domain: scope.domain, from: latest[:label], to: scope.current, chain: chain)
48
+ { chain: chain, edge: edge, kept: edge }
49
+ end
50
+
51
+ def decide(scope, latest, composition)
52
+ removed = superseded(composition, scope.edges, latest)
53
+ refusal = refusal_for(scope, removed, composition)
54
+ return refused(scope, latest, refusal) if refusal
55
+
56
+ verdict(scope, latest, composition, removed.map { |edge| file_for!(scope, edge) })
57
+ end
58
+
59
+ # The edges a composed plan replaces: every chain step when the chain is composed, and every
60
+ # other edge leaving the latest held era.
61
+ def superseded(composition, edges, latest)
62
+ steps = composition[:chain].size > 1 ? composition[:chain] : []
63
+ branches = edges.select { |edge| edge.from == latest[:label] && !same?(edge, composition[:kept]) }
64
+ (steps + branches).uniq { |edge| key(edge) }
65
+ end
66
+
67
+ # The named reason the change is refused: an earlier era still using an edge the change
68
+ # deletes, or an earlier era the change would leave with no way to the current shape.
69
+ def refusal_for(scope, removed, composition)
70
+ after = scope.edges.reject { |edge| removed.any? { |gone| same?(gone, edge) } }
71
+ after += [composition[:edge]] if composition[:edge]
72
+ earlier = scope.held[0...-1]
73
+ still_used(scope.domain, earlier, removed) || uncovered(scope, earlier, after)
74
+ end
75
+
76
+ def still_used(domain, earlier, removed)
77
+ removed.each do |edge|
78
+ era = earlier.find { |held| held[:label] == edge.from }
79
+ next unless era
80
+
81
+ return "#{domain}: era #{era[:ordinal]} (#{era[:label]}) still uses the edge #{edge.from} -> " \
82
+ "#{edge.to}, which the change would delete"
83
+ end
84
+ nil
85
+ end
86
+
87
+ def uncovered(scope, earlier, after)
88
+ era = earlier.find { |held| !EdgeRoute.reachable?(held[:label], scope.current, after) }
89
+ return nil unless era
90
+
91
+ verb = EdgeRoute.reachable?(era[:label], scope.current, scope.edges) ? "the change would leave" : "no edge leads"
92
+ "#{scope.domain}: #{verb} era #{era[:ordinal]} (#{era[:label]}) a way to #{scope.current}"
93
+ end
94
+
95
+ def verdict(scope, latest, composition, removal_paths)
96
+ labels = [latest[:label], *composition[:chain].map(&:to)]
97
+ edge = composition[:edge]
98
+ status = edge || removal_paths.any? ? :write : :clean
99
+ Plan.new(domain: scope.domain, status: status, latest: latest, current: scope.current, labels: labels,
100
+ edge: edge, write_path: write_path_for(scope, latest, edge),
101
+ text: edge && ComposeRender.render(edge, labels: labels), removals: removal_paths,
102
+ mints: latest[:label] != scope.current)
103
+ end
104
+
105
+ def write_path_for(scope, latest, edge)
106
+ return nil unless edge
107
+
108
+ EdgeFiles.path_for(scope.directory, from: latest[:label], to: scope.current)
109
+ end
110
+
111
+ def refused(scope, latest, refusal)
112
+ Plan.new(domain: scope.domain, status: :refused, latest: latest, current: scope.current, refusal: refusal)
113
+ end
114
+
115
+ # @return [String] the path of the edge file that carries `edge`
116
+ # @raise [Runtime::WiringError] when no file under the translations directory holds it
117
+ def file_for!(scope, edge)
118
+ found = scope.files.find { |file| file.domain == scope.domain && file.from == edge.from && file.to == edge.to }
119
+ return found.path if found
120
+
121
+ raise Runtime::WiringError,
122
+ "#{scope.domain}: no edge file holds #{edge.from} -> #{edge.to}, so it cannot be removed"
123
+ end
124
+
125
+ def same?(left, right) = !right.nil? && key(left) == key(right)
126
+
127
+ def key(edge) = [edge.from, edge.to]
128
+ end
129
+ end
130
+ end