hecks 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (373) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hecks/adapters/driven/claude_code.rb +72 -7
  3. data/lib/hecks/adapters/driven/d1.rb +187 -23
  4. data/lib/hecks/adapters/driven/folder.rb +83 -10
  5. data/lib/hecks/adapters/driven/google_authentication.rb +33 -12
  6. data/lib/hecks/adapters/driven/governance_authorization.rb +87 -18
  7. data/lib/hecks/adapters/driven/heki/journal.rb +13 -4
  8. data/lib/hecks/adapters/driven/heki/saga_store.rb +56 -10
  9. data/lib/hecks/adapters/driven/heki/snapshot.rb +1 -1
  10. data/lib/hecks/adapters/driven/heki.rb +106 -9
  11. data/lib/hecks/adapters/driven/identity_registry.rb +12 -2
  12. data/lib/hecks/adapters/driven/in_memory_ordering.rb +25 -3
  13. data/lib/hecks/adapters/driven/in_process_key_vault.adapter +3 -0
  14. data/lib/hecks/adapters/driven/in_process_key_vault.rb +53 -0
  15. data/lib/hecks/adapters/driven/lambda/client.rb +67 -14
  16. data/lib/hecks/adapters/driven/lambda.rb +82 -35
  17. data/lib/hecks/adapters/driven/local_storage.rb +83 -10
  18. data/lib/hecks/adapters/driven/memory.rb +205 -9
  19. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +21 -1
  20. data/lib/hecks/adapters/driven/postgres/codec.rb +27 -11
  21. data/lib/hecks/adapters/driven/postgres/outbox.rb +40 -2
  22. data/lib/hecks/adapters/driven/postgres/reconnect.rb +23 -7
  23. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +14 -14
  24. data/lib/hecks/adapters/driven/postgres.rb +175 -28
  25. data/lib/hecks/adapters/driven/prism.rb +50 -11
  26. data/lib/hecks/adapters/driven/secure_random_identity.rb +3 -0
  27. data/lib/hecks/adapters/driven/sql_query_builder.rb +34 -22
  28. data/lib/hecks/adapters/driven/sqlite/codec.rb +38 -10
  29. data/lib/hecks/adapters/driven/sqlite/projection.rb +60 -32
  30. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +12 -12
  31. data/lib/hecks/adapters/driven/sqlite.rb +181 -21
  32. data/lib/hecks/adapters/driven/system_clock.rb +3 -0
  33. data/lib/hecks/adapters/driven/tenant_provisioner.adapter +3 -0
  34. data/lib/hecks/adapters/driven/tenant_provisioner.rb +66 -0
  35. data/lib/hecks/adapters/driven.rb +6 -4
  36. data/lib/hecks/adapters/driving/github_webhook.rb +31 -18
  37. data/lib/hecks/behaviors/dsl.rb +60 -2
  38. data/lib/hecks/behaviors/expectations.rb +190 -29
  39. data/lib/hecks/behaviors/ir.rb +12 -1
  40. data/lib/hecks/behaviors/rspec.rb +9 -1
  41. data/lib/hecks/behaviors/runner.rb +21 -2
  42. data/lib/hecks/behaviors.rb +9 -1
  43. data/lib/hecks/bluebook/aggregate.rb +43 -13
  44. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +17 -10
  45. data/lib/hecks/bluebook/assembly/build.rb +26 -5
  46. data/lib/hecks/bluebook/assembly/contract.rb +98 -23
  47. data/lib/hecks/bluebook/assembly/contracts.rb +59 -52
  48. data/lib/hecks/bluebook/assembly/marks.rb +159 -30
  49. data/lib/hecks/bluebook/assembly/specializer.rb +38 -21
  50. data/lib/hecks/bluebook/assembly.rb +32 -14
  51. data/lib/hecks/bluebook/attribute.rb +26 -12
  52. data/lib/hecks/bluebook/behaviour/aggregate.rb +43 -11
  53. data/lib/hecks/bluebook/behaviour/attribute.rb +18 -5
  54. data/lib/hecks/bluebook/behaviour/chapter.rb +76 -5
  55. data/lib/hecks/bluebook/behaviour/command.rb +55 -25
  56. data/lib/hecks/bluebook/behaviour/domain_port.rb +27 -7
  57. data/lib/hecks/bluebook/behaviour/entity.rb +20 -8
  58. data/lib/hecks/bluebook/behaviour/hexagon.rb +30 -4
  59. data/lib/hecks/bluebook/behaviour/lifecycle.rb +27 -6
  60. data/lib/hecks/bluebook/behaviour/policy.rb +42 -17
  61. data/lib/hecks/bluebook/behaviour/process_manager.rb +39 -8
  62. data/lib/hecks/bluebook/behaviour/query.rb +6 -1
  63. data/lib/hecks/bluebook/behaviour/read_model.rb +29 -8
  64. data/lib/hecks/bluebook/behaviour/traits.rb +48 -12
  65. data/lib/hecks/bluebook/behaviour/value_object.rb +21 -9
  66. data/lib/hecks/bluebook/capabilities.rb +27 -0
  67. data/lib/hecks/bluebook/chapter.rb +51 -9
  68. data/lib/hecks/bluebook/command.rb +62 -17
  69. data/lib/hecks/bluebook/domain_port.rb +34 -9
  70. data/lib/hecks/bluebook/dsl/adapter_builder.rb +24 -0
  71. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +49 -49
  72. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +282 -123
  73. data/lib/hecks/bluebook/dsl/attribute_collector.rb +112 -75
  74. data/lib/hecks/bluebook/dsl/binding_proxy.rb +81 -2
  75. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +486 -117
  76. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +179 -47
  77. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
  78. data/lib/hecks/bluebook/dsl/command_builder.rb +284 -122
  79. data/lib/hecks/bluebook/dsl/const_shim.rb +46 -15
  80. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +90 -25
  81. data/lib/hecks/bluebook/dsl/entity_builder.rb +191 -61
  82. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
  83. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +130 -30
  84. data/lib/hecks/bluebook/dsl/identity_declaration.rb +38 -21
  85. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
  86. data/lib/hecks/bluebook/dsl/policy_builder.rb +86 -36
  87. data/lib/hecks/bluebook/dsl/port_builder.rb +38 -7
  88. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +56 -22
  89. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +111 -47
  90. data/lib/hecks/bluebook/dsl/query_builder.rb +37 -8
  91. data/lib/hecks/bluebook/dsl/read_model_builder.rb +127 -52
  92. data/lib/hecks/bluebook/dsl/rule_reference.rb +97 -43
  93. data/lib/hecks/bluebook/dsl/translation_builder.rb +150 -44
  94. data/lib/hecks/bluebook/dsl/value_object_builder.rb +68 -20
  95. data/lib/hecks/bluebook/dsl/word_gate.rb +59 -53
  96. data/lib/hecks/bluebook/dsl/world_builder.rb +51 -8
  97. data/lib/hecks/bluebook/entity.rb +40 -11
  98. data/lib/hecks/bluebook/expression/ast_json.rb +128 -36
  99. data/lib/hecks/bluebook/expression/ast_reader.rb +32 -3
  100. data/lib/hecks/bluebook/expression/canonical_form.rb +55 -16
  101. data/lib/hecks/bluebook/expression/evaluator.rb +221 -43
  102. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +54 -18
  103. data/lib/hecks/bluebook/expression/resolver.rb +369 -128
  104. data/lib/hecks/bluebook/hexagon.rb +35 -1
  105. data/lib/hecks/bluebook/lifecycle.rb +12 -1
  106. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +2 -1
  107. data/lib/hecks/bluebook/meta_validator/judge.rb +126 -108
  108. data/lib/hecks/bluebook/meta_validator/plan.rb +81 -46
  109. data/lib/hecks/bluebook/meta_validator/port_judge.rb +3 -2
  110. data/lib/hecks/bluebook/meta_validator/readings.rb +200 -50
  111. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +68 -41
  112. data/lib/hecks/bluebook/meta_validator/shapes.rb +166 -21
  113. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +286 -46
  114. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +11 -10
  115. data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -5
  116. data/lib/hecks/bluebook/meta_validator.rb +235 -139
  117. data/lib/hecks/bluebook/model_check.rb +434 -104
  118. data/lib/hecks/bluebook/pattern_subset.rb +32 -10
  119. data/lib/hecks/bluebook/policy.rb +15 -13
  120. data/lib/hecks/bluebook/process_manager.rb +27 -14
  121. data/lib/hecks/bluebook/project_discovery.rb +5 -0
  122. data/lib/hecks/bluebook/project_loader.rb +40 -0
  123. data/lib/hecks/bluebook/project_register.rb +50 -6
  124. data/lib/hecks/bluebook/query.rb +31 -4
  125. data/lib/hecks/bluebook/read_model.rb +35 -15
  126. data/lib/hecks/bluebook/reference.rb +26 -13
  127. data/lib/hecks/bluebook/smoke_test.rb +46 -23
  128. data/lib/hecks/bluebook/synthesizer.rb +46 -12
  129. data/lib/hecks/bluebook/translation.rb +34 -5
  130. data/lib/hecks/bluebook/value_object.rb +29 -11
  131. data/lib/hecks/bluebook.rb +5 -6
  132. data/lib/hecks/codemod.rb +138 -50
  133. data/lib/hecks/construct.rb +21 -7
  134. data/lib/hecks/corpus.rb +438 -0
  135. data/lib/hecks/deploy/bluebook/deploy.hecksagon +19 -0
  136. data/lib/hecks/doc/reference.rb +200 -31
  137. data/lib/hecks/embryonaut_bluebook.rb +38 -15
  138. data/lib/hecks/facade/cli_door.rb +69 -10
  139. data/lib/hecks/facade/cli_runner.rb +105 -24
  140. data/lib/hecks/facade/command_request.rb +23 -0
  141. data/lib/hecks/facade/handle.rb +155 -35
  142. data/lib/hecks/facade/json_door.rb +106 -25
  143. data/lib/hecks/facade/surface/aggregate_door.rb +50 -27
  144. data/lib/hecks/facade/surface/chapter.rb +26 -17
  145. data/lib/hecks/facade/surface.rb +16 -3
  146. data/lib/hecks/facade.rb +15 -4
  147. data/lib/hecks/forms/app.rb +46 -30
  148. data/lib/hecks/forms/command_form_renderer.rb +70 -9
  149. data/lib/hecks/forms/field_renderer.rb +142 -6
  150. data/lib/hecks/forms/field_shape.rb +183 -19
  151. data/lib/hecks/forms/html.rb +51 -7
  152. data/lib/hecks/forms/index_renderer.rb +14 -2
  153. data/lib/hecks/forms/page.rb +14 -0
  154. data/lib/hecks/forms/params.rb +120 -23
  155. data/lib/hecks/forms/port_argument.rb +14 -2
  156. data/lib/hecks/forms/query_form_renderer.rb +65 -2
  157. data/lib/hecks/forms/record_renderer.rb +60 -2
  158. data/lib/hecks/forms/record_table.rb +28 -1
  159. data/lib/hecks/forms/reference_options.rb +24 -0
  160. data/lib/hecks/forms/value_object_shape.rb +13 -3
  161. data/lib/hecks/forms.rb +24 -4
  162. data/lib/hecks/fqn.rb +59 -1
  163. data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
  164. data/lib/hecks/framework/bluebook/privacy.bluebook +155 -0
  165. data/lib/hecks/framework/oidc.json +15 -0
  166. data/lib/hecks/framework.rb +79 -25
  167. data/lib/hecks/freezer.rb +27 -11
  168. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +241 -106
  169. data/lib/hecks/fuzzing/combination_miner.rb +178 -0
  170. data/lib/hecks/fuzzing/concurrent_dispatch.rb +241 -45
  171. data/lib/hecks/fuzzing/coverage_campaign.rb +161 -0
  172. data/lib/hecks/fuzzing/differential.rb +192 -0
  173. data/lib/hecks/fuzzing/domain_generator.rb +871 -0
  174. data/lib/hecks/fuzzing/era_boundary.rb +68 -19
  175. data/lib/hecks/fuzzing/form_census.rb +121 -20
  176. data/lib/hecks/fuzzing/generated_domain_check.rb +171 -0
  177. data/lib/hecks/fuzzing/invalid_value_generator.rb +45 -6
  178. data/lib/hecks/fuzzing/isolated_boot.rb +137 -80
  179. data/lib/hecks/fuzzing/nondeterministic.rb +79 -0
  180. data/lib/hecks/fuzzing/persistence_parity.rb +111 -21
  181. data/lib/hecks/fuzzing/properties/corrections.rb +34 -9
  182. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +300 -51
  183. data/lib/hecks/fuzzing/properties/guards.rb +86 -42
  184. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +80 -32
  185. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +40 -35
  186. data/lib/hecks/fuzzing/properties/outbox.rb +70 -32
  187. data/lib/hecks/fuzzing/properties/querying.rb +82 -28
  188. data/lib/hecks/fuzzing/properties.rb +84 -51
  189. data/lib/hecks/fuzzing/qa_settings.rb +164 -0
  190. data/lib/hecks/fuzzing/replay.rb +241 -133
  191. data/lib/hecks/fuzzing/rotation_priority.rb +48 -28
  192. data/lib/hecks/fuzzing/rust_gap_manifest.rb +139 -0
  193. data/lib/hecks/fuzzing/self_consistency.rb +315 -137
  194. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +46 -40
  195. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +18 -11
  196. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +13 -12
  197. data/lib/hecks/fuzzing/sequence_generator/picker.rb +21 -12
  198. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +59 -32
  199. data/lib/hecks/fuzzing/sequence_generator.rb +158 -23
  200. data/lib/hecks/fuzzing/shrinker.rb +309 -0
  201. data/lib/hecks/fuzzing/structural_skips.rb +37 -130
  202. data/lib/hecks/fuzzing/sweep_depth.rb +12 -4
  203. data/lib/hecks/fuzzing/target_capabilities.rb +107 -29
  204. data/lib/hecks/fuzzing/value_generator.rb +110 -22
  205. data/lib/hecks/fuzzing.rb +5 -0
  206. data/lib/hecks/grammar/evolve.rb +188 -12
  207. data/lib/hecks/grammar.rb +53 -7
  208. data/lib/hecks/ir.rb +51 -20
  209. data/lib/hecks/language/bluebook/bluebook.bluebook +41 -0
  210. data/lib/hecks/language/bluebook/policy.bluebook +11 -1
  211. data/lib/hecks/language/bluebook/vocabulary.bluebook +348 -13
  212. data/lib/hecks/language/hecksagon/hecksagon.bluebook +11 -0
  213. data/lib/hecks/language/oidc.json +5 -0
  214. data/lib/hecks/literal.rb +41 -9
  215. data/lib/hecks/naming.rb +112 -31
  216. data/lib/hecks/ports/access_control.rb +53 -2
  217. data/lib/hecks/ports/agent/answers.rb +83 -6
  218. data/lib/hecks/ports/agent.rb +119 -35
  219. data/lib/hecks/ports/authentication.rb +44 -4
  220. data/lib/hecks/ports/authorization.rb +50 -11
  221. data/lib/hecks/ports/clock.rb +42 -23
  222. data/lib/hecks/ports/extraction.rb +16 -0
  223. data/lib/hecks/ports/identity_assignment.rb +23 -2
  224. data/lib/hecks/ports/identity_generation.rb +17 -3
  225. data/lib/hecks/ports/identity_resolution.rb +17 -1
  226. data/lib/hecks/ports/key_vault.port +6 -0
  227. data/lib/hecks/ports/key_vault.rb +58 -0
  228. data/lib/hecks/ports/loading.rb +4 -0
  229. data/lib/hecks/ports/persistence/append_only.rb +172 -8
  230. data/lib/hecks/ports/persistence/binding_policy.rb +34 -0
  231. data/lib/hecks/ports/persistence/codec_boundary.rb +178 -0
  232. data/lib/hecks/ports/persistence/execution.rb +4 -0
  233. data/lib/hecks/ports/persistence/null_saga_store.rb +12 -1
  234. data/lib/hecks/ports/persistence/plugin.rb +42 -4
  235. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +218 -25
  236. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +77 -9
  237. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +81 -24
  238. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +29 -18
  239. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +144 -60
  240. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +103 -8
  241. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +98 -23
  242. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +282 -109
  243. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +63 -25
  244. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +118 -66
  245. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +51 -28
  246. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +34 -5
  247. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +25 -12
  248. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +129 -34
  249. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +51 -6
  250. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +33 -9
  251. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +16 -0
  252. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +95 -6
  253. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +25 -2
  254. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +314 -90
  255. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +68 -10
  256. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +9 -3
  257. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +9 -2
  258. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +48 -8
  259. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +16 -1
  260. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +36 -5
  261. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +23 -3
  262. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +58 -19
  263. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +96 -5
  264. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +15 -0
  265. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +9 -1
  266. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -2
  267. data/lib/hecks/ports/persistence/plugins/era.rb +12 -2
  268. data/lib/hecks/ports/persistence/remote_runtime.rb +9 -2
  269. data/lib/hecks/ports/persistence/repository_factory.rb +23 -3
  270. data/lib/hecks/ports/persistence/state_codec.rb +319 -0
  271. data/lib/hecks/ports/persistence.rb +36 -1
  272. data/lib/hecks/ports/projection.rb +61 -7
  273. data/lib/hecks/ports/query/in_memory.rb +41 -8
  274. data/lib/hecks/ports/query/ordering.rb +21 -6
  275. data/lib/hecks/ports/query.rb +35 -0
  276. data/lib/hecks/ports.rb +1 -0
  277. data/lib/hecks/projections/bootstrap_table.rb +147 -0
  278. data/lib/hecks/projections/diagrams.rb +315 -79
  279. data/lib/hecks/projections/glossary/html.rb +91 -3
  280. data/lib/hecks/projections/glossary/markdown.rb +50 -3
  281. data/lib/hecks/projections/glossary/mermaid.rb +49 -1
  282. data/lib/hecks/projections/glossary/sections.rb +1 -1
  283. data/lib/hecks/projections/glossary/sentences.rb +110 -5
  284. data/lib/hecks/projections/glossary.rb +173 -31
  285. data/lib/hecks/projections/ir.rb +1 -1
  286. data/lib/hecks/projections/model/deviations.rb +62 -17
  287. data/lib/hecks/projections/model.rb +75 -21
  288. data/lib/hecks/projections/oidc.rb +45 -8
  289. data/lib/hecks/projections/parser_table.rb +53 -12
  290. data/lib/hecks/projections/reference.rb +15 -3
  291. data/lib/hecks/projections/rust_vocabulary.rb +646 -0
  292. data/lib/hecks/projections/shape.rb +10 -2
  293. data/lib/hecks/projections/statements.rb +69 -22
  294. data/lib/hecks/projections/vocabulary.rb +26 -9
  295. data/lib/hecks/projections.rb +5 -3
  296. data/lib/hecks/projector/cli_projector.rb +245 -37
  297. data/lib/hecks/projector/docs_projector.rb +154 -28
  298. data/lib/hecks/projector/exporter.rb +104 -29
  299. data/lib/hecks/projector/ir_projector.rb +7 -1
  300. data/lib/hecks/projector/narrate_projector.rb +144 -23
  301. data/lib/hecks/projector/target.rb +42 -18
  302. data/lib/hecks/projector.rb +86 -18
  303. data/lib/hecks/query_ir.rb +94 -47
  304. data/lib/hecks/query_specification/common/comparators.rb +19 -3
  305. data/lib/hecks/query_specification/common/comparison.rb +109 -27
  306. data/lib/hecks/query_specification/common/dsl.rb +65 -9
  307. data/lib/hecks/query_specification/common/null_policy.rb +61 -15
  308. data/lib/hecks/query_specification/common/null_semantics.rb +4 -0
  309. data/lib/hecks/query_specification/common/options.rb +25 -0
  310. data/lib/hecks/query_specification/field_path.rb +69 -15
  311. data/lib/hecks/query_specification/hop_path.rb +57 -20
  312. data/lib/hecks/query_specification/read_model/specification.rb +4 -0
  313. data/lib/hecks/rendering.rb +9 -3
  314. data/lib/hecks/router/namespace_installer.rb +16 -3
  315. data/lib/hecks/router.rb +56 -1
  316. data/lib/hecks/runtime/aggregate_lock.rb +20 -11
  317. data/lib/hecks/runtime/boot_gates.rb +21 -3
  318. data/lib/hecks/runtime/caller.rb +40 -8
  319. data/lib/hecks/runtime/capability_graph.rb +13 -2
  320. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +40 -41
  321. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +45 -67
  322. data/lib/hecks/runtime/command_interpreter.rb +195 -113
  323. data/lib/hecks/runtime/command_rules/admissibility.rb +231 -80
  324. data/lib/hecks/runtime/command_rules/arithmetic.rb +154 -96
  325. data/lib/hecks/runtime/command_rules/authorization.rb +38 -17
  326. data/lib/hecks/runtime/command_rules/emission.rb +18 -1
  327. data/lib/hecks/runtime/command_rules/references.rb +145 -55
  328. data/lib/hecks/runtime/command_rules.rb +3 -0
  329. data/lib/hecks/runtime/dependency_planning.rb +56 -11
  330. data/lib/hecks/runtime/dispatcher.rb +263 -92
  331. data/lib/hecks/runtime/entity_element.rb +304 -67
  332. data/lib/hecks/runtime/entity_interpreter.rb +149 -88
  333. data/lib/hecks/runtime/errors.rb +37 -23
  334. data/lib/hecks/runtime/event.rb +15 -10
  335. data/lib/hecks/runtime/identity.rb +91 -23
  336. data/lib/hecks/runtime/instance.rb +103 -18
  337. data/lib/hecks/runtime/interpreting.rb +24 -16
  338. data/lib/hecks/runtime/invocation.rb +358 -0
  339. data/lib/hecks/runtime/loader.rb +105 -19
  340. data/lib/hecks/runtime/outbox.rb +164 -26
  341. data/lib/hecks/runtime/policy_interpreter.rb +73 -60
  342. data/lib/hecks/runtime/port_operation_interpreter.rb +42 -19
  343. data/lib/hecks/runtime/query_interpreter.rb +93 -74
  344. data/lib/hecks/runtime/reaction_invocation.rb +73 -28
  345. data/lib/hecks/runtime/read_model_interpreter.rb +60 -44
  346. data/lib/hecks/runtime/rebuild_sweep.rb +32 -4
  347. data/lib/hecks/runtime/reference_hop.rb +48 -6
  348. data/lib/hecks/runtime/refusal_wording.rb +142 -115
  349. data/lib/hecks/runtime/registry/saga_persistence.rb +32 -21
  350. data/lib/hecks/runtime/registry/verification.rb +153 -28
  351. data/lib/hecks/runtime/registry.rb +202 -32
  352. data/lib/hecks/runtime/remote_dispatcher.rb +125 -24
  353. data/lib/hecks/runtime/routing.rb +36 -154
  354. data/lib/hecks/runtime/saga_interpreter/correlation.rb +25 -27
  355. data/lib/hecks/runtime/saga_interpreter.rb +90 -76
  356. data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
  357. data/lib/hecks/runtime/tenant_check.rb +33 -13
  358. data/lib/hecks/runtime/tenant_scope.rb +23 -5
  359. data/lib/hecks/runtime/value/admission.rb +75 -30
  360. data/lib/hecks/runtime/value/coercion.rb +555 -142
  361. data/lib/hecks/runtime/value/entity_list_coercion.rb +132 -60
  362. data/lib/hecks/runtime/value.rb +71 -21
  363. data/lib/hecks/runtime.rb +39 -7
  364. data/lib/hecks/storehouse.rb +368 -72
  365. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +130 -0
  366. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +32 -0
  367. data/lib/hecks/version.rb +3 -3
  368. data/lib/hecks/vocabulary.rb +205 -4
  369. data/lib/hecks.rb +91 -11
  370. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +19 -11
  371. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +24 -12
  372. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +38 -14
  373. metadata +28 -2
