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
@@ -1,35 +1,42 @@
1
+ require "digest"
2
+ require "fileutils"
3
+
1
4
  module Hecks
2
5
  module Bluebook
3
6
  module MetaValidator
4
- # S14, ADR 0026 — DISPATCHES THE LANGUAGE'S OWN GRAMMAR TABLE INTO
5
- # ITSELF, once, so `Keyword`/`Argument`'s own `status` genuinely IS a
7
+ # S14, ADR 0026 — dispatches the language's own grammar table into
8
+ # itself, once, so `Keyword`/`Argument`'s own `status` genuinely is a
6
9
  # lifecycle — checked at real dispatch time (the same admission/
7
10
  # coercion/lifecycle-guard door every other command goes through),
8
11
  # not merely declared and never exercised.
9
12
  #
10
- # THE SOURCE STAYS STATIC. Aggregate-local `KeywordSeed`/
11
- # `ArgumentSeed` value objects (still hand-written `member` rows —
12
- # now beside the concepts they spell) are what gets WRITTEN ; this
13
- # is what turns them into what gets READ. `Judge`/`Reconstruction`
14
- # already draw exactly this line everywhere else in the meta-domain
15
- # (a chapter's own DECLARATIONS versus what gets DISPATCHED from
16
- # them) — this runs the same distinction one level further out, for
17
- # the language's own grammar table.
13
+ # ## The source stays static
14
+ #
15
+ # Aggregate-local `KeywordSeed`/`ArgumentSeed` value objects (still
16
+ # hand-written `member` rows — now beside the concepts they spell)
17
+ # are what gets written ; this is what turns them into what gets
18
+ # read. `Judge`/`Reconstruction` already draw exactly this line
19
+ # everywhere else in the meta-domain (a chapter's own declarations
20
+ # versus what gets dispatched from them) — this runs the same
21
+ # distinction one level further out, for the language's own grammar
22
+ # table.
23
+ #
24
+ # ## A dedicated runtime
18
25
  #
19
- # A DEDICATED RUNTIME, not `MetaValidator.fresh_runtime`. That one
20
- # is reserved for `Judge`'s own bootstrap (dispatching a CHAPTER's
21
- # declarations INTO the meta-domain's grammar) — a different act
22
- # from this one (dispatching the meta-domain's OWN grammar table
23
- # data into a live "Bluebook" domain instance). Sharing the runtime
24
- # would let one boot's own repository state leak into the other's.
26
+ # Not `MetaValidator.fresh_runtime`. That one is reserved for
27
+ # `Judge`'s own bootstrap (dispatching a chapter's declarations into
28
+ # the meta-domain's grammar) — a different act from this one
29
+ # (dispatching the meta-domain's own grammar table data into a live
30
+ # "Bluebook" domain instance). Sharing the runtime would let one
31
+ # boot's own repository state leak into the other's.
25
32
  #
26
- # Usage:
33
+ # ## Usage
27
34
  #
28
35
  # MetaValidator.syntax_table # => { keywords: [...], arguments: [...] }
29
36
  #
30
37
  # each row a plain Hash, string values, `status` included — the
31
38
  # exact shape `ParserTable`/`syntax_conformance_spec` already read
32
- # off the OLD closed-set members, so neither consumer had to change
39
+ # off the old closed-set members, so neither consumer had to change
33
40
  # what it does with a row, only where the row comes from.
34
41
  module SyntaxBoot
35
42
  module_function
@@ -40,21 +47,21 @@ module Hecks
40
47
  # conformance_spec, bin/reference) should each pay for on every
41
48
  # call.
42
49
  #
43
- # KEYED BY THE GRAMMAR REGISTRY'S OWN CHAPTER SET, not by a
50
+ # Keyed by the grammar registry's own chapter set, not by a
44
51
  # "ready" flag. `boot` reads exactly two things: the registry's
45
52
  # "Bluebook" chapter (the core seed rows) and every chapter in the
46
53
  # registry that `attaches_to` a core context (Paging's rows). So
47
- # the table is a pure function of which chapter OBJECTS the
54
+ # the table is a pure function of which chapter objects the
48
55
  # registry holds — and that is the cache key: the same chapters,
