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
@@ -16,6 +16,10 @@ module Hecks
16
16
  include RuleReference
17
17
  include WordGate
18
18
 
19
+ # @param name [String] the value object's name, as written after `value_object`
20
+ # @param owner_value_objects [Array<Bluebook::ValueObject>] the owning aggregate's other
21
+ # value objects, built so far, that this one's `invariant` references may resolve
22
+ # against
19
23
  def initialize(name, owner_value_objects: [])
20
24
  @name = name
21
25
  @invariants = []
@@ -23,7 +27,7 @@ module Hecks
23
27
  @owner_value_objects = owner_value_objects
24
28
  end
25
29
 
26
- # THE WRAPPER BLOCK IS GONE (ADR 0025, "Attributes" — "closed sets
30
+ # The wrapper block is gone (ADR 0025, "Attributes" — "closed sets
27
31
  # lose the wrapper block"). `member` lines are bare now, written
28
32
  # directly in the `value_object` body with no `one_of do ... end`
29
33
  # around them — `build`'s own `closed_set: @closed_set ||
@@ -39,24 +43,35 @@ module Hecks
39
43
  # use the inline form on one of its own attributes. One `one_of`
40
44
  # method now, not two.
41
45
  #
42
- # LEGACY UNDER SHADOW-PARSING (S0a's own bridge) — frozen era text
46
+ # Legacy under shadow-parsing (S0a's own bridge) — frozen era text
43
47
  # still writes the block form (2 locations in
44
48
  # `examples/banking/data/eras/banking/1.bluebook`, duplicated once
45
49
  # more in that era's own archive copy), so `EraGuard.shadow_parse`
46
50
  # still needs to read it. `block_given?` is what tells the two
47
51
  # calling shapes apart: the type-position form
48
52
  # (`one_of("a", "b")`) never passes a block, only the wrapper does.
49
- # RENAMED FROM `one_of` — item #13's full metaprogrammed dispatch
50
- # (slice 5). SAME NAME as `AttributeCollector#one_of_impl`'s own
53
+ # Declares this value object as a closed set, either from bare values or (under
54
+ # shadow-parsing) a legacy `do ... end` wrapper of `member` lines.
55
+ #
56
+ # Answers the `one_of` word through the table's `calls:` column —
57
+ # item #13's full metaprogrammed dispatch
58
+ # (slice 5). Same name as `AttributeCollector#one_of_impl`'s own
51
59
  # — required for the `super(*values)` call below to keep
52
60
  # resolving; see that method's own comment. Reached directly
53
61
  # through its own "ValueObject"-context Keyword row (the
54
62
  # block-wrapper form has its own row, distinct from "Type"'s),
55
- # not through the Type-position fallback this word's OTHER
63
+ # not through the Type-position fallback this word's other
56
64
  # context uses.
65
+ #
66
+ # @param values [Array<String, Symbol>] permitted values, given bare (no block); at
67
+ # least one is required
68
+ # @yield a legacy `member` wrapper body; only read under shadow-parsing
69
+ # @return [void]
70
+ # @raise [Bluebook::DSL::Malformed] if given no values and no block, or a block outside
71
+ # shadow-parsing
57
72
  def one_of_impl(*values, &block)
58
73
  unless block
59
- # NO VALUES, NO BLOCK is the SCALAR spelling — nonsensical, not
74
+ # No values, no block is the scalar spelling — nonsensical, not
60
75
  # merely inert: it names a closed set with nothing in it. The
61
76
  # original block form caught this by a side effect (`@closed_set
62
77
  # = true` ran unconditionally, before the `if block`), and this
@@ -83,20 +98,27 @@ module Hecks
83
98
  instance_eval(&block)
84
99
  end
85
100
 
86
- # RENAMED FROM `member` — item #13's full metaprogrammed dispatch
87
- # (slice 4c). Bootstrap-reachable, in
88
- # GenericDispatch::BOOTSTRAP_CALLS_FALLBACK.
101
+ # Declares one member row of a closed set, one value per attribute.
102
+ #
103
+ # Answers the `member` word through the table's `calls:` column —
104
+ # item #13's full metaprogrammed dispatch (slice 4c). Bootstrap-reachable, in
105
+ # `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`.
106
+ #
107
+ # @param fields [Hash{Symbol => Object}] each declared attribute's name mapped to this
108
+ # member's value for it
109
+ # @return [Array<Hash{Symbol => Object}>] every member declared so far, this one last
110
+ # @raise [Bluebook::DSL::Malformed] if `fields` is empty
89
111
  def member_impl(**fields)
90
112
  raise Malformed, "#{@name} declared an empty member" if fields.empty?
91
113
 
92
114
  @members << fields
93
115
  end
94
116
 
