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
@@ -11,6 +11,14 @@ module Hecks
11
11
  class << self
12
12
  attr_accessor :resolver
13
13
 
14
+ # Installs a resolver for the duration of a block, restoring the earlier one afterwards
15
+ # even if the block raises.
16
+ #
17
+ # @param resolver [#call] called with the missing constant's name as a Symbol; whatever
18
+ # it returns is what the bare constant evaluates to
19
+ # @yield the DSL code whose undeclared constants the resolver should answer
20
+ # @yieldreturn [Object] any value; it becomes this method's result
21
+ # @return [Object] whatever the block returns
14
22
  def with(resolver)
15
23
  previous = @resolver
16
24
  @resolver = resolver
@@ -19,45 +27,63 @@ module Hecks
19
27
  @resolver = previous
20
28
  end
21
29
 
30
+ # Reports whether a resolver is installed, meaning code is running inside a DSL block.
31
+ #
32
+ # @return [Boolean] true inside a `with` block, or after `resolver=` set one directly
22
33
  def active? = !@resolver.nil?
23
34
  end
24
35
 
25
- # THE SCOPED-CONSTANT BRIDGE (ADR 0025, docs/dsl-work-slices.md's
36
+ # The scoped-constant bridge (ADR 0025, docs/dsl-work-slices.md's
26
37
  # S0b) — a Symbol cannot answer `::`, so `Account::Debit` and
27
38
  # `admits: Account::LedgerDirection` could never resolve past
28
- # their FIRST segment while `Account` returned a bare Symbol
39
+ # their first segment while `Account` returned a bare Symbol
29
40
  # (`resolver = ->(const) { const }`, bluebook_builder.rb's own
30
- # comment on why that was right for a BARE name): Ruby's `::`
41
+ # comment on why that was right for a bare name): Ruby's `::`
31
42
  # operator raises `TypeError` on the returned value before any
32
43
  # DSL code runs at all, unless that value is itself a Module.
33
44
  #
34
- # A REAL `Module` subclass, not a decorated Symbol, for exactly
35
- # that reason — nothing else answers `::`. Every existing
36
- # consumer keeps working duck-typed, not because nothing
37
- # changed: `Attribute#spell`'s `type.is_a?(Module)` branch now
38
- # fires where it used to fall to `type.to_s`, and
39
- # `Naming.demodulise` (`path.split("::").last`) gives the
40
- # IDENTICAL string either way for a single segment — the two
41
- # branches were already equivalent for a name with no `::` in
42
- # it, which is every bareword before this.
45
+ # A real `Module` subclass, not a decorated Symbol, for exactly
46
+ # that reason — nothing else answers `::`. Every consumer of a
47
+ # bareword type keeps working duck-typed: `Attribute#spell`'s
48
+ # `type.is_a?(Module)` branch fires for one of these where a
49
+ # bare Symbol falls to `type.to_s`, and `Naming.demodulise`
50
+ # (`path.split("::").last`) gives the identical string either
51
+ # way for a single segment — the two branches are equivalent
52
+ # for a name with no `::` in it, which is every unscoped
53
+ # bareword.
43
54
  class ScopedConstant < Module
55
+ # Wraps a constant path, the form a `ConstShim` resolver hands back for a bareword.
56
+ #
57
+ # @param path [Symbol, String] one segment such as `:Account`, or a `::`-joined path
58
+ # @return [Bluebook::DSL::ConstShim::ScopedConstant] a module standing in for that path
44
59
  def self.for(path) = new(path.to_s)
45
60
 
61
+ # @param path [String] the constant path this module stands in for
46
62
  def initialize(path)
47
63
  super()
48
64
  @path = path
49
65
  end
50
66
 
51
- # ONE MORE SEGMENT, the same way an unresolved const anywhere
67
+ # Extends the path by one segment, so `Account::Debit` resolves past `Account`.
68
+ #
69
+ # One more segment, the same way an unresolved const anywhere
52
70
  # else does — `Account::Debit::Anything` keeps chaining rather
53
71
  # than refusing, since nothing here knows how deep a reference
54
72
  # is meant to go; the DSL keyword that finally reads `.to_s`
55
73
  # is the one place that does.
74
+ #
75
+ # @param name [Symbol] the segment written after `::`
76
+ # @return [Bluebook::DSL::ConstShim::ScopedConstant] a new constant for the longer path
56
77
  def const_missing(name) = ScopedConstant.for("#{@path}::#{name}")
57
78
 
58
79
  def to_s = @path
59
80
  def to_sym = @path.to_sym
