hecks 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hecks/adapters/driven/claude_code.rb +65 -0
  3. data/lib/hecks/adapters/driven/folder.rb +73 -0
  4. data/lib/hecks/adapters/driven/google_authentication.rb +25 -4
  5. data/lib/hecks/adapters/driven/governance_authorization.rb +57 -6
  6. data/lib/hecks/adapters/driven/heki/journal.rb +8 -0
  7. data/lib/hecks/adapters/driven/heki/saga_store.rb +53 -7
  8. data/lib/hecks/adapters/driven/heki/snapshot.rb +1 -1
  9. data/lib/hecks/adapters/driven/heki.rb +93 -2
  10. data/lib/hecks/adapters/driven/identity_registry.rb +10 -0
  11. data/lib/hecks/adapters/driven/in_memory_ordering.rb +22 -0
  12. data/lib/hecks/adapters/driven/in_process_key_vault.adapter +3 -0
  13. data/lib/hecks/adapters/driven/in_process_key_vault.rb +53 -0
  14. data/lib/hecks/adapters/driven/lambda/client.rb +35 -7
  15. data/lib/hecks/adapters/driven/lambda.rb +43 -2
  16. data/lib/hecks/adapters/driven/local_storage.rb +67 -1
  17. data/lib/hecks/adapters/driven/memory.rb +13 -13
  18. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +20 -0
  19. data/lib/hecks/adapters/driven/prism.rb +46 -7
  20. data/lib/hecks/adapters/driven/secure_random_identity.rb +3 -0
  21. data/lib/hecks/adapters/driven/system_clock.rb +3 -0
  22. data/lib/hecks/adapters/driven/tenant_provisioner.adapter +3 -0
  23. data/lib/hecks/adapters/driven/tenant_provisioner.rb +66 -0
  24. data/lib/hecks/adapters/driven.rb +2 -0
  25. data/lib/hecks/adapters/driving/github_webhook.rb +14 -1
  26. data/lib/hecks/behaviors/dsl.rb +58 -0
  27. data/lib/hecks/behaviors/expectations.rb +168 -7
  28. data/lib/hecks/behaviors/ir.rb +11 -0
  29. data/lib/hecks/behaviors/rspec.rb +8 -0
  30. data/lib/hecks/behaviors/runner.rb +19 -0
  31. data/lib/hecks/behaviors.rb +8 -0
  32. data/lib/hecks/bluebook/aggregate.rb +30 -0
  33. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +7 -0
  34. data/lib/hecks/bluebook/assembly/build.rb +25 -4
  35. data/lib/hecks/bluebook/assembly/contract.rb +66 -14
  36. data/lib/hecks/bluebook/assembly/contracts.rb +24 -19
  37. data/lib/hecks/bluebook/assembly/marks.rb +143 -14
  38. data/lib/hecks/bluebook/assembly/specializer.rb +19 -0
  39. data/lib/hecks/bluebook/assembly.rb +25 -7
  40. data/lib/hecks/bluebook/attribute.rb +17 -3
  41. data/lib/hecks/bluebook/behaviour/aggregate.rb +32 -0
  42. data/lib/hecks/bluebook/behaviour/attribute.rb +13 -0
  43. data/lib/hecks/bluebook/behaviour/chapter.rb +55 -2
  44. data/lib/hecks/bluebook/behaviour/command.rb +33 -3
  45. data/lib/hecks/bluebook/behaviour/domain_port.rb +20 -0
  46. data/lib/hecks/bluebook/behaviour/entity.rb +12 -0
  47. data/lib/hecks/bluebook/behaviour/hexagon.rb +26 -0
  48. data/lib/hecks/bluebook/behaviour/lifecycle.rb +23 -2
  49. data/lib/hecks/bluebook/behaviour/policy.rb +34 -9
  50. data/lib/hecks/bluebook/behaviour/process_manager.rb +32 -1
  51. data/lib/hecks/bluebook/behaviour/query.rb +5 -0
  52. data/lib/hecks/bluebook/behaviour/read_model.rb +21 -0
  53. data/lib/hecks/bluebook/behaviour/traits.rb +36 -0
  54. data/lib/hecks/bluebook/behaviour/value_object.rb +17 -5
  55. data/lib/hecks/bluebook/chapter.rb +23 -0
  56. data/lib/hecks/bluebook/command.rb +53 -8
  57. data/lib/hecks/bluebook/domain_port.rb +25 -0
  58. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +196 -37
  59. data/lib/hecks/bluebook/dsl/attribute_collector.rb +79 -42
  60. data/lib/hecks/bluebook/dsl/binding_proxy.rb +59 -0
  61. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +385 -53
  62. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +135 -21
  63. data/lib/hecks/bluebook/dsl/command_builder.rb +189 -27
  64. data/lib/hecks/bluebook/dsl/entity_builder.rb +139 -9
  65. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +41 -0
  66. data/lib/hecks/bluebook/dsl/identity_declaration.rb +23 -6
  67. data/lib/hecks/bluebook/dsl/policy_builder.rb +60 -19
  68. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +79 -15
  69. data/lib/hecks/bluebook/dsl/query_builder.rb +33 -4
  70. data/lib/hecks/bluebook/dsl/read_model_builder.rb +102 -27
  71. data/lib/hecks/bluebook/dsl/rule_reference.rb +60 -8
  72. data/lib/hecks/bluebook/dsl/translation_builder.rb +146 -40
  73. data/lib/hecks/bluebook/dsl/value_object_builder.rb +55 -7
  74. data/lib/hecks/bluebook/entity.rb +29 -0
  75. data/lib/hecks/bluebook/expression/ast_json.rb +115 -23
  76. data/lib/hecks/bluebook/expression/ast_reader.rb +29 -0
  77. data/lib/hecks/bluebook/expression/canonical_form.rb +48 -9
  78. data/lib/hecks/bluebook/expression/evaluator.rb +207 -29
  79. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +36 -0
  80. data/lib/hecks/bluebook/expression/resolver.rb +320 -77
  81. data/lib/hecks/bluebook/hexagon.rb +34 -0
  82. data/lib/hecks/bluebook/lifecycle.rb +11 -0
  83. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -0
  84. data/lib/hecks/bluebook/meta_validator/judge.rb +30 -26
  85. data/lib/hecks/bluebook/meta_validator/plan.rb +43 -8
  86. data/lib/hecks/bluebook/meta_validator/port_judge.rb +1 -0
  87. data/lib/hecks/bluebook/meta_validator/readings.rb +157 -7
  88. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +24 -4
  89. data/lib/hecks/bluebook/meta_validator/shapes.rb +141 -0
  90. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +149 -23
  91. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +5 -4
  92. data/lib/hecks/bluebook/meta_validator/world_judge.rb +1 -0
  93. data/lib/hecks/bluebook/meta_validator.rb +180 -84
  94. data/lib/hecks/bluebook/model_check.rb +268 -24
  95. data/lib/hecks/bluebook/pattern_subset.rb +23 -1
  96. data/lib/hecks/bluebook/process_manager.rb +13 -0
  97. data/lib/hecks/bluebook/project_discovery.rb +5 -0
  98. data/lib/hecks/bluebook/project_loader.rb +40 -0
  99. data/lib/hecks/bluebook/project_register.rb +44 -0
  100. data/lib/hecks/bluebook/query.rb +27 -0
  101. data/lib/hecks/bluebook/read_model.rb +21 -1
  102. data/lib/hecks/bluebook/reference.rb +21 -8
  103. data/lib/hecks/bluebook/smoke_test.rb +29 -6
  104. data/lib/hecks/bluebook/synthesizer.rb +34 -0
  105. data/lib/hecks/bluebook/translation.rb +30 -1
  106. data/lib/hecks/bluebook/value_object.rb +23 -5
  107. data/lib/hecks/bluebook.rb +3 -4
  108. data/lib/hecks/codemod.rb +107 -20
  109. data/lib/hecks/construct.rb +15 -1
  110. data/lib/hecks/corpus.rb +146 -25
  111. data/lib/hecks/deploy/bluebook/deploy.hecksagon +19 -0
  112. data/lib/hecks/doc/reference.rb +185 -16
  113. data/lib/hecks/embryonaut_bluebook.rb +32 -9
  114. data/lib/hecks/facade/handle.rb +76 -3
  115. data/lib/hecks/facade/surface/aggregate_door.rb +8 -0
  116. data/lib/hecks/forms/field_shape.rb +3 -0
  117. data/lib/hecks/forms/page.rb +14 -0
  118. data/lib/hecks/forms/port_argument.rb +12 -0
  119. data/lib/hecks/forms/query_form_renderer.rb +63 -0
  120. data/lib/hecks/forms/record_renderer.rb +58 -0
  121. data/lib/hecks/forms/record_table.rb +27 -0
  122. data/lib/hecks/forms/reference_options.rb +24 -0
  123. data/lib/hecks/forms/value_object_shape.rb +10 -0
  124. data/lib/hecks/fqn.rb +58 -0
  125. data/lib/hecks/framework/bluebook/privacy.bluebook +155 -0
  126. data/lib/hecks/framework/oidc.json +15 -0
  127. data/lib/hecks/framework.rb +43 -20
  128. data/lib/hecks/freezer.rb +17 -1
  129. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +159 -24
  130. data/lib/hecks/fuzzing/combination_miner.rb +59 -0
  131. data/lib/hecks/fuzzing/concurrent_dispatch.rb +109 -8
  132. data/lib/hecks/fuzzing/coverage_campaign.rb +56 -13
  133. data/lib/hecks/fuzzing/differential.rb +34 -0
  134. data/lib/hecks/fuzzing/domain_generator.rb +188 -11
  135. data/lib/hecks/fuzzing/era_boundary.rb +45 -15
  136. data/lib/hecks/fuzzing/form_census.rb +86 -0
  137. data/lib/hecks/fuzzing/generated_domain_check.rb +76 -0
  138. data/lib/hecks/fuzzing/invalid_value_generator.rb +39 -0
  139. data/lib/hecks/fuzzing/isolated_boot.rb +79 -22
  140. data/lib/hecks/fuzzing/nondeterministic.rb +13 -1
  141. data/lib/hecks/fuzzing/persistence_parity.rb +95 -3
  142. data/lib/hecks/fuzzing/properties/corrections.rb +25 -0
  143. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +158 -14
  144. data/lib/hecks/fuzzing/properties/guards.rb +44 -0
  145. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +48 -0
  146. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +18 -0
  147. data/lib/hecks/fuzzing/properties/outbox.rb +49 -11
  148. data/lib/hecks/fuzzing/properties/querying.rb +68 -14
  149. data/lib/hecks/fuzzing/properties.rb +24 -15
  150. data/lib/hecks/fuzzing/qa_settings.rb +12 -0
  151. data/lib/hecks/fuzzing/replay.rb +137 -29
  152. data/lib/hecks/fuzzing/rotation_priority.rb +41 -21
  153. data/lib/hecks/fuzzing/rust_gap_manifest.rb +46 -20
  154. data/lib/hecks/fuzzing/self_consistency.rb +189 -40
  155. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +12 -6
  156. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +1 -1
  157. data/lib/hecks/fuzzing/sequence_generator.rb +47 -0
  158. data/lib/hecks/fuzzing/shrinker.rb +114 -2
  159. data/lib/hecks/fuzzing/structural_skips.rb +18 -4
  160. data/lib/hecks/fuzzing/sweep_depth.rb +8 -0
  161. data/lib/hecks/fuzzing/target_capabilities.rb +61 -14
  162. data/lib/hecks/fuzzing/value_generator.rb +98 -10
  163. data/lib/hecks/grammar/evolve.rb +178 -2
  164. data/lib/hecks/grammar.rb +46 -0
  165. data/lib/hecks/ir.rb +38 -7
  166. data/lib/hecks/language/hecksagon/hecksagon.bluebook +11 -0
  167. data/lib/hecks/literal.rb +32 -0
  168. data/lib/hecks/naming.rb +88 -7
  169. data/lib/hecks/ports/access_control.rb +5 -10
  170. data/lib/hecks/ports/authorization.rb +3 -6
  171. data/lib/hecks/ports/identity_assignment.rb +1 -2
  172. data/lib/hecks/ports/identity_resolution.rb +1 -2
  173. data/lib/hecks/ports/key_vault.port +6 -0
  174. data/lib/hecks/ports/key_vault.rb +58 -0
  175. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +6 -0
  176. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +38 -2
  177. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +6 -0
  178. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +40 -0
  179. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +92 -1
  180. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +15 -0
  181. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +8 -0
  182. data/lib/hecks/ports/query/in_memory.rb +39 -6
  183. data/lib/hecks/ports/query/ordering.rb +15 -0
  184. data/lib/hecks/ports.rb +1 -0
  185. data/lib/hecks/projections/bootstrap_table.rb +43 -8
  186. data/lib/hecks/projections/diagrams.rb +243 -7
  187. data/lib/hecks/projections/glossary/html.rb +88 -0
  188. data/lib/hecks/projections/glossary/markdown.rb +47 -0
  189. data/lib/hecks/projections/glossary/mermaid.rb +48 -0
  190. data/lib/hecks/projections/glossary/sentences.rb +105 -0
  191. data/lib/hecks/projections/glossary.rb +161 -19
  192. data/lib/hecks/projections/model/deviations.rb +44 -0
  193. data/lib/hecks/projections/model.rb +51 -1
  194. data/lib/hecks/projections/oidc.rb +40 -3
  195. data/lib/hecks/projections/parser_table.rb +49 -8
  196. data/lib/hecks/projections/reference.rb +12 -0
  197. data/lib/hecks/projections/rust_vocabulary.rb +219 -16
  198. data/lib/hecks/projections/shape.rb +8 -0
  199. data/lib/hecks/projections/statements.rb +63 -16
  200. data/lib/hecks/projections/vocabulary.rb +17 -0
  201. data/lib/hecks/projector/cli_projector.rb +218 -10
  202. data/lib/hecks/projector/docs_projector.rb +145 -19
  203. data/lib/hecks/projector/exporter.rb +65 -11
  204. data/lib/hecks/projector/ir_projector.rb +6 -0
  205. data/lib/hecks/projector/narrate_projector.rb +136 -15
  206. data/lib/hecks/projector/target.rb +29 -5
  207. data/lib/hecks/projector.rb +74 -6
  208. data/lib/hecks/query_ir.rb +47 -0
  209. data/lib/hecks/query_specification/common/null_policy.rb +5 -3
  210. data/lib/hecks/rendering.rb +6 -0
  211. data/lib/hecks/router/namespace_installer.rb +13 -0
  212. data/lib/hecks/router.rb +55 -0
  213. data/lib/hecks/runtime/aggregate_lock.rb +9 -0
  214. data/lib/hecks/runtime/boot_gates.rb +18 -0
  215. data/lib/hecks/runtime/caller.rb +32 -0
  216. data/lib/hecks/runtime/capability_graph.rb +11 -0
  217. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +23 -21
  218. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +14 -15
  219. data/lib/hecks/runtime/command_interpreter.rb +42 -17
  220. data/lib/hecks/runtime/command_rules/admissibility.rb +165 -14
  221. data/lib/hecks/runtime/command_rules/arithmetic.rb +17 -5
  222. data/lib/hecks/runtime/command_rules/references.rb +118 -28
  223. data/lib/hecks/runtime/dependency_planning.rb +45 -0
  224. data/lib/hecks/runtime/dispatcher.rb +28 -50
  225. data/lib/hecks/runtime/entity_element.rb +161 -8
  226. data/lib/hecks/runtime/entity_interpreter.rb +44 -9
  227. data/lib/hecks/runtime/errors.rb +18 -4
  228. data/lib/hecks/runtime/event.rb +10 -5
  229. data/lib/hecks/runtime/identity.rb +71 -3
  230. data/lib/hecks/runtime/instance.rb +67 -7
  231. data/lib/hecks/runtime/interpreting.rb +13 -5
  232. data/lib/hecks/runtime/invocation.rb +118 -36
  233. data/lib/hecks/runtime/loader.rb +94 -8
  234. data/lib/hecks/runtime/outbox.rb +145 -7
  235. data/lib/hecks/runtime/policy_interpreter.rb +22 -9
  236. data/lib/hecks/runtime/port_operation_interpreter.rb +20 -0
  237. data/lib/hecks/runtime/query_interpreter.rb +40 -12
  238. data/lib/hecks/runtime/reaction_invocation.rb +53 -8
  239. data/lib/hecks/runtime/read_model_interpreter.rb +23 -7
  240. data/lib/hecks/runtime/rebuild_sweep.rb +28 -0
  241. data/lib/hecks/runtime/reference_hop.rb +42 -0
  242. data/lib/hecks/runtime/refusal_wording.rb +50 -0
  243. data/lib/hecks/runtime/registry/saga_persistence.rb +11 -0
  244. data/lib/hecks/runtime/registry/verification.rb +119 -4
  245. data/lib/hecks/runtime/registry.rb +157 -4
  246. data/lib/hecks/runtime/remote_dispatcher.rb +92 -6
  247. data/lib/hecks/runtime/routing.rb +27 -2
  248. data/lib/hecks/runtime/saga_interpreter/correlation.rb +10 -12
  249. data/lib/hecks/runtime/saga_interpreter.rb +27 -13
  250. data/lib/hecks/runtime/tenant_check.rb +26 -6
  251. data/lib/hecks/runtime/tenant_scope.rb +18 -0
  252. data/lib/hecks/runtime/value/coercion.rb +255 -33
  253. data/lib/hecks/runtime/value/entity_list_coercion.rb +102 -30
  254. data/lib/hecks/runtime/value.rb +50 -0
  255. data/lib/hecks/runtime.rb +32 -0
  256. data/lib/hecks/storehouse.rb +305 -9
  257. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +130 -0
  258. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +32 -0
  259. data/lib/hecks/version.rb +1 -1
  260. data/lib/hecks.rb +79 -1
  261. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -0
  262. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +12 -2
  263. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +29 -5
  264. metadata +11 -5
  265. data/lib/hecks/codemod/legacy_dispatch_args.rb +0 -299
  266. data/lib/hecks/codemod/legacy_dispatch_recorder.rb +0 -186
  267. data/lib/hecks/deprecation.rb +0 -95