95
- # NO BLOCK is a REFERENCE, not a fresh declaration — the same
117
+ # No block is a reference, not a fresh declaration — the same
96
118
  # move S10 already made for `CommandBuilder#given` (ADR 0025,
97
119
  # "a precondition shared across commands is declared once... a
98
120
  # command references it by name"), one level over: a rule
99
- # shared across SIBLING value objects on the same aggregate,
121
+ # shared across sibling value objects on the same aggregate,
100
122
  # declared once, on the first one to need it. Real, live
101
123
  # redundancy this closes: `Account`'s own `Money`/`PositiveMoney`
102
124
  # both declared `invariant("a currency is a three-letter code")
@@ -105,9 +127,20 @@ module Hecks
105
127
  # the referencing value object's own build time, against
106
128
  # whatever sibling value objects the aggregate has already
107
129
  # built, the same ordering rule `given` carries.
108
- # RENAMED FROM `invariant` — item #13's full metaprogrammed
109
- # dispatch (slice 4b). Bootstrap-reachable, in
110
- # GenericDispatch::BOOTSTRAP_CALLS_FALLBACK.
130
+ # Declares a rule this value object's own value must satisfy, or references one a sibling
131
+ # value object on the same aggregate already declared.
132
+ #
133
+ # Answers the `invariant` word through the table's `calls:` column —
134
+ # item #13's full metaprogrammed dispatch (slice 4b). Bootstrap-reachable, in
135
+ # `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`.
136
+ #
137
+ # @param description [String] the rule's description; also the name a sibling value
138
+ # object references it by when no block is given
139
+ # @yield the predicate body; evaluated for its extracted source, never called directly
140
+ # @return [void]
141
+ # @raise [Bluebook::DSL::Malformed] if given a block whose source cannot be extracted or
142
+ # uses a pattern construct engines disagree on, or given no block and no sibling value
143
+ # object on this aggregate declares a rule with this description
111
144
  def invariant_impl(description, &predicate)
112
145
  return reference_named_invariant(description) unless predicate
113
146
 
@@ -119,7 +152,7 @@ module Hecks
119
152
 
120
153
  private
121
154
 
122
- # PRIMITIVE 3 (RuleReference#resolve_sibling_scan) — see that
155
+ # Primitive 3 (RuleReference#resolve_sibling_scan) — see that
123
156
  # method's own comment for why this is a live scan, not a pool.
124
157
  def reference_named_invariant(description)
125
158
  verify_resolves_via!("invariant", "ValueObject", "sibling_scan")
@@ -134,6 +167,12 @@ module Hecks
134
167
 
135
168
  public
136
169
 
170
+ # Assembles the declared attributes, invariants and members into a `ValueObject`.
171
+ #
172
+ # @return [Bluebook::ValueObject] the built value object; `closed_set` is true when any
173
+ # member was declared
174
+ # @raise [Bluebook::DSL::Malformed] if `one_of:` was declared on an attribute alongside
175
+ # other attributes on the same value object
137
176
  def build
138
177
  if @inline_closed_set_field && attributes.size > 1
139
178
  raise Malformed,
@@ -149,6 +188,15 @@ module Hecks
149
188
  )
150
189
  end
151
190
 
191
+ # Evaluates a `value_object` block against a fresh builder and returns what it built.
192
+ #
193
+ # @param name [String] the value object's name
194
+ # @param owner_value_objects [Array<Bluebook::ValueObject>] the owning aggregate's other
195
+ # value objects, for `invariant` references to resolve against
196
+ # @yield the value object body, evaluated with the builder as `self`; may be omitted
197
+ # @return [Bluebook::ValueObject] the built value object
198
+ # @raise [Bluebook::DSL::Malformed] if the body declares a rule extraction cannot read,
199
+ # `one_of:` on more than one attribute, or uses a word the grammar does not admit
152
200
  def self.build(name, owner_value_objects: [], &block)
153
201
  builder = new(name, owner_value_objects: owner_value_objects)
154
202
  builder.instance_eval(&block) if block
@@ -157,15 +205,15 @@ module Hecks
157
205
 
158
206
  private
159
207
 
160
- # THE NEW SPELLING — `attribute :name, String, one_of: %w[...]`,
161
- # overriding `AttributeCollector`'s own refusal (every OTHER
162
- # includer has no meaningful use for this). PRIVATE, like the
208
+ # **The new spelling** — `attribute :name, String, one_of: %w[...]`,
209
+ # overriding `AttributeCollector`'s own refusal (every other
210
+ # includer has no meaningful use for this). Private, like the
163
211
  # module's own version it overrides — it is a callback `attribute`
164
212
  # invokes on itself, never a word a bluebook author calls by name.
165
- # Refuses a SECOND attribute naming one_of: on the same value
213
+ # Refuses a second attribute naming one_of: on the same value
166
214
  # object outright — a single-field set names exactly one field, by