@@ -3,20 +3,22 @@ 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
9
- # what does it want" for the person implementing against a domain —
10
- # tables of arguments, shapes, refusal reasons. That is the wrong
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
13
- # never read a markdown table in their life. This projects the same IR
14
- # as sentences instead — "Debit — take money out. Issued by a Teller. It
15
- # only goes through if the balance covers it." — so a domain can be
16
- # read back to the person who can validate it without them learning the
17
- # DSL first.
8
+ # ## What this is for
18
9
  #
19
- # SAME SOURCE, SAME GUARANTEE `DocsProjector` gives: nothing here is
10
+ # `DocsProjector` already answers "what can I call and what does it
11
+ # want" for the person implementing against a domain — tables of
12
+ # arguments, shapes, refusal reasons. That is the wrong register for
13
+ # the person who can actually say whether the domain is right: the
14
+ # subject-matter expert who knows what an account is and has never
15
+ # read a markdown table in their life. This projects the same IR as
16
+ # sentences instead — "Debit — take money out. Issued by a Teller.
17
+ # It only goes through if the balance covers it." — so a domain can
18
+ # be read back to the person who can validate it without them
19
+ # learning the DSL first.
20
+ #
21
+ # Same source, same guarantee `DocsProjector` gives: nothing here is
20
22
  # invented. Every sentence quotes a `description`, `goal`, or `given`
