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
@@ -10,9 +10,19 @@ module Hecks
10
10
  # loading, and the smaller checks are also called piecemeal by the
11
11
  # repository factory.
12
12
  module Verification
13
+ # Runs the whole wiring gate against this registry's loaded bluebooks,
14
+ # hexagons, ports and adapters.
15
+ #
16
+ # @return [Runtime::Registry] self
17
+ # @raise [Runtime::WiringError] if a bind names an undeclared aggregate, an
18
+ # adapter cannot satisfy its port's verb or declared `answers`, a world
19
+ # setting names a field its adapter does not declare, the default adapter
20
+ # is unusable, or a command declares a role with no authorization provider
21
+ # attached
13
22
  def verify!
14
23
  verify_default_adapter!
15
24
  verify_singleton_port_answers!
25
+ refuse_cross_package_bluebook_merge!
16
26
 
17
27
  @hecksagons.each_value do |hexagon|
18
28
  refuse_ungoverned_roles!(hexagon)
@@ -21,11 +31,11 @@ module Hecks
21
31
  # A domain-level default (§0) — `persisted_by "Heki"` bare,
22
32
  # applying to whichever aggregates don't override it — names
23
33
  # no aggregate of its own, so there's nothing to look up in
24
- # the bluebook for THIS row specifically. Still validate its
34
+ # the bluebook for this row specifically. Still validate its
25
35
  # own adapter/verb shape (the same reason
26
36
  # `verify_default_adapter!` checks the framework-wide
27
37
  # default the same way, aggregate-less). Coverage of real
28
- # aggregates that only resolve THROUGH this default comes
38
+ # aggregates that only resolve through this default comes
29
39
  # from their own dispatch-time `BindingPolicy.resolve` —
30
40
  # deliberately not required to be exhaustive here, the same
31
41
  # leniency this method already extended to any aggregate
@@ -50,6 +60,13 @@ module Hecks
50
60
  self
51
61
  end
52
62
 
63
+ # Checks that the framework-wide default persistence adapter (used by any
64
+ # aggregate left out of an explicit bind list) is itself wired correctly.
65
+ #
66
+ # @return [Runtime::Registry] self
67
+ # @raise [Runtime::WiringError] if the default adapter cannot satisfy the
68
+ # persistence port's verb, is missing a declared `answers` method, or has
69
+ # no Ruby implementation
53
70
  def verify_default_adapter!
54
71
  name = Ports::Persistence::DEFAULT_ADAPTER
55
72
 
@@ -68,6 +85,14 @@ module Hecks
68
85
  "aggregate with no bind could not be given one: #{e.message}"
69
86
  end
70
87
 
88
+ # Checks that `bind`'s adapter implements the port it names and satisfies
89
+ # the verb the bind declares.
90
+ #
91
+ # @param bind [Bluebook::Bind] the bind to check
92
+ # @return [void]
93
+ # @raise [Runtime::WiringError] if `bind`'s adapter is unknown, declares an
94
+ # unknown port, is missing a declared `answers` method, or cannot satisfy
95
+ # `bind`'s own verb
71
96
  def check_verb(bind)
72
97
  port = port_for(bind)
73
98
  check_answers(port, bind.adapter)
@@ -78,13 +103,20 @@ module Hecks
78
103
  "and cannot satisfy #{bind.verb}"
79
104
  end
80
105
 
81
- # THE METHOD CONTRACT A `.port` FILE'S `verb`/`signal` NEVER
82
- # CARRIED — an adapter can name the right port, satisfy the right
106
+ # The method contract a `.port` file's `verb`/`signal` never
107
+ # carried — an adapter can name the right port, satisfy the right
83
108
  # verb, and admit every `.world` setting `check_settings` checks,
84
109
  # and still be missing the one method a live dispatch will
85
110
  # actually call. `answers` is optional per port (an empty list is
86
111
  # today's pre-existing behavior, unchecked), so this only ever
87
112
  # tightens a port that opted in.
113
+ #
114
+ # @param port [Bluebook::Port] the port whose declared `answers` methods
115
+ # `adapter_name` must respond to
116
+ # @param adapter_name [String] the adapter's declared name to check
117
+ # @return [void]
118
+ # @raise [Runtime::WiringError] if `adapter_name` has no Ruby implementation,
119
+ # or its implementation does not respond to one of `port.answers`
88
120
  def check_answers(port, adapter_name)
89
121
  answers = Array(port.answers)
90
122
  return if answers.empty?
