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
@@ -14,32 +14,32 @@ require_relative "properties/outbox"
14
14
 
15
15
  module Hecks
16
16
  module Fuzzing
17
- # Declared properties, checked over a REPLAYED history — the other
17
+ # Declared properties, checked over a replayed history — the other
18
18
  # half of property-based testing the fuzzer was missing: it already
19
19
  # generates and (with bin/fuzz's shrinker) minimizes, but checked
20
20
  # nothing beyond "did the interpreter crash" and "did the replay
21
21
  # match the claim." A property here is a fact that should hold of
22
- # ANY history a valid domain produces, independent of which seed
22
+ # any history a valid domain produces, independent of which seed
23
23
  # produced it.
24
24
  #
25
25
  # Each property is `name => ->(history) { true/false, or a message
26
26
  # string naming what broke }` — a truthy return (including `true`)
27
- # is a pass; a String return is a failure, and the string IS the
27
+ # is a pass; a String return is a failure, and the string is the
28
28
  # finding. `history` is Replay's return shape.
29
29
  #
30
- # EVERY PROPERTY DECLARES THE LANGUAGE FEATURE IT COVERS, in
30
+ # Every property declares the language feature it covers, in
31
31
  # `FEATURE_COVERAGE` below — a "Construct#attribute" pair spelled
32
32
  # exactly as `Bluebook::MetaValidator.grammar_registry` names it,
33
- # the SAME meta-domain that judges every real bluebook (see that
33
+ # the same meta-domain that judges every real bluebook (see that
34
34
  # module's own header: "the language IS the source"). That is the
35
35
  # link this file exists to make real: a construct the language
36
36
  # declares is a fact `spec/meta_domain_coverage_spec.rb` can
37
37
  # enumerate on its own, without anyone re-typing the list here —
38
38
  # so a new attribute added to `language/bluebook/*.bluebook` shows
39
- # up in that spec as UNCLAIMED the moment it lands, not whenever
39
+ # up in that spec as unclaimed the moment it lands, not whenever
40
40
  # someone remembers to go looking. Claiming a feature here is a
41
41
  # deliberate act (a real property, checked at least once failing
42
- # AND once passing — `spec/fuzzing/properties_spec.rb`'s own
42
+ # and once passing — `spec/fuzzing/properties_spec.rb`'s own
43
43
  # discipline) or an explicit, reasoned exemption in that same
44
44
  # spec — never silence.
45
45
  module Properties
@@ -64,11 +64,11 @@ module Hecks
64
64
 
65
65
  module_function
66
66
 
67
- # WHICH LANGUAGE FEATURE EACH PROPERTY IS ANSWERABLE FOR. Not
67
+ # Which language feature each property is answerable for. Not
68
68
  # exhaustive of everything a property's body happens to touch —
69
69
  # `Command#attributes`, say, is exercised by nearly every property
70
- # here without being what any of them was WRITTEN to guard — but
71
- # exhaustive of the feature that would go UNCHECKED if this
70
+ # here without being what any of them was written to guard — but
71
+ # exhaustive of the feature that would go unchecked if this
72
72
  # property did not exist. That is the question the coverage gate
73
73
  # actually asks.