@@ -4,14 +4,16 @@ module Hecks
4
4
  module Projector
5
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
8
- # user of it needs: what each aggregate is (`description`), what each verb
9
- # is for (`goal`) and who issues it (`role`), which states it moves
7
+ # ## What this is for
8
+ #
9
+ # A chapter in this corpus already contains everything a user of it
10
+ # needs: what each aggregate is (`description`), what each verb is
11
+ # for (`goal`) and who issues it (`role`), which states it moves
10
12
  # between, what it refuses and in whose words (`given`, `invariant`,
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.
13
- # They read the source, or they read a document somebody wrote beside the
14
- # source and stopped updating.
13
+ # `ensures`), and what each list is worth reading (`description` on
14
+ # a query). None of that reaches the person who has to call the
15
+ # domain. They read the source, or they read a document somebody
16
+ # wrote beside the source and stopped updating.
15
17
  #
16
18
  # `docs/implemented/reference/` is the precedent and the contrast. `bin/reference`
17
19
  # generates it from the language's own Syntax chapter, so the reference
@@ -19,12 +21,14 @@ module Hecks
19
21
  # down: the usage document for a domain, generated from that domain, so it
20
22
  # cannot drift from the domain either.
21
23
  #
22
- # Why it is a projection and not a generator script. `Projector` is the
23
- # repository's registry of "canonical IR in, external artifact out" (§30
24
- # of the implementation plan), and this is exactly that shape: one
25
- # bluebook's IR in, markdown out, no runtime needed and no store touched.
26
- # Registered as `:docs` beside `:ir`, and reachable the way every
27
- # projector is — `Projector.call(:docs, bluebook: ...)`.
24
+ # ## Why a projection, not a generator script
25
+ #
26
+ # `Projector` is the repository's registry of "canonical IR in,
27
+ # external artifact out" (§30 of the implementation plan), and this
28
+ # is exactly that shape: one bluebook's IR in, markdown out, no
29
+ # runtime needed and no store touched. Registered as `:docs` beside
30
+ # `:ir`, and reachable the way every projector is —
31
+ # `Projector.call(:docs, bluebook: ...)`.
28
32
  #
