hecks 2.9.0 → 3.0.1

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 (600) hide show
  1. checksums.yaml +4 -4
  2. data/exe/hecks +51 -6
  3. data/lib/hecks/adapters/driven/heki/journal.rb +1 -1
  4. data/lib/hecks/adapters/driven/lambda/client.rb +1 -1
  5. data/lib/hecks/adapters/driven/lambda.rb +1 -1
  6. data/lib/hecks/adapters/driven/memory.rb +16 -3
  7. data/lib/hecks/adapters/driven/postgres/reconnect.rb +6 -2
  8. data/lib/hecks/adapters/driven/postgres/shared_connection.rb +180 -0
  9. data/lib/hecks/adapters/driven/postgres.rb +13 -4
  10. data/lib/hecks/adapters/driven/prism.rb +41 -8
  11. data/lib/hecks/adapters/driven/tenant_provisioner.rb +73 -16
  12. data/lib/hecks/behaviors/expectations.rb +1 -1
  13. data/lib/hecks/behaviors.rb +1 -1
  14. data/lib/hecks/bench/cli.rb +62 -0
  15. data/lib/hecks/bench/environment.rb +71 -0
  16. data/lib/hecks/bench/postgres_probe.rb +30 -0
  17. data/lib/hecks/bench/report.rb +76 -0
  18. data/lib/hecks/bench/ruby_runner.rb +93 -0
  19. data/lib/hecks/bench/run.rb +55 -0
  20. data/lib/hecks/bench/rust_runner.rb +101 -0
  21. data/lib/hecks/bench/stats.rb +46 -0
  22. data/lib/hecks/bench/suite.rb +98 -0
  23. data/lib/hecks/bench/workload.rb +89 -0
  24. data/lib/hecks/bench.rb +17 -0
  25. data/lib/hecks/bluebook/assembly/contracts.rb +2 -0
  26. data/lib/hecks/bluebook/attribute.rb +1 -1
  27. data/lib/hecks/bluebook/behaviour/aggregate.rb +30 -1
  28. data/lib/hecks/bluebook/behaviour/domain_port.rb +7 -0
  29. data/lib/hecks/bluebook/chapter.rb +6 -2
  30. data/lib/hecks/bluebook/domain_port.rb +36 -5
  31. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +10 -0
  32. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +8 -0
  33. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -1
  34. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +31 -3
  35. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +122 -3
  36. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +1 -1
  37. data/lib/hecks/bluebook/dsl/query_builder.rb +27 -1
  38. data/lib/hecks/bluebook/dsl/rule_reference.rb +1 -1
  39. data/lib/hecks/bluebook/dsl/world_builder.rb +29 -2
  40. data/lib/hecks/bluebook/expression/canonical_form.rb +1 -1
  41. data/lib/hecks/bluebook/expression/resolver.rb +3 -2
  42. data/lib/hecks/bluebook/hexagon.rb +13 -5
  43. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +1 -0
  44. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +4 -1
  45. data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +246 -0
  46. data/lib/hecks/bluebook/meta_validator.rb +9 -4
  47. data/lib/hecks/bluebook/model_check.rb +23 -0
  48. data/lib/hecks/bluebook/pattern_subset.rb +38 -0
  49. data/lib/hecks/bluebook/query.rb +20 -5
  50. data/lib/hecks/bluebook/smoke_test.rb +1 -1
  51. data/lib/hecks/canonical_json.rb +30 -0
  52. data/lib/hecks/chapters.rb +102 -0
  53. data/lib/hecks/cli/behaviors.rb +61 -0
  54. data/lib/hecks/cli/canonicalise.rb +25 -0
  55. data/lib/hecks/cli/check_engine_agreement.rb +32 -0
  56. data/lib/hecks/cli/check_era.rb +88 -0
  57. data/lib/hecks/cli/console.rb +66 -0
  58. data/lib/hecks/cli/deploy_template_diff.rb +40 -0
  59. data/lib/hecks/cli/doc_coverage.rb +32 -0
  60. data/lib/hecks/cli/document.rb +2 -2
  61. data/lib/hecks/cli/expression_projection.rb +41 -0
  62. data/lib/hecks/cli/follow.rb +88 -0
  63. data/lib/hecks/cli/history.rb +44 -0
  64. data/lib/hecks/cli/ir.rb +1 -1
  65. data/lib/hecks/cli/mcp.rb +3 -3
  66. data/lib/hecks/cli/model_check.rb +15 -8
  67. data/lib/hecks/cli/pattern_cases.rb +47 -0
  68. data/lib/hecks/cli/present.rb +39 -0
  69. data/lib/hecks/cli/project_cli.rb +224 -32
  70. data/lib/hecks/cli/project_diagrams.rb +4 -3
  71. data/lib/hecks/cli/project_glossary.rb +44 -0
  72. data/lib/hecks/cli/reference.rb +46 -0
  73. data/lib/hecks/cli/refresh_projections.rb +44 -0
  74. data/lib/hecks/cli/refresh_rspec_runtime_baseline.rb +150 -0
  75. data/lib/hecks/cli/release.rb +70 -0
  76. data/lib/hecks/cli/release_gem.rb +59 -0
  77. data/lib/hecks/cli/rspec_io_parallel_files.rb +137 -0
  78. data/lib/hecks/cli/rspec_shard_files.rb +66 -0
  79. data/lib/hecks/cli/run.rb +3 -3
  80. data/lib/hecks/cli/seed_semantics_corpus.rb +71 -0
  81. data/lib/hecks/cli/serve_query_ir_mcp.rb +30 -0
  82. data/lib/hecks/cli/shape.rb +70 -0
  83. data/lib/hecks/cli/smoke_http.rb +230 -0
  84. data/lib/hecks/cli/smoke_test.rb +5 -3
  85. data/lib/hecks/cli/spec_example.rb +27 -0
  86. data/lib/hecks/cli/statements.rb +32 -0
  87. data/lib/hecks/cli/stores.rb +4 -5
  88. data/lib/hecks/cli/stress_concurrency_specs.rb +177 -0
  89. data/lib/hecks/cli.rb +201 -52
  90. data/lib/hecks/codemod.rb +324 -0
  91. data/lib/hecks/corpus.rb +423 -0
  92. data/lib/hecks/deploy/bluebook/deploy.bluebook +591 -23
  93. data/lib/hecks/deploy/bluebook/deploy.hecksagon +41 -4
  94. data/lib/hecks/deploy/oidc.json +82 -2
  95. data/lib/hecks/doc/reference.rb +351 -0
  96. data/lib/hecks/doors/cli_door.rb +196 -0
  97. data/lib/hecks/doors/cli_runner.rb +297 -0
  98. data/lib/hecks/{facade → doors}/command_request.rb +1 -1
  99. data/lib/hecks/{facade → doors}/handle.rb +2 -2
  100. data/lib/hecks/{facade → doors}/json_door.rb +4 -4
  101. data/lib/hecks/doors/launcher_options.rb +129 -0
  102. data/lib/hecks/{cli → doors}/mcp_door.rb +7 -7
  103. data/lib/hecks/doors/mcp_door_scope.rb +97 -0
  104. data/lib/hecks/{facade/surface → doors/ruby_door}/aggregate_door.rb +8 -3
  105. data/lib/hecks/{facade/surface → doors/ruby_door}/chapter.rb +3 -3
  106. data/lib/hecks/{facade/surface.rb → doors/ruby_door.rb} +36 -5
  107. data/lib/hecks/doors.rb +61 -0
  108. data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +1 -1
  109. data/lib/hecks/embryonaut_bluebook.rb +1 -1
  110. data/lib/hecks/engine_agreement.rb +156 -0
  111. data/lib/hecks/facade.rb +24 -37
  112. data/lib/hecks/forms/app.rb +6 -6
  113. data/lib/hecks/forms/banking_presentation.rb +50 -0
  114. data/lib/hecks/forms/field_shape.rb +2 -1
  115. data/lib/hecks/forms/port_argument.rb +1 -1
  116. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +393 -0
  117. data/lib/hecks/fuzzing/combination_miner.rb +86 -0
  118. data/lib/hecks/fuzzing/concurrent_dispatch.rb +214 -0
  119. data/lib/hecks/fuzzing/coverage_campaign.rb +143 -0
  120. data/lib/hecks/fuzzing/differential.rb +156 -0
  121. data/lib/hecks/fuzzing/domain_generator.rb +615 -0
  122. data/lib/hecks/fuzzing/era_boundary.rb +71 -0
  123. data/lib/hecks/fuzzing/form_census.rb +136 -0
  124. data/lib/hecks/fuzzing/generated_domain_check.rb +93 -0
  125. data/lib/hecks/fuzzing/invalid_value_generator.rb +64 -0
  126. data/lib/hecks/fuzzing/isolated_boot.rb +301 -0
  127. data/lib/hecks/fuzzing/nondeterministic.rb +51 -0
  128. data/lib/hecks/fuzzing/outside_world.rb +45 -0
  129. data/lib/hecks/fuzzing/persistence_parity.rb +102 -0
  130. data/lib/hecks/fuzzing/properties/corrections.rb +88 -0
  131. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +314 -0
  132. data/lib/hecks/fuzzing/properties/guards.rb +190 -0
  133. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +300 -0
  134. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +93 -0
  135. data/lib/hecks/fuzzing/properties/outbox.rb +89 -0
  136. data/lib/hecks/fuzzing/properties/querying.rb +175 -0
  137. data/lib/hecks/fuzzing/properties.rb +162 -0
  138. data/lib/hecks/fuzzing/qa_settings.rb +123 -0
  139. data/lib/hecks/fuzzing/replay.rb +505 -0
  140. data/lib/hecks/fuzzing/rotation_priority.rb +48 -0
  141. data/lib/hecks/fuzzing/rust_gap_manifest.rb +108 -0
  142. data/lib/hecks/fuzzing/self_consistency.rb +339 -0
  143. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +385 -0
  144. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +132 -0
  145. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +95 -0
  146. data/lib/hecks/fuzzing/sequence_generator/picker.rb +77 -0
  147. data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +109 -0
  148. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +237 -0
  149. data/lib/hecks/fuzzing/sequence_generator.rb +189 -0
  150. data/lib/hecks/fuzzing/shrinker.rb +166 -0
  151. data/lib/hecks/fuzzing/structural_skips.rb +33 -0
  152. data/lib/hecks/fuzzing/sweep_depth.rb +43 -0
  153. data/lib/hecks/fuzzing/target_capabilities.rb +163 -0
  154. data/lib/hecks/fuzzing/value_generator.rb +216 -0
  155. data/lib/hecks/fuzzing.rb +23 -0
  156. data/lib/hecks/grammar/evolve.rb +336 -0
  157. data/lib/hecks/hecks/adapters/codebase/codemods.rb +45 -0
  158. data/lib/hecks/hecks/adapters/codebase/conformance.rb +71 -0
  159. data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +202 -0
  160. data/lib/hecks/hecks/adapters/codebase/gem_registry.rb +80 -0
  161. data/lib/hecks/hecks/adapters/codebase/kernel_tables.rb +54 -0
  162. data/lib/hecks/hecks/adapters/codebase/language.rb +240 -0
  163. data/lib/hecks/hecks/adapters/codebase/npm_registry.rb +102 -0
  164. data/lib/hecks/hecks/adapters/codebase/publishing.rb +142 -0
  165. data/lib/hecks/hecks/adapters/codebase/regeneration.rb +67 -0
  166. data/lib/hecks/hecks/adapters/codebase/release_facts.rb +90 -0
  167. data/lib/hecks/hecks/adapters/codebase/ruby_child.rb +83 -0
  168. data/lib/hecks/hecks/adapters/codebase/secret_vault.rb +36 -0
  169. data/lib/hecks/hecks/adapters/codebase/source_tree.adapter +7 -0
  170. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +189 -0
  171. data/lib/hecks/hecks/adapters/codebase/sqlite_fixture.rb +71 -0
  172. data/lib/hecks/hecks/adapters/codebase/style.rb +171 -0
  173. data/lib/hecks/hecks/adapters/codebase/test_runner.rb +55 -0
  174. data/lib/hecks/hecks/adapters/codebase/test_suite.rb +165 -0
  175. data/lib/hecks/hecks/adapters/codebase/tree.rb +111 -0
  176. data/lib/hecks/hecks/adapters/console_capture.rb +79 -0
  177. data/lib/hecks/hecks/adapters/deploy_toolchain.adapter +7 -0
  178. data/lib/hecks/hecks/adapters/deploy_toolchain.rb +108 -0
  179. data/lib/hecks/hecks/adapters/git.adapter +7 -0
  180. data/lib/hecks/hecks/adapters/git.rb +102 -0
  181. data/lib/hecks/hecks/adapters/host_http.adapter +7 -0
  182. data/lib/hecks/hecks/adapters/host_http.rb +65 -0
  183. data/lib/hecks/hecks/adapters/in_process_boot.adapter +7 -0
  184. data/lib/hecks/hecks/adapters/in_process_boot.rb +197 -0
  185. data/lib/hecks/hecks/adapters/in_process_operations.rb +202 -0
  186. data/lib/hecks/hecks/adapters/journal_store/changes.rb +143 -0
  187. data/lib/hecks/hecks/adapters/journal_store/compaction.rb +145 -0
  188. data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +132 -0
  189. data/lib/hecks/hecks/adapters/journal_store/examination.rb +135 -0
  190. data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +163 -0
  191. data/lib/hecks/hecks/adapters/journal_store/readings.rb +187 -0
  192. data/lib/hecks/hecks/adapters/journal_store.adapter +7 -0
  193. data/lib/hecks/hecks/adapters/journal_store.rb +64 -0
  194. data/lib/hecks/hecks/adapters/local_files.adapter +7 -0
  195. data/lib/hecks/hecks/adapters/local_files.rb +46 -0
  196. data/lib/hecks/hecks/adapters/pg_admin.adapter +7 -0
  197. data/lib/hecks/hecks/adapters/pg_admin.rb +245 -0
  198. data/lib/hecks/hecks/adapters/process_pool.adapter +7 -0
  199. data/lib/hecks/hecks/adapters/process_pool.rb +119 -0
  200. data/lib/hecks/hecks/adapters/rust_toolchain.adapter +7 -0
  201. data/lib/hecks/hecks/adapters/rust_toolchain.rb +179 -0
  202. data/lib/hecks/hecks/adapters/rust_workspace.rb +150 -0
  203. data/lib/hecks/hecks/adapters/shell.adapter +7 -0
  204. data/lib/hecks/hecks/adapters/shell.rb +49 -0
  205. data/lib/hecks/hecks/adapters/terminal.adapter +7 -0
  206. data/lib/hecks/hecks/adapters/terminal.rb +74 -0
  207. data/lib/hecks/hecks/codebase.behaviors +172 -0
  208. data/lib/hecks/hecks/codebase.bluebook +2453 -0
  209. data/lib/hecks/hecks/context_map.hecksagon +13 -0
  210. data/lib/hecks/hecks/custodian.behaviors +179 -0
  211. data/lib/hecks/hecks/custodian.bluebook +1847 -0
  212. data/lib/hecks/hecks/deploy.behaviors +74 -0
  213. data/lib/hecks/hecks/deploy.world +12 -0
  214. data/lib/hecks/hecks/environments/memory.world +10 -0
  215. data/lib/hecks/hecks/governance.world +7 -0
  216. data/lib/hecks/hecks/hecks.behaviors +62 -0
  217. data/lib/hecks/hecks/hecks.bluebook +124 -0
  218. data/lib/hecks/hecks/hecks.hecksagon +390 -0
  219. data/lib/hecks/hecks/hecks.world +42 -0
  220. data/lib/hecks/kernel_capabilities.rb +194 -0
  221. data/lib/hecks/language/bluebook/bluebook.bluebook +11 -0
  222. data/lib/hecks/language/bluebook/query.bluebook +7 -0
  223. data/lib/hecks/language/bluebook/vocabulary.bluebook +4 -4
  224. data/lib/hecks/language/hecksagon/domain_port.bluebook +10 -0
  225. data/lib/hecks/language/hecksagon/hecksagon.bluebook +20 -0
  226. data/lib/hecks/mcp_stdio_guard.rb +2 -2
  227. data/lib/hecks/persistence_legacy_fixture/regenerate.rb +161 -0
  228. data/lib/hecks/persistence_legacy_fixture.rb +113 -0
  229. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +8 -5
  230. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +4 -1
  231. data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +15 -2
  232. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +1 -1
  233. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +1 -1
  234. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +1 -1
  235. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +32 -15
  236. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +15 -5
  237. data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +227 -0
  238. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +1 -1
  239. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +43 -24
  240. data/lib/hecks/ports/persistence/plugins/era/translation.rb +1 -0
  241. data/lib/hecks/ports/persistence/state_codec.rb +24 -5
  242. data/lib/hecks/projection_files.rb +285 -0
  243. data/lib/hecks/projections/bootstrap_table.rb +1 -1
  244. data/lib/hecks/projections/deploy/fargate.rb +5 -5
  245. data/lib/hecks/projections/deploy/lambda.rb +43 -32
  246. data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
  247. data/lib/hecks/projections/deploy/preview/template.rb +1 -1
  248. data/lib/hecks/projections/deploy/scripts/deploy-service.sh.tmpl +1 -1
  249. data/lib/hecks/projections/deploy/scripts/expected-era.header +1 -1
  250. data/lib/hecks/projections/deploy/scripts/hosting.mk.tmpl +3 -3
  251. data/lib/hecks/projections/deploy/scripts/smoke-after-deploy.sh.tmpl +1 -1
  252. data/lib/hecks/projections/deploy/shared.rb +1 -1
  253. data/lib/hecks/projections/diagrams.rb +3 -3
  254. data/lib/hecks/projections/model/deviations.rb +1 -1
  255. data/lib/hecks/projections/parser_table.rb +4 -4
  256. data/lib/hecks/projections/reference.rb +2 -3
  257. data/lib/hecks/projections/rust_vocabulary.rb +1 -1
  258. data/lib/hecks/projector/cli_projector.rb +106 -37
  259. data/lib/hecks/projector.rb +1 -1
  260. data/lib/hecks/quality_control/adapters/agent.adapter +7 -0
  261. data/lib/hecks/quality_control/adapters/agent.rb +58 -0
  262. data/lib/hecks/quality_control/adapters/angle_tools.adapter +7 -0
  263. data/lib/hecks/quality_control/adapters/angle_tools.rb +15 -0
  264. data/lib/hecks/quality_control/adapters/clearance_recorder.rb +36 -0
  265. data/lib/hecks/quality_control/adapters/clearance_tools.adapter +7 -0
  266. data/lib/hecks/quality_control/adapters/clearance_tools.rb +17 -0
  267. data/lib/hecks/quality_control/adapters/git_pr.adapter +7 -0
  268. data/lib/hecks/quality_control/adapters/git_pr.rb +226 -0
  269. data/lib/hecks/quality_control/adapters/github_checks.adapter +9 -0
  270. data/lib/hecks/quality_control/adapters/github_checks.rb +83 -0
  271. data/lib/hecks/quality_control/adapters/github_ci_webhook.rb +56 -0
  272. data/lib/hecks/quality_control/adapters/github_issues.adapter +7 -0
  273. data/lib/hecks/quality_control/adapters/github_issues.rb +49 -0
  274. data/lib/hecks/quality_control/adapters/qa_tool.rb +59 -0
  275. data/lib/hecks/quality_control/adapters/sweep_tools.adapter +7 -0
  276. data/lib/hecks/quality_control/adapters/sweep_tools.rb +51 -0
  277. data/lib/hecks/quality_control/adapters/target_tools.adapter +7 -0
  278. data/lib/hecks/quality_control/adapters/target_tools.rb +43 -0
  279. data/lib/hecks/quality_control/cli/child.rb +45 -0
  280. data/lib/hecks/quality_control/cli/qa_concurrency_racer.rb +45 -0
  281. data/lib/hecks/quality_control/cli/qa_discover_external_domains.rb +248 -0
  282. data/lib/hecks/quality_control/cli/qa_domain_novelty.rb +163 -0
  283. data/lib/hecks/quality_control/cli/qa_generated_domains.rb +450 -0
  284. data/lib/hecks/quality_control/cli/qa_log_bug.rb +195 -0
  285. data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +220 -0
  286. data/lib/hecks/quality_control/cli/qa_open_pr.rb +305 -0
  287. data/lib/hecks/quality_control/cli/qa_postgres_migrate.rb +154 -0
  288. data/lib/hecks/quality_control/cli/qa_postgres_role.rb +159 -0
  289. data/lib/hecks/quality_control/cli/qa_pr_check.rb +232 -0
  290. data/lib/hecks/quality_control/cli/qa_seed_angles.rb +142 -0
  291. data/lib/hecks/quality_control/cli/qa_seed_targets.rb +69 -0
  292. data/lib/hecks/quality_control/cli/qa_sweep/all_mode.rb +280 -0
  293. data/lib/hecks/quality_control/cli/qa_sweep/checks.rb +329 -0
  294. data/lib/hecks/quality_control/cli/qa_sweep/dials.rb +92 -0
  295. data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +117 -0
  296. data/lib/hecks/quality_control/cli/qa_sweep/parsing.rb +175 -0
  297. data/lib/hecks/quality_control/cli/qa_sweep/release_mode.rb +60 -0
  298. data/lib/hecks/quality_control/cli/qa_sweep.rb +611 -0
  299. data/lib/hecks/quality_control/cli/qa_tick.rb +274 -0
  300. data/lib/hecks/quality_control/quality_control.bluebook +2224 -0
  301. data/lib/hecks/quality_control/quality_control.hecksagon +13 -0
  302. data/lib/hecks/quality_control/quality_control.ports.hecksagon +54 -0
  303. data/lib/hecks/query_ir.rb +346 -0
  304. data/lib/hecks/query_ir_mcp.rb +198 -0
  305. data/lib/hecks/release/runner/ci_publisher.rb +2 -2
  306. data/lib/hecks/release/runner/clean_tree.rb +42 -0
  307. data/lib/hecks/release/runner/console.rb +6 -0
  308. data/lib/hecks/release/runner/gem_publisher.rb +14 -7
  309. data/lib/hecks/release/runner/npm_publisher.rb +6 -36
  310. data/lib/hecks/release/runner/preflight.rb +26 -2
  311. data/lib/hecks/release/runner/published.rb +8 -26
  312. data/lib/hecks/release/runner/tagger.rb +30 -15
  313. data/lib/hecks/release/runner.rb +57 -6
  314. data/lib/hecks/router/namespace_installer.rb +3 -3
  315. data/lib/hecks/runtime/adapter_lookup.rb +124 -0
  316. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +4 -0
  317. data/lib/hecks/runtime/dependency_planning.rb +20 -2
  318. data/lib/hecks/runtime/dispatcher.rb +53 -3
  319. data/lib/hecks/runtime/interpreting.rb +3 -0
  320. data/lib/hecks/runtime/loader.rb +65 -12
  321. data/lib/hecks/runtime/outbox.rb +1 -1
  322. data/lib/hecks/runtime/policy_interpreter.rb +5 -2
  323. data/lib/hecks/runtime/port_operation_interpreter.rb +2 -10
  324. data/lib/hecks/runtime/query_interpreter.rb +77 -0
  325. data/lib/hecks/runtime/reaction_outcome.rb +89 -0
  326. data/lib/hecks/runtime/refusal_wording.rb +23 -1
  327. data/lib/hecks/runtime/registry/verification.rb +163 -4
  328. data/lib/hecks/runtime/registry.rb +57 -3
  329. data/lib/hecks/runtime/remote_dispatcher.rb +31 -2
  330. data/lib/hecks/runtime/value/coercion.rb +23 -135
  331. data/lib/hecks/runtime/value/field_checks.rb +172 -0
  332. data/lib/hecks/runtime/value.rb +4 -1
  333. data/lib/hecks/runtime.rb +20 -6
  334. data/lib/hecks/rust_build/conformance.rb +158 -0
  335. data/lib/hecks/rust_build/conformance_fuzz.rb +71 -0
  336. data/lib/hecks/rust_build/coverage.rb +218 -0
  337. data/lib/hecks/rust_build/native_build.rb +124 -0
  338. data/lib/hecks/rust_build/project_rust/root_mod.rb +120 -0
  339. data/lib/hecks/rust_build/project_rust.rb +332 -0
  340. data/lib/hecks/rust_build/wasm.rb +105 -0
  341. data/lib/hecks/rust_build/wasm_browser.rb +113 -0
  342. data/lib/hecks/rust_build.rb +144 -0
  343. data/lib/hecks/storehouse.rb +16 -16
  344. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +2 -2
  345. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +1 -1
  346. data/lib/hecks/three_zero/forms.yml +127 -0
  347. data/lib/hecks/three_zero.rb +12 -0
  348. data/lib/hecks/tools/argument_gate_matrix.rb +316 -0
  349. data/lib/hecks/tools/comment_style.rb +1070 -0
  350. data/lib/hecks/tools/corpus_report.rb +87 -0
  351. data/lib/hecks/tools/deploy_recipe.rb +255 -0
  352. data/lib/hecks/tools/deploy_recipe_lint.rb +282 -0
  353. data/lib/hecks/tools/drop_implicit_append_fields.rb +95 -0
  354. data/lib/hecks/tools/era_projection_backfill.rb +94 -0
  355. data/lib/hecks/tools/era_reattest.rb +146 -0
  356. data/lib/hecks/tools/evolve_run.rb +189 -0
  357. data/lib/hecks/tools/fuzz_sweep.rb +373 -0
  358. data/lib/hecks/tools/heki_compaction.rb +100 -0
  359. data/lib/hecks/tools/hoist_local_givens.rb +246 -0
  360. data/lib/hecks/tools/journal_compaction.rb +89 -0
  361. data/lib/hecks/tools/oidc_manifests.rb +70 -0
  362. data/lib/hecks/tools/query_ir_run.rb +81 -0
  363. data/lib/hecks/tools/regeneration_run.rb +166 -0
  364. data/lib/hecks/tools/rust_comment_style.rb +642 -0
  365. data/lib/hecks/tools/sequence_script.rb +62 -0
  366. data/lib/hecks/tools/tail_merge.rb +117 -0
  367. data/lib/hecks/tools/tenant_provisioning.rb +142 -0
  368. data/lib/hecks/tools/translation_audit.rb +208 -0
  369. data/lib/hecks/tools/translation_scaffold.rb +131 -0
  370. data/lib/hecks/tools.rb +86 -0
  371. data/lib/hecks/version.rb +1 -1
  372. data/lib/hecks.rb +32 -15
  373. data/qa/settings.yml +66 -0
  374. data/rust/.cargo/config.toml +6 -0
  375. data/rust/.gitignore +2 -0
  376. data/rust/Cargo.lock +54 -0
  377. data/rust/Cargo.toml +61 -0
  378. data/rust/build/.cargo/config.toml +3 -0
  379. data/rust/build/.gitignore +1 -0
  380. data/rust/build/Cargo.lock +7 -0
  381. data/rust/build/Cargo.toml +19 -0
  382. data/rust/build/src/build_artifact.rs +47 -0
  383. data/rust/build/src/cargo_sync.rs +248 -0
  384. data/rust/build/src/json.rs +421 -0
  385. data/rust/build/src/lineage_pass.rs +285 -0
  386. data/rust/build/src/main.rs +51 -0
  387. data/rust/build/src/optional_pass.rs +150 -0
  388. data/rust/build/src/pipeline.rs +264 -0
  389. data/rust/build/src/reserved_names.rs +76 -0
  390. data/rust/build/src/resolve.rs +124 -0
  391. data/rust/build/src/root.rs +25 -0
  392. data/rust/build/src/sidecars.rs +73 -0
  393. data/rust/build/src/subprocess.rs +51 -0
  394. data/rust/build/src/tmp.rs +31 -0
  395. data/rust/codegen/.cargo/config.toml +3 -0
  396. data/rust/codegen/.gitignore +1 -0
  397. data/rust/codegen/Cargo.lock +7 -0
  398. data/rust/codegen/Cargo.toml +20 -0
  399. data/rust/codegen/src/attr.rs +32 -0
  400. data/rust/codegen/src/bridging.rs +481 -0
  401. data/rust/codegen/src/commands.rs +1226 -0
  402. data/rust/codegen/src/constraints.rs +109 -0
  403. data/rust/codegen/src/dependency_planning.rs +209 -0
  404. data/rust/codegen/src/domain_generator.rs +1288 -0
  405. data/rust/codegen/src/exemplar.rs +230 -0
  406. data/rust/codegen/src/expr_emitter.rs +87 -0
  407. data/rust/codegen/src/fielded.rs +192 -0
  408. data/rust/codegen/src/hecks_naming.rs +33 -0
  409. data/rust/codegen/src/json.rs +335 -0
  410. data/rust/codegen/src/json_codec.rs +697 -0
  411. data/rust/codegen/src/literal.rs +207 -0
  412. data/rust/codegen/src/main.rs +290 -0
  413. data/rust/codegen/src/manifest.rs +227 -0
  414. data/rust/codegen/src/mutations.rs +784 -0
  415. data/rust/codegen/src/naming.rs +286 -0
  416. data/rust/codegen/src/ports.rs +208 -0
  417. data/rust/codegen/src/prelude.rs +116 -0
  418. data/rust/codegen/src/queries.rs +786 -0
  419. data/rust/codegen/src/reactions.rs +532 -0
  420. data/rust/codegen/src/read_models.rs +697 -0
  421. data/rust/codegen/src/reference_specs.rs +40 -0
  422. data/rust/codegen/src/registry.rs +966 -0
  423. data/rust/codegen/src/reserved_names.rs +76 -0
  424. data/rust/codegen/src/shared.rs +93 -0
  425. data/rust/codegen/src/skip_reason.rs +25 -0
  426. data/rust/codegen/src/types.rs +362 -0
  427. data/rust/host/.cargo/config.toml +3 -0
  428. data/rust/host/.gitignore +1 -0
  429. data/rust/host/Cargo.lock +4297 -0
  430. data/rust/host/Cargo.toml +61 -0
  431. data/rust/host/HECKS_RELEASE +1 -0
  432. data/rust/host/rds-ca-bundle.pem +2736 -0
  433. data/rust/host/src/api.rs +1596 -0
  434. data/rust/host/src/approval.rs +652 -0
  435. data/rust/host/src/auth.rs +1287 -0
  436. data/rust/host/src/bin/expr_harness.rs +46 -0
  437. data/rust/host/src/bin/lineage_harness.rs +119 -0
  438. data/rust/host/src/bin/mint_harness.rs +135 -0
  439. data/rust/host/src/checkout.rs +265 -0
  440. data/rust/host/src/dispatch.rs +1443 -0
  441. data/rust/host/src/expr_json.rs +1117 -0
  442. data/rust/host/src/field_hints.rs +52 -0
  443. data/rust/host/src/ir.rs +733 -0
  444. data/rust/host/src/journal.rs +874 -0
  445. data/rust/host/src/lambda_client.rs +378 -0
  446. data/rust/host/src/log.rs +147 -0
  447. data/rust/host/src/main.rs +474 -0
  448. data/rust/host/src/mint.rs +1865 -0
  449. data/rust/host/src/payments/keystore.rs +330 -0
  450. data/rust/host/src/payments/tests.rs +1400 -0
  451. data/rust/host/src/payments.rs +767 -0
  452. data/rust/host/src/presentation.rs +698 -0
  453. data/rust/host/src/presentation_write.rs +1431 -0
  454. data/rust/host/src/query_step.rs +149 -0
  455. data/rust/host/src/rate_limit.rs +904 -0
  456. data/rust/host/src/reference_transform.rs +241 -0
  457. data/rust/host/src/reference_validate.rs +338 -0
  458. data/rust/host/src/resend.rs +296 -0
  459. data/rust/host/src/secrets.rs +106 -0
  460. data/rust/host/src/server.rs +663 -0
  461. data/rust/host/src/storage_shape.rs +175 -0
  462. data/rust/host/src/test_pg.rs +40 -0
  463. data/rust/host/src/ui_schema.rs +1351 -0
  464. data/rust/host/src/wasm_runner.rs +191 -0
  465. data/rust/host/src/web/newsletter/cooldown.rs +126 -0
  466. data/rust/host/src/web/newsletter/tests.rs +104 -0
  467. data/rust/host/src/web/newsletter.rs +310 -0
  468. data/rust/host/src/web/newsletter_send.rs +357 -0
  469. data/rust/host/src/web/registrations.rs +1505 -0
  470. data/rust/host/src/web.rs +2937 -0
  471. data/rust/lsp/.gitignore +1 -0
  472. data/rust/lsp/Cargo.lock +7 -0
  473. data/rust/lsp/Cargo.toml +16 -0
  474. data/rust/lsp/README.md +133 -0
  475. data/rust/lsp/src/diagnostics.rs +165 -0
  476. data/rust/lsp/src/json.rs +385 -0
  477. data/rust/lsp/src/main.rs +399 -0
  478. data/rust/lsp/src/outline.rs +177 -0
  479. data/rust/lsp/src/rpc.rs +67 -0
  480. data/rust/parser/.cargo/config.toml +5 -0
  481. data/rust/parser/.gitignore +1 -0
  482. data/rust/parser/Cargo.lock +7 -0
  483. data/rust/parser/Cargo.toml +21 -0
  484. data/rust/parser/src/build/closed_sets.rs +27 -0
  485. data/rust/parser/src/build/identity.rs +276 -0
  486. data/rust/parser/src/build/mod.rs +12 -0
  487. data/rust/parser/src/build/naming.rs +141 -0
  488. data/rust/parser/src/build/pattern_subset.rs +161 -0
  489. data/rust/parser/src/build/query_derive.rs +72 -0
  490. data/rust/parser/src/build/query_inference.rs +269 -0
  491. data/rust/parser/src/build/query_options.rs +100 -0
  492. data/rust/parser/src/build/read_model.rs +94 -0
  493. data/rust/parser/src/build/references.rs +32 -0
  494. data/rust/parser/src/canonical.rs +114 -0
  495. data/rust/parser/src/diag.rs +55 -0
  496. data/rust/parser/src/emit.rs +953 -0
  497. data/rust/parser/src/expr/ast_json.rs +147 -0
  498. data/rust/parser/src/expr/evaluator.rs +81 -0
  499. data/rust/parser/src/expr/mod.rs +84 -0
  500. data/rust/parser/src/expr/resolver.rs +497 -0
  501. data/rust/parser/src/ir.rs +387 -0
  502. data/rust/parser/src/keywords.rs +585 -0
  503. data/rust/parser/src/lex.rs +662 -0
  504. data/rust/parser/src/main.rs +252 -0
  505. data/rust/parser/src/parse/aggregate.rs +612 -0
  506. data/rust/parser/src/parse/chapter.rs +517 -0
  507. data/rust/parser/src/parse/command.rs +637 -0
  508. data/rust/parser/src/parse/domain_port.rs +127 -0
  509. data/rust/parser/src/parse/entity.rs +527 -0
  510. data/rust/parser/src/parse/file.rs +67 -0
  511. data/rust/parser/src/parse/hecksagon.rs +231 -0
  512. data/rust/parser/src/parse/lifecycle.rs +162 -0
  513. data/rust/parser/src/parse/mod.rs +1262 -0
  514. data/rust/parser/src/parse/policy.rs +87 -0
  515. data/rust/parser/src/parse/process_manager.rs +305 -0
  516. data/rust/parser/src/parse/query.rs +100 -0
  517. data/rust/parser/src/parse/read_model.rs +214 -0
  518. data/rust/parser/src/parse/value_object.rs +250 -0
  519. data/rust/parser/src/ruby_value.rs +422 -0
  520. data/rust/project/bridging.rb +257 -0
  521. data/rust/project/commands.rb +877 -0
  522. data/rust/project/constraints.rb +86 -0
  523. data/rust/project/dependency_planning.rb +178 -0
  524. data/rust/project/domain_generator.rb +980 -0
  525. data/rust/project/exemplar.rb +148 -0
  526. data/rust/project/expr_emitter.rb +72 -0
  527. data/rust/project/fielded.rb +141 -0
  528. data/rust/project/json_codec.rb +641 -0
  529. data/rust/project/mutations.rb +721 -0
  530. data/rust/project/naming.rb +205 -0
  531. data/rust/project/ports.rb +65 -0
  532. data/rust/project/queries.rb +493 -0
  533. data/rust/project/reactions.rb +317 -0
  534. data/rust/project/read_models.rb +690 -0
  535. data/rust/project/reference_specs.rb +27 -0
  536. data/rust/project/registry.rb +396 -0
  537. data/rust/project/skip_reason.rb +29 -0
  538. data/rust/project/types.rb +225 -0
  539. data/rust/project/write_if_changed.rb +83 -0
  540. data/rust/project.rb +30 -0
  541. data/rust/project_rust_pipeline.rb +383 -0
  542. data/rust/src/exemplar/commands.rs +279 -0
  543. data/rust/src/exemplar/constraints.rs +18 -0
  544. data/rust/src/exemplar/fielded.rs +280 -0
  545. data/rust/src/exemplar/json.rs +277 -0
  546. data/rust/src/exemplar/mod.rs +17 -0
  547. data/rust/src/exemplar/mutations.rs +80 -0
  548. data/rust/src/exemplar/queries.rs +33 -0
  549. data/rust/src/exemplar/reactions.rs +88 -0
  550. data/rust/src/exemplar/read_models.rs +48 -0
  551. data/rust/src/exemplar/registry.rs +166 -0
  552. data/rust/src/exemplar/types.rs +44 -0
  553. data/rust/src/kernel/attribute_shapes/composite.rs +88 -0
  554. data/rust/src/kernel/attribute_shapes/list.rs +18 -0
  555. data/rust/src/kernel/attribute_shapes/mod.rs +44 -0
  556. data/rust/src/kernel/attribute_shapes/optional.rs +9 -0
  557. data/rust/src/kernel/attribute_shapes/scalar.rs +39 -0
  558. data/rust/src/kernel/cli.rs +585 -0
  559. data/rust/src/kernel/dispatch.rs +1090 -0
  560. data/rust/src/kernel/expr.rs +458 -0
  561. data/rust/src/kernel/expression_operators/arithmetic.rs +128 -0
  562. data/rust/src/kernel/expression_operators/comparison.rs +48 -0
  563. data/rust/src/kernel/expression_operators/enumeration.rs +232 -0
  564. data/rust/src/kernel/expression_operators/logical.rs +13 -0
  565. data/rust/src/kernel/expression_operators/membership.rs +18 -0
  566. data/rust/src/kernel/expression_operators/mod.rs +68 -0
  567. data/rust/src/kernel/expression_operators/pattern_match.rs +73 -0
  568. data/rust/src/kernel/expression_operators/positional.rs +126 -0
  569. data/rust/src/kernel/expression_operators/presence.rs +101 -0
  570. data/rust/src/kernel/expression_operators/sign_test.rs +16 -0
  571. data/rust/src/kernel/expression_operators/sized.rs +35 -0
  572. data/rust/src/kernel/expression_operators/text.rs +79 -0
  573. data/rust/src/kernel/expression_operators/to_string.rs +18 -0
  574. data/rust/src/kernel/json.rs +779 -0
  575. data/rust/src/kernel/mod.rs +133 -0
  576. data/rust/src/kernel/named_query.rs +219 -0
  577. data/rust/src/kernel/orchestrate.rs +1810 -0
  578. data/rust/src/kernel/pattern.rs +307 -0
  579. data/rust/src/kernel/query_comparators.rs +206 -0
  580. data/rust/src/kernel/query_ordering.rs +103 -0
  581. data/rust/src/kernel/read_model.rs +914 -0
  582. data/rust/src/kernel/reference_lookup.rs +149 -0
  583. data/rust/src/kernel/repository.rs +468 -0
  584. data/rust/src/kernel/routing.rs +612 -0
  585. data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +118 -0
  586. data/rust/src/kernel/vocab/entity_dispatch_order.rs +114 -0
  587. data/rust/src/kernel/vocab/field_hint.rs +70 -0
  588. data/rust/src/kernel/vocab/mod.rs +20 -0
  589. data/rust/src/kernel/vocab/query_comparator.rs +65 -0
  590. data/rust/src/kernel/vocab/refusal_template.rs +1592 -0
  591. data/rust/src/lib.rs +8 -0
  592. data/rust/src/main.rs +16 -0
  593. data/rust/web/.gitignore +1 -0
  594. data/rust/web/Cargo.lock +119 -0
  595. data/rust/web/Cargo.toml +18 -0
  596. data/rust/web/src/lib.rs +8 -0
  597. metadata +477 -12
  598. data/lib/hecks/facade/cli_door.rb +0 -96
  599. data/lib/hecks/facade/cli_runner.rb +0 -127
  600. data/lib/hecks/mcp_door_scope.rb +0 -95
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../tools"
4
+
5
+ module Hecks
6
+ module Tools
7
+ # Removes a command's `attribute :field, Type` line when it only repeats what an `append:`
8
+ # target element already declares (see `CommandBuilder#resolve_append_fields!`). The
9
+ # edit/reboot/diff-or-revert machinery lives in `Hecks::Codemod`.
10
+ #
11
+ # hecks drop_implicit_append_fields [--dry-run]
12
+ module DropImplicitAppendFields
13
+ # One attribute a command repeats.
14
+ Candidate = Struct.new(:command_name, :field, :type, keyword_init: true)
15
+
16
+ module_function
17
+
18
+ # Runs the codemod over every example domain and prints what it found.
19
+ #
20
+ # @param argv [Array<String>] `--dry-run` to restore every file after the rewrite is checked
21
+ # @param root [String] the checkout (unused: the examples come from `Codemod`)
22
+ # @return [Integer] 0
23
+ def main(argv, root: Tools::ROOT)
24
+ _ = root
25
+ require "hecks"
26
+ require "hecks/codemod"
27
+ dry_run = argv.include?("--dry-run")
28
+
29
+ runner = Hecks::Codemod::Runner.new(find_candidates: method(:find_candidates),
30
+ apply_candidate: method(:apply_candidate),
31
+ label: ->(c) { "#{c.command_name}##{c.field} (#{c.type})" })
32
+ results = runner.run(dry_run: dry_run)
33
+ runner.report(results, dry_run: dry_run)
34
+ 0
35
+ end
36
+
37
+ # @param candidate [Candidate] the attribute a command declares
38
+ # @param owner_attr [Object, nil] the same attribute on the element the command appends to
39
+ # @return [Boolean] whether the two declare the same thing
40
+ def identical_attribute?(candidate, owner_attr)
41
+ return false unless owner_attr
42
+
43
+ %i[type pattern optional? admits].all? { |m| candidate.public_send(m) == owner_attr.public_send(m) }
44
+ end
45
+
46
+ # Replays `CommandBuilder#resolve_append_fields!`'s lookup against the booted IR.
47
+ #
48
+ # @param registry [Object] the booted bluebooks
49
+ # @return [Array<Candidate>] the attributes that can be dropped
50
+ def find_candidates(registry)
51
+ found = []
52
+ Hecks::Codemod.each_command(registry) do |construct, command|
53
+ command.mutations.select { |m| m.op == :append }.each do |mutation|
54
+ element = Hecks::Codemod.element_construct_for(construct, mutation.target)
55
+ next unless element
56
+
57
+ mutation.source.each do |field, value|
58
+ next unless value.is_a?(Symbol) && value.to_s == field.to_s
59
+
60
+ local = command.attributes.find { |attr| attr.name.to_s == field.to_s }
61
+ next unless local
62
+
63
+ owner_attr = Hecks::Codemod.owner_attribute(element, field)
64
+ next unless identical_attribute?(local, owner_attr)
65
+
66
+ found << Candidate.new(command_name: command.hecks_name, field: field.to_s, type: local.type)
67
+ end
68
+ end
69
+ end
70
+ found
71
+ end
72
+
73
+ # @param text [String] a bluebook's source
74
+ # @param candidate [Candidate] the attribute to drop
75
+ # @return [Array(String, Boolean)] `[new_text, true]` on a single clean match, `[text, false]`
76
+ # when ambiguous or absent
77
+ def apply_candidate(text, candidate)
78
+ window_start = text.index(/^\s*command\s+"#{Regexp.escape(candidate.command_name)}"/)
79
+ return [text, false] unless window_start
80
+
81
+ window_end = text.index(/^\s*emits\s/, window_start)
82
+ return [text, false] unless window_end
83
+
84
+ before = text[0...window_start]
85
+ window = text[window_start...window_end]
86
+ after = text[window_end..]
87
+
88
+ pattern = /^[ \t]*attribute\s+:#{Regexp.escape(candidate.field)}\s*,[^\n]*\n/
89
+ return [text, false] unless window.scan(pattern).size == 1
90
+
91
+ [before + window.sub(pattern, "") + after, true]
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hecks"
4
+ require_relative "../tools"
5
+
6
+ module Hecks
7
+ module Tools
8
+ # Backfills `hecks_eras.held_projection` for rows that predate it, by calling the same path
9
+ # `EraStore#eras` already takes on boot, explicitly. Idempotent.
10
+ #
11
+ # hecks backfill_projections <domain path>
12
+ module EraProjectionBackfill
13
+ module_function
14
+
15
+ # Backfills the rows that carry no projection and prints how many it fixed.
16
+ #
17
+ # @param argv [Array<String>] the domain directory
18
+ # @param root [String] unused: the domain is read from the path given
19
+ # @return [Integer] 0, or 1 when a row fails its own integrity check
20
+ # @raise [SystemExit] when the domain cannot be loaded or its adapter holds no eras
21
+ def main(argv, **)
22
+ domain_path = argv.first
23
+ abort "usage: hecks backfill_projections <domain path>" unless domain_path
24
+
25
+ loading = Hecks::Ports::Loading.bootstrap
26
+ directory = loading.bluebook_directory(domain_path)
27
+ registry = Hecks::Runtime::Registry.new(root: File.dirname(directory))
28
+ Hecks.with_registry(registry) do
29
+ loading.load_library
30
+ loading.load_project(loading.shared_root(nil, directory))
31
+ loading.load_each(directory, %w[*.port *.adapter *.bluebook *.hecksagon *.world])
32
+ end
33
+
34
+ bluebook = registry.bluebooks.values.first or abort "no bluebook in #{directory}"
35
+ backfill(bluebook, lineage_settings(registry, bluebook))
36
+ end
37
+
38
+ # @return [Hash] the persistence binding's settings
39
+ # @raise [SystemExit] when the bound adapter holds no eras
40
+ def lineage_settings(registry, bluebook)
41
+ first = bluebook.aggregates.first or abort "#{bluebook.name} declares no aggregates"
42
+ adapter_name = Hecks::Ports::Persistence::BindingPolicy.resolve(registry, bluebook.name, first).adapter
43
+ capable =
44
+ begin
45
+ adapter_class = registry.adapter_class(adapter_name)
46
+ adapter_class.respond_to?(:lineage_capable?) && adapter_class.lineage_capable?
47
+ rescue StandardError
48
+ false
49
+ end
50
+ # Only a lineage-capable adapter holds eras — every other adapter has no held_projection
51
+ # column to migrate.
52
+ abort "#{bluebook.name} is bound to #{adapter_name}, which holds no eras — nothing to migrate." unless capable
53
+
54
+ registry.binding_settings(bluebook.name, Hecks::Ports::Persistence::VERB, adapter_name)
55
+ end
56
+
57
+ # @return [Integer] the exit status
58
+ def backfill(bluebook, settings)
59
+ db = Hecks::Adapters::PostgresEra.connect_for(bluebook.name, settings)
60
+ lineage = Hecks::Adapters::PostgresEra::Lineage.new(db, bluebook.name)
61
+ lineage.ensure_base!
62
+
63
+ missing = lambda {
64
+ db.exec_params(
65
+ "SELECT count(*)::int AS n FROM hecks_eras WHERE domain = $1 AND held_projection IS NULL",
66
+ [bluebook.name]
67
+ )[0]["n"].to_i
68
+ }
69
+
70
+ before = missing.call
71
+ if before.zero?
72
+ puts "#{bluebook.name}: every held era already carries a projection — nothing to do."
73
+ return 0
74
+ end
75
+
76
+ begin
77
+ # Raises on the first row whose own digest check fails — a tampered row, not a merely
78
+ # legacy one, which backfilling must not paper over.
79
+ lineage.eras
80
+ rescue Hecks::Runtime::WiringError => e
81
+ puts "#{bluebook.name}: stopped at a row that isn't merely legacy — it fails its own integrity check:"
82
+ puts " #{e.message}"
83
+ puts " resolve that first (see hecks reattest), then run this again for the remaining rows."
84
+ return 1
85
+ end
86
+
87
+ backfilled = before - missing.call
88
+ puts "#{bluebook.name}: backfilled #{backfilled} era#{'s' unless backfilled == 1} — " \
89
+ "every row now carries a projection."
90
+ 0
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hecks"
4
+ require "json"
5
+ require_relative "../tools"
6
+
7
+ module Hecks
8
+ module Tools
9
+ # Re-attests an era's held text after a digest-integrity refusal, once a human has read it.
10
+ # Refuses without `--accept`; each attestation (old digest, new digest, when) is logged. The
11
+ # digest is tamper-evidence against drift, not against an adversary.
12
+ #
13
+ # hecks reattest <domain> <era ordinal> [--accept]
14
+ module EraReattest
15
+ USAGE = "usage: hecks reattest <domain> <era ordinal> [--accept]"
16
+
17
+ module_function
18
+
19
+ # Shows the held text against its recorded digest and, with `--accept`, re-freezes it.
20
+ #
21
+ # @param argv [Array<String>] the domain directory, the era's ordinal and `--accept`
22
+ # @param root [String] unused: the domain is read from the path given
23
+ # @return [Integer] 0 when nothing needed attesting or it was attested, 1 when refused
24
+ # @raise [SystemExit] when the domain cannot be loaded or holds no such era
25
+ def main(argv, **)
26
+ argv = argv.dup
27
+ accept = argv.delete("--accept")
28
+ domain_path = argv.shift
29
+ ordinal = argv.shift&.to_i
30
+ abort USAGE unless domain_path && ordinal&.positive?
31
+
32
+ registry, bluebook = load_domain(domain_path)
33
+ lineage = open_lineage(registry, bluebook)
34
+ era = lineage.raw_era(ordinal) or abort "#{bluebook.name} holds no era #{ordinal}"
35
+ verify(bluebook, lineage, era, ordinal, accept)
36
+ end
37
+
38
+ # No translations are loaded: an unresolved edge must not block attesting a text.
39
+ #
40
+ # @param domain_path [String] the domain directory
41
+ # @return [Array] the registry and its bluebook
42
+ def load_domain(domain_path)
43
+ loading = Hecks::Ports::Loading.bootstrap
44
+ directory = loading.bluebook_directory(domain_path)
45
+ registry = Hecks::Runtime::Registry.new(root: File.dirname(directory))
46
+ Hecks.with_registry(registry) do
47
+ loading.load_library
48
+ loading.load_project(loading.shared_root(nil, directory))
49
+ loading.load_each(directory, %w[*.port *.adapter *.bluebook *.hecksagon *.world])
50
+ end
51
+ bluebook = registry.bluebooks.values.first or abort "no bluebook in #{directory}"
52
+ [registry, bluebook]
53
+ end
54
+
55
+ # @return [Object] the domain's ensured lineage
56
+ # @raise [SystemExit] when the bound adapter holds no eras
57
+ def open_lineage(registry, bluebook)
58
+ first = bluebook.aggregates.first or abort "#{bluebook.name} declares no aggregates"
59
+ adapter_name = Hecks::Ports::Persistence::BindingPolicy.resolve(registry, bluebook.name, first).adapter
60
+ capable =
61
+ begin
62
+ adapter_class = registry.adapter_class(adapter_name)
63
+ adapter_class.respond_to?(:lineage_capable?) && adapter_class.lineage_capable?
64
+ rescue StandardError
65
+ false
66
+ end
67
+ # Only a lineage-capable adapter holds era texts.
68
+ unless capable
69
+ abort "#{bluebook.name} is bound to #{adapter_name}, which holds no eras — " \
70
+ "there is no frozen text to re-attest."
71
+ end
72
+
73
+ settings = registry.world(bluebook.name)&.for_binding(Hecks::Ports::Persistence::VERB, adapter_name) || {}
74
+ db = Hecks::Adapters::PostgresEra.connect_for(bluebook.name, settings)
75
+ lineage = Hecks::Adapters::PostgresEra::Lineage.new(db, bluebook.name)
76
+ lineage.ensure_base!
77
+ lineage
78
+ end
79
+
80
+ # @return [Integer] the exit status
81
+ def verify(bluebook, lineage, era, ordinal, accept)
82
+ text = era[:held_text]
83
+ stored = era[:held_digest]
84
+ computed = Digest::SHA256.hexdigest(text)
85
+ if stored == computed
86
+ puts "#{bluebook.name} era #{ordinal}: the held text matches its digest — nothing to re-attest."
87
+ return 0
88
+ end
89
+
90
+ puts "#{bluebook.name} era #{ordinal}: the held text does NOT match its recorded digest."
91
+ puts " recorded: #{stored || '(none)'}"
92
+ puts " computed: #{computed}"
93
+ return 1 unless shape_unchanged?(bluebook, era, ordinal, text)
94
+
95
+ show(text)
96
+ return refuse_without_accept unless accept
97
+
98
+ fresh = lineage.reattest!(ordinal)
99
+ puts
100
+ puts "ATTESTED: era #{ordinal} re-frozen as #{fresh[0, 12]}… — the old and new digests are recorded."
101
+ 0
102
+ end
103
+
104
+ # A shape change refuses hard; no `--accept` passes it.
105
+ #
106
+ # @return [Boolean] false after printing the refusal
107
+ def shape_unchanged?(bluebook, era, ordinal, text)
108
+ stored_projection = era[:held_projection] && JSON.parse(era[:held_projection])
109
+ verdict = Hecks::Translation::Reattest.shape_guard!(
110
+ domain: bluebook.name, ordinal: ordinal, text: text,
111
+ stored_hash: era[:hash], stored_projection: stored_projection
112
+ )
113
+ case verdict
114
+ when :cosmetic
115
+ puts " shape: unchanged — the edit is cosmetic (the text still projects to era #{ordinal}'s " \
116
+ "minted name)"
117
+ when :unnamed
118
+ puts " shape: era #{ordinal} was never named, so no shape comparison is possible — read carefully"
119
+ end
120
+ true
121
+ rescue Hecks::Runtime::WiringError => e
122
+ puts
123
+ puts "REFUSED: #{e.message}"
124
+ false
125
+ end
126
+
127
+ # @return [void]
128
+ def show(text)
129
+ puts
130
+ puts "The held text AS IT NOW STANDS — the original is gone; this is what you would be attesting to:"
131
+ puts "─" * 72
132
+ puts text
133
+ puts "─" * 72
134
+ end
135
+
136
+ # @return [Integer] 1
137
+ def refuse_without_accept
138
+ puts
139
+ puts "REFUSED: nothing changed. If you have read the text above and accept it as this era's"
140
+ puts "frozen source, run again with --accept. The attestation (old digest, new digest, when)"
141
+ puts "goes on the record either way you decide."
142
+ 1
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,189 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../tools"
4
+
5
+ module Hecks
6
+ module Tools
7
+ # Walks a bluebook language change (syntax row, builder, golden) through its lifecycle:
8
+ # `status`, `propose`, `admit`, `deprecate`, `retire` and `rename` a word, and the
9
+ # `argument-*` forms of the same for a keyword's arguments (see `USAGE`).
10
+ #
11
+ # hecks evolve propose <word> --context Aggregate a row enters, proposed
12
+ # hecks evolve admit <word> --context Aggregate proposed -> admitted
13
+ # hecks evolve rename <word> --context X --to new
14
+ #
15
+ # Mutating commands regenerate the golden and run the gates; a failing gate restores every file.
16
+ module EvolveRun
17
+ # The specs a change must keep green.
18
+ GATES = %w[
19
+ spec/syntax_conformance_spec.rb
20
+ spec/syntax_lifecycle_spec.rb
21
+ spec/ir_golden_spec.rb
22
+ ].freeze
23
+
24
+ # Words that move a word to a status.
25
+ WORD_STATUS = { "admit" => "admitted", "deprecate" => "deprecated", "retire" => "retired" }.freeze
26
+
27
+ # Words that move a keyword's argument to a status.
28
+ ARGUMENT_STATUS = { "argument-admit" => "admitted", "argument-deprecate" => "deprecated",
29
+ "argument-retire" => "retired" }.freeze
30
+
31
+ USAGE = "usage: hecks evolve status|propose|admit|deprecate|retire <word> --context <Context> " \
32
+ "[--body none|keywords|source|rows] [--inner X] [--opens X] [--fills x]\n " \
33
+ "or: hecks evolve argument-propose|argument-admit|argument-deprecate|argument-retire " \
34
+ "<keyword> --context <Context> [--kind K] [--at N] [--named NAME] [--required true|false] " \
35
+ "[--fills F]"
36
+
37
+ module_function
38
+
39
+ # Runs one lifecycle command.
40
+ #
41
+ # @param argv [Array<String>] the command, its word or keyword, then its flags
42
+ # @param root [String] the checkout whose syntax tables and golden are edited
43
+ # @return [Integer] 0, or 1 when a gate refuses and the files are restored
44
+ # @raise [SystemExit] with the reason when an argument is missing
45
+ def main(argv, root: Tools::ROOT)
46
+ require "hecks/grammar/evolve"
47
+ args = argv.dup
48
+ command = args.shift
49
+ evolve = Hecks::Grammar::Evolve
50
+ ok =
51
+ case command
52
+ when "status" then status(evolve)
53
+ when "propose" then propose(args, evolve, root)
54
+ when *WORD_STATUS.keys then set_status(command, args, evolve, root)
55
+ when "rename" then rename(args, evolve, root)
56
+ when "argument-propose" then propose_argument(args, evolve, root)
57
+ when *ARGUMENT_STATUS.keys then set_argument_status(command, args, evolve, root)
58
+ else abort USAGE
59
+ end
60
+ ok == false ? 1 : 0
61
+ end
62
+
63
+ # Rewrites spec/golden/ir/Bluebook.json by running its spec with `GOLDEN=rewrite`.
64
+ #
65
+ # @param root [String] the checkout
66
+ # @return [Boolean, nil] whether the spec ran green
67
+ def regenerate!(root)
68
+ system({ "GOLDEN" => "rewrite" },
69
+ "bundle", "exec", "rspec", "spec/ir_golden_spec.rb",
70
+ chdir: root, out: File::NULL, err: File::NULL)
71
+ end
72
+
73
+ # Runs the syntax conformance, syntax lifecycle and golden IR specs.
74
+ #
75
+ # @param root [String] the checkout
76
+ # @return [Boolean, nil] whether they passed
77
+ def gates_pass?(root)
78
+ system("bundle", "exec", "rspec", *GATES, chdir: root)
79
+ end
80
+
81
+ # Runs one grammar edit inside the snapshot/regenerate/gate sequence, restoring every
82
+ # snapshot if the block raises or a gate fails.
83
+ #
84
+ # @param word [String] what the edit is about, for the report
85
+ # @param context [String] the aggregate the word belongs to
86
+ # @param evolve [Module] `Hecks::Grammar::Evolve`
87
+ # @param root [String] the checkout
88
+ # @return [Boolean] true if the block ran and the gates held
89
+ def guarded(word, context, evolve, root, &)
90
+ paths = evolve.syntax_paths + [File.join(root, "spec/golden/ir/Bluebook.json")]
91
+ snapshots = paths.to_h { |path| [path, File.read(path)] }
92
+ evolve.restore_on_raise(paths, &)
93
+ regenerate!(root)
94
+ if gates_pass?(root)
95
+ puts "\n#{context}.#{word} — the gates hold. Regenerated projections and the golden are in the tree."
96
+ true
97
+ else
98
+ snapshots.each { |path, content| File.write(path, content) }
99
+ puts "\nRESTORED — the gates refused, and the failures above are the checklist. " \
100
+ "Nothing was changed."
101
+ false
102
+ end
103
+ rescue StandardError => e
104
+ # `restore_on_raise` has already restored the files; this only reports.
105
+ puts "\nRESTORED — #{e.message} Nothing was changed."
106
+ false
107
+ end
108
+
109
+ # @return [true]
110
+ def status(evolve) # rubocop:disable Naming/PredicateMethod
111
+ rows = evolve.keyword_rows
112
+ moving = rows.reject { |row| row[:status] == "admitted" }
113
+ renamed = rows.reject { |row| row[:was].to_s.empty? }
114
+ puts "#{rows.size} keyword rows; #{moving.size} not simply admitted; #{renamed.size} renamed"
115
+ moving.each { |row| puts " #{row[:status].ljust(10)} #{row[:context]}.#{row[:word]}" }
116
+ renamed.each { |row| puts " renamed #{row[:context]}.#{row[:word]} (was #{row[:was]})" }
117
+ true
118
+ end
119
+
120
+ # @return [Boolean] whether the gates held
121
+ def propose(args, evolve, root)
122
+ word = args.shift or abort "propose what word?"
123
+ context = evolve.option(args, "context") or abort "--context is required — a word is a word somewhere"
124
+ ok = guarded(word, context, evolve, root) do
125
+ evolve.propose(word: word, context: context,
126
+ body: evolve.option(args, "body", "none"), inner: evolve.option(args, "inner", ""),
127
+ opens: evolve.option(args, "opens", ""), fills: evolve.option(args, "fills", ""))
128
+ end
129
+ if ok
130
+ puts "Proposed. It reaches no projection until admitted. Before `hecks evolve admit`:"
131
+ puts " 1. teach the #{context} builder the word (and its spec/dsl_spec example)"
132
+ end
133
+ ok
134
+ end
135
+
136
+ # @return [Boolean] whether the gates held
137
+ def set_status(command, args, evolve, root)
138
+ word = args.shift or abort "#{command} what word?"
139
+ context = evolve.option(args, "context") or abort "--context is required"
140
+ guarded(word, context, evolve, root) do
141
+ evolve.set_status(word: word, context: context, to: WORD_STATUS.fetch(command))
142
+ end
143
+ end
144
+
145
+ # @return [Boolean] whether the gates held
146
+ def rename(args, evolve, root)
147
+ word = args.shift or abort "rename what word?"
148
+ context = evolve.option(args, "context") or abort "--context is required"
149
+ to = evolve.option(args, "to") or abort "--to is required — a rename goes somewhere"
150
+ ok = guarded(word, context, evolve, root) { evolve.rename(word: word, context: context, to: to) }
151
+ if ok
152
+ puts "Renamed. The old spelling keeps parsing — that is the point. Before this holds:"
153
+ puts " 1. alias the new word to the old in the #{context} builder (alias_method :#{to}, :#{word})"
154
+ puts " 2. add the identical-IR example to spec/dsl_spec.rb"
155
+ end
156
+ ok
157
+ end
158
+
159
+ # @return [Boolean] whether the gates held
160
+ def propose_argument(args, evolve, root)
161
+ keyword = args.shift or abort "argument-propose which keyword's argument?"
162
+ context = evolve.option(args, "context") or abort "--context is required"
163
+ kind = evolve.option(args, "kind") or abort "--kind is required — text|symbol|number|flag|literal|constant|pairs|list"
164
+ ok = guarded("#{keyword} argument", context, evolve, root) do
165
+ evolve.propose_argument(keyword: keyword, context: context, kind: kind,
166
+ required: evolve.option(args, "required", "false"),
167
+ at: evolve.option(args, "at", ""), named: evolve.option(args, "named", ""),
168
+ fills: evolve.option(args, "fills", ""),
169
+ pairs_shape: evolve.option(args, "pairs-shape"))
170
+ end
171
+ if ok
172
+ puts "Proposed. It reaches no projection until admitted. Before `hecks evolve argument-admit`:"
173
+ puts " 1. teach the #{context} builder's #{keyword} method the argument"
174
+ end
175
+ ok
176
+ end
177
+
178
+ # @return [Boolean] whether the gates held
179
+ def set_argument_status(command, args, evolve, root)
180
+ keyword = args.shift or abort "#{command} which keyword's argument?"
181
+ context = evolve.option(args, "context") or abort "--context is required"
182
+ guarded("#{keyword} argument", context, evolve, root) do
183
+ evolve.set_argument_status(keyword: keyword, context: context, to: ARGUMENT_STATUS.fetch(command),
184
+ at: evolve.option(args, "at", ""), named: evolve.option(args, "named", ""))
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end