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,22 +3,24 @@ require "json"
3
3
 
4
4
  module Hecks
5
5
  module Bluebook
6
- # Judges a bluebook by DISPATCHING it into the language declared in itself.
6
+ # Judges a bluebook by dispatching it into the language declared in itself.
7
7
  #
8
- # `lib/hecks/language/bluebook/` declares what a bluebook IS —
8
+ # `lib/hecks/language/bluebook/` declares what a bluebook is —
9
9
  # Chapter, Root, Verb, Shape, Ask, Piece, and the rest, split across files
10
10
  # by domain concept and merged into one chapter at load time (see
11
11
  # GRAMMAR_FILES below). This replays a built IR into that domain and turns
12
12
  # any refusal into a Malformed, so the meta-domain is what actually judges
13
13
  # rather than a description sitting beside the code — for whatever rules
14
14
  # it carries. `spec/meta_rules_spec.rb`'s own header names the plan: port
15
- # the language's rules OUT of builder `raise Malformed` calls and INTO
15
+ # the language's rules out of builder `raise Malformed` calls and into
16
16
  # `given`/`invariant` here, where they are declarations any reader of the
17
17
  # meta-domain can consume instead of behavior buried in a builder.
18
18
  #
19
- # THIS MIGRATION IS PARTIAL, NOT DONE. As of this writing the meta-domain
20
- # declares 62 given/invariant/ensures rules (`Hecks::QueryIR.collect_rules`
21
- # against `grammar_registry.bluebook("Bluebook")` enumerates them) —
19
+ # ## This migration is partial, not done
20
+ #
21
+ # As of this writing the meta-domain declares 62 given/invariant/ensures
22
+ # rules (`Hecks::QueryIR.collect_rules` against
23
+ # `grammar_registry.bluebook("Bluebook")` enumerates them) —
22
24
  # `spec/meta_rule_reachability_spec.rb` is what proves, per declaration,
23
25
  # not per verb, that most of them still lack a spec exercising the
24
26
  # refusal at all (see that file's own KNOWN_GAPS for the current count).
@@ -35,17 +37,18 @@ module Hecks
35
37
  # So: "delete language/bluebook/ and validation stops" is true for the 62
36
38
  # rules actually declared here, and false for whatever a builder's own
37
39
  # `raise Malformed` still checks — the language does not yet own its own
38
- # enforcement end to end, and this comment used to claim it already did.
39
- # The self-hosting mechanism itself is real and is the point worth
40
- # keeping : a self-description that only describes is indistinguishable
41
- # from enforcement, and the first version of this file was deleted for
42
- # exactly that reason. What is not yet real is that self-hosting being
43
- # the WHOLE of validation.
40
+ # enforcement end to end. The self-hosting mechanism itself is real and
41
+ # is the point worth keeping : a self-description that only describes is
42
+ # indistinguishable from enforcement, and the first version of this file
43
+ # was deleted for exactly that reason. What is not yet real is that
44
+ # self-hosting being the whole of validation.
45
+ #
46
+ # ## Isolation
44
47
  #
45
- # The meta-domain is loaded ONCE and its registry reused ; each bluebook is
48
+ # The meta-domain is loaded once and its registry reused ; each bluebook is
46
49
  # judged in a fresh in-memory store so no domain can see another's records.
47
50
  module MetaValidator
48
- # THE FOLDER IS THE CHAPTER. Files are grouped by the domain concept they
51
+ # **The folder is the chapter**. Files are grouped by the domain concept they
49
52
  # describe and every one reopens the same `Hecks.bluebook "Bluebook"`.
50
53
  # `BluebookBuilder.build` keeps one builder open per chapter name across
51
54
  # calls, so the sorted folder accumulates one domain. Adding or renaming a
@@ -67,25 +70,25 @@ module Hecks
67
70
  # TranslationJudge.
68
71
  TRANSLATION_GRAMMAR = Dir.glob(File.expand_path("../language/translation/*.bluebook", __dir__)).freeze
69
72
 
70
- # ADR 0026's OWN SEAM: THE CORE DOES NOT NAME ITS EXTENSION POINTS.
73
+ # ADR 0026's own seam: the core does not name its extension points.
71
74
  #
72
- # A sub-language chapter (Paging, so far the only one) is an ORDINARY
75
+ # A sub-language chapter (Paging, so far the only one) is an ordinary
73
76
  # bluebook — declared with the same `aggregate`/`value_object`/
74
77
  # `attaches_to` words every domain has, judged through the language
75
78
  # the normal way, not bootstrapped raw the way GRAMMAR_FILES is. What
76
- # makes it special is only where it LIVES: any file in this directory
79
+ # makes it special is only where it lives: any file in this directory
77
80
  # is discovered and loaded here, by the directory's own existence,
78
81
  # never by a name this file would have to know. Add a chapter here
79
82
  # and it is attached ; nothing in this file changes.
80
83
  ATTACHED_GRAMMAR_DIR = File.expand_path("../language/bluebook/attaches", __dir__).freeze
81
84
 