21
23
  # already declared in the chapter; where a chapter says nothing, this
22
24
  # says nothing rather than manufacturing a sentence out of an
@@ -24,17 +26,30 @@ module Hecks
24
26
  # (`Projector.call(:narrate, bluebook: ...)`), same aggregate-scoping
25
27
  # via `options[:aggregate]`.
26
28
  #
27
- # WHAT IT DOES NOT DO: replace `DocsProjector`. A shape table still says
28
- # "id of a Customer" more precisely than any sentence would, and an
29
- # implementer still wants that. This is the other document the same IR
30
- # is owed — one written for the reader who is being asked "is this
31
- # right?", not "how do I call it?"
29
+ # ## What it does not do
30
+ #
31
+ # Replace `DocsProjector`. A shape table still says "id of a
32
+ # Customer" more precisely than any sentence would, and an
33
+ # implementer still wants that. This is the other document the same
34
+ # IR is owed — one written for the reader who is being asked "is
35
+ # this right?", not "how do I call it?"
32
36
  module NarrateProjector
33
37
  module_function
34
38
 
39
+ # Projects `bluebook` as prose an SME can read back and confirm.
40
+ #
35
41
  # `options[:heading]` sets the top heading level, exactly as
36
42
  # `DocsProjector` does — so this, too, can be spliced into a larger