@@ -99,7 +131,7 @@ module Hecks
99
131
  "#{answers.map(&:inspect).join(', ')}"
100
132
  end
101
133
 
102
- # THE NINE SINGLETON PORTS' OWN GAP — `persistence`, `projection`
134
+ # **The nine singleton ports' own gap** — `persistence`, `projection`
103
135
  # and `loading` are per-aggregate bindings, checked above through
104
136
  # every real `bind` a hexagon declares; a singleton port
105
137
  # (`clock`, `authorization`, …) is never bound to an aggregate at
@@ -108,12 +140,18 @@ module Hecks
108
140
  # already refuses zero or multiple implementations, live, at
109
141
  # first dispatch — that stays exactly as-is here (0 or 2+ is
110
142
  # ambiguity, not a method-contract question, and asserting every
111
- # declared port MUST have exactly one adapter would wrongly
143
+ # declared port must have exactly one adapter would wrongly
112
144
  # refuse a boot that simply never wires a port it doesn't use).
113
- # This only ever tightens the ONE case those checks don't cover:
145
+ # This only ever tightens the one case those checks don't cover:
114
146
  # exactly one adapter, wired, missing a method `answers` names.
115
147
  PER_AGGREGATE_PORTS = %w[persistence projection loading].freeze
116
148
 
149
+ # Checks every singleton port (not per-aggregate-bound) with exactly one
150
+ # wired adapter against its own declared `answers` methods.
151
+ #
152
+ # @return [Runtime::Registry] self
153
+ # @raise [Runtime::WiringError] if a singleton port's one wired adapter is
154
+ # missing one of its declared `answers` methods
117
155
  def verify_singleton_port_answers!
118
156
  @ports.each_value do |port|
119
157
  next if PER_AGGREGATE_PORTS.include?(port.name)
@@ -127,6 +165,16 @@ module Hecks
127
165
  self
128
166
  end
129
167
 
168
+ # Checks that every setting `settings` declares (besides `:adapter`) is a
169
+ # field `bind`'s adapter actually admits.
170
+ #
171
+ # @param bind [Bluebook::Bind] the bind naming the adapter to check against;
172
+ # a no-op if its adapter is unknown
173
+ # @param settings [Hash{Symbol => Object}] the world's declared settings for
174
+ # this bind
175
+ # @return [void]
176
+ # @raise [Runtime::WiringError] if `settings` declares a field `bind`'s
177
+ # adapter does not declare
130
178
  def check_settings(bind, settings)
131
179
  adapter = @adapters[bind.adapter]
132
180
  return unless adapter
@@ -141,6 +189,12 @@ module Hecks
141
189
  "Add the field to the adapter, or remove it from the world."
142
190
  end
143
191
 
192
+ # Finds the port `bind`'s adapter declares.
193
+ #
194
+ # @param bind [Bluebook::Bind] the bind naming the adapter to look up
195
+ # @return [Bluebook::Port] the port `bind`'s adapter declares
196
+ # @raise [Runtime::WiringError] if `bind` names an unknown adapter, or one
197
+ # declaring an unknown port
144
198
  def port_for(bind)
145
199
  adapter = @adapters[bind.adapter]
146
200
  raise WiringError, "unknown adapter #{bind.adapter.inspect}" unless adapter
@@ -149,6 +203,12 @@ module Hecks
149
203
  raise(WiringError, "adapter #{bind.adapter} declares unknown port #{adapter.port.inspect}")
150
204
  end
151
205
 
206
+ # Finds the Ruby module implementing the adapter declared `name`.
207
+ #
208
+ # @param name [String] the adapter's declared name, such as `"PostgresEra"`
209
+ # @return [Module] the adapter module or class under `Hecks::Adapters`
210
+ # @raise [Runtime::WiringError] if no Ruby implementation named `name` exists
211
+ # under `Hecks::Adapters`
152
212
  def adapter_class(name)
153
213
  Adapters.const_get(name)
154
214
  rescue NameError
@@ -158,34 +218,35 @@ module Hecks
158
218
 
159
219
  private
160
220
 
161
- # `role` IS REAL ACCESS CONTROL ONLY WHEN GOVERNANCE CAN CHECK IT
162
- # AGAINST SOMETHING — a command that declares a role but whose
221
+ # `role` is real access control only when governance can check it
222
+ # against something — a command that declares a role but whose
163
223
  # domain never attaches Governance would leave that role forever
164
224
  # unchecked, exactly the defect ADR 0025 §9 names ("role gates
165
225
  # access control by exact string equality ... Governance ...