82
- # The chapters that ARE the language — loaded raw during bootstrap, then
85
+ # The chapters that are the language — loaded raw during bootstrap, then
83
86
  # judged through themselves and replaced by their own assembled graphs
84
87
  # (see grammar_registry). Each is named after its file : Bluebook describes
85
88
  # bluebooks (language/bluebook/) ; World describes worlds (world.bluebook),
86
89
  # and backs the WorldJudge door ; Hecksagon describes hecksagons
87
90
  # (hecksagon.bluebook) — declared for the same self-description reasons as
88
- # World, but WITHOUT a judge door of its own : nothing dispatches a real
91
+ # World, but without a judge door of its own : nothing dispatches a real
89
92
  # .hecksagon file through it yet, so HecksagonBuilder's own behavior is
90
93
  # unchanged. What this buys is what syntax.bluebook needed — a real shape
91
94
  # `subscribe`'s `fills: "subscriptions"` can point at — not new validation
@@ -94,18 +97,21 @@ module Hecks
94
97
 
95
98
  # The meta-domain is itself a bluebook. Judging it while loading it would
96
99
  # recurse, so the load path marks the bootstrap and skips — but the skip
97
- # is only the FIRST pass. Once every grammar file is loaded and merged,
100
+ # is only the first pass. Once every grammar file is loaded and merged,
98
101
  # grammar_registry judges the language through itself and keeps the
99
102
  # assembled result (the fixpoint, made load-bearing).
103
+ #
104
+ # @return [Boolean] whether the language's own grammar is still
105
+ # loading raw, unjudged
100
106
  def self.bootstrapping? = @bootstrapping
101
107
 
102
- # A CHAPTER MAY BE SPLIT ACROSS FILES, so it cannot be judged until
108
+ # A chapter may be split across files, so it cannot be judged until
103
109
  # every file has been read.
104
110
  #
105
- # `BluebookBuilder.build` already MERGES — `registry.bluebook_builder
111
+ # `BluebookBuilder.build` already merges — `registry.bluebook_builder
106
112
  # (name)` memoises one builder per chapter name, so nine files each
107
113
  # saying `Hecks.bluebook "Bluebook"` accumulate into one. What it
108
- # also does is call `MetaValidator.call` once PER FILE, judging a
114
+ # also does is call `MetaValidator.call` once per file, judging a
109
115
  # chapter that is still eight files short: `Aggregate`'s reference
110
116
  # to `Bluebook` dangles because `Bluebook` has not been declared
111
117
  # yet, and the load dies.
@@ -119,12 +125,21 @@ module Hecks
119
125
  # once, before hecksagons and worlds load (DOMAIN_ORDER already
120
126
  # puts every chapter ahead of those).
121
127
  #
122
- # Only chapters DECLARED INSIDE the window are judged afterwards.
128
+ # Only chapters declared inside the window are judged afterwards.
123
129
  # Re-judging one already assembled — a framework member pulled in
124
130
  # earlier by `uses_framework`, say — would re-run Assembly and hand
125
131
  # out a second set of classes for a graph something already holds.
132
+ #
133
+ # @return [Boolean] whether `call` is currently queuing chapters
134
+ # instead of judging them
126
135
  def self.deferring? = @deferring
127
136
 
137
+ # Opens the two-phase-load window: every chapter `call` sees while
138
+ # the block runs is queued (see `deferred_chapters`) rather than
139
+ # judged immediately.
140
+ #
141
+ # @yield the caller's own load of every file in one chapter window
142
+ # @return [Object] the block's own return value
128
143
  def self.defer
129
144
  previous = @deferring
130
145
  @deferring = true
@@ -133,8 +148,22 @@ module Hecks
133
148
  @deferring = previous
134
149
  end
135
150
 
151
+ # The chapters queued while `defer`'s block ran, awaiting
152
+ # `judge_deferred!`.
153
+ #
154
+ # @return [Array<String>] each deferred chapter's own `hecks_name`,
155
+ # queued during the current or most recent `defer` window
136
156
  def self.deferred_chapters = @deferred_chapters ||= []
137
157
 
158
+ # Judges every chapter queued by `defer`, once each, then clears the
159
+ # queue.
160
+ #
161
+ # @param registry [Runtime::Registry, nil] the registry to judge
162
+ # against; a no-op if `nil`
163
+ # @return [void]
164
+ # @raise [DSL::Malformed] if a chapter's own whole-chapter battery
165
+ # (`BluebookBuilder.validate_assembled!`) or the meta-domain itself
166
+ # (`call`) refuses it
138
167
  def self.judge_deferred!(registry)
139
168
  pending = deferred_chapters.uniq
140
169
  @deferred_chapters = []
@@ -144,18 +173,18 @@ module Hecks
144
173
  chapter = registry.bluebook(name)
145
174
  next unless chapter
146
175
 
147
- # A bare chapter-given left PENDING by any file of this
176
+ # A bare chapter-given left pending by any file of this
148
177
  # chapter (`AggregateBuilder#pending_chapter_given`) resolves
149
178
  # first — before anything below reads a `Given`'s fields.