37
43
  # document rather than always starting at H1.
44
+ #
45
+ # @param bluebook [Bluebook::Behaviour::Chapter] the chapter to narrate
46
+ # @param options [Hash] optional inputs
47
+ # @option options [Integer, String] :heading the top heading level; defaults to 1
48
+ # @option options [String, Symbol, nil] :aggregate narrows the narrative to one
49
+ # aggregate, omitting the chapter intro and reactions sections
50
+ # @return [String] the narrative, as Markdown prose, ending in a newline
51
+ # @raise [Runtime::NotFound] if `options[:aggregate]` names no aggregate `bluebook`
52
+ # declares
38
53
  def call(bluebook:, options: {})
39
54
  depth = (options[:heading] || 1).to_i
40
55
  only = options[:aggregate]
@@ -50,6 +65,12 @@ module Hecks
50
65
 
51
66
  # ── the chapter ───────────────────────────────────────────────────
52
67
 
68
+ # Narrates the chapter-level intro: its vision, classification, former
69
+ # name, and the aggregates it's told through.
70
+ #
71
+ # @param bluebook [Bluebook::Behaviour::Chapter] the chapter to narrate
72
+ # @param depth [Integer] the heading level for the chapter's own title
73
+ # @return [String] the intro's Markdown prose
53
74
  def chapter_intro(bluebook, depth)