167
215
  # construction; two would be structurally ambiguous about which
168
- # field each member line belongs to. `build` refuses the OTHER
216
+ # field each member line belongs to. `build` refuses the other
169
217
  # half of that same rule (this attribute coexisting with unrelated
170
218
  # ones on a multi-field object).
171
219
  def install_inline_closed_set(field, values)
@@ -2,46 +2,49 @@ require_relative "generic_dispatch"
2
2
  module Hecks
3
3
  module Bluebook
4
4
  module DSL
5
- # THE RUBY-SIDE `word_gate` (`rust/parser/src/parse/mod.rs`'s own,
5
+ # The Ruby-side `word_gate` (`rust/parser/src/parse/mod.rs`'s own,
6
6
  # read there first — this is the same job, one level up: Rust's own
7
- # gate refuses a mistyped/inadmissible WORD directly, at the
8
- # moment it reads a line of `.bluebook` SOURCE TEXT, before any
7
+ # gate refuses a mistyped/inadmissible word directly, at the
8
+ # moment it reads a line of `.bluebook` source text, before any
9
9
  # per-construct parsing runs. Ruby never lexes source text — a
10
- # `.bluebook` file IS Ruby, so `given("x")` is ALREADY a real
10
+ # `.bluebook` file is Ruby, so `given("x")` is already a real
11
11
  # method call by the time any of this code runs, and Ruby's own
12
- # method dispatch already refuses an ARITY mismatch on an
13
- # EXISTING method for free. What Ruby's own dispatch does NOT do
14
- # is consult the self-hosted grammar table AT ALL — a mistyped
12
+ # method dispatch already refuses an arity mismatch on an
13
+ # existing method for free. What Ruby's own dispatch does not do
14
+ # is consult the self-hosted grammar table at all — a mistyped
15
15
  # word (`giv3n("x")`) or a word used in the wrong context
16
16
  # (`identified_by` inside a `command` block) just raises Ruby's
17
17
  # own generic `NoMethodError`, naming nothing about what the
18
18
  # language actually admits. This module closes that gap.
19
19
  #
20
- # `method_missing`/`respond_to_missing?` ONLY — a word this
20
+ # ## What reaches this module
21
+ #
22
+ # `method_missing`/`respond_to_missing?` only — a word this
21
23
  # builder class already answers with an ordinary `def` (or a
22
- # shared mixin method like `AttributeCollector#attribute`) never
24
+ # method a shared mixin such as `AttributeCollector` defines) never
23
25
  # reaches this module at all; Ruby's own method lookup finds it
24
- # first, and item #13's later slices haven't removed every one of
25
- # those yet. This WAS "zero behavior change for every currently-
26
- # valid line in the entire corpus" in its own first slice — since
27
- # item #13's full metaprogrammed dispatch (slice 1, whole-project
28
- # table-unification survey) started REMOVING the hand-written
29
- # methods this module's own admissibility check used to defer to,
30
- # some words now execute for real here too, via `GenericDispatch`
31
- # — see that module's own header for exactly which ones, and the
32
- # full account of what was verified before each was migrated. A
33
- # mistyped or wrongly-contexted word still gets the same real,
34
- # helpful, table-driven refusal Rust's own `word_gate` already
35
- # gives, instead of Ruby's own generic `NoMethodError` — that half
36
- # is genuinely unchanged.
26
+ # first, and not every word has had its hand-written method
27
+ # removed. A word the grammar admits whose builder has no
28
+ # hand-written method executes for real here, via `GenericDispatch`
29
+ # (item #13's full metaprogrammed dispatch, whole-project
30
+ # table-unification survey) — see that module's own header for
31
+ # exactly which ones, and the full account of what was verified
32
+ # before each was migrated. A mistyped or wrongly-contexted word
33
+ # gets the same real, helpful, table-driven refusal Rust's own
34
+ # `word_gate` already gives, instead of Ruby's own generic
35
+ # `NoMethodError`.
36
+ #
37
+ # ## `GRAMMAR_CONTEXT`
37
38
  #
38
39
  # `self.class::GRAMMAR_CONTEXT` — each including class names which
39
40
  # row of the self-hosted `Context` closed set it corresponds to
40
41
  # (`AggregateBuilder::GRAMMAR_CONTEXT = "Aggregate"`, etc.) — the
41
- # SAME string `word_gate`'s own `context` parameter already is on
42
+ # same string `word_gate`'s own `context` parameter already is on
42
43
  # the Rust side, read off the identical table.
43
44
  #
44
- # BOOTSTRAPPING GATED, the same reason `RuleReference#lookup`
45
+ # ## Bootstrapping
46
+ #
47
+ # Bootstrapping gated, the same reason `RuleReference#lookup`
45
48
  # already is (`rule_reference.rb`'s own comment has the full