150
- # `registry.bluebook_builder(name)` is the SAME instance every
179
+ # `registry.bluebook_builder(name)` is the same instance every
151
180
  # one of this chapter's own files built onto (`#self.build`'s
152
181
  # own comment); it is guaranteed already open here, since it
153
182
  # is what produced `chapter` in the first place — the block is
154
183
  # dead code, never actually invoked.
155
184
  builder = registry.bluebook_builder(name) { raise "internal: no open builder for #{name}" }
156
185
  builder.resolve_pending_chapter_givens!
157
- # THE ENTITY-SCOPED ANALOGUE, one level down — same reason,
158
- # same timing: a bare entity-level given left PENDING by any
186
+ # The entity-scoped analogue, one level down — same reason,
187
+ # same timing: a bare entity-level given left pending by any
159
188
  # file of this chapter (`EntityBuilder#pending_chapter_entity_
160
189
  # given`) must resolve before anything below reads a piece's
161
190
  # own `Given` fields too.
@@ -163,11 +192,11 @@ module Hecks
163
192
 
164
193
  # `BluebookBuilder#build` skipped its own whole-chapter battery
165
194
  # (hops, projected fields, correlation keys, event shapes,
166
- # `with:` projections) for every file of THIS chapter while
195
+ # `with:` projections) for every file of this chapter while
167
196
  # `deferring?` was true, the same reason `call` below queued
168
197
  # instead of judging — each of those checks needs every file
169
198
  # loaded first (see `BluebookBuilder.validate_assembled!`'s own
170
- # comment). `chapter` here is exactly that: whatever the LAST
199
+ # comment). `chapter` here is exactly that: whatever the last
171
200
  # file's own `add_bluebook` left in the registry, which by now
172
201
  # holds every aggregate/policy/process_manager the whole
173
202
  # chapter declares. Run once, here, instead of once per file.
@@ -178,39 +207,44 @@ module Hecks
178
207
 
179
208
  # `&& !@forcing_fixpoint` — see `while_forcing_fixpoint` below, whose own
180
209
  # window must win even while a growth spec's `while_disabled` is open,
181
- # for the reason recorded there. Otherwise the SAME stack-restore shape
182
- # `while_shadow_parsing`/`while_forcing_fixpoint` use, not a bare env
183
- # toggle any more — it used to be exactly that (`ENV["HECKS_META_
184
- # VALIDATION"] == "off"`, read directly, with no `previous`/`ensure` of
185
- # its own), and the gap between "bare toggle" and "stack-restore" was
186
- # not cosmetic: a test's temporary window could reach code it was never
187
- # meant to touch. If `grammar_registry`'s ONE-TIME lazy build (below)
188
- # happened to land inside that window, EVERY language chapter got
189
- # cached in its raw, never-judged form for the rest of the process —
190
- # `unmark_scalar`'s String->Integer/Boolean fix (assembly/marks.rb)
191
- # never ran, so a `Command`'s own `required: true` stayed
192
- # `required: "true"` forever after, permanently memoized. Found live:
193
- # an intermittent, parallel_rspec-only ir_golden_spec.rb failure,
194
- # order-dependent on whether identifier_numeric_coercion_growth_spec.rb's
195
- # disabled-validation window raced the ONE lazy build in its own worker
210
+ # for the reason recorded there. Uses the same stack-restore shape
211
+ # `while_shadow_parsing`/`while_forcing_fixpoint` use, deliberately not
212
+ # a bare env toggle (`ENV["HECKS_META_VALIDATION"] == "off"`, read
213
+ # directly, with no `previous`/`ensure` of its own): the gap between
214
+ # "bare toggle" and "stack-restore" is not cosmetic — a test's
215
+ # temporary window could reach code it was never meant to touch. If
216
+ # `grammar_registry`'s one-time lazy build (below) happens to land
217
+ # inside that window, every language chapter gets cached in its raw,
218
+ # never-judged form for the rest of the process — `unmark_scalar`'s
219
+ # String->Integer/Boolean fix (assembly/marks.rb) never runs, so a
220
+ # `Command`'s own `required: true` stays `required: "true"` forever
221
+ # after, permanently memoized. Found live: an intermittent,
222
+ # parallel_rspec-only ir_golden_spec.rb failure, order-dependent on
223
+ # whether identifier_numeric_coercion_growth_spec.rb's
224
+ # disabled-validation window raced the one lazy build in its own worker
196
225
  # process — reproduced in isolation by disabling validation before the
197
226
  # first `grammar_registry` call. `&& !@forcing_fixpoint` was the first
198
- # fix and is kept ; converting `@disabled` itself to this shape closes
199
- # the gap for every OTHER caller of `while_disabled`, not just the one
200
- # race that was actually observed — nothing outside this file reads
201
- # `ENV["HECKS_META_VALIDATION"]` any more (confirmed: every one of the
202
- # dozen growth specs that used to hand-roll `previous = ENV[...] ;
203
- # ENV[...] = "off" ; ... ; ensure ENV[...] = previous` now calls
204
- # `while_disabled` instead), so there is no bare global left to race.
227
+ # fix and is kept ; `@disabled` itself uses this same stack-restore
228
+ # shape to close the gap for every other caller of `while_disabled`,
229
+ # not just the one race that was actually observed — nothing outside
230
+ # this file reads `ENV["HECKS_META_VALIDATION"]` (every growth spec
231
+ # calls `while_disabled` instead), so there is no bare global left to
232
+ # race.
233
+ #
234
+ # @return [Boolean] whether meta-domain judging is currently switched
235
+ # off (and not overridden by a fixpoint build in progress)
205
236
  def self.disabled? = @disabled && !@forcing_fixpoint
