hecks 2.8.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 (886) hide show
  1. checksums.yaml +4 -4
  2. data/exe/hecks +50 -7
  3. data/lib/hecks/adapters/driven/claude_code.rb +4 -104
  4. data/lib/hecks/adapters/driven/d1.rb +44 -237
  5. data/lib/hecks/adapters/driven/folder.rb +17 -76
  6. data/lib/hecks/adapters/driven/google_authentication.rb +15 -49
  7. data/lib/hecks/adapters/driven/governance_authorization.rb +23 -70
  8. data/lib/hecks/adapters/driven/heki/journal.rb +8 -41
  9. data/lib/hecks/adapters/driven/heki/saga_store.rb +12 -54
  10. data/lib/hecks/adapters/driven/heki/snapshot.rb +4 -12
  11. data/lib/hecks/adapters/driven/heki.rb +13 -42
  12. data/lib/hecks/adapters/driven/identity_registry.rb +3 -14
  13. data/lib/hecks/adapters/driven/in_memory_ordering.rb +9 -26
  14. data/lib/hecks/adapters/driven/in_process_key_vault.rb +2 -14
  15. data/lib/hecks/adapters/driven/lambda/client.rb +10 -51
  16. data/lib/hecks/adapters/driven/lambda.rb +10 -84
  17. data/lib/hecks/adapters/driven/local_storage.rb +5 -40
  18. data/lib/hecks/adapters/driven/memory.rb +32 -100
  19. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +5 -21
  20. data/lib/hecks/adapters/driven/postgres/codec.rb +6 -28
  21. data/lib/hecks/adapters/driven/postgres/outbox.rb +3 -12
  22. data/lib/hecks/adapters/driven/postgres/reconnect.rb +10 -32
  23. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +34 -108
  24. data/lib/hecks/adapters/driven/postgres/shared_connection.rb +180 -0
  25. data/lib/hecks/adapters/driven/postgres.rb +149 -136
  26. data/lib/hecks/adapters/driven/postgres_era.adapter +4 -15
  27. data/lib/hecks/adapters/driven/prism.rb +48 -38
  28. data/lib/hecks/adapters/driven/sql_query_builder.rb +21 -121
  29. data/lib/hecks/adapters/driven/sqlite/codec.rb +8 -40
  30. data/lib/hecks/adapters/driven/sqlite/projection.rb +26 -82
  31. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +40 -84
  32. data/lib/hecks/adapters/driven/sqlite.rb +128 -65
  33. data/lib/hecks/adapters/driven/tenant_provisioner.rb +76 -62
  34. data/lib/hecks/adapters/driven.rb +5 -36
  35. data/lib/hecks/adapters/driving/github_webhook.rb +8 -76
  36. data/lib/hecks/behaviors/dsl.rb +7 -25
  37. data/lib/hecks/behaviors/expectations.rb +32 -142
  38. data/lib/hecks/behaviors/ir.rb +7 -20
  39. data/lib/hecks/behaviors/rspec.rb +6 -17
  40. data/lib/hecks/behaviors/runner.rb +10 -34
  41. data/lib/hecks/behaviors.rb +4 -8
  42. data/lib/hecks/bench/cli.rb +62 -0
  43. data/lib/hecks/bench/environment.rb +71 -0
  44. data/lib/hecks/bench/postgres_probe.rb +30 -0
  45. data/lib/hecks/bench/report.rb +76 -0
  46. data/lib/hecks/bench/ruby_runner.rb +93 -0
  47. data/lib/hecks/bench/run.rb +55 -0
  48. data/lib/hecks/bench/rust_runner.rb +101 -0
  49. data/lib/hecks/bench/stats.rb +46 -0
  50. data/lib/hecks/bench/suite.rb +98 -0
  51. data/lib/hecks/bench/workload.rb +89 -0
  52. data/lib/hecks/bench.rb +17 -0
  53. data/lib/hecks/bluebook/aggregate.rb +9 -75
  54. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +9 -38
  55. data/lib/hecks/bluebook/assembly/build.rb +4 -15
  56. data/lib/hecks/bluebook/assembly/contract.rb +15 -136
  57. data/lib/hecks/bluebook/assembly/contracts.rb +56 -174
  58. data/lib/hecks/bluebook/assembly/marks.rb +31 -223
  59. data/lib/hecks/bluebook/assembly/specializer.rb +6 -52
  60. data/lib/hecks/bluebook/assembly.rb +6 -45
  61. data/lib/hecks/bluebook/attribute.rb +12 -61
  62. data/lib/hecks/bluebook/behaviour/aggregate.rb +39 -39
  63. data/lib/hecks/bluebook/behaviour/attribute.rb +3 -14
  64. data/lib/hecks/bluebook/behaviour/chapter.rb +14 -42
  65. data/lib/hecks/bluebook/behaviour/command.rb +14 -66
  66. data/lib/hecks/bluebook/behaviour/domain_port.rb +11 -20
  67. data/lib/hecks/bluebook/behaviour/entity.rb +7 -27
  68. data/lib/hecks/bluebook/behaviour/hexagon.rb +23 -41
  69. data/lib/hecks/bluebook/behaviour/lifecycle.rb +15 -40
  70. data/lib/hecks/bluebook/behaviour/policy.rb +23 -52
  71. data/lib/hecks/bluebook/behaviour/process_manager.rb +30 -57
  72. data/lib/hecks/bluebook/behaviour/read_model.rb +25 -31
  73. data/lib/hecks/bluebook/behaviour/traits.rb +27 -64
  74. data/lib/hecks/bluebook/behaviour/value_object.rb +5 -24
  75. data/lib/hecks/bluebook/capabilities.rb +17 -49
  76. data/lib/hecks/bluebook/chapter.rb +16 -50
  77. data/lib/hecks/bluebook/command.rb +23 -81
  78. data/lib/hecks/bluebook/domain_port.rb +49 -67
  79. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +50 -229
  80. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +50 -377
  81. data/lib/hecks/bluebook/dsl/attribute_collector.rb +33 -204
  82. data/lib/hecks/bluebook/dsl/binding_proxy.rb +16 -69
  83. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +96 -708
  84. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +63 -169
  85. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +3 -1
  86. data/lib/hecks/bluebook/dsl/command_builder.rb +74 -546
  87. data/lib/hecks/bluebook/dsl/const_shim.rb +8 -41
  88. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +42 -89
  89. data/lib/hecks/bluebook/dsl/entity_builder.rb +44 -330
  90. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +20 -269
  91. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +148 -197
  92. data/lib/hecks/bluebook/dsl/identity_declaration.rb +13 -88
  93. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +6 -20
  94. data/lib/hecks/bluebook/dsl/policy_builder.rb +16 -88
  95. data/lib/hecks/bluebook/dsl/port_builder.rb +2 -6
  96. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +22 -96
  97. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +31 -184
  98. data/lib/hecks/bluebook/dsl/query_builder.rb +38 -43
  99. data/lib/hecks/bluebook/dsl/read_model_builder.rb +75 -201
  100. data/lib/hecks/bluebook/dsl/rule_reference.rb +22 -163
  101. data/lib/hecks/bluebook/dsl/translation_builder.rb +28 -114
  102. data/lib/hecks/bluebook/dsl/value_object_builder.rb +10 -124
  103. data/lib/hecks/bluebook/dsl/word_gate.rb +16 -162
  104. data/lib/hecks/bluebook/dsl/world_builder.rb +46 -111
  105. data/lib/hecks/bluebook/entity.rb +13 -71
  106. data/lib/hecks/bluebook/expression/ast_json.rb +24 -190
  107. data/lib/hecks/bluebook/expression/ast_reader.rb +12 -54
  108. data/lib/hecks/bluebook/expression/canonical_form.rb +11 -88
  109. data/lib/hecks/bluebook/expression/evaluator.rb +47 -295
  110. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +30 -147
  111. data/lib/hecks/bluebook/expression/resolver.rb +39 -661
  112. data/lib/hecks/bluebook/hexagon.rb +18 -33
  113. data/lib/hecks/bluebook/lifecycle.rb +5 -16
  114. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -7
  115. data/lib/hecks/bluebook/meta_validator/judge.rb +111 -361
  116. data/lib/hecks/bluebook/meta_validator/plan.rb +33 -162
  117. data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -7
  118. data/lib/hecks/bluebook/meta_validator/readings.rb +56 -342
  119. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +25 -176
  120. data/lib/hecks/bluebook/meta_validator/shapes.rb +53 -267
  121. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +47 -306
  122. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +7 -43
  123. data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +246 -0
  124. data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -18
  125. data/lib/hecks/bluebook/meta_validator.rb +87 -352
  126. data/lib/hecks/bluebook/model_check/client_profile.rb +6 -21
  127. data/lib/hecks/bluebook/model_check.rb +122 -596
  128. data/lib/hecks/bluebook/pattern_subset.rb +49 -69
  129. data/lib/hecks/bluebook/process_manager.rb +13 -83
  130. data/lib/hecks/bluebook/project_loader.rb +3 -39
  131. data/lib/hecks/bluebook/project_register.rb +12 -60
  132. data/lib/hecks/bluebook/query.rb +27 -51
  133. data/lib/hecks/bluebook/read_model.rb +57 -81
  134. data/lib/hecks/bluebook/reference.rb +7 -41
  135. data/lib/hecks/bluebook/smoke_test.rb +9 -99
  136. data/lib/hecks/bluebook/synthesizer.rb +13 -62
  137. data/lib/hecks/bluebook/translation.rb +26 -62
  138. data/lib/hecks/bluebook/value_object.rb +11 -36
  139. data/lib/hecks/bluebook.rb +5 -22
  140. data/lib/hecks/cache_dir.rb +4 -23
  141. data/lib/hecks/canonical_json.rb +30 -0
  142. data/lib/hecks/chapters.rb +102 -0
  143. data/lib/hecks/cli/behaviors.rb +61 -0
  144. data/lib/hecks/cli/canonicalise.rb +25 -0
  145. data/lib/hecks/cli/check_engine_agreement.rb +32 -0
  146. data/lib/hecks/cli/check_era.rb +88 -0
  147. data/lib/hecks/cli/console.rb +66 -0
  148. data/lib/hecks/cli/deploy_template_diff.rb +40 -0
  149. data/lib/hecks/cli/doc_coverage.rb +32 -0
  150. data/lib/hecks/cli/document.rb +5 -23
  151. data/lib/hecks/cli/expression_projection.rb +41 -0
  152. data/lib/hecks/cli/follow.rb +88 -0
  153. data/lib/hecks/cli/history.rb +44 -0
  154. data/lib/hecks/cli/ir.rb +4 -6
  155. data/lib/hecks/cli/mcp.rb +5 -6
  156. data/lib/hecks/cli/model_check.rb +24 -88
  157. data/lib/hecks/cli/pattern_cases.rb +47 -0
  158. data/lib/hecks/cli/present.rb +39 -0
  159. data/lib/hecks/cli/project_cli.rb +223 -64
  160. data/lib/hecks/cli/project_diagrams.rb +4 -10
  161. data/lib/hecks/cli/project_glossary.rb +44 -0
  162. data/lib/hecks/cli/reference.rb +46 -0
  163. data/lib/hecks/cli/refresh_projections.rb +44 -0
  164. data/lib/hecks/cli/refresh_rspec_runtime_baseline.rb +150 -0
  165. data/lib/hecks/cli/release.rb +70 -0
  166. data/lib/hecks/cli/release_gem.rb +59 -0
  167. data/lib/hecks/cli/rspec_io_parallel_files.rb +137 -0
  168. data/lib/hecks/cli/rspec_shard_files.rb +66 -0
  169. data/lib/hecks/cli/run.rb +15 -61
  170. data/lib/hecks/cli/seed_semantics_corpus.rb +71 -0
  171. data/lib/hecks/cli/serve_query_ir_mcp.rb +30 -0
  172. data/lib/hecks/cli/shape.rb +70 -0
  173. data/lib/hecks/cli/smoke_http.rb +230 -0
  174. data/lib/hecks/cli/smoke_test.rb +7 -19
  175. data/lib/hecks/cli/spec_example.rb +27 -0
  176. data/lib/hecks/cli/statements.rb +32 -0
  177. data/lib/hecks/cli/stores.rb +4 -6
  178. data/lib/hecks/cli/stress_concurrency_specs.rb +177 -0
  179. data/lib/hecks/cli.rb +216 -73
  180. data/lib/hecks/codemod.rb +324 -0
  181. data/lib/hecks/construct.rb +8 -34
  182. data/lib/hecks/corpus.rb +423 -0
  183. data/lib/hecks/deploy/bluebook/deploy.bluebook +617 -158
  184. data/lib/hecks/deploy/bluebook/deploy.hecksagon +41 -11
  185. data/lib/hecks/deploy/oidc.json +82 -2
  186. data/lib/hecks/doc/reference.rb +351 -0
  187. data/lib/hecks/doors/cli_door.rb +196 -0
  188. data/lib/hecks/doors/cli_runner.rb +297 -0
  189. data/lib/hecks/{facade → doors}/command_request.rb +14 -34
  190. data/lib/hecks/doors/handle.rb +206 -0
  191. data/lib/hecks/doors/json_door.rb +132 -0
  192. data/lib/hecks/doors/launcher_options.rb +129 -0
  193. data/lib/hecks/{cli → doors}/mcp_door.rb +25 -120
  194. data/lib/hecks/doors/mcp_door_scope.rb +97 -0
  195. data/lib/hecks/doors/ruby_door/aggregate_door.rb +162 -0
  196. data/lib/hecks/doors/ruby_door/chapter.rb +72 -0
  197. data/lib/hecks/doors/ruby_door.rb +77 -0
  198. data/lib/hecks/doors.rb +61 -0
  199. data/lib/hecks/embryonaut_bluebook/lock.rb +3 -35
  200. data/lib/hecks/embryonaut_bluebook/shape.rb +1 -17
  201. data/lib/hecks/embryonaut_bluebook/vendor.rb +8 -53
  202. data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +4 -19
  203. data/lib/hecks/embryonaut_bluebook.rb +5 -72
  204. data/lib/hecks/engine_agreement.rb +156 -0
  205. data/lib/hecks/facade.rb +23 -51
  206. data/lib/hecks/forms/app.rb +21 -76
  207. data/lib/hecks/forms/banking_presentation.rb +50 -0
  208. data/lib/hecks/forms/command_form_renderer.rb +10 -29
  209. data/lib/hecks/forms/field_renderer.rb +17 -156
  210. data/lib/hecks/forms/field_shape.rb +31 -222
  211. data/lib/hecks/forms/html.rb +12 -47
  212. data/lib/hecks/forms/index_renderer.rb +1 -4
  213. data/lib/hecks/forms/page.rb +3 -9
  214. data/lib/hecks/forms/params.rb +14 -60
  215. data/lib/hecks/forms/port_argument.rb +2 -19
  216. data/lib/hecks/forms/query_form_renderer.rb +4 -79
  217. data/lib/hecks/forms/record_renderer.rb +8 -75
  218. data/lib/hecks/forms/record_table.rb +7 -50
  219. data/lib/hecks/forms/reference_options.rb +3 -30
  220. data/lib/hecks/forms/value_object_shape.rb +8 -35
  221. data/lib/hecks/forms.rb +5 -33
  222. data/lib/hecks/fqn.rb +5 -12
  223. data/lib/hecks/framework/bluebook/compliance.bluebook +16 -65
  224. data/lib/hecks/framework/bluebook/console_settings.bluebook +47 -134
  225. data/lib/hecks/framework/bluebook/framework.hecksagon +5 -14
  226. data/lib/hecks/framework/bluebook/governance.bluebook +13 -34
  227. data/lib/hecks/framework/bluebook/identity.bluebook +11 -26
  228. data/lib/hecks/framework/bluebook/privacy.bluebook +8 -33
  229. data/lib/hecks/framework.rb +11 -80
  230. data/lib/hecks/freezer.rb +5 -39
  231. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +393 -0
  232. data/lib/hecks/fuzzing/combination_miner.rb +86 -0
  233. data/lib/hecks/fuzzing/concurrent_dispatch.rb +214 -0
  234. data/lib/hecks/fuzzing/coverage_campaign.rb +143 -0
  235. data/lib/hecks/fuzzing/differential.rb +156 -0
  236. data/lib/hecks/fuzzing/domain_generator.rb +615 -0
  237. data/lib/hecks/fuzzing/era_boundary.rb +71 -0
  238. data/lib/hecks/fuzzing/form_census.rb +136 -0
  239. data/lib/hecks/fuzzing/generated_domain_check.rb +93 -0
  240. data/lib/hecks/fuzzing/invalid_value_generator.rb +64 -0
  241. data/lib/hecks/fuzzing/isolated_boot.rb +301 -0
  242. data/lib/hecks/fuzzing/nondeterministic.rb +51 -0
  243. data/lib/hecks/fuzzing/outside_world.rb +45 -0
  244. data/lib/hecks/fuzzing/persistence_parity.rb +102 -0
  245. data/lib/hecks/fuzzing/properties/corrections.rb +88 -0
  246. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +314 -0
  247. data/lib/hecks/fuzzing/properties/guards.rb +190 -0
  248. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +300 -0
  249. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +93 -0
  250. data/lib/hecks/fuzzing/properties/outbox.rb +89 -0
  251. data/lib/hecks/fuzzing/properties/querying.rb +175 -0
  252. data/lib/hecks/fuzzing/properties.rb +162 -0
  253. data/lib/hecks/fuzzing/qa_settings.rb +123 -0
  254. data/lib/hecks/fuzzing/replay.rb +505 -0
  255. data/lib/hecks/fuzzing/rotation_priority.rb +48 -0
  256. data/lib/hecks/fuzzing/rust_gap_manifest.rb +108 -0
  257. data/lib/hecks/fuzzing/self_consistency.rb +339 -0
  258. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +385 -0
  259. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +132 -0
  260. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +95 -0
  261. data/lib/hecks/fuzzing/sequence_generator/picker.rb +77 -0
  262. data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +109 -0
  263. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +237 -0
  264. data/lib/hecks/fuzzing/sequence_generator.rb +189 -0
  265. data/lib/hecks/fuzzing/shrinker.rb +166 -0
  266. data/lib/hecks/fuzzing/structural_skips.rb +33 -0
  267. data/lib/hecks/fuzzing/sweep_depth.rb +43 -0
  268. data/lib/hecks/fuzzing/target_capabilities.rb +163 -0
  269. data/lib/hecks/fuzzing/value_generator.rb +216 -0
  270. data/lib/hecks/fuzzing.rb +23 -0
  271. data/lib/hecks/grammar/evolve.rb +336 -0
  272. data/lib/hecks/grammar/expression.bluebook +15 -40
  273. data/lib/hecks/grammar/translation.bluebook +3 -6
  274. data/lib/hecks/grammar.rb +16 -41
  275. data/lib/hecks/hecks/adapters/codebase/codemods.rb +45 -0
  276. data/lib/hecks/hecks/adapters/codebase/conformance.rb +71 -0
  277. data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +202 -0
  278. data/lib/hecks/hecks/adapters/codebase/gem_registry.rb +80 -0
  279. data/lib/hecks/hecks/adapters/codebase/kernel_tables.rb +54 -0
  280. data/lib/hecks/hecks/adapters/codebase/language.rb +240 -0
  281. data/lib/hecks/hecks/adapters/codebase/npm_registry.rb +102 -0
  282. data/lib/hecks/hecks/adapters/codebase/publishing.rb +142 -0
  283. data/lib/hecks/hecks/adapters/codebase/regeneration.rb +67 -0
  284. data/lib/hecks/hecks/adapters/codebase/release_facts.rb +90 -0
  285. data/lib/hecks/hecks/adapters/codebase/ruby_child.rb +83 -0
  286. data/lib/hecks/hecks/adapters/codebase/secret_vault.rb +36 -0
  287. data/lib/hecks/hecks/adapters/codebase/source_tree.adapter +7 -0
  288. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +189 -0
  289. data/lib/hecks/hecks/adapters/codebase/sqlite_fixture.rb +71 -0
  290. data/lib/hecks/hecks/adapters/codebase/style.rb +171 -0
  291. data/lib/hecks/hecks/adapters/codebase/test_runner.rb +55 -0
  292. data/lib/hecks/hecks/adapters/codebase/test_suite.rb +165 -0
  293. data/lib/hecks/hecks/adapters/codebase/tree.rb +111 -0
  294. data/lib/hecks/hecks/adapters/console_capture.rb +79 -0
  295. data/lib/hecks/hecks/adapters/deploy_toolchain.adapter +7 -0
  296. data/lib/hecks/hecks/adapters/deploy_toolchain.rb +108 -0
  297. data/lib/hecks/hecks/adapters/git.adapter +7 -0
  298. data/lib/hecks/hecks/adapters/git.rb +102 -0
  299. data/lib/hecks/hecks/adapters/host_http.adapter +7 -0
  300. data/lib/hecks/hecks/adapters/host_http.rb +65 -0
  301. data/lib/hecks/hecks/adapters/in_process_boot.adapter +7 -0
  302. data/lib/hecks/hecks/adapters/in_process_boot.rb +197 -0
  303. data/lib/hecks/hecks/adapters/in_process_operations.rb +202 -0
  304. data/lib/hecks/hecks/adapters/journal_store/changes.rb +143 -0
  305. data/lib/hecks/hecks/adapters/journal_store/compaction.rb +145 -0
  306. data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +132 -0
  307. data/lib/hecks/hecks/adapters/journal_store/examination.rb +135 -0
  308. data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +163 -0
  309. data/lib/hecks/hecks/adapters/journal_store/readings.rb +187 -0
  310. data/lib/hecks/hecks/adapters/journal_store.adapter +7 -0
  311. data/lib/hecks/hecks/adapters/journal_store.rb +64 -0
  312. data/lib/hecks/hecks/adapters/local_files.adapter +7 -0
  313. data/lib/hecks/hecks/adapters/local_files.rb +46 -0
  314. data/lib/hecks/hecks/adapters/pg_admin.adapter +7 -0
  315. data/lib/hecks/hecks/adapters/pg_admin.rb +245 -0
  316. data/lib/hecks/hecks/adapters/process_pool.adapter +7 -0
  317. data/lib/hecks/hecks/adapters/process_pool.rb +119 -0
  318. data/lib/hecks/hecks/adapters/rust_toolchain.adapter +7 -0
  319. data/lib/hecks/hecks/adapters/rust_toolchain.rb +179 -0
  320. data/lib/hecks/hecks/adapters/rust_workspace.rb +150 -0
  321. data/lib/hecks/hecks/adapters/shell.adapter +7 -0
  322. data/lib/hecks/hecks/adapters/shell.rb +49 -0
  323. data/lib/hecks/hecks/adapters/terminal.adapter +7 -0
  324. data/lib/hecks/hecks/adapters/terminal.rb +74 -0
  325. data/lib/hecks/hecks/codebase.behaviors +172 -0
  326. data/lib/hecks/hecks/codebase.bluebook +2453 -0
  327. data/lib/hecks/hecks/context_map.hecksagon +13 -0
  328. data/lib/hecks/hecks/custodian.behaviors +179 -0
  329. data/lib/hecks/hecks/custodian.bluebook +1847 -0
  330. data/lib/hecks/hecks/deploy.behaviors +74 -0
  331. data/lib/hecks/hecks/deploy.world +12 -0
  332. data/lib/hecks/hecks/environments/memory.world +10 -0
  333. data/lib/hecks/hecks/governance.world +7 -0
  334. data/lib/hecks/hecks/hecks.behaviors +62 -0
  335. data/lib/hecks/hecks/hecks.bluebook +124 -0
  336. data/lib/hecks/hecks/hecks.hecksagon +390 -0
  337. data/lib/hecks/hecks/hecks.world +42 -0
  338. data/lib/hecks/ir.rb +14 -83
  339. data/lib/hecks/kernel_capabilities.rb +194 -0
  340. data/lib/hecks/language/adapter.bluebook +2 -7
  341. data/lib/hecks/language/bluebook/aggregate.bluebook +56 -221
  342. data/lib/hecks/language/bluebook/attaches/paging.bluebook +6 -15
  343. data/lib/hecks/language/bluebook/bluebook.bluebook +26 -83
  344. data/lib/hecks/language/bluebook/bluebook.hecksagon +3 -15
  345. data/lib/hecks/language/bluebook/command.bluebook +27 -144
  346. data/lib/hecks/language/bluebook/entity.bluebook +24 -97
  347. data/lib/hecks/language/bluebook/policy.bluebook +16 -67
  348. data/lib/hecks/language/bluebook/process_manager.bluebook +28 -151
  349. data/lib/hecks/language/bluebook/projection.bluebook +126 -60
  350. data/lib/hecks/language/bluebook/query.bluebook +19 -43
  351. data/lib/hecks/language/bluebook/shape.bluebook +23 -73
  352. data/lib/hecks/language/bluebook/syntax.bluebook +51 -262
  353. data/lib/hecks/language/bluebook/vocabulary.bluebook +83 -198
  354. data/lib/hecks/language/hecksagon/adapter_binding.bluebook +3 -10
  355. data/lib/hecks/language/hecksagon/domain_port.bluebook +14 -19
  356. data/lib/hecks/language/hecksagon/hecksagon.bluebook +26 -25
  357. data/lib/hecks/language/hecksagon/port_operation.bluebook +3 -14
  358. data/lib/hecks/language/oidc.json +35 -0
  359. data/lib/hecks/language/port.bluebook +4 -17
  360. data/lib/hecks/language/translation/translation.bluebook +3 -9
  361. data/lib/hecks/language/translation/translation_aggregate.bluebook +5 -24
  362. data/lib/hecks/language/world/wiring.bluebook +1 -3
  363. data/lib/hecks/language/world/world.bluebook +5 -7
  364. data/lib/hecks/literal.rb +8 -50
  365. data/lib/hecks/mcp_stdio_guard.rb +4 -41
  366. data/lib/hecks/naming.rb +29 -137
  367. data/lib/hecks/persistence_legacy_fixture/regenerate.rb +161 -0
  368. data/lib/hecks/persistence_legacy_fixture.rb +113 -0
  369. data/lib/hecks/ports/access_control.rb +7 -23
  370. data/lib/hecks/ports/agent/answers.rb +5 -23
  371. data/lib/hecks/ports/agent.rb +47 -172
  372. data/lib/hecks/ports/authentication.rb +12 -39
  373. data/lib/hecks/ports/authorization.rb +16 -43
  374. data/lib/hecks/ports/clock.rb +5 -45
  375. data/lib/hecks/ports/extraction.rb +7 -12
  376. data/lib/hecks/ports/identity_assignment.rb +8 -29
  377. data/lib/hecks/ports/identity_generation.rb +6 -29
  378. data/lib/hecks/ports/identity_resolution.rb +2 -10
  379. data/lib/hecks/ports/key_vault.rb +9 -45
  380. data/lib/hecks/ports/persistence/append_only.rb +61 -64
  381. data/lib/hecks/ports/persistence/binding_policy.rb +4 -14
  382. data/lib/hecks/ports/persistence/codec_boundary.rb +20 -80
  383. data/lib/hecks/ports/persistence/null_saga_store.rb +4 -20
  384. data/lib/hecks/ports/persistence/plugin.rb +13 -42
  385. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +44 -144
  386. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +15 -115
  387. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +20 -85
  388. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +6 -25
  389. data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +18 -24
  390. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +55 -181
  391. data/lib/hecks/ports/persistence/plugins/era/postgres_era/events.rb +77 -0
  392. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +12 -122
  393. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +28 -128
  394. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +73 -474
  395. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +31 -124
  396. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +45 -219
  397. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +13 -108
  398. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +19 -76
  399. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +9 -49
  400. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +6 -123
  401. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +23 -55
  402. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +14 -72
  403. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +7 -16
  404. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +43 -127
  405. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +13 -45
  406. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +131 -555
  407. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +13 -63
  408. data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +227 -0
  409. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +2 -17
  410. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +3 -14
  411. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +13 -58
  412. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -17
  413. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +9 -26
  414. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +50 -47
  415. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +18 -124
  416. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +34 -123
  417. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +5 -12
  418. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +3 -9
  419. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -14
  420. data/lib/hecks/ports/persistence/plugins/era/translation.rb +1 -0
  421. data/lib/hecks/ports/persistence/plugins/era.rb +4 -23
  422. data/lib/hecks/ports/persistence/postgres_dump/connection.rb +7 -23
  423. data/lib/hecks/ports/persistence/postgres_dump.rb +4 -35
  424. data/lib/hecks/ports/persistence/remote_runtime.rb +2 -24
  425. data/lib/hecks/ports/persistence/repository_factory.rb +8 -27
  426. data/lib/hecks/ports/persistence/state_codec.rb +47 -108
  427. data/lib/hecks/ports/persistence.rb +7 -14
  428. data/lib/hecks/ports/projection.rb +42 -84
  429. data/lib/hecks/ports/query/in_memory.rb +10 -58
  430. data/lib/hecks/ports/query/ordering.rb +11 -32
  431. data/lib/hecks/ports/query.rb +14 -37
  432. data/lib/hecks/ports.rb +2 -8
  433. data/lib/hecks/projection_files.rb +285 -0
  434. data/lib/hecks/projections/bootstrap_table.rb +12 -44
  435. data/lib/hecks/projections/deploy/fargate/assembly.rb +2 -15
  436. data/lib/hecks/projections/deploy/fargate/cdn.rb +1 -48
  437. data/lib/hecks/projections/deploy/fargate/cdn_default.rb +2 -11
  438. data/lib/hecks/projections/deploy/fargate/check.rb +5 -14
  439. data/lib/hecks/projections/deploy/fargate/containers.rb +3 -78
  440. data/lib/hecks/projections/deploy/fargate/extras.rb +3 -44
  441. data/lib/hecks/projections/deploy/fargate/monitoring.rb +0 -44
  442. data/lib/hecks/projections/deploy/fargate/settings.rb +2 -54
  443. data/lib/hecks/projections/deploy/fargate/yaml.rb +13 -58
  444. data/lib/hecks/projections/deploy/fargate.rb +40 -207
  445. data/lib/hecks/projections/deploy/lambda.rb +191 -708
  446. data/lib/hecks/projections/deploy/preview/access_yaml.rb +1 -33
  447. data/lib/hecks/projections/deploy/preview/containers.rb +6 -111
  448. data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
  449. data/lib/hecks/projections/deploy/preview/routing_yaml.rb +4 -56
  450. data/lib/hecks/projections/deploy/preview/script.rb +2 -14
  451. data/lib/hecks/projections/deploy/preview/settings.rb +3 -33
  452. data/lib/hecks/projections/deploy/preview/task_yaml.rb +2 -38
  453. data/lib/hecks/projections/deploy/preview/template.rb +3 -59
  454. data/lib/hecks/projections/deploy/preview.rb +2 -67
  455. data/lib/hecks/projections/deploy/scripts/deploy-service.sh.tmpl +1 -1
  456. data/lib/hecks/projections/deploy/scripts/expected-era.header +1 -1
  457. data/lib/hecks/projections/deploy/scripts/hosting.mk.tmpl +3 -3
  458. data/lib/hecks/projections/deploy/scripts/settings.rb +3 -25
  459. data/lib/hecks/projections/deploy/scripts/smoke-after-deploy.sh.tmpl +1 -1
  460. data/lib/hecks/projections/deploy/scripts.rb +4 -57
  461. data/lib/hecks/projections/deploy/shared.rb +48 -147
  462. data/lib/hecks/projections/deploy/smoke.rb +4 -61
  463. data/lib/hecks/projections/deploy/template_diff/comparer.rb +3 -25
  464. data/lib/hecks/projections/deploy/template_diff/loader.rb +2 -11
  465. data/lib/hecks/projections/deploy/template_diff.rb +5 -57
  466. data/lib/hecks/projections/diagrams.rb +22 -584
  467. data/lib/hecks/projections/glossary/html.rb +14 -130
  468. data/lib/hecks/projections/glossary/markdown.rb +8 -69
  469. data/lib/hecks/projections/glossary/mermaid.rb +9 -66
  470. data/lib/hecks/projections/glossary/sensitivity.rb +5 -34
  471. data/lib/hecks/projections/glossary/sentences.rb +14 -159
  472. data/lib/hecks/projections/glossary.rb +30 -222
  473. data/lib/hecks/projections/ir.rb +3 -10
  474. data/lib/hecks/projections/model/deviations.rb +16 -43
  475. data/lib/hecks/projections/model.rb +12 -51
  476. data/lib/hecks/projections/oidc.rb +12 -56
  477. data/lib/hecks/projections/parser_table.rb +13 -32
  478. data/lib/hecks/projections/reference.rb +4 -18
  479. data/lib/hecks/projections/rust_vocabulary.rb +13 -251
  480. data/lib/hecks/projections/shape.rb +3 -29
  481. data/lib/hecks/projections/statements.rb +7 -86
  482. data/lib/hecks/projections/vocabulary.rb +6 -22
  483. data/lib/hecks/projections.rb +2 -16
  484. data/lib/hecks/projector/cli_projector.rb +154 -400
  485. data/lib/hecks/projector/docs_projector.rb +10 -201
  486. data/lib/hecks/projector/exporter.rb +42 -134
  487. data/lib/hecks/projector/ir_projector.rb +2 -9
  488. data/lib/hecks/projector/narrate_projector.rb +15 -189
  489. data/lib/hecks/projector/target.rb +13 -76
  490. data/lib/hecks/projector.rb +30 -121
  491. data/lib/hecks/quality_control/adapters/agent.adapter +7 -0
  492. data/lib/hecks/quality_control/adapters/agent.rb +58 -0
  493. data/lib/hecks/quality_control/adapters/angle_tools.adapter +7 -0
  494. data/lib/hecks/quality_control/adapters/angle_tools.rb +15 -0
  495. data/lib/hecks/quality_control/adapters/clearance_recorder.rb +36 -0
  496. data/lib/hecks/quality_control/adapters/clearance_tools.adapter +7 -0
  497. data/lib/hecks/quality_control/adapters/clearance_tools.rb +17 -0
  498. data/lib/hecks/quality_control/adapters/git_pr.adapter +7 -0
  499. data/lib/hecks/quality_control/adapters/git_pr.rb +226 -0
  500. data/lib/hecks/quality_control/adapters/github_checks.adapter +9 -0
  501. data/lib/hecks/quality_control/adapters/github_checks.rb +83 -0
  502. data/lib/hecks/quality_control/adapters/github_ci_webhook.rb +56 -0
  503. data/lib/hecks/quality_control/adapters/github_issues.adapter +7 -0
  504. data/lib/hecks/quality_control/adapters/github_issues.rb +49 -0
  505. data/lib/hecks/quality_control/adapters/qa_tool.rb +59 -0
  506. data/lib/hecks/quality_control/adapters/sweep_tools.adapter +7 -0
  507. data/lib/hecks/quality_control/adapters/sweep_tools.rb +51 -0
  508. data/lib/hecks/quality_control/adapters/target_tools.adapter +7 -0
  509. data/lib/hecks/quality_control/adapters/target_tools.rb +43 -0
  510. data/lib/hecks/quality_control/cli/child.rb +45 -0
  511. data/lib/hecks/quality_control/cli/qa_concurrency_racer.rb +45 -0
  512. data/lib/hecks/quality_control/cli/qa_discover_external_domains.rb +248 -0
  513. data/lib/hecks/quality_control/cli/qa_domain_novelty.rb +163 -0
  514. data/lib/hecks/quality_control/cli/qa_generated_domains.rb +450 -0
  515. data/lib/hecks/quality_control/cli/qa_log_bug.rb +195 -0
  516. data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +220 -0
  517. data/lib/hecks/quality_control/cli/qa_open_pr.rb +305 -0
  518. data/lib/hecks/quality_control/cli/qa_postgres_migrate.rb +154 -0
  519. data/lib/hecks/quality_control/cli/qa_postgres_role.rb +159 -0
  520. data/lib/hecks/quality_control/cli/qa_pr_check.rb +232 -0
  521. data/lib/hecks/quality_control/cli/qa_seed_angles.rb +142 -0
  522. data/lib/hecks/quality_control/cli/qa_seed_targets.rb +69 -0
  523. data/lib/hecks/quality_control/cli/qa_sweep/all_mode.rb +280 -0
  524. data/lib/hecks/quality_control/cli/qa_sweep/checks.rb +329 -0
  525. data/lib/hecks/quality_control/cli/qa_sweep/dials.rb +92 -0
  526. data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +117 -0
  527. data/lib/hecks/quality_control/cli/qa_sweep/parsing.rb +175 -0
  528. data/lib/hecks/quality_control/cli/qa_sweep/release_mode.rb +60 -0
  529. data/lib/hecks/quality_control/cli/qa_sweep.rb +611 -0
  530. data/lib/hecks/quality_control/cli/qa_tick.rb +274 -0
  531. data/lib/hecks/quality_control/quality_control.bluebook +2224 -0
  532. data/lib/hecks/quality_control/quality_control.hecksagon +13 -0
  533. data/lib/hecks/quality_control/quality_control.ports.hecksagon +54 -0
  534. data/lib/hecks/query_ir.rb +346 -0
  535. data/lib/hecks/query_ir_mcp.rb +198 -0
  536. data/lib/hecks/query_specification/common/comparators.rb +5 -26
  537. data/lib/hecks/query_specification/common/comparison.rb +35 -143
  538. data/lib/hecks/query_specification/common/dsl.rb +8 -24
  539. data/lib/hecks/query_specification/common/null_policy.rb +20 -70
  540. data/lib/hecks/query_specification/common/options.rb +2 -6
  541. data/lib/hecks/query_specification/common/where_clause.rb +2 -10
  542. data/lib/hecks/query_specification/field_path.rb +37 -77
  543. data/lib/hecks/query_specification/hop_path.rb +12 -76
  544. data/lib/hecks/release/gem_pin.rb +2 -26
  545. data/lib/hecks/release/runner/ci_publisher.rb +5 -8
  546. data/lib/hecks/release/runner/clean_tree.rb +42 -0
  547. data/lib/hecks/release/runner/commands.rb +2 -4
  548. data/lib/hecks/release/runner/console.rb +6 -0
  549. data/lib/hecks/release/runner/gem_publisher.rb +15 -9
  550. data/lib/hecks/release/runner/npm_publisher.rb +9 -53
  551. data/lib/hecks/release/runner/preflight.rb +29 -20
  552. data/lib/hecks/release/runner/published.rb +8 -26
  553. data/lib/hecks/release/runner/tagger.rb +31 -23
  554. data/lib/hecks/release/runner.rb +60 -38
  555. data/lib/hecks/rendering.rb +4 -26
  556. data/lib/hecks/router/namespace_installer.rb +15 -39
  557. data/lib/hecks/router.rb +4 -8
  558. data/lib/hecks/runtime/adapter_lookup.rb +124 -0
  559. data/lib/hecks/runtime/aggregate_lock.rb +8 -33
  560. data/lib/hecks/runtime/boot_gates.rb +3 -18
  561. data/lib/hecks/runtime/caller.rb +19 -59
  562. data/lib/hecks/runtime/capability_graph.rb +5 -21
  563. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +16 -78
  564. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +27 -160
  565. data/lib/hecks/runtime/command_interpreter.rb +124 -306
  566. data/lib/hecks/runtime/command_rules/admissibility.rb +95 -366
  567. data/lib/hecks/runtime/command_rules/arithmetic.rb +66 -196
  568. data/lib/hecks/runtime/command_rules/authorization.rb +13 -45
  569. data/lib/hecks/runtime/command_rules/emission.rb +8 -21
  570. data/lib/hecks/runtime/command_rules/references.rb +23 -202
  571. data/lib/hecks/runtime/dependency_planning.rb +50 -104
  572. data/lib/hecks/runtime/dispatcher.rb +134 -243
  573. data/lib/hecks/runtime/entity_element.rb +40 -462
  574. data/lib/hecks/runtime/entity_interpreter.rb +64 -219
  575. data/lib/hecks/runtime/errors.rb +32 -115
  576. data/lib/hecks/runtime/event.rb +3 -25
  577. data/lib/hecks/runtime/identity.rb +18 -154
  578. data/lib/hecks/runtime/instance.rb +18 -150
  579. data/lib/hecks/runtime/interpreting.rb +14 -70
  580. data/lib/hecks/runtime/invocation.rb +28 -164
  581. data/lib/hecks/runtime/loader.rb +109 -194
  582. data/lib/hecks/runtime/outbox.rb +39 -262
  583. data/lib/hecks/runtime/policy_interpreter.rb +43 -202
  584. data/lib/hecks/runtime/port_operation_interpreter.rb +28 -97
  585. data/lib/hecks/runtime/query_interpreter.rb +122 -187
  586. data/lib/hecks/runtime/reaction_invocation.rb +36 -138
  587. data/lib/hecks/runtime/reaction_outcome.rb +89 -0
  588. data/lib/hecks/runtime/read_model_interpreter.rb +117 -208
  589. data/lib/hecks/runtime/rebuild_sweep.rb +8 -54
  590. data/lib/hecks/runtime/reference_hop.rb +8 -81
  591. data/lib/hecks/runtime/refusal_wording.rb +28 -87
  592. data/lib/hecks/runtime/registry/saga_persistence.rb +10 -102
  593. data/lib/hecks/runtime/registry/verification.rb +213 -227
  594. data/lib/hecks/runtime/registry/world_defaults.rb +4 -53
  595. data/lib/hecks/runtime/registry.rb +156 -247
  596. data/lib/hecks/runtime/remote_dispatcher.rb +68 -154
  597. data/lib/hecks/runtime/routing.rb +4 -15
  598. data/lib/hecks/runtime/saga_interpreter/correlation.rb +14 -61
  599. data/lib/hecks/runtime/saga_interpreter.rb +79 -263
  600. data/lib/hecks/runtime/saga_pending_dispatch.rb +2 -43
  601. data/lib/hecks/runtime/tenant_check.rb +6 -68
  602. data/lib/hecks/runtime/tenant_scope.rb +3 -28
  603. data/lib/hecks/runtime/value/admission.rb +16 -67
  604. data/lib/hecks/runtime/value/coercion.rb +162 -760
  605. data/lib/hecks/runtime/value/entity_list_coercion.rb +26 -219
  606. data/lib/hecks/runtime/value/field_checks.rb +172 -0
  607. data/lib/hecks/runtime/value.rb +15 -84
  608. data/lib/hecks/runtime.rb +35 -69
  609. data/lib/hecks/rust_build/conformance.rb +158 -0
  610. data/lib/hecks/rust_build/conformance_fuzz.rb +71 -0
  611. data/lib/hecks/rust_build/coverage.rb +218 -0
  612. data/lib/hecks/rust_build/native_build.rb +124 -0
  613. data/lib/hecks/rust_build/project_rust/root_mod.rb +120 -0
  614. data/lib/hecks/rust_build/project_rust.rb +332 -0
  615. data/lib/hecks/rust_build/wasm.rb +105 -0
  616. data/lib/hecks/rust_build/wasm_browser.rb +113 -0
  617. data/lib/hecks/rust_build.rb +144 -0
  618. data/lib/hecks/storehouse.rb +155 -582
  619. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +10 -34
  620. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +6 -23
  621. data/lib/hecks/three_zero/forms.yml +127 -0
  622. data/lib/hecks/three_zero.rb +12 -0
  623. data/lib/hecks/tools/argument_gate_matrix.rb +316 -0
  624. data/lib/hecks/tools/comment_style.rb +1070 -0
  625. data/lib/hecks/tools/corpus_report.rb +87 -0
  626. data/lib/hecks/tools/deploy_recipe.rb +255 -0
  627. data/lib/hecks/tools/deploy_recipe_lint.rb +282 -0
  628. data/lib/hecks/tools/drop_implicit_append_fields.rb +95 -0
  629. data/lib/hecks/tools/era_projection_backfill.rb +94 -0
  630. data/lib/hecks/tools/era_reattest.rb +146 -0
  631. data/lib/hecks/tools/evolve_run.rb +189 -0
  632. data/lib/hecks/tools/fuzz_sweep.rb +373 -0
  633. data/lib/hecks/tools/heki_compaction.rb +100 -0
  634. data/lib/hecks/tools/hoist_local_givens.rb +246 -0
  635. data/lib/hecks/tools/journal_compaction.rb +89 -0
  636. data/lib/hecks/tools/oidc_manifests.rb +70 -0
  637. data/lib/hecks/tools/query_ir_run.rb +81 -0
  638. data/lib/hecks/tools/regeneration_run.rb +166 -0
  639. data/lib/hecks/tools/rust_comment_style.rb +642 -0
  640. data/lib/hecks/tools/sequence_script.rb +62 -0
  641. data/lib/hecks/tools/tail_merge.rb +117 -0
  642. data/lib/hecks/tools/tenant_provisioning.rb +142 -0
  643. data/lib/hecks/tools/translation_audit.rb +208 -0
  644. data/lib/hecks/tools/translation_scaffold.rb +131 -0
  645. data/lib/hecks/tools.rb +86 -0
  646. data/lib/hecks/vendoring/git_environment.rb +2 -8
  647. data/lib/hecks/vendoring/git_source.rb +4 -10
  648. data/lib/hecks/vendoring.rb +7 -46
  649. data/lib/hecks/version.rb +3 -14
  650. data/lib/hecks.rb +52 -90
  651. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -46
  652. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +11 -80
  653. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +10 -99
  654. data/qa/settings.yml +66 -0
  655. data/rust/.cargo/config.toml +6 -0
  656. data/rust/.gitignore +2 -0
  657. data/rust/Cargo.lock +54 -0
  658. data/rust/Cargo.toml +61 -0
  659. data/rust/build/.cargo/config.toml +3 -0
  660. data/rust/build/.gitignore +1 -0
  661. data/rust/build/Cargo.lock +7 -0
  662. data/rust/build/Cargo.toml +19 -0
  663. data/rust/build/src/build_artifact.rs +47 -0
  664. data/rust/build/src/cargo_sync.rs +248 -0
  665. data/rust/build/src/json.rs +421 -0
  666. data/rust/build/src/lineage_pass.rs +285 -0
  667. data/rust/build/src/main.rs +51 -0
  668. data/rust/build/src/optional_pass.rs +150 -0
  669. data/rust/build/src/pipeline.rs +264 -0
  670. data/rust/build/src/reserved_names.rs +76 -0
  671. data/rust/build/src/resolve.rs +124 -0
  672. data/rust/build/src/root.rs +25 -0
  673. data/rust/build/src/sidecars.rs +73 -0
  674. data/rust/build/src/subprocess.rs +51 -0
  675. data/rust/build/src/tmp.rs +31 -0
  676. data/rust/codegen/.cargo/config.toml +3 -0
  677. data/rust/codegen/.gitignore +1 -0
  678. data/rust/codegen/Cargo.lock +7 -0
  679. data/rust/codegen/Cargo.toml +20 -0
  680. data/rust/codegen/src/attr.rs +32 -0
  681. data/rust/codegen/src/bridging.rs +481 -0
  682. data/rust/codegen/src/commands.rs +1226 -0
  683. data/rust/codegen/src/constraints.rs +109 -0
  684. data/rust/codegen/src/dependency_planning.rs +209 -0
  685. data/rust/codegen/src/domain_generator.rs +1288 -0
  686. data/rust/codegen/src/exemplar.rs +230 -0
  687. data/rust/codegen/src/expr_emitter.rs +87 -0
  688. data/rust/codegen/src/fielded.rs +192 -0
  689. data/rust/codegen/src/hecks_naming.rs +33 -0
  690. data/rust/codegen/src/json.rs +335 -0
  691. data/rust/codegen/src/json_codec.rs +697 -0
  692. data/rust/codegen/src/literal.rs +207 -0
  693. data/rust/codegen/src/main.rs +290 -0
  694. data/rust/codegen/src/manifest.rs +227 -0
  695. data/rust/codegen/src/mutations.rs +784 -0
  696. data/rust/codegen/src/naming.rs +286 -0
  697. data/rust/codegen/src/ports.rs +208 -0
  698. data/rust/codegen/src/prelude.rs +116 -0
  699. data/rust/codegen/src/queries.rs +786 -0
  700. data/rust/codegen/src/reactions.rs +532 -0
  701. data/rust/codegen/src/read_models.rs +697 -0
  702. data/rust/codegen/src/reference_specs.rs +40 -0
  703. data/rust/codegen/src/registry.rs +966 -0
  704. data/rust/codegen/src/reserved_names.rs +76 -0
  705. data/rust/codegen/src/shared.rs +93 -0
  706. data/rust/codegen/src/skip_reason.rs +25 -0
  707. data/rust/codegen/src/types.rs +362 -0
  708. data/rust/host/.cargo/config.toml +3 -0
  709. data/rust/host/.gitignore +1 -0
  710. data/rust/host/Cargo.lock +4297 -0
  711. data/rust/host/Cargo.toml +61 -0
  712. data/rust/host/HECKS_RELEASE +1 -0
  713. data/rust/host/rds-ca-bundle.pem +2736 -0
  714. data/rust/host/src/api.rs +1596 -0
  715. data/rust/host/src/approval.rs +652 -0
  716. data/rust/host/src/auth.rs +1287 -0
  717. data/rust/host/src/bin/expr_harness.rs +46 -0
  718. data/rust/host/src/bin/lineage_harness.rs +119 -0
  719. data/rust/host/src/bin/mint_harness.rs +135 -0
  720. data/rust/host/src/checkout.rs +265 -0
  721. data/rust/host/src/dispatch.rs +1443 -0
  722. data/rust/host/src/expr_json.rs +1117 -0
  723. data/rust/host/src/field_hints.rs +52 -0
  724. data/rust/host/src/ir.rs +733 -0
  725. data/rust/host/src/journal.rs +874 -0
  726. data/rust/host/src/lambda_client.rs +378 -0
  727. data/rust/host/src/log.rs +147 -0
  728. data/rust/host/src/main.rs +474 -0
  729. data/rust/host/src/mint.rs +1865 -0
  730. data/rust/host/src/payments/keystore.rs +330 -0
  731. data/rust/host/src/payments/tests.rs +1400 -0
  732. data/rust/host/src/payments.rs +767 -0
  733. data/rust/host/src/presentation.rs +698 -0
  734. data/rust/host/src/presentation_write.rs +1431 -0
  735. data/rust/host/src/query_step.rs +149 -0
  736. data/rust/host/src/rate_limit.rs +904 -0
  737. data/rust/host/src/reference_transform.rs +241 -0
  738. data/rust/host/src/reference_validate.rs +338 -0
  739. data/rust/host/src/resend.rs +296 -0
  740. data/rust/host/src/secrets.rs +106 -0
  741. data/rust/host/src/server.rs +663 -0
  742. data/rust/host/src/storage_shape.rs +175 -0
  743. data/rust/host/src/test_pg.rs +40 -0
  744. data/rust/host/src/ui_schema.rs +1351 -0
  745. data/rust/host/src/wasm_runner.rs +191 -0
  746. data/rust/host/src/web/newsletter/cooldown.rs +126 -0
  747. data/rust/host/src/web/newsletter/tests.rs +104 -0
  748. data/rust/host/src/web/newsletter.rs +310 -0
  749. data/rust/host/src/web/newsletter_send.rs +357 -0
  750. data/rust/host/src/web/registrations.rs +1505 -0
  751. data/rust/host/src/web.rs +2937 -0
  752. data/rust/lsp/.gitignore +1 -0
  753. data/rust/lsp/Cargo.lock +7 -0
  754. data/rust/lsp/Cargo.toml +16 -0
  755. data/rust/lsp/README.md +133 -0
  756. data/rust/lsp/src/diagnostics.rs +165 -0
  757. data/rust/lsp/src/json.rs +385 -0
  758. data/rust/lsp/src/main.rs +399 -0
  759. data/rust/lsp/src/outline.rs +177 -0
  760. data/rust/lsp/src/rpc.rs +67 -0
  761. data/rust/parser/.cargo/config.toml +5 -0
  762. data/rust/parser/.gitignore +1 -0
  763. data/rust/parser/Cargo.lock +7 -0
  764. data/rust/parser/Cargo.toml +21 -0
  765. data/rust/parser/src/build/closed_sets.rs +27 -0
  766. data/rust/parser/src/build/identity.rs +276 -0
  767. data/rust/parser/src/build/mod.rs +12 -0
  768. data/rust/parser/src/build/naming.rs +141 -0
  769. data/rust/parser/src/build/pattern_subset.rs +161 -0
  770. data/rust/parser/src/build/query_derive.rs +72 -0
  771. data/rust/parser/src/build/query_inference.rs +269 -0
  772. data/rust/parser/src/build/query_options.rs +100 -0
  773. data/rust/parser/src/build/read_model.rs +94 -0
  774. data/rust/parser/src/build/references.rs +32 -0
  775. data/rust/parser/src/canonical.rs +114 -0
  776. data/rust/parser/src/diag.rs +55 -0
  777. data/rust/parser/src/emit.rs +953 -0
  778. data/rust/parser/src/expr/ast_json.rs +147 -0
  779. data/rust/parser/src/expr/evaluator.rs +81 -0
  780. data/rust/parser/src/expr/mod.rs +84 -0
  781. data/rust/parser/src/expr/resolver.rs +497 -0
  782. data/rust/parser/src/ir.rs +387 -0
  783. data/rust/parser/src/keywords.rs +585 -0
  784. data/rust/parser/src/lex.rs +662 -0
  785. data/rust/parser/src/main.rs +252 -0
  786. data/rust/parser/src/parse/aggregate.rs +612 -0
  787. data/rust/parser/src/parse/chapter.rs +517 -0
  788. data/rust/parser/src/parse/command.rs +637 -0
  789. data/rust/parser/src/parse/domain_port.rs +127 -0
  790. data/rust/parser/src/parse/entity.rs +527 -0
  791. data/rust/parser/src/parse/file.rs +67 -0
  792. data/rust/parser/src/parse/hecksagon.rs +231 -0
  793. data/rust/parser/src/parse/lifecycle.rs +162 -0
  794. data/rust/parser/src/parse/mod.rs +1262 -0
  795. data/rust/parser/src/parse/policy.rs +87 -0
  796. data/rust/parser/src/parse/process_manager.rs +305 -0
  797. data/rust/parser/src/parse/query.rs +100 -0
  798. data/rust/parser/src/parse/read_model.rs +214 -0
  799. data/rust/parser/src/parse/value_object.rs +250 -0
  800. data/rust/parser/src/ruby_value.rs +422 -0
  801. data/rust/project/bridging.rb +257 -0
  802. data/rust/project/commands.rb +877 -0
  803. data/rust/project/constraints.rb +86 -0
  804. data/rust/project/dependency_planning.rb +178 -0
  805. data/rust/project/domain_generator.rb +980 -0
  806. data/rust/project/exemplar.rb +148 -0
  807. data/rust/project/expr_emitter.rb +72 -0
  808. data/rust/project/fielded.rb +141 -0
  809. data/rust/project/json_codec.rb +641 -0
  810. data/rust/project/mutations.rb +721 -0
  811. data/rust/project/naming.rb +205 -0
  812. data/rust/project/ports.rb +65 -0
  813. data/rust/project/queries.rb +493 -0
  814. data/rust/project/reactions.rb +317 -0
  815. data/rust/project/read_models.rb +690 -0
  816. data/rust/project/reference_specs.rb +27 -0
  817. data/rust/project/registry.rb +396 -0
  818. data/rust/project/skip_reason.rb +29 -0
  819. data/rust/project/types.rb +225 -0
  820. data/rust/project/write_if_changed.rb +83 -0
  821. data/rust/project.rb +30 -0
  822. data/rust/project_rust_pipeline.rb +383 -0
  823. data/rust/src/exemplar/commands.rs +279 -0
  824. data/rust/src/exemplar/constraints.rs +18 -0
  825. data/rust/src/exemplar/fielded.rs +280 -0
  826. data/rust/src/exemplar/json.rs +277 -0
  827. data/rust/src/exemplar/mod.rs +17 -0
  828. data/rust/src/exemplar/mutations.rs +80 -0
  829. data/rust/src/exemplar/queries.rs +33 -0
  830. data/rust/src/exemplar/reactions.rs +88 -0
  831. data/rust/src/exemplar/read_models.rs +48 -0
  832. data/rust/src/exemplar/registry.rs +166 -0
  833. data/rust/src/exemplar/types.rs +44 -0
  834. data/rust/src/kernel/attribute_shapes/composite.rs +88 -0
  835. data/rust/src/kernel/attribute_shapes/list.rs +18 -0
  836. data/rust/src/kernel/attribute_shapes/mod.rs +44 -0
  837. data/rust/src/kernel/attribute_shapes/optional.rs +9 -0
  838. data/rust/src/kernel/attribute_shapes/scalar.rs +39 -0
  839. data/rust/src/kernel/cli.rs +585 -0
  840. data/rust/src/kernel/dispatch.rs +1090 -0
  841. data/rust/src/kernel/expr.rs +458 -0
  842. data/rust/src/kernel/expression_operators/arithmetic.rs +128 -0
  843. data/rust/src/kernel/expression_operators/comparison.rs +48 -0
  844. data/rust/src/kernel/expression_operators/enumeration.rs +232 -0
  845. data/rust/src/kernel/expression_operators/logical.rs +13 -0
  846. data/rust/src/kernel/expression_operators/membership.rs +18 -0
  847. data/rust/src/kernel/expression_operators/mod.rs +68 -0
  848. data/rust/src/kernel/expression_operators/pattern_match.rs +73 -0
  849. data/rust/src/kernel/expression_operators/positional.rs +126 -0
  850. data/rust/src/kernel/expression_operators/presence.rs +101 -0
  851. data/rust/src/kernel/expression_operators/sign_test.rs +16 -0
  852. data/rust/src/kernel/expression_operators/sized.rs +35 -0
  853. data/rust/src/kernel/expression_operators/text.rs +79 -0
  854. data/rust/src/kernel/expression_operators/to_string.rs +18 -0
  855. data/rust/src/kernel/json.rs +779 -0
  856. data/rust/src/kernel/mod.rs +133 -0
  857. data/rust/src/kernel/named_query.rs +219 -0
  858. data/rust/src/kernel/orchestrate.rs +1810 -0
  859. data/rust/src/kernel/pattern.rs +307 -0
  860. data/rust/src/kernel/query_comparators.rs +206 -0
  861. data/rust/src/kernel/query_ordering.rs +103 -0
  862. data/rust/src/kernel/read_model.rs +914 -0
  863. data/rust/src/kernel/reference_lookup.rs +149 -0
  864. data/rust/src/kernel/repository.rs +468 -0
  865. data/rust/src/kernel/routing.rs +612 -0
  866. data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +118 -0
  867. data/rust/src/kernel/vocab/entity_dispatch_order.rs +114 -0
  868. data/rust/src/kernel/vocab/field_hint.rs +70 -0
  869. data/rust/src/kernel/vocab/mod.rs +20 -0
  870. data/rust/src/kernel/vocab/query_comparator.rs +65 -0
  871. data/rust/src/kernel/vocab/refusal_template.rs +1592 -0
  872. data/rust/src/lib.rs +8 -0
  873. data/rust/src/main.rs +16 -0
  874. data/rust/web/.gitignore +1 -0
  875. data/rust/web/Cargo.lock +119 -0
  876. data/rust/web/Cargo.toml +18 -0
  877. data/rust/web/src/lib.rs +8 -0
  878. metadata +478 -12
  879. data/lib/hecks/facade/cli_door.rb +0 -178
  880. data/lib/hecks/facade/cli_runner.rb +0 -271
  881. data/lib/hecks/facade/handle.rb +0 -293
  882. data/lib/hecks/facade/json_door.rb +0 -247
  883. data/lib/hecks/facade/surface/aggregate_door.rb +0 -222
  884. data/lib/hecks/facade/surface/chapter.rb +0 -116
  885. data/lib/hecks/facade/surface.rb +0 -61
  886. data/lib/hecks/mcp_door_scope.rb +0 -176