46
49
  # story) — the meta-domain's own bootstrap calls dozens of
47
50
  # keywords on itself before its own grammar table exists to
@@ -52,28 +55,31 @@ module Hecks
52
55
  # is deliberately no fallback covering the whole ~200-row table —
53
56
  # that would defeat the entire point.
54
57
  #
55
- # ONE NARROW EXCEPTION, added in item #13's full metaprogrammed
56
- # dispatch (slice 3, whole-project table-unification survey):
57
- # `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`, a SMALL, EXPLICIT
58
- # table naming the same (context, word) -> method pairs the real
59
- # `calls:` column would say once it exists — used ONLY for words
60
- # BOTH migrated to the `calls:` shape AND bootstrap-reachable
61
- # (`attribute`, today). This is the one place in this whole arc a
58
+ # One narrow exception (item #13's full metaprogrammed dispatch,
59
+ # slice 3, whole-project table-unification survey):
60
+ # `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`, an explicit table
61
+ # naming the same (context, word) -> method pairs the real
62
+ # `calls:` column carries, projected ahead of time into the
63
+ # committed `bootstrap_table.rb` — it matters only for words both
64
+ # migrated to the `calls:` shape and bootstrap-reachable
65
+ # (`attribute`, for one). This is the one place in this whole arc a
62
66
  # bootstrap fallback was worth building despite `RuleReference`'s
63
- # own precedent against it: it duplicates NO logic, only a METHOD
64
- # NAME — `attribute_impl`'s own real, hand-written body is called
67
+ # own precedent against it: it duplicates no logic, only a method
68
+ # name — `attribute_impl`'s own real, hand-written body is called
65
69
  # either way, bootstrap or not, so there is nothing here that can
66
70
  # drift the way a full behavioral duplicate could.
67
71
  #
68
- # TWO FURTHER PIECES, both slice 5:
72
+ # ## `word_gate_dispatch` and the type-position fallback
73
+ #
74
+ # Two further pieces, both slice 5:
69
75
  # - `word_gate_dispatch` is the same admission+dispatch logic
70
76
  # `method_missing` below runs, factored out so a class with
71
- # its OWN class-level `method_missing` (`HecksagonBuilder`'s/
77
+ # its own class-level `method_missing` (`HecksagonBuilder`'s/
72
78
  # `WorldBuilder`'s genuinely open-ended verb vocabulary) can
73
79
  # call it directly and fall back to its own open-verb handling
74
80
  # only when this returns `NOT_ADMITTED` — a class-level `def`
75
81
  # always wins over an included module's in Ruby's own method
76
- # resolution, so their OWN `method_missing` is the only one
82
+ # resolution, so their own `method_missing` is the only one
77
83
  # that ever runs for them, and this is what lets a real,
78
84
  # closed-set word (`port`/`realm`/`latest`) still reach
79
85
  # `GenericDispatch` despite that.
@@ -84,7 +90,7 @@ module Hecks
84
90
  # "Type" builder `self.class::GRAMMAR_CONTEXT` could ever
85
91
  # name. See that method's own comment.
86
92
  module WordGate
87
- # A caller with its OWN class-level `method_missing`
93
+ # A caller with its own class-level `method_missing`
88
94
  # (`HecksagonBuilder`/`WorldBuilder` — see `word_gate_dispatch`'s
89
95
  # own header) checks for this to tell "not a word the grammar
90
96
  # admits at all" apart from every other outcome below (a real
@@ -92,7 +98,7 @@ module Hecks
92
98
  # caller can fall through to its own open-ended handling instead.
93
99
  NOT_ADMITTED = Object.new.freeze
94
100
 
95
- # PRIVATE, matching Ruby's own convention for both (`Object`
101
+ # Private, matching Ruby's own convention for both (`Object`
96
102
  # defines them private too) — and load-bearing here, not just
97
103
  # style: `spec/syntax_conformance_spec.rb`'s "declares every
98
104
  # word X answers" check walks `public_instance_methods`, and a
@@ -129,23 +135,23 @@ module Hecks
129
135
  result
130
136
  end
131
137
 
132
- # THE CORE ADMISSION+DISPATCH LOGIC, factored out of
138
+ # The core admission+dispatch logic, factored out of
133
139
  # `method_missing` — item #13's full metaprogrammed dispatch
134
140
  # (slice 5, whole-project table-unification survey) — so a
135
- # class with its OWN class-level `method_missing`
141
+ # class with its own class-level `method_missing`
136
142
  # (`HecksagonBuilder`'s/`WorldBuilder`'s genuinely open-ended
137
143
  # verb vocabulary, `persisted_by "Heki"`/`posted_by "Carrier"`,
