hecks 1.3.0 → 1.5.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 (373) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hecks/adapters/driven/claude_code.rb +72 -7
  3. data/lib/hecks/adapters/driven/d1.rb +187 -23
  4. data/lib/hecks/adapters/driven/folder.rb +83 -10
  5. data/lib/hecks/adapters/driven/google_authentication.rb +33 -12
  6. data/lib/hecks/adapters/driven/governance_authorization.rb +87 -18
  7. data/lib/hecks/adapters/driven/heki/journal.rb +13 -4
  8. data/lib/hecks/adapters/driven/heki/saga_store.rb +56 -10
  9. data/lib/hecks/adapters/driven/heki/snapshot.rb +1 -1
  10. data/lib/hecks/adapters/driven/heki.rb +106 -9
  11. data/lib/hecks/adapters/driven/identity_registry.rb +12 -2
  12. data/lib/hecks/adapters/driven/in_memory_ordering.rb +25 -3
  13. data/lib/hecks/adapters/driven/in_process_key_vault.adapter +3 -0
  14. data/lib/hecks/adapters/driven/in_process_key_vault.rb +53 -0
  15. data/lib/hecks/adapters/driven/lambda/client.rb +67 -14
  16. data/lib/hecks/adapters/driven/lambda.rb +82 -35
  17. data/lib/hecks/adapters/driven/local_storage.rb +83 -10
  18. data/lib/hecks/adapters/driven/memory.rb +205 -9
  19. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +21 -1
  20. data/lib/hecks/adapters/driven/postgres/codec.rb +27 -11
  21. data/lib/hecks/adapters/driven/postgres/outbox.rb +40 -2
  22. data/lib/hecks/adapters/driven/postgres/reconnect.rb +23 -7
  23. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +14 -14
  24. data/lib/hecks/adapters/driven/postgres.rb +175 -28
  25. data/lib/hecks/adapters/driven/prism.rb +50 -11
  26. data/lib/hecks/adapters/driven/secure_random_identity.rb +3 -0
  27. data/lib/hecks/adapters/driven/sql_query_builder.rb +34 -22
  28. data/lib/hecks/adapters/driven/sqlite/codec.rb +38 -10
  29. data/lib/hecks/adapters/driven/sqlite/projection.rb +60 -32
  30. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +12 -12
  31. data/lib/hecks/adapters/driven/sqlite.rb +181 -21
  32. data/lib/hecks/adapters/driven/system_clock.rb +3 -0
  33. data/lib/hecks/adapters/driven/tenant_provisioner.adapter +3 -0
  34. data/lib/hecks/adapters/driven/tenant_provisioner.rb +66 -0
  35. data/lib/hecks/adapters/driven.rb +6 -4
  36. data/lib/hecks/adapters/driving/github_webhook.rb +31 -18
  37. data/lib/hecks/behaviors/dsl.rb +60 -2
  38. data/lib/hecks/behaviors/expectations.rb +190 -29
  39. data/lib/hecks/behaviors/ir.rb +12 -1
  40. data/lib/hecks/behaviors/rspec.rb +9 -1
  41. data/lib/hecks/behaviors/runner.rb +21 -2
  42. data/lib/hecks/behaviors.rb +9 -1
  43. data/lib/hecks/bluebook/aggregate.rb +43 -13
  44. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +17 -10
  45. data/lib/hecks/bluebook/assembly/build.rb +26 -5
  46. data/lib/hecks/bluebook/assembly/contract.rb +98 -23
  47. data/lib/hecks/bluebook/assembly/contracts.rb +59 -52
  48. data/lib/hecks/bluebook/assembly/marks.rb +159 -30
  49. data/lib/hecks/bluebook/assembly/specializer.rb +38 -21
  50. data/lib/hecks/bluebook/assembly.rb +32 -14
  51. data/lib/hecks/bluebook/attribute.rb +26 -12
  52. data/lib/hecks/bluebook/behaviour/aggregate.rb +43 -11
  53. data/lib/hecks/bluebook/behaviour/attribute.rb +18 -5
  54. data/lib/hecks/bluebook/behaviour/chapter.rb +76 -5
  55. data/lib/hecks/bluebook/behaviour/command.rb +55 -25
  56. data/lib/hecks/bluebook/behaviour/domain_port.rb +27 -7
  57. data/lib/hecks/bluebook/behaviour/entity.rb +20 -8
  58. data/lib/hecks/bluebook/behaviour/hexagon.rb +30 -4
  59. data/lib/hecks/bluebook/behaviour/lifecycle.rb +27 -6
  60. data/lib/hecks/bluebook/behaviour/policy.rb +42 -17
  61. data/lib/hecks/bluebook/behaviour/process_manager.rb +39 -8
  62. data/lib/hecks/bluebook/behaviour/query.rb +6 -1
  63. data/lib/hecks/bluebook/behaviour/read_model.rb +29 -8
  64. data/lib/hecks/bluebook/behaviour/traits.rb +48 -12
  65. data/lib/hecks/bluebook/behaviour/value_object.rb +21 -9
  66. data/lib/hecks/bluebook/capabilities.rb +27 -0
  67. data/lib/hecks/bluebook/chapter.rb +51 -9
  68. data/lib/hecks/bluebook/command.rb +62 -17
  69. data/lib/hecks/bluebook/domain_port.rb +34 -9
  70. data/lib/hecks/bluebook/dsl/adapter_builder.rb +24 -0
  71. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +49 -49
  72. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +282 -123
  73. data/lib/hecks/bluebook/dsl/attribute_collector.rb +112 -75
  74. data/lib/hecks/bluebook/dsl/binding_proxy.rb +81 -2
  75. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +486 -117
  76. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +179 -47
  77. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
  78. data/lib/hecks/bluebook/dsl/command_builder.rb +284 -122
  79. data/lib/hecks/bluebook/dsl/const_shim.rb +46 -15
  80. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +90 -25
  81. data/lib/hecks/bluebook/dsl/entity_builder.rb +191 -61
  82. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
  83. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +130 -30
  84. data/lib/hecks/bluebook/dsl/identity_declaration.rb +38 -21
  85. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
  86. data/lib/hecks/bluebook/dsl/policy_builder.rb +86 -36
  87. data/lib/hecks/bluebook/dsl/port_builder.rb +38 -7
  88. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +56 -22
  89. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +111 -47
  90. data/lib/hecks/bluebook/dsl/query_builder.rb +37 -8
  91. data/lib/hecks/bluebook/dsl/read_model_builder.rb +127 -52
  92. data/lib/hecks/bluebook/dsl/rule_reference.rb +97 -43
  93. data/lib/hecks/bluebook/dsl/translation_builder.rb +150 -44
  94. data/lib/hecks/bluebook/dsl/value_object_builder.rb +68 -20
  95. data/lib/hecks/bluebook/dsl/word_gate.rb +59 -53
  96. data/lib/hecks/bluebook/dsl/world_builder.rb +51 -8
  97. data/lib/hecks/bluebook/entity.rb +40 -11
  98. data/lib/hecks/bluebook/expression/ast_json.rb +128 -36
  99. data/lib/hecks/bluebook/expression/ast_reader.rb +32 -3
  100. data/lib/hecks/bluebook/expression/canonical_form.rb +55 -16
  101. data/lib/hecks/bluebook/expression/evaluator.rb +221 -43
  102. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +54 -18
  103. data/lib/hecks/bluebook/expression/resolver.rb +369 -128
  104. data/lib/hecks/bluebook/hexagon.rb +35 -1
  105. data/lib/hecks/bluebook/lifecycle.rb +12 -1
  106. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +2 -1
  107. data/lib/hecks/bluebook/meta_validator/judge.rb +126 -108
  108. data/lib/hecks/bluebook/meta_validator/plan.rb +81 -46
  109. data/lib/hecks/bluebook/meta_validator/port_judge.rb +3 -2
  110. data/lib/hecks/bluebook/meta_validator/readings.rb +200 -50
  111. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +68 -41
  112. data/lib/hecks/bluebook/meta_validator/shapes.rb +166 -21
  113. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +286 -46
  114. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +11 -10
  115. data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -5
  116. data/lib/hecks/bluebook/meta_validator.rb +235 -139
  117. data/lib/hecks/bluebook/model_check.rb +434 -104
  118. data/lib/hecks/bluebook/pattern_subset.rb +32 -10
  119. data/lib/hecks/bluebook/policy.rb +15 -13
  120. data/lib/hecks/bluebook/process_manager.rb +27 -14
  121. data/lib/hecks/bluebook/project_discovery.rb +5 -0
  122. data/lib/hecks/bluebook/project_loader.rb +40 -0
  123. data/lib/hecks/bluebook/project_register.rb +50 -6
  124. data/lib/hecks/bluebook/query.rb +31 -4
  125. data/lib/hecks/bluebook/read_model.rb +35 -15
  126. data/lib/hecks/bluebook/reference.rb +26 -13
  127. data/lib/hecks/bluebook/smoke_test.rb +46 -23
  128. data/lib/hecks/bluebook/synthesizer.rb +46 -12
  129. data/lib/hecks/bluebook/translation.rb +34 -5
  130. data/lib/hecks/bluebook/value_object.rb +29 -11
  131. data/lib/hecks/bluebook.rb +5 -6
  132. data/lib/hecks/codemod.rb +138 -50
  133. data/lib/hecks/construct.rb +21 -7
  134. data/lib/hecks/corpus.rb +438 -0
  135. data/lib/hecks/deploy/bluebook/deploy.hecksagon +19 -0
  136. data/lib/hecks/doc/reference.rb +200 -31
  137. data/lib/hecks/embryonaut_bluebook.rb +38 -15
  138. data/lib/hecks/facade/cli_door.rb +69 -10
  139. data/lib/hecks/facade/cli_runner.rb +105 -24
  140. data/lib/hecks/facade/command_request.rb +23 -0
  141. data/lib/hecks/facade/handle.rb +155 -35
  142. data/lib/hecks/facade/json_door.rb +106 -25
  143. data/lib/hecks/facade/surface/aggregate_door.rb +50 -27
  144. data/lib/hecks/facade/surface/chapter.rb +26 -17
  145. data/lib/hecks/facade/surface.rb +16 -3
  146. data/lib/hecks/facade.rb +15 -4
  147. data/lib/hecks/forms/app.rb +46 -30
  148. data/lib/hecks/forms/command_form_renderer.rb +70 -9
  149. data/lib/hecks/forms/field_renderer.rb +142 -6
  150. data/lib/hecks/forms/field_shape.rb +183 -19
  151. data/lib/hecks/forms/html.rb +51 -7
  152. data/lib/hecks/forms/index_renderer.rb +14 -2
  153. data/lib/hecks/forms/page.rb +14 -0
  154. data/lib/hecks/forms/params.rb +120 -23
  155. data/lib/hecks/forms/port_argument.rb +14 -2
  156. data/lib/hecks/forms/query_form_renderer.rb +65 -2
  157. data/lib/hecks/forms/record_renderer.rb +60 -2
  158. data/lib/hecks/forms/record_table.rb +28 -1
  159. data/lib/hecks/forms/reference_options.rb +24 -0
  160. data/lib/hecks/forms/value_object_shape.rb +13 -3
  161. data/lib/hecks/forms.rb +24 -4
  162. data/lib/hecks/fqn.rb +59 -1
  163. data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
  164. data/lib/hecks/framework/bluebook/privacy.bluebook +155 -0
  165. data/lib/hecks/framework/oidc.json +15 -0
  166. data/lib/hecks/framework.rb +79 -25
  167. data/lib/hecks/freezer.rb +27 -11
  168. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +241 -106
  169. data/lib/hecks/fuzzing/combination_miner.rb +178 -0
  170. data/lib/hecks/fuzzing/concurrent_dispatch.rb +241 -45
  171. data/lib/hecks/fuzzing/coverage_campaign.rb +161 -0
  172. data/lib/hecks/fuzzing/differential.rb +192 -0
  173. data/lib/hecks/fuzzing/domain_generator.rb +871 -0
  174. data/lib/hecks/fuzzing/era_boundary.rb +68 -19
  175. data/lib/hecks/fuzzing/form_census.rb +121 -20
  176. data/lib/hecks/fuzzing/generated_domain_check.rb +171 -0
  177. data/lib/hecks/fuzzing/invalid_value_generator.rb +45 -6
  178. data/lib/hecks/fuzzing/isolated_boot.rb +137 -80
  179. data/lib/hecks/fuzzing/nondeterministic.rb +79 -0
  180. data/lib/hecks/fuzzing/persistence_parity.rb +111 -21
  181. data/lib/hecks/fuzzing/properties/corrections.rb +34 -9
  182. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +300 -51
  183. data/lib/hecks/fuzzing/properties/guards.rb +86 -42
  184. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +80 -32
  185. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +40 -35
  186. data/lib/hecks/fuzzing/properties/outbox.rb +70 -32
  187. data/lib/hecks/fuzzing/properties/querying.rb +82 -28
  188. data/lib/hecks/fuzzing/properties.rb +84 -51
  189. data/lib/hecks/fuzzing/qa_settings.rb +164 -0
  190. data/lib/hecks/fuzzing/replay.rb +241 -133
  191. data/lib/hecks/fuzzing/rotation_priority.rb +48 -28
  192. data/lib/hecks/fuzzing/rust_gap_manifest.rb +139 -0
  193. data/lib/hecks/fuzzing/self_consistency.rb +315 -137
  194. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +46 -40
  195. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +18 -11
  196. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +13 -12
  197. data/lib/hecks/fuzzing/sequence_generator/picker.rb +21 -12
  198. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +59 -32
  199. data/lib/hecks/fuzzing/sequence_generator.rb +158 -23
  200. data/lib/hecks/fuzzing/shrinker.rb +309 -0
  201. data/lib/hecks/fuzzing/structural_skips.rb +37 -130
  202. data/lib/hecks/fuzzing/sweep_depth.rb +12 -4
  203. data/lib/hecks/fuzzing/target_capabilities.rb +107 -29
  204. data/lib/hecks/fuzzing/value_generator.rb +110 -22
  205. data/lib/hecks/fuzzing.rb +5 -0
  206. data/lib/hecks/grammar/evolve.rb +188 -12
  207. data/lib/hecks/grammar.rb +53 -7
  208. data/lib/hecks/ir.rb +51 -20
  209. data/lib/hecks/language/bluebook/bluebook.bluebook +41 -0
  210. data/lib/hecks/language/bluebook/policy.bluebook +11 -1
  211. data/lib/hecks/language/bluebook/vocabulary.bluebook +348 -13
  212. data/lib/hecks/language/hecksagon/hecksagon.bluebook +11 -0
  213. data/lib/hecks/language/oidc.json +5 -0
  214. data/lib/hecks/literal.rb +41 -9
  215. data/lib/hecks/naming.rb +112 -31
  216. data/lib/hecks/ports/access_control.rb +53 -2
  217. data/lib/hecks/ports/agent/answers.rb +83 -6
  218. data/lib/hecks/ports/agent.rb +119 -35
  219. data/lib/hecks/ports/authentication.rb +44 -4
  220. data/lib/hecks/ports/authorization.rb +50 -11
  221. data/lib/hecks/ports/clock.rb +42 -23
  222. data/lib/hecks/ports/extraction.rb +16 -0
  223. data/lib/hecks/ports/identity_assignment.rb +23 -2
  224. data/lib/hecks/ports/identity_generation.rb +17 -3
  225. data/lib/hecks/ports/identity_resolution.rb +17 -1
  226. data/lib/hecks/ports/key_vault.port +6 -0
  227. data/lib/hecks/ports/key_vault.rb +58 -0
  228. data/lib/hecks/ports/loading.rb +4 -0
  229. data/lib/hecks/ports/persistence/append_only.rb +172 -8
  230. data/lib/hecks/ports/persistence/binding_policy.rb +34 -0
  231. data/lib/hecks/ports/persistence/codec_boundary.rb +178 -0
  232. data/lib/hecks/ports/persistence/execution.rb +4 -0
  233. data/lib/hecks/ports/persistence/null_saga_store.rb +12 -1
  234. data/lib/hecks/ports/persistence/plugin.rb +42 -4
  235. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +218 -25
  236. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +77 -9
  237. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +81 -24
  238. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +29 -18
  239. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +144 -60
  240. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +103 -8
  241. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +98 -23
  242. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +282 -109
  243. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +63 -25
  244. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +118 -66
  245. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +51 -28
  246. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +34 -5
  247. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +25 -12
  248. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +129 -34
  249. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +51 -6
  250. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +33 -9
  251. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +16 -0
  252. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +95 -6
  253. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +25 -2
  254. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +314 -90
  255. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +68 -10
  256. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +9 -3
  257. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +9 -2
  258. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +48 -8
  259. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +16 -1
  260. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +36 -5
  261. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +23 -3
  262. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +58 -19
  263. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +96 -5
  264. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +15 -0
  265. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +9 -1
  266. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -2
  267. data/lib/hecks/ports/persistence/plugins/era.rb +12 -2
  268. data/lib/hecks/ports/persistence/remote_runtime.rb +9 -2
  269. data/lib/hecks/ports/persistence/repository_factory.rb +23 -3
  270. data/lib/hecks/ports/persistence/state_codec.rb +319 -0
  271. data/lib/hecks/ports/persistence.rb +36 -1
  272. data/lib/hecks/ports/projection.rb +61 -7
  273. data/lib/hecks/ports/query/in_memory.rb +41 -8
  274. data/lib/hecks/ports/query/ordering.rb +21 -6
  275. data/lib/hecks/ports/query.rb +35 -0
  276. data/lib/hecks/ports.rb +1 -0
  277. data/lib/hecks/projections/bootstrap_table.rb +147 -0
  278. data/lib/hecks/projections/diagrams.rb +315 -79
  279. data/lib/hecks/projections/glossary/html.rb +91 -3
  280. data/lib/hecks/projections/glossary/markdown.rb +50 -3
  281. data/lib/hecks/projections/glossary/mermaid.rb +49 -1
  282. data/lib/hecks/projections/glossary/sections.rb +1 -1
  283. data/lib/hecks/projections/glossary/sentences.rb +110 -5
  284. data/lib/hecks/projections/glossary.rb +173 -31
  285. data/lib/hecks/projections/ir.rb +1 -1
  286. data/lib/hecks/projections/model/deviations.rb +62 -17
  287. data/lib/hecks/projections/model.rb +75 -21
  288. data/lib/hecks/projections/oidc.rb +45 -8
  289. data/lib/hecks/projections/parser_table.rb +53 -12
  290. data/lib/hecks/projections/reference.rb +15 -3
  291. data/lib/hecks/projections/rust_vocabulary.rb +646 -0
  292. data/lib/hecks/projections/shape.rb +10 -2
  293. data/lib/hecks/projections/statements.rb +69 -22
  294. data/lib/hecks/projections/vocabulary.rb +26 -9
  295. data/lib/hecks/projections.rb +5 -3
  296. data/lib/hecks/projector/cli_projector.rb +245 -37
  297. data/lib/hecks/projector/docs_projector.rb +154 -28
  298. data/lib/hecks/projector/exporter.rb +104 -29
  299. data/lib/hecks/projector/ir_projector.rb +7 -1
  300. data/lib/hecks/projector/narrate_projector.rb +144 -23
  301. data/lib/hecks/projector/target.rb +42 -18
  302. data/lib/hecks/projector.rb +86 -18
  303. data/lib/hecks/query_ir.rb +94 -47
  304. data/lib/hecks/query_specification/common/comparators.rb +19 -3
  305. data/lib/hecks/query_specification/common/comparison.rb +109 -27
  306. data/lib/hecks/query_specification/common/dsl.rb +65 -9
  307. data/lib/hecks/query_specification/common/null_policy.rb +61 -15
  308. data/lib/hecks/query_specification/common/null_semantics.rb +4 -0
  309. data/lib/hecks/query_specification/common/options.rb +25 -0
  310. data/lib/hecks/query_specification/field_path.rb +69 -15
  311. data/lib/hecks/query_specification/hop_path.rb +57 -20
  312. data/lib/hecks/query_specification/read_model/specification.rb +4 -0
  313. data/lib/hecks/rendering.rb +9 -3
  314. data/lib/hecks/router/namespace_installer.rb +16 -3
  315. data/lib/hecks/router.rb +56 -1
  316. data/lib/hecks/runtime/aggregate_lock.rb +20 -11
  317. data/lib/hecks/runtime/boot_gates.rb +21 -3
  318. data/lib/hecks/runtime/caller.rb +40 -8
  319. data/lib/hecks/runtime/capability_graph.rb +13 -2
  320. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +40 -41
  321. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +45 -67
  322. data/lib/hecks/runtime/command_interpreter.rb +195 -113
  323. data/lib/hecks/runtime/command_rules/admissibility.rb +231 -80
  324. data/lib/hecks/runtime/command_rules/arithmetic.rb +154 -96
  325. data/lib/hecks/runtime/command_rules/authorization.rb +38 -17
  326. data/lib/hecks/runtime/command_rules/emission.rb +18 -1
  327. data/lib/hecks/runtime/command_rules/references.rb +145 -55
  328. data/lib/hecks/runtime/command_rules.rb +3 -0
  329. data/lib/hecks/runtime/dependency_planning.rb +56 -11
  330. data/lib/hecks/runtime/dispatcher.rb +263 -92
  331. data/lib/hecks/runtime/entity_element.rb +304 -67
  332. data/lib/hecks/runtime/entity_interpreter.rb +149 -88
  333. data/lib/hecks/runtime/errors.rb +37 -23
  334. data/lib/hecks/runtime/event.rb +15 -10
  335. data/lib/hecks/runtime/identity.rb +91 -23
  336. data/lib/hecks/runtime/instance.rb +103 -18
  337. data/lib/hecks/runtime/interpreting.rb +24 -16
  338. data/lib/hecks/runtime/invocation.rb +358 -0
  339. data/lib/hecks/runtime/loader.rb +105 -19
  340. data/lib/hecks/runtime/outbox.rb +164 -26
  341. data/lib/hecks/runtime/policy_interpreter.rb +73 -60
  342. data/lib/hecks/runtime/port_operation_interpreter.rb +42 -19
  343. data/lib/hecks/runtime/query_interpreter.rb +93 -74
  344. data/lib/hecks/runtime/reaction_invocation.rb +73 -28
  345. data/lib/hecks/runtime/read_model_interpreter.rb +60 -44
  346. data/lib/hecks/runtime/rebuild_sweep.rb +32 -4
  347. data/lib/hecks/runtime/reference_hop.rb +48 -6
  348. data/lib/hecks/runtime/refusal_wording.rb +142 -115
  349. data/lib/hecks/runtime/registry/saga_persistence.rb +32 -21
  350. data/lib/hecks/runtime/registry/verification.rb +153 -28
  351. data/lib/hecks/runtime/registry.rb +202 -32
  352. data/lib/hecks/runtime/remote_dispatcher.rb +125 -24
  353. data/lib/hecks/runtime/routing.rb +36 -154
  354. data/lib/hecks/runtime/saga_interpreter/correlation.rb +25 -27
  355. data/lib/hecks/runtime/saga_interpreter.rb +90 -76
  356. data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
  357. data/lib/hecks/runtime/tenant_check.rb +33 -13
  358. data/lib/hecks/runtime/tenant_scope.rb +23 -5
  359. data/lib/hecks/runtime/value/admission.rb +75 -30
  360. data/lib/hecks/runtime/value/coercion.rb +555 -142
  361. data/lib/hecks/runtime/value/entity_list_coercion.rb +132 -60
  362. data/lib/hecks/runtime/value.rb +71 -21
  363. data/lib/hecks/runtime.rb +39 -7
  364. data/lib/hecks/storehouse.rb +368 -72
  365. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +130 -0
  366. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +32 -0
  367. data/lib/hecks/version.rb +3 -3
  368. data/lib/hecks/vocabulary.rb +205 -4
  369. data/lib/hecks.rb +91 -11
  370. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +19 -11
  371. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +24 -12
  372. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +38 -14
  373. metadata +28 -2
