hecks 2.9.0 → 3.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 (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 +4 -2
  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 +7 -4
  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 +6 -3
  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,42 @@
1
+ # Where the Hecks domain keeps its journal: every command's events, one entry per aggregate.
2
+ #
3
+ # **PostgresEra** is the default, for real runs: a client's `hecks` and a maintainer's on main
4
+ # or in the merge queue. The journal then survives the process, and `hecks stores` and
5
+ # `hecks history` read it back. `default_adapter` binds every aggregate of the chapter (and of
6
+ # the attached Governance chapter, see governance.world) in one line, which is what lets the
7
+ # environments/memory.world overlay swap the whole domain to Memory.
8
+ #
9
+ # **Memory** is for anything that must not touch a database: specs and pull-request runs.
10
+ # Select it with `HECKS_ENVIRONMENT=memory` (Hecks.boot reads the variable) or
11
+ # `Hecks.boot(path, environment: "memory")`. Queries and refused or dry-run requests record
12
+ # nothing worth keeping, so they behave the same on either.
13
+ #
14
+ # The database is `HECKS_DATABASE` when set, so a client points the domain at their own
15
+ # Postgres (a `postgres://` URL, or a bare database name); otherwise a local database named for
16
+ # the domain. It connects as the ordinary role `hecks`, never a superuser: the era write-fence
17
+ # (row-level security) is bypassed by one, so boot refuses it. Create the role once with
18
+ # `hecks quality_control create_ledger_role hecks` and the database with `createdb -O hecks hecks`.
19
+ #
20
+ # PostgresEra versions the storage shape. Once a database holds an era, a later change to this
21
+ # domain's shape needs a translation edge, kept in `translations/` beside the bluebook; an
22
+ # edge is written only when such a change is made.
23
+ #
24
+ # **launcher** switches on the opt-in parts of the generated `hecks` launcher (see
25
+ # `Doors::LauncherOptions`): `run_keys` mints a `run` key for a creating command given none,
26
+ # `failure_states` are the lifecycle states `--wait` reports as exit 1 (every failure state the
27
+ # chapter's lifecycles declare), `names` gives a command the launcher name clients type,
28
+ # `executable` is where `hecks project_cli` writes this chapter's launcher (the gem's
29
+ # `exe/hecks`), and `legacy` names the verbs that launcher hands to `Hecks::CLI` in the
30
+ # positional forms ADR 0066 shipped.
31
+ Hecks.world "Hecks" do
32
+ realm "Hecks"
33
+ default_adapter "PostgresEra"
34
+ default_database ENV.fetch("HECKS_DATABASE", "postgres://hecks@localhost/hecks")
35
+ launcher "Launcher", run_keys: true,
36
+ failure_states: %w[flagged failed drifted unreachable refused faulted halted stopped
37
+ abandoned red needs_fix],
38
+ names: { "mcp" => "serve_mcp", "console" => "open_console" },
39
+ executable: "exe/hecks",
40
+ legacy: %w[run docs narrate ir stores model_check smoke_test
41
+ project_diagrams project_cli mcp]
42
+ end
@@ -0,0 +1,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "projection_files"
4
+
5
+ module Hecks
6
+ # The capability enums the hand-written Rust kernel is matched against, and the check that every
7
+ # capability the Ruby grammar admits has its hand-written file.
8
+ #
9
+ # The `pub mod` roster and the enum of `rust/src/kernel/attribute_shapes/mod.rs` and
10
+ # `rust/src/kernel/expression_operators/mod.rs` are generated; the `<name>.rs` files beside them
11
+ # are hand-written, so a grammar change can leave a `pub mod` naming a file nobody has written.
12
+ # Ground truth is `Coercion::SHAPES` and `Grammar.admitted_operators`, never a copied list.
13
+ # Query comparators are one flat hand-maintained file with no per-variant files to check.
14
+ module KernelCapabilities
15
+ # One capability a hand-written kernel file should exist for.
16
+ #
17
+ # @!attribute [r] name
18
+ # @return [String] the capability's name
19
+ # @!attribute [r] path
20
+ # @return [String] where its file conventionally stands
21
+ # @!attribute [r] present
22
+ # @return [Boolean] whether the file exists
23
+ # @!attribute [r] source
24
+ # @return [String] where the capability is declared
25
+ Row = Struct.new(:name, :path, :present, :source)
26
+
27
+ module_function
28
+
29
+ # Renders both capability modules without writing them.
30
+ #
31
+ # @param root [String] the checkout
32
+ # @return [ProjectionFiles::Result] each `mod.rs`, and nothing stale
33
+ def build(root: ProjectionFiles::ROOT)
34
+ require "hecks"
35
+ require "hecks/grammar"
36
+ require "hecks/runtime/value/coercion"
37
+ kernel = File.join(root, "rust/src/kernel")
38
+ content = {
39
+ File.join(kernel, "attribute_shapes/mod.rs") => shapes_module,
40
+ File.join(kernel, "expression_operators/mod.rs") => operators_module
41
+ }
42
+ ProjectionFiles::Result.new(content, [])
43
+ end
44
+
45
+ # Writes the capability modules.
46
+ #
47
+ # @param root [String] the checkout
48
+ # @return [Array<String>] one `wrote <path>` line for each file
49
+ def write(root: ProjectionFiles::ROOT)
50
+ build(root: root).content.map { |path, text| ProjectionFiles.write_atomically(path, text) }
51
+ end
52
+
53
+ # Checks every capability the live grammar admits against the kernel's files.
54
+ #
55
+ # @param root [String] the checkout
56
+ # @return [Array<Row>] one row for each attribute shape and expression-operator category
57
+ def coverage(root: ProjectionFiles::ROOT)
58
+ require "hecks"
59
+ require "hecks/grammar"
60
+ require "hecks/runtime/value/coercion"
61
+ check(root, "attribute_shapes", Hecks::Runtime::Value::Coercion::SHAPES, "Coercion::SHAPES") +
62
+ check(root, "expression_operators", operator_categories,
63
+ "Grammar.admitted_operators's own category field")
64
+ end
65
+
66
+ # The coverage check as text: one line for each capability, then a verdict.
67
+ #
68
+ # @param root [String] the checkout
69
+ # @return [Array(String, String, Boolean)] the lines of the check, the closing verdict (the
70
+ # complaint, when a file is missing), and whether every file is present
71
+ def coverage_report(root: ProjectionFiles::ROOT)
72
+ rows = coverage(root: root)
73
+ prefix = "#{root}/"
74
+ lines = rows.map do |row|
75
+ "#{row.present ? 'OK ' : 'MISS'} #{row.path.delete_prefix(prefix)} (#{row.source}: #{row.name.inspect})"
76
+ end
77
+ missing = rows.reject(&:present)
78
+ return [lines.join("\n"), coverage_verdict(rows.size), true] if missing.empty?
79
+
80
+ gaps = missing.map { |row| " #{row.path.delete_prefix(prefix)}" }
81
+ head = "#{missing.size} capability file(s) missing — the grammar admits these but no " \
82
+ "hand-written Rust interpretation exists for them yet:"
83
+ [lines.join("\n"), [head, *gaps].join("\n"), false]
84
+ end
85
+
86
+ # @param count [Integer] how many capabilities there are
87
+ # @return [String] the clean verdict
88
+ def coverage_verdict(count)
89
+ "#{count}/#{count} kernel capability files present — every attribute shape and " \
90
+ "expression-operator category the live Ruby grammar admits has a rust/src/kernel/ file " \
91
+ "at its conventional path."
92
+ end
93
+
94
+ # @param name [String, Symbol] a snake_case capability name
95
+ # @return [String] the PascalCase Rust variant
96
+ def pascal(name)
97
+ name.to_s.split("_").map { |part| part[0].upcase + part[1..] }.join
98
+ end
99
+
100
+ # `category` in first-appearance order over the admission ledger, never alphabetized, so a
101
+ # re-run does not reshuffle variants.
102
+ #
103
+ # @return [Array<String>] the expression-operator categories
104
+ def operator_categories
105
+ Hecks::Grammar.admitted_operators.map { |op| op[:category] }.uniq
106
+ end
107
+
108
+ # @param root [String] the checkout
109
+ # @param category_dir [String] the directory below `rust/src/kernel/`
110
+ # @param names [Array<String>] the capabilities to look for
111
+ # @param source [String] where they are declared
112
+ # @return [Array<Row>] a row for each
113
+ def check(root, category_dir, names, source)
114
+ names.map do |name|
115
+ path = File.join(root, "rust/src/kernel", category_dir, "#{name}.rs")
116
+ Row.new(name, path, File.file?(path), source)
117
+ end
118
+ end
119
+
120
+ # @return [String] the whole `attribute_shapes/mod.rs`
121
+ def shapes_module
122
+ header = <<~HEADER.strip
123
+ // Ground truth: Hecks::Runtime::Value::Coercion::SHAPES
124
+ // (lib/hecks/runtime/value/coercion.rb) — the four branches
125
+ // `for_attribute` itself takes, read directly rather than
126
+ // re-derived here.
127
+ HEADER
128
+ render(header: header, enum_name: "AttributeShape", names: Hecks::Runtime::Value::Coercion::SHAPES,
129
+ doc_line: "Coercion::SHAPES")
130
+ end
131
+
132
+ # @return [String] the whole `expression_operators/mod.rs`
133
+ def operators_module
134
+ header = <<~HEADER.strip
135
+ // Ground truth: Hecks::Grammar.admitted_operators (lib/hecks/
136
+ // grammar.rb), the SAME live-booted call hecks project_expression_tables
137
+ // uses — grouped by the `category` field Evaluator::PROJECTION already
138
+ // partitions comparison operators by (evaluator.rb).
139
+ HEADER
140
+ render(header: header, enum_name: "OperatorCategory", names: operator_categories,
141
+ doc_line: "Grammar.admitted_operators's own `category` field")
142
+ end
143
+
144
+ # Renders one capability module: `pub mod` lines, the enum, and its `ALL`.
145
+ #
146
+ # @param header [String] the ground-truth comment
147
+ # @param enum_name [String] `AttributeShape` or `OperatorCategory`
148
+ # @param names [Array<String>] the capabilities, in declaration order
149
+ # @param doc_line [String] where the capabilities are declared
150
+ # @return [String] the Rust source
151
+ def render(header:, enum_name:, names:, doc_line:)
152
+ shapes = enum_name == "AttributeShape"
153
+ variants = names.map { |name| " #{pascal(name)}," }.join("\n")
154
+ mods = names.map { |name| "pub mod #{name};" }.join("\n")
155
+ arms = names.map { |name| " #{enum_name}::#{pascal(name)} => #{name.to_s.inspect}," }.join("\n")
156
+ all = names.map { |name| "#{enum_name}::#{pascal(name)}" }.join(", ")
157
+
158
+ <<~RUST
159
+ // GENERATED by hecks project_kernel_capabilities from #{doc_line}.
160
+ // Do not hand-edit — re-run hecks project_kernel_capabilities instead.
161
+ //
162
+ #{header}
163
+
164
+ #{mods}
165
+
166
+ /// One variant per real #{shapes ? 'attribute shape' : 'expression-operator category'} the Ruby
167
+ /// grammar admits, in the order #{doc_line} declares them. Every match
168
+ /// over this enum in the kernel (rust/src/kernel/expr.rs) is written
169
+ /// WITHOUT a wildcard `_ =>` arm — see expr.rs's own header for why:
170
+ /// a wildcard here would let a newly-admitted capability compile
171
+ /// silently into a router that never calls the new file this same
172
+ /// regeneration adds a `pub mod` line for above.
173
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
174
+ pub enum #{enum_name} {
175
+ #{variants}
176
+ }
177
+
178
+ impl #{enum_name} {
179
+ /// The `rust/src/kernel/#{shapes ? 'attribute_shapes' : 'expression_operators'}/<name>.rs` file this
180
+ /// variant names — `hecks measure_kernel_coverage`'s own existence
181
+ /// check reads this, so the file-name spelling here is the ONE
182
+ /// place that ever needs to change if a name is renamed.
183
+ pub fn module_name(&self) -> &'static str {
184
+ match self {
185
+ #{arms}
186
+ }
187
+ }
188
+
189
+ pub const ALL: &'static [#{enum_name}] = &[#{all}];
190
+ }
191
+ RUST
192
+ end
193
+ end
194
+ end
@@ -33,6 +33,7 @@ Hecks.bluebook "Bluebook", version: "1" do
33
33
  attribute :version, Version