138
- # can never BE a closed table) can still route a word THE
139
- # GRAMMAR ADMITS through here first, falling back to its own
144
+ # can never be a closed table) can still route a word the
145
+ # grammar admits through here first, falling back to its own
140
146
  # open-verb handling only for what this doesn't recognize at
141
147
  # all — unblocking `Hecksagon#port`/`World#realm`/`World#latest`,
142
148
  # each a real, closed-set word that happened to sit on a class
143
149
  # whose own `method_missing` a class-level `def` always wins
144
150
  # over Ruby's own module-inclusion order.
145
151
  #
146
- # NEVER RAISES "not admitted" — returns `NOT_ADMITTED` instead,
152
+ # Never raises "not admitted" — returns `NOT_ADMITTED` instead,
147
153
  # so the caller (this module's own `method_missing`, or one of
148
- # the two above) decides what that means for it. DOES still
154
+ # the two above) decides what that means for it. Does still
149
155
  # raise the richer, table-driven refusals below once a word is
150
156
  # admitted-but-unimplemented, or admitted-somewhere-else-only —
151
157
  # those are real, useful refusals regardless of which
@@ -161,16 +167,16 @@ module Hecks
161
167
  keywords = rows[:keywords]
162
168
  admitted = keywords.select { |row| row[:context] == context && (row[:word] == word.to_s || row[:was] == word.to_s) }
163
169
 
164
- # THE TYPE-POSITION FALLBACK — item #13's full metaprogrammed
170
+ # **The type-position fallback** — item #13's full metaprogrammed
165
171
  # dispatch (slice 5). `one_of`/`list_of`, called inside an
166
172
  # attribute's own type argument (`attribute :x,
167
173
  # one_of("a","b")`), run with `self` as whatever builder is
168
- # CURRENTLY instance_eval'ing — there is no dedicated "Type"
174
+ # currently instance_eval'ing — there is no dedicated "Type"
169
175
  # builder of its own; every attribute()-taking builder answers
170
176
  # these identically, through the shared `AttributeCollector`
171
- # mixin. `self.class::GRAMMAR_CONTEXT` can never actually BE
177
+ # mixin. `self.class::GRAMMAR_CONTEXT` can never actually be
172
178
  # "Type", so a word admitted only there would otherwise look
173
- # inadmissible everywhere. Checked only once THIS context has
179
+ # inadmissible everywhere. Checked only once this context has
174
180
  # already come up empty, so a context with its own same-named
175
181
  # row (`ValueObject`'s own `one_of`, the block-wrapper form)
176
182
  # keeps using that instead, unaffected.
@@ -190,11 +196,11 @@ module Hecks
190
196
  "'#{word}' is not a word #{context} admits — legal words here: #{legal.join(', ')}"
191
197
  end
192
198
 
193
- # ITEM #13's FULL METAPROGRAMMED DISPATCH — the word IS
199
+ # Item #13's full metaprogrammed dispatch — the word is
194
200
  # admitted here, and this builder has no hand-written method
195
201
  # left for it; before falling through to the "not yet
196
202
  # implemented" refusal every word without one still gets,
197
- # offer it to GenericDispatch — the SAFE, verified subset of
203
+ # offer it to GenericDispatch — the safe, verified subset of
198
204
  # words whose whole behavior is now executed off this same
199
205
  # table, not just checked against it.
200
206
  dispatched = GenericDispatch.try(self, context, word.to_s, args, kwargs, block, rows)
@@ -215,13 +221,13 @@ module Hecks
215
221
  super
216
222
  end
217
223
 
218
- # A word admitted SOMEWHERE, just not in THIS context, still
224
+ # A word admitted somewhere, just not in this context, still
219
225
  # falls through to Ruby's own `NoMethodError` rather than this
220
226
  # module's own richer refusal — `method_missing` fires for
221
227
  # every typo in the whole codebase (this class's own genuinely
222
228
  # private helper methods included), not just DSL keyword calls;
223
- # only raise the RICH, table-driven message when the word is at
224
- # least SOMETHING the grammar knows about, anywhere, so an
229
+ # only raise the rich, table-driven message when the word is at
230
+ # least something the grammar knows about, anywhere, so an
225
231
  # unrelated Ruby-level typo inside a builder's own private code
226
232
  # keeps its own ordinary, unconfusing `NoMethodError`.
227
233
  def admitted_anywhere?(rows, word)
@@ -8,6 +8,7 @@ module Hecks
8
8
  # vocabulary of its own; `WorldBuilder#record_binding` reads `#to_h`
9
9
  # back out.
10
10
  class SettingsCollector
11
+ # Starts with no settings recorded.
11
12
  def initialize = @values = {}
12
13
 
13
14
  def method_missing(key, *args, &)
@@ -19,9 +20,9 @@ module Hecks
19
20
  def to_h = @values
