hecks 1.3.0 → 1.4.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 (355) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hecks/adapters/driven/claude_code.rb +7 -7
  3. data/lib/hecks/adapters/driven/d1.rb +187 -23
  4. data/lib/hecks/adapters/driven/folder.rb +10 -10
  5. data/lib/hecks/adapters/driven/google_authentication.rb +8 -8
  6. data/lib/hecks/adapters/driven/governance_authorization.rb +31 -13
  7. data/lib/hecks/adapters/driven/heki/journal.rb +5 -4
  8. data/lib/hecks/adapters/driven/heki/saga_store.rb +5 -5
  9. data/lib/hecks/adapters/driven/heki.rb +13 -7
  10. data/lib/hecks/adapters/driven/identity_registry.rb +2 -2
  11. data/lib/hecks/adapters/driven/in_memory_ordering.rb +3 -3
  12. data/lib/hecks/adapters/driven/lambda/client.rb +34 -9
  13. data/lib/hecks/adapters/driven/lambda.rb +39 -33
  14. data/lib/hecks/adapters/driven/local_storage.rb +17 -10
  15. data/lib/hecks/adapters/driven/memory.rb +205 -9
  16. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +1 -1
  17. data/lib/hecks/adapters/driven/postgres/codec.rb +27 -11
  18. data/lib/hecks/adapters/driven/postgres/outbox.rb +40 -2
  19. data/lib/hecks/adapters/driven/postgres/reconnect.rb +23 -7
  20. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +14 -14
  21. data/lib/hecks/adapters/driven/postgres.rb +175 -28
  22. data/lib/hecks/adapters/driven/prism.rb +4 -4
  23. data/lib/hecks/adapters/driven/sql_query_builder.rb +34 -22
  24. data/lib/hecks/adapters/driven/sqlite/codec.rb +38 -10
  25. data/lib/hecks/adapters/driven/sqlite/projection.rb +60 -32
  26. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +12 -12
  27. data/lib/hecks/adapters/driven/sqlite.rb +181 -21
  28. data/lib/hecks/adapters/driven.rb +4 -4
  29. data/lib/hecks/adapters/driving/github_webhook.rb +18 -18
  30. data/lib/hecks/behaviors/dsl.rb +2 -2
  31. data/lib/hecks/behaviors/expectations.rb +23 -23
  32. data/lib/hecks/behaviors/ir.rb +1 -1
  33. data/lib/hecks/behaviors/rspec.rb +1 -1
  34. data/lib/hecks/behaviors/runner.rb +2 -2
  35. data/lib/hecks/behaviors.rb +1 -1
  36. data/lib/hecks/bluebook/aggregate.rb +13 -13
  37. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +10 -10
  38. data/lib/hecks/bluebook/assembly/build.rb +1 -1
  39. data/lib/hecks/bluebook/assembly/contract.rb +39 -16
  40. data/lib/hecks/bluebook/assembly/contracts.rb +40 -38
  41. data/lib/hecks/bluebook/assembly/marks.rb +18 -18
  42. data/lib/hecks/bluebook/assembly/specializer.rb +19 -21
  43. data/lib/hecks/bluebook/assembly.rb +9 -9
  44. data/lib/hecks/bluebook/attribute.rb +9 -9
  45. data/lib/hecks/bluebook/behaviour/aggregate.rb +11 -11
  46. data/lib/hecks/bluebook/behaviour/attribute.rb +5 -5
  47. data/lib/hecks/bluebook/behaviour/chapter.rb +23 -5
  48. data/lib/hecks/bluebook/behaviour/command.rb +23 -23
  49. data/lib/hecks/bluebook/behaviour/domain_port.rb +7 -7
  50. data/lib/hecks/bluebook/behaviour/entity.rb +8 -8
  51. data/lib/hecks/bluebook/behaviour/hexagon.rb +4 -4
  52. data/lib/hecks/bluebook/behaviour/lifecycle.rb +5 -5
  53. data/lib/hecks/bluebook/behaviour/policy.rb +12 -12
  54. data/lib/hecks/bluebook/behaviour/process_manager.rb +7 -7
  55. data/lib/hecks/bluebook/behaviour/query.rb +1 -1
  56. data/lib/hecks/bluebook/behaviour/read_model.rb +8 -8
  57. data/lib/hecks/bluebook/behaviour/traits.rb +12 -12
  58. data/lib/hecks/bluebook/behaviour/value_object.rb +6 -6
  59. data/lib/hecks/bluebook/capabilities.rb +27 -0
  60. data/lib/hecks/bluebook/chapter.rb +28 -9
  61. data/lib/hecks/bluebook/command.rb +12 -12
  62. data/lib/hecks/bluebook/domain_port.rb +9 -9
  63. data/lib/hecks/bluebook/dsl/adapter_builder.rb +24 -0
  64. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +49 -49
  65. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +96 -96
  66. data/lib/hecks/bluebook/dsl/attribute_collector.rb +41 -41
  67. data/lib/hecks/bluebook/dsl/binding_proxy.rb +22 -2
  68. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +111 -74
  69. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +48 -30
  70. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
  71. data/lib/hecks/bluebook/dsl/command_builder.rb +103 -103
  72. data/lib/hecks/bluebook/dsl/const_shim.rb +46 -15
  73. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +90 -25
  74. data/lib/hecks/bluebook/dsl/entity_builder.rb +56 -56
  75. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
  76. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +89 -30
  77. data/lib/hecks/bluebook/dsl/identity_declaration.rb +17 -17
  78. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
  79. data/lib/hecks/bluebook/dsl/policy_builder.rb +30 -21
  80. data/lib/hecks/bluebook/dsl/port_builder.rb +38 -7
  81. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +56 -22
  82. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +35 -35
  83. data/lib/hecks/bluebook/dsl/query_builder.rb +5 -5
  84. data/lib/hecks/bluebook/dsl/read_model_builder.rb +34 -34
  85. data/lib/hecks/bluebook/dsl/rule_reference.rb +41 -39
  86. data/lib/hecks/bluebook/dsl/translation_builder.rb +9 -9
  87. data/lib/hecks/bluebook/dsl/value_object_builder.rb +16 -16
  88. data/lib/hecks/bluebook/dsl/word_gate.rb +59 -53
  89. data/lib/hecks/bluebook/dsl/world_builder.rb +51 -8
  90. data/lib/hecks/bluebook/entity.rb +11 -11
  91. data/lib/hecks/bluebook/expression/ast_json.rb +20 -20
  92. data/lib/hecks/bluebook/expression/ast_reader.rb +3 -3
  93. data/lib/hecks/bluebook/expression/canonical_form.rb +9 -9
  94. data/lib/hecks/bluebook/expression/evaluator.rb +18 -18
  95. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +18 -18
  96. data/lib/hecks/bluebook/expression/resolver.rb +60 -62
  97. data/lib/hecks/bluebook/hexagon.rb +1 -1
  98. data/lib/hecks/bluebook/lifecycle.rb +1 -1
  99. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -1
  100. data/lib/hecks/bluebook/meta_validator/judge.rb +101 -87
  101. data/lib/hecks/bluebook/meta_validator/plan.rb +39 -39
  102. data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -2
  103. data/lib/hecks/bluebook/meta_validator/readings.rb +43 -43
  104. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +44 -37
  105. data/lib/hecks/bluebook/meta_validator/shapes.rb +25 -21
  106. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +145 -31
  107. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +6 -6
  108. data/lib/hecks/bluebook/meta_validator/world_judge.rb +5 -5
  109. data/lib/hecks/bluebook/meta_validator.rb +70 -70
  110. data/lib/hecks/bluebook/model_check.rb +170 -84
  111. data/lib/hecks/bluebook/pattern_subset.rb +9 -9
  112. data/lib/hecks/bluebook/policy.rb +15 -13
  113. data/lib/hecks/bluebook/process_manager.rb +14 -14
  114. data/lib/hecks/bluebook/project_register.rb +6 -6
  115. data/lib/hecks/bluebook/query.rb +4 -4
  116. data/lib/hecks/bluebook/read_model.rb +14 -14
  117. data/lib/hecks/bluebook/reference.rb +8 -8
  118. data/lib/hecks/bluebook/smoke_test.rb +19 -19
  119. data/lib/hecks/bluebook/synthesizer.rb +12 -12
  120. data/lib/hecks/bluebook/translation.rb +4 -4
  121. data/lib/hecks/bluebook/value_object.rb +6 -6
  122. data/lib/hecks/bluebook.rb +2 -2
  123. data/lib/hecks/codemod/legacy_dispatch_args.rb +299 -0
  124. data/lib/hecks/codemod/legacy_dispatch_recorder.rb +186 -0
  125. data/lib/hecks/codemod.rb +36 -35
  126. data/lib/hecks/construct.rb +6 -6
  127. data/lib/hecks/corpus.rb +317 -0
  128. data/lib/hecks/deprecation.rb +95 -0
  129. data/lib/hecks/doc/reference.rb +19 -19
  130. data/lib/hecks/embryonaut_bluebook.rb +11 -11
  131. data/lib/hecks/facade/cli_door.rb +69 -10
  132. data/lib/hecks/facade/cli_runner.rb +105 -24
  133. data/lib/hecks/facade/command_request.rb +23 -0
  134. data/lib/hecks/facade/handle.rb +79 -32
  135. data/lib/hecks/facade/json_door.rb +106 -25
  136. data/lib/hecks/facade/surface/aggregate_door.rb +42 -27
  137. data/lib/hecks/facade/surface/chapter.rb +26 -17
  138. data/lib/hecks/facade/surface.rb +16 -3
  139. data/lib/hecks/facade.rb +15 -4
  140. data/lib/hecks/forms/app.rb +46 -30
  141. data/lib/hecks/forms/command_form_renderer.rb +70 -9
  142. data/lib/hecks/forms/field_renderer.rb +142 -6
  143. data/lib/hecks/forms/field_shape.rb +180 -19
  144. data/lib/hecks/forms/html.rb +51 -7
  145. data/lib/hecks/forms/index_renderer.rb +14 -2
  146. data/lib/hecks/forms/params.rb +120 -23
  147. data/lib/hecks/forms/port_argument.rb +2 -2
  148. data/lib/hecks/forms/query_form_renderer.rb +2 -2
  149. data/lib/hecks/forms/record_renderer.rb +2 -2
  150. data/lib/hecks/forms/record_table.rb +1 -1
  151. data/lib/hecks/forms/value_object_shape.rb +3 -3
  152. data/lib/hecks/forms.rb +24 -4
  153. data/lib/hecks/fqn.rb +1 -1
  154. data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
  155. data/lib/hecks/framework.rb +48 -17
  156. data/lib/hecks/freezer.rb +11 -11
  157. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +90 -90
  158. data/lib/hecks/fuzzing/combination_miner.rb +119 -0
  159. data/lib/hecks/fuzzing/concurrent_dispatch.rb +140 -45
  160. data/lib/hecks/fuzzing/coverage_campaign.rb +118 -0
  161. data/lib/hecks/fuzzing/differential.rb +158 -0
  162. data/lib/hecks/fuzzing/domain_generator.rb +694 -0
  163. data/lib/hecks/fuzzing/era_boundary.rb +37 -18
  164. data/lib/hecks/fuzzing/form_census.rb +35 -20
  165. data/lib/hecks/fuzzing/generated_domain_check.rb +95 -0
  166. data/lib/hecks/fuzzing/invalid_value_generator.rb +6 -6
  167. data/lib/hecks/fuzzing/isolated_boot.rb +62 -62
  168. data/lib/hecks/fuzzing/nondeterministic.rb +67 -0
  169. data/lib/hecks/fuzzing/persistence_parity.rb +19 -21
  170. data/lib/hecks/fuzzing/properties/corrections.rb +9 -9
  171. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +154 -49
  172. data/lib/hecks/fuzzing/properties/guards.rb +42 -42
  173. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +32 -32
  174. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +22 -35
  175. data/lib/hecks/fuzzing/properties/outbox.rb +24 -24
  176. data/lib/hecks/fuzzing/properties/querying.rb +17 -17
  177. data/lib/hecks/fuzzing/properties.rb +64 -40
  178. data/lib/hecks/fuzzing/qa_settings.rb +152 -0
  179. data/lib/hecks/fuzzing/replay.rb +114 -114
  180. data/lib/hecks/fuzzing/rotation_priority.rb +10 -10
  181. data/lib/hecks/fuzzing/rust_gap_manifest.rb +113 -0
  182. data/lib/hecks/fuzzing/self_consistency.rb +141 -112
  183. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +35 -35
  184. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +18 -11
  185. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +13 -12
  186. data/lib/hecks/fuzzing/sequence_generator/picker.rb +21 -12
  187. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +58 -31
  188. data/lib/hecks/fuzzing/sequence_generator.rb +111 -23
  189. data/lib/hecks/fuzzing/shrinker.rb +197 -0
  190. data/lib/hecks/fuzzing/structural_skips.rb +23 -130
  191. data/lib/hecks/fuzzing/sweep_depth.rb +4 -4
  192. data/lib/hecks/fuzzing/target_capabilities.rb +49 -18
  193. data/lib/hecks/fuzzing/value_generator.rb +14 -14
  194. data/lib/hecks/fuzzing.rb +5 -0
  195. data/lib/hecks/grammar/evolve.rb +10 -10
  196. data/lib/hecks/grammar.rb +7 -7
  197. data/lib/hecks/ir.rb +13 -13
  198. data/lib/hecks/language/bluebook/bluebook.bluebook +41 -0
  199. data/lib/hecks/language/bluebook/policy.bluebook +11 -1
  200. data/lib/hecks/language/bluebook/vocabulary.bluebook +348 -13
  201. data/lib/hecks/language/oidc.json +5 -0
  202. data/lib/hecks/literal.rb +9 -9
  203. data/lib/hecks/naming.rb +25 -25
  204. data/lib/hecks/ports/access_control.rb +58 -2
  205. data/lib/hecks/ports/agent/answers.rb +83 -6
  206. data/lib/hecks/ports/agent.rb +119 -35
  207. data/lib/hecks/ports/authentication.rb +44 -4
  208. data/lib/hecks/ports/authorization.rb +53 -11
  209. data/lib/hecks/ports/clock.rb +42 -23
  210. data/lib/hecks/ports/extraction.rb +16 -0
  211. data/lib/hecks/ports/identity_assignment.rb +24 -2
  212. data/lib/hecks/ports/identity_generation.rb +17 -3
  213. data/lib/hecks/ports/identity_resolution.rb +18 -1
  214. data/lib/hecks/ports/loading.rb +4 -0
  215. data/lib/hecks/ports/persistence/append_only.rb +172 -8
  216. data/lib/hecks/ports/persistence/binding_policy.rb +34 -0
  217. data/lib/hecks/ports/persistence/codec_boundary.rb +178 -0
  218. data/lib/hecks/ports/persistence/execution.rb +4 -0
  219. data/lib/hecks/ports/persistence/null_saga_store.rb +12 -1
  220. data/lib/hecks/ports/persistence/plugin.rb +42 -4
  221. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +218 -25
  222. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +77 -9
  223. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +81 -24
  224. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +29 -18
  225. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +144 -60
  226. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +103 -8
  227. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +98 -23
  228. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +282 -109
  229. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +63 -25
  230. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +118 -66
  231. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +51 -28
  232. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +28 -5
  233. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +25 -12
  234. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +129 -34
  235. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +51 -6
  236. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +33 -9
  237. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +16 -0
  238. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +57 -4
  239. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +25 -2
  240. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +314 -90
  241. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +68 -10
  242. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +9 -3
  243. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +9 -2
  244. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +42 -8
  245. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +16 -1
  246. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +36 -5
  247. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +23 -3
  248. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +18 -19
  249. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +5 -5
  250. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +1 -1
  251. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -2
  252. data/lib/hecks/ports/persistence/plugins/era.rb +12 -2
  253. data/lib/hecks/ports/persistence/remote_runtime.rb +9 -2
  254. data/lib/hecks/ports/persistence/repository_factory.rb +23 -3
  255. data/lib/hecks/ports/persistence/state_codec.rb +319 -0
  256. data/lib/hecks/ports/persistence.rb +36 -1
  257. data/lib/hecks/ports/projection.rb +61 -7
  258. data/lib/hecks/ports/query/in_memory.rb +3 -3
  259. data/lib/hecks/ports/query/ordering.rb +6 -6
  260. data/lib/hecks/ports/query.rb +35 -0
  261. data/lib/hecks/projections/bootstrap_table.rb +112 -0
  262. data/lib/hecks/projections/diagrams.rb +75 -75
  263. data/lib/hecks/projections/glossary/html.rb +3 -3
  264. data/lib/hecks/projections/glossary/markdown.rb +3 -3
  265. data/lib/hecks/projections/glossary/mermaid.rb +1 -1
  266. data/lib/hecks/projections/glossary/sections.rb +1 -1
  267. data/lib/hecks/projections/glossary/sentences.rb +5 -5
  268. data/lib/hecks/projections/glossary.rb +15 -15
  269. data/lib/hecks/projections/ir.rb +1 -1
  270. data/lib/hecks/projections/model/deviations.rb +18 -17
  271. data/lib/hecks/projections/model.rb +25 -21
  272. data/lib/hecks/projections/oidc.rb +7 -7
  273. data/lib/hecks/projections/parser_table.rb +5 -5
  274. data/lib/hecks/projections/reference.rb +3 -3
  275. data/lib/hecks/projections/rust_vocabulary.rb +443 -0
  276. data/lib/hecks/projections/shape.rb +2 -2
  277. data/lib/hecks/projections/statements.rb +11 -11
  278. data/lib/hecks/projections/vocabulary.rb +9 -9
  279. data/lib/hecks/projections.rb +5 -3
  280. data/lib/hecks/projector/cli_projector.rb +29 -29
  281. data/lib/hecks/projector/docs_projector.rb +13 -13
  282. data/lib/hecks/projector/exporter.rb +42 -21
  283. data/lib/hecks/projector/ir_projector.rb +1 -1
  284. data/lib/hecks/projector/narrate_projector.rb +11 -11
  285. data/lib/hecks/projector/target.rb +13 -13
  286. data/lib/hecks/projector.rb +15 -15
  287. data/lib/hecks/query_ir.rb +47 -47
  288. data/lib/hecks/query_specification/common/comparators.rb +19 -3
  289. data/lib/hecks/query_specification/common/comparison.rb +109 -27
  290. data/lib/hecks/query_specification/common/dsl.rb +65 -9
  291. data/lib/hecks/query_specification/common/null_policy.rb +57 -13
  292. data/lib/hecks/query_specification/common/null_semantics.rb +4 -0
  293. data/lib/hecks/query_specification/common/options.rb +25 -0
  294. data/lib/hecks/query_specification/field_path.rb +69 -15
  295. data/lib/hecks/query_specification/hop_path.rb +57 -20
  296. data/lib/hecks/query_specification/read_model/specification.rb +4 -0
  297. data/lib/hecks/rendering.rb +3 -3
  298. data/lib/hecks/router/namespace_installer.rb +3 -3
  299. data/lib/hecks/router.rb +1 -1
  300. data/lib/hecks/runtime/aggregate_lock.rb +11 -11
  301. data/lib/hecks/runtime/boot_gates.rb +3 -3
  302. data/lib/hecks/runtime/caller.rb +8 -8
  303. data/lib/hecks/runtime/capability_graph.rb +2 -2
  304. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +22 -25
  305. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +42 -63
  306. data/lib/hecks/runtime/command_interpreter.rb +159 -102
  307. data/lib/hecks/runtime/command_rules/admissibility.rb +69 -69
  308. data/lib/hecks/runtime/command_rules/arithmetic.rb +137 -91
  309. data/lib/hecks/runtime/command_rules/authorization.rb +38 -17
  310. data/lib/hecks/runtime/command_rules/emission.rb +18 -1
  311. data/lib/hecks/runtime/command_rules/references.rb +35 -35
  312. data/lib/hecks/runtime/command_rules.rb +3 -0
  313. data/lib/hecks/runtime/dependency_planning.rb +11 -11
  314. data/lib/hecks/runtime/dispatcher.rb +283 -90
  315. data/lib/hecks/runtime/entity_element.rb +151 -67
  316. data/lib/hecks/runtime/entity_interpreter.rb +108 -82
  317. data/lib/hecks/runtime/errors.rb +19 -19
  318. data/lib/hecks/runtime/event.rb +6 -6
  319. data/lib/hecks/runtime/identity.rb +22 -22
  320. data/lib/hecks/runtime/instance.rb +39 -14
  321. data/lib/hecks/runtime/interpreting.rb +12 -12
  322. data/lib/hecks/runtime/invocation.rb +276 -0
  323. data/lib/hecks/runtime/loader.rb +14 -14
  324. data/lib/hecks/runtime/outbox.rb +23 -23
  325. data/lib/hecks/runtime/policy_interpreter.rb +54 -54
  326. data/lib/hecks/runtime/port_operation_interpreter.rb +22 -19
  327. data/lib/hecks/runtime/query_interpreter.rb +55 -64
  328. data/lib/hecks/runtime/reaction_invocation.rb +20 -20
  329. data/lib/hecks/runtime/read_model_interpreter.rb +40 -40
  330. data/lib/hecks/runtime/rebuild_sweep.rb +4 -4
  331. data/lib/hecks/runtime/reference_hop.rb +6 -6
  332. data/lib/hecks/runtime/refusal_wording.rb +92 -115
  333. data/lib/hecks/runtime/registry/saga_persistence.rb +21 -21
  334. data/lib/hecks/runtime/registry/verification.rb +36 -26
  335. data/lib/hecks/runtime/registry.rb +45 -28
  336. data/lib/hecks/runtime/remote_dispatcher.rb +38 -23
  337. data/lib/hecks/runtime/routing.rb +10 -153
  338. data/lib/hecks/runtime/saga_interpreter/correlation.rb +17 -17
  339. data/lib/hecks/runtime/saga_interpreter.rb +64 -64
  340. data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
  341. data/lib/hecks/runtime/tenant_check.rb +9 -9
  342. data/lib/hecks/runtime/tenant_scope.rb +5 -5
  343. data/lib/hecks/runtime/value/admission.rb +75 -30
  344. data/lib/hecks/runtime/value/coercion.rb +318 -127
  345. data/lib/hecks/runtime/value/entity_list_coercion.rb +34 -34
  346. data/lib/hecks/runtime/value.rb +21 -21
  347. data/lib/hecks/runtime.rb +7 -7
  348. data/lib/hecks/storehouse.rb +64 -64
  349. data/lib/hecks/version.rb +3 -3
  350. data/lib/hecks/vocabulary.rb +205 -4
  351. data/lib/hecks.rb +13 -11
  352. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +11 -11
  353. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +14 -12
  354. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +11 -11
  355. metadata +22 -2