34
34
  # A rename is a declared fact. Absent is not empty.
35
35
  attribute :formerly_known_as, FormerlyKnownAs
36
+ attribute :namespace, ChapterNamespace
36
37
  attribute :normalisations, list_of(NormalisationRule)
37
38
  # Core contexts a sub-language chapter contributes Syntax rows for (ADR 0026).
38
39
  # Absent is not empty: most chapters attach to nothing.
@@ -92,6 +93,12 @@ Hecks.bluebook "Bluebook", version: "1" do
92
93
  invariant("a formerly_known_as says something") { !value.to_s.empty? }
93
94
  end
94
95
 
96
+ # The Ruby module a chapter's constants install under instead of its own name.
97
+ value_object "ChapterNamespace" do
98
+ attribute :value, String
99
+ invariant("a namespace is a constant path") { value.to_s.match?(/\A[A-Z][A-Za-z0-9_]*(::[A-Z][A-Za-z0-9_]*)*\z/) }
100
+ end
101
+
95
102
  value_object "AttachesToContext" do
96
103
  attribute :value, String
97
104
  invariant("an attachment names a context") { !value.to_s.empty? }
@@ -106,12 +113,14 @@ Hecks.bluebook "Bluebook", version: "1" do
106
113
  attribute :classification, Classification, optional: true
