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
@@ -1,13 +1,13 @@
1
1
  module Hecks
2
2
  module Runtime
3
- # THE ONE SHARED CONSTANT between `SagaInterpreter` (the writer) and
3
+ # The one shared constant between `SagaInterpreter` (the writer) and
4
4
  # `Registry::SagaPersistence#rehydrate_sagas!` (the reader) for a
5
5
  # scoped, minimal answer to the saga-durability review's item 8 (a
6
6
  # durable outbox): a marker that survives exactly the window a crash
7
7
  # in `advance_saga`/`unwind` can otherwise hide.
8
8
  #
9
- # THE PROBLEM THIS CLOSES — `checkpoint` persists a saga's new state
10
- # BEFORE the leg that justifies it (`handler.dispatches`) runs, and
9
+ # **The problem this closes** — `checkpoint` persists a saga's new state
10
+ # before the leg that justifies it (`handler.dispatches`) runs, and
11
11
  # deliberately so: the mutex it holds is not reentrant, and a
12
12
  # dispatch can re-enter this same interpreter. If the process dies
13
13
  # in that window, the store says the transition happened and there
@@ -15,33 +15,33 @@ module Hecks
15
15
  # domain never got asked), not a defect (nothing raised), just
16
16
  # silence indistinguishable from a leg that finished cleanly.
17
17
  #
18
- # THE FIX — `checkpoint` now writes this key into the SAME already-
18
+ # **The fix** — `checkpoint` now writes this key into the same already-
19
19
  # durable `memory` blob (no new column, no adapter/schema change:
20
20
  # `memory` is already an opaque, adapter-agnostic JSON blob every
21
21
  # `save_saga` implementation round-trips verbatim) whenever it
22
- # checkpoints a state a dispatch cascade hasn't run for YET, and
22
+ # checkpoints a state a dispatch cascade hasn't run for yet, and
23
23
  # clears it (a second checkpoint, `pending: nil`) once that cascade
24
24
  # — success, refusal-compensated, defect-compensated, or ceiling-
25
25
  # compensated — has actually run. A crash between those two writes
26
26
  # leaves the marker standing; `rehydrate_sagas!` strips it back out
27
- # of the LIVE instance's own `:memory` (so no dispatch/`given`/
27
+ # of the live instance's own `:memory` (so no dispatch/`given`/
28
28
  # fuzzer/doc consumer of a saga's memory ever sees this key — it
29
29
  # exists only in the persisted blob) and surfaces it loudly instead.
30
30
  #
31
- # WHAT THIS DELIBERATELY DOES NOT DO — auto-redrive the pending leg.
31
+ # **What this deliberately does not do** — auto-redrive the pending leg.
32
32
  # Redelivering a dispatch whose outcome is genuinely unknown is only
33
33
  # safe with idempotent delivery (the downstream command recognizing
34
34
  # and no-op'ing a duplicate), which hecks's command/event pipeline
35
35
  # has no mechanism for today. Blindly re-dispatching without that is
36
- # how a stalled transfer becomes a DOUBLE-CREDITED one — a strictly
36
+ # how a stalled transfer becomes a double-credited one — a strictly
37
37
  # worse defect than the stall it would replace. So this is real,
38
- # durable, crash-surviving VISIBILITY into exactly what a stalled
38
+ # durable, crash-surviving visibility into exactly what a stalled
39
39
  # saga was doing when the process died — the missing half of "no
40
40
  # reconciliation pass exists". The full pending → claimed →
41
41
  # delivered outbox now exists too (`Runtime::Outbox`, ADR 0053) and
42
- # is COMPLEMENTARY, not a replacement: the outbox row names the
43
- # EVENT owed to this saga (and redrives it if the saga never got to
44
- # claim it); this marker names the saga's own LEG mid-flight after
42
+ # is complementary, not a replacement: the outbox row names the
43
+ # event owed to this saga (and redrives it if the saga never got to
44
+ # claim it); this marker names the saga's own leg mid-flight after
45
45
  # the event was delivered. A crash can leave either standing.