@@ -1,134 +1,161 @@
1
+ require_relative "../vocabulary"
2
+
1
3
  module Hecks
2
4
  module Runtime
3
5
  # Every DomainRefusal wording that is not already data — `given`/
4
6
  # `ensures`/a declared `invariant` already carry their own description,
5
7
  # read at dispatch time off the command or value object that declared
6
- # them. These are different in kind: LANGUAGE-LEVEL refusals, the same
8
+ # them. These are different in kind: language-level refusals, the same
7
9
  # wording for every domain, not authored per-bluebook.
8
10
  #
9
- # Declared the same way in Vocabulary::RefusalTemplate
10
- # (language/bluebook/vocabulary.bluebook) — spec/refusal_wording_
11
- # conformance_spec holds this table equal to the language, both
12
- # directions. Hand-typed rather than read live off the meta-domain at
13
- # every dispatch, the same reason Expression::Evaluator::
14
- # SIGN_TEST_OPERATORS is hand-typed beside Vocabulary::SignTest.
11
+ # Read off the generated table, not typed a second time. The rows are
12
+ # Vocabulary::RefusalTemplate (language/bluebook/vocabulary.bluebook),
13
+ # projected into lib/hecks/vocabulary.rb by bin/project_vocabulary and
14
+ # into rust/src/kernel/vocab/refusal_template.rs by
15
+ # bin/project_rust_vocabulary; both regenerations are diffed in CI, so
16
+ # there is no hand copy left here to drift. Declared order is kept.
17
+ #
18
+ # **The arguments are data too**. Vocabulary::RefusalSiteArgument names the
19
+ # values each site takes and how each is written (a list's separator,
20
+ # its sort, its empty reading, its quoting). Call sites use
21
+ # `render_site` and hand over raw values; the Rust kernel's typed
22
+ # `render_args` applies the same rows, and the projection that writes it
23
+ # pins every site's output against `format_argument`/`substitute` here.
15
24
  module RefusalWording
