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
@@ -2,15 +2,15 @@ require_relative "../naming"
2
2
 
3
3
  module Hecks
4
4
  module Projector
5
- # A BLUEBOOK, PROJECTED AS ITS OWN COMMAND-LINE SURFACE.
5
+ # A bluebook, projected as its own command-line surface.
6
6
  #
7
7
  # Every verb a domain declares is a subcommand; every argument is an
8
- # option whose TYPE, whose admitted values and whose required-ness are
8
+ # option whose type, whose admitted values and whose required-ness are
9
9
  # already stated in the chapter. A hand-written CLI restates all of it and
10
10
  # then drifts — and the first thing to drift is the help text, which is the
11
11
  # only part anybody reads.
12
12
  #
13
- # WHAT IS PROJECTED, AND WHAT IS NOT. This answers the SURFACE — the verb
13
+ # What is projected, and what is not. This answers the surface — the verb
14
14
  # tree, the argument spec, the usage text — and nothing executes here. One
15
15
  # small generic runner (`bin/run`) boots a domain, asks for this, parses
16
16
  # against it and dispatches.
@@ -23,7 +23,7 @@ module Hecks
23
23
  # reads. Projecting the surface keeps one dispatcher and a help text that
24
24
  # cannot be stale, because it is computed at the moment it is printed.
25
25
  #
26
- # THE TYPING IS THE POINT. A CLI hands everything over as a String.
26
+ # **The typing is the point**. A CLI hands everything over as a String.
27
27
  # `sequence.value=99` has to become the Integer 99 or the runtime refuses
28
28
  # it, and the only honest place to learn that is the value object's own
29
29
  # declared field type. A CLI that guessed — "it looks like a number" —
@@ -32,7 +32,7 @@ module Hecks
32
32
  module CliProjector
33
33
  module_function
34
34
 
35
- # TWO NAMESPACES, NOT ONE — `{ verbs:, questions:, usage: }`.
35
+ # Two namespaces, not one — `{ verbs:, questions:, usage: }`.
36
36
  #
37
37
  # A chapter may legally declare a command and a query of one name: the
38
38
  # language namespaces them and `Banking::Account.Open` is both, in the
@@ -62,32 +62,32 @@ module Hecks
62
62
  end
63
63
  end
64
64
 
65
- # A PORT IS A VERB TOO, and leaving it off the map was a real gap
65
+ # A port is a verb too, and leaving it off the map was a real gap
66
66
  # rather than a tasteful omission. The runtime has always dispatched
67
67
  # a port operation by exactly the same name as a command — the
68
68
  # projection simply never listed one, so `run_specs` and `file`
69
69
  # answered "no such verb" while working perfectly through Ruby.
70
70
  #
71
71
  # It matters most for the caller with no other door. An agent that
72
- # may not shell out reaches this domain ONLY through the projected
72
+ # may not shell out reaches this domain only through the projected
73
73
  # CLI, and a port it cannot see is a capability it does not have.
74
74
  aggregate.ports.each do |port|
75
75
  port.operations.each { |o| claim(verbs, name_for(aggregate, o), port_spec(bluebook, aggregate, port, o)) }
76
76
  end
77
77
  end
78
78
 
79
- # A REPORT IS A QUESTION TOO, and leaving it off was the same gap the
79
+ # A report is a question too, and leaving it off was the same gap the
80
80
  # ports had: `Dispatcher#query` has always answered `Domain.ReportName`,
81
81
  # the projection simply never listed one — so the composed reads worked
82
82
  # from Ruby and did not exist for anybody whose only door is the command
83
83
  # line.
84
84
  #
85
- # It matters most for exactly what a report is FOR. Every other question
85
+ # It matters most for exactly what a report is for. Every other question
86
86
  # here answers with rows and leaves the arithmetic to the reader; a
87
87
  # `group_by` report is the one that counts. An agent that cannot reach
88
88
  # it can list bugs all day and never answer "how are we doing".
89
89
  #
90
- # ONE DOT, NOT TWO — a report belongs to the chapter rather than to any
90
+ # **One dot, not two** — a report belongs to the chapter rather than to any
91
91
  # aggregate (that is what rootless means), so it is addressed
92
92
  # `QualityControl.BugsByStatus` where a query is
93
93
  # `QualityControl::Bug.Queue`. `Dispatcher#query` splits on precisely
@@ -96,7 +96,7 @@ module Hecks
96
96
  claim(questions, Naming.snake(model.hecks_name), report_spec(bluebook, model))
97
97
  end
98
98
 
99
- # THE SHORT SPELLING, WHERE IT CANNOT BE AMBIGUOUS. `pizzas
99
+ # The short spelling, where it cannot be ambiguous. `pizzas
100
100
  # create_pizza` rather than `pizzas order.create_pizza` — the
101
101
  # aggregate is worth typing only when two of them declare the same