29
33
  # And as a method, which is the half that makes it get used.
30
34
  # `Facade::Surface` already installs a module per chapter carrying
@@ -33,17 +37,30 @@ module Hecks
33
37
  # `QualityControl::Bug.docs`. A document you have to remember a script for
34
38
  # is a document nobody reads.
35
39
  #
36
- # **What it deliberately does not do**: invent. Every sentence below comes out
37
- # of the chapter. Where a chapter says nothing — an aggregate with no
38
- # `description`, a command with no `goal` — the document says nothing
39
- # rather than filling the gap with a restatement of the name, because a
40
- # generated paragraph that only rephrases an identifier teaches a reader
41
- # to skim the ones that do not.
40
+ # ## What it deliberately does not do
41
+ #
42
+ # Invent. Every sentence below comes out of the chapter. Where a
43
+ # chapter says nothing — an aggregate with no `description`, a
44
+ # command with no `goal` — the document says nothing rather than
45
+ # filling the gap with a restatement of the name, because a
46
+ # generated paragraph that only rephrases an identifier teaches a
47
+ # reader to skim the ones that do not.
42
48
  module DocsProjector
43
49
  module_function
44
50
 
51
+ # Projects `bluebook` as its own usage documentation.
52
+ #
45
53
  # `options[:heading]` sets the top heading level (default 1), so a