166
- # connected to none of it"). Checked here, at `verify!` — RECOVERED
167
- # and MOVED, not new: this used to run per-block, at HECKSAGON
168
- # build time (Bluebook::DSL::HecksagonBuilder#build), which broke
169
- # the moment a domain could be split across multiple hecksagon
226
+ # connected to none of it"). Checked here, at `verify!` — recovered
227
+ # and moved, not new: running this per-block, at hecksagon
228
+ # build time (Bluebook::DSL::HecksagonBuilder#build), breaks
229
+ # the moment a domain is split across multiple hecksagon
170
230
  # blocks (base + an `environments/<name>.hecksagon` overlay,
171
231
  # Runtime::Loader.boot's `environment:` — see its own comment for
172
232
  # the recovery provenance): every block but the one declaring
173
233
  # `uses_framework "Governance"` would be refused there, even
174
234
  # though `Registry#add_hecksagon` merges every block for a domain
175
- # into ONE Hecksagon before anything ever dispatches against it.
176
- # Checking the MERGED result once, here, after every file for
235
+ # into one Hecksagon before anything ever dispatches against it.
236
+ # Checking the merged result once, here, after every file for
177
237
  # this domain has loaded, is both more permissive (no need to
178
238
  # repeat `uses_framework` in every file) and strictly more
179
239
  # correct (a check against an incomplete, not-yet-merged
180
240
  # hecksagon can never see the real final shape).
181
241
  #
182
- # GOVERNANCE ITSELF IS EXEMPT — it cannot `uses_framework` its own
183
- # aggregates, and it IS the source of truth a role check runs
184
- # against, the same self-reference `CommandRules::Authorization
185
- # #governance_attached?` grants it at dispatch time.
242
+ # A provider is recognised by its declaration, not its name —
243
+ # `authorization_provider_for` answers for the domain's own
244
+ # chapter too, so Governance (which declares `provides
245
+ # "authorization"`) passes here because of what it declares, and
246
+ # the same rule `CommandRules::Authorization#governance_attached?`
247
+ # applies at dispatch time.
186
248
  def refuse_ungoverned_roles!(hexagon)
187
- return if hexagon.domain == "Governance"
188
- return if hexagon.framework_members.include?("Governance")
249
+ return if authorization_provider_for(hexagon.domain)
189
250
 
190
251
  bluebook_ir = bluebook(hexagon.domain)
191
252
  return unless bluebook_ir
@@ -195,18 +256,82 @@ module Hecks
195
256
 
196
257
  raise WiringError,
197
258
  "#{offender.hecks_fqn} declares role #{offender.role.inspect}, but " \
198
- "#{hexagon.domain}'s hecksagon never uses_framework \"Governance\" — role is only " \
199
- "real access control once Governance is attached to check it against; without that " \
200
- "it is silent decoration, the exact defect this refusal exists to catch"
259
+ "#{hexagon.domain}'s hecksagon never #{authorization_attachment_hint} — role is only " \
260
+ "real access control once an authorization provider is attached to check it against; " \
261
+ "without that it is silent decoration, the exact defect this refusal exists to catch"
201
262
  end
202
263
 
203
- # Every command this domain declares, an aggregate's own AND every
264
+ # The suggestion, derived from whichever framework members actually
265
+ # declare `provides "authorization"` — never a hardcoded name.
266
+ def authorization_attachment_hint
267
+ providers = Framework.providers_of(Bluebook::Capabilities::AUTHORIZATION)
268
+ return "attaches a chapter that provides \"authorization\" (no framework member declares one)" if providers.empty?
269
+
270
+ providers.map { |name| "uses_framework #{name.inspect}" }.join(" or ")
271
+ end
272
+
273
+ # Every command this domain declares, an aggregate's own and every
204
274
  # entity nested inside one — the same reach `refuse_role_mismatch`
205
275
  # itself needs at dispatch time, just walked ahead of time here.
206
276
  def commands_in(bluebook_ir)
207
277
  bluebook_ir.aggregates.flat_map { |aggregate| aggregate.commands + aggregate.entities.flat_map(&:commands) }
208
278
  end
209
279
 