49
56
  # by identity, mean the same table; a chapter replaced (the
50
57
  # fixpoint judge swapping a raw chapter for its assembled self)
51
58
  # or added (`load_attached_grammar_into`) means a fresh boot.
52
59
  #
53
- # WHY NOT THE EARLIER `grammar_registry_ready?` GUARD. That guard
60
+ # Why not the earlier `grammar_registry_ready?` guard. That guard
54
61
  # was right about the hazard — a snapshot taken mid-build, before
55
62
  # Paging attached, would be missing limit/offset/cursor/nulls
56
63
  # forever — and wrong about the cost of its cure. It refused to
57
- # cache ANYTHING until the whole grammar registry had finished
64
+ # cache anything until the whole grammar registry had finished
58
65
  # building, calling that window "narrow" and recomputing in it
59
66
  # "cheap". Measured (hecks_ai_training, a six-entity chess domain,
60
67
  # 2026-08-22): every `word_gate_dispatch` landing in that window
@@ -69,20 +76,37 @@ module Hecks
69
76
  # chapter objects, so it invalidates this the same way it always
70
77
  # invalidated `grammar_registry_ready?`.
71
78
  #
72
- # `equal?`, NOT `==`, on the chapters — identity is the fact being
79
+ # `equal?`, not `==`, on the chapters — identity is the fact being
73
80
  # tracked. Holding the chapter objects themselves (not their ids)
74
81
  # in the key also means a collected chapter can never hand its id
75
82
  # to a newcomer behind this cache's back.
83
+ # @return [Hash{Symbol => Array<Hash{Symbol => String}>}] `:keywords`
84
+ # and `:arguments`, each an array of plain, string-valued row
85
+ # hashes (`status` included)
76
86
  def call
77
87
  chapters = MetaValidator.grammar_registry.bluebooks.to_a
78
88
  return @call if @call && same_chapters?(@call_chapters, chapters)
79
89
 
80
- result = boot
90
+ result = read_disk_cache(chapters) || begin
91
+ fresh = boot
92
+ write_disk_cache(chapters, fresh)
93
+ fresh
94
+ end
95
+
81
96
  @call = result
82
97
  @call_chapters = chapters
83
98
  result
84
99
  end
85
100
 
101
+ # Compares the in-process cache key against the registry's current
102
+ # chapters.
103
+ #
104
+ # @param cached [Array<Array(String, Object)>] the `[name, chapter]`
105
+ # pairs `call` last cached against
106
+ # @param current [Array<Array(String, Object)>] the registry's
107
+ # current `[name, chapter]` pairs
108
+ # @return [Boolean] whether `cached` and `current` name the same
109
+ # chapters, by identity, in the same order
86
110
  def same_chapters?(cached, current)
87
111
  cached.size == current.size &&
88
112
  cached.zip(current).all? do |(cached_name, cached_chapter), (name, chapter)|
@@ -90,14 +114,155 @@ module Hecks
90
114
  end
91
115
  end
92
116
 
