hecks 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (358) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hecks/adapters/driven/claude_code.rb +7 -7
  3. data/lib/hecks/adapters/driven/d1.rb +187 -23
  4. data/lib/hecks/adapters/driven/folder.rb +10 -10
  5. data/lib/hecks/adapters/driven/google_authentication.rb +8 -8
  6. data/lib/hecks/adapters/driven/governance_authorization.rb +31 -13
  7. data/lib/hecks/adapters/driven/heki/journal.rb +60 -2
  8. data/lib/hecks/adapters/driven/heki/saga_store.rb +5 -5
  9. data/lib/hecks/adapters/driven/heki.rb +13 -7
  10. data/lib/hecks/adapters/driven/identity_registry.rb +2 -2
  11. data/lib/hecks/adapters/driven/in_memory_ordering.rb +3 -3
  12. data/lib/hecks/adapters/driven/lambda/client.rb +34 -9
  13. data/lib/hecks/adapters/driven/lambda.rb +39 -33
  14. data/lib/hecks/adapters/driven/local_storage.rb +17 -10
  15. data/lib/hecks/adapters/driven/memory.rb +205 -9
  16. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +1 -1
  17. data/lib/hecks/adapters/driven/postgres/codec.rb +27 -11
  18. data/lib/hecks/adapters/driven/postgres/outbox.rb +40 -2
  19. data/lib/hecks/adapters/driven/postgres/reconnect.rb +23 -7
  20. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +14 -14
  21. data/lib/hecks/adapters/driven/postgres.rb +175 -28
  22. data/lib/hecks/adapters/driven/postgres_era.adapter +5 -0
  23. data/lib/hecks/adapters/driven/prism.rb +4 -4
  24. data/lib/hecks/adapters/driven/sql_query_builder.rb +34 -22
  25. data/lib/hecks/adapters/driven/sqlite/codec.rb +38 -10
  26. data/lib/hecks/adapters/driven/sqlite/projection.rb +60 -32
  27. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +12 -12
  28. data/lib/hecks/adapters/driven/sqlite.rb +181 -21
  29. data/lib/hecks/adapters/driven.rb +4 -4
  30. data/lib/hecks/adapters/driving/github_webhook.rb +145 -0
  31. data/lib/hecks/behaviors/dsl.rb +2 -2
  32. data/lib/hecks/behaviors/expectations.rb +51 -23
  33. data/lib/hecks/behaviors/ir.rb +1 -1
  34. data/lib/hecks/behaviors/rspec.rb +1 -1
  35. data/lib/hecks/behaviors/runner.rb +2 -2
  36. data/lib/hecks/behaviors.rb +1 -1
  37. data/lib/hecks/bluebook/aggregate.rb +13 -13
  38. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +10 -10
  39. data/lib/hecks/bluebook/assembly/build.rb +1 -1
  40. data/lib/hecks/bluebook/assembly/contract.rb +39 -16
  41. data/lib/hecks/bluebook/assembly/contracts.rb +40 -38
  42. data/lib/hecks/bluebook/assembly/marks.rb +18 -18
  43. data/lib/hecks/bluebook/assembly/specializer.rb +19 -21
  44. data/lib/hecks/bluebook/assembly.rb +9 -9
  45. data/lib/hecks/bluebook/attribute.rb +9 -9
  46. data/lib/hecks/bluebook/behaviour/aggregate.rb +11 -11
  47. data/lib/hecks/bluebook/behaviour/attribute.rb +5 -5
  48. data/lib/hecks/bluebook/behaviour/chapter.rb +23 -5
  49. data/lib/hecks/bluebook/behaviour/command.rb +23 -23
  50. data/lib/hecks/bluebook/behaviour/domain_port.rb +27 -3
  51. data/lib/hecks/bluebook/behaviour/entity.rb +8 -8
  52. data/lib/hecks/bluebook/behaviour/hexagon.rb +4 -4
  53. data/lib/hecks/bluebook/behaviour/lifecycle.rb +5 -5
  54. data/lib/hecks/bluebook/behaviour/policy.rb +12 -12
  55. data/lib/hecks/bluebook/behaviour/process_manager.rb +7 -7
  56. data/lib/hecks/bluebook/behaviour/query.rb +1 -1
  57. data/lib/hecks/bluebook/behaviour/read_model.rb +8 -8
  58. data/lib/hecks/bluebook/behaviour/traits.rb +12 -12
  59. data/lib/hecks/bluebook/behaviour/value_object.rb +6 -6
  60. data/lib/hecks/bluebook/capabilities.rb +27 -0
  61. data/lib/hecks/bluebook/chapter.rb +28 -9
  62. data/lib/hecks/bluebook/command.rb +12 -12
  63. data/lib/hecks/bluebook/domain_port.rb +9 -9
  64. data/lib/hecks/bluebook/dsl/adapter_builder.rb +24 -0
  65. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +49 -49
  66. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +96 -96
  67. data/lib/hecks/bluebook/dsl/attribute_collector.rb +41 -41
  68. data/lib/hecks/bluebook/dsl/binding_proxy.rb +22 -2
  69. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +111 -74
  70. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +48 -30
  71. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
  72. data/lib/hecks/bluebook/dsl/command_builder.rb +103 -103
  73. data/lib/hecks/bluebook/dsl/const_shim.rb +46 -15
  74. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +90 -25
  75. data/lib/hecks/bluebook/dsl/entity_builder.rb +56 -56
  76. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
  77. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +89 -30
  78. data/lib/hecks/bluebook/dsl/identity_declaration.rb +17 -17
  79. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
  80. data/lib/hecks/bluebook/dsl/policy_builder.rb +30 -21
  81. data/lib/hecks/bluebook/dsl/port_builder.rb +38 -7
  82. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +56 -22
  83. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +35 -35
  84. data/lib/hecks/bluebook/dsl/query_builder.rb +5 -5
  85. data/lib/hecks/bluebook/dsl/read_model_builder.rb +34 -34
  86. data/lib/hecks/bluebook/dsl/rule_reference.rb +41 -39
  87. data/lib/hecks/bluebook/dsl/translation_builder.rb +9 -9
  88. data/lib/hecks/bluebook/dsl/value_object_builder.rb +16 -16
  89. data/lib/hecks/bluebook/dsl/word_gate.rb +59 -53
  90. data/lib/hecks/bluebook/dsl/world_builder.rb +51 -8
  91. data/lib/hecks/bluebook/entity.rb +11 -11
  92. data/lib/hecks/bluebook/expression/ast_json.rb +20 -20
  93. data/lib/hecks/bluebook/expression/ast_reader.rb +3 -3
  94. data/lib/hecks/bluebook/expression/canonical_form.rb +9 -9
  95. data/lib/hecks/bluebook/expression/evaluator.rb +18 -18
  96. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +18 -18
  97. data/lib/hecks/bluebook/expression/resolver.rb +60 -62
  98. data/lib/hecks/bluebook/hexagon.rb +1 -1
  99. data/lib/hecks/bluebook/lifecycle.rb +1 -1
  100. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -1
  101. data/lib/hecks/bluebook/meta_validator/judge.rb +122 -86
  102. data/lib/hecks/bluebook/meta_validator/plan.rb +39 -39
  103. data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -2
  104. data/lib/hecks/bluebook/meta_validator/readings.rb +43 -43
  105. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +44 -37
  106. data/lib/hecks/bluebook/meta_validator/shapes.rb +25 -21
  107. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +145 -31
  108. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +6 -6
  109. data/lib/hecks/bluebook/meta_validator/world_judge.rb +5 -5
  110. data/lib/hecks/bluebook/meta_validator.rb +70 -70
  111. data/lib/hecks/bluebook/model_check.rb +301 -84
  112. data/lib/hecks/bluebook/pattern_subset.rb +9 -9
  113. data/lib/hecks/bluebook/policy.rb +15 -13
  114. data/lib/hecks/bluebook/process_manager.rb +14 -14
  115. data/lib/hecks/bluebook/project_register.rb +6 -6
  116. data/lib/hecks/bluebook/query.rb +4 -4
  117. data/lib/hecks/bluebook/read_model.rb +14 -14
  118. data/lib/hecks/bluebook/reference.rb +8 -8
  119. data/lib/hecks/bluebook/smoke_test.rb +19 -19
  120. data/lib/hecks/bluebook/synthesizer.rb +12 -12
  121. data/lib/hecks/bluebook/translation.rb +4 -4
  122. data/lib/hecks/bluebook/value_object.rb +6 -6
  123. data/lib/hecks/bluebook.rb +2 -2
  124. data/lib/hecks/codemod/legacy_dispatch_args.rb +299 -0
  125. data/lib/hecks/codemod/legacy_dispatch_recorder.rb +186 -0
  126. data/lib/hecks/codemod.rb +36 -35
  127. data/lib/hecks/construct.rb +6 -6
  128. data/lib/hecks/corpus.rb +317 -0
  129. data/lib/hecks/deprecation.rb +95 -0
  130. data/lib/hecks/doc/reference.rb +19 -19
  131. data/lib/hecks/embryonaut_bluebook.rb +11 -11
  132. data/lib/hecks/facade/cli_door.rb +69 -10
  133. data/lib/hecks/facade/cli_runner.rb +105 -24
  134. data/lib/hecks/facade/command_request.rb +23 -0
  135. data/lib/hecks/facade/handle.rb +79 -32
  136. data/lib/hecks/facade/json_door.rb +106 -25
  137. data/lib/hecks/facade/surface/aggregate_door.rb +42 -27
  138. data/lib/hecks/facade/surface/chapter.rb +26 -17
  139. data/lib/hecks/facade/surface.rb +16 -3
  140. data/lib/hecks/facade.rb +15 -4
  141. data/lib/hecks/forms/app.rb +46 -30
  142. data/lib/hecks/forms/command_form_renderer.rb +70 -9
  143. data/lib/hecks/forms/field_renderer.rb +142 -6
  144. data/lib/hecks/forms/field_shape.rb +183 -20
  145. data/lib/hecks/forms/html.rb +51 -7
  146. data/lib/hecks/forms/index_renderer.rb +14 -2
  147. data/lib/hecks/forms/params.rb +120 -23
  148. data/lib/hecks/forms/port_argument.rb +2 -2
  149. data/lib/hecks/forms/query_form_renderer.rb +2 -2
  150. data/lib/hecks/forms/record_renderer.rb +2 -2
  151. data/lib/hecks/forms/record_table.rb +1 -1
  152. data/lib/hecks/forms/value_object_shape.rb +3 -3
  153. data/lib/hecks/forms.rb +24 -4
  154. data/lib/hecks/fqn.rb +1 -1
  155. data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
  156. data/lib/hecks/framework.rb +48 -17
  157. data/lib/hecks/freezer.rb +11 -11
  158. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +90 -90
  159. data/lib/hecks/fuzzing/combination_miner.rb +119 -0
  160. data/lib/hecks/fuzzing/concurrent_dispatch.rb +361 -0
  161. data/lib/hecks/fuzzing/coverage_campaign.rb +118 -0
  162. data/lib/hecks/fuzzing/differential.rb +158 -0
  163. data/lib/hecks/fuzzing/domain_generator.rb +694 -0
  164. data/lib/hecks/fuzzing/era_boundary.rb +124 -0
  165. data/lib/hecks/fuzzing/form_census.rb +199 -0
  166. data/lib/hecks/fuzzing/generated_domain_check.rb +95 -0
  167. data/lib/hecks/fuzzing/invalid_value_generator.rb +6 -6
  168. data/lib/hecks/fuzzing/isolated_boot.rb +226 -38
  169. data/lib/hecks/fuzzing/nondeterministic.rb +67 -0
  170. data/lib/hecks/fuzzing/persistence_parity.rb +161 -0
  171. data/lib/hecks/fuzzing/properties/corrections.rb +100 -0
  172. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +321 -41
  173. data/lib/hecks/fuzzing/properties/guards.rb +129 -26
  174. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +32 -32
  175. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +31 -11
  176. data/lib/hecks/fuzzing/properties/outbox.rb +142 -0
  177. data/lib/hecks/fuzzing/properties/querying.rb +17 -17
  178. data/lib/hecks/fuzzing/properties.rb +84 -39
  179. data/lib/hecks/fuzzing/qa_settings.rb +152 -0
  180. data/lib/hecks/fuzzing/replay.rb +224 -114
  181. data/lib/hecks/fuzzing/rotation_priority.rb +94 -0
  182. data/lib/hecks/fuzzing/rust_gap_manifest.rb +113 -0
  183. data/lib/hecks/fuzzing/self_consistency.rb +676 -0
  184. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +526 -0
  185. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +101 -30
  186. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +55 -8
  187. data/lib/hecks/fuzzing/sequence_generator/picker.rb +31 -11
  188. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +123 -32
  189. data/lib/hecks/fuzzing/sequence_generator.rb +152 -31
  190. data/lib/hecks/fuzzing/shrinker.rb +197 -0
  191. data/lib/hecks/fuzzing/structural_skips.rb +39 -0
  192. data/lib/hecks/fuzzing/sweep_depth.rb +53 -0
  193. data/lib/hecks/fuzzing/target_capabilities.rb +180 -0
  194. data/lib/hecks/fuzzing/value_generator.rb +66 -14
  195. data/lib/hecks/fuzzing.rb +11 -0
  196. data/lib/hecks/grammar/evolve.rb +10 -10
  197. data/lib/hecks/grammar.rb +7 -7
  198. data/lib/hecks/ir.rb +13 -13
  199. data/lib/hecks/language/bluebook/bluebook.bluebook +41 -0
  200. data/lib/hecks/language/bluebook/policy.bluebook +11 -1
  201. data/lib/hecks/language/bluebook/vocabulary.bluebook +365 -15
  202. data/lib/hecks/language/oidc.json +5 -0
  203. data/lib/hecks/literal.rb +9 -9
  204. data/lib/hecks/naming.rb +89 -21
  205. data/lib/hecks/ports/access_control.rb +58 -2
  206. data/lib/hecks/ports/agent/answers.rb +83 -6
  207. data/lib/hecks/ports/agent.rb +119 -35
  208. data/lib/hecks/ports/authentication.rb +44 -4
  209. data/lib/hecks/ports/authorization.rb +53 -11
  210. data/lib/hecks/ports/clock.rb +42 -23
  211. data/lib/hecks/ports/extraction.rb +16 -0
  212. data/lib/hecks/ports/identity_assignment.rb +24 -2
  213. data/lib/hecks/ports/identity_generation.rb +17 -3
  214. data/lib/hecks/ports/identity_resolution.rb +18 -1
  215. data/lib/hecks/ports/loading.rb +4 -0
  216. data/lib/hecks/ports/persistence/append_only.rb +172 -8
  217. data/lib/hecks/ports/persistence/binding_policy.rb +34 -0
  218. data/lib/hecks/ports/persistence/codec_boundary.rb +178 -0
  219. data/lib/hecks/ports/persistence/execution.rb +4 -0
  220. data/lib/hecks/ports/persistence/null_saga_store.rb +12 -1
  221. data/lib/hecks/ports/persistence/plugin.rb +42 -4
  222. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +218 -25
  223. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +77 -9
  224. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +81 -24
  225. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +29 -18
  226. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +144 -60
  227. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +103 -8
  228. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +98 -23
  229. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +282 -109
  230. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +63 -25
  231. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +160 -58
  232. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +51 -28
  233. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +28 -5
  234. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +25 -12
  235. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +129 -34
  236. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +51 -6
  237. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +44 -6
  238. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +16 -0
  239. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +57 -4
  240. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +25 -2
  241. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +338 -83
  242. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +68 -10
  243. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +9 -3
  244. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +9 -2
  245. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +42 -8
  246. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +16 -1
  247. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +36 -5
  248. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +23 -3
  249. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +18 -19
  250. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +5 -5
  251. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +1 -1
  252. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -2
  253. data/lib/hecks/ports/persistence/plugins/era.rb +12 -2
  254. data/lib/hecks/ports/persistence/remote_runtime.rb +9 -2
  255. data/lib/hecks/ports/persistence/repository_factory.rb +29 -6
  256. data/lib/hecks/ports/persistence/state_codec.rb +319 -0
  257. data/lib/hecks/ports/persistence.rb +36 -1
  258. data/lib/hecks/ports/projection.rb +61 -7
  259. data/lib/hecks/ports/query/in_memory.rb +3 -3
  260. data/lib/hecks/ports/query/ordering.rb +6 -6
  261. data/lib/hecks/ports/query.rb +35 -0
  262. data/lib/hecks/projections/bootstrap_table.rb +112 -0
  263. data/lib/hecks/projections/diagrams.rb +75 -75
  264. data/lib/hecks/projections/glossary/html.rb +250 -0
  265. data/lib/hecks/projections/glossary/markdown.rb +105 -0
  266. data/lib/hecks/projections/glossary/mermaid.rb +110 -0
  267. data/lib/hecks/projections/glossary/page.css +271 -0
  268. data/lib/hecks/projections/glossary/page.js +72 -0
  269. data/lib/hecks/projections/glossary/sections.rb +17 -0
  270. data/lib/hecks/projections/glossary/sentences.rb +205 -0
  271. data/lib/hecks/projections/glossary.rb +214 -286
  272. data/lib/hecks/projections/ir.rb +1 -1
  273. data/lib/hecks/projections/model/deviations.rb +18 -17
  274. data/lib/hecks/projections/model.rb +25 -21
  275. data/lib/hecks/projections/oidc.rb +7 -7
  276. data/lib/hecks/projections/parser_table.rb +5 -5
  277. data/lib/hecks/projections/reference.rb +3 -3
  278. data/lib/hecks/projections/rust_vocabulary.rb +443 -0
  279. data/lib/hecks/projections/shape.rb +2 -2
  280. data/lib/hecks/projections/statements.rb +11 -11
  281. data/lib/hecks/projections/vocabulary.rb +9 -9
  282. data/lib/hecks/projections.rb +5 -3
  283. data/lib/hecks/projector/cli_projector.rb +29 -29
  284. data/lib/hecks/projector/docs_projector.rb +13 -13
  285. data/lib/hecks/projector/exporter.rb +42 -21
  286. data/lib/hecks/projector/ir_projector.rb +1 -1
  287. data/lib/hecks/projector/narrate_projector.rb +15 -22
  288. data/lib/hecks/projector/target.rb +13 -13
  289. data/lib/hecks/projector.rb +15 -15
  290. data/lib/hecks/query_ir.rb +47 -47
  291. data/lib/hecks/query_specification/common/comparators.rb +19 -3
  292. data/lib/hecks/query_specification/common/comparison.rb +132 -24
  293. data/lib/hecks/query_specification/common/dsl.rb +65 -9
  294. data/lib/hecks/query_specification/common/null_policy.rb +57 -13
  295. data/lib/hecks/query_specification/common/null_semantics.rb +4 -0
  296. data/lib/hecks/query_specification/common/options.rb +25 -0
  297. data/lib/hecks/query_specification/field_path.rb +69 -15
  298. data/lib/hecks/query_specification/hop_path.rb +57 -20
  299. data/lib/hecks/query_specification/read_model/specification.rb +4 -0
  300. data/lib/hecks/rendering.rb +3 -3
  301. data/lib/hecks/router/namespace_installer.rb +3 -3
  302. data/lib/hecks/router.rb +1 -1
  303. data/lib/hecks/runtime/aggregate_lock.rb +11 -11
  304. data/lib/hecks/runtime/boot_gates.rb +3 -3
  305. data/lib/hecks/runtime/caller.rb +8 -8
  306. data/lib/hecks/runtime/capability_graph.rb +2 -2
  307. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +22 -25
  308. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +64 -67
  309. data/lib/hecks/runtime/command_interpreter.rb +159 -102
  310. data/lib/hecks/runtime/command_rules/admissibility.rb +69 -69
  311. data/lib/hecks/runtime/command_rules/arithmetic.rb +137 -91
  312. data/lib/hecks/runtime/command_rules/authorization.rb +38 -17
  313. data/lib/hecks/runtime/command_rules/emission.rb +18 -1
  314. data/lib/hecks/runtime/command_rules/references.rb +94 -19
  315. data/lib/hecks/runtime/command_rules.rb +3 -0
  316. data/lib/hecks/runtime/dependency_planning.rb +11 -11
  317. data/lib/hecks/runtime/dispatcher.rb +283 -90
  318. data/lib/hecks/runtime/entity_element.rb +282 -46
  319. data/lib/hecks/runtime/entity_interpreter.rb +160 -69
  320. data/lib/hecks/runtime/errors.rb +19 -19
  321. data/lib/hecks/runtime/event.rb +6 -6
  322. data/lib/hecks/runtime/identity.rb +22 -22
  323. data/lib/hecks/runtime/instance.rb +39 -14
  324. data/lib/hecks/runtime/interpreting.rb +12 -12
  325. data/lib/hecks/runtime/invocation.rb +276 -0
  326. data/lib/hecks/runtime/loader.rb +14 -14
  327. data/lib/hecks/runtime/outbox.rb +23 -23
  328. data/lib/hecks/runtime/policy_interpreter.rb +54 -54
  329. data/lib/hecks/runtime/port_operation_interpreter.rb +22 -19
  330. data/lib/hecks/runtime/query_interpreter.rb +111 -56
  331. data/lib/hecks/runtime/reaction_invocation.rb +76 -9
  332. data/lib/hecks/runtime/read_model_interpreter.rb +40 -40
  333. data/lib/hecks/runtime/rebuild_sweep.rb +4 -4
  334. data/lib/hecks/runtime/reference_hop.rb +6 -6
  335. data/lib/hecks/runtime/refusal_wording.rb +92 -112
  336. data/lib/hecks/runtime/registry/saga_persistence.rb +21 -21
  337. data/lib/hecks/runtime/registry/verification.rb +36 -26
  338. data/lib/hecks/runtime/registry.rb +56 -27
  339. data/lib/hecks/runtime/remote_dispatcher.rb +38 -23
  340. data/lib/hecks/runtime/routing.rb +10 -88
  341. data/lib/hecks/runtime/saga_interpreter/correlation.rb +17 -17
  342. data/lib/hecks/runtime/saga_interpreter.rb +93 -56
  343. data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
  344. data/lib/hecks/runtime/tenant_check.rb +9 -9
  345. data/lib/hecks/runtime/tenant_scope.rb +5 -5
  346. data/lib/hecks/runtime/value/admission.rb +75 -30
  347. data/lib/hecks/runtime/value/coercion.rb +379 -226
  348. data/lib/hecks/runtime/value/entity_list_coercion.rb +248 -0
  349. data/lib/hecks/runtime/value.rb +28 -23
  350. data/lib/hecks/runtime.rb +7 -7
  351. data/lib/hecks/storehouse.rb +64 -64
  352. data/lib/hecks/version.rb +3 -3
  353. data/lib/hecks/vocabulary.rb +207 -5
  354. data/lib/hecks.rb +13 -11
  355. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +11 -11
  356. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +14 -12
  357. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +11 -11
  358. metadata +43 -2