102
102
  # verb, and in a one-aggregate domain it never is. Both spellings are
@@ -129,7 +129,7 @@ module Hecks
129
129
  end
130
130
  end
131
131
 
132
- # A NAME IS CLAIMED ONCE. A command and a query of one name are legal in
132
+ # A name is claimed once. A command and a query of one name are legal in
133
133
  # a chapter — the language namespaces them — and ambiguous as
134
134
  # subcommands. Refused here rather than silently resolving to whichever
135
135
  # was walked first, which is how `Ticket.Filed` (a command) and
@@ -156,7 +156,7 @@ module Hecks
156
156
  entity ? "#{entity.hecks_name}." : "", verb.hecks_name].join
157
157
  end
158
158
 
159
- # THE ARGUMENTS A RECEIVER ADDS, BEFORE ANY VERB-SPECIFIC ONE. Shared by
159
+ # The arguments a receiver adds, before any verb-specific one. Shared by
160
160
  # `command_spec` and `port_spec` — a port operation always addresses an
161
161
  # aggregate record (`port_spec` passes `receiver: :aggregate`, never
162
162
  # `:entity` or `nil`, because a port is declared on an aggregate, never
@@ -194,7 +194,7 @@ module Hecks
194
194
  (command.creates? ? nil : :aggregate)
195
195
  end
196
196
 
197
- # THE RECEIVER IS NOT A COMMAND ARGUMENT. An aggregate command names
197
+ # The receiver is not a command argument. An aggregate command names
198
198
  # its record through to; an entity command needs both the aggregate
199
199
  # record and the entity element within it. Keeping those paths in the
200
200
  # projected option list makes the human-facing request complete while
@@ -215,7 +215,7 @@ module Hecks
215
215
  refusals: refusals(command, holder), arguments: arguments }
216
216
  end
217
217
 
218
- # A PORT OPERATION READS AS A VERB BUT REPORTS AS A BOUNDARY.
218
+ # A port operation reads as a verb but reports as a boundary.
219
219
  #
220
220
  # `creates: false` because it makes no record, and `refusals: []`
221
221
  # because it has none in the sense every other verb means: a command's
@@ -223,7 +223,7 @@ module Hecks
223
223
  # outbound operation's failure is somebody else's sentence, unknowable
224
224
  # from here.
225
225
  #
226
- # THE SUMMARY NAMES BOTH ENDINGS, which is the one thing a caller most
226
+ # The summary names both endings, which is the one thing a caller most
227
227
  # needs and cannot infer. `run_specs` looks like it either works or
228
228
  # errors; what it actually does is answer `SpecsCompleted` even when the
229
229
  # suite is red, and refuse only when rspec could not run. Somebody
@@ -233,7 +233,7 @@ module Hecks
233
233
  arguments = receiver_options(:aggregate, aggregate, nil) +
234
234
  operation.attributes.flat_map { |a| options_for(a, aggregate, aggregate) }
235
235
 
236
- # THE WIRE NAME CARRIES THE PORT, THE TYPED NAME DOES NOT.
236
+ # The wire name carries the port, the typed name does not.
237
237
  #
238
238
  # `Dispatcher#dispatch` splits a verb into head and sub and looks the
239
239
  # head up as a port, so a port operation is addressed
@@ -244,7 +244,7 @@ module Hecks
244
244
  # names it short, which is the same split `shorten` already makes.