117
+ # Cross-process persistence for the same ~1s-per-build work `call`
118
+ # above already memoizes in-process — this file's own chapter-
119
+ # identity keying only ever helps callers sharing one process (the
120
+ # in-memory fixpoint window, or `bin/reference`'s single run); every
121
+ # fresh process (a `qa_sweep --all` child, a `parallel_rspec`
122
+ # worker, `bin/qa_sweep` itself) pays both real builds again from
123
+ # scratch even when the grammar source hasn't changed at all since
124
+ # the last process that built it.
125
+ #
126
+ # Not a fix for the two-builds-per-process shape — investigated and
127
+ # confirmed genuine, not waste (see this module's own header
128
+ # history: the previous "wait for the whole registry" design cost
129
+ # 42 rebuilds/32s per process precisely because real callers need a
130
+ # correct table mid-bootstrap, before Paging attaches). Both builds
131
+ # still happen once each per process; this only makes each of them
132
+ # a cheap disk read instead of a real ~1s dispatch, once some
133
+ # earlier process has already paid for that exact chapter-set +
134
+ # grammar-content combination.
135
+ #
136
+ # Keyed the same way `same_chapters?` is, translated across process
137
+ # boundaries — chapter *object identity* (what the in-memory cache
138
+ # keys on) means nothing to a different process; chapter *names*,
139
+ # in the same order, do. Combined with a content hash of every
140
+ # grammar file `boot` can read from (`seed_chapters` walks every
141
+ # bluebook the registry holds, so this must cover the core chapters
142
+ # and every attached one, not just "Bluebook" + "Paging") — a
143
+ # source edit anywhere in that set correctly misses the old cache
144
+ # entry rather than silently serving a stale table.
145
+ #
146
+ # Fails toward a real boot, never toward a wrong table — same
147
+ # loud-not-silent discipline this codebase already holds CI to
148
+ # (`postgres_io_relevant_changed`'s own header). A missing file, a
149
+ # corrupt Marshal blob, a permission error, an unwritable `tmp/` —
150
+ # every one of these degrades to "no disk cache today", never to a
151
+ # crash or a served-but-wrong table. `tmp/` is already gitignored
152
+ # for exactly this kind of local, disposable-but-useful-while-it-
153
+ # lasts artifact (`Storehouse::LOG_ROOT`'s own header names the
154
+ # same convention).
155
+ #
156
+ # **Atomic write, not a lock** — `qa_sweep --all` spawns up to 4
157
+ # children at once, any of which could reach a cold cache
158
+ # simultaneously and each compute the identical real boot result
159
+ # for the identical key. Writing to a PID-suffixed temp file and
160
+ # `File.rename`ing it into place (a single atomic syscall on the
161
+ # same filesystem) means a concurrent second writer's rename just
162
+ # overwrites the first with byte-identical content — never a torn
163
+ # or partially-written file a concurrent reader could observe.
164
+ #
165
+ # `HECKS_SYNTAX_BOOT_CACHE=off` — an escape hatch needing no code
166
+ # change, the same shape `Storehouse::BOOT_ROOT`'s own
167
+ # `HECKS_STOREHOUSE_ROOT` override uses, for the day this needs to
168
+ # be ruled out while debugging something else entirely.
169
+ CACHE_DIR = File.expand_path("../../../../tmp/hecks_syntax_boot_cache", __dir__).freeze
170
+
171
+ # Reports whether the cross-process disk cache is turned on.
172
+ #
173
+ # @return [Boolean] whether the cross-process disk cache is turned
174
+ # on (`HECKS_SYNTAX_BOOT_CACHE=off` turns it off)
175
+ def disk_cache_enabled? = ENV["HECKS_SYNTAX_BOOT_CACHE"] != "off"
176
+
177
+ # Reads an already-written cache entry for `chapters`, if any.
178
+ #
179
+ # @param chapters [Array<Array(String, Object)>] the `[name,
180
+ # chapter]` pairs to key the cache entry on
181
+ # @return [Hash{Symbol => Array<Hash{Symbol => String}>}, nil] the
182
+ # cached syntax table, or `nil` if the cache is disabled, no entry
183
+ # exists yet, or the entry could not be read
184
+ def read_disk_cache(chapters)
185
+ return nil unless disk_cache_enabled?
186
+
187
+ path = disk_cache_path(chapters)
188
+ return nil unless File.exist?(path)
189
+
190
+ Marshal.load(File.binread(path)) # rubocop:disable Security/MarshalLoad -- own process-local cache, never external input
191
+ rescue StandardError
192
+ nil
193
+ end
194
+
195
+ # Writes `result` to the disk cache for `chapters`, atomically.
196
+ #
197
+ # @param chapters [Array<Array(String, Object)>] the `[name,
198
+ # chapter]` pairs to key the cache entry on
199
+ # @param result [Hash{Symbol => Array<Hash{Symbol => String}>}] the
200
+ # syntax table to persist
201
+ # @return [void]
202
+ def write_disk_cache(chapters, result)
203
+ return unless disk_cache_enabled?
204
+
205
+ path = disk_cache_path(chapters)
206
+ FileUtils.mkdir_p(CACHE_DIR)
207
+ tmp_path = "#{path}.#{Process.pid}.tmp"
208
+ File.binwrite(tmp_path, Marshal.dump(result))
209
+ File.rename(tmp_path, path)
210
+ rescue StandardError
211
+ nil
212
+ end
213
+
214
+ # Builds the disk cache file path for `chapters`.
215
+ #
216
+ # @param chapters [Array<Array(String, Object)>] the `[name,
217
+ # chapter]` pairs to key the cache entry on
218
+ # @return [String] the disk cache file path for `chapters`
219
+ def disk_cache_path(chapters)
220
+ File.join(CACHE_DIR, "#{disk_cache_key(chapters)}.marshal")
221
+ end
222
+
223
+ # Builds the cache key for `chapters`.
224
+ #
225
+ # @param chapters [Array<Array(String, Object)>] the `[name,
226
+ # chapter]` pairs to key the cache entry on
227
+ # @return [String] a SHA-256 digest of the chapter names and the
228
+ # current grammar source
229
+ def disk_cache_key(chapters)
230
+ names = chapters.map { |name, _chapter| name }
231
+ Digest::SHA256.hexdigest("#{names.join(',')}:#{grammar_content_digest}")
232
+ end
233
+
234
+ # Every file `boot` can possibly read from, via `seed_chapters`
235
+ # walking every bluebook the registry holds — not just "Bluebook"
236
+ # and "Paging" (the two chapters this module's own comments name
237
+ # most often), because World/Hecksagon/any future attached chapter
238
+ # are equally eligible to carry their own `KeywordSeed`/
239
+ # `ArgumentSeed` value object. Coarser than strictly necessary (any
240
+ # one file changing invalidates every cached key, not just the
241
+ # chapter it belongs to) — deliberately, since under-covering this
242
+ # set is a correctness bug (a stale table survives a real grammar
243
+ # edit) and over-covering it is only ever a wasted cache miss.
244
+ # @return [String] a SHA-256 digest of every grammar file's own
245
+ # current content, sorted for a stable digest
246
+ def grammar_content_digest
247
+ files = (MetaValidator::GRAMMAR_FILES + MetaValidator::WORLD_GRAMMAR + MetaValidator::HECKSAGON_GRAMMAR +
248
+ Dir.glob(File.join(MetaValidator::ATTACHED_GRAMMAR_DIR, "*.bluebook"))).sort
249
+ Digest::SHA256.hexdigest(files.map { |file| File.read(file) }.join("\0"))
250
+ end
251
+
252
+ # Dispatches every seed row into a fresh "Bluebook" instance and
253
+ # reads the result back.
254
+ #
255
+ # @return [Hash{Symbol => Array<Hash{Symbol => String}>}] `:keywords`
256
+ # and `:arguments`, each an array of plain, string-valued row
257
+ # hashes (`status` included)
93
258
  def boot