60
81
  def inspect = @path
82
+
83
+ # Exposes the raw path under a name no ordinary `Module` answers, so `==` can compare
84
+ # two scoped constants without going through `to_s`.
85
+ #
86
+ # @return [String] the `::`-joined path, such as `"Account::Debit"`
61
87
  def hecks_path = @path
62
88
 
63
89
  def ==(other) = other.is_a?(ScopedConstant) ? @path == other.hecks_path : @path.to_sym == other
@@ -65,15 +91,20 @@ module Hecks
65
91
  def hash = @path.hash
66
92
  end
67
93
 
68
- # A SCOPED NAME IS WRITTEN AS TEXT, NOT AS A CONSTANT PATH — see the
94
+ # A scoped name is written as text, not as a constant path — see the
69
95
  # note on `admits:` in AttributeCollector. A resolver returning a
70
96
  # Module (so that `Vocabulary::QueryComparator` reaches a second
71
97
  # `const_missing`) was tried and cannot be made to hold : `Facade::
72
- # Surface` installs EVERY aggregate name as a top-level constant, so
98
+ # Surface` installs every aggregate name as a top-level constant, so
73
99
  # once any facade is built, `Vocabulary` resolves to that real module
74
100
  # and never reaches this hook at all. A spelling that works only
75
101
  # before a facade exists is worse than one that always works.
76
102
  module Hook
103
+ # Answers an undeclared top-level constant from the active resolver, if there is one.
104
+ #
105
+ # @param name [Symbol] the missing constant's name
106
+ # @return [Object] whatever the active resolver returns for `name`
107
+ # @raise [NameError] if no resolver is installed, as Ruby raises for any unknown constant
77
108
  def const_missing(name)
78
109
  resolver = ConstShim.resolver
79
110
  resolver ? resolver.call(name) : super
@@ -14,21 +14,27 @@ module Hecks
14
14
 
15
15
  include WordGate
16
16
 
17
- # `legacy_bare_port:` — ONLY `Hecks.port`'s own top-level method
17
+ # `legacy_bare_port:` — only `Hecks.port`'s own top-level method
18
18
  # (lib/hecks.rb) passes `true`. `PortBuilder#build` never refused
19
19
  # an empty build (no verb, no signal, nothing) — `Port.new(verb:
20
20
  # nil, signal: :reply)` is a real, allowed shape dsl_spec.rb's own
21
21
  # "a port" tests rely on (`signal`-only, no `verb` at all). The
22
- # AGGREGATE-scoped (`BindingProxy#port`) and hecksagon-ROOT
23
- # (`HecksagonBuilder#port_impl`) callers both reach this SAME
22
+ # aggregate-scoped (`BindingProxy#port`) and hecksagon-root
23
+ # (`HecksagonBuilder#port_impl`) callers both reach this same
24
24
  # class with `owner: nil` too when they're building the bare-verb
25
- # shape (`port_impl`'s own root-level port can be EITHER shape,
25
+ # shape (`port_impl`'s own root-level port can be either shape,
26
26
  # decided only after `build` returns) — so `owner.nil?` cannot be
27
27
  # the discriminator between "old Hecks.port semantics" and "real
28
28
  # DomainPort semantics"; those two callers correctly want the
29
29
  # stricter "declares no verb and no operations" refusal `build`
30
30
  # already raises below, unchanged. Only the literal top-level
31
31
  # `.port` file caller wants the older, looser rule.
32
+ #
33
+ # @param name [String] the port's name
34
+ # @param owner [String, nil] name of the aggregate the port is declared on, handed to
35
+ # each operation's builder; nil for a root-level or top-level port
36
+ # @param legacy_bare_port [Boolean] true only for `Hecks.port`: an empty body then builds
37
+ # a verbless `Port` rather than being refused
32
38
  def initialize(name, owner: nil, legacy_bare_port: false)
33
39
  @name = name
34
40
  @owner = owner
@@ -38,40 +44,63 @@ module Hecks
38
44
  @legacy_bare_port = legacy_bare_port
39
45
  end
40
46
 
41
- # WHAT THE OUTSIDE TELLS US — an external fact arriving, translated
42
- # into this domain's own word for it. Spelled `operation` before it
43
- # had a twin, and `operation` still works: the corpus is full of it,
44
- # and renaming a word costs every chapter that uses it for no gain a
45
- # reader can feel.
47
+ # Declares an inbound operation: a fact the outside world delivers to this domain.
48
+ #
49
+ # **What the outside tells us** — an external fact arriving, translated
50
+ # into this domain's own word for it. Spelled `operation` or `tells`;
51
+ # `operation` stays because the corpus is full of it, and renaming
52
+ # a word costs every chapter that uses it for no gain a reader can
53
+ # feel.
46
54
  #