16
- # `Layout/HashAlignment`'s repo-wide `table` style (.rubocop.yml)
17
- # would force every template string below onto the SAME column —
18
- # matching the widest key — leaving the longer wordings almost no
19
- # room to wrap under Layout/LineLength's own 130-column limit.
20
- # Disabled for exactly this one hash literal (a single statement, so
21
- # disable-next rather than a disable/enable pair).
22
- # rubocop:disable-next Layout/HashAlignment
23
- TEMPLATES = {
24
- %w[NotFound creating_no_identity] =>
25
- "{command} creates a {aggregate} — pass {identity}:",
26
- %w[AlreadyExists creating_duplicate] =>
27
- "{command} creates a {aggregate} that already exists — {identity} {offered}",
28
- %w[AlreadyExists entity_duplicate] =>
29
- "a {entity} already exists on {aggregate} — {identity} {offered}",
30
- %w[NotFound acting_no_identity] =>
31
- "{command} acts on an existing {aggregate} — pass {identity}:",
32
- %w[NotFound record_missing] =>
33
- "no {aggregate} with {identity} {offered}",
34
- %w[NotFound entity_parent_no_identity] =>
35
- "{command} acts on a {aggregate}'s {entity} — pass {identity}:",
36
- %w[UnknownVerb entity_unknown] =>
37
- "{aggregate} has no entity {entity}",
38
- %w[NotFound entity_element_no_identity] =>
39
- "{command} acts on one {entity} — pass {identity}:",
40
- %w[NotFound entity_element_missing] =>
41
- "no {entity} with {identity} {wants} on {aggregate} {parent_id}",
42
- %w[NotFound reference_target_missing] =>
43
- "no {target} with {heads} {key}",
44
- %w[NotFound read_model_reference_missing] =>
45
- "no {aggregate} with reference {offered}",
46
- %w[TypeMismatch read_model_object_reference] =>
47
- "{query} refused — a reference is an id, and {field} arrived as an object",
48
- %w[UnknownVerb no_query] =>
49
- "{aggregate} has no query {query}",
50
- %w[UnknownVerb entity_query_missing] =>
51
- "{entity} has no query {query}",
52
- %w[UnknownVerb entity_holds_no_list] =>
53
- "{aggregate} holds no list of {entity}",
54
- %w[UnknownVerb entity_no_command] =>
55
- "{entity} has no command {command}",
56
- %w[UnknownVerb aggregate_no_command] =>
57
- "{aggregate} has no command {command}",
58
- %w[UnknownVerb port_no_operation] =>
59
- "{port} has no operation {operation}",
60
- %w[UnknownVerb no_domain] =>
61
- "no domain {domain} loaded (verb {verb})",
62
- %w[UnknownVerb no_read_model] =>
63
- "{domain} has no read model {query}",
64
- %w[UnknownVerb not_fully_qualified] =>
65
- "{verb} is not a fully-qualified verb (Domain::Aggregate.Command)",
66
- %w[UnknownVerb no_aggregate] =>
67
- "{domain} has no aggregate {aggregate}",
68
- %w[LifecycleRefused transition_blocked] =>
69
- "{command} refused — {field} is {current}, and {command} moves it only from {allowed}",
70
- %w[TypeMismatch value_object_shape] =>
71
- "{name} is a {type} — pass its fields as an object, not {offered}",
72
- %w[TypeMismatch reference_wrong_shape] =>
73
- "{command} refused — a reference is an id, and {attribute} arrived as {offered}{known_by}",
74
- %w[TypeMismatch multi_field_scalar] =>
75
- "{type} has multiple fields and cannot stand in for a scalar",
76
- %w[TypeMismatch composite_identity] =>
77
- "{type} is a composite identity — an identity must have exactly one field",
78
- %w[TypeMismatch numeric_field] =>
79
- "{type}.{field} expects {expected}, got {offered}",
80
- %w[TypeMismatch non_finite_field] =>
81
- "{type}.{field} must be a finite number, got {offered}",
82
- %w[TypeMismatch integer_range] =>
83
- "{type}.{field} must fit in a 64-bit integer, got {offered}",
84
- %w[TypeMismatch pattern_mismatch] =>
85
- "{type}.{field} must match {pattern}, got {offered}",
86
- %w[TypeMismatch arithmetic_amount] =>
87
- "{op} of {target} needs an Integer, got {offered}",
88
- %w[TypeMismatch arithmetic_current] =>
89
- "{op} of {target} needs an Integer {target}, got {offered}",
90
- %w[TypeMismatch arithmetic_shared_field] =>
91
- "{op} of {target} needs a value object with one shared Integer field",
92
- %w[UnknownArgument unknown_args] =>
93
- "{command} does not declare {unknown} — it takes {declared}",
94
- %w[AbsentArgument absent_args] =>
95
- "{command} was not given {absent} — it takes {declared}",
96
- %w[InvariantViolation closed_set_member] =>
97
- "{type} admits {admitted} — got {offered}",
98
- %w[InvariantViolation value_object_invariant] =>
99
- "{name} invariant violated — {description} (given {offered})",
100
- %w[InvariantViolation admits_declared_set] =>
101
- "{name} admits {admits} — {admitted} — got {offered}",
102
- %w[InvariantViolation undeclared_set] =>
103
- "{name} admits {admits}, which this chapter does not declare — a closed set is named Aggregate::SetName, and it must " \
104
- "be one the bluebook actually holds",
105
- %w[Unauthorized tenant_required] =>
106
- "{query} declares authorize with tenant: {field} — pass {field}: to name which {field} this ask is scoped to",
107
- %w[Unauthorized role_mismatch] =>
108
- "{command} refused — role: {role}, and the caller stated {caller_role}",
109
- %w[Unauthorized cross_tenant_reference] =>
110
- "{aggregate} {field} is {tenant}, but {attribute} names a {target} whose own {target_field} " \
111
- "is {other} — a cross-tenant reference",
112
- %w[AttributeAbsent absent_read] =>
113
- "{aggregate} {field} is absent on this record — declared, not optional, and added since it was written. Backfill it " \
114
- "in a translation (backfill :{field}, default: ...), or declare it optional: true",
115
- %w[ProjectionAbsent absent_read] =>
116
- "{aggregate} {field} is not yet projected on this record — declared via projects :{field}, but no rebuild sweep has " \
117
- "populated it. Run the sweep, or read {reference}.{remote_field} directly if this rule cannot wait"
118
- }.freeze
25
+ TEMPLATES = Hecks::Vocabulary.rows("RefusalTemplate")
26
+ .to_h { |row| [[row["refusal"], row["site"]].freeze, row["template"]] }
27
+ .freeze
28
+
29
+ SHAPES = %w[scalar list].freeze
30
+ QUOTINGS = %w[none inspect].freeze
119
31
 