107
114
  attribute :version, Version, optional: true
108
115
  attribute :formerly_known_as, FormerlyKnownAs, optional: true
116
+ attribute :namespace, ChapterNamespace, optional: true
109
117
 
110
118
  sets :name
111
119
  sets :vision
112
120
  sets :classification
113
121
  sets :version
114
122
  sets :formerly_known_as
123
+ sets :namespace
115
124
 
116
125
  emits ChapterDeclared
117
126
  end
@@ -185,6 +194,7 @@ Hecks.bluebook "Bluebook", version: "1" do
185
194
  member word: "bluebook", context: "File", body: "keywords", inner: "Bluebook", opens: "Bluebook", fills: ""
186
195
  member word: "vision", context: "Bluebook", body: "none", inner: "", opens: "", fills: "vision"
187
196
  member word: "formerly_known_as", context: "Bluebook", body: "none", inner: "", opens: "", fills: "formerly_known_as"
197
+ member word: "namespace", context: "Bluebook", body: "none", inner: "", opens: "", fills: "namespace"
188
198
  member word: "attaches_to", context: "Bluebook", body: "none", inner: "", opens: "", fills: "attaches_to", calls: "attaches_to_impl"
189
199
  member word: "provides", context: "Bluebook", body: "none", inner: "", opens: "", fills: "provides", calls: "provides_impl"
