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
@@ -3,45 +3,8 @@ require "digest"
3
3
 
4
4
  module Hecks
5
5
  module Runtime
6
- # The storage-shape projection of a bluebook: exactly the parts of
7
- # the IR that decide what persisted data looks like — aggregates,
8
- # their attributes (with full value-object/entity structure and
9
- # cardinality), references, the lifecycle field, and the identity
10
- # paths. Everything behavioral — commands, invariants, queries,
11
- # policies, lifecycle transitions, defaults, descriptions, comments,
12
- # the declared routing `version:` — is excluded, so editing behavior
13
- # never bumps an era and editing shape always does.
14
- #
15
- # ## Constraints left out on purpose
16
- #
17
- # Three attribute facts on the wire are constraints on persisted
18
- # values, not behavior, and are still excluded — each decided, not
19
- # overlooked:
20
- #
21
- # `optional` — required-ness is enforced at dispatch; stored rows
22
- # are never re-validated on read, so flipping it
23
- # strands nothing already written. Excluded.
24
- # `pattern` — same argument: a fact about what may be written
25
- # next, not about what was stored. Excluded.
26
- # `admits` — the sharpest of the three: narrowing a closed set
27
- # can strand stored rows outside it, and the wire
28
- # carries only the set's name, so a set whose members
29
- # changed under a stable name is invisible even to a
30
- # projection that included the fact (the same lesson
31
- # recursive value-object drift taught). Excluded, and
32
- # named as a gap: constraint tightening has no
33
- # translation-rule vocabulary to acknowledge it yet,
34
- # so including it would mint era bumps nothing can
35
- # explain. When the translation language grows a
36
- # constraint-acknowledgment rule, `admits` (by member
37
- # list, not by name) is first in line, and that
38
- # change bumps `FORM_VERSION`.
39
- #
40
- # ## Structural comparison over the dump form
41
- #
42
- # Projection runs over the canonical dump form (`to_h`, JSON
43
- # round-tripped), so a verdict depends only on the IR — never on live
44
- # object graphs. Structural comparison only, never a hash comparison.
6
+ # The storage-shape projection of a bluebook: only the IR parts that decide how persisted
7
+ # data looks, so behavior edits never bump an era and shape edits always do.
45
8
  module StorageShape
46
9
  module_function
47
10
 
@@ -67,10 +30,7 @@ module Hecks
67
30
  # @return [Boolean] true when both project to an equal structure
68
31
  def same?(held, current) = project(held) == project(current)
69
32
 
70
- # The canonical serialization the Ruby scaffold hashes at mint time
71
- # — the one moment identity is computed. Nothing ever recomputes a
72
- # stored era name to verify it, so this form can evolve freely.
73
- #
33
+ # The canonical serialization hashed at mint time; stored era names are never recomputed.
74
34
  # @param bluebook [Bluebook::Chapter] the bluebook to serialize
75
35
  # @return [String] compact JSON text of `project(bluebook)`
76
36
  def canonical(bluebook) = JSON.generate(project(bluebook))
@@ -89,12 +49,8 @@ module Hecks
89
49
  # @return [String] the first `LABEL_LENGTH` hex characters of `mint_hash(bluebook)`
90
50
  def mint_label(bluebook) = mint_hash(bluebook)[0, LABEL_LENGTH]
91
51
 
92
- # The version of the canonical serialization above. Minted-once
93
- # means a stored name stays valid across form changes — but only
94
- # if each name records which form minted it, so v1-named and
95
- # v2-named eras coexist legibly. Stored beside every minted hash
96
- # (names.tsv fourth field / hecks_eras.canon_form); bump this in
97
- # the same change that alters project/canonical output.
52
+ # Version of the canonical form, stored beside each minted hash; bump it with any
53
+ # change to project/canonical output.
98
54
  FORM_VERSION = 1
99
55
 
100
56
  # Projects one dumped aggregate down to its identity, lifecycle field and attributes.
@@ -108,11 +64,7 @@ module Hecks
108
64
  def project_aggregate(aggregate)