120
32
  module_function
121
33
 
34
+ # Renders `refusal`/`site`'s template, substituting `values` verbatim.
35
+ #
122
36
  # Plain text substitution, never expression syntax — a template is
123
- # read, not evaluated. `render` computes the placeholder VALUES via
124
- # whatever the call site already had (a joined list, a rendered
125
- # identity reading, …) and this only replaces the markers.
37
+ # read, not evaluated. Values arrive already formatted; prefer
38
+ # `render_site`, which formats them off the declared rows.
39
+ #
40
+ # @param refusal [String] the `DomainRefusal` class name, such as `"UnknownArgument"`
41
+ # @param site [String] the template site within `refusal`, such as `"unknown_args"`
42
+ # @param values [Hash{Symbol => #to_s}] each `{name}` placeholder's already-formatted
43
+ # replacement text
44
+ # @return [String] the rendered refusal message
45
+ # @raise [KeyError] if no `RefusalTemplate` row declares `refusal`/`site`
126
46
  def render(refusal, site, **values)
127
- template = TEMPLATES.fetch([refusal, site]) do
47
+ substitute(template(refusal, site), values)
48
+ end
49
+
50
+ # The one door call sites use: exactly the arguments the site
51
+ # declares (a missing or undeclared one raises ArgumentError before
52
+ # any wording exists), each formatted by its RefusalSiteArgument row,
53
+ # substituted in declared order.
54
+ #
55
+ # RefusalWording.render_site("UnknownArgument", "unknown_args",
56
+ # command: "Close", unknown: [:parcel], declared: [])
57
+ # # => "Close does not declare parcel — it takes none"
58
+ #
59
+ # @param refusal [String] the `DomainRefusal` class name, such as `"UnknownArgument"`
60
+ # @param site [String] the template site within `refusal`, such as `"unknown_args"`
61
+ # @param arguments [Hash{Symbol => Object}] raw values, one per `RefusalSiteArgument`
62
+ # row declared for `refusal`/`site`; formatted per row before substitution
63
+ # @return [String] the rendered refusal message
64
+ # @raise [ArgumentError] if `arguments` is missing a declared argument or offers one
65
+ # `refusal`/`site` does not declare
66
+ # @raise [KeyError] if no `RefusalTemplate`/`RefusalSiteArgument` rows declare
67
+ # `refusal`/`site`
68
+ def render_site(refusal, site, **arguments)
69
+ specs = argument_rows(refusal, site)
70
+ declared = specs.map { |spec| spec["argument"].to_sym }
71
+ missing = declared - arguments.keys
72
+ extra = arguments.keys - declared
73
+ if missing.any? || extra.any?
74
+ raise ArgumentError, "#{refusal}/#{site} takes #{declared.join(', ')} — " \
75
+ "missing: #{missing.join(', ')}; undeclared: #{extra.join(', ')}"
76
+ end
77
+
78
+ render_with(template(refusal, site), specs, arguments)
79
+ end
80
+
81
+ # `render_site` without the registry lookups: a template, its
82
+ # argument rows, and raw values. The Rust projection calls this with
83
+ # the chapter's own rows to compute the expected wording it pins.
84
+ #
85
+ # @param template [String] the raw template, `{name}` placeholders unsubstituted
86
+ # @param specs [Array<Hash>] the `RefusalSiteArgument` rows to format `arguments`
87
+ # against, one per declared argument name
88
+ # @param arguments [Hash{Symbol => Object}] raw values, one per entry in `specs`
89
+ # @return [String] the rendered refusal message
90
+ def render_with(template, specs, arguments)
91
+ values = specs.to_h do |spec|
92
+ name = spec["argument"].to_sym
93
+ [name, format_argument(spec, arguments.fetch(name))]
94
+ end
95
+ substitute(template, values)
96
+ end
97
+
98
+ # One argument, written the way its row says. A list is sorted first
99
+ # (before quoting), then each item quoted, then joined; an empty list
100
+ # reads `when_empty`. A scalar is quoted or taken as its own text.
101
+ #
102
+ # @param spec [Hash] the argument's `RefusalSiteArgument` row (`"shape"`, `"quoting"`,
103
+ # and, for a list, `"sorted"`, `"separator"`, `"when_empty"`)
104
+ # @param value [Object, Array] the raw value to format; an Array (or anything
105
+ # `Array()`-coercible) for a `"list"`-shaped spec, a scalar otherwise
106
+ # @return [String] the formatted text
107
+ def format_argument(spec, value)
108
+ inspect = spec.fetch("quoting") == "inspect"
109
+ return inspect ? value.inspect : value.to_s unless spec.fetch("shape") == "list"
110
+
111
+ items = Array(value)
112
+ items = items.sort if spec.fetch("sorted") == "true"
113
+ items = items.map(&:inspect) if inspect
114
+ items.empty? ? spec.fetch("when_empty") : items.join(spec.fetch("separator"))
115
+ end
116
+
117
+ # Replaces each `{name}` placeholder in `template` with its value's text.
118
+ #
119
+ # @param template [String] the raw template, `{name}` placeholders unsubstituted
120
+ # @param values [Hash{Symbol => #to_s}] each placeholder name mapped to its
121
+ # replacement text
122
+ # @return [String] `template` with every `{name}` placeholder substituted
123
+ def substitute(template, values)
124
+ values.reduce(template) { |text, (key, value)| text.gsub("{#{key}}", value.to_s) }
125
+ end
126
+
127
+ # Looks up the raw template text declared for one refusal site.
128
+ #
129
+ # @param refusal [String] the `DomainRefusal` class name, such as `"UnknownArgument"`
130
+ # @param site [String] the template site within `refusal`, such as `"unknown_args"`
131
+ # @return [String] the raw `RefusalTemplate` text, `{name}` placeholders unsubstituted
132
+ # @raise [KeyError] if no `RefusalTemplate` row declares `refusal`/`site`
133
+ def template(refusal, site)
134
+ TEMPLATES.fetch([refusal, site]) do
128
135
  raise KeyError, "no refusal template for #{refusal}/#{site} — declare it in " \