206
237
 
207
- # THE SAME STACK-RESTORE SHAPE `while_shadow_parsing`/`while_forcing_
208
- # fixpoint` USE. This toggle's real, intended use is a growth spec
238
+ # The same stack-restore shape `while_shadow_parsing`/`while_forcing_
239
+ # fixpoint` use. This toggle's real, intended use is a growth spec
209
240
  # that boots a scratch bluebook from a tempfile and wants the runtime
210
241
  # behaviour without the validation overhead ; that is always a single
211
242
  # bounded window around one boot, never a flag meant to survive past
212
243
  # it, so the flag itself is scoped in the same `previous`/`ensure`
213
244
  # shape rather than a plain assignment a caller could forget to undo.
245
+ #
246
+ # @yield the caller's own boot, with `disabled?` true throughout
247
+ # @return [Object] the block's own return value
214
248
  def self.while_disabled
215
249
  previous = @disabled
216
250
  @disabled = true
@@ -220,23 +254,29 @@ module Hecks
220
254
  end
221
255
 
222
256
  # ADR 0025's own prerequisite (docs/dsl-work-slices.md, S0a): a word
223
- # a later slice removes from the LIVE grammar must still parse
224
- # FROZEN ERA TEXT — `EraGuard.shadow_parse` (runtime/era_guard.rb)
257
+ # a later slice removes from the live grammar must still parse
258
+ # frozen era text — `EraGuard.shadow_parse` (runtime/era_guard.rb)
225
259
  # is a plain `Kernel.eval` of stored source, run at boot, at mint,
226
260
  # and during tamper detection, against whatever grammar is live
227
- # TODAY, not whatever grammar was live when that text was written.
261
+ # today, not whatever grammar was live when that text was written.
228
262
  # Judging it again here would refuse history the day a spelling it
229
- # used is removed — proved with a rule that already lives ONLY in
263
+ # used is removed — proved with a rule that already lives only in
230
264
  # the meta-domain, never duplicated as a builder's own `raise
231
265
  # Malformed` (`vision`'s own comment: "moved to the language").
232
266
  #
233
- # Mirrors `defer`'s own stack-restore shape, not `disabled?`'s bare
234
- # env toggle — this must never leak past the one shadow-parse call
235
- # that set it, the same reason `ConstShim.with`/`.active?`
236
- # (bluebook/dsl/const_shim.rb) restores in an `ensure` rather than
237
- # being flipped and left.
267
+ # Mirrors `defer`'s own stack-restore shape — this must never leak
268
+ # past the one shadow-parse call that set it, the same reason
269
+ # `ConstShim.with`/`.active?` (bluebook/dsl/const_shim.rb) restores in
270
+ # an `ensure` rather than being flipped and left.
271
+ #
272
+ # @return [Boolean] whether frozen era text is currently being
273
+ # shadow-parsed
238
274
  def self.shadow_parsing? = @shadow_parsing
239
275
 
276
+ # Wraps `block` with `shadow_parsing?` true, restoring it afterward.
277
+ #
278
+ # @yield the caller's own shadow-parse of one piece of frozen era text
279
+ # @return [Object] the block's own return value
240
280
  def self.while_shadow_parsing
241
281
  previous = @shadow_parsing
242
282
  @shadow_parsing = true
@@ -245,13 +285,17 @@ module Hecks
245
285
  @shadow_parsing = previous
246
286
  end
247
287
 
248
- # THE SAME STACK-RESTORE SHAPE `while_shadow_parsing` USES, for the
288
+ # The same stack-restore shape `while_shadow_parsing` uses, for the
249
289
  # same reason: whatever this wraps must never see `disabled?` answer
250
- # true, however a test elsewhere has the env toggle set at that
251
- # exact moment. Only `grammar_registry`'s own one-time build (below)
252
- # wraps itself in this — nothing else needs it, and nothing else
253
- # should reach for it just to dodge `disabled?` for a domain
290
+ # true, however a test elsewhere has a `while_disabled` window open at
291
+ # that exact moment. Only `grammar_registry`'s own one-time build
292
+ # (below) wraps itself in this — nothing else needs it, and nothing
293
+ # else should reach for it just to dodge `disabled?` for a domain
254
294
  # bluebook, which is precisely the toggle's real, intended use.
295
+ #
296
+ # @yield the one-time fixpoint build, with `disabled?` forced false
297
+ # throughout
298
+ # @return [Object] the block's own return value
255
299
  def self.while_forcing_fixpoint
256
300
  previous = @forcing_fixpoint
257
301
  @forcing_fixpoint = true