54
75
  parts = [DocsProjector.h(depth, bluebook.name)]
55
76
  parts << bluebook.vision if bluebook.vision
@@ -66,6 +87,12 @@ module Hecks
66
87
 
67
88
  # ── one aggregate ─────────────────────────────────────────────────
68
89
 
90
+ # Narrates one aggregate: its description, identity, references,
91
+ # lifecycle, commands, queries, and nested entities.
92
+ #
93
+ # @param aggregate [Bluebook::Aggregate] the aggregate to narrate
94
+ # @param depth [Integer] the heading level for the aggregate's own title
95
+ # @return [String] the aggregate's Markdown prose
69
96
  def aggregate_narrative(aggregate, depth)
70
97
  parts = [DocsProjector.h(depth, aggregate.hecks_name)]
71
98
  parts << aggregate.description if aggregate.description
@@ -86,12 +113,23 @@ module Hecks
86
113
  parts.compact.join("\n\n")
87
114
  end
88
115
 
116
+ # Names how a holder is identified.
117
+ #
118
+ # @param holder [Bluebook::Aggregate, Bluebook::Entity] the holder to describe
119
+ # @return [String, nil] a sentence naming `holder`'s identity fields, or nil if
120
+ # it declares none
89
121
  def identity_sentence(holder)