47
- # RENAMED FROM `tells` — item #13's full metaprogrammed dispatch
48
- # (slice 4c). `operation`/`tells` are TWO separate Keyword rows
55
+ # Answers both words — item #13's full metaprogrammed dispatch
56
+ # (slice 4c). `operation`/`tells` are two separate Keyword rows
49
57
  # (a word admitting two forms) that both name `calls: "tells_impl"`
50
- # — the routing between the two spellings now lives in the table,
58
+ # — the routing between the two spellings lives in the table,
51
59
  # not in a Ruby `alias`. Not bootstrap-reachable (checked
52
- # directly), so no BOOTSTRAP_CALLS_FALLBACK entry needed.
60
+ # directly), so its `BOOTSTRAP_CALLS_FALLBACK` row is never consulted.
61
+ #
62
+ # @param name [String] the operation's name, such as `"PaymentSettled"`
63
+ # @param to [Symbol, String, Module, nil] the aggregate the operation routes to, written
64
+ # as a bare constant; nil leaves routing to the operation's own attributes
65
+ # @yield the operation body (`attribute`, `emits`), evaluated against a
66
+ # `PortOperationBuilder`
67
+ # @return [Array<Bluebook::PortOperation>] every operation declared so far, this one last
68
+ # @raise [Bluebook::DSL::Malformed] if the body declares no `emits`, or uses `answers` or
69
+ # `refuses`, which belong to an `asks`
53
70
  def tells_impl(name, to: nil, &)
54
71
  @operations << PortOperationBuilder.build(name, to: to, owner: @owner, direction: :inbound, &)
55
72
  end
56
73
 
57
- # WHAT WE ASK OF THE OUTSIDE — the direction this language did not
58
- # have. Before this, a domain could be CALLED by an adapter and never
59
- # call one. An `asks` is dispatched like any other port operation, so
74
+ # Declares an outbound operation: a question this domain puts to the outside world.
75
+ #
76
+ # **What we ask of the outside** — the direction that lets a domain
77
+ # call an adapter, not only be called by one. An `asks` is dispatched
78
+ # like any other port operation, so
60
79
  # a `policy` can trigger it off an event, and it comes back as one of
61
80
  # the two events it named — which is what makes the outside world
62
81
  # something the model can reason about rather than a place exceptions
63
82
  # come from.
64
83
  #
65
- # RENAMED FROM `asks` — item #13's full metaprogrammed dispatch
66
- # (slice 4c), same reasoning as tells_impl above.
84
+ # Answers the `asks` word through the table's `calls:` column — item
85
+ # #13's full metaprogrammed dispatch (slice 4c), same reasoning as
86
+ # `tells_impl` above.
87
+ #
88
+ # @param name [String] the operation's name
89
+ # @param to [Symbol, String, Module, nil] the aggregate the operation routes to, written
90
+ # as a bare constant; nil leaves routing to the operation's own attributes
91
+ # @yield the operation body (`attribute`, `answers`, `refuses`), evaluated against a
92
+ # `PortOperationBuilder`
93
+ # @return [Array<Bluebook::PortOperation>] every operation declared so far, this one last
94
+ # @raise [Bluebook::DSL::Malformed] if the body declares `emits`, or lacks either
95
+ # `answers` or `refuses`
67
96
  def asks_impl(name, to: nil, &)
68
97
  @operations << PortOperationBuilder.build(name, to: to, owner: @owner, direction: :outbound, &)
69
98
  end
70
99
 
71
- # THE DRIVEN HALF OF THE SAME WORD. `operation`/`emits` translates an
100
+ # The driven half of the same word. `operation`/`emits` translates an
72
101
  # inbound fact into this domain's own event vocabulary — there is no
73
102
  # channel back to a caller beyond the events it emits. `verb` is the
74
- # opposite direction: the domain calling OUT to a swappable adapter
103
+ # opposite direction: the domain calling out to a swappable adapter
75
104
  # and getting a real value back (a checkout URL, a fetched document),
76
105
  # exactly what `Hecks.port "name" do verb "x" end` already builds —
77
106
  # this is that same `Port`, reached from the same `port` call
@@ -83,9 +112,12 @@ module Hecks
83
112
  # coerce-and-assign with nothing else, now executed by
84
113
  # `GenericDispatch`.