129
136
  "Vocabulary::RefusalTemplate first"
130
137
  end
131
- values.reduce(template) { |text, (key, value)| text.gsub("{#{key}}", value.to_s) }
138
+ end
139
+
140
+ # The declared arguments for one refusal site.
141
+ #
142
+ # Read lazily, not into a constant: bin/project_vocabulary boots
143
+ # `hecks` (and so this file) before it writes the table a newly
144
+ # declared site's rows live in.
145
+ #
146
+ # @param refusal [String] the `DomainRefusal` class name, such as `"UnknownArgument"`
147
+ # @param site [String] the template site within `refusal`, such as `"unknown_args"`
148
+ # @return [Array<Hash>] the `RefusalSiteArgument` rows declared for `refusal`/`site`
149
+ # @raise [KeyError] if no `RefusalSiteArgument` rows declare `refusal`/`site`
150
+ def argument_rows(refusal, site)
151
+ @argument_rows ||= Hecks::Vocabulary.rows("RefusalSiteArgument")
152
+ .group_by { |row| [row["refusal"], row["site"]] }
153
+ .transform_values(&:freeze)
154
+ .freeze
155
+ @argument_rows.fetch([refusal, site]) do
156
+ raise KeyError, "no refusal arguments for #{refusal}/#{site} — declare them in " \
157
+ "Vocabulary::RefusalSiteArgument first"
158
+ end
132
159
  end