46
54
  # caller splicing this into a larger document can push it down.
55
+ #
56
+ # @param bluebook [Bluebook::Behaviour::Chapter] the chapter to document
57
+ # @param options [Hash] optional inputs
58
+ # @option options [Integer, String] :heading the top heading level; defaults to 1
59
+ # @option options [String, Symbol, nil] :aggregate narrows the document to one
60
+ # aggregate, omitting the chapter header and closing sections
61
+ # @return [String] the document, as Markdown, ending in a newline
62
+ # @raise [Runtime::NotFound] if `options[:aggregate]` names no aggregate `bluebook`
63
+ # declares
47
64
  def call(bluebook:, options: {})
48
65
  depth = (options[:heading] || 1).to_i
49
66
  only = options[:aggregate]
@@ -60,6 +77,13 @@ module Hecks
60
77
  # matched no head returned "" and exit 0, which is the silent-wrong-
61
78
  # answer shape this repository has already been bitten by twice in the
62
79
  # query engine. A misspelling should cost a sentence, not a puzzle.
80
+ #
81
+ # @param bluebook [Bluebook::Behaviour::Chapter] the chapter to look in
82
+ # @param only [String, Symbol, nil] narrows the result to the one aggregate
83
+ # named; nil answers every aggregate
84
+ # @return [Array<Bluebook::Aggregate>, Bluebook::Aggregate] every declared
85
+ # aggregate, or the single aggregate `only` names
86
+ # @raise [Runtime::NotFound] if `only` names no aggregate `bluebook` declares
63
87
  def aggregates(bluebook, only)