@@ -3,30 +3,30 @@ require_relative "../../query_specification/common/null_policy"
3
3
  module Hecks
4
4
  module Ports
5
5
  module Query
6
- # What order an ask ANSWERS IN — the meaning of the ask, not a property
6
+ # What order an ask answers in — the meaning of the ask, not a property
7
7
  # of the store that happens to hold it. Declared here once so an adapter
8
8
  # may satisfy it natively but never redefine it : SQLite pushes both
9
9
  # tiers into SQL (NullPolicy.sql_order renders `field DIR, id DIR`),
10
10
  # while Heki and Memory have no query engine and delegate straight back
11
11
  # to InMemory.
12
12
  #
13
- # Two tiers, in this order : the DECLARED order_by when there is one,
14
- # then IDENTITY, always. The identity tier is what makes an ask total.
13
+ # Two tiers, in this order : the declared order_by when there is one,
14
+ # then identity, always. The identity tier is what makes an ask total.
15
15
  # Without it, an ask with no order_by — or a declared order with tied
16
16
  # keys — hands back whatever order the store happened to hold, and
17
17
  # store order was quietly standing in for a rule while every
18
18
  # hand-written query in the corpus stayed green : not one of them
19
19
  # had a tie for store order to decide.
20
20
  #
21
- # An adapter that pushes ordering down MUST push limit down with it.
21
+ # An adapter that pushes ordering down must push limit down with it.
22
22
  # Re-ordering a page the store already cut would be a top-N of the