133
160
  end
134
161
  end
@@ -3,43 +3,52 @@ require_relative "../saga_pending_dispatch"
3
3
  module Hecks
4
4
  module Runtime
5
5
  class Registry
6
- # SAGA PERSISTENCE — no new DSL verb, no new world setting.
6
+ # **Saga persistence** — no new DSL verb, no new world setting.
7
7
  # Whatever adapter a domain's own aggregates already use for
8
8
  # durability, its sagas use the same one, automatically: three
9
- # OPTIONAL adapter methods (`save_saga`/`delete_saga`/`each_saga`),
9
+ # optional adapter methods (`save_saga`/`delete_saga`/`each_saga`),
10
10
  # `respond_to?`-checked the same way `Ports::Persistence::AppendOnly`
11
11
  # already treats an adapter's own optional methods
12
12
  # (`find`/`all`/`count`/`reset!`/`events`/`record_event`).
13
13
  module SagaPersistence
14
- # Resolved and memoized per domain — the FIRST real aggregate
14
+ # Resolved and memoized per domain — the first real aggregate
15
15
  # declared in the domain's own bluebook is the resolution
16
16
  # anchor. `Ports::Persistence::BindingPolicy.resolve` for that
17
17
  # aggregate already falls back to a domain-level default bind
18
18
  # (§0's `Hecksagon#bind_for`) before falling back to Memory, so