@@ -262,11 +306,21 @@ module Hecks
262
306
 
263
307
  # The same bluebook judged twice gets the same verdict, and a suite reloads
264
308
  # its fixtures constantly — banking alone is ~200 dispatches per build.
265
- # Keyed on the IR itself, so a CHANGED bluebook is always re-judged.
309
+ # Keyed on the IR itself, so a changed bluebook is always re-judged.
310
+ #
311
+ # @return [Hash{String => Array<String>}] the process-wide judging
312
+ # cache, keyed by a SHA-256 digest of the judged artifact; each
313
+ # value is that artifact's own list of refusal messages (`[]` when
314
+ # well formed)
266
315
  def self.verdicts = @verdicts ||= {}
267
316
 
268
317
  # A world is not a bluebook, so it gets its own door. Same judge, same
269
318
  # meta-domain registry — a different artifact and a different language file.
319
+ #
320
+ # @param world [Bluebook::World] the world to judge
321
+ # @return [Bluebook::World] `world` unchanged, if well formed or
322
+ # while disabled/bootstrapping/shadow-parsing
323
+ # @raise [DSL::Malformed] if `WorldJudge` finds `world` malformed
270
324
  def self.call_world(world)
271
325
  return world if disabled? || bootstrapping? || shadow_parsing?
272
326
 
@@ -281,6 +335,11 @@ module Hecks
281
335
  # A port is not a bluebook either — same door shape as call_world,
282
336
  # one artifact over. Whole-project table-unification survey, item
283
337
  # #13's remaining builders.
338
+ #
339
+ # @param port [Bluebook::Port] the port to judge
340
+ # @return [Bluebook::Port] `port` unchanged, if well formed or while
341
+ # disabled/bootstrapping/shadow-parsing
342
+ # @raise [DSL::Malformed] if `PortJudge` finds `port` malformed
284
343
  def self.call_port(port)
285
344
  return port if disabled? || bootstrapping? || shadow_parsing?
286
345
 
@@ -295,6 +354,11 @@ module Hecks
295
354
  # An adapter is not a bluebook either — same door shape, one more
296
355
  # artifact over. Whole-project table-unification survey, item
297
356
  # #13's remaining builders.
357
+ #
358
+ # @param adapter [Bluebook::Adapter] the adapter to judge
359
+ # @return [Bluebook::Adapter] `adapter` unchanged, if well formed or
360
+ # while disabled/bootstrapping/shadow-parsing
361
+ # @raise [DSL::Malformed] if `AdapterJudge` finds `adapter` malformed
298
362
  def self.call_adapter(adapter)
299
363
  return adapter if disabled? || bootstrapping? || shadow_parsing?
300
364
 
@@ -309,14 +373,20 @@ module Hecks
309
373
  # A translation is not a bluebook either — same door shape, one more
310
374
  # artifact over (a `translations/*.bluebook` edge — `data_translation`'s
311
375
  # own real, established convention; there is no separate extension).
312
- # TranslationJudge walks the WHOLE built translation (every nested
376
+ # TranslationJudge walks the whole built translation (every nested
313
377
  # aggregate's own rule table) in one pass — only
314
378
  # TranslationBuilder's own top-level `build` calls this;
315
379
  # TranslationAggregateBuilder#build stays a plain struct constructor,
316
380
  # the same way `WorldJudge` judges every `Wiring` a `.world` declares
317
- # in ONE pass over `World::World.Declare`'s own caller, not from a
381
+ # in one pass over `World::World.Declare`'s own caller, not from a
318
382
  # separate door per binding. Whole-project table-unification survey,
319
383
  # item #13's remaining builders.
384
+ #
385
+ # @param translation [Bluebook::Translation] the translation to judge
386
+ # @return [Bluebook::Translation] `translation` unchanged, if well
387
+ # formed or while disabled/bootstrapping/shadow-parsing
388
+ # @raise [DSL::Malformed] if `TranslationJudge` finds `translation`
389
+ # malformed
320
390
  def self.call_translation(translation)
321
391
  return translation if disabled? || bootstrapping? || shadow_parsing?
322
392
 
@@ -337,55 +407,54 @@ module Hecks
337
407
  "#{translation.domain}'s translation is not well formed; #{refusals.join('; ')}"
338
408
  end
339
409
 