64
88
  return bluebook.aggregates unless only
65
89
 
@@ -69,10 +93,21 @@ module Hecks
69
93
  "it declares #{bluebook.aggregates.map(&:hecks_name).sort.join(', ')}")
70
94
  end
71
95
 
96
+ # Renders a Markdown heading line.
97
+ #
98
+ # @param depth [Integer] the heading level
99
+ # @param text [String] the heading text
100
+ # @return [String] a Markdown `#`-prefixed heading line
72
101
  def h(depth, text) = "#{'#' * depth} #{text}"
73
102
 
74
103
  # ── the chapter ───────────────────────────────────────────────────
75
104
 
105
+ # Renders the chapter-level header: its title, vision, classification,
106
+ # former name, and aggregate list.
107
+ #
108
+ # @param bluebook [Bluebook::Behaviour::Chapter] the chapter to document
109
+ # @param depth [Integer] the heading level for the chapter's own title
110
+ # @return [String] the header's Markdown source
76
111
  def chapter_header(bluebook, depth)
77
112
  out = [h(depth, bluebook.name), ""]
78
113
  # The vision first and as a quote. It is the one sentence in a chapter
@@ -86,6 +121,10 @@ module Hecks
86
121
  out.join("\n")
87
122
  end
88
123
 
124
+ # Builds a construct's own GitHub-style heading anchor.
125
+ #
126
+ # @param name [String, Symbol] the construct name to anchor
127
+ # @return [String] the construct's own GitHub-style heading anchor
89
128
  def anchor(name) = Naming.snake(name).tr("_", "-")
90
129
 
91
130
  # What happens without anybody asking — the part of a domain a caller
@@ -93,6 +132,11 @@ module Hecks
93
132
  # them. A policy means one dispatch causes another, sometimes into a
94
133
  # different domain entirely; a saga means a sequence is being driven on
95
134
  # their behalf and can end in more than one place.
135
+ #
136
+ # @param bluebook [Bluebook::Behaviour::Chapter] the chapter to document
137
+ # @param depth [Integer] the heading level for the "Reactions"/saga sections
138
+ # @return [String, nil] the closing's Markdown source, or nil if `bluebook`
139
+ # declares no policy and no process manager
96
140
  def closing(bluebook, depth)
97
141
  out = []
98
142
 
@@ -123,6 +167,12 @@ module Hecks
123
167
 
124
168
  # ── one aggregate ─────────────────────────────────────────────────
125
169
 
170
+ # Documents one aggregate: its description, identity, references,
171
+ # attributes, lifecycle, verbs, queries, and nested entities.
172
+ #
173
+ # @param aggregate [Bluebook::Aggregate] the aggregate to document
174
+ # @param depth [Integer] the heading level for the aggregate's own title
175
+ # @return [String] the aggregate's Markdown section
126
176
  def aggregate_section(aggregate, depth)
127
177
  out = [h(depth, aggregate.hecks_name), ""]
128
178
  out += [aggregate.description, ""] if aggregate.description
@@ -142,6 +192,12 @@ module Hecks
142
192
  out.compact.join("\n")
143
193
  end
144
194
 
195
+ # Documents one entity nested under `aggregate`.
196
+ #
197
+ # @param aggregate [Bluebook::Aggregate] the entity's own owning aggregate
198
+ # @param entity [Bluebook::Entity] the entity to document
199
+ # @param depth [Integer] the heading level for the entity's own title
200
+ # @return [String] the entity's Markdown section
145
201
  def entity_section(aggregate, entity, depth)
146
202
  out = [h(depth, "#{entity.hecks_name} (within #{aggregate.hecks_name})"), ""]
147
203
  out += [entity.description, ""] if entity.description
@@ -161,6 +217,12 @@ module Hecks
161
217
 
162
218
  # ── the shape ─────────────────────────────────────────────────────
163
219
 
220
+ # Renders a holder's own non-reference attributes as a Markdown table.
221
+ #
222
+ # @param holder [Bluebook::Aggregate, Bluebook::Entity] the holder whose
223
+ # attributes to render
224
+ # @return [String, nil] the attribute/shape/rules table, or nil if `holder`
225
+ # declares no non-reference attribute
164
226
  def attributes_table(holder)
165
227
  attributes = holder.attributes.reject(&:reference?)
166
228
  return nil if attributes.empty?
@@ -175,6 +237,11 @@ module Hecks
175
237
  # a caller nothing; `{ value: String }` tells them what to send, which is
176
238
  # the single most common thing to get wrong at this boundary — a bare