23
23
  # wrong N — the one way this can be got quietly, expensively wrong.
24
24
  module Ordering
25
25
  module_function
26
26
 
27
27
  def apply(rows, order_by, null_semantics = nil, identity:, &value_of)
28
- # STABLE, because sort_by is not : two rows whose identity ties would
29
- # otherwise swap arbitrarily, and a tier meant to REMOVE store-dependence
28
+ # Stable, because sort_by is not : two rows whose identity ties would
29
+ # otherwise swap arbitrarily, and a tier meant to remove store-dependence
30
30
  # would be adding a coin flip of its own.
31
31
  rows = rows.each_with_index.sort_by { |row, index| [identity.call(row), index] }.map(&:first)
32
32
  return rows unless order_by
@@ -6,10 +6,34 @@ module Hecks
6
6
  # fallback engine for stores with no query engine of their own) are
7
7
  # its collaborators — query/ordering.rb and query/in_memory.rb.
8
8
  module Query
9
+ # A declared query the chosen adapter cannot honour as written — refused
10
+ # by `validate!` before the adapter runs it.
9
11
  class Unsupported < StandardError; end
10
12
 
11
13
  module_function
12
14
 
15
+ # Runs a declared query through the adapter's own `query` hook, if it has one.
16
+ #
17
+ # A nil answer means "no native engine here", and the caller falls back to the shared
18
+ # interpreter or to `InMemory.execute`; it never means "no rows", which is `[]`.
19
+ #
20
+ # @param repository [Persistence::AppendOnly, Object] the repository whose `adapter` is
21
+ # asked, or a bare persistence adapter (anything that does not answer `adapter`)
22
+ # @param specification [QuerySpecification::Common::Options] the declared query: a
23
+ # `Bluebook::Query`, a read-model specification, or a delegator wrapping one
24
+ # @param args [Hash{Symbol => Object}] the caller's arguments, which a where-clause,
25
+ # limit or offset written as a Symbol reads its value from
26
+ # @param context [Hash{Symbol => Object}] passed through to the adapter: `domain:`,
27
+ # `aggregate:` and, where a comparator needs one, `registry:`
28
+ # @return [Array<Runtime::Instance>, nil] the matching records, filtered, ordered and
29
+ # paged by the adapter (`[]` when none match); nil if the adapter has no `query`
30
+ # method
31
+ # @raise [Ports::Query::Unsupported] if the specification combines cursor and offset
32
+ # pagination, or asks for inspection the adapter cannot provide
33
+ # @raise [Runtime::WiringError] if a where-clause names an operation no comparator
34
+ # handles (raised by the in-memory engine behind `Memory`, `Heki` and the like)
35
+ # @raise [ArgumentError] if a SQL-backed adapter cannot compile a where-clause's
36
+ # operator
13
37
  def execute(repository, specification, args = {}, context: {})