340
- # THE LANGUAGE HANDS THE GRAPH BACK.
341
- #
342
- # This used to return the bluebook it was given — dispatch every declaration
343
- # in, collect refusals, throw the records away — which is all JUDGING needs
344
- # and exactly why the language could only validate. It returns what the
345
- # meta-domain HOLDS instead, assembled into the graph the runtime runs. The
346
- # builder's own object graph now exists only to be dispatched; nothing keeps
347
- # it.
348
- #
349
- # `Hecks.bluebook` registers whatever comes back from here, so this one line
350
- # is the difference between a language that checks a domain and a language
351
- # that is the source of one.
352
- #
353
- # What is CACHED is the declarations, not the graph. A hash carries no Ruby
354
- # classes, so a second load of the same chapter re-assembles fresh ones —
355
- # which is the behaviour `Namespace.install` and `spec/construct_spec` both
356
- # expect. Caching the graph would hand two boots the same classes.
357
- # THE LANGUAGE IS THE SOURCE. This is the line that makes it one.
358
- #
359
- # `Hecks.bluebook` registers whatever comes back from here, so returning the
360
- # assembled graph rather than the bluebook it was handed is the whole swap: the
361
- # runtime runs what the meta-domain HOLDS. The builder's own graph exists only
362
- # to be dispatched in ; nothing keeps it.
363
- #
364
- # It stayed unlanded for one wrong belief, worth naming because it looked so
365
- # much like a wall: that the language may only hold what `to_h` carries.
366
- # `ReadModel#to_h` omitted a read model's filters until 2026-08-11, so
367
- # read-model filtering seemed impossible to read back —
368
- # and hoisted policies lost which head declared them for the same reason.
369
- # But `to_h` is a PROJECTION and the language is the
370
- # SOURCE. They must agree about everything to_h spells ; they need not be the
371
- # same size. Both were held even before the wire format carried them, as
372
- # declarations the wire format didn't yet see.
373
- #
374
- # UPDATE, 2026-08-11: the wire format DID move, on purpose, for a reason
375
- # unrelated to this file — a Rust-codegen task needed `wheres`/
376
- # `order_by`/`limit` on the wire to compile a read model's real declared
377
- # filtering, and the boundary described above was never load-bearing for
378
- # THIS mechanism (`option_rows`/`filter_options` in meta_validator/
379
- # readings.rb read `node.wheres`/`node.order_by`/`node.limit` off the
380
- # live object directly, never off `to_h`), so extending `to_h` changed
381
- # nothing here. `ReadModel#to_h` now spells all three explicitly, the
382
- # same mechanism `Query#to_h` already used — purely additive, still
410
+ # The language hands the graph back.
411
+ #
412
+ # A bluebook judged elsewhere only checks: dispatch every declaration
413
+ # in, collect refusals, throw the records away — which is all a
414
+ # boundary check needs. This is the one call that returns what the
415
+ # meta-domain holds instead, assembled into the graph the runtime
416
+ # runs, so the builder's own object graph exists only to be
417
+ # dispatched — nothing keeps it. `Hecks.bluebook` registers whatever
418
+ # comes back from here, so this one line is the difference between a
419
+ # language that checks a domain and a language that is the source of
420
+ # one: the runtime runs what the meta-domain holds, not the builder's
421
+ # own graph.
422
+ #
423
+ # What is cached is the declarations, not the graph. A hash carries no
424
+ # Ruby classes, so a second load of the same chapter re-assembles
425
+ # fresh ones — the behaviour `Namespace.install` and
426
+ # `spec/construct_spec` both expect. Caching the graph would hand two
427
+ # boots the same classes.
428
+ #
429
+ # One belief stayed unlanded for a while, worth naming because it
430
+ # looked so much like a wall: that the language may only hold what
431
+ # `to_h` carries. `ReadModel#to_h` omitted a read model's filters
432
+ # until 2026-08-11, so read-model filtering seemed impossible to read
433
+ # back — and hoisted policies lost which head declared them for the
434
+ # same reason. But `to_h` is a projection and the language is the
435
+ # source. They must agree about everything `to_h` spells ; they need
436
+ # not be the same size. Both were held even before the wire format
437
+ # carried them, as declarations the wire format didn't yet see.
438
+ #
439
+ # The wire format did later move, for a reason unrelated to this
440
+ # file — a Rust-codegen task needed `wheres`/`order_by`/`limit` on
441
+ # the wire to compile a read model's real declared filtering, and the
442
+ # boundary described above was never load-bearing for this mechanism
443
+ # (`option_rows`/`filter_options` in meta_validator/readings.rb read
444
+ # `node.wheres`/`node.order_by`/`node.limit` off the live object
445
+ # directly, never off `to_h`), so extending `to_h` changed nothing
446
+ # here. `ReadModel#to_h` now spells all three explicitly, the same
447
+ # mechanism `Query#to_h` already used — purely additive, still
383
448
  # agreeing with the language about everything it spells.
384
449
  #
385
- # What is CACHED is the declarations, not the graph. A hash carries no Ruby
386
- # classes, so a second load of the same chapter assembles fresh ones — which is
387
- # what `Namespace.install` and `spec/construct_spec` both expect. Caching the
388
- # graph would hand two boots the same classes.
450
+ # @param bluebook [Object] a built bluebook chapter graph (an
451
+ # `Aggregate`/`Entity`/`ValueObject`/... instance from
452
+ # `lib/hecks/bluebook/`), answering `to_h` and `hecks_name`
453
+ # @return [Object] `bluebook` unchanged while disabled, bootstrapping,
454
+ # shadow-parsing, or deferring; otherwise the graph the meta-domain
455
+ # assembles from its own judged declarations
456
+ # @raise [DSL::Malformed] if the meta-domain refuses any of
457
+ # `bluebook`'s declarations
389
458
  def self.call(bluebook)