@@ -4,30 +4,30 @@ require "securerandom"
4
4
 
5
5
  module Hecks
6
6
  module Fuzzing
7
- # A FRESH, IN-PROCESS ADAPTER FOR EVERY EPHEMERAL BOOT.
7
+ # A fresh, in-process adapter for every ephemeral boot.
8
8
  #
9
9
  # Fuzzing/replay copies a domain to a tmpdir and boots from there
10
10
  # specifically to get zero-history state — `rm_rf`ing the copy's own
11
11
  # `data/` achieves that for a file-based adapter (Memory,
12
- # SqlitePersistence, Heki), because copying the DIRECTORY copies the
12
+ # SqlitePersistence, Heki), because copying the directory copies the
13
13
  # store. It achieves nothing for an adapter that lives outside the
14
14
  # copied directory entirely — Postgres, named by a fixed connection
15
15
  # string in `.world` (examples/pizzas/bluebook/pizzas.world, for
16
16
  # instance). Copying the directory does not copy or isolate the
17
- # DATABASE, so a "fresh" boot against a Postgres-bound domain would
17
+ # database, so a "fresh" boot against a Postgres-bound domain would
18
18
  # still see every record any other run, ever, wrote to it.
19
19
  #
20
20
  # So every `.hecksagon` in the copy gets its persistence binding