14
38
  adapter = repository.respond_to?(:adapter) ? repository.adapter : repository
15
39
  return nil unless adapter.respond_to?(:query)
@@ -18,6 +42,17 @@ module Hecks
18
42
  adapter.query(specification, args, context: context)
19
43
  end
20
44
 
45
+ # Refuses a specification the adapter about to run it cannot honour.
46
+ #
47
+ # Inspection is honoured by an adapter with an `inspect_query` method, or, for the
48
+ # `"sql"` mode alone, by any adapter with a native `query`.
49
+ #
50
+ # @param specification [QuerySpecification::Common::Options] the declared query to check
51
+ # @param adapter [Object] the persistence adapter that would run it; only what it
52
+ # responds to (`inspect_query`, `query`) is read
53
+ # @return [void]
54
+ # @raise [Ports::Query::Unsupported] if the specification declares both a cursor and
55
+ # an offset, or asks for inspection the adapter cannot provide
21
56
  def validate!(specification, adapter)
22
57
  raise Unsupported, "a query cannot combine cursor and offset pagination" if specification.cursor && specification.offset
23
58
 
@@ -0,0 +1,112 @@
1
+ require_relative "../projector"
2
+
3
+ module Hecks
4
+ module Projections
5
+ # **The bootstrap-window fallbacks, projected** — lib/hecks/bluebook/dsl/
6
+ # bootstrap_table.rb rendered from the chapter's own Keyword rows.
7
+ #
8
+ # Projector.call(:bootstrap_table, bluebook: <the Bluebook chapter>)
9
+ #
10
+ # While `MetaValidator.bootstrapping?` the grammar table does not exist
11
+ # yet, so `WordGate#method_missing` and `RuleReference#lookup` cannot
12
+ # read `calls:`/`resolves_via:`/`disambiguator:` off it. They used to
13
+ # read two hand-kept Hashes instead, each "kept in sync by hand" with
14
+ # those columns. Kept in sync by hand meant a subset: 48 of the 87 live
15
+ # `calls:` rows, chosen word by word by grepping which ones the core
16
+ # chapters happened to use during bootstrap. A partition, not a filter:
17
+ # every live row now lands in the table, and the ones no bootstrap
18
+ # chapter calls cost nothing — a builder with its own `def` never
19
+ # reaches `method_missing` at all.
20
+ #
21
+ # The table cannot be built at boot for the same reason it exists: it
22
+ # is read before the grammar it comes from has been assembled. So it is
23
+ # committed, like lib/hecks/vocabulary.rb, and spec/bootstrap_table_
24
+ # spec.rb re-projects it in memory and refuses a diff.
25
+ module BootstrapTable
26
+ extend Projector::Target
27
+
28
+ projects_as :bootstrap_table, declares: "Syntax"
29
+
30
+ class Conflict < StandardError; end
31
+
32
+ HEADER = <<~RUBY.freeze
33
+ # Generated — projected from the language's own Keyword rows (the
34
+ # `calls:`, `resolves_via:` and `disambiguator:` columns of every
35
+ # KeywordSeed under lib/hecks/language/).
36
+ #
37
+ # Do not edit. spec/bootstrap_table_spec.rb re-projects this in memory
38
+ # and refuses a diff — run bin/project_bootstrap_table instead.
39
+ #
40
+ # Plain data, no requires: this is read while the grammar table it was
41
+ # projected from is still being built (`MetaValidator.bootstrapping?`).
42
+ RUBY
43
+
44
+ module_function
45
+
46
+ def call(bluebook:, options: {}) = render(bluebook)
47
+
48
+ # Retired rows are out of the language; admitted and deprecated rows
49
+ # still dispatch — the same `status != "retired"` reading
50
+ # `GenericDispatch.shape_for` gives the live table.
51
+ def live_keywords
52
+ Bluebook::MetaValidator::SyntaxBoot.call[:keywords].reject { |row| row[:status] == "retired" }
53
+ end
54
+
55
+ # `[context, word] => :method` — WordGate's own key order.
56
+ #
57
+ # An overloaded word has one row per argument shape, and they all
58
+ # name the same method. Two that don't would make the table keep
59
+ # whichever came last, so that is refused instead.
60
+ def calls(rows = live_keywords)
61
+ rows.reject { |row| row[:calls].to_s.empty? }
62
+ .group_by { |row| [row[:context], row[:word]] }
63
+ .to_h do |key, same_word|
64
+ targets = same_word.map { |row| row[:calls] }.uniq
65
+ raise Conflict, "#{key.inspect} names more than one method: #{targets.join(', ')}" if targets.size > 1
66
+
67
+ [key, targets.first.to_sym]
68
+ end
69
+ end
70
+
71
+ # `[word, context] => { resolves_via:, disambiguator: }` —
72
+ # RuleReference's own key order, blank columns omitted, the same
73
+ # shape its live `lookup` answers.
74
+ def resolves(rows = live_keywords)
75
+ rows.reject { |row| row[:resolves_via].to_s.empty? }.to_h do |row|
76
+ rule = { resolves_via: row[:resolves_via], disambiguator: row[:disambiguator] }
77
+ [[row[:word], row[:context]], rule.reject { |_, value| value.to_s.empty? }]
78
+ end
79
+ end
80
+
81
+ def render(_bluebook)
82
+ rows = live_keywords
83
+ calls_lines = calls(rows).map { |key, target| " #{key.inspect} => #{target.inspect}" }
84
+ resolves_lines = resolves(rows).map do |key, rule|
85
+ fields = rule.map { |name, value| "#{name}: #{value.inspect}" }.join(", ")
86
+ " #{key.inspect} => { #{fields} }.freeze"
87
+ end
88
+
89
+ <<~RUBY
90
+ #{HEADER}
91
+ module Hecks
92
+ module Bluebook
93
+ module DSL
94
+ module BootstrapTable
95
+ # [context, word] => the method a word's whole call forwards to.
96
+ CALLS = {
97
+ #{calls_lines.join(",\n")}
98
+ }.freeze
99
+
100
+ # [word, context] => the RuleReference primitive a bare reference resolves through.
101
+ RESOLVES = {
102
+ #{resolves_lines.join(",\n")}
103
+ }.freeze
104
+ end
105
+ end
106
+ end
107
+ end
108
+ RUBY
109
+ end
110
+ end
111
+ end
112
+ end
@@ -2,30 +2,30 @@ require_relative "../projector"
2
2
 