190
200
  member word: "core", context: "Bluebook", body: "none", inner: "", opens: "", fills: "classification"
@@ -218,6 +228,7 @@ Hecks.bluebook "Bluebook", version: "1" do
218
228
  member keyword: "bluebook", context: "File", at: "", named: "version", kind: "text", required: "false", fills: "version"
219
229
  member keyword: "vision", context: "Bluebook", at: "1", named: "", kind: "text", required: "true", fills: "vision", coerce: "false"
220
230
  member keyword: "formerly_known_as", context: "Bluebook", at: "1", named: "", kind: "text", required: "true", fills: "formerly_known_as"
231
+ member keyword: "namespace", context: "Bluebook", at: "1", named: "", kind: "text", required: "true", fills: "namespace"
221
232
  member keyword: "attaches_to", context: "Bluebook", at: "1", named: "", kind: "text", required: "true", fills: "attaches_to", variadic: "true"
222
233
  member keyword: "provides", context: "Bluebook", at: "1", named: "", kind: "text", required: "true", fills: "provides"
223
234
  member keyword: "provides", context: "Bluebook", at: "", named: "assignments", kind: "text", required: "false", fills: "provides"
@@ -15,6 +15,9 @@ Hecks.bluebook "Bluebook" do
15
15
  attribute :order_field, QueryText
16
16
  attribute :order_way, QueryText
17
17
  attribute :limit, QueryText
18
+ # The value object that shapes an answer given from outside the domain, spelled as written:
19
+ # `Name` or `list_of(Name)`. Empty when the answer is the aggregate's own row.
20
+ attribute :returns, QueryText
18
21
  attribute :wheres, list_of(Filter)
19
22
  attribute :attributes, list_of(AskArgument)
20
23
  # Every other option a query carries, as an open map: rows of option name, key within it, and
@@ -88,6 +91,7 @@ Hecks.bluebook "Bluebook" do
88
91
  attribute :order_field, QueryText, optional: true
89
92
  attribute :order_way, QueryText, optional: true
90
93
  attribute :limit, QueryText, optional: true
94
+ attribute :returns, QueryText, optional: true
91
95
 
92
96
  attribute :position, Position, optional: true
93
97
  sets :aggregate
@@ -97,6 +101,7 @@ Hecks.bluebook "Bluebook" do
97
101
  sets :order_field
98
102
  sets :order_way
99
103
  sets :limit
104
+ sets :returns
100
105
  sets :position
101
106
  emits AskDeclared
102
107
  end
@@ -173,6 +178,7 @@ Hecks.bluebook "Bluebook" do
173
178
  member word: "order_by", context: "Query", body: "none", inner: "", opens: "", fills: "order_field", calls: "order_by_impl"