74
74
  FEATURE_COVERAGE = {
@@ -80,17 +80,17 @@ module Hecks
80
80
  authorize_scopes_or_refuses: %w[Query#options],
81
81
  guard_refusals_are_declared: %w[Command#givens Command#ensures],
82
82
  lifecycle_guard_and_given_violations_are_refused: %w[Command#from Aggregate#preconditions Entity#preconditions],
83
- # Dispatch#command_name/Dispatch#with_spec are NOT claimable
84
- # feature names — META_DOMAIN_ALL_FEATURES only walks ONE level
83
+ # Dispatch#command_name/Dispatch#with_spec are not claimable
84
+ # feature names — META_DOMAIN_ALL_FEATURES only walks one level
85
85
  # of entity nesting (`agg.entities.flat_map`, meta_domain_
86
- # coverage_spec.rb), and Dispatch sits TWO deep (ProcessManager
86
+ # coverage_spec.rb), and Dispatch sits two deep (ProcessManager
87
87
  # -> Handler -> Dispatch), so those strings never exist there
88
88
  # to claim — a pre-existing meta-domain coverage-generation gap,
89
89
  # found here (their old META_DOMAIN_KNOWN_GAPS entries were
90
90
  # themselves already-orphaned strings no completeness check ever
91
91
  # verified, since KNOWN_GAPS has no "never lets a gap rot" check
92
92
  # the way FEATURE_COVERAGE/GUARANTEED_BY_CONSTRUCTION both do).
93
- # This property still closes the REAL behavior both would have
93
+ # This property still closes the real behavior both would have
94
94
  # named — a Dispatch's own command_name/with_spec are exactly
95
95
  # what dispatch_args resolves and this property checks — the
96
96
  # grammar just has no feature string for either one.
@@ -102,50 +102,69 @@ module Hecks
102
102
  aggregation_matches_recompute: %w[ReadModel#count ReadModel#median_field],
103
103
  stored_records_satisfy_declared_invariants: %w[Aggregate#invariants Entity#invariants],
104
104
  group_by_matches_recompute: %w[ReadModel#group_by],
105
- # A RUNTIME DOOR, NOT A GRAMMAR CONSTRUCT — `Dispatcher#dry_run?`
105
+ # A runtime door, not a grammar construct — `Dispatcher#dry_run?`
106
106
  # is something an application asks of a booted domain, not a
107
107
  # word a bluebook can declare, so there is no feature string
108
108
  # for it to claim. Listed (empty) rather than omitted so the
109
109
  # discipline this table states — every property names what it
110
110
  # is answerable for — has no silent exception.
111
111
  dry_runs_leave_no_trace: [],
112
- # ANOTHER RUNTIME DOOR, NOT A GRAMMAR CONSTRUCT — same reasoning
112
+ # Another runtime door, not a grammar construct — same reasoning
113
113
  # as dry_runs_leave_no_trace right above: `Runtime::Outbox` is
114
114
  # something a persistence adapter provides underneath a booted
115
115
  # domain, never a word a bluebook declares.
116
- outbox_rows_match_reactions: []
116
+ outbox_rows_match_reactions: [],
117
+ # **The `corrects` mutation's own target** — this property reads
118
+ # `command.mutations.select { op == :corrects }` and asks whether
119
+ # the event each one names was ever actually emitted, so the
120
+ # feature it answers for is the mutation list, the same one
121
+ # `mutations_match_recompute` reads for a different question.
122
+ # (Not `Command#references`: that field is the dangling-reference
123
+ # question no property asks yet, and it stays a named gap.)
124
+ corrections_reference_an_emitted_event: %w[Command#mutations],
125
+ # No feature string exists for what this one reads. It depends on
126
+ # an argument's own `relationship` (which reference-typed argument
127
+ # points at which aggregate) — but `Argument` is a value object,
128
+ # and the meta-domain walk enumerates aggregate and entity fields
129
+ # only, so no `Argument#…` name is claimable. The declaration side
130
+ # it shares with queries, `authorize …, tenant:`, is
131
+ # `Query#options`, already claimed by `authorize_scopes_or_refuses`;
132
+ # claiming it twice would say this property covers a query
133
+ # question it never asks. Listed (empty) rather than omitted, the
134
+ # same discipline the two runtime doors above keep.
135
+ commands_respect_tenant_scope: []
117
136
  }.freeze
118
137
 
119
- # FEATURES A REPLAY PROPERTY COULD NEVER CATCH VIOLATED, because the
120
- # RUNTIME'S OWN CONSTRUCTION makes the violation impossible to
138
+ # Features a replay property could never catch violated, because the
139
+ # runtime's own construction makes the violation impossible to
121
140
  # produce in the first place — not "untested," but unfalsifiable by
122
141
  # a history, the same class of guarantee this codebase already
123
- # states for identity ("NOTHING IS MINTED" — command_interpreter.rb's
124
- # own header) and now generalises. Each entry names the ONE place in
142
+ # states for identity ("nothing is minted" — command_interpreter.rb's
143
+ # own header) and now generalises. Each entry names the one place in
125
144
  # the runtime that makes it true, universally, for every domain and
126
145
  # every adapter — never per-domain logic a future domain could
127
146
  # accidentally route around.
128
147
  #
129
- # THE BLUEBOOK/HECKSAGON BOUNDARY IS WHY THIS WORKS: a bluebook
130
- # declares SHAPE (attribute types, patterns, closed sets, VO
148
+ # The bluebook/hecksagon boundary is why this works: a bluebook
149
+ # declares shape (attribute types, patterns, closed sets, VO
131
150
  # invariants — see docs/decisions/0009), and shape is enforced by
132
- # ONE coercion door every domain's every attribute passes through
151
+ # one coercion door every domain's every attribute passes through
133
152
  # (`Runtime::Value.build`, via value/coercion.rb + value/admission.rb)
134
153
  # regardless of which hecksagon later binds the aggregate to Memory,
135
154
  # Postgres, or anything else. A value that violated its own declared
136
- # pattern, invariant, or closed set could never be COERCED, so it
137
- # could never be STORED, so it could never appear in a replay's own
155
+ # pattern, invariant, or closed set could never be coerced, so it
156
+ # could never be stored, so it could never appear in a replay's own
138
157
  # `:instances` to be caught violating it. Checking for it after the
139
158
  # fact would be watching for something the construction path already
140
159
  # made impossible.
141
160
  #
142
- # NOT a place to hide a real gap — a feature belongs here only once
143
- # the SPECIFIC enforcing code path has been read and confirmed, the
161
+ # Not a place to hide a real gap — a feature belongs here only once
162
+ # the specific enforcing code path has been read and confirmed, the
144
163
  # same discipline `spec/fuzzing/meta_domain_coverage_spec.rb` demands
145
164
  # of `KNOWN_GAPS` in the other direction. `Entity#identified_by` was
146
- # checked FOR this category once before and found NOT to qualify —
165
+ # checked for this category once before and found not to qualify —
147
166
  # `command_interpreter.rb`'s `AlreadyExists` refusal was given to
148
- # every CREATING AGGREGATE command uniformly, and MutationApplier
167
+ # every creating aggregate command uniformly, and MutationApplier
149
168
  # (command_interpreter/mutation_applier.rb) had no matching check on
150
169
  # an entity's own append. It does now: #check_entity_collision runs
151
170
  # unconditionally on both branches an entity identity can arrive by
@@ -171,13 +190,18 @@ module Hecks
171
190
  "(command_interpreter.rb, command.creates?) for every creating command uniformly, " \
172
191
  "before a duplicate id can ever be stored — collision is refused at the door, not " \
173
192
  "produced and later caught",
174
- "Entity#identified_by" => "MutationApplier#check_entity_collision (command_interpreter/mutation_applier.rb) " \
175
- "checks Array(current) against every part of the entity's own identity before an " \
176
- "append can land, on both branches identity arrives by (caller-supplied, or " \
177
- "composite) the same AlreadyExists refusal Aggregate#identified_by gets above, " \
178
- "one level down. Auto-minted entities never reach the check (current.size + 1 " \
179
- "can't repeat unless something remove:s from the list between mints, which no " \
180
- "real domain does today see the comment on #entity_element itself)",
193
+ "Entity#identified_by" => "EntityElement.check_entity_collision (runtime/entity_element.rb, moved there " \
194
+ "BUG#145 so both call sites share it) checks Array(current) against every part " \
195
+ "of the entity's own identity before an append can land MutationApplier#" \
196
+ "entity_element's aggregate-owned call (Workspace.boards, on both branches " \
197
+ "identity arrives by: caller-supplied, or composite) AND EntityElement#" \
198
+ "appended_to_element's entity-owned, nested-one-hop-further call (Board.cards " \
199
+ "unconditional, no auto-mint branch exists at that depth) the same " \
200
+ "AlreadyExists refusal Aggregate#identified_by gets above, one or two levels " \
201
+ "down. Auto-minted (aggregate-owned) entities never reach the check " \
202
+ "(current.size + 1 can't repeat unless something remove:s from the list between " \
203
+ "mints, which no real domain does today — see the comment on #entity_element " \
204
+ "itself)",
181
205
  "Command#attributes" => "command arguments are coerced through the SAME Value.build door as any other " \
182
206
  "attribute — an accepted dispatch's own args already passed pattern/admits/invariant checks",
183
207
  "Command#emits" => "CommandRules::Emission#emit iterates command.emits ITSELF to construct every " \
@@ -198,7 +222,7 @@ module Hecks
198
222
  # ValueObject), so this reads "Member#pairs", not "Member#shape" —
199
223
  # the free-text, un-parsed spelling a standalone root once needed
200
224
  # no longer exists at all, an entity's own element is never
201
- # serialized as text. "ValueObject#members" is the SAME fact
225
+ # serialized as text. "ValueObject#members" is the same fact
202
226
  # "ValueObject#rows" already counts, seen from the other side — a
203
227
  # value object cannot declare admitted rows without a members list
204
228
  # to hold them, and vice versa.
@@ -206,8 +230,8 @@ module Hecks
206
230
  "Member#pairs" => "one level into ValueObject#rows — same door"
207
231
  }.freeze
208
232
 
209
- # THE STANDARD BATTERY, run over one replayed history — everything
210
- # except determinism, which needs to replay TWICE itself and so
233
+ # The standard battery, run over one replayed history — everything
234
+ # except determinism, which needs to replay twice itself and so
211
235
  # takes the steps directly rather than a single history.
212
236
  def check(history)
213
237
  { lifecycle_values_are_declared: lifecycle_values_are_declared(history),
@@ -0,0 +1,152 @@
1
+ require "yaml"
2
+
3
+ module Hecks
4
+ module Fuzzing
5
+ # Reads `qa/settings.yml` — the hecks_qa practice's own dials, now
6
+ # data in a file rather than Ruby constant literals. The why of each
7
+ # dial (what it does, who reads it) stays exactly one place: the
8
+ # comments on `QualityControlDials` in `qa/bluebook/quality_control.
9
+ # bluebook`, which now sources every value from an instance of this
10
+ # class instead of writing it inline. This class is only the loading
11
+ # and the validation — no dial policy lives here.
12
+ #
13
+ # **Fails loud, not quiet** — the whole practice's own opening line
14
+ # ("the enemy is the quiet divergence") applies to its own settings
15
+ # file too: a missing key, an extra key nothing recognises, or a
16
+ # value of the wrong shape all raise immediately, at load time
17
+ # (which is bluebook-load time, i.e. `Hecks.boot`), naming exactly
18
+ # what's wrong — never a `nil` dial silently reaching a script that
19
+ # assumes a number.
20
+ #
21
+ # **Plain data in, frozen data out**. `.load` parses the YAML with
22
+ # `Psych.safe_load_file` (no custom tags, no arbitrary Ruby objects)
23
+ # and hands back an instance whose accessors are the exact values a
24
+ # human wrote in the file — a `Hash`/`Array` for the nested dials,
25
+ # never a second, richer wrapper type nothing else in this practice
26
+ # expects.
27
+ class QaSettings
28
+ # One entry per dial this class knows about: the accessor name
29
+ # (matching `qa/settings.yml`'s own key, and `QualityControlDials`'
30
+ # constant name snake_cased) mapped to the class (or classes) a
31
+ # valid value must be an instance of. `TrueClass`/`FalseClass`
32
+ # both name a boolean dial — Ruby has no single class both `true`
33
+ # and `false` share. `Numeric` admits both an Integer and a Float
34
+ # for a fraction dial (`0` and `0.0` are both a human plausibly
35
+ # types for "off").
36
+ EXPECTED_TYPES = {
37
+ cadence_seconds: Integer,
38
+ pr_cap_per_day: Integer,
39
+ widening_tiers: Array,
40
+ sweep_max_parallel: Integer,
41
+ liveness_fallback_seconds: Integer,
42
+ draft_only: [TrueClass, FalseClass],
43
+ auto_merge: [TrueClass, FalseClass],
44
+ branch_prefix: String,
45
+ adversarial_fraction: Numeric,
46
+ guided_generation: [TrueClass, FalseClass],
47
+ corpus_splice_probability: Numeric,
48
+ favor_rare_verbs: Integer,
49
+ self_consistency_checks: [TrueClass, FalseClass],
50
+ shrink_budget: Integer,
51
+ yield_weight_seconds: Integer,
52
+ yield_decay_percent: Integer,
53
+ rotation_stale_floor_seconds: Integer,
54
+ persistence_parity_seed_cap: Integer,
55
+ concurrency_seed_cap: Integer,
56
+ adapter_parity_pairs: Hash,
57
+ modes: Hash,
58
+ role_draw_probability: Numeric,
59
+ dry_run_fraction: Numeric,
60
+ generated_domains_per_tick: Integer,
61
+ generated_domains_rust: [TrueClass, FalseClass],
62
+ generated_domain_seeds: Integer,
63
+ structural_refusal_boundary: Array
64
+ }.freeze
65
+
66
+ attr_reader(*EXPECTED_TYPES.keys)
67
+
68
+ # **The real file, always** — resolved off this file's own `__dir__`
69
+ # (lib/hecks/fuzzing/), never off the caller's. `QualityControlDials`
70
+ # is defined inside `qa/bluebook/quality_control.bluebook`, and that
71
+ # exact directory gets copied to a tmpdir for every isolated/replayed
72
+ # boot (`Hecks::Fuzzing::IsolatedBoot#copy_dereferencing` copies only
73
+ # `qa/bluebook`'s own contents, never its parent `qa/`) — a path
74
+ # resolved from the bluebook's own `__dir__` would silently point at
75
+ # a copy with no `settings.yml` beside it at all. `qa/settings.yml`
76
+ # is read-only, human-edited data with no lifecycle (see this class's
77
+ # own header) — there is no isolation reason to ever read a copy of
78
+ # it, real boot or fuzzed one, so every caller gets the one real file
79
+ # by default. `qa_settings_spec.rb` passes its own fixture paths
80
+ # explicitly instead, the same way every other test in this practice
81
+ # that needs a non-default dial passes one in rather than mutating
82
+ # global state.
83
+ DEFAULT_PATH = File.expand_path("../../../qa/settings.yml", __dir__)
84
+
85
+ class << self
86
+ def load(path = DEFAULT_PATH)
87
+ raise ArgumentError, "qa settings file not found: #{path}" unless File.file?(path)
88
+
89
+ raw = begin
90
+ YAML.safe_load_file(path, symbolize_names: true)
91
+ rescue Psych::SyntaxError => e
92
+ raise ArgumentError, "#{path} is not valid YAML: #{e.message}"
93
+ end
94
+ raise ArgumentError, "#{path} must be a YAML mapping at the top level, got #{raw.class}" unless raw.is_a?(Hash)
95
+
96
+ new(raw, path)
97
+ end
98
+ end
99
+
100
+ def initialize(raw, path)
101
+ missing = EXPECTED_TYPES.keys - raw.keys
102
+ raise ArgumentError, "#{path} is missing #{missing.sort.join(', ')}" if missing.any?
103
+
104
+ extra = raw.keys - EXPECTED_TYPES.keys
105
+ if extra.any?
106
+ raise ArgumentError,
107
+ "#{path} declares unknown key(s) #{extra.sort.join(', ')} — " \
108
+ "Hecks::Fuzzing::QaSettings::EXPECTED_TYPES doesn't recognise them"
109
+ end
110
+
111
+ EXPECTED_TYPES.each do |key, expected|
112
+ value = raw.fetch(key)
113
+ expected_classes = Array(expected)
114
+ unless expected_classes.any? { |klass| value.is_a?(klass) }
115
+ raise ArgumentError,
116
+ "#{path}: #{key} must be a #{expected_classes.map(&:name).join(' or ')}, " \
117
+ "got #{value.class} (#{value.inspect})"
118
+ end
119
+
120
+ instance_variable_set(:"@#{key}", value)
121
+ end
122
+
123
+ symbolize_adapter_parity_pairs!(path)
124
+ freeze_values!
125
+ end
126
+
127
+ private
128
+
129
+ # `left:`/`right:` name adapters `IsolatedBoot` case-matches by
130
+ # symbol (`case adapter when :memory ...`), and YAML has no way to
131
+ # spell a bare Ruby Symbol as a mapping value — only
132
+ # `symbolize_names:` turns a key into one. So `adapter_parity_
133
+ # pairs` is the one dial that needs a coercion step after the
134
+ # type check above, rather than every dial growing one.
135
+ def symbolize_adapter_parity_pairs!(path)
136
+ @adapter_parity_pairs = @adapter_parity_pairs.to_h do |mode, pair|
137
+ unless pair.is_a?(Hash) && pair.key?(:left) && pair.key?(:right)
138
+ raise ArgumentError,
139
+ "#{path}: adapter_parity_pairs.#{mode} must have both left and right, got #{pair.inspect}"
140
+ end
141
+
142
+ [mode, { left: pair[:left].to_sym, right: pair[:right].to_sym }]
143
+ end
144
+ end
145
+
146
+ def freeze_values!
147
+ EXPECTED_TYPES.each_key { |key| instance_variable_get(:"@#{key}").freeze }
148
+ freeze
149
+ end
150
+ end
151
+ end
152
+ end