94
259
  bluebook = MetaValidator.grammar_registry.bluebook("Bluebook")
95
260
  # `MetaValidator.fresh_runtime`, not a brand-new `Runtime::
96
261
  # Registry` — the grammar registry's own copy already has
97
262
  # "Bluebook" registered (`grammar_registry`'s own boot already
98
- # ran `registry.add_bluebook`) AND its adapter ports already
263
+ # ran `registry.add_bluebook`) and its adapter ports already
99
264
  # loaded ; a standalone registry would need both wired by hand.
100
- # `fresh_runtime` resets `@repositories` on the SAME registry —
265
+ # `fresh_runtime` resets `@repositories` on the same registry —
101
266
  # the identical isolation `Judge.new` already relies on for
102
267
  # every real domain it judges, proven safe by every dispatch
103
268
  # this session has ever made.
@@ -110,28 +275,42 @@ module Hecks
110
275
  read_back(runtime, bluebook)
111
276
  end
112
277
 
278
+ # Wraps `text` as a single-field value-object payload, for a
279
+ # dispatch's own `with:`.
280
+ #
113
281
  # An Integer stays an Integer — `position` is `Position`-typed
114
282
  # (`attribute :value, Integer`), so stringifying it fails the type
115
283
  # gate rather than feeding it, the same reading `Judge#v` gives.