@@ -11,42 +11,13 @@ require_relative "../../runtime/errors"
11
11
  require_relative "../../runtime/event"
12
12
  require_relative "postgres/outbox"
13
13
  require_relative "postgres/reconnect"
14
+ require_relative "postgres/shared_connection"
14
15
  require_relative "../../runtime/instance"
15
16
 
16
17
  module Hecks
17
18
  module Adapters
18
- # The plain Postgres store — flat, one table per aggregate, real
19
- # typed columns for scalars and jsonb for nested/list attributes,
20
- # exactly the shape `Sqlite` already made for its own table. Sibling
21
- # to `PostgresEra` (postgres_era.rb), which is the same database with
22
- # full lineage/era machinery on top — pick this one unless a domain
23
- # actually needs to survive a live shape change. See
24
- # docs/implemented/postgres-era-adapter-split-plan.md for why the two are split.
25
- #
26
- # ## What it is not
27
- #
28
- # No `hecks_eras`, no lineage, no advisory-lock-per-write for era
29
- # tracking, no `lineage_capable?`/`era_check!` — this class simply
30
- # doesn't define those methods at all, and the capability idiom
31
- # elsewhere already treats their absence as "not lineage-capable".
32
- #
33
- # ## Storage shape
34
- #
35
- # See postgres/schema_builder.rb for the DDL, postgres/codec.rb for the
36
- # encode/decode.
37
- #
38
- # - One real column per attribute, typed for a scalar
39
- # (`SQL_TYPES`, `text` default), `jsonb` for a nested (value-object)
40
- # or list-typed attribute — never JSON-in-text the way `Sqlite` has
41
- # to, since Postgres has a native jsonb type.
42
- # - `append` and `project` are two real Postgres statements — `save`/
43
- # `delete` wrap them in one transaction, same "the journal insert
44
- # and the snapshot stay atomic" reasoning `PostgresEra#append`'s own
45
- # comment gives: a crash between the two must never leave a
46
- # half-written state. `append`/`project` stay plain, individually-
47
- # callable methods (never wrapping their own transaction) so
48
- # `AppendOnly#recover!`'s replay — `project` alone, no `append` —
49
- # keeps working the same way it does on every other adapter.
19
+ # The plain Postgres store: one table per aggregate, real typed columns
20
+ # for scalars, jsonb for nested/list attributes. See `PostgresEra` for lineage/era support.
50
21
  class Postgres