245
245
  { verb: [fqn(bluebook, aggregate, operation).sub(/\.[^.]+\z/, ""), port.name, operation.hecks_name].join("."),
246
246
  kind: :command, creates: false, receiver: :aggregate, refusals: [],
247
- # `role:` HERE IS DESCRIPTIVE TEXT, NOT AN AUTHORIZATION GATE
247
+ # `role:` here is descriptive text, not an authorization gate
248
248
  # who calls whom through the port, for `--help`/`verb_help`'s
249
249
  # "issued by" line. A port operation never reaches
250
250
  # `CommandRules::Authorization#refuse_role_mismatch` (only
@@ -266,7 +266,7 @@ module Hecks
266
266
  "Ask #{port.name} — answers #{operation.answers}, refuses #{operation.refuses}"
267
267
  end
268
268
 
269
- # A ROOTLESS REPORT TAKES NOTHING; a rooted one takes the id of the
269
+ # A rootless report takes nothing; a rooted one takes the id of the
270
270
  # record it is a view of, under the name the model gave that reference.
271
271
  def report_spec(bluebook, model)
272
272
  arguments =
@@ -293,14 +293,14 @@ module Hecks
293
293
 
294
294
  # ── one argument, flattened ───────────────────────────────────────
295
295
 
296
- # A VALUE OBJECT BECOMES ONE OPTION PER FIELD, dotted. `commit` typed
296
+ # A value object becomes one option per field, dotted. `commit` typed
297
297
  # `CommitRef` is `--commit.value`, because that is the shape the runtime
298
298
  # wants and a flat `--commit` would have to guess which field it meant.
299
299
  # Single-field value objects — almost all of them — read fine either way,
300
300
  # and the runner accepts the short form for exactly those.
301
- # RECURSIVE, AND IT HAS TO BE. A value object may hold another one —
301
+ # Recursive, and it has to be. A value object may hold another one —
302
302
  # pizzas' `Pizza` holds a `Price` and a `Size` — so stopping after one
303
- # level produced `pizza.price_cents=1500` and sent the STRING "1500"
303
+ # level produced `pizza.price_cents=1500` and sent the string "1500"
304
304
  # where `{ cents: 1500 }` belonged.
305
305
  #
306
306
  # The runtime took it. `qa/FINDINGS.md` #2 is exactly that gap —
@@ -316,13 +316,13 @@ module Hecks
316
316
  value_object = value_object_for(attribute, holder, aggregate)
317
317
  return [scalar_option(path, attribute, optional)] unless value_object
318
318
 
319
- # A LIST SAYS SO, ALL THE WAY DOWN TO ITS LEAVES.
319
+ # A list says so, all the way down to its leaves.
320
320
  #
321
321
  # Without this a `list_of(Tag)` projected exactly like a single Tag:
322
322
  # one option, `tags.value`, indistinguishable from a scalar. So the
323
323
  # help said to pass one, `CliDoor#bury` overwrote the leaf each time,
324
- # and passing two tags stored the second and lost the first WITHOUT
325
- # SAYING ANYTHING. A missing argument is refused loudly; a forgotten
324
+ # and passing two tags stored the second and lost the first without
325
+ # saying anything. A missing argument is refused loudly; a forgotten
326
326
  # one is not, which makes it the more expensive of the two by far.
327
327
  #
328
328
  # The flag is carried on the leaf rather than kept beside the
@@ -390,7 +390,7 @@ module Hecks
390
390
  program = options[:program] || "bin/run"
391
391
  only = options[:verb]
392
392
 
393
- # WHICH NAMESPACE, when both hold the name. `options[:ask]` says so;
393
+ # Which namespace, when both hold the name. `options[:ask]` says so;
394
394
  # without it a `--help` for a question would print the command that
395
395
  # shares its name, which banking has and which is how this was found.
396
396
  if only
@@ -421,16 +421,16 @@ module Hecks
421
421
  name ? "#{spec[:short]} is also #{name}" : ""
422
422
  end
423
423
 
424
- # A QUERY's `description` is written as a paragraph — it argues for why
424
+ # A query's `description` is written as a paragraph — it argues for why
425
425
  # the list is worth reading. A verb table wants the first sentence of
426
426
  # that argument; `--help` still prints the whole thing.
427
427
  def first_sentence(text)
428
428
  text.to_s.split(/(?<=\.)\s/).first.to_s
429
429
  end
430
430
 
431
- # FOUR TEXT BLOCKS, IN FIXED DISPLAY ORDER — meta (name/kind/role),
431
+ # Four text blocks, in fixed display order — meta (name/kind/role),
432
432
  # invocation, arguments, refusals. Each block is independent of the
433
- # others' content (only the OUTPUT ORDER is fixed, and stays fixed
433
+ # others' content (only the output order is fixed, and stays fixed
434
434
  # below), so each is its own method returning the lines it
435
435
  # contributes — `[]` when it contributes none — concatenated in the
436
436
  # same order the original inline version built them in.
@@ -2,38 +2,38 @@ require_relative "../naming"
2
2
 
3
3
  module Hecks
4
4
  module Projector
5
- # A BLUEBOOK, PROJECTED AS ITS OWN USAGE DOCUMENTATION.
5
+ # A bluebook, projected as its own usage documentation.
6
6
  #
7
- # WHAT THIS IS FOR. A chapter in this corpus already contains everything a
7
+ # **What this is for**. A chapter in this corpus already contains everything a
8
8
  # user of it needs: what each aggregate is (`description`), what each verb
9
9
  # is for (`goal`) and who issues it (`role`), which states it moves
10
10
  # between, what it refuses and in whose words (`given`, `invariant`,
11
11
  # `ensures`), and what each list is worth reading (`description` on a
12
- # query). None of that reaches the person who has to CALL the domain.
12
+ # query). None of that reaches the person who has to call the domain.
13
13
  # They read the source, or they read a document somebody wrote beside the
14
14
  # source and stopped updating.
15
15
  #
16
16
  # `docs/implemented/reference/` is the precedent and the contrast. `bin/reference`
17
17
  # generates it from the language's own Syntax chapter, so the reference
18
18
  # for the DSL cannot drift from the DSL. This is the same trick one level
19
- # down: the usage document for a DOMAIN, generated from that domain, so it
19
+ # down: the usage document for a domain, generated from that domain, so it
20
20
  # cannot drift from the domain either.
21
21
  #
22
- # WHY IT IS A PROJECTION AND NOT A GENERATOR SCRIPT. `Projector` is the
22
+ # Why it is a projection and not a generator script. `Projector` is the
23
23
  # repository's registry of "canonical IR in, external artifact out" (§30
24
24
  # of the implementation plan), and this is exactly that shape: one
25
25
  # bluebook's IR in, markdown out, no runtime needed and no store touched.
26
26
  # Registered as `:docs` beside `:ir`, and reachable the way every
27
27
  # projector is — `Projector.call(:docs, bluebook: ...)`.
28
28
  #
29
- # AND AS A METHOD, which is the half that makes it get used.
29
+ # And as a method, which is the half that makes it get used.
30
30
  # `Facade::Surface` already installs a module per chapter carrying
31
31
  # `vision` and `aggregates`; `docs` joins them, so a booted domain answers
32
32
  # `QualityControl.docs` and an aggregate door answers
33
33
  # `QualityControl::Bug.docs`. A document you have to remember a script for
34
34
  # is a document nobody reads.
35
35
  #
36
- # WHAT IT DELIBERATELY DOES NOT DO: invent. Every sentence below comes out
36
+ # **What it deliberately does not do**: invent. Every sentence below comes out
37
37
  # of the chapter. Where a chapter says nothing — an aggregate with no
38
38
  # `description`, a command with no `goal` — the document says nothing
39
39
  # rather than filling the gap with a restatement of the name, because a
@@ -55,7 +55,7 @@ module Hecks
55
55
  "#{out.compact.join("\n").rstrip}\n"
56
56
  end
57
57
 
58
- # A NAME THAT NAMES NOTHING IS REFUSED, not answered with an empty
58
+ # A name that names nothing is refused, not answered with an empty
59
59
  # document. Shipped the other way first: `options[:aggregate]` that
60
60
  # matched no head returned "" and exit 0, which is the silent-wrong-
61
61
  # answer shape this repository has already been bitten by twice in the
@@ -75,7 +75,7 @@ module Hecks
75
75
 
76
76
  def chapter_header(bluebook, depth)
77
77
  out = [h(depth, bluebook.name), ""]
78
- # THE VISION FIRST AND AS A QUOTE. It is the one sentence in a chapter
78
+ # The vision first and as a quote. It is the one sentence in a chapter
79
79
  # written for somebody who does not know the domain yet.
80
80
  out += ["> #{bluebook.vision}", ""] if bluebook.vision
81
81
  out << "#{bluebook.classification.to_s.capitalize} domain." if bluebook.classification
@@ -88,7 +88,7 @@ module Hecks
88
88
 
89
89
  def anchor(name) = Naming.snake(name).tr("_", "-")
90
90
 
91
- # WHAT HAPPENS WITHOUT ANYBODY ASKING — the part of a domain a caller
91
+ # What happens without anybody asking — the part of a domain a caller
92
92
  # cannot discover from any verb list, and the part most likely to surprise
93
93
  # them. A policy means one dispatch causes another, sometimes into a
94
94
  # different domain entirely; a saga means a sequence is being driven on
@@ -145,7 +145,7 @@ module Hecks
145
145
  def entity_section(aggregate, entity, depth)
146
146
  out = [h(depth, "#{entity.hecks_name} (within #{aggregate.hecks_name})"), ""]
147
147
  out += [entity.description, ""] if entity.description
148
- # THE THING A CALLER GETS WRONG FIRST. An entity has no door of its
148
+ # The thing a caller gets wrong first. An entity has no door of its
149
149
  # own: its verb is spelled through the aggregate that holds it, and
150
150
  # the parent's id travels alongside the entity's own identity.
151
151
  out << "Addressed through its holder — `#{aggregate.hecks_name}.#{entity.hecks_name}.<Verb>`, " \
@@ -171,7 +171,7 @@ module Hecks
171
171
  table(%w[attribute shape rules], rows)
172
172
  end
173
173
 
174
- # A VALUE OBJECT'S FIELDS, NOT ITS NAME. `commit` typed `CommitRef` tells
174
+ # A value object's fields, not its name. `commit` typed `CommitRef` tells
175
175
  # a caller nothing; `{ value: String }` tells them what to send, which is
176
176
  # the single most common thing to get wrong at this boundary — a bare
177
177
  # scalar where an object is wanted.
@@ -277,7 +277,7 @@ module Hecks
277
277
  end
278
278
  end
279
279
 
280
- # EVERY WAY THIS VERB CAN SAY NO, gathered from the three places a
280
+ # Every way this verb can say no, gathered from the three places a
281
281
  # chapter states them — the lifecycle it is an edge of, its own
282
282
  # `given`s, and the fact that a reference has to resolve. A caller
283
283
  # reading only the argument list learns none of these, and they are
@@ -5,9 +5,9 @@ module Hecks
5
5
  module Projector
6
6
  # Registry-wide serialization to Hash/JSON: bluebook IR
7
7
  # (`call`/`json`), era-adapter lineage-capability flags (`lineage`),
8
- # and translation edges in both their DIGEST-relevant declared shape
8
+ # and translation edges in both their digest-relevant declared shape
9
9
  # (`translation_hash`, what ApprovalDigest hashes) and their
10
- # consumer-ready COMPILED shape with precompiled SQL attached
10
+ # consumer-ready compiled shape with precompiled SQL attached
11
11
  # (`translations`/`compiled_translation_aggregate`). Read directly by
12
12
  # bin/ir, bin/project_rust, and the translation/audit approval digest.
13
13
  module Exporter
@@ -21,15 +21,15 @@ module Hecks
21
21
  JSON.pretty_generate(call(registry))
22
22
  end
23
23
 
24
- # A BINDING fact, deliberately NOT folded into `call`/`bluebook.to_h`
24
+ # A binding fact, deliberately not folded into `call`/`bluebook.to_h`
25
25
  # above — the canonical IR is runtime-independent by design (ADR
26
- # 0001: it describes what a bluebook DECLARES, never which adapter
26
+ # 0001: it describes what a bluebook declares, never which adapter
27
27
  # a deployment happens to bind it to), and "is this aggregate bound
28
28
  # to a lineage-capable adapter" is exactly the kind of fact that
29
29
  # answer can change per-deployment without the bluebook's own shape
30
30
  # changing at all. Consumers that need it (bin/project_rust's own
31
31
  # `ir.json` sidecar, rust/host's runtime era-aware seed overlay —
32
- # dispatch.rs) merge this in as a SEPARATE top-level key, the same
32
+ # dispatch.rs) merge this in as a separate top-level key, the same
33
33
  # way `translations` already sits beside `call`'s output rather than
34
34
  # inside it.
35
35
  #
@@ -54,13 +54,13 @@ module Hecks
54
54
  { capable_aggregates: capable.map { |aggregate| { name: aggregate.name, storage_name: aggregate.storage_name } } }
55
55
  end
56
56
 
57
- # A BINDING fact, same shape/reasoning as `lineage` above: every
58
- # aggregate's DECLARED persistence adapter name (`persisted_by`),
57
+ # A binding fact, same shape/reasoning as `lineage` above: every
58
+ # aggregate's declared persistence adapter name (`persisted_by`),
59
59
  # not part of the canonical bluebook shape `call` exports (ADR
60
60
  # 0001 — the IR describes what's declared, never which adapter a
61
61
  # deployment binds it to). Unlike `lineage`, this needs no era
62
62
  # plugin — `BindingPolicy` is core, always loaded — and covers
63
- # EVERY aggregate, not just lineage-capable ones: `rust/host`
63
+ # every aggregate, not just lineage-capable ones: `rust/host`
64
64
  # (`ir.rs`'s own `refuse_unsupported_persistence_adapters`) reads
65
65
  # this to refuse loudly, at boot, against a domain bound to an
66
66
  # adapter it has no backend for (Heki, Memory, Sqlite, D1,
@@ -78,7 +78,28 @@ module Hecks
78
78
  { aggregates: aggregates }
79
79
  end
80
80
 
81
- # Translation IR, always as an array, WITH each aggregate's
81
+ # A binding fact, same shape/reasoning as `lineage`/`persistence`
82
+ # above: which chapter this domain's role checks resolve against
83
+ # (`Registry#authorization_provider_for` — the domain's own chapter
84
+ # or a framework member it attaches that declares `provides
85
+ # "authorization"`), with that chapter's declared verbs qualified.
86
+ # `rust/host` (auth.rs) reads this instead of naming Governance.
87
+ # `{}` when nothing this domain attaches provides authorization.
88
+ def authorization(registry, domain_name)
89
+ provider = registry.authorization_provider_for(domain_name)
90
+ return {} unless provider
91
+
92
+ capability = Bluebook::Capabilities::AUTHORIZATION
93
+ assignments = provider.provided_verb(capability, :assignments)
94
+ {
95
+ provider: provider.name,
96
+ grant: provider.provided_verb(capability, :grant),
97
+ assignments: assignments,
98
+ assignment_aggregate: assignments&.split(".")&.first
99
+ }
100
+ end
101
+
102
+ # Translation IR, always as an array, with each aggregate's
82
103
  # precompiled SQL attached (`compiled_translation_aggregate`) —
83
104
  # this is the export a consumer embeds (`ir.json`'s `translations`
84
105
  # key), never the bare digest-relevant shape `edge_digest` hashes
@@ -104,16 +125,16 @@ module Hecks
104
125
  JSON.pretty_generate(translations(registry))
105
126
  end
106
127
 
107
- # THE DIGEST-RELEVANT SHAPE — `ApprovalDigest.edge_digest` hashes
108
- # EXACTLY this, and only this, for exactly the reason `compiled_
128
+ # The digest-relevant shape — `ApprovalDigest.edge_digest` hashes
129
+ # exactly this, and only this, for exactly the reason `compiled_
109
130
  # translation_aggregate` below must never be used for that
110
- # purpose: a digest bound to the COMPILED SQL, not just the
111
- # DECLARED rules, would invalidate an existing human approval the
131
+ # purpose: a digest bound to the compiled SQL, not just the
132
+ # declared rules, would invalidate an existing human approval the
112
133
  # moment `Translation::RuleCompiler`'s own output format changed
113
134
  # for any reason — a compiler refactor, a cosmetic SQL-formatting
114
135
  # change — even when the declared rules an approver actually
115
- # reviewed never changed at all. The approval binds to WHAT WAS
116
- # DECLARED, not to what a particular compiler build happened to
136
+ # reviewed never changed at all. The approval binds to what was
137
+ # declared, not to what a particular compiler build happened to
117
138
  # emit from it.
118
139
  def translation_hash(translation)
119
140
  {
@@ -137,15 +158,15 @@ module Hecks
137
158
  drops: aggregate.drops.map(&:to_s),
138
159
  retypes: aggregate.retypes.map { |retype| { from: retype.from, to: retype.to } },
139
160
  computes: aggregate.computes.map { |compute| { from: compute.from, to: compute.to, sql: compute.sql } },
140
- # PREVIOUSLY MISSING — found live while planning Rust-side mint
161
+ # **Previously missing** — found live while planning Rust-side mint
141
162
  # support. An edge carrying only a rekey (no compute) had its
142
163
  # approval bind to nothing rekey-specific at all: any two
143
164
  # rekey edges with otherwise-identical renames/moves/converts/
144
- # drops/retypes/computes produced the SAME digest regardless
165
+ # drops/retypes/computes produced the same digest regardless
145
166
  # of what their `rekey sql:` actually said, and a rekey's own
146
167
  # SQL could change without invalidating an existing approval.
147
168
  # Same bug shape for `backfills` (present, just never
148
- # exported). Fixing this CHANGES every existing rekey/
169
+ # exported). Fixing this changes every existing rekey/
149
170
  # backfill edge's digest — any approval already recorded for
150
171
  # one is invalidated by this fix and must be re-reviewed.
151
172
  rekeys: aggregate.rekeys.map { |rekey| { sql: rekey.sql } },
@@ -153,14 +174,14 @@ module Hecks
153
174
  }
154
175
  end
155
176
 
156
- # THE EXPORT SHAPE — `translation_aggregate`'s own digest-relevant
177
+ # The export shape — `translation_aggregate`'s own digest-relevant
157
178
  # fields, plus the precompiled SQL (`compiled_state_expression`/
158
179
  # `compiled_id_expression`) a consumer embedding this JSON
159
180
  # (rust/host's own boot-time mint) needs to execute the edge
160
- # without compiling SQL itself. The SAME call head_compiler.rb's
181
+ # without compiling SQL itself. The same call head_compiler.rb's
161
182
  # own `compile_rules(declared)`/`id_case(guard, declared)` make at
162
183
  # mint time, run here once at build/export time instead —
163
- # `Translation::RuleCompiler` is the ONE place this expression is
184
+ # `Translation::RuleCompiler` is the one place this expression is
164
185
  # built, called from both here and from head_compiler.rb's real
165
186
  # per-mint assembly, so a consumer gets Ruby's own compiler's
166
187
  # output verbatim, never a second, independently-authored SQL
@@ -1,6 +1,6 @@
1
1
  module Hecks
2
2
  module Projector
3
- # The trivial case, on purpose: canonical IR projected AS canonical
3
+ # The trivial case, on purpose: canonical IR projected as canonical
4
4
  # IR. `Bluebook#to_h` already satisfies every §30 acceptance
5
5
  # criterion on its own — no live runtime needed to call it, output is
6
6
  # deterministic (the same golden fixtures `spec/ir_golden_spec.rb`
@@ -3,20 +3,20 @@ require_relative "../forms/field_shape"
3
3
 
4
4
  module Hecks
5
5
  module Projector
6
- # A BLUEBOOK, PROJECTED AS PROSE AN SME CAN READ BACK AND CONFIRM.
6
+ # A bluebook, projected as prose an SME can read back and confirm.
7
7
  #
8
- # WHAT THIS IS FOR. `DocsProjector` already answers "what can I call and
8
+ # **What this is for**. `DocsProjector` already answers "what can I call and
9
9
  # what does it want" for the person implementing against a domain —
10
10
  # tables of arguments, shapes, refusal reasons. That is the wrong
11
11
  # register for the person who can actually say whether the domain is
12
- # RIGHT: the subject-matter expert who knows what an account is and has
12
+ # right: the subject-matter expert who knows what an account is and has
13
13
  # never read a markdown table in their life. This projects the same IR
14
14
  # as sentences instead — "Debit — take money out. Issued by a Teller. It
15
15
  # only goes through if the balance covers it." — so a domain can be
16
16
  # read back to the person who can validate it without them learning the
17
17
  # DSL first.
18
18
  #
19
- # SAME SOURCE, SAME GUARANTEE `DocsProjector` gives: nothing here is
19
+ # Same source, same guarantee `DocsProjector` gives: nothing here is
20
20
  # invented. Every sentence quotes a `description`, `goal`, or `given`
21
21
  # already declared in the chapter; where a chapter says nothing, this
22
22
  # says nothing rather than manufacturing a sentence out of an
@@ -24,7 +24,7 @@ module Hecks
24
24
  # (`Projector.call(:narrate, bluebook: ...)`), same aggregate-scoping
25
25
  # via `options[:aggregate]`.
26
26
  #
27
- # WHAT IT DOES NOT DO: replace `DocsProjector`. A shape table still says
27
+ # **What it does not do**: replace `DocsProjector`. A shape table still says
28
28
  # "id of a Customer" more precisely than any sentence would, and an
29
29
  # implementer still wants that. This is the other document the same IR
30
30
  # is owed — one written for the reader who is being asked "is this
@@ -128,7 +128,7 @@ module Hecks
128
128
  "#{header}\n\n#{body}"
129
129
  end
130
130
 
131
- # ONE PARAGRAPH, BUILT FROM INDEPENDENT SENTENCES — each sentence
131
+ # One paragraph, built from independent sentences — each sentence
132
132
  # below states one unrelated fact about `command` (its goal, who
133
133
  # issues it, whether it creates the holder, what it takes, what it
134
134
  # references, what gates it, what it guarantees, what it emits), in
@@ -149,7 +149,7 @@ module Hecks
149
149
  ].compact.join(" ")