20
21
  end
21
22
 
22
- # THE AGGREGATE-QUALIFIED MIRROR (#143) — a `.world` file's own
23
+ # The aggregate-qualified mirror (#143) — a `.world` file's own
23
24
  # `Pizzas::Order.charged_by("Stripe") do ... end` visually mirrors the
24
- # SAME bind line the sibling `.hecksagon` file already writes
25
+ # same bind line the sibling `.hecksagon` file already writes
25
26
  # (`HecksagonBuilder`'s own `BindingProxy`), but `IR::World#for_verb`/
26
27
  # `#for_binding` key purely by verb and adapter name — the aggregate
27
28
  # qualifier is never read back out, it exists only for that visual
@@ -30,12 +31,19 @@ module Hecks
30
31
  # not, has to land in the exact same `@settings` write path
31
32
  # (`WorldBuilder#record_binding`).
32
33
  class WorldConstProxy
34
+ # Mints the stand-in module a bare domain constant resolves to inside a `.world` block.
35
+ #
36
+ # @param builder [Bluebook::DSL::WorldBuilder] the builder every verb called through the
37
+ # module's proxies records its settings on
38
+ # @return [Module] an anonymous module whose `const_missing` answers a `WorldConstProxy`,
39
+ # whatever aggregate name follows `::`
33
40
  def self.namespace(builder)
34
41
  Module.new do
35
42
  define_singleton_method(:const_missing) { |_aggregate| WorldConstProxy.new(builder) }
36
43
  end
37
44
  end
38
45
 
46
+ # @param builder [Bluebook::DSL::WorldBuilder] the builder verb calls are recorded on
39
47
  def initialize(builder) = @builder = builder
40
48
 
41
49
  def method_missing(verb, *args, **kwargs, &block) = @builder.record_binding(verb, args, kwargs, block)
@@ -45,7 +53,7 @@ module Hecks
45
53
 
46
54
  # Parses a `.world` file's top-level DSL block into a `World` — a
47
55
  # domain's own `realm`/`latest` version markers plus its adapter bind
48
- # SETTINGS, one entry per `verb("Adapter") do ... end` call (whether
56
+ # settings, one entry per `verb("Adapter") do ... end` call (whether
49
57
  # written bare or aggregate-qualified through `WorldConstProxy`'s
50
58
  # visual mirror of a sibling `.hecksagon` file's own bind).
51
59
  class WorldBuilder
@@ -53,23 +61,37 @@ module Hecks
53
61
 
54
62
  include WordGate
55
63
 
64
+ # @param domain [String] name of the domain whose world this is
56
65
  def initialize(domain)
57
66
  @domain = domain
58
67
  @settings = {}
59
68
  end
60
69
 
61
- # RENAMED FROM `realm`/`latest` — item #13's full metaprogrammed
62
- # dispatch (slice 5). Neither bootstrap-reachable (checked
63
- # directly). Reached through `WordGate#method_missing`'s new
70
+ # Names the realm this world belongs to, such as `"Examples"` or `"QA"`.
71
+ #
72
+ # `realm_impl`/`latest_impl` answer the `realm`/`latest` words
73
+ # through the table's `calls:` column — item #13's full
74
+ # metaprogrammed dispatch (slice 5). Neither bootstrap-reachable
75
+ # (checked directly). Reached through `WordGate`'s
64
76
  # `word_gate_dispatch`, called explicitly below since
65
77
  # `WorldBuilder`'s own class-level `method_missing` (the
66
78
  # open-verb catch-all beneath this) always wins over the
67
79
  # module's — see `word_gate.rb`'s own header for the full
68
80
  # mechanism.
81
+ #
82
+ # @param value [String, Symbol] the realm's name; blankness is judged by the world
83
+ # language at `build`, not here
84
+ # @return [String] the realm as stored
69
85
  def realm_impl(value)
70
86
  @realm = required(value, "realm")
71
87
  end
72
88
 
89
+ # Names the bluebook version this world treats as latest, which `ProjectRegister` compares
90
+ # with the bluebook's own declared `version`.
91
+ #
92
+ # @param value [String, Symbol, Numeric] the version marker; blankness is judged by the
93
+ # world language at `build`, not here
94
+ # @return [String] the version as stored
73
95
  def latest_impl(value)
74
96
  @latest = required(value, "latest version")
75
97
  end
@@ -83,11 +105,20 @@ module Hecks
83
105
 
84
106
  def respond_to_missing?(_name, _include_private = false) = true
85
107
 
86
- # EXTRACTED from the old `method_missing` body (#143) so
108
+ # Records one bind's settings under both its verb and its `verb:adapter` key.
109
+ #
110
+ # A method of its own, apart from `method_missing` (#143), so
87
111
  # `WorldConstProxy`'s own aggregate-qualified verb calls