21
21
  # rewritten to Memory before booting, and any `projected_by` bind
22
22
  # dropped outright (optional — `Registry#read_repository` already
23
23
  # falls back to the authoritative repository when none exists). The
24
- # domain's own rules and shape are untouched ; only WHICH adapter this
24
+ # domain's own rules and shape are untouched ; only which adapter this
25
25
  # one ephemeral copy answers through changes. What the domain is
26
26
  # bound to for real deployment is never touched — only this tmp copy.
27
27
  #
28
28
  # `adapter:` (PRD 02) — Memory is the default and the only mode every
29
29
  # existing caller still gets with no change. `:sqlite` rebinds to the
30
- # REAL SQLite adapter instead of the in-memory one, for exactly the
30
+ # real SQLite adapter instead of the in-memory one, for exactly the
31
31
  # same reason PRD 02 exists: 15 declared properties (properties.rb)
32
32
  # and every fuzz/replay run has only ever been checked against
33
33
  # Memory's own hand-written repository, never against a real,
@@ -44,8 +44,8 @@ module Hecks
44
44
  # `root:`, which `Hecks.boot(copy)` passes as this ephemeral copy's
45
45
  # own directory — a fresh, empty `data/` per run, exactly like
46
46
  # Memory's own zero-history guarantee, just backed by a real SQLite