150
150
  end
151
151
 
152
- # THE GOAL, VERBATIM — same rule `DocsProjector` holds to: quoted
152
+ # The goal, verbatim — same rule `DocsProjector` holds to: quoted
153
153
  # exactly as declared, not recased to fit mid-sentence, because the
154
154
  # promise this whole projector makes is that a sentence here is a
155
155
  # sentence the chapter actually wrote.
@@ -163,8 +163,8 @@ module Hecks
163
163
  "Issued by #{a_or_an(command.role)} #{command.role}."
164
164
  end
165
165
 
166
- # `acts_on.nil?`, NOT `creates?` — `creates?` answers true for every
167
- # verb an ENTITY declares (it never references itself; see
166
+ # `acts_on.nil?`, not `creates?` — `creates?` answers true for every
167
+ # verb an entity declares (it never references itself; see
168
168
  # `Command#acts_on`'s own comment), so reading it directly here would
169
169
  # tell an SME that `LedgerEntry.Amend` brings a new ledger entry into
170
170
  # being, which is exactly backwards.
@@ -208,7 +208,7 @@ module Hecks
208
208
  "It records `#{command.emits.join('`, `')}` as a fact."
209
209
  end
210
210
 
211
- # EVERY REQUIRED CONDITION, STATED AS SOMETHING THAT MUST BE TRUE
211
+ # Every required condition, stated as something that must be true
212
212
  # the same three sources `DocsProjector#refusals_of` reads (the