19
19
  # this needs no separate "was a default declared" branch of its
20
- # own — whatever adapter that anchor resolves to already IS the
20
+ # own — whatever adapter that anchor resolves to already is the
21
21
  # domain's own default in the normal case (every aggregate
22
22
  # shares one adapter), and is the honest, documented fallback
23
23
  # for the rarer domain genuinely split across more than one
24
24
  # local adapter with no default declared.
25
25
  #
26
- # GENUINELY LAZY, GENUINELY POST-BOOT — unlike `rehydrate_sagas!`
26
+ # Genuinely lazy, genuinely POST-boot — unlike `rehydrate_sagas!`
27
27
  # below (boot-only), this is called from live dispatch
28
28
  # (`SagaInterpreter#checkpoint`/`#end_saga`, on every saga
29
- # transition), so the FIRST call for a given domain can come from
29
+ # transition), so the first call for a given domain can come from
30
30
  # any dispatching thread, not just the boot thread. `@saga_persistence`
31
31
  # itself is a plain Hash stood up once in `Registry#initialize` (no
32
32
  # race on the container), but `resolve_saga_persistence` is real work
33
33
  # (a `BindingPolicy.resolve` plus a lazy `repository` build) whose
34
- # RESULT — the actual adapter instance a domain's sagas persist
35
- # through — must be the SAME object for every caller: two threads
34
+ # result — the actual adapter instance a domain's sagas persist
35
+ # through — must be the same object for every caller: two threads
36
36
  # racing the first lookup and each building their own adapter would