88
112
  # (`Pizzas::Order.charged_by(...)`) write into the exact same
89
113
  # place the bare top-level spelling (`charged_by(...)`) already
90
114
  # does — one write path, two spellings.
115
+ #
116
+ # @param verb [Symbol, String] the bind verb, such as `:persisted_by`
117
+ # @param args [Array<Object>] the call's positional arguments; the first names the adapter
118
+ # @param kwargs [Hash{Symbol => Object}] settings given inline as keyword arguments
119
+ # @param block [Proc, nil] a settings block, evaluated against a `SettingsCollector`
120
+ # @return [Hash{Symbol => Object}] the settings just recorded: `:adapter` (a String),
121
+ # then the keyword arguments, then the block's settings, later ones winning
91
122
  def record_binding(verb, args, kwargs, block)
92
123
  collector = SettingsCollector.new
93
124
  collector.instance_eval(&block) if block
@@ -96,18 +127,30 @@ module Hecks
96
127
  @settings["#{verb}:#{args.first.to_s.downcase}"] = value
97
128
  end
98
129
 
130
+ # Assembles the realm, version marker and bind settings, judged by the world language.
131
+ #
132
+ # @return [Bluebook::World] the world, returned once the language accepts it
133
+ # @raise [Bluebook::DSL::Malformed] if the world language refuses the declaration, such
134
+ # as a blank `realm`
99
135
  def build
100
136
  MetaValidator.call_world(
101
137
  World.new(domain: @domain, realm: @realm, latest: @latest, settings: @settings)
102
138
  )
103
139
  end
104
140
 
141
+ # Evaluates a `Hecks.world` block against a fresh builder and returns the world it declared.
142
+ #
105
143
  # `ConstShim`'s resolver, the same bridge `HecksagonBuilder`/
106
144
  # `BluebookBuilder` already wrap their own `instance_eval` in
107
- # (#143) — without it, `Pizzas::Order.charged_by(...)` raised
145
+ # (#143) — without it, `Pizzas::Order.charged_by(...)` raises
108
146
  # `NameError: uninitialized constant Pizzas` for every `.world`
109
147
  # file using the aggregate-qualified mirror form, since a bare
110
148
  # `Pizzas` has no real constant to resolve to.
149
+ #
150
+ # @param domain [String] name of the domain whose world this is
151
+ # @yield the world body, evaluated with the builder as `self`; may be omitted
152
+ # @return [Bluebook::World] the judged world
153
+ # @raise [Bluebook::DSL::Malformed] if the world language refuses the declaration
111
154
  def self.build(domain, &block)
112
155
  builder = new(domain)
113
156
  resolver = ->(_domain) { WorldConstProxy.namespace(builder) }
@@ -3,10 +3,10 @@ require_relative "expression/ast_json"
3
3
 
4
4
  module Hecks
5
5
  module Bluebook
6
- # An entity, as a RUBY CLASS — a piece of an aggregate that has an identity
6
+ # An entity, as a Ruby class — a piece of an aggregate that has an identity
7
7
  # of its own.
8
8
  #
9
- # Crossing over closes the OWNER CHAIN. An entity declares commands, and
9
+ # Crossing over closes the owner chain. An entity declares commands, and
10
10
  # until now those commands had no owner that could state an identity: an
11
11
  # entity was an IR object, not a construct, so `Construct#hecks_fqn` refused
12
12
  # rather than answering "Deposit" and looking right. Four of banking's
@@ -14,15 +14,15 @@ module Hecks
14
14
  # `Banking::Account.Ledger.Deposit` — which is the id the judge already mints
15
15
  # for them.
16
16
  #
17
- # NOT const_set, for the same reason a command is not: a name inside one
17
+ # Not const_set, for the same reason a command is not: a name inside one
18
18
  # aggregate can denote more than one kind of thing, so the constant tree
19
19
  # cannot index it.
20
20
  #
21
- # It must stay STRUCTURALLY INTERCHANGEABLE with an aggregate — the runtime
21
+ # It must stay structurally interchangeable with an aggregate — the runtime
22
22
  # builds `Instance.new(aggregate: entity)` and `CommandRules` takes either as
23
23
  # `declaring` — so it answers `hecks_name`, `attributes`, `attribute`,
24
24
  # `identified_by` and `lifecycle` exactly as an aggregate does. And it must
25
- # keep NOT answering `value_object`: `Value.for_attribute` sniffs for that
25
+ # keep not answering `value_object`: `Value.for_attribute` sniffs for that
26
26
  # method to tell a piece from a head.
27
27
  class Entity
28
28
  extend Construct
@@ -39,26 +39,26 @@ module Hecks
39
39
  # S17, ADR 0026 — "That is what `entity` is for, and `entity` is