109
65
  {
110
66
  "name" => aggregate["name"],
111
- # The declared identity paths, as a list, in declaration order —
112
- # order is semantic (the paths join in order to form the id).
113
- # No "id" fallback: an aggregate that declares nothing has [],
114
- # and that is a real declared state, distinct from an aggregate
115
- # identified by a field named "id".
67
+ # Declaration order is semantic (paths join in order to form the id); no "id" fallback.
116
68
  "identity" => Array(aggregate["identified_by"]).map(&:to_s),
117
69
  "lifecycle_field" => aggregate.dig("lifecycle", "field"),
118
70
  "attributes" => (aggregate["attributes"] || [])
@@ -121,12 +73,13 @@ module Hecks
121
73
  }
122
74
  end
123
75
 
76
+ # `optional`, `pattern` and `admits` are left out: they constrain future writes, not
77
+ # stored rows. Including `admits` needs a translation rule to acknowledge it first.
78
+ #
124
79
  # Projects one dumped attribute to its name, cardinality and full type signature.
125
80
  #
126
- # @param aggregate [Hash{String => Object}] the dumped aggregate that owns the attribute,
127
- # searched for the value objects and entities its type may name
128
- # @param attribute [Hash{String => Object}] the dumped attribute, read for `"name"`,
129
- # `"list"` and `"type"`
81
+ # @param aggregate [Hash{String => Object}] the dumped aggregate that owns the attribute
82
+ # @param attribute [Hash{String => Object}] the dumped attribute
130
83
  # @param seen [Array<String>] type names already being expanded, which stops a
131
84
  # self-referencing type from recursing forever
132
85
  # @return [Hash{String => Object}] `"name"` (String), `"list"` (Boolean) and `"type"`
@@ -139,11 +92,8 @@ module Hecks
139
92
  }
140
93
  end
141
94
 
142
- # A plain type name for a primitive; the type name plus its
143
- # members' full signatures for a value object or entity — so two
144
- # attributes with the same declared type name but different
145
- # internals are never mistaken for unchanged.
146
- #
95
+ # A primitive's type name, or for a value object or entity the name plus its members'
96
+ # signatures, so same-named types with different internals never look unchanged.
147
97
  # @param aggregate [Hash{String => Object}] the dumped aggregate whose value objects and
148
98
  # entities are searched for `type_name`
149
99
  # @param type_name [String] the attribute's declared type name