213
213
  # lifecycle edge, a reference's existence, and the command's own
214
214
  # `given`s), but kept positive rather than phrased as a refusal
@@ -241,7 +241,7 @@ module Hecks
241
241
  lines = queries.map do |query|
242
242
  shape = query.to_h
243
243
  takes = Array(shape[:attributes]).map { |a| Forms::Humanize.label(a[:name].to_s).downcase }
244
- # `w[:value]` ALREADY WEARS ITS OWN QUOTES OR COLON — it is a
244
+ # `w[:value]` already wears its own quotes or colon — it is a
245
245
  # `Literal.render`ed string (see lib/hecks/literal.rb), not a raw
246
246
  # Ruby value, so wrapping it in `.inspect` here would quote an
247
247
  # already-quoted string a second time.
@@ -284,18 +284,11 @@ module Hecks
284
284
 
285
285
  # ── small sentence carpentry ──────────────────────────────────────
286
286
 
287
- def to_sentence_list(items, conj: "and")
288
- case items.size
289
- when 0 then ""
290
- when 1 then items[0].to_s
291
- when 2 then "#{items[0]} #{conj} #{items[1]}"
292
- else "#{items[0..-2].join(', ')}, #{conj} #{items[-1]}"
293
- end
294
- end
287
+ # Both now live in `Naming` (a second projection, the glossary,
288
+ # needed them); kept here as names so this file reads as it did.
289
+ def to_sentence_list(items, conj: "and") = Naming.to_sentence_list(items, conj: conj)
295
290
 