280
+ # TWO UNRELATED PACKAGES, ONE CHAPTER NAME BY COINCIDENCE — the
281
+ # real risk `Registry#bluebook_sources` exists to catch (found
282
+ # live: a stale `vendor/hecksagain` fork's own copy of Governance/
283
+ # Identity/Deploy, still reachable on 4 consuming apps' own load
284
+ # paths alongside the real gem). `BluebookBuilder.build`'s own
285
+ # accumulation (several files declaring the SAME chapter name ON
286
+ # PURPOSE — `lib/hecks/language/bluebook/*.bluebook` all open
287
+ # `Hecks.bluebook "Bluebook"`) is never touched here — that merge
288
+ # stays unconditional, checked only AFTER every file has loaded,
289
+ # the same "check the merged final result once" timing
290
+ # `refuse_ungoverned_roles!` already uses and for the same reason
291
+ # (a check against an incomplete load can never see the real
292
+ # shape). What distinguishes intentional accumulation from
293
+ # coincidence is PACKAGE ROOT, not file identity: files a real
294
+ # gemspec or a `vendor/` boundary already treats as one unit are
295
+ # expected to share a name; files from two DIFFERENT roots never
296
+ # legitimately do.
297
+ def refuse_cross_package_bluebook_merge!
298
+ @bluebook_sources.each do |name, paths|
299
+ roots = paths.map { |path| package_root_for(path) }.uniq
300
+ next if roots.size <= 1
301
+
302
+ raise WiringError,
303
+ "#{name.inspect} is declared by more than one package: #{roots.join(' and ')} — " \
304
+ "these are two unrelated sources sharing a chapter name by coincidence, not one " \
305
+ "domain split across files, and merging their declarations into one chapter is " \
306
+ "almost certainly a stale/vendored copy left on the load path (paths: " \
307
+ "#{paths.join(', ')})"
308
+ end
309
+ end
310
+
311
+ # THE NEAREST BOUNDARY A PATH ALREADY BELONGS TO — a real
312
+ # gemspec (this IS a package, whatever depends on it or vendors
313
+ # it), or a bare `vendor/` path component, treated as its OWN
314
+ # root regardless of what gemspec might sit above it: vendored
315
+ # code should never be considered "the same package" as whatever
316
+ # it's vendored into, even when nothing else marks the boundary.
317
+ # Neither found, the path's own directory is the root — two
318
+ # files with no closer marker only "belong together" if they are
319
+ # literally the same file.
320
+ def package_root_for(path)
321
+ return path.to_s if path.nil?
322
+
323
+ dir = File.dirname(File.expand_path(path))
324
+ loop do
325
+ return "vendor:#{dir}" if File.basename(dir) == "vendor"
326
+ return dir if Dir.glob(File.join(dir, "*.gemspec")).any?
327
+
328
+ parent = File.dirname(dir)
329
+ return dir if parent == dir
330
+
331
+ dir = parent
332
+ end
333
+ end
334
+
210
335
  # A domain that declares a `process_manager` but whose
211
336
  # `saga_persistence` resolves to `NULL_SAGA_STORE` (no anchor
212
337
  # aggregate, a RemoteRuntime-shaped adapter, an adapter that
@@ -220,7 +345,7 @@ module Hecks
220
345
  # 0025 named for an unchecked `role`, here applied to saga
221
346
  # durability instead.
222
347
  #
223
- # A WARNING, NOT A REFUSAL — unlike `refuse_ungoverned_roles!`,
348
+ # A warning, not a refusal — unlike `refuse_ungoverned_roles!`,
224
349
  # running sagas on a store with no `save_saga` is legitimate on
225
350
  # purpose in a fast in-memory test/dev boot (this project's own
226
351
  # `saga_durability_spec.rb` boots a process manager on `Memory`
@@ -228,13 +353,13 @@ module Hecks
228
353
  # refusing the boot outright would break a choice an author made
229
354
  # deliberately. What a deploy needs is for the gap to be loud and
230
355
  # undeniable, not for local dev/test to become impossible.
231
- # THE OUTBOX'S TWIN OF `warn_undurable_sagas!` — a domain that
356
+ # The outbox's twin of `warn_undurable_sagas!` — a domain that
232
357
  # declares anything a commit could owe a reaction to (a policy
233
358
  # listening to one of its events, or a process manager) but is
234
359
  # bound to an adapter with no outbox (`AppendOnly#outbox?`) gets
235
360
  # reactions the pre-outbox way: run inline, lost on a crash
236
361
  # between commit and reaction. A warning, not a refusal, for the
237
- # reason `warn_undurable_sagas!` gives — and Memory HAS an outbox
362
+ # reason `warn_undurable_sagas!` gives — and Memory has an outbox
238
363
  # (in-process, like everything else it holds), so a dev/test
239
364
  # boot stays quiet; this speaks up for the file/remote adapters
240
365
  # that persist state durably but hand reactions to nothing.