174
179
  member word: "authorize", context: "Query", body: "none", inner: "", opens: "", fills: "options", calls: "authorize_impl"
175
180
  member word: "inspect_query", context: "Query", body: "none", inner: "", opens: "", fills: "options"
181
+ member word: "returns", context: "Query", body: "none", inner: "", opens: "", fills: "returns"
176
182
 
177
183
  end
178
184
 
@@ -210,6 +216,7 @@ Hecks.bluebook "Bluebook" do
210
216
  member keyword: "authorize", context: "Query", at: "1", named: "", kind: "symbol", required: "true", fills: "policy"
211
217
  member keyword: "authorize", context: "Query", at: "", named: "tenant", kind: "symbol", required: "false", fills: "tenant"
212
218
  member keyword: "inspect_query", context: "Query", at: "1", named: "", kind: "symbol", required: "false", fills: "mode"
219
+ member keyword: "returns", context: "Query", at: "1", named: "", kind: "constant", required: "true", fills: "type"
213
220
 
214
221
  end
215
222
  end
@@ -1,7 +1,7 @@
1
1
  Hecks.bluebook "Bluebook" do
2
2
  aggregate "Vocabulary" do
3
3
  # Closed sets the language admits, projected into matching Ruby and Rust
4
- # tables (bin/project_vocabulary, bin/project_rust_vocabulary) so both runtimes agree.
4
+ # tables (hecks project_vocabulary, hecks project_rust_vocabulary) so both runtimes agree.
5
5
  description "The closed sets the language admits — the whole of what any runtime may accept, declared once."
6
6
 
7
7
  # Named only by itself; a vocabulary hangs under nothing else.
@@ -585,7 +585,7 @@ Hecks.bluebook "Bluebook" do
585
585
  # (case-insensitively) to guess an HTML input type or textarea kind, when
586
586
  # nothing more specific already answered (Hecks::Forms::FieldShape's
587
587
  # EMAIL_HINT/URL_HINT/TEL_HINT/TEXTAREA_HINT). `pattern` is the regex
588
- # source, no delimiters or flags. bin/project_field_hints writes the Rust
588
+ # source, no delimiters or flags. hecks project_field_hints writes the Rust
589
589
  # host's copy from these rows, so neither side is typed by hand.
590
590
  #
591
591
  # `resolves_to` names which Field attribute a match sets: html_type for
@@ -604,7 +604,7 @@ Hecks.bluebook "Bluebook" do
604
604
 
605
605
  # Rust keywords (strict + reserved) a generated identifier must not
606
606
  # collide with. RustProjection::Projector::RUST_KEYWORDS reads this;
607
- # bin/project_reserved_names projects it into
607
+ # hecks project_reserved_names projects it into
608
608
  # rust/codegen/src/reserved_names.rs, so Ruby and Rust escape or refuse
609
609
  # the same words from one declaration instead of two hand-typed copies.
610
610
  value_object "RustReservedWord" do
@@ -621,7 +621,7 @@ Hecks.bluebook "Bluebook" do
621
621
  # outside `[features]` (`[package]`, `[lib]`, `[[bin]]`) plus `default`,
622
622
  # which Cargo reserves for the auto-enabled feature set. Read by
623
623
  # RustProjection::Projector::CARGO_RESERVED_DOMAIN_NAMES and projected by
624
- # bin/project_reserved_names, same as RustReservedWord above.
624
+ # hecks project_reserved_names, same as RustReservedWord above.
625
625
  value_object "CargoReservedName" do
626
626
  attribute :name, String,
