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