90
122
  return nil if holder.identity_heads.empty?
91
123
 
92
124
  "Every #{holder.hecks_name} is identified by its #{to_sentence_list(holder.identity_heads.map { |h| "`#{h}`" })}."
93
125
  end
94
126
 
127
+ # Narrates one entity nested under `aggregate`.
128
+ #
129
+ # @param aggregate [Bluebook::Aggregate] the entity's own owning aggregate
130
+ # @param entity [Bluebook::Entity] the entity to narrate
131
+ # @param depth [Integer] the heading level for the entity's own title
132
+ # @return [String] the entity's Markdown prose
95
133
  def entity_narrative(aggregate, entity, depth)
96
134
  parts = [DocsProjector.h(depth, "#{entity.hecks_name} (within #{aggregate.hecks_name})")]
97
135
  parts << entity.description if entity.description
@@ -106,6 +144,11 @@ module Hecks
106
144
 
107
145
  # ── the machine ───────────────────────────────────────────────────
108
146
 
147
+ # Narrates a holder's own lifecycle transitions.
148
+ #
149
+ # @param holder [Bluebook::Aggregate, Bluebook::Entity] the holder to describe
150
+ # @return [String, nil] a sentence per lifecycle transition, or nil if `holder`
151
+ # declares no lifecycle
109
152
  def lifecycle_narrative(holder)
110
153
  lifecycle = holder.lifecycle or return nil
111
154
 
@@ -120,6 +163,13 @@ module Hecks
120
163
 
121
164
  # ── the verbs ─────────────────────────────────────────────────────
122
165
 
166
+ # Narrates every command a holder declares.
167
+ #
168
+ # @param holder [Bluebook::Aggregate, Bluebook::Entity] the holder whose
169
+ # commands to narrate
170
+ # @param depth [Integer] the heading level for the "What can happen" section
171
+ # @return [String, nil] the section's Markdown prose, or nil if `holder`
172
+ # declares no command
123
173
  def verbs_narrative(holder, depth)
124
174
  return nil if holder.commands.empty?
125
175
 
@@ -128,7 +178,7 @@ module Hecks
128
178
  "#{header}\n\n#{body}"
129
179
  end
130
180
 
131
- # ONE PARAGRAPH, BUILT FROM INDEPENDENT SENTENCES — each sentence
181
+ # One paragraph, built from independent sentences — each sentence
132
182
  # below states one unrelated fact about `command` (its goal, who
133
183
  # issues it, whether it creates the holder, what it takes, what it
134
184
  # references, what gates it, what it guarantees, what it emits), in
@@ -136,6 +186,10 @@ module Hecks
136
186
  # sentence depends on anything before it. Same nil-or-string +
137
187
  # `compact.join` shape `aggregate_narrative`/`entity_narrative`
138
188
  # already use above for the identical reason.
189
+ #
190
+ # @param command [Bluebook::Command] the command to narrate
191
+ # @param holder [Bluebook::Aggregate, Bluebook::Entity] `command`'s own holder
192
+ # @return [String] the command's own paragraph
139
193
  def command_paragraph(command, holder)
140
194
  [
141
195
  command_headline_sentence(command),
@@ -149,31 +203,48 @@ module Hecks
149
203
  ].compact.join(" ")
150
204
  end
151
205
 
152
- # THE GOAL, VERBATIM — same rule `DocsProjector` holds to: quoted
206
+ # The goal, verbatim — same rule `DocsProjector` holds to: quoted
153
207
  # exactly as declared, not recased to fit mid-sentence, because the
154
208
  # promise this whole projector makes is that a sentence here is a
155
209
  # sentence the chapter actually wrote.
210
+ #
211
+ # @param command [Bluebook::Command] the command to name
212
+ # @return [String] the command's own bold name, plus its goal if it declares one
156
213
  def command_headline_sentence(command)
157
214
  "**#{command.hecks_name}**#{command.goal ? " — #{command.goal}." : '.'}"
158
215
  end
159
216
 
217
+ # Names who issues a command.
218
+ #
219
+ # @param command [Bluebook::Command] the command to describe
220
+ # @return [String, nil] a sentence naming who issues `command`, or nil if it
221
+ # declares no role
160
222
  def command_role_sentence(command)