46
46
  SAGA_PENDING_DISPATCH_KEY = :__hecks_saga_pending_dispatch__
47
47
  end
@@ -2,26 +2,26 @@ require_relative "../ports/persistence/binding_policy"
2
2
 
3
3
  module Hecks
4
4
  module Runtime
5
- # The capability idiom for MULTI-TENANT hosting — mirrors EraCheck's
5
+ # The capability idiom for multi-tenant hosting — mirrors EraCheck's
6
6
  # own `lineage_capable?`, one level over. An adapter answers
7
- # `tenant_capable?` with true when its OWN instances genuinely
7
+ # `tenant_capable?` with true when its own instances genuinely
8
8
  # isolate one boot's data from another's, given each tenant is its
9
9
  # own separate `Runtime.boot` call (its own Registry, its own
10
10
  # Dispatcher, its own adapter instances) rather than one shared
11
11
  # process switching connections mid-dispatch.
12
12
  #
13
- # THAT LAST PART IS THE FINDING THIS MODULE ENCODES. The project
13
+ # That last part is the finding this module encodes. The project
14
14
  # register (Bluebook::ProjectRegister) already resolves an address's
15
- # REALM to a DISPATCHER at registration time — Router#resolve looks
15
+ # realm to a dispatcher at registration time — Router#resolve looks
16
16
  # the FQN up in one flat table keyed by realm::domain::aggregate.verb,
17
- # and each entry already carries its OWN dispatcher from its OWN
18
- # boot. So "which tenant" is decided ONCE, at boot/registration time
17
+ # and each entry already carries its own dispatcher from its own
18
+ # boot. So "which tenant" is decided once, at boot/registration time
19
19
  # (which of possibly many boots of the same directory a request's
20
20
  # realm resolves to), never per-dispatch inside a shared registry.
21
21
  # No ambient thread-local "current tenant" is needed, and no
22
22
  # connection cache is needed beyond what booting-once-per-tenant
23
23
  # already gives for free — each tenant's own PostgresEra instance
24
- # IS its own connection, held for the life of that boot.
24
+ # is its own connection, held for the life of that boot.
25
25
  #
26
26
  # So `tenant_capable?` asks a narrower question than it might sound:
27
27
  # not "can this adapter switch tenants," but "does booting this
@@ -41,7 +41,7 @@ module Hecks
41
41
  module TenantCheck
42
42
  module_function
43
43
 
44
- # A domain is safe to boot for MORE THAN ONE TENANT only if every
44
+ # A domain is safe to boot for more than one tenant only if every
45
45
  # aggregate's resolved persistence adapter is tenant_capable? — one
46
46
  # ungoverned adapter sharing state across two tenant boots is a
47
47
  # real data leak, not a theoretical one, so this is checked before
@@ -67,7 +67,7 @@ module Hecks
67
67
  "or keep #{domain} single-tenant."
68
68
  end
69
69
 
70
- # The capability idiom itself — an adapter CLASS that answers
70
+ # The capability idiom itself — an adapter class that answers
71
71
  # tenant_capable? with true keeps two boots' data apart by
72
72
  # construction (Memory) or by an explicit per-boot isolation
73
73
  # setting (PostgresEra's schema:). Same defensive shape
@@ -7,9 +7,9 @@ module Hecks
7
7
  module Runtime
8
8
  # `authorize policy, tenant: :field` declared a tenant boundary that
9
9
  # nothing enforced — the policy name and the field were stored and read
10
- # by nothing at dispatch time. This is the half that CAN be enforced
10
+ # by nothing at dispatch time. This is the half that can be enforced
11
11
  # without a caller-identity/session system: the boundary itself, made
12
- # mandatory. Whether THIS caller actually holds `policy` for THIS
12
+ # mandatory. Whether this caller actually holds `policy` for this
13
13
  # tenant needs real identity infrastructure this runtime does not have