51
22
  include SqlQueryBuilder
52
23
  include SchemaBuilder
@@ -94,11 +65,8 @@ module Hecks
94
65
  PG.connect(dbname: declared)
95
66
  end
96
67
 
97
- # **Shared-instance isolation** — same as PostgresEra's own: a
98
- # domain that declares `schema` is sharing its Postgres instance
99
- # with other domains, so every unqualified reference this
100
- # adapter constructs resolves through search_path. A domain with
101
- # no `schema` setting keeps Postgres's own default (public).
68
+ # A domain with `schema` set shares this Postgres instance, so every
69
+ # unqualified reference resolves through search_path.
102
70
  schema = settings.key?(:schema) ? settings[:schema] : settings["schema"]
103
71
  connection.exec("SET search_path TO #{connection.quote_ident(schema)}") if schema.to_s != ""
104
72
 
@@ -112,7 +80,8 @@ module Hecks
112
80
  "cannot bind Postgres at #{declared} for #{name}: #{e.message.strip}"
113
81
  end
114
82
 
115
- # Connects and creates the aggregate, journal, event, saga and outbox tables if absent.
83
+ # Joins the process's shared connection for the declared database and schema, then
84
+ # creates the aggregate, journal, event, saga and outbox tables if absent.
116
85
  #