177
239
  # scalar where an object is wanted.
240
+ #
241
+ # @param attribute [Bluebook::Attribute] the attribute to describe
242
+ # @param holder [Bluebook::Aggregate, Bluebook::Entity] `attribute`'s own holder
243
+ # @return [String] the attribute's shape: its value object's own fields, or its
244
+ # scalar type, with "list of" and "*(optional)*" applied as declared
178
245
  def shape_of(attribute, holder)
179
246
  value_object = value_object_for(attribute, holder)
180
247
  inner =
@@ -187,6 +254,12 @@ module Hecks
187
254
  attribute.optional? ? "#{shape} *(optional)*" : shape
188
255
  end
189
256
 
257
+ # Lists an attribute's own rules: closed-set members, field patterns and
258
+ # defaults, invariants, and its own default.
259
+ #
260
+ # @param attribute [Bluebook::Attribute] the attribute to describe
261
+ # @param holder [Bluebook::Aggregate, Bluebook::Entity] `attribute`'s own holder
262
+ # @return [String] the attribute's rules, joined with "; "; `""` if it has none
190
263
  def rules_of(attribute, holder)
191
264
  value_object = value_object_for(attribute, holder)
192
265
  rules = []
@@ -200,6 +273,11 @@ module Hecks
200
273
  rules.empty? ? "" : rules.join("; ")
201
274
  end
202
275
 
276
+ # Names a closed set's own members.
277
+ #
278
+ # @param value_object [Bluebook::ValueObject, nil] the value object to check
279
+ # @return [Array<Object>] every unique value across `value_object`'s own member
280
+ # rows, or `[]` if `value_object` is nil or not a closed set
203
281
  def closed_members(value_object)
204
282
  return [] unless value_object&.closed_set?
205
283
 
@@ -208,6 +286,12 @@ module Hecks
208
286
 
209
287
  # An entity holds no value objects of its own — its argument types are
210
288
  # declared on the aggregate above it.
289
+ #
290
+ # @param attribute [Bluebook::Attribute] the attribute whose type to resolve
291
+ # @param holder [Bluebook::Aggregate, Bluebook::Entity] `attribute`'s own holder
292
+ # @return [Bluebook::ValueObject, nil] the value object class `attribute`'s type
293
+ # names, found on `holder` or its own owning aggregate; nil if `attribute`'s
294
+ # type is not a value object
211
295
  def value_object_for(attribute, holder)
212
296
  scopes = [holder, holder.respond_to?(:hecks_owner) ? holder.hecks_owner : nil].compact
213
297
  scopes.each do |scope|
@@ -221,6 +305,13 @@ module Hecks
221
305
 
222
306
  # ── the machine ───────────────────────────────────────────────────
223
307
 
308
+ # Renders a holder's own lifecycle as a "starting state" sentence and a
309
+ # verb/from/to table.
310
+ #
311
+ # @param holder [Bluebook::Aggregate, Bluebook::Entity] the holder to document
312
+ # @param depth [Integer] the heading level for the "Lifecycle" section
313
+ # @return [String, nil] the section's Markdown source, or nil if `holder`
314
+ # declares no lifecycle
224
315
  def lifecycle_section(holder, depth)
225
316
  lifecycle = holder.lifecycle or return nil
226
317
 
@@ -234,6 +325,13 @@ module Hecks
234
325
 
235
326
  # ── the verbs ─────────────────────────────────────────────────────
236
327
 
328
+ # Documents every command a holder declares.
329
+ #
330
+ # @param holder [Bluebook::Aggregate, Bluebook::Entity] the holder whose
331
+ # commands to document
332
+ # @param depth [Integer] the heading level for the "Verbs" section
333
+ # @return [String, nil] the section's Markdown source, or nil if `holder`
334
+ # declares no command
237
335
  def verbs_section(holder, depth)
238
336
  commands = holder.commands
239
337
  return nil if commands.empty?
@@ -243,6 +341,13 @@ module Hecks
243
341
  out.join("\n")
244
342
  end
245
343
 
344
+ # Documents one command: its goal, role, arguments, refusals,
345
+ # guarantees, and emitted events.
346
+ #
347
+ # @param command [Bluebook::Command] the command to document
348
+ # @param holder [Bluebook::Aggregate, Bluebook::Entity] `command`'s own holder
349
+ # @param depth [Integer] the heading level for the command's own title
350
+ # @return [String] the command's Markdown section
246
351
  def command_entry(command, holder, depth)
247
352
  out = [h(depth, "#{command.hecks_name}#{' *(creates)*' if command.creates?}"), ""]
248
353
  out += [command.goal, ""] if command.goal
@@ -270,6 +375,11 @@ module Hecks
270
375
  # nothing to share with `command_entry`'s other sections, extracted
271
376
  # only to keep that method to the one shape every section there
272
377
  # follows: build a chunk, append it if non-empty.
378
+ #
379
+ # @param arguments [Array<Bluebook::Attribute>] the command's own arguments
380
+ # @param holder [Bluebook::Aggregate, Bluebook::Entity] the arguments' own holder
381
+ # @return [Array<Array(String, String, String)>] one `[argument, shape, needed]`
382
+ # row per argument
273
383
  def command_argument_rows(arguments, holder)
274
384
  arguments.map do |attribute|
275
385
  shape = attribute.reference? ? "id of a `#{attribute.type.target_name}`" : shape_of(attribute, holder)
@@ -282,6 +392,10 @@ module Hecks
282
392
  # `given`s, and the fact that a reference has to resolve. A caller
283
393
  # reading only the argument list learns none of these, and they are
284
394
  # most of what a domain is.