14
14
  # — that stays a named, open gap, not something this quietly pretends
15
15
  # to answer.
@@ -22,7 +22,7 @@ module Hecks
22
22
  # free, with no per-engine code and no way for one engine to forget it.
23
23
  # `Scoped` is handed only to those engines as their `declared`/
24
24
  # `specification` argument — never returned to a caller that might call
25
- # an IR-level method (`filtered_head_name`, `to_h`, …) whose OWN
25
+ # an IR-level method (`filtered_head_name`, `to_h`, …) whose own
26
26
  # internal `wheres` call would resolve against the original object, not
27
27
  # this override, since `SimpleDelegator` only intercepts calls made
28
28
  # directly on the wrapper.
@@ -35,8 +35,8 @@ module Hecks
35
35
 
36
36
  tenant = tenant.to_sym
37
37
  unless args.key?(tenant)
38
- raise Unauthorized, RefusalWording.render("Unauthorized", "tenant_required",
39
- query: declared.name, field: tenant)
38
+ raise Unauthorized, RefusalWording.render_site("Unauthorized", "tenant_required",
39
+ query: declared.name, field: tenant)
40
40
  end
41
41
 
42
42
  Scoped.new(declared, QuerySpecification::Common::WhereClause.new(field: tenant, op: "eq", value: tenant))
@@ -8,6 +8,15 @@ module Hecks
8
8
  # refusals that make them rules rather than decoration. Extended into
9
9
  # Value beside Coercion; `self` is the Value class.
10
10
  module Admission
11
+ # Refuses fields that match no declared member of a closed-set (`one_of`) value object.
12
+ #
13
+ # @param value_object [Bluebook::ValueObject] the type being built; one with no
14
+ # members is not a closed set and is never refused
15
+ # @param fields [Hash{Symbol => Object}] the offered fields, keyed by attribute name
16
+ # @return [nil] when the type has no members, or some member matches on every
17
+ # field it names
18
+ # @raise [Runtime::InvariantViolation] if no member matches; the message lists the
19
+ # admitted values of the first attribute and the one offered
11
20
  def admit_member(value_object, fields)
12
21
  return if value_object.members.empty?
13
22
 
@@ -17,42 +26,54 @@ module Hecks
17
26
  return if value_object.members.any? { |member| member_matches?(member, fields) }
18
27
 
19
28
  raise InvariantViolation,
20
- RefusalWording.render("InvariantViolation", "closed_set_member",
21
- type: value_object.hecks_name,
22
- admitted: admitted.map(&:inspect).join(", "), offered: offered.inspect)
29
+ RefusalWording.render_site("InvariantViolation", "closed_set_member",
30
+ type: value_object.hecks_name,
31
+ admitted: admitted, offered: offered.inspect)
23
32
  end
24
33
 
25
- # EVERY DECLARED FIELD, not only the discriminant — a multi-column
34
+ # Every declared field, not only the discriminant — a multi-column
26
35
  # `member` row (`StatementFrequency`'s `cadence`/`retention_months`/
27
36
  # `paper_fee_cents`, statements.bluebook) names a whole tuple, and a
28
37
  # caller offering the right `cadence` with the wrong
29
- # `retention_months` is not a genuine member just because the FIRST
30
- # column happened to match. `admitted.include?(offered.to_s)` above
31
- # used to be the entire check, so it stopped at that first column —
38
+ # `retention_months` is not a genuine member just because the first
39
+ # column happened to match. Comparing only the discriminant
40
+ # (`admitted.include?(offered.to_s)`) stops at that first column —
32
41
  # confirmed live: `Value.build(StatementFrequency, cadence:
33
- # "monthly", retention_months: 999, paper_fee_cents: 999)` was
34
- # admitted outright, no member of the closed set declares that row.
42
+ # "monthly", retention_months: 999, paper_fee_cents: 999)` would be
43
+ # admitted outright, though no member of the closed set declares that row.
35
44
  # A single-field set (`AccountKind`, `LedgerDirection`, ...) has