@@ -0,0 +1,227 @@
1
+ require "json"
2
+ require "date"
3
+ require "fileutils"
4
+ require_relative "audit/approval_digest"
5
+ require_relative "../../../../../version"
6
+
7
+ module Hecks
8
+ module Translation
9
+ # The committed form of an edge's approval: `translations/<edge>.approval`, a JSON file beside
10
+ # the edge that a reviewer sees in the same diff.
11
+ #
12
+ # It binds to the edge's content digest and not to the journal's tip, so a production journal
13
+ # that keeps writing between the commit and the deploy does not void it. An edge with a compute
14
+ # or rekey rule also carries the rehearsal a person ran against real data, since the audit's
15
+ # samples are that edge's only other verification.
16
+ module ApprovalFile
17
+ EXTENSION = ".approval".freeze
18
+
19
+ # The keys of a rehearsal block; every one is required when the block is required.
20
+ #
21
+ # `host_version` is the Hecks release the rehearsal ran on, and the gate checks it: a
22
+ # rehearsal counts only on a host whose release shares its `major.minor` (see
23
+ # `host_compatible?`).
24
+ REHEARSAL_KEYS = %w[snapshot host_version result at].freeze
25
+
26
+ # How a `host_version` reads: `major.minor`, an optional patch, an optional pre-release or
27
+ # build suffix. Only the first two numbers take part in the comparison.
28
+ HOST_LINE = /\A(\d+)\.(\d+)(?:\.\d+)?(?:[-+][0-9A-Za-z.+-]+)?\z/
29
+
30
+ # The release the running host reports: the Hecks release this code was built for. The Rust
31
+ # host embeds the same string from `rust/host/HECKS_RELEASE`, and a spec fails when the two
32
+ # drift.
33
+ HOST_RELEASE = Hecks::VERSION
34
+
35
+ # An ISO 8601 time: `2026-09-28T12:30:00Z`, with optional fraction and `+HH:MM` offset. The
36
+ # host's approval.rs accepts the same shape.
37
+ TIMESTAMP = /\A(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})\z/
38
+
39
+ extend Audit::ApprovalDigest
40
+
41
+ module_function
42
+
43
+ # Names an edge by the labels it joins; labels are hex, so the dash cannot be part of one.
44
+ #
45
+ # @param edge [Bluebook::Translation] the parsed translation edge
46
+ # @return [String] `<from>-<to>`
47
+ def edge_name(edge) = "#{edge.from}-#{edge.to}"
48
+
49
+ # Where an edge's approval lives.
50
+ #
51
+ # @param directory [String] the domain's bluebook directory
52
+ # @param edge [Bluebook::Translation] the parsed translation edge
53
+ # @return [String] `<directory>/translations/<edge>.approval`
54
+ def path_for(directory, edge) = File.join(directory, "translations", "#{edge_name(edge)}#{EXTENSION}")
55
+
56
+ # Whether the edge carries a rule whose only verification is a human's review of samples.
57
+ #
58
+ # @param edge [Bluebook::Translation] the parsed translation edge
59
+ # @return [Boolean] true when any aggregate declares a compute or a rekey
60
+ def needs_rehearsal?(edge)
61
+ edge.aggregates.any? { |declared| !declared.computes.empty? || !declared.rekeys.empty? }
62
+ end
63
+
64
+ # Whether a rehearsal block records a run that passed, with every field named.
65
+ #
66
+ # @param rehearsal [Hash, nil] the block, string-keyed as it reads from JSON
67
+ # @return [Boolean] true when all of `REHEARSAL_KEYS` are non-empty Strings and `result` is
68
+ # `pass`
69
+ def rehearsed?(rehearsal)
70
+ return false unless rehearsal.is_a?(Hash)
71
+
72
+ REHEARSAL_KEYS.all? { |key| named?(rehearsal[key]) } && rehearsal["result"] == "pass"
73
+ end
74
+
75
+ # The `major.minor` a version names.
76
+ #
77
+ # @param version [Object] a `host_version` as it reads from JSON
78
+ # @return [Array<Integer>, nil] `[major, minor]`, or nil when it is not a version
79
+ def release_line(version)
80
+ match = HOST_LINE.match(version) if version.is_a?(String)
81
+ match && [match[1].to_i, match[2].to_i]
82
+ end
83
+
84
+ # Whether a rehearsal ran on a host compatible with the running one.
85
+ #
86
+ # The rule is the smallest sound one: equal `major.minor`. A patch release does not change
87
+ # what a mint does, so a rehearsal survives it; a minor or major release may, so a rehearsal
88
+ # from an older host (or a newer one, which proves nothing about this one) does not.
89
+ #
90
+ # @param rehearsal [Hash] a rehearsal block, string-keyed
91
+ # @param host_version [String] the running host's release
92
+ # @return [Boolean] true when both versions parse and share `major.minor`
93
+ def host_compatible?(rehearsal, host_version = HOST_RELEASE)
94
+ line = release_line(rehearsal["host_version"])
95
+ !line.nil? && line == release_line(host_version)
96
+ end
97
+
98
+ # The refusal for a rehearsal that ran on another host line; rust/host words it the same.
99
+ #
100
+ # @param rehearsal [Hash] a rehearsal block, string-keyed
101
+ # @param host_version [String] the running host's release
102
+ # @return [String] names both versions
103
+ def host_refusal(rehearsal, host_version = HOST_RELEASE)
104
+ line = release_line(host_version)&.join(".")
105
+ "the rehearsal ran on Hecks #{rehearsal['host_version']}, but this host is Hecks #{host_version}; " \
106
+ "a rehearsal counts only on a host of the same major.minor (#{line}.x) — " \
107
+ "re-run the rehearsal on this host and approve again"
108
+ end
109
+
110
+ # Whether a value is a String with something in it; a number or nil names nothing.
111
+ #
112
+ # @param value [Object] a field as it reads from JSON
113
+ # @return [Boolean] true for a String that is not blank
114
+ def named?(value) = value.is_a?(String) && !value.strip.empty?
115
+
116
+ # Whether a value is an ISO 8601 time that exists on the calendar.
117
+ #
118
+ # @param value [Object] a field as it reads from JSON
119
+ # @return [Boolean] true for a String of the `TIMESTAMP` shape with a real date and clock time
120
+ def timestamp?(value)
121
+ return false unless value.is_a?(String) && (match = TIMESTAMP.match(value))
122
+
123
+ year, month, day, hour, minute, second = match.captures.map(&:to_i)
124
+ Date.valid_date?(year, month, day) && hour < 24 && minute < 60 && second < 60
125
+ end
126
+
127
+ # Whether an approval names who approved it and when, the two things a reviewer signs.
128
+ #
129
+ # @param approval [Hash] the file, string-keyed as it reads from JSON
130
+ # @return [Boolean] true when `approved_by` is a non-empty String and `approved_at` a time
131
+ def attributed?(approval)
132
+ named?(approval["approved_by"]) && timestamp?(approval["approved_at"])
133
+ end
134
+
135
+ # Builds the JSON document of one approval.
136
+ #
137
+ # @param edge [Bluebook::Translation] the parsed translation edge
138
+ # @param approved_by [String] who approved it, from their git identity
139
+ # @param approved_at [String] when, as an ISO 8601 UTC time
140
+ # @param rehearsal [Hash{String => String}, nil] the block; required for a compute or rekey
141
+ # @return [Hash{String => Object}] `edge`, `edge_digest`, `approved_by`, `approved_at`, and
142
+ # `rehearsal` when one was given
143
+ # @raise [ArgumentError] if `approved_by` is blank or `approved_at` is not a time, or if the
144
+ # edge needs a rehearsal and none that passed was given
145
+ def build(edge:, approved_by:, approved_at:, rehearsal: nil)
146
+ unless attributed?("approved_by" => approved_by, "approved_at" => approved_at)
147
+ raise ArgumentError, "an approval names who approved it (approved_by, your git identity) " \
148
+ "and when (approved_at, an ISO 8601 time)"
149
+ end
150
+
151
+ if needs_rehearsal?(edge) && !rehearsed?(rehearsal)
152
+ raise ArgumentError, "an edge with a compute or rekey rule is approved on a rehearsal that passed: " \
153
+ "name the snapshot, the host version, the result (pass) and when it ran"
154
+ end
155
+
156
+ document = { "edge" => edge_name(edge), "edge_digest" => edge_digest(edge),
157
+ "approved_by" => approved_by, "approved_at" => approved_at }
158
+ document["rehearsal"] = rehearsal.slice(*REHEARSAL_KEYS) if rehearsal
159
+ document
160
+ end
161
+
162
+ # Writes an edge's approval file, replacing an earlier approval of the same edge.
163
+ #
164
+ # @param directory [String] the domain's bluebook directory
165
+ # @param edge [Bluebook::Translation] the parsed translation edge
166
+ # @param document [Hash{String => Object}] what `build` answered
167
+ # @return [String] the path written
168
+ def write!(directory, edge, document)
169
+ path = path_for(directory, edge)
170
+ FileUtils.mkdir_p(File.dirname(path))
171
+ File.write(path, "#{JSON.pretty_generate(document)}\n")
172
+ path
173
+ end
174
+
175
+ # Reads every committed approval of a domain.
176
+ #
177
+ # @param directory [String] the domain's bluebook directory
178
+ # @return [Array<Hash{String => Object}>] each parsed file; one that is not JSON is skipped,
179
+ # since a file that cannot be read approves nothing
180
+ def read_all(directory)
181
+ Dir[File.join(directory, "translations", "*#{EXTENSION}")].filter_map do |path|
182
+ parsed = JSON.parse(File.read(path))
183
+ parsed if parsed.is_a?(Hash)
184
+ rescue JSON::ParserError
185
+ nil
186
+ end
187
+ end
188
+
189
+ # The committed approval that lets an edge mint, if there is one.
190
+ #
191
+ # @param directory [String, nil] the domain's bluebook directory
192
+ # @param edge [Bluebook::Translation] the parsed translation edge
193
+ # @param host_version [String] the running host's release
194
+ # @return [Hash{String => Object}, nil] an approval whose digest is the edge's, that names
195
+ # who approved it and when, and whose rehearsal passed on a compatible host when the edge
196
+ # needs one; nil when none applies
197
+ def applicable(directory, edge, host_version: HOST_RELEASE)
198
+ return unless directory
199
+
200
+ digest = edge_digest(edge)
201
+ read_all(directory).find do |approval|
202
+ approval["edge_digest"] == digest && attributed?(approval) &&
203
+ (!needs_rehearsal?(edge) ||
204
+ (rehearsed?(approval["rehearsal"]) && host_compatible?(approval["rehearsal"], host_version)))
205
+ end
206
+ end
207
+
208
+ # Why a committed approval that covers the edge was still not applied, when the only thing
209
+ # wrong is the host it was rehearsed on.
210
+ #
211
+ # @param directory [String, nil] the domain's bluebook directory
212
+ # @param edge [Bluebook::Translation] the parsed translation edge
213
+ # @param host_version [String] the running host's release
214
+ # @return [String, nil] `host_refusal`'s text, or nil when no such approval exists
215
+ def host_mismatch(directory, edge, host_version: HOST_RELEASE)
216
+ return unless directory && needs_rehearsal?(edge)
217
+
218
+ digest = edge_digest(edge)
219
+ stale = read_all(directory).find do |approval|
220
+ approval["edge_digest"] == digest && attributed?(approval) && rehearsed?(approval["rehearsal"]) &&
221
+ !host_compatible?(approval["rehearsal"], host_version)
222
+ end
223
+ host_refusal(stale["rehearsal"], host_version) if stale
224
+ end
225
+ end
226
+ end
227
+ end
@@ -4,23 +4,8 @@ require_relative "../../../../../../projector/exporter"
4
4
  module Hecks