395
+ #
396
+ # @param command [Bluebook::Command] the command to gather refusals for
397
+ # @param holder [Bluebook::Aggregate, Bluebook::Entity] `command`'s own holder
398
+ # @return [Array<String>] every way `command` can refuse, stated as sentences
285
399
  def refusals_of(command, holder)
286
400
  refusals = []
287
401
 
@@ -304,6 +418,12 @@ module Hecks
304
418
 
305
419
  # ── the reads ─────────────────────────────────────────────────────
306
420
 
421
+ # Renders a list of queries as one Markdown paragraph per query.
422
+ #
423
+ # @param queries [Array<Bluebook::Query>] the queries to document
424
+ # @param depth [Integer] the heading level for `title`
425
+ # @param title [String] the section's own heading text
426
+ # @return [String, nil] the section's Markdown source, or nil if `queries` is empty
307
427
  def queries_section(queries, depth, title)
308
428
  return nil if queries.empty?
309
429
 
@@ -320,6 +440,12 @@ module Hecks
320
440
  out.join("\n")
321
441
  end
322
442
 
443
+ # Renders a Markdown pipe table.
444
+ #
445
+ # @param headers [Array<String>] the column headers
446
+ # @param rows [Array<Array<String>>] each row's own cell values, matching
447
+ # `headers`' width
448
+ # @return [String] the rendered table, ending in a blank line
323
449
  def table(headers, rows)
324
450
  lines = ["| #{headers.join(' | ')} |", "|#{headers.map { '---' }.join('|')}|"]
325
451
  rows.each { |row| lines << "| #{row.join(' | ')} |" }
@@ -13,10 +13,18 @@ module Hecks
13
13
  module Exporter
14
14
  module_function
15
15
 
16
+ # Exports every booted bluebook's own canonical IR.
17
+ #
18
+ # @param registry [Runtime::Registry] the booted registry to export
19
+ # @return [Hash{String => Hash}] each domain name, mapped to its bluebook's `to_h`
16
20
  def call(registry)
17
21
  registry.bluebooks.transform_values(&:to_h)
18
22
  end
19
23
 
24
+ # Exports every booted bluebook's own canonical IR as JSON.
25
+ #
26
+ # @param registry [Runtime::Registry] the booted registry to export
27
+ # @return [String] `call`'s output, as pretty-printed JSON
20
28
  def json(registry)
21
29
  JSON.pretty_generate(call(registry))
22
30
  end
@@ -42,6 +50,12 @@ module Hecks
42
50
  # already answers for a domain with nothing lineage-capable bound —
43
51
  # `capable_aggregates: []` — rather than raising on an undefined
44
52
  # constant.
53
+ # @param registry [Runtime::Registry] the booted registry `domain_name` is loaded in
54
+ # @param domain_name [String] the domain to check era-adapter lineage capability for
55
+ # @return [Hash{Symbol => Array<Hash{Symbol => String}>}] `:capable_aggregates`,
56
+ # each a `:name`/`:storage_name` Hash; empty when the era plugin is unloaded or
57
+ # nothing this domain binds is lineage-capable
58
+ # @raise [KeyError] if `domain_name` is not a loaded domain
45
59
  def lineage(registry, domain_name)
46
60
  return { capable_aggregates: [] } unless Ports::Persistence.plugin?(:era)
47
61
 
@@ -68,6 +82,13 @@ module Hecks
68
82
  # today), rather than silently building up a second, disjoint
69
83
  # history nothing but Rust ever reads while the real state stays
70
84
  # wherever its own adapter actually wrote it.
85
+ # @param registry [Runtime::Registry] the booted registry `domain_name` is loaded in
86
+ # @param domain_name [String] the domain to export persistence bindings for
87
+ # @return [Hash{Symbol => Array<Hash{Symbol => Object}>}] `:aggregates`, each a
88
+ # `:name`/`:storage_name`/`:adapter` Hash
89
+ # @raise [KeyError] if `domain_name` is not a loaded domain
90
+ # @raise [Runtime::WiringError] if an aggregate has no authoritative bind, more
91
+ # than one, or a bind with a role this port does not support
71
92
  def persistence(registry, domain_name)
72
93
  bluebook = registry.bluebooks.fetch(domain_name)
73
94
  aggregates = bluebook.aggregates.map do |aggregate|
@@ -85,6 +106,12 @@ module Hecks
85
106
  # "authorization"`), with that chapter's declared verbs qualified.
86
107
  # `rust/host` (auth.rs) reads this instead of naming Governance.
87
108
  # `{}` when nothing this domain attaches provides authorization.
109
+ # @param registry [Runtime::Registry] the booted registry `domain_name` is loaded in
110
+ # @param domain_name [String] the domain to export the authorization binding for
111
+ # @return [Hash{Symbol => String, nil}] `:provider` (name), `:grant`, `:assignments`
112
+ # (both provided-verb names), and `:assignment_aggregate` (`:assignments`' own
113
+ # leading aggregate name); `{}` if nothing this domain attaches provides
114
+ # authorization
88
115
  def authorization(registry, domain_name)
89
116
  provider = registry.authorization_provider_for(domain_name)
90
117
  return {} unless provider
@@ -107,10 +134,19 @@ module Hecks
107
134
  # separate). `values:` tables serialize as `[key, value]` pairs,
108
135
  # never an object, because JSON object keys are always strings and
109
136
  # a convert's keys are typed.
137
+ # @param registry [Runtime::Registry] the booted registry to export translations from
138
+ # @return [Array<Hash>] every registered translation, as `compiled_translation_hash`
139
+ # builds
110
140
  def translations(registry)
