hecks 0.2.0 → 0.3.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 (408) hide show
  1. checksums.yaml +5 -5
  2. data/lib/hecks/adapters/driven/claude_code.adapter +3 -0
  3. data/lib/hecks/adapters/driven/claude_code.rb +127 -0
  4. data/lib/hecks/adapters/driven/d1.adapter +12 -0
  5. data/lib/hecks/adapters/driven/d1.rb +403 -0
  6. data/lib/hecks/adapters/driven/folder.adapter +3 -0
  7. data/lib/hecks/adapters/driven/folder.rb +199 -0
  8. data/lib/hecks/adapters/driven/google_authentication.adapter +3 -0
  9. data/lib/hecks/adapters/driven/google_authentication.rb +100 -0
  10. data/lib/hecks/adapters/driven/governance_authorization.adapter +3 -0
  11. data/lib/hecks/adapters/driven/governance_authorization.rb +62 -0
  12. data/lib/hecks/adapters/driven/heki/journal.rb +51 -0
  13. data/lib/hecks/adapters/driven/heki/saga_store.rb +0 -0
  14. data/lib/hecks/adapters/driven/heki/snapshot.rb +38 -0
  15. data/lib/hecks/adapters/driven/heki.adapter +4 -0
  16. data/lib/hecks/adapters/driven/heki.rb +148 -0
  17. data/lib/hecks/adapters/driven/identity_registry.adapter +3 -0
  18. data/lib/hecks/adapters/driven/identity_registry.rb +26 -0
  19. data/lib/hecks/adapters/driven/in_memory_ordering.rb +51 -0
  20. data/lib/hecks/adapters/driven/lambda/client.rb +63 -0
  21. data/lib/hecks/adapters/driven/lambda.adapter +4 -0
  22. data/lib/hecks/adapters/driven/lambda.rb +131 -0
  23. data/lib/hecks/adapters/driven/memory.adapter +3 -0
  24. data/lib/hecks/adapters/driven/memory.rb +103 -0
  25. data/lib/hecks/adapters/driven/mock_stripe_adapter.adapter +3 -0
  26. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +28 -0
  27. data/lib/hecks/adapters/driven/postgres/codec.rb +80 -0
  28. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +168 -0
  29. data/lib/hecks/adapters/driven/postgres.adapter +5 -0
  30. data/lib/hecks/adapters/driven/postgres.rb +391 -0
  31. data/lib/hecks/adapters/driven/postgres_era/lineage/era_store.rb +171 -0
  32. data/lib/hecks/adapters/driven/postgres_era/lineage/field_cache.rb +190 -0
  33. data/lib/hecks/adapters/driven/postgres_era/lineage/head_compiler.rb +429 -0
  34. data/lib/hecks/adapters/driven/postgres_era/lineage/mint_transaction.rb +166 -0
  35. data/lib/hecks/adapters/driven/postgres_era/lineage/provisioning.rb +286 -0
  36. data/lib/hecks/adapters/driven/postgres_era/lineage/resumable_backfill.rb +168 -0
  37. data/lib/hecks/adapters/driven/postgres_era/lineage/tail_merge.rb +163 -0
  38. data/lib/hecks/adapters/driven/postgres_era/lineage/transform_installer.rb +114 -0
  39. data/lib/hecks/adapters/driven/postgres_era/lineage.rb +137 -0
  40. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/coverage_check.rb +89 -0
  41. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/era_resolver.rb +82 -0
  42. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/merge_coordinator.rb +43 -0
  43. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/minter.rb +132 -0
  44. data/lib/hecks/adapters/driven/postgres_era/lineage_manager.rb +74 -0
  45. data/lib/hecks/adapters/driven/postgres_era.adapter +6 -0
  46. data/lib/hecks/adapters/driven/postgres_era.rb +671 -0
  47. data/lib/hecks/adapters/driven/prism.adapter +3 -0
  48. data/lib/hecks/adapters/driven/prism.rb +80 -0
  49. data/lib/hecks/adapters/driven/secure_random_identity.adapter +3 -0
  50. data/lib/hecks/adapters/driven/secure_random_identity.rb +14 -0
  51. data/lib/hecks/adapters/driven/sql_query_builder.rb +221 -0
  52. data/lib/hecks/adapters/driven/sqlite/codec.rb +63 -0
  53. data/lib/hecks/adapters/driven/sqlite/projection.rb +105 -0
  54. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +180 -0
  55. data/lib/hecks/adapters/driven/sqlite.adapter +9 -0
  56. data/lib/hecks/adapters/driven/sqlite.rb +285 -0
  57. data/lib/hecks/adapters/driven/system_clock.adapter +3 -0
  58. data/lib/hecks/adapters/driven/system_clock.rb +14 -0
  59. data/lib/hecks/adapters/driven.rb +36 -0
  60. data/lib/hecks/adapters.rb +6 -0
  61. data/lib/hecks/behaviors/dsl.rb +96 -0
  62. data/lib/hecks/behaviors/expectations.rb +280 -0
  63. data/lib/hecks/behaviors/ir.rb +31 -0
  64. data/lib/hecks/behaviors/rspec.rb +42 -0
  65. data/lib/hecks/behaviors/runner.rb +96 -0
  66. data/lib/hecks/behaviors.rb +25 -0
  67. data/lib/hecks/bluebook/aggregate.rb +108 -0
  68. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +134 -0
  69. data/lib/hecks/bluebook/assembly/build.rb +48 -0
  70. data/lib/hecks/bluebook/assembly/contract.rb +112 -0
  71. data/lib/hecks/bluebook/assembly/contracts.rb +409 -0
  72. data/lib/hecks/bluebook/assembly/marks.rb +227 -0
  73. data/lib/hecks/bluebook/assembly/specializer.rb +70 -0
  74. data/lib/hecks/bluebook/assembly.rb +78 -0
  75. data/lib/hecks/bluebook/attribute.rb +96 -0
  76. data/lib/hecks/bluebook/behaviour/aggregate.rb +83 -0
  77. data/lib/hecks/bluebook/behaviour/attribute.rb +27 -0
  78. data/lib/hecks/bluebook/behaviour/chapter.rb +72 -0
  79. data/lib/hecks/bluebook/behaviour/command.rb +116 -0
  80. data/lib/hecks/bluebook/behaviour/domain_port.rb +25 -0
  81. data/lib/hecks/bluebook/behaviour/entity.rb +59 -0
  82. data/lib/hecks/bluebook/behaviour/hexagon.rb +50 -0
  83. data/lib/hecks/bluebook/behaviour/lifecycle.rb +51 -0
  84. data/lib/hecks/bluebook/behaviour/policy.rb +52 -0
  85. data/lib/hecks/bluebook/behaviour/process_manager.rb +38 -0
  86. data/lib/hecks/bluebook/behaviour/query.rb +10 -0
  87. data/lib/hecks/bluebook/behaviour/read_model.rb +29 -0
  88. data/lib/hecks/bluebook/behaviour/traits.rb +81 -0
  89. data/lib/hecks/bluebook/behaviour/value_object.rb +33 -0
  90. data/lib/hecks/bluebook/chapter.rb +68 -0
  91. data/lib/hecks/bluebook/command.rb +124 -0
  92. data/lib/hecks/bluebook/domain_port.rb +102 -0
  93. data/lib/hecks/bluebook/dsl/adapter_builder.rb +34 -0
  94. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +906 -0
  95. data/lib/hecks/bluebook/dsl/attribute_collector.rb +327 -0
  96. data/lib/hecks/bluebook/dsl/binding_proxy.rb +71 -0
  97. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +1018 -0
  98. data/lib/hecks/bluebook/dsl/command_builder.rb +626 -0
  99. data/lib/hecks/bluebook/dsl/const_shim.rb +81 -0
  100. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +79 -0
  101. data/lib/hecks/bluebook/dsl/entity_builder.rb +305 -0
  102. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +366 -0
  103. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +163 -0
  104. data/lib/hecks/bluebook/dsl/identity_declaration.rb +191 -0
  105. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +44 -0
  106. data/lib/hecks/bluebook/dsl/malformed.rb +7 -0
  107. data/lib/hecks/bluebook/dsl/policy_builder.rb +120 -0
  108. data/lib/hecks/bluebook/dsl/port_builder.rb +30 -0
  109. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +142 -0
  110. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +207 -0
  111. data/lib/hecks/bluebook/dsl/query_builder.rb +113 -0
  112. data/lib/hecks/bluebook/dsl/read_model_builder.rb +233 -0
  113. data/lib/hecks/bluebook/dsl/rule_reference.rb +172 -0
  114. data/lib/hecks/bluebook/dsl/translation_builder.rb +243 -0
  115. data/lib/hecks/bluebook/dsl/value_object_builder.rb +178 -0
  116. data/lib/hecks/bluebook/dsl/word_gate.rb +221 -0
  117. data/lib/hecks/bluebook/dsl/world_builder.rb +77 -0
  118. data/lib/hecks/bluebook/dsl.rb +45 -0
  119. data/lib/hecks/bluebook/entity.rb +103 -0
  120. data/lib/hecks/bluebook/expression/canonical_form.rb +55 -0
  121. data/lib/hecks/bluebook/expression/evaluator.rb +261 -0
  122. data/lib/hecks/bluebook/expression/projection.json +170 -0
  123. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +233 -0
  124. data/lib/hecks/bluebook/expression/resolver.rb +630 -0
  125. data/lib/hecks/bluebook/expression.rb +13 -0
  126. data/lib/hecks/bluebook/hexagon.rb +60 -0
  127. data/lib/hecks/bluebook/lifecycle.rb +42 -0
  128. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +54 -0
  129. data/lib/hecks/bluebook/meta_validator/judge.rb +559 -0
  130. data/lib/hecks/bluebook/meta_validator/plan.rb +332 -0
  131. data/lib/hecks/bluebook/meta_validator/port_judge.rb +47 -0
  132. data/lib/hecks/bluebook/meta_validator/readings.rb +350 -0
  133. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +312 -0
  134. data/lib/hecks/bluebook/meta_validator/shapes.rb +246 -0
  135. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +255 -0
  136. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +138 -0
  137. data/lib/hecks/bluebook/meta_validator/world_judge.rb +78 -0
  138. data/lib/hecks/bluebook/meta_validator.rb +452 -0
  139. data/lib/hecks/bluebook/model_check.rb +318 -0
  140. data/lib/hecks/bluebook/pattern_subset.rb +120 -0
  141. data/lib/hecks/bluebook/policy.rb +41 -0
  142. data/lib/hecks/bluebook/process_manager.rb +86 -0
  143. data/lib/hecks/bluebook/project_discovery.rb +30 -0
  144. data/lib/hecks/bluebook/project_loader.rb +40 -0
  145. data/lib/hecks/bluebook/project_register.rb +82 -0
  146. data/lib/hecks/bluebook/query.rb +61 -0
  147. data/lib/hecks/bluebook/read_model.rb +109 -0
  148. data/lib/hecks/bluebook/reference.rb +74 -0
  149. data/lib/hecks/bluebook/smoke_test.rb +166 -0
  150. data/lib/hecks/bluebook/synthesizer.rb +95 -0
  151. data/lib/hecks/bluebook/translation.rb +92 -0
  152. data/lib/hecks/bluebook/value_object.rb +58 -0
  153. data/lib/hecks/bluebook.rb +74 -0
  154. data/lib/hecks/codemod.rb +342 -0
  155. data/lib/hecks/construct.rb +71 -0
  156. data/lib/hecks/deploy/bluebook/deploy.bluebook +219 -0
  157. data/lib/hecks/deploy/bluebook/deploy.hecksagon +4 -0
  158. data/lib/hecks/deploy/oidc.json +18 -0
  159. data/lib/hecks/doc/reference.rb +389 -0
  160. data/lib/hecks/embryonaut_bluebook.rb +75 -0
  161. data/lib/hecks/facade/cli_door.rb +116 -0
  162. data/lib/hecks/facade/cli_runner.rb +190 -0
  163. data/lib/hecks/facade/command_request.rb +105 -0
  164. data/lib/hecks/facade/handle.rb +173 -0
  165. data/lib/hecks/facade/json_door.rb +154 -0
  166. data/lib/hecks/facade/surface/aggregate_door.rb +185 -0
  167. data/lib/hecks/facade/surface/chapter.rb +107 -0
  168. data/lib/hecks/facade/surface.rb +48 -0
  169. data/lib/hecks/facade.rb +44 -0
  170. data/lib/hecks/forms/app.rb +300 -0
  171. data/lib/hecks/forms/command_form_renderer.rb +113 -0
  172. data/lib/hecks/forms/examples/banking_console.bluebook +3 -0
  173. data/lib/hecks/forms/field_renderer.rb +170 -0
  174. data/lib/hecks/forms/field_shape.rb +232 -0
  175. data/lib/hecks/forms/html.rb +53 -0
  176. data/lib/hecks/forms/index_renderer.rb +35 -0
  177. data/lib/hecks/forms/page.rb +157 -0
  178. data/lib/hecks/forms/params.rb +132 -0
  179. data/lib/hecks/forms/query_form_renderer.rb +114 -0
  180. data/lib/hecks/forms/record_renderer.rb +115 -0
  181. data/lib/hecks/forms/record_table.rb +63 -0
  182. data/lib/hecks/forms/reference_options.rb +30 -0
  183. data/lib/hecks/forms/value_object_shape.rb +46 -0
  184. data/lib/hecks/forms.rb +54 -0
  185. data/lib/hecks/fqn.rb +94 -0
  186. data/lib/hecks/framework/bluebook/compliance.bluebook +1 -0
  187. data/lib/hecks/framework/bluebook/console_settings.bluebook +489 -0
  188. data/lib/hecks/framework/bluebook/framework.hecksagon +32 -0
  189. data/lib/hecks/framework/bluebook/governance.bluebook +130 -0
  190. data/lib/hecks/framework/bluebook/identity.bluebook +90 -0
  191. data/lib/hecks/framework/oidc.json +39 -0
  192. data/lib/hecks/framework.rb +90 -0
  193. data/lib/hecks/freezer.rb +67 -0
  194. data/lib/hecks/fuzzing/invalid_value_generator.rb +86 -0
  195. data/lib/hecks/fuzzing/isolated_boot.rb +92 -0
  196. data/lib/hecks/fuzzing/properties.rb +1146 -0
  197. data/lib/hecks/fuzzing/replay.rb +635 -0
  198. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +93 -0
  199. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +54 -0
  200. data/lib/hecks/fuzzing/sequence_generator/picker.rb +96 -0
  201. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +185 -0
  202. data/lib/hecks/fuzzing/sequence_generator.rb +130 -0
  203. data/lib/hecks/fuzzing/value_generator.rb +177 -0
  204. data/lib/hecks/fuzzing.rb +12 -0
  205. data/lib/hecks/grammar/evolve.rb +291 -0
  206. data/lib/hecks/grammar/expression.bluebook +417 -0
  207. data/lib/hecks/grammar/expression_operators.json +1152 -0
  208. data/lib/hecks/grammar/grammar.hecksagon +20 -0
  209. data/lib/hecks/grammar/oidc.json +69 -0
  210. data/lib/hecks/grammar/translation.bluebook +203 -0
  211. data/lib/hecks/grammar.rb +178 -0
  212. data/lib/hecks/ir.rb +126 -0
  213. data/lib/hecks/language/adapter.bluebook +116 -0
  214. data/lib/hecks/language/bluebook/aggregate.bluebook +581 -0
  215. data/lib/hecks/language/bluebook/attaches/paging.bluebook +76 -0
  216. data/lib/hecks/language/bluebook/bluebook.bluebook +256 -0
  217. data/lib/hecks/language/bluebook/bluebook.hecksagon +24 -0
  218. data/lib/hecks/language/bluebook/command.bluebook +437 -0
  219. data/lib/hecks/language/bluebook/entity.bluebook +316 -0
  220. data/lib/hecks/language/bluebook/policy.bluebook +172 -0
  221. data/lib/hecks/language/bluebook/process_manager.bluebook +264 -0
  222. data/lib/hecks/language/bluebook/projection.bluebook +267 -0
  223. data/lib/hecks/language/bluebook/query.bluebook +245 -0
  224. data/lib/hecks/language/bluebook/shape.bluebook +292 -0
  225. data/lib/hecks/language/bluebook/syntax.bluebook +447 -0
  226. data/lib/hecks/language/bluebook/vocabulary.bluebook +364 -0
  227. data/lib/hecks/language/hecksagon/adapter_binding.bluebook +51 -0
  228. data/lib/hecks/language/hecksagon/domain_port.bluebook +76 -0
  229. data/lib/hecks/language/hecksagon/hecksagon.bluebook +131 -0
  230. data/lib/hecks/language/hecksagon/port_operation.bluebook +102 -0
  231. data/lib/hecks/language/oidc.json +313 -0
  232. data/lib/hecks/language/port.bluebook +92 -0
  233. data/lib/hecks/language/translation/translation.bluebook +110 -0
  234. data/lib/hecks/language/translation/translation_aggregate.bluebook +267 -0
  235. data/lib/hecks/language/world/wiring.bluebook +62 -0
  236. data/lib/hecks/language/world/world.bluebook +84 -0
  237. data/lib/hecks/literal.rb +125 -0
  238. data/lib/hecks/naming.rb +121 -0
  239. data/lib/hecks/ports/access_control.port +4 -0
  240. data/lib/hecks/ports/access_control.rb +62 -0
  241. data/lib/hecks/ports/agent/answers.rb +104 -0
  242. data/lib/hecks/ports/agent.port +4 -0
  243. data/lib/hecks/ports/agent.rb +167 -0
  244. data/lib/hecks/ports/authentication.port +4 -0
  245. data/lib/hecks/ports/authentication.rb +50 -0
  246. data/lib/hecks/ports/authorization.port +4 -0
  247. data/lib/hecks/ports/authorization.rb +59 -0
  248. data/lib/hecks/ports/clock.port +4 -0
  249. data/lib/hecks/ports/clock.rb +62 -0
  250. data/lib/hecks/ports/extraction.port +4 -0
  251. data/lib/hecks/ports/extraction.rb +37 -0
  252. data/lib/hecks/ports/identity_assignment.port +4 -0
  253. data/lib/hecks/ports/identity_assignment.rb +45 -0
  254. data/lib/hecks/ports/identity_generation.port +4 -0
  255. data/lib/hecks/ports/identity_generation.rb +49 -0
  256. data/lib/hecks/ports/identity_resolution.port +4 -0
  257. data/lib/hecks/ports/identity_resolution.rb +40 -0
  258. data/lib/hecks/ports/loading.port +4 -0
  259. data/lib/hecks/ports/loading.rb +13 -0
  260. data/lib/hecks/ports/persistence/append_only.rb +102 -0
  261. data/lib/hecks/ports/persistence/binding_policy.rb +56 -0
  262. data/lib/hecks/ports/persistence/execution.rb +18 -0
  263. data/lib/hecks/ports/persistence/lineage.rb +277 -0
  264. data/lib/hecks/ports/persistence/null_saga_store.rb +25 -0
  265. data/lib/hecks/ports/persistence/remote_runtime.rb +42 -0
  266. data/lib/hecks/ports/persistence/repository_factory.rb +30 -0
  267. data/lib/hecks/ports/persistence.port +4 -0
  268. data/lib/hecks/ports/persistence.rb +39 -0
  269. data/lib/hecks/ports/projection.port +4 -0
  270. data/lib/hecks/ports/projection.rb +76 -0
  271. data/lib/hecks/ports/query/in_memory.rb +60 -0
  272. data/lib/hecks/ports/query/ordering.rb +41 -0
  273. data/lib/hecks/ports/query.rb +36 -0
  274. data/lib/hecks/ports.rb +27 -0
  275. data/lib/hecks/projections/diagrams.rb +413 -0
  276. data/lib/hecks/projections/ir.rb +18 -0
  277. data/lib/hecks/projections/model/deviations.rb +98 -0
  278. data/lib/hecks/projections/model.rb +145 -0
  279. data/lib/hecks/projections/oidc.rb +110 -0
  280. data/lib/hecks/projections/parser_table.rb +159 -0
  281. data/lib/hecks/projections/reference.rb +38 -0
  282. data/lib/hecks/projections/shape.rb +31 -0
  283. data/lib/hecks/projections/statements.rb +110 -0
  284. data/lib/hecks/projections/vocabulary.rb +100 -0
  285. data/lib/hecks/projections.rb +32 -0
  286. data/lib/hecks/projector/cli_projector.rb +437 -0
  287. data/lib/hecks/projector/docs_projector.rb +321 -0
  288. data/lib/hecks/projector/exporter.rb +141 -0
  289. data/lib/hecks/projector/ir_projector.rb +18 -0
  290. data/lib/hecks/projector/narrate_projector.rb +243 -0
  291. data/lib/hecks/projector/target.rb +97 -0
  292. data/lib/hecks/projector.rb +186 -0
  293. data/lib/hecks/query_ir.rb +392 -0
  294. data/lib/hecks/query_specification/common/authorization_spec.rb +9 -0
  295. data/lib/hecks/query_specification/common/comparators.rb +25 -0
  296. data/lib/hecks/query_specification/common/comparison.rb +174 -0
  297. data/lib/hecks/query_specification/common/cursor_spec.rb +9 -0
  298. data/lib/hecks/query_specification/common/dsl.rb +58 -0
  299. data/lib/hecks/query_specification/common/inspection_spec.rb +9 -0
  300. data/lib/hecks/query_specification/common/limit_spec.rb +9 -0
  301. data/lib/hecks/query_specification/common/null_policy.rb +80 -0
  302. data/lib/hecks/query_specification/common/null_semantics.rb +11 -0
  303. data/lib/hecks/query_specification/common/offset_spec.rb +9 -0
  304. data/lib/hecks/query_specification/common/options.rb +32 -0
  305. data/lib/hecks/query_specification/common/order_by.rb +9 -0
  306. data/lib/hecks/query_specification/common/specification.rb +10 -0
  307. data/lib/hecks/query_specification/common/where_clause.rb +9 -0
  308. data/lib/hecks/query_specification/field_path.rb +89 -0
  309. data/lib/hecks/query_specification/hop_path.rb +130 -0
  310. data/lib/hecks/query_specification/read_model/specification.rb +18 -0
  311. data/lib/hecks/query_specification.rb +14 -0
  312. data/lib/hecks/rendering.rb +48 -0
  313. data/lib/hecks/router/namespace_installer.rb +157 -0
  314. data/lib/hecks/router.rb +70 -0
  315. data/lib/hecks/runtime/caller.rb +50 -0
  316. data/lib/hecks/runtime/capability_graph.rb +44 -0
  317. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +124 -0
  318. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +248 -0
  319. data/lib/hecks/runtime/command_interpreter.rb +413 -0
  320. data/lib/hecks/runtime/command_rules/admissibility.rb +307 -0
  321. data/lib/hecks/runtime/command_rules/arithmetic.rb +254 -0
  322. data/lib/hecks/runtime/command_rules/authorization.rb +64 -0
  323. data/lib/hecks/runtime/command_rules/emission.rb +34 -0
  324. data/lib/hecks/runtime/command_rules/references.rb +181 -0
  325. data/lib/hecks/runtime/command_rules.rb +28 -0
  326. data/lib/hecks/runtime/dependency_planning.rb +231 -0
  327. data/lib/hecks/runtime/dispatcher.rb +286 -0
  328. data/lib/hecks/runtime/entity_element.rb +253 -0
  329. data/lib/hecks/runtime/entity_interpreter.rb +228 -0
  330. data/lib/hecks/runtime/era_check.rb +136 -0
  331. data/lib/hecks/runtime/era_guard/shape_diff.rb +128 -0
  332. data/lib/hecks/runtime/era_guard.rb +193 -0
  333. data/lib/hecks/runtime/era_tamper.rb +61 -0
  334. data/lib/hecks/runtime/errors.rb +100 -0
  335. data/lib/hecks/runtime/event.rb +51 -0
  336. data/lib/hecks/runtime/identity.rb +128 -0
  337. data/lib/hecks/runtime/instance.rb +120 -0
  338. data/lib/hecks/runtime/interpreting.rb +69 -0
  339. data/lib/hecks/runtime/loader.rb +143 -0
  340. data/lib/hecks/runtime/policy_interpreter.rb +366 -0
  341. data/lib/hecks/runtime/port_operation_interpreter.rb +210 -0
  342. data/lib/hecks/runtime/query_interpreter.rb +254 -0
  343. data/lib/hecks/runtime/reaction_invocation.rb +245 -0
  344. data/lib/hecks/runtime/read_model_interpreter.rb +266 -0
  345. data/lib/hecks/runtime/rebuild_sweep.rb +74 -0
  346. data/lib/hecks/runtime/reference_hop.rb +99 -0
  347. data/lib/hecks/runtime/refusal_wording.rb +124 -0
  348. data/lib/hecks/runtime/registry/saga_persistence.rb +70 -0
  349. data/lib/hecks/runtime/registry/verification.rb +158 -0
  350. data/lib/hecks/runtime/registry.rb +218 -0
  351. data/lib/hecks/runtime/remote_dispatcher.rb +143 -0
  352. data/lib/hecks/runtime/routing.rb +96 -0
  353. data/lib/hecks/runtime/saga_interpreter/correlation.rb +97 -0
  354. data/lib/hecks/runtime/saga_interpreter.rb +301 -0
  355. data/lib/hecks/runtime/storage_shape.rb +120 -0
  356. data/lib/hecks/runtime/tenant_check.rb +84 -0
  357. data/lib/hecks/runtime/tenant_scope.rb +55 -0
  358. data/lib/hecks/runtime/value/admission.rb +112 -0
  359. data/lib/hecks/runtime/value/coercion.rb +500 -0
  360. data/lib/hecks/runtime/value/invariant_violation.rb +5 -0
  361. data/lib/hecks/runtime/value.rb +125 -0
  362. data/lib/hecks/runtime.rb +95 -0
  363. data/lib/hecks/translation/audit/approval_digest.rb +31 -0
  364. data/lib/hecks/translation/audit/layer_one.rb +37 -0
  365. data/lib/hecks/translation/audit/layer_two.rb +77 -0
  366. data/lib/hecks/translation/audit/unfed_report.rb +42 -0
  367. data/lib/hecks/translation/audit.rb +70 -0
  368. data/lib/hecks/translation/reattest.rb +72 -0
  369. data/lib/hecks/translation/rule_compiler.rb +120 -0
  370. data/lib/hecks/translation/scaffold/differ.rb +183 -0
  371. data/lib/hecks/translation/scaffold/renderer.rb +41 -0
  372. data/lib/hecks/translation/scaffold/writer.rb +28 -0
  373. data/lib/hecks/translation/scaffold.rb +30 -0
  374. data/lib/hecks/translation.rb +11 -0
  375. data/lib/hecks/version.rb +16 -0
  376. data/lib/hecks/vocabulary.rb +213 -0
  377. data/lib/hecks.rb +118 -6
  378. metadata +401 -222
  379. data/bin/hecks +0 -7
  380. data/bin/hecks-package +0 -65
  381. data/bin/hecks_console +0 -12
  382. data/bin/hecks_serverless +0 -6
  383. data/lib/cli/build.rb +0 -14
  384. data/lib/cli/command_runner.rb +0 -28
  385. data/lib/cli/console.rb +0 -10
  386. data/lib/cli/generate.rb +0 -37
  387. data/lib/cli/hecks-cli.rb +0 -27
  388. data/lib/cli/test.rb +0 -57
  389. data/lib/console/commands.rb +0 -8
  390. data/lib/console/hecks-console.rb +0 -1
  391. data/lib/packager/README.md +0 -0
  392. data/lib/packager/app_runner.rb +0 -21
  393. data/lib/packager/args.rb +0 -26
  394. data/lib/packager/compatibility/fixnum.rb +0 -6
  395. data/lib/packager/hecks.rb +0 -39
  396. data/lib/packager/query_runner.rb +0 -21
  397. data/lib/packager/resources/Dockerfile +0 -11
  398. data/lib/packager/resources/app_binary +0 -7
  399. data/lib/packager/resources/bundle_config +0 -3
  400. data/lib/packager/resources/traveling-ruby-20150715-2.2.2-linux-x86_64.tar.gz +0 -0
  401. data/lib/packager/resources/traveling-ruby-20150715-2.2.2-osx.tar.gz +0 -0
  402. data/lib/serverless/Domain +0 -32
  403. data/lib/serverless/cli.rb +0 -75
  404. data/lib/serverless/resources/command_name.js +0 -5
  405. data/lib/serverless/resources/environment.js +0 -7
  406. data/lib/serverless/resources/handler.js.tt +0 -28
  407. data/lib/serverless/resources/run_binary.js +0 -22
  408. data/lib/serverless/resources/serverless.yml +0 -20