85
114
 
115
+ # Names the verb aggregates call this port by, which makes it a driven `Port` rather than
116
+ # a `DomainPort` of operations.
117
+ #
86
118
  # `Hecks.port "x" do verb "y"; signal :effect end`'s own two words,
87
119
  # reachable here too — a bare-verb `DomainPortBuilder.build` falls
88
- # back to the SAME `Port` object `PortBuilder` produces (`build`,
120
+ # back to the same `Port` object `PortBuilder` produces (`build`,
89
121
  # below), so any `.port` file can migrate to being parsed by this
90
122
  # builder with zero change to its own text, or to any caller that
91
123
  # reads `.verb`/`.signal` off the `Port` it gets back. Ordinary
@@ -93,20 +125,40 @@ module Hecks
93
125
  # header is explicit that a word answered this way never reaches
94
126
  # its `method_missing`, so no new self-hosted grammar row is
95
127
  # needed for either word under this context.
128
+ #
129
+ # @param value [String, Symbol] the verb, such as `"charged_by"`
130
+ # @return [String] the verb as stored
96
131
  def verb(value) = @verb = value.to_s
132
+
133
+ # Sets whether a verb-shaped port hands a value back; one left unset signals `:reply`.
134
+ #
135
+ # @param value [Symbol, String] `:reply` when the adapter answers with a value,
136
+ # `:effect` when it is called only for its effect
137
+ # @return [Symbol] the signal as stored
97
138
  def signal(value) = @signal = value.to_sym
98
139
 
99
- # THE METHOD CONTRACT — `PortBuilder#answers`'s own twin, added
100
- # here after the fact: a `.port` file migrated to parse through
101
- # this builder (the repoint `lib/hecks.rb#port`'s own comment
102
- # describes) can still declare one (`extraction.port`'s own
140
+ # Declares one method an adapter bound to a verb-shaped port must respond to.
141
+ #
142
+ # **The method contract** — `PortBuilder#answers`'s own twin: a
143
+ # `.port` file parsed through this builder (the repoint
144
+ # `lib/hecks.rb#port`'s own comment describes) can declare one
145
+ # (`extraction.port`'s own
103
146
  # `answers :canonical`, real, live corpus text) — this builder's
104
147
  # bare-verb fallback needs to carry it through to the same `Port`
105
148
  # object `PortBuilder` itself would have built, or the migration
106
149
  # would silently drop a method-contract check for any `.port`
107
150
  # file that uses this word.
151
+ #
152
+ # @param name [Symbol, String] the method name, such as `:canonical`
153
+ # @return [Array<Symbol>] every method declared so far, this one last
108
154
  def answers(name) = @answers << name.to_sym
109
155
 
156
+ # Assembles whichever of the two port shapes the body declared.
157
+ #
158
+ # @return [Bluebook::Port, Bluebook::DomainPort] a `Port` when the body named a `verb`
159
+ # (or was empty under `legacy_bare_port:`), otherwise a `DomainPort` of its operations
160
+ # @raise [Bluebook::DSL::Malformed] if the body declares both a verb and operations,
161
+ # declares neither without `legacy_bare_port:`, or the port language refuses the `Port`
110
162
  def build
111
163
  if @verb && !@operations.empty?
112
164
  raise Malformed,
@@ -123,6 +175,19 @@ module Hecks
123
175
  DomainPort.new(name: @name, operations: @operations)
124
176
  end
125
177
 
178
+ # Evaluates a `port` block against a fresh builder and returns whichever shape it declared.
179
+ #
180
+ # @param name [String] the port's name
181
+ # @param owner [String, nil] name of the aggregate the port is declared on, or nil for a
182
+ # root-level or top-level port
183
+ # @param legacy_bare_port [Boolean] true only for `Hecks.port`, which lets an empty body
184
+ # build a verbless `Port`
185
+ # @yield the port body, evaluated with the builder as `self`; may be omitted
186
+ # @return [Bluebook::Port, Bluebook::DomainPort] a verb-shaped `Port`, or a `DomainPort`
187
+ # holding the declared operations
188
+ # @raise [Bluebook::DSL::Malformed] if the body declares both shapes, declares neither
189
+ # without `legacy_bare_port:`, holds an operation its builder refuses, or uses a word
190
+ # the `DomainPort` grammar does not admit
126
191
  def self.build(name, owner: nil, legacy_bare_port: false, &block)
127
192
  builder = new(name, owner: owner, legacy_bare_port: legacy_bare_port)
128
193
  builder.instance_eval(&block) if block