296
- def a_or_an(word)
297
- %w[a e i o u].include?(word.to_s[0].to_s.downcase) ? "an" : "a"
298
- end
291
+ def a_or_an(word) = Naming.a_or_an(word)
299
292
  end
300
293
  end
301
294
  end
@@ -1,6 +1,6 @@
1
1
  module Hecks
2
2
  module Projector
3
- # WHAT MAKES A MODULE A PROJECTION TARGET. `Projector.register` has
3
+ # What makes a module a projection target. `Projector.register` has
4
4
  # always accepted anything answering `call(bluebook:, options:)` —
5
5
  # this only removes the second step, so a target declares its own key
6
6
  # beside its own implementation instead of being registered from
@@ -15,20 +15,20 @@ module Hecks
15
15
  # def call(bluebook:, options: {}) = { ... }
16
16
  # end
17
17
  #
18
- # NAMED `Target`, NOT `Projection`, on purpose. "Projection" already
18
+ # Named `Target`, not `Projection`, on purpose. "Projection" already
19
19
  # means two other things in this codebase: `Ports::Projection` is
20
20
  # read-model catch-up (events folded into view state), and `bin/project`
21
21
  # forces that catch-up by hand. Neither has anything to do with
22
22
  # "canonical IR in, external artifact out". A third meaning under the