40
40
  # declared by the language and used zero times in it" (the ADR's
41
41
  # own words). Dispatch nests inside Handler, so an entity's own
42
- # NESTED entities are part of its wire shape now, the same way
42
+ # nested entities are part of its wire shape now, the same way
43
43
  # an aggregate's always were — the field the ADR names as
44
44
  # declared-but-unused until this slice.
45
45
  entities: many(:entities),
46
- # ADR 0028 — a piece's own `given`, the SAME shape
46
+ # ADR 0028 — a piece's own `given`, the same shape
47
47
  # `Aggregate#preconditions` already carries (its own `emits_ir`
48
48
  # row, identical). A precondition shared across this piece's own
49
49
  # commands, declared once — a command references it back by
50
- # name; the resolved text still lands on EACH referencing
50
+ # name; the resolved text still lands on each referencing
51
51
  # command's own `givens` either way, so this field is read-only
52
52
  # documentation of what the piece itself declared, the same
53
53
  # relationship `Aggregate.preconditions` already has to its own
54
54
  # commands.
55
55
  preconditions: -> { preconditions.map { |rule| Expression::AstJson.rule_row(rule) } },
56
- # A piece's OWN shape rule, checked against EVERY instance of
56
+ # A piece's own shape rule, checked against every instance of
57
57
  # this piece the aggregate holds (Admissibility#enforce_
58
- # invariants' own recursive walk) — the SAME relationship
58
+ # invariants' own recursive walk) — the same relationship
59
59
  # `ValueObject#invariants` already has to its own instances,
60
60
  # one level up the construct tree. Not a separate enforcement
61
- # boundary; still checked at the SAME two points (after every
61
+ # boundary; still checked at the same two points (after every
62
62
  # mutation, before save) the aggregate's own invariants always
63
63
  # were — see that method's own comment for why this does not
64
64
  # contradict "there is no separate entity invariant."
@@ -70,6 +70,24 @@ module Hecks
70
70
  attr_reader :description, :identified_by, :identity_paths, :identity_heads,
71
71
  :attributes, :commands, :queries, :entities, :preconditions, :invariants, :lifecycle
72
72
 
73
+ # Mints a new piece class for one declared entity and absorbs its fields into it.
74
+ #
75
+ # @param name [String, Symbol] the entity's declared name
76
+ # @param description [String, nil] the entity's declared prose description
77
+ # @param identified_by [String, Symbol, Array<String, Symbol>, nil] the identity
78
+ # path(s) this entity is addressed by
79
+ # @param attributes [Array<Bluebook::Attribute>] the entity's declared fields
80
+ # @param commands [Array<Class>] the command classes (`Bluebook::Command` subclasses)
81
+ # declared on this entity
82
+ # @param queries [Array<Bluebook::Query>] the queries declared on this entity
83
+ # @param entities [Array<Class>] the entity classes (`Bluebook::Entity` subclasses)
84
+ # nested directly under this entity
85
+ # @param preconditions [Array<Bluebook::Given>] this entity's own named `given`s
86
+ # @param invariants [Array<Bluebook::Invariant>] the rules checked against every
87
+ # instance of this entity
88
+ # @param lifecycle [Bluebook::Lifecycle, nil] the entity's declared state machine,
89
+ # or `nil` if it declares none
90
+ # @return [Class] the minted piece class (a `Bluebook::Entity` subclass)
73
91
  def declare(name:, description: nil, identified_by: nil, attributes: [],
74
92
  commands: [], queries: [], entities: [], preconditions: [], invariants: [], lifecycle: nil)
75
93
  piece = Class.new(self)
@@ -85,6 +103,17 @@ module Hecks
85
103
  # Assigns what the language declares, then hands off to the
86
104
  # behaviour's own `settle` — derived identity and the name
87
105
  # indexes, neither of which the declaration states.
106
+ #
107
+ # @param description [String, nil] see `declare`
108
+ # @param identified_by [String, Symbol, Array<String, Symbol>, nil] see `declare`
109
+ # @param attributes [Array<Bluebook::Attribute>] see `declare`
110
+ # @param commands [Array<Class>] see `declare`
111
+ # @param queries [Array<Bluebook::Query>] see `declare`
112
+ # @param entities [Array<Class>] see `declare`
113
+ # @param preconditions [Array<Bluebook::Given>] see `declare`
114
+ # @param invariants [Array<Bluebook::Invariant>] see `declare`
115
+ # @param lifecycle [Bluebook::Lifecycle, nil] see `declare`
116
+ # @return [Class] this piece's own class, self, once identity and indexes are derived
88
117
  def absorb(description:, identified_by:, attributes:, commands:, queries:, entities:, preconditions:, invariants:,
89
118
  lifecycle:)
90
119
  @description = description