5
5
  module Translation
6
6
  module Audit
7
- # ── the human gate, made real ──────────────────────────────────
8
- #
9
- # For the five portable rule kinds the mint verifies the edge
10
- # mechanically (Layer 2 is the cross-execution equivalence gate).
11
- # A compute has no mechanical verification — the Layer-3 sample a
12
- # human reads is the only one there is — so a compute edge cannot
13
- # mint until someone has run `bin/translation_audit … --approve`.
14
- #
15
- # The approval binds to what was actually reviewed, on both axes:
16
- # a content digest of the parsed edge (change the edge's meaning
17
- # and the approval lapses; a comment does not), and the journal's
18
- # high-water ordinal at review time, recorded in the target
19
- # database — a token in the repo could not bind to the data, and
20
- # samples reviewed against staging or against production-as-of-T
21
- # say nothing about a database that has moved on. Mint stays
22
- # non-interactive and its lock stays short; the human decision
23
- # happens in the audit tool, where the samples are.
7
+ # The human gate: a compute edge cannot mint until `hecks audit_translation --approve`
8
+ # binds an approval to the edge's content digest and the journal's ordinal at review.
24
9
  module ApprovalDigest
25
10
  # Fingerprints a parsed translation edge, so an approval lapses when the edge's
26
11
  # meaning changes but not when only a comment does.