284
+ #
285
+ # @param text [String, Integer] the scalar to wrap
286
+ # @return [Hash{Symbol => String, Integer}] `{value: text}`,
287
+ # stringified unless `text` is already an Integer
116
288
  def v(text)
117
289
  return { value: text } if text.is_a?(Integer)
118
290
 
119
291
  { value: text.to_s }
120
292
  end
121
293
 
122
- # ABSENT STAYS ABSENT. A seed row's own optional columns ("was",
294
+ # Wraps `text` as an `optional: true` command argument's own
295
+ # payload, or omits it.
296
+ #
297
+ # Absent stays absent. A seed row's own optional columns ("was",
123
298
  # "at", "named", ...) are empty strings, not nil — `Literal`/CSV-
124
299
  # shaped grammar data has no `nil` to write — so this is the one
125
300
  # place that decides "" means "not given" for the purpose of an
126
301
  # `optional: true` command argument, the same reading `Judge#v`
127
302
  # makes for the meta-domain's own dispatches.
303
+ #
304
+ # @param text [String, nil] the seed row's own column value
305
+ # @return [Hash{Symbol => String}, nil] `v(text)`, or `nil` when
306
+ # `text` is `nil` or empty
128
307
  def optional(text)
129
308
  return nil if text.nil? || text.to_s.empty?
130
309
 
131
310
  v(text)
132
311
  end
133
312
 
134
- # `Syntax.Declare`'s own `reference_to Bluebook` names the CHAPTER
313
+ # `Syntax.Declare`'s own `reference_to Bluebook` names the chapter
135
314
  # it belongs to — the same fact every other top-level aggregate's
136
315
  # own creating command carries (`ProcessManager.Declare`,
137
316
  # `Policy.Declare`, ...). This fresh runtime holds no chapter
@@ -139,25 +318,39 @@ module Hecks
139
318
  # declared first, named after the real chapter, purely to satisfy
140
319
  # the reference — its own vision/classification are never read
141
320
  # by anything this boot does.
321
+ #
322
+ # @param runtime [Runtime::Dispatcher] the fresh runtime to
323
+ # dispatch into
324
+ # @param bluebook [Bluebook::Chapter] the language's own assembled
325
+ # "Bluebook" chapter, read for its `hecks_name` and `"Syntax"`
326
+ # aggregate
327
+ # @return [void]
142
328
  def declare_syntax(runtime, bluebook)
143
329
  syntax = bluebook.aggregate("Syntax")
144
- runtime.dispatch("Bluebook::Bluebook.Declare", name: v(bluebook.hecks_name),
145
- vision: v("the language's own grammar table, " \
146
- "dispatched into itself"),
147
- classification: v("core"))
148
- runtime.dispatch("Bluebook::Syntax.Declare", bluebook: bluebook.hecks_name, name: v(syntax.hecks_name))
330
+ runtime.dispatch("Bluebook::Bluebook.Declare",
331
+ with: { name: v(bluebook.hecks_name),
332
+ vision: v("the language's own grammar table, " \
333
+ "dispatched into itself"),
334
+ classification: v("core") })
335
+ runtime.dispatch("Bluebook::Syntax.Declare",
336
+ with: { bluebook: bluebook.hecks_name, name: v(syntax.hecks_name) })
149
337
  end
150
338
 
151
- # `to: "Syntax"` NAMES THE RECORD ALREADY OPENED BY `declare_syntax`
152
- # ABOVE — an append onto an existing aggregate, not a second creation
153
- # of it. This used to smuggle `name: v("Syntax")` into the payload
154
- # instead, the pre-routing convention `Judge#appends` (the same
155
- # append shape, for `ValueObject.Member`/`ProcessManager.Handler`)
156
- # already left behind for `to:`/`with:` — carrying the receiver in
157
- # the payload made `Syntax.Keyword`'s own `command.creates?` (true:
158
- # it declares no `reference_to`) look like a fresh identity to mint,
159
- # which collided with the very "Syntax" row `declare_syntax` had
160
- # just opened.
339
+ # `to: "Syntax"` names the record already opened by `declare_syntax`
340
+ # above — an append onto an existing aggregate, not a second creation
341
+ # of it. Smuggling `name: v("Syntax")` into the payload instead — the
342
+ # pre-routing convention `Judge#appends` (the same append shape, for
343
+ # `ValueObject.Member`/`ProcessManager.Handler`) already left behind
344
+ # for `to:`/`with:` — would carry the receiver in the payload,
345
+ # making `Syntax.Keyword`'s own `command.creates?` (true: it
346
+ # declares no `reference_to`) look like a fresh identity to mint,
347
+ # colliding with the very "Syntax" row `declare_syntax` just opened.
348
+ #
349
+ # @param runtime [Runtime::Dispatcher] the fresh runtime to
350
+ # dispatch into
351
+ # @param bluebook [Bluebook::Chapter] the language's own assembled
352
+ # "Bluebook" chapter, read for its `KeywordSeed` rows
353
+ # @return [void]
161
354
  def admit_keywords(runtime, bluebook)