161
223
  return nil unless command.role
162
224
 
163
225
  "Issued by #{a_or_an(command.role)} #{command.role}."
164
226
  end
165
227
 
166
- # `acts_on.nil?`, NOT `creates?` — `creates?` answers true for every
167
- # verb an ENTITY declares (it never references itself; see
228
+ # `acts_on.nil?`, not `creates?` — `creates?` answers true for every
229
+ # verb an entity declares (it never references itself; see
168
230
  # `Command#acts_on`'s own comment), so reading it directly here would
169
231
  # tell an SME that `LedgerEntry.Amend` brings a new ledger entry into
170
232
  # being, which is exactly backwards.
233
+ # @param command [Bluebook::Command] the command to check
234
+ # @param holder [Bluebook::Aggregate, Bluebook::Entity] `command`'s own holder
235
+ # @return [String, nil] a sentence saying `command` creates `holder`, or nil if
236
+ # `command` acts on an existing record instead
171
237
  def command_creation_sentence(command, holder)
172
238
  return nil unless command.acts_on.nil?
173
239
 
174
240
  "This is how a new #{holder.hecks_name} comes into being."
175
241
  end
176
242
 
243
+ # Lists a command's own non-reference arguments.
244
+ #
245
+ # @param command [Bluebook::Command] the command to describe
246
+ # @return [String, nil] a sentence listing `command`'s non-reference arguments,
247
+ # or nil if it declares none
177
248
  def command_arguments_sentence(command)
178
249
  arguments = command.attributes.reject(&:reference?)
179
250
  return nil if arguments.empty?
@@ -181,6 +252,11 @@ module Hecks
181
252
  "It takes #{to_sentence_list(arguments.map { |a| Forms::Humanize.label(a.name.to_s).downcase })}."
182
253
  end
183
254
 
255
+ # Names what a command references.
256
+ #
257
+ # @param command [Bluebook::Command] the command to describe
258
+ # @return [String, nil] a sentence naming what `command` references, or nil if
259
+ # it declares no reference argument
184
260
  def command_references_sentence(command)
185
261
  refs = command.attributes.select(&:reference?)
186
262
  return nil if refs.empty?
@@ -188,6 +264,12 @@ module Hecks
188
264
  "It's aimed at one existing #{to_sentence_list(refs.map { |r| r.type.target_name })}, by id."
189
265
  end
190
266
 
267
+ # Lists a command's own required conditions.
268
+ #
269
+ # @param command [Bluebook::Command] the command to describe
270
+ # @param holder [Bluebook::Aggregate, Bluebook::Entity] `command`'s own holder
271
+ # @return [String, nil] a sentence listing every required condition, or nil if
272
+ # `command` declares none
191
273
  def command_conditions_sentence(command, holder)
192
274
  conditions = conditions_of(command, holder)
193
275
  return nil if conditions.empty?
@@ -195,6 +277,11 @@ module Hecks
195
277
  "It only goes through if #{conditions.join('; ')}."
196
278
  end
197
279
 
280
+ # Lists what a command guarantees when it succeeds.
281
+ #
282
+ # @param command [Bluebook::Command] the command to describe
283
+ # @return [String, nil] a sentence listing `command`'s own `ensures`, or nil if
284
+ # it declares none
198
285
  def command_guarantees_sentence(command)
199
286
  guarantees = command.ensures.map(&:description)
200
287
  return nil if guarantees.empty?
@@ -202,18 +289,27 @@ module Hecks
202
289
  "When it succeeds: #{guarantees.join('; ')}."
203
290
  end
204
291
 
292
+ # Names what a command records.
293
+ #
294
+ # @param command [Bluebook::Command] the command to describe
295
+ # @return [String, nil] a sentence naming what `command` records, or nil if it
296
+ # emits nothing
205
297
  def command_emits_sentence(command)
206
298
  return nil if command.emits.empty?
207
299
 
208
300
  "It records `#{command.emits.join('`, `')}` as a fact."
209
301
  end
210
302
 
211
- # EVERY REQUIRED CONDITION, STATED AS SOMETHING THAT MUST BE TRUE —
303
+ # Every required condition, stated as something that must be true —
212
304
  # the same three sources `DocsProjector#refusals_of` reads (the
213
305
  # lifecycle edge, a reference's existence, and the command's own
214
306
  # `given`s), but kept positive rather than phrased as a refusal
215
307
  # reason. "Refused unless not X" is a sentence a reader has to
216
308
  # invert in their head; "only goes through if X" is not.
309
+ #
310
+ # @param command [Bluebook::Command] the command to gather conditions for
311
+ # @param holder [Bluebook::Aggregate, Bluebook::Entity] `command`'s own holder
312
+ # @return [Array<String>] every required condition, stated positively
217
313
  def conditions_of(command, holder)
218
314
  conditions = []
219
315
 
@@ -234,6 +330,12 @@ module Hecks
234
330
 
235
331
  # ── the reads ─────────────────────────────────────────────────────
236
332
 
333
+ # Narrates every query as one line per question.
334
+ #
335
+ # @param queries [Array<Bluebook::Query>] the queries to narrate
336
+ # @param holder_name [String] the queries' own holder's name
337
+ # @param depth [Integer] the heading level for the "Questions you can ask" section
338
+ # @return [String, nil] the section's Markdown prose, or nil if `queries` is empty
237
339
  def queries_narrative(queries, holder_name, depth)
238
340
  return nil if queries.empty?
239
341
 
@@ -241,7 +343,7 @@ module Hecks
241
343
  lines = queries.map do |query|
242
344
  shape = query.to_h
243
345
  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