@@ -6,12 +6,8 @@ require_relative "../../../../../../bluebook/model_check"
6
6
  module Hecks
7
7
  module Translation
8
8
  module Audit
9
- # Layer 1 — from the bluebook alone: every translated state must
10
- # pass the new era's types, value-object invariants, and lifecycle.
11
- # This is also where a new, stricter invariant that old records
12
- # violate surfaces — there is no "grandfather old records"
13
- # construct, and the remedy is relaxing the invariant or explicit
14
- # remediation, never a translation rule.
9
+ # Layer 1: every translated state must pass the new era's types, invariants and lifecycle.
10
+ # Old records that violate a stricter invariant surface here; no translation rule fixes them.
15
11
  module LayerOne
16
12
  # Hydrates every translated state as a current-era instance and records each one
17
13
  # the era's types, invariants or lifecycle refuse.
@@ -28,14 +24,7 @@ module Hecks
28
24
  next unless lifecycle
29
25
 
30
26
  held = instance[lifecycle.field]
31
- # `Lifecycle#states` answers default+targets only — a state
32
- # legitimately declared just as a `from:` (a terminal
33
- # transition's source, never anyone's target) is real and
34
- # reachable but invisible to it. `ModelCheck.full_states`
35
- # is the full declared set (default, every target, and
36
- # every from) that `fuzzing/properties.rb`'s own replay
37
- # check already uses for this identical question — see its
38
- # comment on this same hole.
27
+ # `Lifecycle#states` omits states declared only as a `from:`; `full_states` has them.
39
28
  allowed = Bluebook::ModelCheck.full_states(lifecycle)
40
29
  unless held.nil? || allowed.include?(held.to_s)
41
30
  violations << "#{aggregate.name}##{id}: #{lifecycle.field} is #{held.inspect}, " \
@@ -4,48 +4,27 @@ require_relative "../../lineage"
4
4
  module Hecks