47
- # file instead of a Hash. STALE AS OF THIS PARAGRAPH'S ORIGINAL
48
- # WRITING — both `Postgres` (PRD 02, docs/prds/02-fuzzer-real-
47
+ # file instead of a Hash. Stale as of this paragraph's original
48
+ # writing — both `Postgres` (PRD 02, docs/prds/02-fuzzer-real-
49
49
  # adapters.md) and `PostgresEra` (this mode's own header, below,
50
50
  # `rebind_to_postgres_era!`) since gained real `adapter:` modes here.
51
51
  # Each writes its own fresh `.world` per `.hecksagon` rather than
@@ -54,7 +54,7 @@ module Hecks
54
54
  # connection safely" turned out not to be true: `:postgres` sources
55
55
  # one shared, permanent scratch database/schema this module itself
56
56
  # owns (see `FUZZ_POSTGRES_DATABASE`'s own header); `:postgres_era`
57
- # instead requires the CALLER to supply (and own the lifecycle of) its
57
+ # instead requires the caller to supply (and own the lifecycle of) its
58
58
  # own throwaway `database:`/`schema:`, since its only caller
59
59
  # (`bin/qa_sweep --persistence-parity`) already has to manage a
60
60
  # disposable database of its own, never a shared one this module could
@@ -62,7 +62,7 @@ module Hecks
62
62
  module IsolatedBoot
63
63
  module_function
64
64
 
65
- # PRD 02 (docs/future-features.md) — `adapter:` picks WHICH real
65
+ # PRD 02 (docs/future-features.md) — `adapter:` picks which real
66
66
  # persistence this one ephemeral boot answers through, not just
67
67
  # Memory. `:memory` is the original, zero-config behavior (every
68
68
  # other caller in this codebase that doesn't pass `adapter:` gets
@@ -75,7 +75,7 @@ module Hecks
75
75
  # so `bin/fuzz --adapter postgres` is meant to run with smaller
76
76
  # seed/step counts than the Memory default, not as a like-for-like
77
77
  # swap; see that flag's own comment.
78
- # `database:`/`schema:` are ONLY meaningful for `adapter: :postgres_era`
78
+ # `database:`/`schema:` are only meaningful for `adapter: :postgres_era`
79
79
  # — see `rebind_to_postgres_era!`'s own header for why that mode takes
80
80
  # caller-supplied connection identity instead of a hardcoded shared
81
81
  # constant the way `:postgres` does. Every other adapter ignores both;
@@ -100,8 +100,8 @@ module Hecks
100
100
  end
101
101
  end
102
102
 
103
- # SYMLINKS ARE FOLLOWED, NOT COPIED. `FileUtils.cp_r` reproduces a
104
- # symlink AS a symlink, and a RELATIVE one then points at nothing
103
+ # Symlinks are followed, not copied. `FileUtils.cp_r` reproduces a
104
+ # symlink as a symlink, and a relative one then points at nothing
105
105
  # from a tmpdir — `lib/hecks/framework/bluebook/compliance
106
106
  # .bluebook` is exactly that, a link to
107
107
  # `examples/compliance/bluebook/compliance.bluebook`, so the whole
@@ -109,7 +109,7 @@ module Hecks
109
109
  # path under /var/folders that had never existed. Nothing about the
110
110
  # domain was wrong; the copy was.
111
111
  #
112
- # `FileUtils.cp` follows a symlink and copies its CONTENT, which is
112
+ # `FileUtils.cp` follows a symlink and copies its content, which is
113
113
  # what an isolated boot wants: the copy has to stand alone, since
114
114
  # rebind! rewrites files in it and must not reach back through a
115
115
  # link into the real tree.
@@ -140,8 +140,8 @@ module Hecks
140
140
  rewrite_bindings!(copy, "Memory")
141
141
  strip_translations!(copy)
142
142
 
143
- # THE SETTINGS, NOT JUST THE BIND — `WorldBuilder#method_missing`
144
- # stores a settings block under BOTH "verb:adapter" and the bare
143
+ # **The settings, not just the bind** — `WorldBuilder#method_missing`
144
+ # stores a settings block under both "verb:adapter" and the bare
145
145
  # "verb" (world_builder.rb:32-33), so a bind rewritten to Memory
146
146
  # still falls back to whatever adapter's settings were declared
147
147
  # bare — Postgres's `database:`, which Memory does not take and
@@ -154,7 +154,7 @@ module Hecks
154
154
  Dir.glob(File.join(copy, "**", "*.world")).each { |path| File.delete(path) }
155
155
  end
156
156
 
157
- # SAME DANCE AS MEMORY, ONE ADAPTER OVER — `Adapters::Sqlite#
157
+ # Same dance as memory, one adapter over — `Adapters::Sqlite#
158
158
  # resolve_path` (adapters/driven/sqlite.rb) defaults to
159
159
  # `data/<table>.db` under the boot's own root when no `database`
160
160
  # setting is declared, which `data/` already being cleared makes a
@@ -168,24 +168,24 @@ module Hecks
168
168
  Dir.glob(File.join(copy, "**", "*.world")).each { |path| File.delete(path) }