117
86
  # @param aggregate [Bluebook::Aggregate] the aggregate whose table this adapter owns
118
87
  # @param settings [Hash{Symbol, String => Object}] world settings for the binding:
@@ -126,11 +95,9 @@ module Hecks
126
95
  def initialize(aggregate:, settings: {}, root: nil)
127
96
  @aggregate = aggregate
128
97
  @settings = settings
129
- @db = self.class.connect_for(aggregate.name, settings)
130
- # The optional saga-persistence capability's own scoping column
131
- # (§2/§4) — falls back to the aggregate's own storage name for a
132
- # directly-instantiated adapter (specs), same fallback shape
133
- # Sqlite's own @domain already uses.
98
+ @db = PostgresSharedConnection.for(aggregate.name, settings, connector: self.class)
99
+ # Scopes saga rows; falls back to the aggregate's storage name when
100
+ # settings gives no domain (e.g. a directly-instantiated adapter).
134
101
  @domain = (
135
102
  if settings.key?(:domain)
136
103
  settings[:domain]
@@ -146,6 +113,7 @@ module Hecks
146
113
  create_event_table!
147
114
  create_saga_table!
148
115
  create_outbox_table!
116
+ create_checkpoint_table!
149
117
  end
150
118
 
151
119
  # Names the aggregate's table; the journal table and outbox rows are keyed off it.
@@ -169,9 +137,7 @@ module Hecks
169
137
 
170
138
  # Lists every stored record, ordered by id unless an ordering attribute is given.
171
139
  #
172
- # order_by is a runtime value — see Sqlite#all's own reasoning;
173
- # whitelisted the identical way before it ever reaches
174
- # order_expression.
140
+ # order_by is a runtime value, whitelisted the same way Sqlite#all does.
175
141
  #
176
142
  # @param order_by [String, Symbol, nil] attribute (or dotted value-object path) to sort
177
143
  # by, with id as the tie-break; nil orders by id alone
@@ -205,54 +171,39 @@ module Hecks
205
171
 
206
172
  # Inserts one journal row, outside any transaction of its own.
207
173
  #
174
+ # Stamps the row's assigned `sequence` onto `entry` in place, so a `project` call right
175
+ # after (same object, same transaction) can advance the checkpoint with no extra query.
176
+ #
208
177
  # @param entry [Ports::Persistence::Entry] the save or delete to journal; `state` is
209
178
  # encoded through the state codec and `mirrors` stored as JSON, or NULL when nil
210
- # @return [Ports::Persistence::Entry] the same `entry`
179
+ # @return [Ports::Persistence::Entry] the same `entry`, `sequence` now set
211
180
  # @raise [PG::Error] if the insert fails
212
181
  def append(entry)
213
- pg_exec_params(
214
- "INSERT INTO #{quoted_entry_table} (aggregate_id, operation, state, mirrors) VALUES ($1, $2, $3, $4)",
215
- # `mirrors` (unlike `state`) is a nullable column — an absent
216
- # mirrors hash must bind a real SQL NULL, not the four-character
217
- # JSON text `"null"` (`JSON.generate(nil)`), or a future `IS NULL`
218
- # check against it would never match. Same guard `sqlite.rb`/
219
- # `d1.rb`/`postgres_era.rb` already use for their own journal's
220
- # `mirrors` column.
182
+ result = pg_exec_params(
183
+ "INSERT INTO #{quoted_entry_table} (aggregate_id, operation, state, mirrors) VALUES ($1, $2, $3, $4) " \
184
+ "RETURNING sequence",
185
+ # `mirrors` is nullable; an absent hash must bind SQL NULL, not the
186
+ # string "null", or a future IS NULL check would never match.
221
187
  [entry.id, entry.operation, state_json(entry.state), entry.mirrors && JSON.generate(entry.mirrors)]
222
188
  )
189
+ entry.sequence = result[0]["sequence"].to_i
223
190
  entry
224
191
  end
225
192
 
226
193
  # Upserts or deletes the aggregate's row for one journal entry, bumping its version.
227
194
  #
228
- # `expected_version:` requests optimistic-concurrency CAS (see
229
- # `persistence_capabilities`/`Ports::Persistence::AppendOnly#save`).
230
- # `hecks_version` is adapter bookkeeping — never in `persisted_fields`
231
- # (Codec), so it never reaches `decode`'s domain-state hash. It goes
232
- # in the INSERT column list at `1` (a genuinely new row) and bumps by
233
- # one in the `ON CONFLICT DO UPDATE` branch; when `expected_version`
234
- # is given, that update branch additionally requires
235
- # `hecks_version = expected_version` to apply at all — Postgres's own
236
- # `INSERT ... ON CONFLICT DO UPDATE ... WHERE`, which gates only
237
- # whether the conflict branch's update applies. A genuinely new row
238
- # never reaches that branch at all, so it always inserts regardless
239
- # of this where. `RETURNING hecks_version` plus `ntuples.zero?` is
240
- # how a real version mismatch is told apart from an ordinary write:
241
- # zero rows back means the conflict branch's where excluded the row
242
- # entirely — the version had already moved — so `nil` is returned
243
- # for the caller (`AppendOnly#save`) to treat as "stale, no-op".
195
+ # `expected_version` requests optimistic-concurrency CAS; nil back means it didn't match.
244
196
  #
245
197
  # @param entry [Ports::Persistence::Entry] the save or delete to materialize
246
198
  # @param expected_version [Integer, nil] the `hecks_version` the row must still hold for
247
199
  # an update to apply; nil writes unconditionally. Ignored for a delete
248
- # @return [Runtime::Instance, PG::Result, nil] for a save, a new instance over the
249
- # entry's state with `version` set to the stored `hecks_version`, or nil when
250
- # `expected_version` did not match and nothing was written; for a delete, the
251
- # `DELETE` statement's `PG::Result`
200
+ # @return [Runtime::Instance, PG::Result, nil] a new instance with `version` set, nil on
201
+ # CAS mismatch, or the `DELETE` statement's `PG::Result`
252
202
  # @raise [PG::Error] if the statement fails
253
203
  # rubocop:disable Metrics/AbcSize -- the CAS/plain upsert split is one
254
204
  # protocol; splitting it would hide the version handshake.
255
205
  def project(entry, expected_version: nil)
206
+ advance_checkpoint!(entry.sequence) if entry.sequence
256
207
  return pg_exec_params("DELETE FROM #{quoted_table} WHERE id = $1", [entry.id]) if entry.delete?
257
208
 
258
209
  instance = Runtime::Instance.new(aggregate: @aggregate, id: entry.id, state: entry.state)
@@ -286,15 +237,66 @@ module Hecks
286
237
  # @raise [PG::Error] if the statement fails
287
238
  # @raise [JSON::ParserError] if a stored `state` or `mirrors` value is not valid JSON
288
239
  def entries
289
- pg_exec("SELECT aggregate_id, operation, state, mirrors FROM #{quoted_entry_table} ORDER BY sequence").map do |row|
290
- state = JSON.parse(row["state"])
291
- Ports::Persistence::Entry.new(
292
- operation: row["operation"] || "save",
293
- id: row["aggregate_id"],
294
- state: Ports::Persistence::StateCodec.decode(@aggregate, state),
295
- mirrors: row["mirrors"] && JSON.parse(row["mirrors"])
296
- )
297
- end
240
+ entries_matching(
241
+ "SELECT aggregate_id, operation, state, mirrors, sequence FROM #{quoted_entry_table} ORDER BY sequence"
242
+ )
243
+ end
244
+
245
+ # Reads only the journal rows past a given `sequence`, for `AppendOnly#recover!` to replay
246
+ # after a checkpoint instead of the whole journal.
247
+ #
248
+ # @param sequence [Integer] the highest `sequence` already projected; rows at or below it
249
+ # are skipped
250
+ # @return [Array<Ports::Persistence::Entry>] the journalled entries past `sequence`, in
251
+ # append order, decoded exactly as `entries` decodes them; `[]` when none are newer
252
+ # @raise [PG::Error] if the statement fails
253
+ # @raise [JSON::ParserError] if a stored `state` or `mirrors` value is not valid JSON
254
+ def entries_since(sequence)
255
+ entries_matching(
256
+ "SELECT aggregate_id, operation, state, mirrors, sequence FROM #{quoted_entry_table} " \
257
+ "WHERE sequence > $1 ORDER BY sequence",
258
+ [sequence]
259
+ )
260
+ end
261
+
262
+ # Reads the highest journal `sequence` this table has already had projected into it.
263
+ #
264
+ # @return [Integer] `0` when the table has never been checkpointed (a fresh table, or one
265
+ # from before this bookkeeping existed) — `entries_since(0)` then reads the whole journal,
266
+ # matching `entries`' own full replay
267
+ # @raise [PG::Error] if the statement fails
268
+ def checkpoint
269
+ result = pg_exec_params("SELECT last_sequence FROM hecks_checkpoints WHERE aggregate_table = $1", [table])
270
+ result.ntuples.zero? ? 0 : result[0]["last_sequence"].to_i
271
+ end
272
+
273
+ # Reads the highest journal `sequence` compaction has already deleted from this table.
274
+ #
275
+ # @return [Integer] `0` when nothing has ever been compacted
276
+ # @raise [PG::Error] if the statement fails
277
+ def compacted_through
278
+ result = pg_exec_params("SELECT compacted_through FROM hecks_checkpoints WHERE aggregate_table = $1", [table])
279
+ result.ntuples.zero? ? 0 : result[0]["compacted_through"].to_i
280
+ end
281
+
282
+ # Deletes every journal row at or before `through` and records it, so a `:strict`
283
+ # projection catch-up can tell whether it still has the history it needs.
284
+ #
285
+ # Never moves `compacted_through` backwards, and never touches the aggregate's own
286
+ # table — only the journal, which a `:refresh` projection rebuild never needs.
287
+ #
288
+ # @param through [Integer] the highest `sequence` to delete
289
+ # @return [Integer] the number of journal rows deleted
290
+ # @raise [PG::Error] if a statement fails
291
+ def compact_entries!(through:)
292
+ removed = pg_exec_params("DELETE FROM #{quoted_entry_table} WHERE sequence <= $1", [through]).cmd_tuples
293
+ pg_exec_params(
294
+ "INSERT INTO hecks_checkpoints (aggregate_table, compacted_through) VALUES ($1, $2) " \
295
+ "ON CONFLICT (aggregate_table) DO UPDATE SET " \
296
+ "compacted_through = GREATEST(hecks_checkpoints.compacted_through, excluded.compacted_through)",
297
+ [table, through]
298
+ )
299
+ removed
298
300
  end
299
301
 
300
302
  # Deletes every row of the aggregate's table and its journal; events, saga rows and
@@ -310,13 +312,8 @@ module Hecks
310
312
 
311
313
  # Journals and upserts an instance's current state atomically.
312
314
  #
313
- # One transaction, not the plain append-then-project two-step a
314
- # file-based adapter needs a crash-recovery replay for (Heki) —
315
- # real Postgres ACID atomicity is sitting right there, so a crash
316
- # between the journal insert and the table upsert must not leave
317
- # the two disagreeing. `append`/`project` themselves stay plain,
318
- # transaction-free methods (see the class comment above) — the
319
- # transaction lives here, the one caller that runs both together.
315
+ # One transaction, so a crash between the journal insert and the row upsert
316
+ # can never leave the two disagreeing.
320
317
  #
321
318
  # @param instance [Runtime::Instance] the instance to store
322
319
  # @return [Runtime::Instance] a new instance over the saved state, `version` set to the
@@ -405,13 +402,32 @@ module Hecks
405
402
  end
406
403
  end
407
404
 
405
+ # Reads back one record's recorded events, oldest first — pushed down as a `WHERE`
406
+ # clause (`hecks_events_aggregate_id_idx`) instead of filtering `#events`'s whole-table
407
+ # read, since a `corrects` command's history lookup only ever needs this one record.
408
+ #
409
+ # @param aggregate [String] the `"domain::AggregateName"` key events are stored under
410
+ # @param id [String, Object] the record's identity, matched as `id.to_s`
411
+ # @return [Array<Runtime::Event>] the record's stored events; `[]` when it has none
412
+ # @raise [PG::Error] if the statement fails
413
+ def events_for(aggregate:, id:)
414
+ pg_exec_params(
415
+ "SELECT * FROM events WHERE aggregate = $1 AND aggregate_id = $2 ORDER BY id",
416
+ [aggregate, id.to_s]
417
+ ).map do |row|
418
+ Runtime::Event.new(
419
+ name: row["name"],
420
+ aggregate: row["aggregate"],
421
+ id: row["aggregate_id"],
422
+ payload: JSON.parse(row["payload"], symbolize_names: true),
423
+ occurred_at: row["occurred_at"]
424
+ )
425
+ end
426
+ end
427
+
408
428
  # Upserts one saga instance's checkpoint, keyed by domain, process manager and
409
429
  # correlation.
410
430
  #
411
- # ── the optional saga-persistence capability (§2) — same DDL and
412
- # shape as PostgresEra's own (postgres_era.rb), not lineage-
413
- # specific, copied verbatim.
414
- #
415
431
  # @param process_manager [String, Symbol] the process manager's name
416
432
  # @param correlation [String, Object] the instance's correlation value, stored as
417
433
  # `correlation.to_s`
@@ -475,8 +491,6 @@ module Hecks
475
491
 
476
492
  private
477
493
 
478
- # ── SqlQueryBuilder's dialect hooks ─────────────────────────────
479
-
480
494
  def select_list = "*"
481
495
  def from_relation = quoted_table
482
496
  def dialect_name = "Postgres"
@@ -491,11 +505,8 @@ module Hecks
491
505
  "position(#{placeholder} in #{expression}) > 0"
492
506
  end
493
507
 
494
- # The list column itself is the JSONB array — no reaching into a
495
- # shared blob a jsonb path has to walk into first (PostgresEra's
496
- # own version does, since every attribute there shares one `state`
497
- # column). Here, `column` names a real column of its own, already
498
- # jsonb, already the array.
508
+ # `column` is already a real jsonb column and already the array — no shared
509
+ # `state` blob to walk into first, unlike PostgresEra's own version.
499
510
  def list_contains_clause(column, member, placeholder)
500
511
  target = member.empty? ? "elem #>> '{}'" : "elem ->> #{text_literal(member)}"
501
512
  elements = "jsonb_array_elements(#{quote_ident(column)}) AS elem"
@@ -504,28 +515,15 @@ module Hecks
504
515
 
505
516
  def plain_column(name) = quote_ident(name)
506
517
 
507
- # PostgresEra's own `jsonb_path` walks `[name, *path]` into one
508
- # shared `state` column — the attribute name is part of the path
509
- # there. Here the attribute name is the column: the path into it
510
- # is whatever is left after the column, never the column name
511
- # repeated inside its own path.
518
+ # The attribute name is the column itself here, not part of the path (unlike
519
+ # PostgresEra's shared `state` blob) — the path is whatever follows the column.
512
520
  def nested_expression(name, path, member)
513
521
  segments = path.empty? ? [(member || "value").to_s] : path
514
522
  jsonb_path(name, segments)
515
523
  end
516
524
 
517
- # Scalar, non-value-object attributes get a real typed column
518
- # (bigint/double precision/text) — comparing and sorting one needs
519
- # no cast at all, unlike PostgresEra's shared jsonb `state` blob,
520
- # where even a top-level scalar only ever comes out of `#>>` as
521
- # text. A jsonb-extracted value (a value-object member reached
522
- # through `nested_expression`/`jsonb_path` above) still comes out
523
- # of `#>>` as text the exact same way PostgresEra's own does, and
524
- # still needs the same `::numeric` cast to compare/sort
525
- # numerically rather than lexicographically. `jsonb_extraction?`
526
- # tells the two apart by inspecting the expression `query_expression`
527
- # already built — never a second, hand-rolled field walk that
528
- # could disagree with the one the SQL actually uses.
525
+ # A jsonb-extracted value still comes out as text; cast it to compare/sort
526
+ # numerically instead of lexicographically.
529
527
  def comparable_expression(expression, value)
530
528
  value.is_a?(Numeric) && jsonb_extraction?(expression) ? "(#{expression})::numeric" : expression
531
529
  end
@@ -534,19 +532,14 @@ module Hecks
534
532
  pg_exec_params(sql, binds).map { |row| instance_from_row(row) }
535
533
  end
536
534
 
537
- # Stamps `.version` (adapter bookkeeping, never domain state — see
538
- # `Instance`'s own comment) from the row's `hecks_version` column on
539
- # every Instance this adapter builds from a real stored row, so a
540
- # later `save`'s optimistic-concurrency CAS has something to check
541
- # against.
535
+ # Stamps `.version` from `hecks_version` so a later CAS save has something
536
+ # to check against.
542
537
  def instance_from_row(row)
543
538
  instance = Runtime::Instance.new(aggregate: @aggregate, id: row["id"], state: decode(row))
544
539
  instance.version = row["hecks_version"].to_i
545
540
  instance
546
541
  end
547
542
 
548
- # ── the rest of the dialect ─────────────────────────────────────
549
-
550
543
  def quote_ident(name) = PG::Connection.quote_ident(name.to_s)
551
544
  def quoted_table = quote_ident(table)
552
545
  def entry_table = "#{table}_entries"
@@ -559,11 +552,8 @@ module Hecks
559
552
  jsonb_extraction?(expression) && numeric_field?(field) ? "(#{expression})::numeric" : expression
560
553
  end
561
554
 
562
- # Postgres defaults to NULLS LAST on ASC — same override
563
- # PostgresEra's own order_clause carries, so a declared query
564
- # answers identically no matter which adapter serves it (the
565
- # port's in-memory semantics, which Sqlite's own default happens
566
- # to match, put null rows FIRST ascending and LAST descending).
555
+ # Overrides Postgres's default NULLS LAST on ASC so every adapter answers
556
+ # a declared query's null ordering identically.
567
557
  def order_clause(order_by, policy)
568
558
  direction = order_by.direction.to_s.downcase == "desc" ? "DESC" : "ASC"
569
559
  nulls = case policy&.mode.to_s
@@ -584,15 +574,38 @@ module Hecks
584
574
  end
585
575
  end
586
576
 
587
- # Array[...] of individually-escaped literals — same escaping
588
- # PostgresEra's own jsonb_path carries and the same reason: a
589
- # hand-rolled '{a,b,c}' array literal has no escaping at all, and
590
- # a segment is a field or value-object member name this method has
591
- # no way to know is always schema-declared.
577
+ # Builds an escaped Array[...] literal rather than a hand-rolled '{a,b,c}'
578
+ # string, since a segment name isn't guaranteed schema-declared.
592
579
  def jsonb_path(column, segments)
593
580
  "#{quote_ident(column)} #>> ARRAY[#{segments.map { |segment| text_literal(segment) }.join(', ')}]::text[]"
594
581
  end
595
582
 
583
+ # Shared decode step for `entries`/`entries_since` — runs a query already selecting
584
+ # `aggregate_id, operation, state, mirrors, sequence` and builds one Entry per row.
585
+ def entries_matching(sql, binds = [])
586
+ pg_exec_params(sql, binds).map do |row|
587
+ state = JSON.parse(row["state"])
588
+ Ports::Persistence::Entry.new(
589
+ operation: row["operation"] || "save",
590
+ id: row["aggregate_id"],
591
+ state: Ports::Persistence::StateCodec.decode(@aggregate, state),
592
+ mirrors: row["mirrors"] && JSON.parse(row["mirrors"]),
593
+ sequence: row["sequence"].to_i
594
+ )
595
+ end
596
+ end
597
+
598
+ # Advances this table's checkpoint to `sequence`, never backwards — two overlapping
599
+ # `project` calls for out-of-order entries must not let an older sequence win.
600
+ def advance_checkpoint!(sequence)
601
+ pg_exec_params(
602
+ "INSERT INTO hecks_checkpoints (aggregate_table, last_sequence) VALUES ($1, $2) " \
603
+ "ON CONFLICT (aggregate_table) DO UPDATE SET " \
604
+ "last_sequence = GREATEST(hecks_checkpoints.last_sequence, EXCLUDED.last_sequence)",
605
+ [table, sequence]
606
+ )
607
+ end
608
+
596
609
  def text_literal(text) = "'#{text.to_s.gsub("'", "''")}'"
597
610
  end
598
611
  end
@@ -1,22 +1,11 @@
1
- # ADR 0033 — the `PostgresEra` Ruby CLASS moved into the loadable era
2
- # persistence plugin (`lib/hecks/ports/persistence/plugins/era/`); this
3
- # declaration stays here, unmoved. `Adapters::Folder#load_library` globs
4
- # `.adapter` files by DIRECTORY, not by require path, on every boot,
5
- # regardless of which adapters a given domain actually binds — the same
6
- # reason every other adapter's own `.adapter` file lives beside this one.
7
- # Pure metadata (which port, which fields) — no era-specific code, and
8
- # harmless for a domain that never binds `persisted_by "PostgresEra"`.
9
- # `registry.adapter_class("PostgresEra")` is only ever resolved for a
10
- # domain that actually binds to it, at which point the plugin must
11
- # already be `require`d for the class to exist.
1
+ # Declares the PostgresEra adapter's port and fields; the class lives in the era plugin.
2
+ # This file is loaded on every boot, so it stays metadata only.
12
3
  Hecks.adapter "PostgresEra" do
13
4
  port "persistence"
14
5
  field :database
15
6
  field :role
16
7
  field :schema
17
- # `allow_superuser true` — the explicit, on-the-record opt-in to boot
18
- # over a connection whose role is a Postgres superuser or carries
19
- # BYPASSRLS, which the era write-fence (row-level security) cannot
20
- # bite. Refused by default; see `Lineage#check_fence_applies!` (BUG#24).
8
+ # Opt-in to boot over a superuser or BYPASSRLS role, which the era write fence cannot
9
+ # bite. Refused by default; see `Lineage#check_fence_applies!`.
21
10
  field :allow_superuser
22
11
  end
@@ -1,3 +1,4 @@
1
+ require "monitor"
1
2
  require "prism"
2
3
  require_relative "../../bluebook/expression/canonical_form"
3
4
 
@@ -5,13 +6,8 @@ module Hecks
5
6
  module Adapters
6
7
  class NotExtractable < StandardError; end
7
8
 
8
- # Extracts a `given`/`ensures`/invariant block's own source text back out
9
- # of the `.rb` file it was defined in (via `block.source_location`),
10
- # parses it with the `prism` gem, and canonicalises it — how a rule's
11
- # predicate becomes readable, comparable text (era diffing, docs) rather
12
- # than an opaque compiled Proc. Per-process `TREES` cache keyed by file
13
- # path; `forget`/`forget_all` exist for a caller that reloads an edited
14
- # file in-process (see their own comment).
9
+ # Extracts a `given`/`ensures`/invariant block's own source text via
10
+ # `prism`, canonicalised into readable, comparable text (era diffing, docs).
15
11
  module Prism
16
12
  # Not frozen — a real cache, keyed by file path and mutated by
17
13
  # #tree_for below (`TREES[file] ||= ...`) and #forget/#forget_all.
@@ -19,6 +15,16 @@ module Hecks
19
15
  # rubocop:disable-next Style/MutableConstant
20
16
  TREES = {}
21
17
 
18
+ # The first block node starting on each line, per parsed tree (by identity), built by one
19
+ # walk of the tree the first time a block in its file is looked up. Dropped with its tree
20
+ # by #forget/#forget_all.
21
+ BLOCKS_BY_LINE = {}.compare_by_identity
22
+
23
+ # Guards `TREES` and `BLOCKS_BY_LINE` together, so a parse or an index build never
24
+ # interleaves with a `forget` that drops the same tree. Reentrant: `block_node_at`
25
+ # calls `tree_for` under it.
26
+ LOCK = Monitor.new
27
+
22
28
  module_function
23
29
 
24
30
  # Recovers a block's body as canonical source text.
@@ -52,14 +58,25 @@ module Hecks
52
58
  # @return [Prism::BlockNode, nil] the first block node found starting on `line`, or nil
53
59
  # if none does
54
60
  def block_node_at(file, line)
55
- found = nil
56
- walk(tree_for(file)) do |node|
61
+ LOCK.synchronize do
62
+ tree = tree_for(file)
63
+ BLOCKS_BY_LINE[tree] ||= blocks_by_line(tree)
64
+ BLOCKS_BY_LINE[tree][line]
65
+ end
66
+ end
67
+
68
+ # Indexes a tree's block nodes by the line they start on, keeping the first in walk order.
69
+ #
70
+ # @param tree [Prism::ProgramNode] a parsed file's root node
71
+ # @return [Hash{Integer => Prism::BlockNode}] block node by 1-based start line
72
+ def blocks_by_line(tree)
73
+ index = {}
74
+ walk(tree) do |node|
57
75
  next unless node.is_a?(::Prism::BlockNode)
58
- next unless node.location.start_line == line
59
76
 
60
- found ||= node
77
+ index[node.location.start_line] ||= node
61
78
  end
62
- found
79
+ index
63
80
  end
64
81
 
65
82
  # Parses a file with `prism`, caching the result for the life of the process.
@@ -67,42 +84,35 @@ module Hecks
67
84
  # @param file [String] the file path to parse and cache under
68
85
  # @return [Prism::ProgramNode] the parsed syntax tree's root node
69
86
  def tree_for(file)
70
- # ::Prism.parse_file(file) reads the file itself, at the C
71
- # extension level — bypassing Ruby's own File/IO layer entirely.
72
- # That's invisible to anything that virtualizes the filesystem at
73
- # the Ruby level instead of the OS level (e.g. tebako's memfs,
74
- # which presses a hecks-based app into a single executable —
75
- # see domain/README.md's "Deploying" section in a client site's domain for
76
- # why that matters). ::Prism.parse(File.read(file)) parses the
77
- # exact same bytes, just read through Ruby's File.read first,
78
- # which those tools do intercept.
79
- TREES[file] ||= ::Prism.parse(File.read(file)).value
87
+ # ::Prism.parse_file reads the file at the C-extension level, bypassing
88
+ # Ruby's File/IO layer — invisible to tools that virtualize the filesystem
89
+ # there (e.g. tebako's memfs). Read through File.read first so they intercept it.
90
+ LOCK.synchronize { TREES[file] ||= ::Prism.parse(File.read(file)).value }
80
91
  end
81
92
 
82
- # `TREES` caches for the life of the process, keyed by path, with
83
- # no staleness check — correct for every ordinary caller (a file
84
- # loads once per process: one `bin/ir` run, one rspec worker,
85
- # never edited out from under it), but wrong for anything that
86
- # legitimately reloads an edited file in-process: a stale cached
87
- # tree reports a `given`/`ensures` block at its old line number,
88
- # which does not match the freshly re-executed file's own
89
- # `block.source_location` — surfacing as "did not survive
90
- # extraction" on a perfectly valid file. Built for real building
91
- # `Hecks::Codemod` (lib/hecks/codemod.rb), which needs exactly this
92
- # invalidation rather than reaching into `TREES.clear` directly — a
93
- # private implementation detail poked from outside. `forget`/
94
- # `forget_all` are the real API so nothing else that reloads an
95
- # edited file in-process has to know `TREES` exists at all.
93
+ # Drops one file from the process-wide parse cache, for a caller (`Hecks::Codemod`)
94
+ # that reloads an edited file in-process and needs the stale tree invalidated.
96
95
  #
97
96
  # @param file [String] the file path to drop from the cache
98
97
  # @return [Prism::ProgramNode, nil] the cached tree that was removed, or nil if
99
98
  # nothing was cached for `file`
100
- def forget(file) = TREES.delete(file)
99
+ def forget(file)
100
+ LOCK.synchronize do
101
+ tree = TREES.delete(file)
102
+ BLOCKS_BY_LINE.delete(tree)
103
+ tree
104
+ end
105
+ end
101
106
 
102
107
  # Drops every cached parse tree.
103
108
  #
104
109
  # @return [Hash] the now-empty cache
105
- def forget_all = TREES.clear
110
+ def forget_all
111
+ LOCK.synchronize do
112
+ BLOCKS_BY_LINE.clear
113
+ TREES.clear
114
+ end
115
+ end
106
116
 
107
117
  # Visits `node` and every descendant depth-first, calling `visit` on each.
108
118
  #