5
5
  module Translation
6
6
  module Audit
7
- # Layer 2 — from the edge alone: per-rule value preservation, no
8
- # leftover source keys, and id-set conservation across the edge.
7
+ # Layer 2: from the edge alone, per-rule value preservation and id-set conservation.
9
8
  module LayerTwo
10
9
  # Checks one aggregate's records across the edge for id conservation and, where a
11
10
  # per-record comparison is possible, agreement with the reference transform.
12
11
  #
13
- # Per-rule value preservation and leftover source keys are checked
14
- # against the reference transform in full — never rule by rule in
15
- # isolation, because rules interact (a rename whose value a later
16
- # move partially consumes preserves exactly what the transform
17
- # says it preserves, no more). This makes every mint a run of the
18
- # cross-execution equivalence gate for the five portable rule
19
- # kinds: the compiled SQL produced `after`; the port's entry-JSON
20
- # transform produces `expected`; they must agree byte-for-byte on
21
- # every path a compute doesn't own. Compute paths are exempt — the
22
- # SQL is their only implementation, and the Layer-3 sample is
23
- # their only review. A rekeyed aggregate is exempt from this whole
24
- # per-record check, for the same reason and one more: there is no
25
- # old-id → new-id correspondence to look `after` up by once the id
26
- # itself is what changed.
12
+ # Compute paths and rekeyed aggregates are exempt from the comparison.
27
13
  #
28
14
  # @param violations [Array<String>] collector this method appends messages to
29
15
  # @param aggregate [Bluebook::Aggregate] the current era's IR for the aggregate
30
- # @param declared [Bluebook::TranslationAggregate, nil] this edge's rules for the
31
- # aggregate; nil limits the check to id conservation
16
+ # @param declared [Bluebook::TranslationAggregate, nil] nil limits the check to ids
32
17
  # @param before [Hash{String => Hash}] source state per record id, as parsed JSON
33
18
  # @param after [Hash{String => Hash}] translated state per record id, as parsed JSON
34
19
  # @return [void]
35
- # @raise [Runtime::WiringError] if the reference transform cannot translate a `before`
36
- # state: a convert meets an unmapped value, or a move nests under a non-Hash
20
+ # @raise [Runtime::WiringError] if the reference transform cannot translate `before`
37
21
  def layer_two!(violations, aggregate, declared, before, after)
38
22
  rekeyed = declared && !declared.rekeys.empty?
39
23
 
40
24
  check_id_conservation!(violations, aggregate, before, after, rekeyed: rekeyed)
41
25
 
42
26
  return unless declared
43
- # No correspondence between an old id and its rekeyed row exists
44
- # in Ruby (the rekey's SQL is its only implementation, same as
45
- # compute's) — exempt from the per-record equivalence gate for
46
- # the same reason compute paths already are, extended to the
47
- # whole record since an old-id lookup into `after` can never
48
- # succeed once the id itself has changed.
27
+ # A rekey's SQL is its only implementation and `after` cannot be looked up by old id.
49
28
  return if rekeyed
50
29
 
51
30
  check_value_preservation!(violations, aggregate, declared, before, after)
@@ -53,12 +32,6 @@ module Hecks
53
32
 
54
33
  # Records a violation when the edge loses, gains or collides record ids.
55
34
  #
56
- # Record-count / id-set conservation — one of the two properties
57
- # this module's own header names, and independent of the other
58
- # (per-rule value preservation, below): it needs only `before`,
59
- # `after`, and whether the edge rekeyed, never the declared rules
60
- # themselves.
61
- #
62
35
  # @param violations [Array<String>] collector this method appends at most one message to
63
36
  # @param aggregate [Bluebook::Aggregate] the aggregate, named in the message
64
37
  # @param before [Hash{String => Hash}] source state per record id
@@ -67,13 +40,7 @@ module Hecks
67
40
  # counts instead of id sets
68
41
  # @return [void]
69
42
  def check_id_conservation!(violations, aggregate, before, after, rekeyed:)
70
- # A rekey legitimately changes the id set (that's the entire
71
- # point) — set-equality would flag every honest rekey as data
72
- # loss. What must still hold is record count: a botched rekey
73
- # colliding two distinct old ids onto one new id, or dropping one
74
- # (its SQL returning NULL), shows up as the count going down —
75
- # caught here without needing to track the old→new mapping
76
- # itself.
43
+ # A rekey changes the id set, so compare counts: a collision or a NULL id lowers it.
77
44
  if rekeyed