23
23
  # same word would make the two impossible to grep apart — and from a
24
- # domain's point of view `project(X)` really does read as "project TO
24
+ # domain's point of view `project(X)` really does read as "project to
25
25
  # a target", so the narrower word is also the more accurate one.
26
26
  module Target
27
27
  # Registering at declaration time means `require`ing a target is
28
28
  # the whole of installing it — there is no separate manifest that
29
29
  # can silently disagree about which targets exist.
30
30
  #
31
- # `requires:` NAMES A CAPABILITY, not a shape word.
31
+ # `requires:` names a capability, not a shape word.
32
32
  #
33
33
  # This began as `from: :chapter` / `from: :any` — two hand-kept
34
34
  # symbols, admitted by duck-typing on `.aggregates`, which is a
@@ -44,26 +44,26 @@ module Hecks
44
44
  # It also composes: a projection needing two capabilities names
45
45
  # both, instead of a third symbol being invented for the pair.
46
46
  #
47
- # THE FAIL-QUIET THIS CLOSES, unchanged in substance: every
48
- # construct emits its own IR, so handing a projector an AGGREGATE
47
+ # The fail-quiet this closes, unchanged in substance: every
48
+ # construct emits its own IR, so handing a projector an aggregate
49
49
  # instead of a chapter is the natural thing to try. `bluebook:` was