162
355
  all_rows(bluebook, "KeywordSeed").each_with_index do |row, index|
163
356
  runtime.dispatch("Bluebook::Syntax.Keyword", to: "Syntax",
@@ -175,6 +368,14 @@ module Hecks
175
368
  end
176
369
  end
177
370
 
371
+ # Admits every `ArgumentSeed` row as a `Syntax.Argument`, the same
372
+ # `to: "Syntax"` append shape `admit_keywords` uses one type over.
373
+ #
374
+ # @param runtime [Runtime::Dispatcher] the fresh runtime to
375
+ # dispatch into
376
+ # @param bluebook [Bluebook::Chapter] the language's own assembled
377
+ # "Bluebook" chapter, read for its `ArgumentSeed` rows
378
+ # @return [void]
178
379
  def admit_arguments(runtime, bluebook)
179
380
  all_rows(bluebook, "ArgumentSeed").each_with_index do |row, index|
180
381
  runtime.dispatch("Bluebook::Syntax.Argument", to: "Syntax",
@@ -194,13 +395,21 @@ module Hecks
194
395
  end
195
396
  end
196
397
 
197
- # EVERY AGGREGATE-LOCAL TABLE IN EVERY LOADED LANGUAGE CHAPTER.
398
+ # Every aggregate-local table in every loaded language chapter.
198
399
  # The table's presence is the registration: no chapter, aggregate,
199
400
  # filename, or context catalog is maintained here. This finds the core
200
401
  # Bluebook concepts, the sibling artifact languages (World, Hecksagon,
201
402
  # Port, Adapter, Translation), and attached sub-languages alike.
202
- # Concatenated into ONE sequence because `position` is minted from the
403
+ # Concatenated into one sequence because `position` is minted from the
203
404
  # walk index and must not collide across concepts.
405
+ #
406
+ # @param bluebook [Bluebook::Chapter] the language's own assembled
407
+ # "Bluebook" chapter, read first, before every other loaded
408
+ # chapter
409
+ # @param name [String] the local value-object name to collect rows
410
+ # of, such as `"KeywordSeed"`
411
+ # @return [Array<Hash{Symbol => String}>] every matching row, across
412
+ # every loaded chapter, in chapter order
204
413
  def all_rows(bluebook, name)
205
414
  seed_chapters(bluebook).flat_map { |chapter| rows(chapter, name) }
206
415
  end
@@ -209,6 +418,11 @@ module Hecks
209
418
  # sibling/extension. The name rejection avoids reading the same core
210
419
  # object twice without relying on object identity across fixpoint
211
420
  # assembly.
421
+ #
422
+ # @param bluebook [Bluebook::Chapter] the language's own assembled
423
+ # "Bluebook" chapter
424
+ # @return [Array<Bluebook::Chapter>] `bluebook` first, then every
425
+ # other chapter the grammar registry holds
212
426
  def seed_chapters(bluebook)
213
427
  [bluebook] + MetaValidator.grammar_registry.bluebooks.values.reject { |chapter| chapter.name == bluebook.name }
214
428
  end
@@ -217,6 +431,13 @@ module Hecks
217
431
  # every aggregate that owns a same-named local value object. Repeating
218
432
  # the value-object shape is deliberate: each concept remains readable
219
433
  # by itself, while this discovery is the only grouping mechanism.
434
+ #
435
+ # @param bluebook [Bluebook::Chapter] the chapter to search
436
+ # @param name [String] the local value-object name to collect rows
437
+ # of, such as `"KeywordSeed"`
438
+ # @return [Array<Hash{Symbol => String}>] one hash per member row,
439
+ # `[]` if no aggregate in `bluebook` owns a value object named
440
+ # `name`
220
441
  def rows(bluebook, name)
221
442
  bluebook.aggregates.flat_map do |aggregate|
222
443
  value_object = aggregate.value_objects.find { |vo| vo.hecks_name == name }
@@ -226,10 +447,18 @@ module Hecks
226
447
  end
227
448
  end
228
449
 
229
- # Reads the dispatched result back into the SAME shape `rows`
450
+ # Reads the dispatched result back into the same shape `rows`
230
451
  # above hands the seed data in as — plain hashes, string values,
231
452
  # `status` included — so `ParserTable`/`syntax_conformance_spec`
232
453
  # need not know or care that a real dispatch happened in between.
454
+ #
455
+ # @param runtime [Runtime::Dispatcher] the runtime `boot` dispatched
456
+ # every seed row into
457
+ # @param bluebook [Bluebook::Chapter] the language's own assembled
458
+ # "Bluebook" chapter, read for its `"Syntax"` aggregate
459
+ # @return [Hash{Symbol => Array<Hash{Symbol => String}>}] `:keywords`
460
+ # and `:arguments`, each an array of plain, string-valued row
461
+ # hashes (`status` included)
233
462
  def read_back(runtime, bluebook)
234
463
  syntax = bluebook.aggregate("Syntax")
235
464
  repository = runtime.registry.repository("Bluebook", syntax)
@@ -241,12 +470,23 @@ module Hecks
241
470
  }
242
471
  end
243
472
 
473
+ # Flattens one dispatched record into a plain, string-valued row.
474
+ #
475
+ # @param row [Object] one dispatched keyword or argument record,
476
+ # answering `to_h`
477
+ # @return [Hash{Symbol => String}] `row`'s own fields, each value
478
+ # unwrapped and stringified
244
479
  def stringify(row)
245
480
  row.to_h.transform_values do |cell|
246
481
  scalar(cell).to_s
247
482
  end
248
483
  end
249
484
 
485
+ # Unwraps one field's stored value to its bare scalar.
486
+ #
487
+ # @param cell [Object] one field's stored value
488
+ # @return [Object] `cell`'s sole wrapped value, if `cell` is a
489
+ # to_h-able non-String; `cell` unchanged otherwise
250
490
  def scalar(cell)
251
491
  return cell.to_h.values.first if cell.respond_to?(:to_h) && !cell.is_a?(String)
252
492
 
@@ -10,7 +10,7 @@ module Hecks
10
10
  # AdapterJudge already are — its own file, its own door, judged
11
11
  # through its own self-hosted language (translation.bluebook)
12
12
  # rather than left as plain Ruby structs nothing checks. Walks the
13
- # WHOLE built translation in one pass — the top-level Declare/
13
+ # whole built translation in one pass — the top-level Declare/
14
14
  # Retire commands, then every nested aggregate's own Declare plus
15
15
  # one Add* command per rule it carries — the same "one judge, every
16
16
  # nested record" shape WorldJudge already gives Wiring. Field names
@@ -22,6 +22,7 @@ module Hecks
22
22
  class TranslationJudge
23
23
  attr_reader :refusals
24
24
 
25
+ # @param translation [Bluebook::Translation] the built translation to judge
25
26
  def initialize(translation)
26
27
  @translation = translation
27
28
  @refusals = []
@@ -35,18 +36,18 @@ module Hecks
35
36
 
36
37
  def args(pairs) = pairs.compact
37
38
 
38
- # `Runtime::AlreadyExists` is rescued HERE but not by World/Port/
39
+ # `Runtime::AlreadyExists` is rescued here but not by World/Port/
39
40
  # Adapter's own sibling judges — found live, not by inspection.
40
- # Those three each judge a SINGLE aggregate per build (Port/Adapter)
41
+ # Those three each judge a single aggregate per build (Port/Adapter)
41
42
  # or a Hash keyed by verb (World's own settings, deduped by
42
43
  # construction), so a duplicate-identity Declare can never reach
43
44
  # their own runtime. `TranslationBuilder#aggregate` appends every
44
45
  # block to a plain Array (`@aggregates << builder.build`) with no
45
46
  # dedup — two `aggregate "Account" do ... end` blocks in the same
46
- # translation are syntactically legal and reach here for real. Left
47
- # unrescued, the second Declare's `AlreadyExists` crashed straight
48
- # through `call_translation` instead of becoming a clean refusal —
49
- # confirmed via direct dispatch before this fix.
47
+ # translation are syntactically legal and reach here for real.
48
+ # Left unrescued, the second Declare's `AlreadyExists` would crash
49
+ # straight through `call_translation` instead of becoming a clean
50
+ # refusal — confirmed via direct dispatch.
50
51
  def offer(label)
51
52
  yield
52
53
  rescue Runtime::GivenNotMet, Runtime::InvariantViolation, Runtime::TypeMismatch,
@@ -65,13 +66,13 @@ module Hecks
65
66
  send_to("Translation::Translation.Declare", t.domain,
66
67
  with: { domain: v(t.domain), from: v(t.from), to: v(t.to) })
67
68
 
68
- # `id:`, computed the SAME way — `Naming.identity([domain, from,
69
- # to])` — Translation's own identity is COMPOSITE
69
+ # `id:`, computed the same way — `Naming.identity([domain, from,
70
+ # to])` — Translation's own identity is composite
70
71
  # (`identified_by :domain, :from, :to`), the same reason
71
72
  # `Wiring.Set`'s own self-reference dispatch (world.bluebook,
72
73
  # `WorldJudge#judge_wiring`) needs a computed `id:` rather than
73
74
  # a bare field value — a single-field identity (TranslationAggregate's
74
- # own `name`) is the ONE case where the bare value itself IS the
75
+ # own `name`) is the one case where the bare value itself is the
75
76
  # id, confirmed live via direct dispatch testing, not assumed.
76
77
  translation_id = Naming.identity([t.domain, t.from, t.to])
77
78
  Array(t.retired).each do |name|
@@ -3,13 +3,14 @@ module Hecks
3
3
  module MetaValidator
4
4
  # Offers a built .world to the language that describes worlds.
5
5
  #
6
- # A world is a SIBLING of a bluebook — the same domain runs in many of
6
+ # A world is a sibling of a bluebook — the same domain runs in many of
7
7
  # them — so it is judged through its own door, against its own language
8
8
  # file, and its settings normalise the same way a mutation's fields do:
9
9
  # an open map becomes one Wiring per verb and one Setting row per value.
10
10
  class WorldJudge
11
11
  attr_reader :refusals
12
12
 
13
+ # @param world [Bluebook::World] the built world to judge
13
14
  def initialize(world)
14
15
  @world = world
15
16
  @refusals = []
@@ -51,14 +52,14 @@ module Hecks
51
52
  end
52
53
 
53
54
  def judge_wiring(domain, verb, values)
54
- # THE SAME JOIN THE LANGUAGE ITSELF DERIVES. Wiring is
55
+ # The same join the language itself derives. Wiring is
55
56
  # `identified_by do world; verb.value end` — `Wiring.Declare`
56
- # (a creating command) ignores this `id:` entirely and computes its OWN
57
+ # (a creating command) ignores this `id:` entirely and computes its own
57
58
  # from `world`/`verb`, so a locally minted "#{domain}.#{verb}" named a
58
59
  # record `Wiring.Set` could never find : the id passed here has to be
59
- # the SAME derivation, not a second guess at what it must be.
60
+ # the same derivation, not a second guess at what it must be.
60
61
  id = Naming.identity([domain, verb])
61
- # `world_ref` is the WORLD's id and goes bare, the way every reference
62
+ # `world_ref` is the world's id and goes bare, the way every reference
62
63
  # does now ; the language gives it that explicit `as:` because `world`
63
64
  # beside it is an ordinary text attribute that happens to hold the same
64
65
  # string, and stays a value object. The two look alike and are not —