346
+ # `w[:value]` already wears its own quotes or colon — it is a
245
347
  # `Literal.render`ed string (see lib/hecks/literal.rb), not a raw
246
348
  # Ruby value, so wrapping it in `.inspect` here would quote an
247
349
  # already-quoted string a second time.
@@ -257,12 +359,23 @@ module Hecks
257
359
  "#{header}\n\n#{lines.join("\n")}"
258
360
  end
259
361
 
362
+ # Translates a query comparator into plain English.
363
+ #
364
+ # @param comparator [String, Symbol] a where-clause comparator, such as `:eq`
365
+ # @return [String] the comparator in plain words, or `comparator.to_s` verbatim
366
+ # for one this file has no rendering rule for
260
367
  def op_words(comparator)
261
368
  { eq: "is", lt: "under", lte: "at most", gt: "over", gte: "at least" }[comparator.to_s.to_sym] || comparator.to_s
262
369
  end
263
370
 
264
371
  # ── what happens on its own ───────────────────────────────────────
265
372
 
373
+ # Narrates what happens on its own: every policy and every saga.
374
+ #
375
+ # @param bluebook [Bluebook::Behaviour::Chapter] the chapter to narrate
376
+ # @param depth [Integer] the heading level for the "Reactions" section
377
+ # @return [String, nil] the section's Markdown prose, or nil if `bluebook`
378
+ # declares no policy and no process manager
266
379
  def reactions_narrative(bluebook, depth)
267
380
  return nil if bluebook.policies.empty? && bluebook.process_managers.empty?
268
381
 
@@ -286,8 +399,16 @@ module Hecks
286
399
 
287
400
  # Both now live in `Naming` (a second projection, the glossary,
288
401
  # needed them); kept here as names so this file reads as it did.
402
+ #
403
+ # @param items [Array<#to_s>] items to join, in order
404
+ # @param conj [String] conjunction placed before the last item
405
+ # @return [String] the joined sentence fragment, `""` for an empty `items`
289
406
  def to_sentence_list(items, conj: "and") = Naming.to_sentence_list(items, conj: conj)
290
407
 
408
+ # Picks the English indefinite article for a word.
409
+ #
410
+ # @param word [String, Symbol] the word the article precedes
411
+ # @return [String] `"a"` or `"an"`
291
412
  def a_or_an(word) = Naming.a_or_an(word)
292
413
  end
293
414
  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,32 +44,43 @@ 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
58
- # aggregate, `:parser_table` one declaring Syntax — and both used
59
- # to state that as a `raise` in their own body, which is a
60
- # requirement written as behaviour instead of declared. Stated
61
- # here, the registry refuses before the projection runs and the
62
- # projection stops carrying a guard about its own admission.
63
- # `emits:` SAYS WHAT KIND OF ARTIFACT COMES BACK.
56
+ # A capability says what a construct can do; this says what it must
57
+ # carry. `:vocabulary` needs a chapter declaring a Vocabulary
58
+ # aggregate, `:parser_table` one declaring Syntax — declared here
59
+ # rather than as a `raise` in the projection's own body, so the
60
+ # requirement is stated instead of written as behaviour. The
61
+ # registry refuses before the projection runs, so the projection
62
+ # itself no longer carries a guard about its own admission.
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
70
70
  # an ordinary Hash is guesswork — `{"name" => "Pizzas"}` is
71
71
  # indistinguishable from a one-file tree — so `write` asks what the
72
72
  # projection said rather than inspecting what it returned.
73
+ # Registers `self` as a projection target under `key`, and stores the
74
+ # capability and aggregate requirements `admits!` later checks against.
75
+ #
76
+ # @param key [String, Symbol] the key to register `self` under (converted to a symbol)
77
+ # @param requires [Module, Array<Module>, nil] capability module(s) a construct must
78
+ # satisfy; nil or empty means `Bluebook::Behaviour::Chapter`
79
+ # @param declares [String, Symbol, Array<String, Symbol>, nil] aggregate name(s) the
80
+ # chapter must declare; nil means none
81
+ # @param emits [Symbol] the kind of artifact `self` returns: `:artifact` (the default,
82
+ # a single Hash or String) or `:files` (a path => contents tree)
83
+ # @return [Symbol] the registered key
73
84
  def projects_as(key, requires: nil, declares: nil, emits: :artifact)
74
85
  @projection_emits = emits
75
86
  @projection_key = key.to_sym
@@ -79,15 +90,28 @@ module Hecks
79
90
  @projection_key
80
91
  end
81
92
 
93
+ # Gives the key this target registered under.
94
+ #
95
+ # @return [Symbol, nil] the key given to `projects_as`, or nil before it is called
82
96
  def projection_key = @projection_key
83
97
 
84
- # Empty means "a chapter" — resolved HERE rather than as a default
98
+ # Empty means "a chapter" — resolved here rather than as a default
85
99
  # argument, because Behaviour::Chapter is not loaded yet when this
86
100
  # file is.
101
+ #
102
+ # @return [Array<String, Symbol>] aggregate names the chapter must declare; empty
103
+ # for none
87
104
  def projection_declares = @projection_declares || []
88
105
 
106
+ # Gives the kind of artifact this target returns.
107
+ #
108
+ # @return [Symbol] the kind of artifact `self` returns: `:artifact` or `:files`
89
109
  def projection_emits = @projection_emits || :artifact
90
110
 
111
+ # Names the capability module(s) a construct must satisfy, defaulting
112
+ # to plain chapter-hood when `projects_as` named none.
113
+ #
114
+ # @return [Array<Module>] required capability modules
91
115
  def projection_requires
92
116
  req = @projection_requires
93
117
  req.nil? || req.empty? ? [Bluebook::Behaviour::Chapter] : req