36
- # exactly one key here, so this reduces to the original discriminant-
37
- # only comparison for every set that only ever had one column to
38
- # begin with — same refusal, same wording, unchanged.
45
+ # exactly one key here, so this reduces to a discriminant-only
46
+ # comparison for every set that has one column — same refusal,
47
+ # same wording.
39
48
  private def member_matches?(member, fields)
40
49
  member.all? { |field, value| fields[field].to_s == value.to_s }
41
50
  end
42
51
 
43
- # THE SAME REFUSAL, FOR A SET NAMED SOMEWHERE ELSE.
52
+ # Checks a value against the closed set its attribute names with `admits:`.
53
+ #
54
+ # The same refusal, for a set named somewhere else.
44
55
  #
45
- # `admit_member` above refuses a non-member when the value object BEING
46
- # BUILT is itself the closed set — which is the only shape `one_of` can
47
- # make, because it SYNTHESISES the set from the values written inline. An
56
+ # `admit_member` above refuses a non-member when the value object being
57
+ # built is itself the closed set — which is the only shape `one_of` can
58
+ # make, because it synthesises the set from the values written inline. An
48
59
  # `admits:` attribute is the other direction: the value is an ordinary
49
60
  # String or a plain text holder, and the set it must belong to was
50
61
  # declared once, elsewhere, and is named rather than restated.
51
62
  #
52
- # Without this the word was a DECLARATION and nothing more — read by
63
+ # Without this the word was a declaration and nothing more — read by
53
64
  # projections, read by nobody at the door. A rule
54
65
  # that cannot be the one to refuse is decoration, and this language has
55
66
  # paid for that mistake before.
67
+ #
68
+ # @param owner [Bluebook::Aggregate, Bluebook::Entity, Bluebook::ValueObject] the
69
+ # construct that declares `attribute`; the walk to the chapter starts here
70
+ # @param attribute [Bluebook::Attribute, nil] the attribute being filled; nil, or one
71
+ # with no `admits:`, means nothing is checked
72
+ # @param value [Object, nil] the coerced value: a bare scalar, or a `Runtime::Value`
73
+ # holding one field; nil is never checked
74
+ # @return [Object, nil] `value`, unchanged
75
+ # @raise [Runtime::InvariantViolation] if the value is not a member of the named set,
76
+ # or `admits:` names a set no aggregate in the chapter declares
56
77
  def admit_declared_set(owner, attribute, value)
57
78
  return value if attribute.nil? || attribute.admits.nil? || value.nil?
58
79
 
@@ -61,21 +82,32 @@ module Hecks
61
82
  return value if admitted.include?(offered.to_s)
62
83
 
63
84
  raise InvariantViolation,
64
- RefusalWording.render("InvariantViolation", "admits_declared_set",
65
- name: attribute.name, admits: attribute.admits,
66
- admitted: admitted.map(&:inspect).join(", "), offered: offered.inspect)
85
+ RefusalWording.render_site("InvariantViolation", "admits_declared_set",
86
+ name: attribute.name, admits: attribute.admits,
87
+ admitted: admitted, offered: offered.inspect)
67
88
  end
68
89
 
69
- # `Vocabulary::MutationOp` the aggregate that HOLDS the set, then the
70
- # set. Qualified because a closed set is a value object INSIDE an
90
+ # Lists the values the closed set named by an attribute's `admits:` allows.
91
+ #
92
+ # `Vocabulary::MutationOp` — the aggregate that holds the set, then the
93
+ # set. Qualified because a closed set is a value object inside an
71
94
  # aggregate, which is exactly why `admits` could not be spelled as a
72
95
  # reference: `reference_to` reaches aggregate heads and nothing below one.
73
96
  #
74
- # RESOLVED LATE, like `Reference#resolve` and for the same reason — the
97
+ # Resolved late, like `Reference#resolve` and for the same reason — the
75
98
  # set may be declared further down the file than the attribute that names