169
169
  end
170
170
 
171
- # THE EXPENSIVE ONE — Postgres has no zero-config default the way
171
+ # **The expensive one** — Postgres has no zero-config default the way
172
172
  # Sqlite/Memory do (`Adapters::Postgres.connect_for` refuses outright
173
173
  # with no `database` setting), so dropping `.world` the way the
174
174
  # other two do would just move the WiringError from "wrong adapter"
175
175
  # to "no adapter." Every domain name this copy declares gets a
176
- # FRESH `.world` written for it instead — not a rewrite of whatever
176
+ # fresh `.world` written for it instead — not a rewrite of whatever
177
177
  # was there, a replacement, same reasoning `rewrite_bindings!`
178
178
  # already applies to `.hecksagon`: this ephemeral boot owns every
179
179
  # binding decision, nothing about a real deployment's own settings
180
180
  # is relevant or safe to half-preserve here.
181
181
  #
182
- # ONE SHARED SCHEMA, DROPPED AND RECREATED BEFORE EVERY BOOT — not a
182
+ # One shared schema, dropped and recreated before every boot — not a
183
183
  # fresh randomly-named one per call. `bin/fuzz` drives every
184
184
  # ephemeral boot sequentially (one `IsolatedBoot.call` fully exits
185
185
  # before the next begins — see that file's own single-threaded
186
186
  # `while` loop), so nothing is ever concurrent here ; a fresh name
187
187
  # every time would just leak schemas in `FUZZ_POSTGRES_DATABASE`
188
- # forever with nothing to ever drop them. If a caller ever DOES
188
+ # forever with nothing to ever drop them. If a caller ever does
189
189
  # start running fuzz adapters concurrently, this needs to move to a
190
190
  # process-unique schema name (`SecureRandom.hex` is already
191
191
  # `require`d here for exactly that day) — flagged, not solved,
@@ -199,11 +199,11 @@ module Hecks
199
199
  strip_translations!(copy)
200
200
  ensure_fuzz_schema!
201
201
 
202
- # ONE `.world` PER DIRECTORY A `.hecksagon` ACTUALLY LIVES IN, not
202
+ # One `.world` per directory a `.hecksagon` actually lives in, not
203
203
  # one at `copy`'s own root — `Folder#load_domain` resolves a
204
- # SINGLE `bluebook_directory` and globs `*.world` there, non-
204
+ # single `bluebook_directory` and globs `*.world` there, non-
205
205
  # recursively (`Folder#load_each`); a domain can hold several
206
- # `Hecks.hecksagon "<Name>" do ... end` SIBLING blocks in that one
206
+ # `Hecks.hecksagon "<Name>" do ... end` sibling blocks in that one
207
207
  # file (banking.hecksagon declares "Banking", "Governance", and
208
208
  # "Identity" together), so every name found in one `.hecksagon`
209
209
  # file gets bundled into one `.world` written beside it, not
@@ -225,7 +225,7 @@ module Hecks
225
225
  end.join("\n"))
226
226
  end
227
227
 
228
- # Any PRE-EXISTING `.world` this copy shipped with (a real
228
+ # Any pre-existing `.world` this copy shipped with (a real
229
229
  # deployment's own connection string) is now redundant with — and
230
230
  # would conflict with, `Registry#add_world`'s own header on
231
231
  # loading the same domain name twice — the fresh one just
@@ -236,20 +236,20 @@ module Hecks
236
236
  end
237
237
  end
238
238
 
239
- # ADMIN CONNECTION LIVES OUTSIDE THE TMP COPY ENTIRELY — same as
239
+ # Admin connection lives outside the tmp copy entirely — same as
240
240
  # every other real-Postgres spec in this repo (`support/
241
241
  # postgres_probe.rb`'s own header). Database created once per
242
242
  # process and remembered (`@fuzz_database_ready` on this module's
243
243
  # own singleton, the same memoization shape `PostgresProbe
244
244
  # .available?` already uses) ; the schema inside it is dropped and
245
- # recreated on EVERY call, which is what actually isolates one
245
+ # recreated on every call, which is what actually isolates one
246
246
  # ephemeral boot's data from the next.
247
247
  def ensure_fuzz_schema!
248
- # `Adapters::Postgres#initialize` opens ONE real `PG::Connection`
249
- # PER AGGREGATE and never explicitly closes it — fine for a
248
+ # `Adapters::Postgres#initialize` opens one real `PG::Connection`
249
+ # per aggregate and never explicitly closes it — fine for a
250
250
  # process that boots once and runs, exactly what every other
251
251
  # caller of this adapter is. A fuzz run boots dozens to hundreds
252
- # of EPHEMERAL times in one process (every seed does at least a
252
+ # of ephemeral times in one process (every seed does at least a
253
253
  # generate + a replay, `replay_is_deterministic` doubles that,
254
254
  # shrinking multiplies it further), and `PG::Connection` only
255
255
  # actually closes its socket when Ruby's GC finalizes the
@@ -261,7 +261,7 @@ module Hecks
261
261
  # ephemeral boots, `PG::ConnectionBad: ... "too many clients
262
262
  # already"`. A `GC.start` here — right before the next ephemeral
263
263
  # boot's connections open, not on some timer — reclaims every
264
- # connection the PREVIOUS boot's now-unreferenced adapters held,
264
+ # connection the previous boot's now-unreferenced adapters held,
265
265
  # keeping the live count bounded regardless of run length. This
266
266
  # is a real constraint on running Postgres in a loop, not
267
267
  # something to route around by connecting less carefully.
@@ -278,7 +278,7 @@ module Hecks
278
278
  end
279
279
 
280
280
  db = PG.connect(dbname: FUZZ_POSTGRES_DATABASE)
281
- # QUIET ON PURPOSE — same as `Adapters::Postgres.connect_for`'s
281
+ # **Quiet on purpose** — same as `Adapters::Postgres.connect_for`'s
282
282
  # own `SET client_min_messages`: a `DROP SCHEMA ... CASCADE` that
283
283
  # actually has something to drop (every boot after the first)
284
284
  # NOTICEs once per dropped object, which is the ordinary case
@@ -292,11 +292,11 @@ module Hecks
292
292
  db.close
293
293
  end
294
294
 
295
- # THE ADAPTER `:postgres` NEVER TOUCHES — `Postgres` and `PostgresEra`
296
- # are SIBLING, NOT interchangeable, adapters (see postgres_era.rb's
295
+ # The adapter `:postgres` never touches — `Postgres` and `PostgresEra`
296
+ # are sibling, not interchangeable, adapters (see postgres_era.rb's
297
297
  # own header: "the only one that declares the LINEAGE capability").
298
298
  # PRD 02 (docs/prds/02-fuzzer-real-adapters.md) shipped `:postgres`
299
- # and explicitly scoped `PostgresEra` OUT: "nothing here touches
299
+ # and explicitly scoped `PostgresEra` out: "nothing here touches
300
300
  # era/lineage machinery." That gap is real, not cosmetic —
301
301
  # `examples/directory` (a `compute`/`rekey` translation edge, the
302
302
  # one domain in this corpus that actually exercises PostgresEra-
@@ -305,20 +305,20 @@ module Hecks
305
305
  # `:memory` included, structurally cannot reach it. This mode closes
306
306
  # that — `bin/qa_sweep --persistence-parity` is its first caller.
307
307
  #
308
- # NO SHARED CONSTANT DATABASE, UNLIKE `:postgres` ABOVE — deliberate.
308
+ # No shared constant database, unlike `:postgres` above — deliberate.
309
309
  # `rebind_to_postgres!`'s own `FUZZ_POSTGRES_DATABASE`/`_SCHEMA` are