3
3
  module Hecks
4
4
  module Projections
5
- # A DOMAIN'S OWN SHAPE, PROJECTED AS MERMAID DIAGRAMS — the same
5
+ # A domain's own shape, projected as MERMAID diagrams — the same
6
6
  # trick `Projections::Reference`/`DocsProjector` already play for
7
- # prose, one level further: a diagram generated FROM the
7
+ # prose, one level further: a diagram generated from the
8
8
  # declaration can't drift from it the way a hand-drawn one
9
9
  # inevitably does, because there is no second copy to forget to
10
10
  # update.
11
11
  #
12
- # MERMAID, NOT GRAPHVIZ (the two considered) — every diagram type
12
+ # MERMAID, not Graphviz (the two considered) — every diagram type
13
13
  # below has a Mermaid form purpose-built for exactly what the
14
- # underlying construct already is (a `lifecycle` IS a state
14
+ # underlying construct already is (a `lifecycle` is a state
15
15
  # machine, `has_many`/`belongs_to` already speaks in cardinality,
16
- # `emits`/`trigger` already IS a directed graph), and the output is
16
+ # `emits`/`trigger` already is a directed graph), and the output is
17
17
  # plain text that renders natively wherever this project's own docs
18
18
  # already live — GitHub markdown, this repo's generated docs, Claude
19
19
  # Artifacts — with no build step and no external binary. Graphviz's
20
- # DOT format needs an actual render step (a `dot` binary, or a WASM
20
+ # dot format needs an actual render step (a `dot` binary, or a WASM
21
21
  # port) to become anything viewable, which is a real dependency this
22
22
  # repository's own discipline (see rust/parser's Cargo.toml: "no
23
23
  # dependency earns its way past std") would rather not take just to
24
24
  # draw a diagram.
25
25
  #
26
- # FOUR DIAGRAM KINDS, one file each per domain except lifecycles
26
+ # Four diagram kinds, one file each per domain except lifecycles
27
27
  # (one per lifecycle-bearing construct, since that's how a reader
28
- # actually reaches for it — looking at ONE aggregate's states, not
28
+ # actually reaches for it — looking at one aggregate's states, not
29
29
  # every aggregate's at once):
30
30
  #
31
31
  # <Name>_lifecycle.mmd stateDiagram-v2 one per lifecycle
@@ -47,27 +47,27 @@ module Hecks
47
47
  # <Name>_surface.mmd flowchart one per aggregate/entity that
48
48
  # declares at least one command
49
49
  # or query — everything you can
50
- # DO to it and ASK about it,
51
- # AND what each command WRITES,
50
+ # do to it and ask about it,
51
+ # and what each command writes,
52
52
  # in one place
53
53
  # <Name>_saga.mmd stateDiagram-v2 one per process_manager —
54
54
  # its own states, and what
55
55
  # each transition dispatches
56
56
  # elsewhere in the domain
57
- # frameworks.mmd flowchart every OTHER domain this one
57
+ # frameworks.mmd flowchart every other domain this one
58
58
  # depends on — a shared
59
59
  # framework it `uses_framework`,
60
60
  # or a domain a policy reaches
61
61
  # `across` — the one diagram
62
- # here that looks OUTWARD past
62
+ # here that looks outward past
63
63
  # this domain's own boundary
64
64
  #