78
45
  unless before.keys.size == after.keys.size
79
46
  violations << "#{aggregate.name}: the record count changed across a rekeying edge " \
@@ -88,15 +55,14 @@ module Hecks
88
55
  end
89
56
  end
90
57
 
91
- # Per-rule value preservation — the other property this module's
92
- # header names. Only reached once `layer_two!` has already ruled
93
- # out "no declared edge" and "rekeyed" (see its own comment on
94
- # that second guard); this method assumes both are false.
58
+ # Records a violation for each record whose translated state diverges from the reference.
59
+ # Assumes the edge is declared and does not rekey.
60
+ #
95
61
  # @param violations [Array<String>] mutated in place with one message per divergent id
96
62
  # @param aggregate [Bluebook::Aggregate] the aggregate being audited
97
63
  # @param declared [Bluebook::Translation] the declared translation to check against
98
- # @param before [Hash{String => Hash}] the old world's own records, keyed by id
99
- # @param after [Hash{String => Hash}] the new world's own records, keyed by id
64
+ # @param before [Hash{String => Hash}] source records, keyed by id
65
+ # @param after [Hash{String => Hash}] translated records, keyed by id
100
66
  # @return [void]
101
67
  def check_value_preservation!(violations, aggregate, declared, before, after)
102
68
  rules = Ports::Persistence::Lineage.from_declared(declared, aggregate.name)
@@ -124,19 +90,8 @@ module Hecks
124
90
 
125
91
  # Removes the paths a compute owns from a normalized state, in place.
126
92
  #
127
- # Exempts exactly the paths a compute owns, not the whole
128
- # top-level attribute it happens to live under. A bare path
129
- # ("price_cents") is itself the compute's entire value — dropping
130
- # the whole top-level key is correct, there's nothing else there
131
- # to check. A dotted path ("price.cents") only owns that one
132
- # member of the value object it reaches into; every sibling
133
- # member (e.g. "price.currency") is untouched by the compute and
134
- # must stay subject to the equivalence check below. Blanket-
135
- # dropping the whole top-level key for a dotted compute would
136
- # exempt the entire attribute — silent data loss elsewhere in the
137
- # same value object (a migration that nulls or drops a sibling
138
- # field) would produce zero violations, defeating the one gate
139
- # whose entire purpose is to catch exactly that.
93
+ # A dotted path removes only its own member, never the whole top-level key, so sibling
94
+ # members stay under the equivalence check and silent loss there is still caught.
140
95
  #
141
96
  # @param state [Hash{String => Object}] a normalized state; mutated
142
97
  # @param paths [Array<String>] bare (`"price_cents"`) or dotted (`"price.cents"`)
@@ -1,22 +1,11 @@
1
1
  module Hecks
2
2
  module Translation
3
3
  module Audit
4
- # New-era attributes that nothing feeds: absent from every
5
- # translated state, produced by no rule, and carrying no default.
6
- # A report, not a violation — the remedy is a `default:` on the
7
- # attribute, and the loud refusal for a required one comes from
8
- # Layer 1 the moment an invariant reads it.
4
+ # New-era attributes that nothing feeds: no translated state, rule or default supplies them.
5
+ # A report, not a violation; a required one is refused by Layer 1 once an invariant reads it.
9
6
  module UnfedReport
10
7
  # Lists the current era's attributes that no rule, default or translated record feeds.
11
8
  #
12
- # `fed` build-up plus one ordered guard chain per attribute
13
- # (already fed, has a default, actually present in some record, no
14
- # records to check at all) answering a single question — "is
15
- # anything, ever, going to feed this field" — where each `next`
16
- # rules out one way the answer is "not unfed after all" before the
17
- # next is even worth asking. Splitting the guards out would just
18
- # turn each into a same-shaped one-line predicate call.
19
- #
20
9
  # @param aggregate [Bluebook::Aggregate] the current era's IR for the aggregate
21
10
  # @param declared [Bluebook::TranslationAggregate, nil] this edge's rules; the
22
11
  # destinations of its renames, moves, converts and computes count as fed
@@ -57,10 +46,7 @@ module Hecks
57
46
  path.to_s.split(".").reduce(state) do |node, segment|