310
310
  # module-level constants because `bin/fuzz --adapter postgres` is a
311
311
  # general-purpose, run-it-anytime tool with no caller-tracked
312
312
  # lifecycle of its own. This mode's only caller
313
313
  # (`bin/qa_sweep --persistence-parity`) is different: it dispatches
314
314
  # through `QualityControl::Target.claim!`'s own cross-process lock
315
- # first (see that script's own header), so at most ONE sweep is ever
315
+ # first (see that script's own header), so at most one sweep is ever
316
316
  # touching a given target's own disposable database at a time — but
317
- # the caller, not this module, is what OWNS that database's name and
317
+ # the caller, not this module, is what owns that database's name and
318
318
  # lifecycle (created, and genuinely dropped, by the caller itself),
319
- # exactly the discipline `spec/qa_sweep_all_spec.rb`'s own header
319
+ # exactly the discipline `spec/support/qa_sweep_all_fixture.rb`'s own header
320
320
  # describes and this repository's persistence-parity work is
321
- # required to follow. Accepting `database:`/`schema:` as REQUIRED
321
+ # required to follow. Accepting `database:`/`schema:` as required
322
322
  # keyword arguments (never a fallback constant) is what keeps that
323
323
  # ownership from silently drifting back onto this module the way
324
324
  # `:postgres`'s own `FUZZ_POSTGRES_DATABASE` already has.
@@ -334,25 +334,25 @@ module Hecks
334
334
  rewrite_bindings!(copy, "PostgresEra")
335
335
  ensure_postgres_era_schema!(database: database, schema: schema)
336
336
 
337
- # SAME ONE-`.world`-PER-`.hecksagon`-DIRECTORY SHAPE `rebind_to_
337
+ # Same one-`.world`-per-`.hecksagon`-directory shape `rebind_to_
338
338
  # postgres!` already uses, for the identical reason (`Folder#
339
339
  # load_domain` globs `*.world` non-recursively) — see that
340
340
  # method's own comment on `world_path` above. `PostgresEra`
341
- # additionally takes `schema:` (postgres_era.rb's own "SHARED-
342
- # INSTANCE ISOLATION" comment): the caller-supplied throwaway
341
+ # additionally takes `schema:` (postgres_era.rb's own "shared-
342
+ # instance isolation" comment): the caller-supplied throwaway
343
343
  # schema is what actually isolates this one ephemeral boot from
344
344
  # the next, the same job `FUZZ_POSTGRES_SCHEMA` does for `:postgres`
345
345
  # — `connect_for` itself idempotently `CREATE SCHEMA IF NOT
346
- # EXISTS`s it, so this method only ever needs to DROP it first
346
+ # EXISTS`s it, so this method only ever needs to drop it first
347
347
  # (in `ensure_postgres_era_schema!`, below) for the zero-history
348
348
  # guarantee every other adapter mode already gives.
349
349
  #
350
- # `allow_superuser true` — ON THE RECORD, ON PURPOSE. A bare
350
+ # `allow_superuser true` — on the record, on purpose. A bare
351
351
  # `database` connects as the ambient Postgres user, and
352
352
  # PostgresEra refuses to boot at all when that user is a
353
353
  # superuser (its era write-fence is row-level security, which a
354
354
  # superuser walks through — `Lineage#check_fence_applies!`,
355
- # BUG#24). That refusal protects a REAL ledger from an old
355
+ # BUG#24). That refusal protects a real ledger from an old
356
356
  # checkout's stale writes; nothing here is one. This is an
357
357
  # ephemeral boot into a throwaway schema the caller itself
358
358
  # creates and drops, whose data no second checkout ever shares,
@@ -386,18 +386,18 @@ module Hecks
386
386
  end
387
387
  end
388
388
 
389
- # THE ZERO-HISTORY GUARANTEE FOR THIS MODE — `DROP SCHEMA ... CASCADE`
389
+ # The zero-history guarantee for this mode — `DROP SCHEMA ... CASCADE`
390
390
  # before every ephemeral boot, mirroring `ensure_fuzz_schema!` above
391
391
  # (same `GC.start`-before-connecting fix for the identical
392
392
  # `max_connections` exhaustion that method's own comment documents —
393
393
  # `PostgresEra` opens real `PG::Connection`s exactly like `Postgres`
394
- # does, same unclosed-until-GC'd lifetime). The DATABASE itself is
394
+ # does, same unclosed-until-GC'd lifetime). The database itself is
395
395
  # created here too, idempotently (`CREATE DATABASE IF NOT EXISTS`
396
396
  # has no Postgres spelling, hence the existence check) — but never
397
- # DROPPED here: this module creates it once per process because
397
+ # dropped here: this module creates it once per process because
398
398
  # `Hecks.boot` needs it to exist before `PostgresEra.connect_for`'s
399
399
  # own `PG.connect(dbname: ...)` can succeed at all, but dropping it
400
- # again is the CALLER's own job (its name and lifecycle belong to
400
+ # again is the caller's own job (its name and lifecycle belong to
401
401
  # the caller — see `rebind_to_postgres_era!`'s own header), not
402
402
  # something this per-ephemeral-boot helper should ever do mid-sweep.
403
403
  def ensure_postgres_era_schema!(database:, schema:)
@@ -417,7 +417,7 @@ module Hecks
417
417
  db.close
418
418
  end
419
419
 
420
- # THE SHARED REWRITE — factored out of `rebind_to_memory!` when
420
+ # The shared rewrite — factored out of `rebind_to_memory!` when
421
421
  # Sqlite/Postgres modes needed the identical `.hecksagon` surgery
422
422
  # with only the target adapter name differing. `persisted_by`/
423
423
  # `projected_by` can be spelled two ways: aggregate-scoped
@@ -431,41 +431,41 @@ module Hecks
431
431
  # #read_repository` already falls back to the authoritative
432
432
  # repository when none exists, so a read model this ephemeral copy
433
433
  # never wires is simply unread, not broken.
434
- # A `compute`/`rekey` TRANSLATION EDGE REFUSES TO BOOT AT ALL UNDER
435
- # ANY NON-LINEAGE-CAPABLE ADAPTER — found live, wiring this very
434
+ # A `compute`/`rekey` translation edge refuses to boot at all under
435
+ # any non-lineage-capable adapter — found live, wiring this very
436
436
  # mode up against `examples/directory`: `Runtime::EraCheck
437
- # .check_compute_rules!` (era_check.rb) runs UNCONDITIONALLY for
437
+ # .check_compute_rules!` (era_check.rb) runs unconditionally for
438
438
  # every loaded bluebook once the era plugin is loaded at all
439
439
  # (`bin/qa_sweep`'s own top-of-file `require "hecks/ports/
440
440
  # persistence/plugins/era"`, needed for the ledger's own
441
441
  # PostgresEra-bound aggregates), and refuses outright — "compute
442
- # rules require the Postgres adapter" — for ANY aggregate whose
443
- # lineage carries a `compute` rule and whose BOUND adapter is not
442
+ # rules require the Postgres adapter" — for any aggregate whose
443
+ # lineage carries a `compute` rule and whose bound adapter is not
444
444
  # lineage-capable. `PostgresEra` is the only adapter that answers
445
445
  # `lineage_capable? == true` (postgres_era.rb's own `self.
446
- # lineage_capable? = true`) — plain `Postgres` does NOT, so this
446
+ # lineage_capable? = true`) — plain `Postgres` does not, so this
447
447
  # refusal was already real for `:postgres`/`:sqlite`/`:memory`
448
448
  # alike, for any domain with a translation edge, before this
449
449
  # mode's own `:postgres_era` ever existed. This is very likely the
450
- # MECHANICAL reason `examples/directory` had to be shelved out of
450
+ # mechanical reason `examples/directory` had to be shelved out of
451
451
  # `hecks_qa`'s own rotation in the first place — not merely "less