65
- # CONSTRUCT NAMES (aggregate/entity/command/event) ARE USED BARE,
66
- # UNSANITIZED, as Mermaid node/entity ids — safe because this
65
+ # Construct names (aggregate/entity/command/event) are used bare,
66
+ # unsanitized, as Mermaid node/entity ids — safe because this
67
67
  # language's own word grammar only ever admits simple CamelCase/
68
68
  # snake_case identifiers there (confirmed: no space or punctuation
69
69
  # appears in any real aggregate/command/event name across the corpus
70
- # this projects from). A `role:` STRING IS FREE TEXT, though — the
70
+ # this projects from). A `role:` string is free text, though — the
71
71
  # real corpus already has "Back office"/"Vault officer"/"Branch
72
72
  # clerk" — so `roles.mmd` is the one diagram here that sanitizes a
73
73
  # name into an id (`role_id`) while keeping the real string as the
@@ -125,8 +125,8 @@ module Hecks
125
125
 
126
126
  # ── shared ────────────────────────────────────────────────────────
127
127
 
128
- # AN ENTITY CAN CARRY ITS OWN LIFECYCLE, RELATIONSHIP, OR COMMAND
129
- # TOO — its own `lifecycle`/`reference_to`/`command` block,
128
+ # An entity can carry its own lifecycle, relationship, or command
129
+ # too — its own `lifecycle`/`reference_to`/`command` block,
130
130
  # addressed through its holding aggregate the same way
131
131
  # `DocsProjector` already treats an aggregate and its entities
132
132
  # alike. Walking both here means a domain's entity gaining any of
@@ -137,7 +137,7 @@ module Hecks
137
137
 
138
138
  def holders_with_lifecycle(bluebook) = holders(bluebook).select(&:lifecycle)
139
139
 
140
- # `chapter_name` DRIVES THE RE-RUN HINT ALWAYS — that's the one
140
+ # `chapter_name` drives the re-run hint always — that's the one
141
141
  # argument `bin/project_diagrams` actually takes, regardless of
142
142
  # which single aggregate/entity `subject` happens to name. Passing
143
143
  # the wrong one here once already produced a real, committed
@@ -169,12 +169,12 @@ module Hecks
169
169
 
170
170
  # ── relationships -> erDiagram ───────────────────────────────────
171
171
 
172
- # STANDARD CROW'S-FOOT READING, the same convention every ORM's own
172
+ # Standard crow's-foot reading, the same convention every ORM's own
173
173
  # ERD generator (Rails' erd gem included) already uses:
174
- # `has_many`/`has_one` are read from the OWNING side — one Holder
174
+ # `has_many`/`has_one` are read from the owning side — one Holder
175
175
  # relates to many/one Target. `belongs_to`/`reference_to` are read
176
- # from the TARGET's side instead — one Target can be pointed at by
177
- # MANY Holders — because a bare reference carries no promise about
176
+ # from the target's side instead — one Target can be pointed at by
177
+ # many Holders — because a bare reference carries no promise about
178
178
  # how many holders point back at it; "many" is the honest default
179
179
  # absent a declared uniqueness rule this language doesn't expose.
180
180
  # `optional?` only ever softens the side that can genuinely be
@@ -205,8 +205,8 @@ module Hecks
205
205
 
206
206
  # ── dispatch -> flowchart ─────────────────────────────────────────
207
207
 
208
- # A COMMAND NODE, STADIUM-SHAPED (`(["..."])`); AN EVENT NODE,
209
- # HEXAGONAL (`{{"..."}}`) — one visual vocabulary for "a thing
208
+ # A command node, stadium-shaped (`(["..."])`); an event node,
209
+ # hexagonal (`{{"..."}}`) — one visual vocabulary for "a thing
210
210
  # someone does" versus "a fact that happened", matching the
211
211
  # language's own verb/event distinction. Command ids are qualified
212
212
  # by their owning aggregate (`cmd_Order_Purchase`) since two