111
141
  registry.translations.map { |translation| compiled_translation_hash(translation) }
112
142
  end
113
143
 
144
+ # Exports one translation, its aggregates' compiled SQL included.
145
+ #
146
+ # @param translation [Bluebook::Translation] the translation to export
147
+ # @return [Hash{Symbol => Object}] `:domain` (String), `:from`/`:to` (the era
148
+ # identifiers as declared), `:retired` (`Array<String>`), and `:aggregates`
149
+ # (each `compiled_translation_aggregate`'s own Hash)
114
150
  def compiled_translation_hash(translation)
115
151
  {
116
152
  domain: translation.domain,
@@ -121,6 +157,10 @@ module Hecks
121
157
  }
122
158
  end
123
159
 
160
+ # Exports every registered translation as JSON.
161
+ #
162
+ # @param registry [Runtime::Registry] the booted registry to export translations from
163
+ # @return [String] `translations`' output, as pretty-printed JSON
124
164
  def translations_json(registry)
125
165
  JSON.pretty_generate(translations(registry))
126
166
  end
@@ -136,6 +176,10 @@ module Hecks
136
176
  # reviewed never changed at all. The approval binds to what was
137
177
  # declared, not to what a particular compiler build happened to
138
178
  # emit from it.
179
+ # @param translation [Bluebook::Translation] the translation to digest
180
+ # @return [Hash{Symbol => Object}] `:domain` (String), `:from`/`:to` (the era
181
+ # identifiers as declared), `:retired` (`Array<String>`), and `:aggregates`
182
+ # (each `translation_aggregate`'s own Hash)
139
183
  def translation_hash(translation)
140
184
  {
141
185
  domain: translation.domain,
@@ -146,6 +190,14 @@ module Hecks
146
190
  }
147
191
  end
148
192
 
193
+ # Digests one aggregate's own declared translation rules.
194
+ #
195
+ # @param aggregate [Bluebook::TranslationAggregate] the aggregate's own
196
+ # translation rules to digest
197
+ # @return [Hash{Symbol => Object}] `:name` (String), `:was` (String, nil),
198
+ # `:renames` (`Hash{String => String}`), `:moves`/`:converts`/`:retypes`/
199
+ # `:computes`/`:rekeys`/`:backfills` (each an `Array<Hash>`), `:drops`
200
+ # (`Array<String>`)
149
201
  def translation_aggregate(aggregate)
150
202
  {
151
203
  name: aggregate.name,
@@ -158,17 +210,14 @@ module Hecks
158
210
  drops: aggregate.drops.map(&:to_s),
159
211
  retypes: aggregate.retypes.map { |retype| { from: retype.from, to: retype.to } },
160
212
  computes: aggregate.computes.map { |compute| { from: compute.from, to: compute.to, sql: compute.sql } },
161
- # **Previously missing** — found live while planning Rust-side mint
162
- # support. An edge carrying only a rekey (no compute) had its
163
- # approval bind to nothing rekey-specific at all: any two
164
- # rekey edges with otherwise-identical renames/moves/converts/
165
- # drops/retypes/computes produced the same digest regardless
166
- # of what their `rekey sql:` actually said, and a rekey's own
167
- # SQL could change without invalidating an existing approval.
168
- # Same bug shape for `backfills` (present, just never
169
- # exported). Fixing this changes every existing rekey/
170
- # backfill edge's digest — any approval already recorded for
171
- # one is invalidated by this fix and must be re-reviewed.
213
+ # **`rekeys`/`backfills` are digest-relevant too.** Without them, an
214
+ # edge carrying only a rekey (no compute) would bind its approval
215
+ # to nothing rekey-specific: any two rekey edges with otherwise-
216
+ # identical renames/moves/converts/drops/retypes/computes would
217
+ # produce the same digest regardless of what their `rekey sql:`
218
+ # actually said, letting a rekey's own SQL change without
219
+ # invalidating an existing approval. Same reasoning covers
220
+ # `backfills`.
172
221
  rekeys: aggregate.rekeys.map { |rekey| { sql: rekey.sql } },
173
222
  backfills: aggregate.backfills.map { |backfill| { name: backfill.name.to_s, default: backfill.default } }
174
223
  }
@@ -197,6 +246,11 @@ module Hecks
197
246
  # declared-rules shape, just without precompiled SQL to execute —
198
247
  # consistent with there being no mint/audit machinery to run it
199
248
  # against either.
249
+ # @param aggregate [Bluebook::TranslationAggregate] the aggregate's own
250
+ # translation rules to compile and export
251
+ # @return [Hash{Symbol => Object}] `translation_aggregate`'s own Hash, plus
252
+ # `:compiled_state_expression` (String) and `:compiled_id_expression`
253
+ # (String, nil) when the era persistence plugin is loaded
200
254
  def compiled_translation_aggregate(aggregate)
201
255
  return translation_aggregate(aggregate) unless Ports::Persistence.plugin?(:era)
202
256
 
@@ -12,6 +12,12 @@ module Hecks
12
12
  module IRProjector
13
13
  module_function
14
14
 
15
+ # Projects `bluebook` as its own canonical IR.
16
+ #
17
+ # @param bluebook [Hecks::IR] the IR-emitting construct to project
18
+ # @param options [Hash] unused; accepted to satisfy the registry's call shape
19
+ # @return [Hash] `bluebook`'s canonical IR, as built by `Hecks::IR::Emits#to_h`
20
+ # @raise [Hecks::IR::Undeclared] if `bluebook` never declared its shape with `emits_ir`
15
21
  def call(bluebook:, options: {}) = bluebook.to_h
16
22
  end
17
23
  end