452
- # interesting to fuzz on Memory," but "cannot BOOT on Memory at
452
+ # interesting to fuzz on Memory," but "cannot boot on Memory at
453
453
  # all" once the era plugin is loaded, which every real
454
454
  # `bin/qa_sweep` invocation already does.
455
455
  #
456
- # THE FIX IS TO DROP THE EDGE, NOT TO CHASE THE REFUSAL — an
456
+ # The fix is to drop the edge, not to chase the refusal — an
457
457
  # ephemeral, zero-history replay boot (every mode `IsolatedBoot`
458
458
  # offers) never has a pre-existing era-1 row to translate in the
459
- # first place, so the translation edge is IRRELEVANT to anything a
459
+ # first place, so the translation edge is irrelevant to anything a
460
460
  # fuzz/replay run actually exercises (ordinary command dispatch
461
- # against a fresh boot) — it only ever matters at MINT time,
461
+ # against a fresh boot) — it only ever matters at mint time,
462
462
  # against a real, pre-existing database
463
463
  # (`PostgresEra::LineageManager.check!`, a wholly separate,
464
464
  # human-approved path this harness was never meant to reach).
465
465
  # Dropping it here is exactly the same move `rebind_to_memory!`
466
466
  # already makes for `.world` (irrelevant/conflicting settings for
467
467
  # an ephemeral boot, deleted outright) — never called for
468
- # `:postgres_era` itself, where the bound adapter genuinely IS
468
+ # `:postgres_era` itself, where the bound adapter genuinely is
469
469
  # lineage-capable and the edge causes no refusal to begin with.
470
470
  def strip_translations!(copy)
471
471
  Dir.glob(File.join(copy, "**", "translations", "*.bluebook")).each { |path| File.delete(path) }
@@ -0,0 +1,67 @@
1
+ module Hecks
2
+ module Fuzzing
3
+ # The one declared set of FIELDS that leave a comparison — partition,
4
+ # not filter. Every comparison in lib/hecks/fuzzing that drops a field
5
+ # before comparing two histories names a group here instead of writing
6
+ # its own literal `except(...)` list, so the reason for dropping a field
7
+ # is written exactly once, beside the field.
8
+ #
9
+ # Two specs hold this set honest (spec/fuzzing/nondeterministic_spec.rb):
10
+ # a literal except-list naming any of these fields anywhere under
11
+ # lib/hecks/fuzzing fails the build (the tolerance must be declared
12
+ # here, not re-derived at a call site), and every declared field must
13
+ # actually be produced where its group says — a field nothing emits any
14
+ # more is a stale tolerance and fails too.
15
+ #
16
+ # Groups are keyed by the shape the field rides on, since the same name
17
+ # can be compared on one surface and dropped on another:
18
+ #
19
+ # - `query_row` — one entry of `Replay.call`'s `:queries`.
20
+ # - `outbox_row` — one row of an `:outbox_traces` entry's `:rows`.
21
+ # - `event` — an event hash in full `Event#to_h` form (an outbox
22
+ # row's `:event`, or a Rust binary's `events` entries,
23
+ # string-keyed there).
24
+ # - `history` — the top-level `Replay.call` history hash itself.
25
+ module Nondeterministic
26
+ FIELDS = {
27
+ query_row: {
28
+ instances_at: "a full state snapshot taken for the query oracle's own use " \
29
+ "(Properties::Querying); the same state is already compared as `instances`, " \
30
+ "so keeping it would re-report one instances divergence under every query step"
31
+ }.freeze,
32
+ outbox_row: {
33
+ event_uid: "`Runtime::Outbox::Fanout#rows_for`'s own SecureRandom.uuid, minted fresh per " \
34
+ "enqueue and kept off `Event#to_h` — relay bookkeeping, never something the " \
35
+ "domain produced, so two otherwise-identical replays carry two different uuids",
36
+ delivery_id: "`\"\#{event_uid}/\#{consumer}\"` — inherits event_uid's per-enqueue uuid"
37
+ }.freeze,
38
+ event: {
39
+ occurred_at: "a wall-clock read; never reproducible byte-for-byte between two independent " \
40
+ "runs (Replay's own projected `:events` leave it off for the same reason)"
41
+ }.freeze,
42
+ history: {
43
+ bluebook: "a live IR object (the first-loaded bluebook) handed to properties — an object " \
44
+ "identity, not a value, so two boots never compare equal on it",
45
+ bluebooks: "the full live IR map, keyed by domain — object identities, same as `bluebook`"
46
+ }.freeze
47
+ }.freeze
48
+
49
+ module_function
50
+
51
+ def names(group)
52
+ FIELDS.fetch(group).keys
53
+ end
54
+
55
+ # Every declared name, across every group.
56
+ def all_names
57
+ FIELDS.values.flat_map(&:keys).uniq
58
+ end
59
+
60
+ # `hash` without `group`'s fields — symbol keys, the shape every
61
+ # Ruby-side history carries.
62
+ def strip(hash, group)
63
+ hash.except(*names(group))
64
+ end
65
+ end
66
+ end
67
+ end
@@ -1,16 +1,17 @@
1
1
  require "json"
2
2
  require_relative "replay"
3
+ require_relative "nondeterministic"
3
4
 
4
5
  module Hecks
5
6
  module Fuzzing
6
- # THE SECOND DIFFERENTIAL AXIS — bin/qa_sweep's own `diff_ruby_vs_rust`
7
- # compares two DIFFERENT ENGINES (the Ruby interpreter vs the compiled
8
- # Rust kernel) against the SAME persistence (Memory, always — see
9
- # `SequenceGenerator`'s own header: sequence GENERATION stays
7
+ # **The second differential axis** — bin/qa_sweep's own `diff_ruby_vs_rust`
8
+ # compares two different engines (the Ruby interpreter vs the compiled
9
+ # Rust kernel) against the same persistence (Memory, always — see
10
+ # `SequenceGenerator`'s own header: sequence generation stays
10
11
  # Memory-only, and `IsolatedBoot`'s own header explains why every
11
12
  # existing fuzz/replay path structurally cannot reach a real Postgres-
12
13
  # bound domain's own SQL compilation). This module compares the
13
- # opposite pairing: the SAME ONE Ruby engine, against two DIFFERENT
14
+ # opposite pairing: the same one Ruby engine, against two different
14
15
  # persistences — Memory (the reference, exactly as fast and as
15
16
  # deterministic as every other fuzz check) and a real, disposable
16
17
  # `PostgresEra` database (the actual SQL compute/rekey/query-pushdown
@@ -26,20 +27,20 @@ module Hecks
26
27
  # Memory, unconditionally, no matter what `directory.world` itself
27
28
  # declares.
28
29
  #
29
- # NOT a replacement for `bin/qa_sweep`'s own Ruby-vs-Rust differential
30
+ # Not a replacement for `bin/qa_sweep`'s own Ruby-vs-Rust differential
30
31
  # mode — a genuinely separate axis, opt-in (`--persistence-parity`),
31
32
  # because this one pays for a real `PG.connect` and real SQL per
32
33
  # dispatch where Memory-vs-Rust pays for neither. See `bin/qa_sweep`'s
33
34
  # own `--persistence-parity` handling for the seed-count dial that
34
35
  # keeps that cost bounded.
35
36
  #
36
- # GENERALIZED TO `left:`/`right:` — originally hardcoded to Memory vs
37
+ # Generalized to `left:`/`right:` — originally hardcoded to Memory vs
37
38
  # PostgresEra (the only pairing that existed), now any two of
38
39
  # `IsolatedBoot`'s own adapter symbols (`:memory`, `:sqlite`,