390
459
  return bluebook if disabled? || bootstrapping? || shadow_parsing?
391
460
 
@@ -405,9 +474,16 @@ module Hecks
405
474
  Assembly.call(held[:declaration])
406
475
  end
407
476
 
408
- # Dispatch it in and READ IT BACK. A refused chapter has no declarations to
477
+ # Dispatch it in and read it back. A refused chapter has no declarations to
409
478
  # read — the records are half-written by definition — so it carries refusals
410
479
  # and nothing else.
480
+ #
481
+ # @param bluebook [Object] a built bluebook chapter graph, as `call`
482
+ # receives it
483
+ # @return [Hash{Symbol => Object}] `{refusals: [...]}` when `Judge`
484
+ # refuses any declaration; otherwise `{refusals: [],
485
+ # declaration: Hash}`, the assembled graph `Reconstruction.of` reads
486
+ # back from the judged records
411
487
  def self.hold(bluebook)
412
488
  judge = Judge.new(bluebook)
413
489
  return { refusals: judge.refusals } unless judge.refusals.empty?
@@ -415,52 +491,57 @@ module Hecks
415
491
  { refusals: [], declaration: Reconstruction.of(judge.runtime, bluebook.hecks_name) }
416
492
  end
417
493
 
494
+ # The booted, self-judged language registry every ordinary bluebook is
495
+ # judged against — built once per process and memoized.
496
+ #
497
+ # @return [Runtime::Registry] the booted, fixpoint-judged language
498
+ # registry
418
499
  def self.grammar_registry
419
500
  @grammar_registry ||= begin
420
501
  registry = load_grammar_into(Runtime::Registry.new)
421
- # Assigned BEFORE the fixpoint judge below: judging re-enters
502
+ # Assigned before the fixpoint judge below: judging re-enters
422
503
  # grammar_registry through fresh_runtime (judge.rb) and Plan.for
423
504
  # (judge.rb, reconstruction.rb) — a bare ||= would still be nil
424
505
  # while its right-hand side evaluates, and recurse forever. That
425
- # reentrancy window is real: a caller landing here BEFORE the
426
- # fixpoint/attach below have run sees the SAME registry object,
506
+ # reentrancy window is real: a caller landing here before the
507
+ # fixpoint/attach below have run sees the same registry object,
427
508
  # correctly, but one still missing the attached chapters (Paging's
428
509
  # `attaches_to` among them) — see grammar_registry_ready? below.
429
510
  @grammar_registry = registry
430
- # THE FIXPOINT MADE LOAD-BEARING. The bootstrap loaded the language
511
+ # **The fixpoint made load-bearing**. The bootstrap loaded the language
431
512
  # raw ; now the language judges itself, its records are read back,
432
- # and the ASSEMBLED graph replaces the raw one — so every bluebook
513
+ # and the assembled graph replaces the raw one — so every bluebook
433
514
  # judged from here on is judged by the language the language itself
434
515
  # produced. Outside load_grammar_into on purpose : its ensure clears
435
516
  # @bootstrapping, and call() must see bootstrapping? == false to do
436
517
  # anything at all. `while_forcing_fixpoint`-wrapped so a growth
437
518
  # spec's own `while_disabled` window can never leave this
438
- # ONE-TIME build cached in its raw, never-judged form — see
519
+ # one-time build cached in its raw, never-judged form — see
439
520
  # `disabled?`'s own comment.
440
521
  while_forcing_fixpoint do
441
522
  LANGUAGE_CHAPTERS.each { |name| registry.add_bluebook(call(registry.bluebook(name))) }
442
523
  load_attached_grammar_into(registry)
443
524
  end
444
- # Stamped LAST, keyed by this registry's own identity rather than
525
+ # Stamped last, keyed by this registry's own identity rather than
445
526
  # a bare boolean — a manual reset (fixpoint_spec.rb's own
446
527
  # `@grammar_registry = nil`) makes @grammar_registry not equal
447
528
  # this object_id again until a fresh build finishes, so a stale
448
- # "ready" from the PREVIOUS cycle can never leak into the next.
529
+ # "ready" from the previous cycle can never leak into the next.
449
530
  @grammar_ready_for = registry.object_id
450
531
  registry
451
532
  end
452
533
  end
453
534
 
454
- # A REENTRANT CALL DURING THE FIXPOINT/ATTACH WINDOW ABOVE gets a
535
+ # A reentrant call during the fixpoint/attach window above gets a
455
536
  # real, correctly-mutating registry object back — no infinite loop,
456
- # no wrong data for THAT caller's own purposes. But anything that
457
- # MEMOIZES a snapshot derived from it must not lock that snapshot
537
+ # no wrong data for that caller's own purposes. But anything that
538
+ # memoizes a snapshot derived from it must not lock that snapshot
458
539
  # in forever : this is one signal such a cache can check. Found
459
540
  # live — SyntaxBoot.call had cached a Query keyword list missing
460
541
  # every Paging-attached word (limit/offset/cursor/nulls) because
461
542
  # something called it inside this exact window.
462
543
  #