@@ -0,0 +1,78 @@
1
+ module Hecks
2
+ module Bluebook
3
+ module MetaValidator
4
+ # Offers a built .world to the language that describes worlds.
5
+ #
6
+ # A world is a SIBLING of a bluebook — the same domain runs in many of
7
+ # them — so it is judged through its own door, against its own language
8
+ # file, and its settings normalise the same way a mutation's fields do:
9
+ # an open map becomes one Wiring per verb and one Setting row per value.
10
+ class WorldJudge
11
+ attr_reader :refusals
12
+
13
+ def initialize(world)
14
+ @world = world
15
+ @refusals = []
16
+ @runtime = MetaValidator.fresh_runtime
17
+ judge!
18
+ end
19
+
20
+ private
21
+
22
+ def v(text) = text.nil? ? nil : { value: text.to_s }
23
+
24
+ def args(pairs) = pairs.reject { |_, value| value.nil? }
25
+
26
+ def offer(label)
27
+ yield
28
+ rescue Runtime::GivenNotMet, Runtime::InvariantViolation,
29
+ Runtime::TypeMismatch, Runtime::NotFound => e
30
+ @refusals << "#{label}: #{e.message}"
31
+ rescue Runtime::UnknownVerb
32
+ nil
33
+ end
34
+
35
+ def send_to(verb, label, to: nil, **payload)
36
+ offer(label) { @runtime.dispatch(verb, to: to, with: args(payload)) }
37
+ end
38
+
39
+ def judge!
40
+ domain = @world.domain
41
+ send_to("World::World.Declare", domain, domain: v(domain),
42
+ realm: v(@world.realm), latest: v(@world.latest))
43
+
44
+ Hash(@world.settings).each do |verb, values|
45
+ # the DSL records each binding twice — once by verb, once by
46
+ # "verb:adapter" — so only the plain verb is offered
47
+ next if verb.to_s.include?(":")
48
+
49
+ judge_wiring(domain, verb, values)
50
+ end
51
+ end
52
+
53
+ def judge_wiring(domain, verb, values)
54
+ # THE SAME JOIN THE LANGUAGE ITSELF DERIVES. Wiring is
55
+ # `identified_by do world; verb.value end` — `Wiring.Declare`
56
+ # (a creating command) ignores this `id:` entirely and computes its OWN
57
+ # from `world`/`verb`, so a locally minted "#{domain}.#{verb}" named a
58
+ # 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
+ id = Naming.identity([domain, verb])
61
+ # `world_ref` is the WORLD's id and goes bare, the way every reference
62
+ # does now ; the language gives it that explicit `as:` because `world`
63
+ # beside it is an ordinary text attribute that happens to hold the same
64
+ # string, and stays a value object. The two look alike and are not —
65
+ # which is exactly why the judge asks the type rather than the name.
66
+ send_to("World::Wiring.Declare", id, world_ref: v(domain),
67
+ world: v(domain), verb: v(verb), adapter: v(Hash(values)[:adapter]))
68
+
69
+ Hash(values).each do |key, value|
70
+ next if key.to_sym == :adapter
71
+
72
+ send_to("World::Wiring.Set", id, to: id, key: v(key), value: v(value))
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,452 @@
1
+ require "digest"
2
+ require "json"
3
+
4
+ module Hecks
5
+ module Bluebook
6
+ # Judges a bluebook by DISPATCHING it into the language declared in itself.
7
+ #
8
+ # `lib/hecks/language/bluebook/` declares what a bluebook IS —
9
+ # Chapter, Root, Verb, Shape, Ask, Piece, and the rest, split across files
10
+ # by domain concept and merged into one chapter at load time (see
11
+ # GRAMMAR_FILES below) — and carries the language's rules as `given` and
12
+ # `invariant` rather than as `raise Malformed` scattered across seven
13
+ # builder files. This replays a built IR into that domain and turns any
14
+ # refusal into a Malformed, so the meta-domain is what actually judges
15
+ # rather than a description sitting beside the code.
16
+ #
17
+ # Delete language/bluebook/ and validation stops. That is the whole
18
+ # point : a self-description that only describes is indistinguishable from
19
+ # enforcement, and the first version of this file was deleted for exactly
20
+ # that reason.
21
+ #
22
+ # The meta-domain is loaded ONCE and its registry reused ; each bluebook is
23
+ # judged in a fresh in-memory store so no domain can see another's records.
24
+ module MetaValidator
25
+ # THE FOLDER IS THE CHAPTER. Files are grouped by the domain concept they
26
+ # describe and every one reopens the same `Hecks.bluebook "Bluebook"`.
27
+ # `BluebookBuilder.build` keeps one builder open per chapter name across
28
+ # calls, so the sorted folder accumulates one domain. Adding or renaming a
29
+ # concept file requires no second catalog here; deterministic filename
30
+ # order is the source order exported to IR.
31
+ GRAMMAR_DIR = File.expand_path("../language/bluebook", __dir__).freeze
32
+ GRAMMAR_FILES = Dir.glob(File.join(GRAMMAR_DIR, "*.bluebook")).sort.freeze
33
+ # Sibling artifact languages use the same folder-is-the-chapter rule.
34
+ # Their arrays are discovered, sorted source sets—not filename catalogs.
35
+ WORLD_GRAMMAR = Dir.glob(File.expand_path("../language/world/*.bluebook", __dir__)).sort.freeze
36
+ HECKSAGON_GRAMMAR = Dir.glob(File.expand_path("../language/hecksagon/*.bluebook", __dir__)).sort.freeze
37
+ # so is a port — whole-project table-unification survey, item #13's
38
+ # remaining builders. Backs the new PortJudge door the same way
39
+ # WORLD_GRAMMAR backs WorldJudge.
40
+ PORT_GRAMMAR = File.expand_path("../language/port.bluebook", __dir__).freeze
41
+ # so is an adapter — same reasoning, one file over. Backs AdapterJudge.
42
+ ADAPTER_GRAMMAR = File.expand_path("../language/adapter.bluebook", __dir__).freeze
43
+ # so is a translation — same reasoning, one file over. Backs
44
+ # TranslationJudge.
45
+ TRANSLATION_GRAMMAR = Dir.glob(File.expand_path("../language/translation/*.bluebook", __dir__)).sort.freeze
46
+
47
+ # ADR 0026's OWN SEAM: THE CORE DOES NOT NAME ITS EXTENSION POINTS.
48
+ #
49
+ # A sub-language chapter (Paging, so far the only one) is an ORDINARY
50
+ # bluebook — declared with the same `aggregate`/`value_object`/
51
+ # `attaches_to` words every domain has, judged through the language
52
+ # the normal way, not bootstrapped raw the way GRAMMAR_FILES is. What
53
+ # makes it special is only where it LIVES: any file in this directory
54
+ # is discovered and loaded here, by the directory's own existence,
55
+ # never by a name this file would have to know. Add a chapter here
56
+ # and it is attached ; nothing in this file changes.
57
+ ATTACHED_GRAMMAR_DIR = File.expand_path("../language/bluebook/attaches", __dir__).freeze
58
+
59
+ # The chapters that ARE the language — loaded raw during bootstrap, then
60
+ # judged through themselves and replaced by their own assembled graphs
61
+ # (see grammar_registry). Each is named after its file : Bluebook describes
62
+ # bluebooks (language/bluebook/) ; World describes worlds (world.bluebook),
63
+ # and backs the WorldJudge door ; Hecksagon describes hecksagons
64
+ # (hecksagon.bluebook) — declared for the same self-description reasons as
65
+ # World, but WITHOUT a judge door of its own : nothing dispatches a real
66
+ # .hecksagon file through it yet, so HecksagonBuilder's own behavior is
67
+ # unchanged. What this buys is what syntax.bluebook needed — a real shape
68
+ # `subscribe`'s `fills: "subscriptions"` can point at — not new validation
69
+ # on top of the corpus's existing .hecksagon files.
70
+ LANGUAGE_CHAPTERS = %w[Bluebook World Hecksagon].freeze
71
+
72
+ # The meta-domain is itself a bluebook. Judging it while loading it would
73
+ # recurse, so the load path marks the bootstrap and skips — but the skip
74
+ # is only the FIRST pass. Once every grammar file is loaded and merged,
75
+ # grammar_registry judges the language through itself and keeps the
76
+ # assembled result (the fixpoint, made load-bearing).
77
+ def self.bootstrapping? = @bootstrapping
78
+
79
+ # A CHAPTER MAY BE SPLIT ACROSS FILES, so it cannot be judged until
80
+ # every file has been read.
81
+ #
82
+ # `BluebookBuilder.build` already MERGES — `registry.bluebook_builder
83
+ # (name)` memoises one builder per chapter name, so nine files each
84
+ # saying `Hecks.bluebook "Bluebook"` accumulate into one. What it
85
+ # also does is call `MetaValidator.call` once PER FILE, judging a
86
+ # chapter that is still eight files short: `Aggregate`'s reference
87
+ # to `Bluebook` dangles because `Bluebook` has not been declared
88
+ # yet, and the load dies.
89
+ #
90
+ # The language's own grammar has always needed this and got it
91
+ # privately, through `@bootstrapping` (see load_grammar_into) —
92
+ # which is exactly why the language could not boot the way the
93
+ # domains it describes boot. This is that same two-phase load,
94
+ # available to anything: `Folder#load_domain` reads every
95
+ # `*.bluebook` inside `defer`, then judges each composed chapter
96
+ # once, before hecksagons and worlds load (DOMAIN_ORDER already
97
+ # puts every chapter ahead of those).
98
+ #
99
+ # Only chapters DECLARED INSIDE the window are judged afterwards.
100
+ # Re-judging one already assembled — a framework member pulled in
101
+ # earlier by `uses_framework`, say — would re-run Assembly and hand
102
+ # out a second set of classes for a graph something already holds.
103
+ def self.deferring? = @deferring
104
+
105
+ def self.defer
106
+ previous = @deferring
107
+ @deferring = true
108
+ yield
109
+ ensure
110
+ @deferring = previous
111
+ end
112
+
113
+ def self.deferred_chapters = @deferred_chapters ||= []
114
+
115
+ def self.judge_deferred!(registry)
116
+ pending = deferred_chapters.uniq
117
+ @deferred_chapters = []
118
+ return unless registry
119
+
120
+ pending.each do |name|
121
+ chapter = registry.bluebook(name)
122
+ next unless chapter
123
+
124
+ # A bare chapter-given left PENDING by any file of this
125
+ # chapter (`AggregateBuilder#pending_chapter_given`) resolves
126
+ # first — before anything below reads a `Given`'s fields.
127
+ # `registry.bluebook_builder(name)` is the SAME instance every
128
+ # one of this chapter's own files built onto (`#self.build`'s
129
+ # own comment); it is guaranteed already open here, since it
130
+ # is what produced `chapter` in the first place — the block is
131
+ # dead code, never actually invoked.
132
+ builder = registry.bluebook_builder(name) { raise "internal: no open builder for #{name}" }
133
+ builder.resolve_pending_chapter_givens!
134
+
135
+ # `BluebookBuilder#build` skipped its own whole-chapter battery
136
+ # (hops, projected fields, correlation keys, event shapes,
137
+ # `with:` projections) for every file of THIS chapter while
138
+ # `deferring?` was true, the same reason `call` below queued
139
+ # instead of judging — each of those checks needs every file
140
+ # loaded first (see `BluebookBuilder.validate_assembled!`'s own
141
+ # comment). `chapter` here is exactly that: whatever the LAST
142
+ # file's own `add_bluebook` left in the registry, which by now
143
+ # holds every aggregate/policy/process_manager the whole
144
+ # chapter declares. Run once, here, instead of once per file.
145
+ DSL::BluebookBuilder.validate_assembled!(chapter)
146
+ registry.add_bluebook(call(chapter))
147
+ end
148
+ end
149
+
150
+ def self.disabled? = ENV["HECKS_META_VALIDATION"] == "off"
151
+
152
+ # ADR 0025's own prerequisite (docs/dsl-work-slices.md, S0a): a word
153
+ # a later slice removes from the LIVE grammar must still parse
154
+ # FROZEN ERA TEXT — `EraGuard.shadow_parse` (runtime/era_guard.rb)
155
+ # is a plain `Kernel.eval` of stored source, run at boot, at mint,
156
+ # and during tamper detection, against whatever grammar is live
157
+ # TODAY, not whatever grammar was live when that text was written.
158
+ # Judging it again here would refuse history the day a spelling it
159
+ # used is removed — proved with a rule that already lives ONLY in
160
+ # the meta-domain, never duplicated as a builder's own `raise
161
+ # Malformed` (`vision`'s own comment: "moved to the language").
162
+ #
163
+ # Mirrors `defer`'s own stack-restore shape, not `disabled?`'s bare
164
+ # env toggle — this must never leak past the one shadow-parse call
165
+ # that set it, the same reason `ConstShim.with`/`.active?`
166
+ # (bluebook/dsl/const_shim.rb) restores in an `ensure` rather than
167
+ # being flipped and left.
168
+ def self.shadow_parsing? = @shadow_parsing
169
+
170
+ def self.while_shadow_parsing
171
+ previous = @shadow_parsing
172
+ @shadow_parsing = true
173
+ yield
174
+ ensure
175
+ @shadow_parsing = previous
176
+ end
177
+
178
+ # The same bluebook judged twice gets the same verdict, and a suite reloads
179
+ # its fixtures constantly — banking alone is ~200 dispatches per build.
180
+ # Keyed on the IR itself, so a CHANGED bluebook is always re-judged.
181
+ def self.verdicts = @verdicts ||= {}
182
+
183
+ # A world is not a bluebook, so it gets its own door. Same judge, same
184
+ # meta-domain registry — a different artifact and a different language file.
185
+ def self.call_world(world)
186
+ return world if disabled? || bootstrapping? || shadow_parsing?
187
+
188
+ key = Digest::SHA256.hexdigest(JSON.generate([world.domain, world.realm, world.latest, world.settings]))
189
+ refusals = verdicts[key] ||= WorldJudge.new(world).refusals
190
+ return world if refusals.empty?
191
+
192
+ raise DSL::Malformed,
193
+ "#{world.domain}'s world is not well formed; #{refusals.join('; ')}"
194
+ end
195
+
196
+ # A port is not a bluebook either — same door shape as call_world,
197
+ # one artifact over. Whole-project table-unification survey, item
198
+ # #13's remaining builders.
199
+ def self.call_port(port)
200
+ return port if disabled? || bootstrapping? || shadow_parsing?
201
+
202
+ key = Digest::SHA256.hexdigest(JSON.generate([port.name, port.verb, port.signal]))
203
+ refusals = verdicts[key] ||= PortJudge.new(port).refusals
204
+ return port if refusals.empty?
205
+
206
+ raise DSL::Malformed,
207
+ "#{port.name}'s port is not well formed; #{refusals.join('; ')}"
208
+ end
209
+
210
+ # An adapter is not a bluebook either — same door shape, one more
211
+ # artifact over. Whole-project table-unification survey, item
212
+ # #13's remaining builders.
213
+ def self.call_adapter(adapter)
214
+ return adapter if disabled? || bootstrapping? || shadow_parsing?
215
+
216
+ key = Digest::SHA256.hexdigest(JSON.generate([adapter.name, adapter.port, adapter.fields, adapter.secrets]))
217
+ refusals = verdicts[key] ||= AdapterJudge.new(adapter).refusals
218
+ return adapter if refusals.empty?
219
+
220
+ raise DSL::Malformed,
221
+ "#{adapter.name}'s adapter is not well formed; #{refusals.join('; ')}"
222
+ end
223
+
224
+ # A translation is not a bluebook either — same door shape, one more
225
+ # artifact over (a `translations/*.bluebook` edge — `data_translation`'s
226
+ # own real, established convention; there is no separate extension).
227
+ # TranslationJudge walks the WHOLE built translation (every nested
228
+ # aggregate's own rule table) in one pass — only
229
+ # TranslationBuilder's own top-level `build` calls this;
230
+ # TranslationAggregateBuilder#build stays a plain struct constructor,
231
+ # the same way `WorldJudge` judges every `Wiring` a `.world` declares
232
+ # in ONE pass over `World::World.Declare`'s own caller, not from a
233
+ # separate door per binding. Whole-project table-unification survey,
234
+ # item #13's remaining builders.
235
+ def self.call_translation(translation)
236
+ return translation if disabled? || bootstrapping? || shadow_parsing?
237
+
238
+ # `Translation`/`TranslationAggregate` are plain classes (attr_
239
+ # reader, not Struct — lib/hecks/bluebook/translation.rb),
240
+ # so neither carries a `.to_h`; `.inspect` is JSON-safe (a plain
241
+ # string) and, unlike a Struct's own memoization-friendly `.to_h`,
242
+ # doesn't need one — the small correctness cost is that its
243
+ # embedded object-id prefix makes two structurally-identical
244
+ # translations hash differently, so `verdicts` simply never
245
+ # cache-hits here (an efficiency loss, not a correctness one —
246
+ # judged fresh every time instead of memoized).
247
+ key = Digest::SHA256.hexdigest(translation.inspect)
248
+ refusals = verdicts[key] ||= TranslationJudge.new(translation).refusals
249
+ return translation if refusals.empty?
250
+
251
+ raise DSL::Malformed,
252
+ "#{translation.domain}'s translation is not well formed; #{refusals.join('; ')}"
253
+ end
254
+
255
+ # THE LANGUAGE HANDS THE GRAPH BACK.
256
+ #
257
+ # This used to return the bluebook it was given — dispatch every declaration
258
+ # in, collect refusals, throw the records away — which is all JUDGING needs
259
+ # and exactly why the language could only validate. It returns what the
260
+ # meta-domain HOLDS instead, assembled into the graph the runtime runs. The
261
+ # builder's own object graph now exists only to be dispatched; nothing keeps
262
+ # it.
263
+ #
264
+ # `Hecks.bluebook` registers whatever comes back from here, so this one line
265
+ # is the difference between a language that checks a domain and a language
266
+ # that is the source of one.
267
+ #
268
+ # What is CACHED is the declarations, not the graph. A hash carries no Ruby
269
+ # classes, so a second load of the same chapter re-assembles fresh ones —
270
+ # which is the behaviour `Namespace.install` and `spec/construct_spec` both
271
+ # expect. Caching the graph would hand two boots the same classes.
272
+ # THE LANGUAGE IS THE SOURCE. This is the line that makes it one.
273
+ #
274
+ # `Hecks.bluebook` registers whatever comes back from here, so returning the
275
+ # assembled graph rather than the bluebook it was handed is the whole swap: the
276
+ # runtime runs what the meta-domain HOLDS. The builder's own graph exists only
277
+ # to be dispatched in ; nothing keeps it.
278
+ #
279
+ # It stayed unlanded for one wrong belief, worth naming because it looked so
280
+ # much like a wall: that the language may only hold what `to_h` carries.
281
+ # `ReadModel#to_h` omitted a read model's filters until 2026-08-11, so
282
+ # read-model filtering seemed impossible to read back —
283
+ # and hoisted policies lost which head declared them for the same reason.
284
+ # But `to_h` is a PROJECTION and the language is the
285
+ # SOURCE. They must agree about everything to_h spells ; they need not be the
286
+ # same size. Both were held even before the wire format carried them, as
287
+ # declarations the wire format didn't yet see.
288
+ #
289
+ # UPDATE, 2026-08-11: the wire format DID move, on purpose, for a reason
290
+ # unrelated to this file — a Rust-codegen task needed `wheres`/
291
+ # `order_by`/`limit` on the wire to compile a read model's real declared
292
+ # filtering, and the boundary described above was never load-bearing for
293
+ # THIS mechanism (`option_rows`/`filter_options` in meta_validator/
294
+ # readings.rb read `node.wheres`/`node.order_by`/`node.limit` off the
295
+ # live object directly, never off `to_h`), so extending `to_h` changed
296
+ # nothing here. `ReadModel#to_h` now spells all three explicitly, the
297
+ # same mechanism `Query#to_h` already used — purely additive, still
298
+ # agreeing with the language about everything it spells.
299
+ #
300
+ # What is CACHED is the declarations, not the graph. A hash carries no Ruby
301
+ # classes, so a second load of the same chapter assembles fresh ones — which is
302
+ # what `Namespace.install` and `spec/construct_spec` both expect. Caching the
303
+ # graph would hand two boots the same classes.
304
+ def self.call(bluebook)
305
+ return bluebook if disabled? || bootstrapping? || shadow_parsing?
306
+
307
+ if deferring?
308
+ deferred_chapters << bluebook.hecks_name
309
+ return bluebook
310
+ end
311
+
312
+ key = Digest::SHA256.hexdigest(JSON.generate(bluebook.to_h))
313
+ held = verdicts[key] ||= hold(bluebook)
314
+
315
+ unless held[:refusals].empty?
316
+ raise DSL::Malformed,
317
+ "#{bluebook.hecks_name} is not a well-formed bluebook; #{held[:refusals].join('; ')}"
318
+ end
319
+
320
+ Assembly.call(held[:declaration])
321
+ end
322
+
323
+ # Dispatch it in and READ IT BACK. A refused chapter has no declarations to
324
+ # read — the records are half-written by definition — so it carries refusals
325
+ # and nothing else.
326
+ def self.hold(bluebook)
327
+ judge = Judge.new(bluebook)
328
+ return { refusals: judge.refusals } unless judge.refusals.empty?
329
+
330
+ { refusals: [], declaration: Reconstruction.of(judge.runtime, bluebook.hecks_name) }
331
+ end
332
+
333
+ def self.grammar_registry
334
+ @grammar_registry ||= begin
335
+ registry = load_grammar_into(Runtime::Registry.new)
336
+ # Assigned BEFORE the fixpoint judge below: judging re-enters
337
+ # grammar_registry through fresh_runtime (judge.rb) and Plan.for
338
+ # (judge.rb, reconstruction.rb) — a bare ||= would still be nil
339
+ # while its right-hand side evaluates, and recurse forever. That
340
+ # reentrancy window is real: a caller landing here BEFORE the
341
+ # fixpoint/attach below have run sees the SAME registry object,
342
+ # correctly, but one still missing the attached chapters (Paging's
343
+ # `attaches_to` among them) — see grammar_registry_ready? below.
344
+ @grammar_registry = registry
345
+ # THE FIXPOINT MADE LOAD-BEARING. The bootstrap loaded the language
346
+ # raw ; now the language judges itself, its records are read back,
347
+ # and the ASSEMBLED graph replaces the raw one — so every bluebook
348
+ # judged from here on is judged by the language the language itself
349
+ # produced. Outside load_grammar_into on purpose : its ensure clears
350
+ # @bootstrapping, and call() must see bootstrapping? == false to do
351
+ # anything at all.
352
+ LANGUAGE_CHAPTERS.each { |name| registry.add_bluebook(call(registry.bluebook(name))) }
353
+ load_attached_grammar_into(registry)
354
+ # Stamped LAST, keyed by this registry's own identity rather than
355
+ # a bare boolean — a manual reset (fixpoint_spec.rb's own
356
+ # `@grammar_registry = nil`) makes @grammar_registry not equal
357
+ # this object_id again until a fresh build finishes, so a stale
358
+ # "ready" from the PREVIOUS cycle can never leak into the next.
359
+ @grammar_ready_for = registry.object_id
360
+ registry
361
+ end
362
+ end
363
+
364
+ # A REENTRANT CALL DURING THE FIXPOINT/ATTACH WINDOW ABOVE gets a
365
+ # real, correctly-mutating registry object back — no infinite loop,
366
+ # no wrong data for THAT caller's own purposes. But anything that
367
+ # MEMOIZES a snapshot derived from it must not lock that snapshot
368
+ # in forever : this is one signal such a cache can check. Found
369
+ # live — SyntaxBoot.call had cached a Query keyword list missing
370
+ # every Paging-attached word (limit/offset/cursor/nulls) because
371
+ # something called it inside this exact window.
372
+ #
373
+ # SyntaxBoot.call NO LONGER USES THIS. Gating its cache on "the
374
+ # whole registry is finished" meant nothing was cached for the
375
+ # entire window, and the window is not narrow — every word routed
376
+ # through `word_gate_dispatch` while the language judged itself
377
+ # re-ran the ~284-dispatch syntax boot (42 times, 32 seconds, per
378
+ # process — see SyntaxBoot.call's own comment). It keys its cache
379
+ # on the registry's chapter set instead, which is the actual input
380
+ # the snapshot is a function of. Kept here for any other derived
381
+ # cache that genuinely needs "is the build finished" rather than
382
+ # "have my inputs changed".
383
+ def self.grammar_registry_ready?
384
+ @grammar_registry && @grammar_ready_for == @grammar_registry.object_id
385
+ end
386
+
387
+ # ATTACHED CHAPTERS LOAD AFTER THE FIXPOINT, NOT DURING BOOTSTRAP —
388
+ # they are declared IN the language the language just finished
389
+ # judging itself through, so they are ordinary bluebooks, judged the
390
+ # ordinary way (`Hecks.bluebook` → `BluebookBuilder#build` →
391
+ # `MetaValidator.call`, `bootstrapping?` already false). A directory
392
+ # with nothing in it loads nothing ; this is a no-op until a chapter
393
+ # is added there.
394
+ def self.load_attached_grammar_into(registry)
395
+ Hecks.with_registry(registry) do
396
+ Dir.glob(File.join(ATTACHED_GRAMMAR_DIR, "*.bluebook")).sort.each { |file| Kernel.load(file) }
397
+ end
398
+ end
399
+
400
+ # THE ONE PLACE THE GRAMMAR'S OWN BOOT SEQUENCE IS SPELLED — ports, the
401
+ # memory/prism adapters, the (now nine-file) chapter itself, then the
402
+ # sibling world grammar. `grammar_registry` uses this for its memoised
403
+ # singleton ; anything that needs an ISOLATED registry (a spec wanting a
404
+ # fresh store per example, say) calls this directly instead of hand-
405
+ # copying the sequence.
406
+ #
407
+ # THE BOOTSTRAP GUARD LIVES HERE, not just around the singleton. Splitting
408
+ # the chapter into several files means each file's own `Hecks.bluebook
409
+ # "Bluebook"` call now runs `BluebookBuilder#build` once per file — and
410
+ # `MetaValidator.call` judges whatever it is handed unless `bootstrapping?`
411
+ # is true. A caller that loaded the grammar files by hand into its own
412
+ # registry, without this guard, would get each file DISPATCHED AND JUDGED
413
+ # ALONE the moment it loaded — and a lone file like `aggregate.bluebook`
414
+ # refuses immediately, since `Aggregate.Attribute` references `ValueObject`
415
+ # and `Aggregate.Holds` references `Entity`, both declared in later files.
416
+ # Every caller of the grammar must go through here for exactly that reason.
417
+ def self.load_grammar_into(registry)
418
+ @bootstrapping = true
419
+ Hecks.with_registry(registry) do
420
+ Kernel.load(File.expand_path("../ports/persistence.port", __dir__))
421
+ Kernel.load(File.expand_path("../ports/extraction.port", __dir__))
422
+ Kernel.load(File.expand_path("../adapters/driven/memory.adapter", __dir__))
423
+ Kernel.load(File.expand_path("../adapters/driven/prism.adapter", __dir__))
424
+ GRAMMAR_FILES.each { |file| Kernel.load(file) }
425
+ WORLD_GRAMMAR.each { |file| Kernel.load(file) }
426
+ HECKSAGON_GRAMMAR.each { |file| Kernel.load(file) }
427
+ Kernel.load(PORT_GRAMMAR)
428
+ Kernel.load(ADAPTER_GRAMMAR)
429
+ TRANSLATION_GRAMMAR.each { |file| Kernel.load(file) }
430
+ end
431
+ registry
432
+ ensure
433
+ @bootstrapping = false
434
+ end
435
+
436
+ # A FRESH STORE per bluebook. The registry memoises repositories, so
437
+ # reusing it let every bluebook see the records of every bluebook judged
438
+ # before it. The parsed grammar is reused ; only the records are cleared.
439
+ # (During grammar_registry's own fixpoint judge this clear runs on the
440
+ # singleton mid-memoisation — harmless for the same reason : the grammar
441
+ # is what is kept, the records were never meant to survive a judging.)
442
+ # No `bind_runtime` here : judging dispatches by FQN and never opens the
443
+ # door, and binding would re-install the language's own facade constants
444
+ # once per judged chapter.
445
+ def self.fresh_runtime
446
+ registry = grammar_registry
447
+ registry.instance_variable_set(:@repositories, {})
448
+ Runtime::Dispatcher.new(registry)
449
+ end
450
+ end
451
+ end
452
+ end