@@ -235,13 +235,13 @@ module Hecks
235
235
  %( #{command_node(holder.hecks_name, command.hecks_name)} -->|emits| #{event_node(event)})
236
236
  end
237
237
 
238
- # `on_event` IS SOMETIMES AGGREGATE-QUALIFIED
239
- # (`"Account.AccountFrozen"`) AND SOMETIMES BARE
238
+ # `on_event` is sometimes aggregate-qualified
239
+ # (`"Account.AccountFrozen"`) and sometimes bare
240
240
  # (`"CustomerSuspended"`) in the real corpus — `emits` never is,
241
241
  # so this always matches against the bare tail, the same
242
242
  # normalization a reader has to do by eye today.
243
243
  #
244
- # A TRIGGER CROSSING INTO ANOTHER DOMAIN (`policy.target_domain`)
244
+ # A trigger crossing into another domain (`policy.target_domain`)
245
245
  # still draws — the target command just has no incoming `emits`
246
246
  # edge of its own here, which honestly shows "dispatch continues
247
247
  # elsewhere" rather than silently dropping the edge. The label
@@ -261,7 +261,7 @@ module Hecks
261
261
 
262
262
  # ── roles -> flowchart ────────────────────────────────────────────
263
263
 
264
- # WHO ISSUES WHAT, ACROSS THE WHOLE DOMAIN — data no existing
264
+ # Who issues what, across the whole domain — data no existing
265
265
  # projection draws at all today (the reference pages' own
266
266
  # `command_entry` only ever prints a command's role as a single
267
267
  # line of prose, never assembled across commands). A command with
@@ -284,7 +284,7 @@ module Hecks
284
284
 
285
285
  def role_node(role_name) = %(#{role_id(role_name)}((#{role_name})))
286
286
 
287
- # A ROLE NAME IS FREE TEXT ("Back office", "Vault officer") —
287
+ # A role name is free text ("Back office", "Vault officer") —
288
288
  # unlike every other name this file uses as a bare id, this one
289
289
  # has to be sanitized to become a legal Mermaid identifier. The
290
290
  # real string still appears as the node's own label
@@ -293,7 +293,7 @@ module Hecks
293
293
 
294
294
  # ── ports -> flowchart ───────────────────────────────────────────
295
295
 
296
- # A PORT OPERATION IS A BOUNDARY TRANSLATION, NOT A VERB OR A FACT
296
+ # A port operation is a boundary translation, not a verb or a fact
297
297
  # its own reference page says so plainly ("the builder behind it
298
298
  # defines no `given` or `sets`, so an operation cannot read
299
299
  # aggregate state or mutate a record itself"), so it gets a third
@@ -302,7 +302,7 @@ module Hecks
302
302
  # state landing somewhere, the same reason a data store gets one
303
303
  # in an ordinary flowchart.
304
304
  #
305
- # TWO EDGE KINDS PER OPERATION: a dotted "exposes" edge from the
305
+ # Two edge kinds per operation: a dotted "exposes" edge from the
306
306
  # aggregate the port hangs off (always present — a port always
307
307
  # belongs to exactly one aggregate), and a solid "to:" edge to
308
308
  # whichever aggregate the operation itself names as its receiver
@@ -311,8 +311,8 @@ module Hecks
311
311
  # `emits` reuses `dispatch.mmd`'s own `event_node` unchanged — the
312
312
  # same fact, reached from a different direction.
313
313
  #
314
- # `bluebook.aggregates`, NOT the shared `holders` — unlike a
315
- # lifecycle/relationship/command, a port belongs to an AGGREGATE
314
+ # `bluebook.aggregates`, not the shared `holders` — unlike a
315
+ # lifecycle/relationship/command, a port belongs to an aggregate
316
316
  # only; an entity has no `ports` method at all (confirmed: calling
317
317
  # it raises, it isn't just always empty), so walking entities here
318
318
  # the way every other diagram in this file does would crash on
@@ -343,17 +343,17 @@ module Hecks
343
343
 
344
344
  # ── read models -> flowchart ─────────────────────────────────────
345
345
 
346
- # THE READ-SIDE COMPLEMENT TO `relationships.mmd` — that diagram
347
- # shows how aggregates reference each other for WRITES
346
+ # The read-side complement to `relationships.mmd` — that diagram
347
+ # shows how aggregates reference each other for writes
348
348
  # (`has_many`/`belongs_to`/`reference_to`); this shows how a
349
- # `read_model` ASSEMBLES data for READS, from
349
+ # `read_model` assembles data for reads, from
350
350
  # `aggregate_heads` — the same list `where`/`group_by`/`order_by`
351
351
  # all operate over, and the one fact every read_model has
352
352
  # regardless of whether it's rooted (`reference_target`) or
353
353
  # gathers heads with no root at all (a rootless read model, real
354
354
  # in the corpus: `AccountsByKind`).
355
355
  #
356
- # A READ MODEL IS A SUBROUTINE SHAPE (`[[...]]`, "a predefined
356
+ # A read model is a subroutine shape (`[[...]]`, "a predefined
357
357
  # process") — a fourth shape, beside `ports.mmd`'s trapezoid and
358
358
  # `dispatch.mmd`'s stadium/hexagon: not a verb, not a fact, not a
359
359
  # boundary translation, but a standing, reusable view. Every
@@ -363,7 +363,7 @@ module Hecks
363
363
  # (real in banking: `Account` feeds four) merges into one node
364
364
  # across the whole diagram.
365
365
  #
366
- # THE LABEL NAMES THE SHAPE OF THE ANSWER, NOT JUST THE NAME
366
+ # The label names the shape of the answer, not just the name
367
367
  # `(count)`/`(median: field)` for the two real aggregations in the
368
368
  # corpus, nothing appended for an ordinary row-returning
369
369
  # read_model. Still MVP scope: `where`/`group_by`/`order_by`
@@ -381,12 +381,12 @@ module Hecks
381
381
  shape = read_model.to_h
382
382
  node = %(rm_#{shape[:name]}[["#{read_model_label(shape)}"]])
383
383
  Array(shape[:aggregate_heads]).map do |head|
384
- # QUOTED, NOT BARE — an edge label containing `[` or `]`
384
+ # **Quoted, not bare** — an edge label containing `[` or `]`
385
385
  # (`accounts[]`, marking the "many" side) breaks Mermaid's own
386
386
  # `|label|` parser outright if left unquoted: it reads the
387
- # `[` as the START OF A NEW NODE SHAPE mid-label, not text.
387
+ # `[` as the start of a new node shape mid-label, not text.
388
388
  # Confirmed live against the real parser before this quoting
389
- # existed — every OTHER edge label in this file happens to be
389
+ # existed — every other edge label in this file happens to be
390
390
  # a bare word or already-quoted string, so this is the one
391
391
  # spot that needed it.
392
392
  label = head[:many] ? "#{head[:as]}[]" : head[:as]
@@ -403,49 +403,49 @@ module Hecks
403
403
 
404
404
  # ── surface -> flowchart ─────────────────────────────────────────
405
405
 
406
- # "WHAT CAN I DO TO THIS, WHAT CAN I ASK ABOUT IT" — one file per
406
+ # "What can I do to this, what can I ask about it" — one file per
407
407
  # holder, unlike every other diagram here: `dispatch.mmd` already
408
408
  # shows a command's own onward reaction chain, but never an
409
- # aggregate's own FULL command/query menu in one place, and
409
+ # aggregate's own full command/query menu in one place, and
410
410
  # `roles.mmd` shows who issues a command without saying what else
411
411
  # that same aggregate answers. This is the one diagram meant to
412
412
  # be read starting from the aggregate, not from a verb or a fact.
413
413
  #
414
- # A QUERY IS A DIAMOND — a fifth shape, beside `dispatch.mmd`'s
414
+ # A query is a diamond — a fifth shape, beside `dispatch.mmd`'s
415
415
  # stadium/hexagon, `ports.mmd`'s trapezoid, and `read_models.mmd`'s
416
416
  # subroutine: a question with an answer, not a verb that changes
417
417
  # anything. Command edges are solid ("does"); query edges are
418
418
  # dotted ("asks") — the same solid/dotted split `ports.mmd`
419
419
  # already uses for "routes to:" versus "exposes".
420
420
  #
421
- # A WRITE TARGET IS A PLAIN RECTANGLE — a sixth shape, the first
421
+ # A write target is a plain rectangle — a sixth shape, the first
422
422
  # here with no special bracket at all: an attribute is the
423
423
  # smallest, most passive thing this vocabulary names, a single
424
- # field living INSIDE the cylinder rather than a bounded thing of
424
+ # field living inside the cylinder rather than a bounded thing of
425
425
  # its own. `command.mutations` (`sets`/`increment`/`decrement`/
426
426
  # `append`) was invisible everywhere before this — not just in a
427
- # diagram, in ANY projection, including the prose ones — despite
427
+ # diagram, in any projection, including the prose ones — despite
428
428
  # being the single densest fact in the whole IR (53 real
429
429
  # mutations across pizzas + banking). `dispatch.mmd` draws what a
430
- # command EMITS; this draws what it WRITES, the other half of
430
+ # command emits; this draws what it writes, the other half of
431
431
  # "what actually happens" a command never showed before.
432
432
  #
433
- # THE SAME ATTRIBUTE NODE MERGES ACROSS COMMANDS — real in
433
+ # The same attribute node merges across commands — real in
434
434
  # banking: `Account.Credit` and `Account.Debit` both point at the
435
435
  # same `balance` node, the same "one node, several incoming
436
436
  # edges" merge `read_models.mmd` already does for an aggregate
437
437
  # fed by several read_models.
438
438
  #
439
- # THE LABEL NAMES THE REAL SOURCE, NOT JUST THE VERB — an
439
+ # The label names the real source, not just the verb — an
440
440
  # increment/decrement/set almost always takes its value from an
441
- # argument, but not always the SAME-NAMED one: real in banking,
441
+ # argument, but not always the same-named one: real in banking,
442
442
  # `Account.Credit`'s own `balance` is incremented by its
443
443
  # `amount` argument, and `LedgerEntry.Amend`'s own `amount` is
444
444
  # incremented by its `adjustment` argument. A literal source
445
445
  # (pizzas' own `Order.Purchase` sets `status` to the literal
446
446
  # `"sold"`, not an argument at all) is named as verbatim as
447
447
  # every other fact in this file. `append`'s own fields carry no
448
- # single source at all — its own field NAMES are the fact worth
448
+ # single source at all — its own field names are the fact worth
449
449
  # stating (real: `Order.AddTopping` appends `name, amount`).
450
450
  def surface_diagram(bluebook, holder)
451
451
  lines = holder.commands.map do |command|
@@ -477,9 +477,9 @@ module Hecks
477
477
 
478
478
  def mutation_label(shape)
479
479
  verb = "#{shape[:op]}s"
480
- # `fields:` (not `source:`) IS the multi-binding shape
480
+ # `fields:` (not `source:`) is the multi-binding shape
481
481
  # (`Mutation#to_h`'s own `[:append, :delegate, :corrects]`
482
- # branch) — checked by the KEY'S PRESENCE, not by re-listing
482
+ # branch) — checked by the key's presence, not by re-listing
483
483
  # which ops use it a second time here, the same lesson
484
484
  # `Change.op`'s own `admits: Vocabulary::MutationOp` already
485
485
  # drew (command.bluebook's own comment): a second list of "the
@@ -492,7 +492,7 @@ module Hecks
492
492
  "#{verb}: #{detail}"
493
493
  end
494
494
 
495
- # A LITERAL VALUE CAN CONTAIN A DOUBLE QUOTE OF ITS OWN — real in
495
+ # A literal value can contain a double quote of its own — real in
496
496
  # banking: `Customer.Reinstate` sets `standing` to a rendered
497
497
  # value-object literal, `{:value=>"good"}`, whose own embedded `"`
498
498
  # broke this label's outer `|"..."|` quoting outright (caught by
@@ -515,19 +515,19 @@ module Hecks
515
515
 
516
516
  # ── sagas -> stateDiagram-v2 ─────────────────────────────────────
517
517
 
518
- # A SAGA HAS A LIFECYCLE TOO — the same `stateDiagram-v2` shape
518
+ # A saga has a lifecycle too — the same `stateDiagram-v2` shape
519
519
  # `lifecycle_diagram` already draws, one file per process_manager
520
520
  # the same way lifecycle is one file per lifecycle-bearing holder.
521
521
  # What's different is the label: a lifecycle's own edge is labeled
522
- # by the COMMAND that causes it (an aggregate transitions because
523
- # something was DONE to it); a saga's edge is labeled by the EVENT
524
- # that causes it (a saga advances because something HAPPENED,
522
+ # by the command that causes it (an aggregate transitions because
523
+ # something was done to it); a saga's edge is labeled by the event
524
+ # that causes it (a saga advances because something happened,
525
525
  # possibly nowhere near the saga itself) — the same command/event
526
526
  # split `dispatch.mmd`'s own stadium/hexagon vocabulary already
527
527
  # draws, here spent on which noun labels a stateDiagram-v2 edge
528
528
  # instead.
529
529
  #
530
- # THE LABEL ALSO NAMES WHAT THE TRANSITION DISPATCHES — a fact no
530
+ # The label also names what the transition dispatches — a fact no
531
531
  # existing diagram states for a saga at all: a lifecycle's own
532
532
  # edge only ever names the one command that caused it; a saga's
533
533
  # edge can fire several commands at once (real in banking:
@@ -538,7 +538,7 @@ module Hecks
538
538
  # fires lands inside its own bluebook chapter, so this never needs
539
539
  # `dispatch.mmd`'s own "triggers in X" cross-domain label.
540
540
  #
541
- # THE COMPENSATING LEG READS LIKE ANY OTHER — its own trigger is
541
+ # The compensating leg reads like any other — its own trigger is
542
542
  # the literal string "refused" (`ProcessManager::REFUSED`, this
543
543
  # language's own Trigger vocabulary), not invented text: a
544
544
  # dispatch declined is exactly as real a cause of a state
@@ -556,12 +556,12 @@ module Hecks
556
556
  MERMAID
557
557
  end
558
558
 
559
- # THE REFUSED EDGE'S OWN DISPATCH LIST IS PARTLY DERIVED NOW
559
+ # The REFUSED edge's own dispatch list is partly derived now
560
560
  # per-dispatch saga compensation (`compensates`) moved a saga's own
561
- # compensating dispatches OFF the hand-written `on :refused` leg
561
+ # compensating dispatches off the hand-written `on :refused` leg
562
562
  # and onto whichever forward dispatch each one undoes, so
563
- # `handler.dispatches` alone would render an EMPTY compensating
564
- # edge for any saga using it — accurate to the DECLARATION, wrong
563
+ # `handler.dispatches` alone would render an empty compensating
564
+ # edge for any saga using it — accurate to the declaration, wrong
565
565
  # about what the runtime actually does at refusal (it derives and
566
566
  # fires every declared `compensates`, newest first). `saga` is
567
567
  # passed through for exactly this — only the REFUSED handler needs
@@ -569,7 +569,7 @@ module Hecks
569
569
  # everything real about it.
570
570
  def saga_edge(handler, saga)
571
571
  label = handler.event_type
572
- # DERIVED FIRST, then the hand-written body — the same order
572
+ # Derived first, then the hand-written body — the same order
573
573
  # `SagaInterpreter#unwind` actually runs them in (every
574
574
  # completed leg's own `compensates` before this leg's own
575
575
  # hand-written dispatches), not declaration order on the page.
@@ -591,27 +591,27 @@ module Hecks
591
591
 
592
592
  # ── frameworks -> flowchart ─────────────────────────────────────
593
593
 
594
- # EVERY OTHER DIAGRAM IN THIS FILE STAYS INSIDE ONE DOMAIN'S OWN
595
- # BOUNDARY — this is the one that steps outside it. A real domain
594
+ # Every other diagram in this file stays inside one domain's own
595
+ # boundary — this is the one that steps outside it. A real domain
596
596
  # depends on another domain's own aggregates in exactly two ways:
597
597
  # `uses_framework "X"` in its `.hecksagon` (`Hecksagon#framework_
598
- # members`), which loads X's whole bluebook into THIS registry,
598
+ # members`), which loads X's whole bluebook into this registry,
599
599
  # unconditionally, the moment this domain boots; or a policy's own
600
600
  # `across "X"` (`Policy#target_domain`), which only reaches X when
601
601
  # the policy's declared event actually fires. Same underlying
602
602
  # fact `dispatch.mmd`'s own `trigger_edge` already draws from the
603
603
  # command's side ("triggers in X") — this draws it again from the
604
- # DOMAIN's side, next to the structural `uses_framework` fact
604
+ # domain's side, next to the structural `uses_framework` fact
605
605
  # `dispatch.mmd` never sees at all (that lives in the `.hecksagon`,
606
606
  # which no other diagram here is handed).
607
607
  #
608
- # NEITHER THIS DOMAIN NOR EACH DEPENDENCY GETS THE holders() TREATMENT
609
- # — a whole domain is drawn as ONE cylinder, the same "a bounded,
608
+ # Neither this domain nor each dependency gets the holders() treatment
609
+ # — a whole domain is drawn as one cylinder, the same "a bounded,
610
610
  # addressable thing" shape every other diagram here already spends
611
611
  # on a single aggregate, just scaled up one level: a domain is a
612
612
  # bigger box the same kind of box lives inside.
613
613
  #
614
- # DOTTED FOR `attaches`, SOLID FOR `reaches across` — the reverse
614
+ # Dotted for `attaches`, solid for `reaches across` — the reverse
615
615
  # of which fact is "always true" between the two: attaching a
616
616
  # framework is a standing declaration, true every time this domain
617
617
  # boots, so it gets the same dotted "this always belongs" treatment
@@ -621,8 +621,8 @@ module Hecks
621
621
  # draws for the identical fact, kept solid here so the same
622
622
  # relationship reads the same way in both diagrams.
623
623
  #
624
- # `options[:hecksagon]` IS THE ONE DIAGRAM IN THIS FILE THAT NEEDS
625
- # MORE THAN `bluebook` — `framework_members` lives on the
624
+ # `options[:hecksagon]` is the one diagram in this file that needs
625
+ # more than `bluebook` — `framework_members` lives on the
626
626
  # `Hecksagon`, a sibling IR object `bin/project_diagrams` already
627
627
  # has in hand (`registry.hecksagon(chapter_name)`) but `bluebook`
628
628
  # itself carries no reference to. No hecksagon handed in (an older