58
47
  break nil unless node.is_a?(Hash)
59
48
 
60
- # `key?` decides which spelling answers, never `||` — a
61
- # `false` genuinely held at this path must not fall through
62
- # to the other spelling (usually absent) and read as `nil`,
63
- # which would wrongly mark a `false`-valued attribute "unfed".
49
+ # `key?`, not `||`: a held `false` must not fall through and read as nil (unfed).
64
50
  node.key?(segment) ? node[segment] : node[segment.to_sym]
65
51
  end
66
52
  end
@@ -7,13 +7,8 @@ require_relative "audit/approval_digest"
7
7
 
8
8
  module Hecks
9
9
  module Translation
10
- # The audit derives its assertions; nobody hand-lists them. One file
11
- # per layer beside this one: audit/layer_one.rb (the bluebook's own
12
- # rules), audit/layer_two.rb (the edge against the reference
13
- # transform), audit/unfed_report.rb (what nothing feeds), and
14
- # audit/approval_digest.rb (the human gate's binding). Layer 3 — the
15
- # before/after sample a human approves, intent not being derivable —
16
- # is assembled right here in `check`.
10
+ # Derives the audit's assertions per layer (audit/layer_one, layer_two, unfed_report,
11
+ # approval_digest); Layer 3, the human-approved sample, is assembled in `check`.
17
12
  module Audit
18
13
  Verdict = Struct.new(:violations, :dropped, :unfed, :samples, keyword_init: true) do
19
14
  # Reports whether the mechanical layers found nothing to refuse.
@@ -38,14 +33,12 @@ module Hecks
38
33
  # @param aggregate [Bluebook::Aggregate] the current era's IR for the aggregate
39
34
  # @param declared [Bluebook::TranslationAggregate, nil] this edge's rules for the
40
35
  # aggregate; nil when the edge declares none
41
- # @param before [Hash{String => Hash}] the source era's latest state per record id, as
42
- # parsed JSON
43
- # @param after [Hash{String => Hash}] the translated state per record id, as parsed JSON
44
- # @return [Translation::Audit::Verdict] `violations` (Layer 1 and 2 messages), `dropped`
45
- # (declared drop paths as Strings), `unfed` (attribute names nothing feeds) and
46
- # `samples` (the Layer 3 before/after pairs)
36
+ # @param before [Hash{String => Hash}] source state per record id, as parsed JSON
37
+ # @param after [Hash{String => Hash}] translated state per record id, as parsed JSON
38
+ # @return [Translation::Audit::Verdict] Layer 1 and 2 `violations`, `dropped` paths,
39
+ # `unfed` attribute names and Layer 3 `samples`
47
40
  # @raise [Runtime::WiringError] if Layer 2's reference transform cannot translate a
48
- # `before` state: a convert meets an unmapped value, or a move nests under a non-Hash
41
+ # `before` state
49
42
  def check(aggregate:, declared:, before:, after:)
50
43
  violations = []
51
44
 
@@ -74,19 +67,9 @@ module Hecks
74
67
  before.keys.sort.first(SAMPLE_SIZE).map { |id| { id: id, before: before[id], after: after[id] } }
75
68
  end
76
69
 
77
- # Samples both sides of a rekeying edge separately instead of pairing them by id.
78
- #
79
- # A rekey changes the id itself, so `before`'s and `after`'s
80
- # keyspaces share nothing — pairing by matching id (the ordinary
81
- # path above) would show every `after` as nil, telling a human
82
- # nothing. Nothing in-process can compute the old→new
83
- # correspondence either (the rekey's SQL is its only
84
- # implementation, same as compute's own). Shown side by side
85
- # instead, each labelled by its own id: real records going in,
86
- # real records coming out — not claimed to correspond one-to-one,
87
- # but enough for the human this rule's only verification depends
88
- # on to actually see real shapes and values, not a wall of null.
70
+ # Samples both sides of a rekeying edge separately, each labelled by its own id.
89
71
  #
72
+ # A rekey changes the id, so pairing by id would show every `after` as nil.
90
73
  # @param before [Hash{String => Hash}] source state per old record id
91
74
  # @param after [Hash{String => Hash}] translated state per new record id
92
75
  # @return [Array<Hash{Symbol => Object}>] up to `SAMPLE_SIZE` Hashes whose `:id` ends in