50
- # only ever a PARAMETER NAME, never a contract. `:oidc` failed
50
+ # only ever a parameter name, never a contract. `:oidc` failed
51
51
  # loudly (no `aggregates` method), but `:shape` returned
52
52
  # `{"name" => "Order", "aggregates" => []}` — well-formed,
53
53
  # confident, and wrong.
54
- # `declares:` NAMES AN AGGREGATE THE CHAPTER MUST HAVE.
54
+ # `declares:` names an aggregate the chapter must have.
55
55
  #
56
- # A capability says what a construct can DO; this says what it must
57
- # CARRY. `:vocabulary` needs a chapter declaring a Vocabulary
56
+ # A capability says what a construct can do; this says what it must
57
+ # carry. `:vocabulary` needs a chapter declaring a Vocabulary
58
58
  # aggregate, `:parser_table` one declaring Syntax — and both used
59
59
  # to state that as a `raise` in their own body, which is a
60
60
  # requirement written as behaviour instead of declared. Stated
61
61
  # here, the registry refuses before the projection runs and the
62
62
  # projection stops carrying a guard about its own admission.
63
- # `emits:` SAYS WHAT KIND OF ARTIFACT COMES BACK.
63
+ # `emits:` says what kind of artifact comes back.
64
64
  #
65
65
  # `:artifact` (the default) is one thing — a Hash, or a String.
66
- # `:files` is a TREE: a Hash of relative path => contents, which is
66
+ # `:files` is a tree: a Hash of relative path => contents, which is
67
67
  # what a reference-page or codegen projection produces.
68
68
  #
69
69
  # Declared rather than sniffed, deliberately. Inferring a tree from
@@ -81,7 +81,7 @@ module Hecks
81
81
 
82
82
  def projection_key = @projection_key
83
83
 
84
- # Empty means "a chapter" — resolved HERE rather than as a default
84
+ # Empty means "a chapter" — resolved here rather than as a default
85
85
  # argument, because Behaviour::Chapter is not loaded yet when this
86
86
  # file is.
87
87
  def projection_declares = @projection_declares || []