627
627
  one_of: ["name", "version", "edition", "path", "authors", "license", "description", "default",
@@ -7,6 +7,7 @@ Hecks.bluebook "Hecksagon" do
7
7
  attribute :verb, PortVerb, optional: true
8
8
  attribute :signal, PortSignal, optional: true
9
9
  attribute :answers, list_of(PortAnswer)
10
+ attribute :answered_queries, list_of(QueryBinding)
10
11
 
11
12
  value_object "PortName" do
12
13
  attribute :value, String
@@ -29,6 +30,13 @@ Hecks.bluebook "Hecksagon" do
29
30
  invariant("an answer names a real method") { !value.to_s.empty? }
30
31
  end
31
32
 
33
+ # A query this port's adapter answers. The shape of the answer is the query's own `returns`,
34
+ # declared in the bluebook; the hecksagon only says who answers.
35
+ value_object "QueryBinding" do
36
+ attribute :name, String
37
+ invariant("a bound query is named") { !name.to_s.empty? }
38
+ end
39
+
32
40
  # Keyword rows for DomainPort; SyntaxBoot discovers every aggregate-local seed table.
33
41
  value_object "KeywordSeed" do
34
42
  attribute :word, String
@@ -48,6 +56,7 @@ Hecks.bluebook "Hecksagon" do
48
56
  member word: "verb", context: "DomainPort", body: "none", inner: "", opens: "", fills: "verb"
49
57
  member word: "signal", context: "DomainPort", body: "none", inner: "", opens: "", fills: "signal"
50
58
  member word: "answers", context: "DomainPort", body: "none", inner: "", opens: "", fills: "answers"
59
+ member word: "answers_query", context: "DomainPort", body: "none", inner: "", opens: "", fills: "answered_queries", calls: "answers_query_impl"
51
60
 
52
61
  end
53
62
 
@@ -74,6 +83,7 @@ Hecks.bluebook "Hecksagon" do
74
83
  member keyword: "verb", context: "DomainPort", at: "1", named: "", kind: "text", required: "true", fills: "verb"
75
84
  member keyword: "signal", context: "DomainPort", at: "1", named: "", kind: "symbol", required: "true", fills: "signal"
76
85
  member keyword: "answers", context: "DomainPort", at: "1", named: "", kind: "symbol", required: "true", fills: "answers"
86
+ member keyword: "answers_query", context: "DomainPort", at: "1", named: "", kind: "text", required: "true", fills: "name"
77
87
 
78
88
  # The receiving aggregate is named on the opening word (`to:`), not by `reference_to` inside.
79
89
  member keyword: "operation", context: "DomainPort", at: "", named: "to", kind: "constant", required: "false", fills: "to"
@@ -7,6 +7,7 @@ Hecks.bluebook "Hecksagon" do
7
7
  attribute :subscriptions, list_of(Subscription)
8
8
  attribute :framework_members, list_of(FrameworkMember)
9
9
  attribute :vendored_bluebooks, list_of(VendoredBluebook)
10
+ attribute :attached_chapters, list_of(AttachedChapter)
10
11
 
11
12
  value_object "HecksagonName" do
12
13
  attribute :value, String
@@ -30,6 +31,12 @@ Hecks.bluebook "Hecksagon" do
30
31
  invariant("a vendored bluebook is named") { !value.to_s.empty? }
31
32
  end
32
33
 
34
+ # A chapter the gem carries (the language, Expression, Tenancy, Deploy) this hecksagon attaches.
35
+ value_object "AttachedChapter" do
36
+ attribute :value, String
37
+ invariant("an attached chapter is named") { !value.to_s.empty? }
38
+ end
39
+
33
40
  command "Declare" do
34
41
  role "Deployment"
35
42
  goal "Open a hecksagon for a domain"
@@ -74,6 +81,17 @@ Hecks.bluebook "Hecksagon" do
74
81
  emits "VendoredBluebookAttached"
75
82
  end
76
83
 
84
+ command "Attaches" do
85
+ role "Deployment"
86
+ goal "Attach a chapter the gem carries to this hecksagon, by name"
87
+
88
+ attribute :chapter, AttachedChapter
89
+
90
+ sets :attached_chapters, append: { value: :chapter }
91
+
92
+ emits "ChapterAttached"
93
+ end
94
+
77
95
  # Keyword rows for File and Hecksagon; SyntaxBoot discovers every aggregate-local seed table.
78
96
  value_object "KeywordSeed" do
79
97
  attribute :word, String
@@ -91,6 +109,7 @@ Hecks.bluebook "Hecksagon" do
91
109
  member word: "subscribe", context: "Hecksagon", body: "none", inner: "", opens: "", fills: "subscriptions"
92
110
  member word: "uses_framework", context: "Hecksagon", body: "none", inner: "", opens: "", fills: "framework_members"
93
111
  member word: "uses_embryonaut_bluebook", context: "Hecksagon", body: "none", inner: "", opens: "", fills: "vendored_bluebooks"
112
+ member word: "attaches", context: "Hecksagon", body: "none", inner: "", opens: "", fills: "attached_chapters"
94
113
  member word: "port", context: "Hecksagon", body: "keywords", inner: "DomainPort", opens: "DomainPort", fills: "", calls: "port_impl"
95
114
  # A cross-domain reaction wired here rather than in the bluebook; reuses the Policy context.
96
115
  # No `calls:` — HecksagonBuilder#translates is an ordinary method, found before `word_gate`.
@@ -121,6 +140,7 @@ Hecks.bluebook "Hecksagon" do
121
140
  member keyword: "subscribe", context: "Hecksagon", at: "1", named: "", kind: "text", required: "true", fills: "subscriptions"
122
141
  member keyword: "uses_framework", context: "Hecksagon", at: "1", named: "", kind: "text", required: "true", fills: "framework_members"
123
142
  member keyword: "uses_embryonaut_bluebook", context: "Hecksagon", at: "1", named: "", kind: "text", required: "true", fills: "vendored_bluebooks"
143
+ member keyword: "attaches", context: "Hecksagon", at: "1", named: "", kind: "text", required: "true", fills: "attached_chapters"
124
144
  member keyword: "port", context: "Hecksagon", at: "1", named: "", kind: "text", required: "true", fills: "name"
125
145
  member keyword: "translates", context: "Hecksagon", at: "1", named: "", kind: "text", required: "true", fills: "name"
126
146
 
@@ -1,8 +1,8 @@
1
1
  require "socket"
2
2
 
3
3
  module Hecks
4
- # Startup gate for the stdio MCP servers in `bin/`: refuses network sockets, extra flags and
5
- # unknown `HECKS_MCP_*` variables. It is not authentication (ADR 0062).
4
+ # Startup gate for the stdio MCP servers `hecks mcp` runs: refuses network sockets, extra
5
+ # flags and unknown `HECKS_MCP_*` variables. It is not authentication (ADR 0062).
6
6
  module McpStdioGuard
7
7
  ACCEPTED_ARGS = %w[--stdio].freeze
8
8
  ENV_PREFIX = "HECKS_MCP_".freeze
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "json"
5
+ require "open3"
6
+ require "tmpdir"
7
+
8
+ module Hecks
9
+ module PersistenceLegacyFixture
10
+ # Regenerates the persistence legacy fixtures by writing the seed records through the real
11
+ # adapters: Heki, SQLite, D1, Postgres and PostgresEra. It needs a reachable Postgres (which
12
+ # scratch databases are made in) and the `sqlite3` program (which dumps the SQLite database).
13
+ #
14
+ # The fixtures are the baseline later adapters are compared with, so a caller confirms before
15
+ # this is run, and it is not re-run once adapters write through the state codec: it would
16
+ # overwrite the baseline.
17
+ class Regenerate
18
+ # The scratch database each Postgres-backed store is written to.
19
+ SCRATCH = {
20
+ postgres: "hecks_persistence_legacy_postgres",
21
+ postgres_era: "hecks_persistence_legacy_postgres_era"
22
+ }.freeze
23
+
24
+ # Raised when a fixture cannot be written.
25
+ class Failure < StandardError; end
26
+
27
+ # Rewrites every fixture set.
28
+ #
29
+ # @param dir [String] where the fixtures stand
30
+ # @return [String] `wrote <dir>`
31
+ # @raise [Failure] when the `sqlite3` dump fails
32
+ def self.call(dir: PersistenceLegacyFixture::DIR)
33
+ new(dir).call
34
+ end
35
+
36
+ # @param dir [String] where the fixtures stand
37
+ def initialize(dir)
38
+ @dir = dir
39
+ end
40
+
41
+ # @return [String] `wrote <dir>`
42
+ def call
43
+ require "hecks/ports/persistence/plugins/era"
44
+ require "pg"
45
+ instances = PersistenceLegacyFixture.instances
46
+ write_heki(instances)
47
+ write_sqlite(instances)
48
+ write_d1(instances)
49
+ with_scratch_databases do
50
+ write_postgres(instances)
51
+ write_postgres_era(instances)
52
+ end
53
+ "wrote #{@dir}"
54
+ end
55
+
56
+ private
57
+
58
+ def reset_dir(name)
59
+ dir = File.join(@dir, name)
60
+ FileUtils.rm_rf(dir)
61
+ FileUtils.mkdir_p(dir)
62
+ dir
63
+ end
64
+
65
+ def write_json(relative, value)
66
+ File.write(File.join(@dir, relative), "#{JSON.pretty_generate(value)}\n")
67
+ end
68
+
69
+ def with_scratch_databases
70
+ admin = PG.connect(dbname: "postgres")
71
+ admin.exec("SET client_min_messages = warning")
72
+ SCRATCH.each_value do |name|
73
+ admin.exec("DROP DATABASE IF EXISTS #{name} WITH (FORCE)")
74
+ admin.exec("CREATE DATABASE #{name}")
75
+ end
76
+ yield
77
+ ensure
78
+ SCRATCH.each_value { |name| admin&.exec("DROP DATABASE IF EXISTS #{name} WITH (FORCE)") }
79
+ admin&.close
80
+ end
81
+
82
+ def write_heki(instances)
83
+ out = reset_dir("heki")
84
+ Dir.mktmpdir("hecks-legacy-heki-") do |tmp|
85
+ instances.each do |instance|
86
+ Hecks::Adapters::Heki.new(aggregate: instance.aggregate, settings: { dir: "." }, root: tmp).save(instance)
87
+ %w[heki heki.journal].each do |ext|
88
+ FileUtils.cp(File.join(tmp, "#{instance.aggregate.storage_name}.#{ext}"), out)
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ def write_sqlite(instances)
95
+ out = reset_dir("sqlite")
96
+ Dir.mktmpdir("hecks-legacy-sqlite-") do |tmp|
97
+ instances.each do |instance|
98
+ Hecks::Adapters::Sqlite.new(aggregate: instance.aggregate, settings: { database: "banking.sqlite3" },
99
+ root: tmp).save(instance)
100
+ end
101
+ dump, status = Open3.capture2("sqlite3", File.join(tmp, "banking.sqlite3"), ".dump")
102
+ raise Failure, "sqlite3 .dump failed" unless status.success?
103
+
104
+ File.write(File.join(out, "banking.sql"), dump)
105
+ end
106
+ end
107
+
108
+ def write_d1(instances)
109
+ reset_dir("d1")
110
+ connection = PersistenceLegacyFixture.fake_d1_connection
111
+ tables = instances.flat_map do |instance|
112
+ PersistenceLegacyFixture.with_d1_connection(connection) do
113
+ Hecks::Adapters::D1.new(aggregate: instance.aggregate,
114
+ settings: { account_id: "acc", database_id: "db", api_token: "tok" }).save(instance)
115
+ end
116
+ [instance.aggregate.storage_name, "#{instance.aggregate.storage_name}_entries"]
117
+ end
118
+ write_json("d1/rows.json",
119
+ tables.to_h { |table| [table, connection.execute(%(SELECT * FROM "#{table}" ORDER BY rowid))] })
120
+ end
121
+
122
+ def write_postgres(instances)
123
+ reset_dir("postgres")
124
+ rows = instances.to_h do |instance|
125
+ adapter = Hecks::Adapters::Postgres.new(aggregate: instance.aggregate, settings: { database: SCRATCH[:postgres] })
126
+ adapter.save(instance)
127
+ db = adapter.instance_variable_get(:@db)
128
+ table = PG::Connection.quote_ident(instance.aggregate.storage_name)
129
+ entries = PG::Connection.quote_ident("#{instance.aggregate.storage_name}_entries")
130
+ [instance.aggregate.storage_name, {
131
+ "head" => db.exec("SELECT * FROM #{table} ORDER BY id").to_a,
132
+ "entries" => db.exec("SELECT aggregate_id, operation, state, mirrors FROM #{entries} ORDER BY sequence").to_a
133
+ }]
134
+ end
135
+ write_json("postgres/rows.json", rows)
136
+ end
137
+
138
+ def write_postgres_era(instances)
139
+ reset_dir("postgres_era")
140
+ rows = instances.to_h { |instance| [instance.aggregate.storage_name, era_rows(instance)] }
141
+ write_json("postgres_era/rows.json", rows)
142
+ end
143
+
144
+ def era_rows(instance)
145
+ adapter = Hecks::Adapters::PostgresEra.new(aggregate: instance.aggregate,
146
+ settings: { database: SCRATCH[:postgres_era] })
147
+ adapter.save(instance)
148
+ db = adapter.instance_variable_get(:@db)
149
+ journal = adapter.instance_variable_get(:@lineage).quoted_journal
150
+ snapshot = adapter.send(:quoted_head_snapshot)
151
+ {
152
+ "journal" => db.exec_params(
153
+ "SELECT ordinal, era, aggregate, aggregate_id, operation, state, mirrors FROM #{journal} " \
154
+ "WHERE aggregate = $1 ORDER BY ordinal", [instance.aggregate.storage_name]
155
+ ).to_a,
156
+ "head_snapshot" => db.exec("SELECT id, ordinal, operation, state FROM #{snapshot} ORDER BY id").to_a
157
+ }
158
+ end
159
+ end
160
+ end
161
+ end