37
37
  # silently split one domain's saga writes across two adapter
38
38
  # instances (worse than `Dispatcher#reaction_depth`'s M20 — that bug
39
- # corrupted a counter; this one can corrupt WHICH STORE a saga's
40
- # state lands in). Double-checked locking against a DEDICATED mutex
39
+ # corrupted a counter; this one can corrupt which store a saga's
40
+ # state lands in). Double-checked locking against a dedicated mutex
41
41
  # — never `@saga_mutex` — see `Registry#initialize`'s own comment for
42
42
  # why reusing that one would deadlock.
43
+ # Resolves and memoizes the adapter a domain's sagas persist through.
44
+ #
45
+ # @param domain [String, Symbol] the domain to resolve saga persistence for
46
+ # @return [Adapters::Heki, Adapters::Postgres, Adapters::Sqlite, Adapters::D1,
47
+ # Ports::Persistence::Plugins::Era::PostgresEra, Ports::Persistence::NullSagaStore]
48
+ # the same adapter instance the domain's anchor aggregate persists through, when it
49
+ # implements `save_saga`; `NULL_SAGA_STORE` for a domain with no anchor aggregate, an
50
+ # adapter that does not implement the capability, a `RemoteRuntime`-shaped adapter,
51
+ # or a `Runtime::WiringError` resolving the anchor's own bind
43
52
  def saga_persistence(domain)
44
53
  key = domain.to_s
45
54
  @saga_persistence[key] || @saga_persistence_mutex.synchronize do
@@ -47,26 +56,28 @@ module Hecks
47
56
  end
48
57
  end
49
58
 
50
- # WALKS EVERY LOADED DOMAIN, repopulating `saga_instances` from
59
+ # Walks every loaded domain, repopulating `saga_instances` from
51
60
  # whatever `saga_persistence(domain)` resolves to — a real store
52
61
  # for a domain whose adapter answers the capability, `each_saga`
53
62
  # yielding real rows; `NULL_SAGA_STORE`'s own `each_saga` for
54
63
  # everything else, which never yields at all, so this needs no
55
- # `respond_to?` branch of its own — the SAME reason every other
64
+ # `respond_to?` branch of its own — the same reason every other
56
65
  # call site in this capability never needs one. Called once at
57
66
  # boot (`Loader.boot`, between `verify!` and dispatcher
58
67
  # construction) — a process that's been running has no reason to
59
68
  # re-walk its own already-current `saga_instances`.
60
69
  #
61
- # BOOT-TIME-ONLY (OR ITS TEST-RUNNER EQUIVALENT) — this method's
70
+ # Boot-time-only (or its test-runner equivalent) — this method's
62
71
  # only callers are `Loader.run_boot_gates!` (single-threaded, before
63
72
  # `dispatcher_for` ever exists) and `Registry#reset_runtime_state!`
64
73
  # (single-threaded test runner — see that method's own comment).
65
74
  # Never called from live dispatch, so `@saga_instances` mutation
66
- # here has no concurrent caller to race, unlike its OTHER two write
75
+ # here has no concurrent caller to race, unlike its other two write
67
76
  # points inside `@saga_mutex.synchronize` blocks (`saga_interpreter.
68
77
  # rb`), which genuinely do and are guarded accordingly.
69
78
  # rubocop:disable-next Hecks/ThreadSharedIvarMutation
79
+ #
80
+ # @return [Hecks::Runtime::Registry] self
70
81
  def rehydrate_sagas!
71
82
  @hecksagons.each_key do |domain|
72
83
  saga_persistence(domain).each_saga do |process_manager, correlation, state, memory, completed_compensations = []|
@@ -81,28 +92,28 @@ module Hecks
81
92
 
82
93
  private
83
94
 
84
- # THE OTHER HALF OF THE OUTBOX-SHAPED FIX (see saga_pending_
95
+ # The other half of the outbox-shaped fix (see saga_pending_
85
96
  # dispatch.rb) — `SAGA_PENDING_DISPATCH_KEY`, if the crashed
86
97
  # process left it standing, means the row's own `state` was
87
98
  # checkpointed but the dispatch cascade that justifies it may
88
99
  # never have run. Stripped out of `memory` before it becomes
89
- # this instance's LIVE `:memory` (so nothing downstream — a
100
+ # this instance's live `:memory` (so nothing downstream — a
90
101
  # `given`, a `with:` mapping, the fuzzer — ever sees it), and
91
- # surfaced loudly instead: a WARNING plus a `saga_log` entry,
102
+ # surfaced loudly instead: a warning plus a `saga_log` entry,
92
103
  # never an automatic redrive (see saga_pending_dispatch.rb for
93
104
  # why redriving without idempotent delivery would be worse than
94
- # the stall). This is real, durable crash-recovery VISIBILITY —
105
+ # the stall). This is real, durable crash-recovery visibility —
95
106
  # still not the reconciliation itself, which stays a human's
96
107
  # call until hecks has idempotent redelivery to make it safe.
97
108
  # rubocop:disable-next Hecks/ThreadSharedIvarMutation -- same
98
109
  # justification as `rehydrate_sagas!`'s own disable comment
99
- # above: this method's ONLY caller is that boot-time-only walk,
110
+ # above: this method's only caller is that boot-time-only walk,
100
111
  # never live dispatch, so `@saga_log` has no concurrent writer
101
112
  # to race here.
102
113
  def warn_stalled_saga(domain, process_manager, correlation, state, pending)
103
114
  # `.transform_keys(&:to_sym)` — Heki's own `each_saga` only
104
- # symbolizes `memory`'s TOP-level keys (`SagaStore#each_saga`'s
105
- # own `transform_keys`, one level deep); a value NESTED under
115
+ # symbolizes `memory`'s top-level keys (`SagaStore#each_saga`'s
116
+ # own `transform_keys`, one level deep); a value nested under
106
117
  # one of those keys, like this marker, comes back with plain
107
118
  # string keys from Heki specifically, symbol keys already from
108
119
  # Postgres/SQLite/D1's own `symbolize_names: true` parse. Normalizing