39
40
  # `:postgres`, `:postgres_era`). Defaults preserve the original
40
41
  # pairing exactly, so every existing caller (this file's own spec,
41
42
  # `bin/qa_sweep`'s `--persistence-parity`) is unchanged. The second
42
- # pairing this generalization exists FOR is Memory vs SQLite
43
+ # pairing this generalization exists for is Memory vs SQLite
43
44
  # (`QualityControlDials::ADAPTER_PARITY_PAIRS`, `bin/qa_sweep`'s own
44
45
  # `adapter_parity_sqlite` mode) — `:sqlite` is nearly as cheap as
45
46
  # Memory itself (`IsolatedBoot#rebind_to_sqlite!`'s own header: an
@@ -48,7 +49,7 @@ module Hecks
48
49
  # loop instead of needing a deferred wave of its own the way
49
50
  # PostgresEra does.
50
51
  #
51
- # `database:`/`schema:` — REQUIRED only when `:postgres_era` is one of
52
+ # `database:`/`schema:` — required only when `:postgres_era` is one of
52
53
  # the two adapters (the caller — today, only `bin/qa_sweep` — owns the
53
54
  # disposable database's whole lifecycle: created before the sweep,
54
55
  # dropped after — see that script's own comment, and the discipline
@@ -58,21 +59,21 @@ module Hecks
58
59
  module PersistenceParity
59
60
  module_function
60
61
 
61
- # THE SAME SIX FIELDS `bin/qa_sweep`'s own `diff_ruby_vs_rust`
62
+ # The same six fields `bin/qa_sweep`'s own `diff_ruby_vs_rust`
62
63
  # compares (its own comment: "instances, events, refusals, queries,
63
64
  # sagas, reactions") — deliberately the identical set, so a report
64
65
  # this mode produces reads exactly like the sibling mode's own,
65
66
  # differing only in which two things were compared, not in what
66
67
  # "found something" means.
67
68
  #
68
- # SIMPLER NORMALIZATION THAN `diff_ruby_vs_rust`, on purpose — that
69
- # method reduces Rust's OWN JSON-over-stdout output to "wire
69
+ # Simpler normalization than `diff_ruby_vs_rust`, on purpose — that
70
+ # method reduces Rust's own JSON-over-stdout output to "wire
70
71
  # precision" and filters known Ruby/Rust structural gaps, because
71
72
  # it is comparing two genuinely different engines that are allowed
72
73
  # to differ in already-catalogued, understood ways. Both sides here
73
- # are the SAME Ruby engine (`Replay.call`, called twice, adapter
74
+ # are the same Ruby engine (`Replay.call`, called twice, adapter
74
75
  # only) — there is no second engine's own known-gap catalogue to
75
- # filter against, so any real difference IS the finding. Both
76
+ # filter against, so any real difference is the finding. Both
76
77
  # results still round-trip through `JSON.generate`/`JSON.parse`
77
78
  # before comparing, matching `diff_ruby_vs_rust`'s own discipline —
78
79
  # not because either side needs a wire-format reduction, but so
@@ -128,14 +129,11 @@ module Hecks
128
129
  [{ field: "refusals", left => l, right => r }]
129
130
  end
130
131
 
131
- # `instances_at:` dropped from every entry — the same reason
132
- # `diff_ruby_vs_rust` excludes it (`row.except(:instances_at)`):
133
- # it is a full state snapshot taken for the QUERY oracle's own use,
134
- # already covered by `diff_instances` above, and would make every
135
- # query-step entry re-litigate the SAME instances divergence a
136
- # second time under a different field name.
132
+ # `Nondeterministic`'s `query_row` group dropped from every entry —
133
+ # the same group `Differential.diff` drops, for the reason declared
134
+ # there (already covered by `diff_instances` above).
137
135
  def diff_queries(left_result, right_result, left, right)
138
- strip = ->(rows) { rows.map { |row| row.except(:instances_at) } }
136
+ strip = ->(rows) { rows.map { |row| Nondeterministic.strip(row, :query_row) } }
139
137
  l = as_json(strip.call(left_result[:queries]))
140
138
  r = as_json(strip.call(right_result[:queries]))
141
139
  return [] if l == r
@@ -1,7 +1,7 @@
1
1
  module Hecks
2
2
  module Fuzzing
3
3
  module Properties
4
- # ANGLE-9 — `corrects` (retroactive correction) had exactly one
4
+ # Angle-9 — `corrects` (retroactive correction) had exactly one
5
5
  # declaration anywhere in the corpus (`examples/banking/bluebook/
6
6
  # deposit_accounts.bluebook:353`, aggregate-level) and no property
7
7
  # anywhere in this file ever checked it, and no `FEATURE_COVERAGE`
@@ -9,7 +9,7 @@ module Hecks
9
9
  # stress_domains/corrections` gives it its first real coverage; see
10
10
  # that domain's own NOTES.md for what it found (an entity-level
11
11
  # `corrects` crashes Ruby at dispatch outright, and Rust's own
12
- # generated code has NO admissibility check for it AT ALL — neither
12
+ # generated code has no admissibility check for it at all — neither
13
13
  # engine can be compared on the untested combination this property
14
14
  # was written to watch, which is itself the headline finding).
15
15
  module Corrections
@@ -19,30 +19,30 @@ module Hecks
19
19
  # entities`, below, is the same recursive walk `SequenceGenerator::
20
20
  # Catalog#each_entity_chain` already uses, for the identical reason:
21
21
  # `Aggregate#entities`/`Entity#entities` nest, ADR 0026, S17). For
22
- # every such command, every event THIS history actually recorded
22
+ # every such command, every event this history actually recorded
23
23
  # under one of the command's own `emits` names must have an event
24
24
  # named by the command's own `corrects` target — same aggregate-
25
- # qualified name, same id — appearing STRICTLY EARLIER in the same
25
+ # qualified name, same id — appearing strictly earlier in the same
26
26
  # history.
27
27
  #
28
28
  # `history[:events]` is already in occurrence order (`Replay.call`'s
29
29
  # own `runtime.events`, appended as each step dispatches) — "earlier"
30
30
  # is therefore "earlier in this array," no timestamp comparison
31
31
  # needed, and no per-step attribution back to which command produced
32
- # which event is needed either: an event's own declared NAME already
32
+ # which event is needed either: an event's own declared name already
33
33
  # identifies the one command in its aggregate that can produce it
34
34
  # (`AggregateBuilder::Sealing#seal_correction_targets`'s own
35
35
  # `emitted_by` hash reads the identical fact, one level shallower).
36
36
  #
37
- # WHY THIS CANNOT BE `GUARANTEED_BY_CONSTRUCTION` THE WAY THE
38
- # AGGREGATE-LEVEL CASE ALMOST IS: `CommandRules::Admissibility#
37
+ # Why this cannot be `GUARANTEED_BY_CONSTRUCTION` the way the
38
+ # aggregate-level case almost is: `CommandRules::Admissibility#
39
39
  # enforce_correction_target` (the dispatch-time check) and
40
40
  # `AggregateBuilder::Sealing#seal_correction_targets` (the build-time
41
- # check) both exist ONLY for an aggregate-level `corrects` —
41
+ # check) both exist only for an aggregate-level `corrects` —
42
42
  # `EntityInterpreter#step_enforce_givens` never calls the former at
43
43
  # all, and the latter walks only `@commands` (the aggregate's own
44
44
  # top-level list), never `@entities`. An entity-level `corrects`
45
- # mutation is invisible to BOTH doors today — this property is the
45
+ # mutation is invisible to both doors today — this property is the
46
46
  # only thing anywhere, on either engine, that would ever catch one
47
47
  # going wrong.
48
48
  def corrections_reference_an_emitted_event(history)