463
- # SyntaxBoot.call NO LONGER USES THIS. Gating its cache on "the
544
+ # SyntaxBoot.call no longer uses this. Gating its cache on "the
464
545
  # whole registry is finished" meant nothing was cached for the
465
546
  # entire window, and the window is not narrow — every word routed
466
547
  # through `word_gate_dispatch` while the language judged itself
@@ -470,40 +551,52 @@ module Hecks
470
551
  # the snapshot is a function of. Kept here for any other derived
471
552
  # cache that genuinely needs "is the build finished" rather than
472
553
  # "have my inputs changed".
554
+ #
555
+ # @return [Boolean] whether `grammar_registry`'s memoized singleton
556
+ # has finished its fixpoint judge and attached-chapter load
473
557
  def self.grammar_registry_ready?
474
558
  @grammar_registry && @grammar_ready_for == @grammar_registry.object_id
475
559
  end
476
560
 
477
- # ATTACHED CHAPTERS LOAD AFTER THE FIXPOINT, NOT DURING BOOTSTRAP —
478
- # they are declared IN the language the language just finished
561
+ # Attached chapters load after the fixpoint, not during bootstrap —
562
+ # they are declared in the language the language just finished
479
563
  # judging itself through, so they are ordinary bluebooks, judged the
480
564
  # ordinary way (`Hecks.bluebook` → `BluebookBuilder#build` →
481
565
  # `MetaValidator.call`, `bootstrapping?` already false). A directory
482
566
  # with nothing in it loads nothing ; this is a no-op until a chapter
483
567
  # is added there.
568
+ #
569
+ # @param registry [Runtime::Registry] the registry to load every
570
+ # `ATTACHED_GRAMMAR_DIR` chapter file into
571
+ # @return [void]
484
572
  def self.load_attached_grammar_into(registry)
485
573
  Hecks.with_registry(registry) do
486
574
  Dir.glob(File.join(ATTACHED_GRAMMAR_DIR, "*.bluebook")).each { |file| Kernel.load(file) }
487
575
  end
488
576
  end
489
577
 
490
- # THE ONE PLACE THE GRAMMAR'S OWN BOOT SEQUENCE IS SPELLED — ports, the
578
+ # The one place the grammar's own boot sequence is spelled — ports, the
491
579
  # memory/prism adapters, the (now nine-file) chapter itself, then the
492
580
  # sibling world grammar. `grammar_registry` uses this for its memoised
493
- # singleton ; anything that needs an ISOLATED registry (a spec wanting a
581
+ # singleton ; anything that needs an isolated registry (a spec wanting a
494
582
  # fresh store per example, say) calls this directly instead of hand-
495
583
  # copying the sequence.
496
584
  #
497
- # THE BOOTSTRAP GUARD LIVES HERE, not just around the singleton. Splitting
585
+ # The bootstrap guard lives here, not just around the singleton. Splitting
498
586
  # the chapter into several files means each file's own `Hecks.bluebook
499
587
  # "Bluebook"` call now runs `BluebookBuilder#build` once per file — and
500
588
  # `MetaValidator.call` judges whatever it is handed unless `bootstrapping?`
501
589
  # is true. A caller that loaded the grammar files by hand into its own
502
- # registry, without this guard, would get each file DISPATCHED AND JUDGED
503
- # ALONE the moment it loaded — and a lone file like `aggregate.bluebook`
590
+ # registry, without this guard, would get each file dispatched and judged
591
+ # alone the moment it loaded — and a lone file like `aggregate.bluebook`
504
592
  # refuses immediately, since `Aggregate.Attribute` references `ValueObject`
505
593
  # and `Aggregate.Holds` references `Entity`, both declared in later files.
506
594
  # Every caller of the grammar must go through here for exactly that reason.
595
+ #
596
+ # @param registry [Runtime::Registry] the registry to load the whole
597
+ # grammar (ports, adapters, and every language chapter) into, raw
598
+ # and unjudged
599
+ # @return [Runtime::Registry] `registry`, unchanged in identity
507
600
  def self.load_grammar_into(registry)
508
601
  @bootstrapping = true
509
602
  Hecks.with_registry(registry) do
@@ -523,7 +616,7 @@ module Hecks
523
616
  @bootstrapping = false
524
617
  end
525
618
 
526
- # A FRESH STORE per bluebook. The registry memoises repositories, so
619
+ # A fresh store per bluebook. The registry memoises repositories, so
527
620
  # reusing it let every bluebook see the records of every bluebook judged
528
621
  # before it. The parsed grammar is reused ; only the records are cleared.
529
622
  # (During grammar_registry's own fixpoint judge this clear runs on the
@@ -532,6 +625,9 @@ module Hecks
532
625
  # No `bind_runtime` here : judging dispatches by FQN and never opens the
533
626
  # door, and binding would re-install the language's own facade constants
534
627
  # once per judged chapter.
628
+ #
629
+ # @return [Runtime::Dispatcher] a dispatcher over the shared grammar
630
+ # registry, with its records cleared
535
631
  def self.fresh_runtime
536
632
  registry = grammar_registry
537
633
  registry.instance_variable_set(:@repositories, {})