76
- # it. And REFUSED when it resolves to nothing, also like `Reference`: a
99
+ # it. And refused when it resolves to nothing, also like `Reference`: a
77
100
  # link checked against nothing is worse than no link, because it reads
78
101
  # like a rule.
102
+ #
103
+ # @param owner [Bluebook::Aggregate, Bluebook::Entity, Bluebook::ValueObject] the
104
+ # construct that declares `attribute`
105
+ # @param attribute [Bluebook::Attribute] an attribute whose `admits` is
106
+ # `"Aggregate::SetName"`
107
+ # @return [Array<String>] each member's value for the set's first attribute, as a String,
108
+ # in declaration order
109
+ # @raise [Runtime::InvariantViolation] if `admits` is not qualified, no chapter is
110
+ # reachable from `owner`, or the chapter declares no such aggregate or value object
79
111
  def admitted_members(owner, attribute)
80
112
  aggregate_name, set_name = attribute.admits.to_s.split("::", 2)
81
113
  chapter = chapter_of(owner)
@@ -83,26 +115,39 @@ module Hecks
83
115
 
84
116
  unless set
85
117
  raise InvariantViolation,
86
- RefusalWording.render("InvariantViolation", "undeclared_set",
87
- name: attribute.name, admits: attribute.admits)
118
+ RefusalWording.render_site("InvariantViolation", "undeclared_set",
119
+ name: attribute.name, admits: attribute.admits)
88
120
  end
89
121
 
90
122
  discriminant = set.attributes.first.name
91
123
  set.members.map { |member| member.to_h[discriminant].to_s }
92
124
  end
93
125
 
94
- # UP TO THE CHAPTER, from wherever the attribute was declared. A value
126
+ # Walks `hecks_owner` links upward until it reaches the chapter.
127
+ #
128
+ # Up to the chapter, from wherever the attribute was declared. A value
95
129
  # object's owner is its aggregate and an aggregate's owner is its chapter,
96
130
  # so the walk stops at the first construct that can answer for an
97
- # aggregate BY NAME — which is the chapter, and only the chapter.
131
+ # aggregate by name — which is the chapter, and only the chapter.
132
+ #
133
+ # @param construct [Bluebook::ValueObject, Bluebook::Entity, Bluebook::Aggregate,
134
+ # Bluebook::Chapter, nil] the construct to start from; the chapter answers itself
135
+ # @return [Bluebook::Chapter, nil] the first construct on the way up that responds to
136
+ # `aggregate`; nil when the chain ends without one
98
137
  def chapter_of(construct)
99
138
  node = construct
100
139
  node = node.hecks_owner while node && !node.respond_to?(:aggregate) && node.respond_to?(:hecks_owner)
101
140
  node.respond_to?(:aggregate) ? node : nil
102
141
  end
103
142
 
104
- # An admitted value is a SCALAR however it arrived as a bare string on a
143
+ # Unwraps a one-field value object to the scalar it holds, so membership compares scalars.
144
+ #
145
+ # An admitted value is a scalar however it arrived — as a bare string on a
105
146
  # plain field, or wrapped in the one-field holder its type names.
147
+ #
148
+ # @param value [Object] a bare scalar, or a `Runtime::Value`
149
+ # @return [Object] the sole field's value for a one-field `Runtime::Value`; otherwise
150
+ # `value` itself, a multi-field `Runtime::Value` included
106
151
  def admitted_scalar(value)
107
152
  return value unless value.is_a?(self)
108
153
 
@@ -110,7 +155,7 @@ module Hecks
110
155
  fields.size == 1 ? fields.values.first : value
111
156
  end
112
157
 
113
- # A FIELD OF A VALUE OBJECT MAY NAME A SET TOO.
158
+ # A field of a value object may name a set too.
114
159
  #
115
160
  # Beside check_patterns and for the same reason: `Query::Filter.op` is a
116
161
  # plain String field that admits `Vocabulary::QueryComparator`, and the