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,246 @@
1
+ module Hecks
2
+ module Bluebook
3
+ module MetaValidator
4
+ # The leaf shapes of a reconstructed bluebook, and the encodings that go with
5
+ # them.
6
+ #
7
+ # Reconstruction walks the tree; this rebuilds the small hashes at its tips —
8
+ # an attribute, a rule, a where-clause, a transition — and undoes the two
9
+ # things the walk encodes on the way in: an attribute's type offered as the ID
10
+ # of whatever it names, and an append flattened to one row per binding.
11
+ #
12
+ # Separate from the traversal because they are different concerns, and because
13
+ # the two together were 211 code lines against a 200 limit.
14
+ module Shapes
15
+ # The type came in as the id of what it names, so it goes back out as the
16
+ # name — or, for another aggregate's head, as the encoding the IR spells.
17
+ def attribute(field, aggregate_id)
18
+ type = text(field[:type]).to_s
19
+
20
+ {
21
+ name: text(field[:name])&.to_sym,
22
+ type: owned_type(type, aggregate_id) || reference_type(type),
23
+ list: text(field[:list]).to_s == "true",
24
+ default: decode_literal(text(field[:default])),
25
+ # Read back the same way `list` is — both are booleans about the
26
+ # attribute, held as text, and dropping either would rebuild a
27
+ # bluebook that no longer says what it said.
28
+ optional: text(field[:optional]).to_s == "true",
29
+ pattern: presence(text(field[:pattern])),
30
+ # THE ROUND TRIP IS THE ONLY WAY IN. The grammar registry keeps the
31
+ # ASSEMBLED graph — the language as its own judge read it back — so a
32
+ # fact dropped here is a fact no downstream projection ever sees, no
33
+ # matter how plainly the .bluebook file declares it.
34
+ admits: presence(text(field[:admits])),
35
+ relationship: presence(text(field[:relationship]))
36
+ }
37
+ end
38
+
39
+ # A TYPE THIS AGGREGATE OWNS, offered as its id and read back as its name.
40
+ # Prefixed with the owner and the identity join, because that IS the value
41
+ # object's identity — the aggregate it belongs to, then its name.
42
+ def owned_type(type, aggregate_id)
43
+ prefix = Naming.identity([aggregate_id, ""])
44
+ return nil unless type.start_with?(prefix)
45
+
46
+ type.delete_prefix(prefix)
47
+ end
48
+
49
+ # ONE PART OF AN IDENTITY, read back as the path it went in as. The inverse
50
+ # of `Marks#identity_path`, and named the same so the two directions read
51
+ # as one table.
52
+ def identity_path(part) = text(part[:value]).to_s
53
+
54
+ # An argument, a parameter, a piece's attribute — the three places an
55
+ # attribute is written that carry no owner id to strip.
56
+ #
57
+ # A REFERENCE among them came in as the head's ID, because that is what
58
+ # Command/Entity/Query.Reference offer, so it goes back out as the
59
+ # encoding the IR spells. A qualified name is the tell : an ordinary
60
+ # type names something declared beside it (`Money`, `AccountNumber`) and
61
+ # never carries a chapter, while a head's id always does.
62
+ def shape_field(field)
63
+ type = text(field[:type]).to_s
64
+
65
+ {
66
+ name: text(field[:name])&.to_sym,
67
+ # A QUALIFIED name is the tell : an ordinary type names something
68
+ # declared beside it (`Money`, `AccountNumber`) and carries no join at
69
+ # all, where a head's id always does (chapter + name, joined the way
70
+ # every derived id is).
71
+ type: type.include?(Naming::IDENTITY_JOIN) ? reference_type(type) : text(field[:type]),
72
+ list: text(field[:list]).to_s == "true",
73
+ default: decode_literal(text(field[:default])),
74
+ optional: text(field[:optional]).to_s == "true",
75
+ pattern: presence(text(field[:pattern])),
76
+ admits: presence(text(field[:admits])),
77
+ relationship: presence(text(field[:relationship]))
78
+ }
79
+ end
80
+
81
+ # An ABSENT pattern is nil, not "". The language holds every field as
82
+ # text, so a field nobody set comes back as the empty string — and ""
83
+ # is a real regex (it matches everything), so keeping it would turn
84
+ # "no pattern" into "a pattern that always passes" and quietly cost the
85
+ # IR its round trip.
86
+ def presence(text)
87
+ value = text.to_s
88
+ value.empty? ? nil : value
89
+ end
90
+
91
+ # A literal, read back from the self-describing form Readings#encode_literal
92
+ # wrote — the same reader Assembly::Marks uses, because it is the same
93
+ # spelling. The forms are exactly the five the Primitive vocabulary admits —
94
+ # String, Integer, Float, TrueClass, FalseClass — plus a symbol, and an
95
+ # object literal, which is what `to: { value: "good" }` is: a value object's
96
+ # fields written inline.
97
+ def decode_literal(text) = Literal.read(text)
98
+
99
+ def rule(row) = { description: text(row[:description]), canonical: text(row[:canonical]) }
100
+
101
+ # S12, ADR 0025 — `projects :name, from: :"reference.remote_field"`,
102
+ # read back the same three plain identifiers `rule` above reads
103
+ # description/canonical as.
104
+ def projected_field(row)
105
+ { name: text(row[:name]), reference: text(row[:reference]), remote_field: text(row[:remote_field]) }
106
+ end
107
+
108
+ # `provenance from: {...}` rides the same literal encoding `default:`
109
+ # does — an object literal, self-describing via Hecks::Literal — one
110
+ # level up: a whole keyword's argument rather than an attribute's
111
+ # `default:`.
112
+ def provenance(row) = decode_literal(text(row[:provenance]))
113
+
114
+ # `command "Debit", from: "open"` — the SAME literal encoding
115
+ # `provenance`/`default:` already ride (S10, ADR 0025), one
116
+ # state or an array of them, or nil for a command with no
117
+ # lifecycle guard.
118
+ def from(row) = decode_literal(text(row[:from]))
119
+
120
+ # THE OPTION ROWS, GATHERED BACK into the shapes `extra_options_to_h` spells.
121
+ #
122
+ # One row per part, so a compound option is several rows and a repeated one is
123
+ # several groups told apart by `at`. Grouping by option name and then by `at`
124
+ # rebuilds both without either knowing which options exist — the whole point
125
+ # of holding them as an open map.
126
+ def options_of(row)
127
+ Array(row[:options])
128
+ .group_by { |part| text(part[:option]) }
129
+ .to_h { |option, parts| [option.to_sym, gathered(parts)] }
130
+ end
131
+
132
+ def gathered(parts)
133
+ repeated, single = parts.partition { |part| !text(part[:at]).to_s.empty? }
134
+ return single.to_h { |part| [text(part[:key]).to_sym, text(part[:value])] } if repeated.empty?
135
+
136
+ repeated.group_by { |part| text(part[:at]) }
137
+ .values
138
+ .map { |group| group.to_h { |part| [text(part[:key]).to_sym, text(part[:value])] } }
139
+ end
140
+
141
+ # The IR keeps a where's field as a STRING, not a symbol — it is read back
142
+ # out, never called. The value stays RAW TEXT here on purpose : this
143
+ # feeds the declaration hash Assembly::Marks#where_clause decodes
144
+ # from (via `read`), and decoding twice is worse than once — a
145
+ # kwarg reference (":ceiling") decoded here into the Symbol :ceiling
146
+ # would have its colon stripped by `read`'s own `.to_s` and come
147
+ # back out as the plain string "ceiling", indistinguishable from a
148
+ # literal of the same name. One decode, at the one place that builds
149
+ # the object every comparator actually reads.
150
+ def where_clause(row)
151
+ { field: text(row[:field]), op: text(row[:op]), value: text(row[:value]) }
152
+ end
153
+
154
+ # One object in the IR, two fields in the language.
155
+ def order_by(row)
156
+ field = text(row[:order_field])
157
+ return nil if field.to_s.empty?
158
+
159
+ { field: field, direction: text(row[:order_way]) }
160
+ end
161
+
162
+ def limit(row)
163
+ ceiling = text(row[:limit])
164
+ return nil if ceiling.to_s.empty?
165
+
166
+ { value: ceiling }
167
+ end
168
+
169
+ def transition(row)
170
+ {
171
+ command: text(row[:command]),
172
+ from_state: text(row[:from_state]),
173
+ to_state: text(row[:to_state])
174
+ }
175
+ end
176
+
177
+ def head(row)
178
+ {
179
+ aggregate: text(row[:aggregate]),
180
+ # A String, like an entity's identified_by. The IR is not uniform about
181
+ # this and only a round trip says so.
182
+ as: text(row[:as]),
183
+ many: text(row[:many]).to_s == "true"
184
+ }
185
+ end
186
+
187
+ def group_by_field(row) = { field: text(row[:field]) }
188
+
189
+ # `count`'s own boolean, read back the SAME way `head`'s own
190
+ # `many` is (`text(row[:many]).to_s == "true"`) — except a
191
+ # `ReadModel.Count` command is dispatched AT ALL only when
192
+ # `@count` was truthy (`MetaValidator::Judge#setters` skips a
193
+ # setter whose every source is absent), so an undeclared read
194
+ # model's own `count` field never gets written and comes back
195
+ # `nil` here, never `"false"` — matching `ReadModel#to_h`'s own
196
+ # `true`/`nil` pair (never `false`) exactly, rather than the
197
+ # unconditional `true`/`false` `head`'s own `many` needs (every
198
+ # head DOES get a `Gather` dispatch, declared or derived).
199
+ def read_model_count(row) = (true if text(row[:count]).to_s == "true")
200
+
201
+ # THE APPEND FLATTENING, IN REVERSE.
202
+ #
203
+ # An append binds several fields at once and the language's Change holds one
204
+ # field/kind/source triple, so the walk offers an append once PER BINDING.
205
+ # Rebuilding groups those rows back into the single mutation the IR keeps —
206
+ # the only place here that undoes something rather than simply reading it.
207
+ def mutations(row)
208
+ Array(row[:mutations])
209
+ .group_by { |change| [text(change[:target]), text(change[:op])] }
210
+ .map { |(target, op), bindings| mutation(target, op, bindings) }
211
+ end
212
+
213
+ # `sign:` — read the SAME way the declared side computes it
214
+ # (`Bluebook::Mutation.sign_for`, item #5 of the whole-project
215
+ # table-unification survey), not stored on any row here — the
216
+ # meta-domain's own Change entity carries no `sign` field of its
217
+ # own (it is a pure function of `op`, nothing to persist), so
218
+ # reconstruction recomputes it the same way a freshly-built
219
+ # Mutation's own `to_h` lambda does, rather than leaving the key
220
+ # silently absent (spec/round_trip_spec's whole point: a field the
221
+ # language does not hold is a named gap, not a byte-for-byte one).
222
+ def mutation(target, op, bindings)
223
+ base = { target: target.to_sym, op: op.to_sym, sign: Hecks::Bluebook::Mutation.sign_for(op) }
224
+ # `:delegate` (CommandBuilder#delegates_to's own comment) rides
225
+ # the SAME multi-binding shape `:append` does.
226
+ return base.merge(fields: appended(bindings)) if ["append", "delegate"].include?(op)
227
+
228
+ base.merge(source: classified(bindings.first))
229
+ end
230
+
231
+ def appended(bindings)
232
+ bindings.to_h { |binding| [text(binding[:field]).to_sym, text(binding[:source])] }
233
+ end
234
+
235
+ def classified(binding)
236
+ kind = text(binding[:kind])
237
+ value = text(binding[:source])
238
+
239
+ return { kind: kind, name: value } if %w[argument state].include?(kind)
240
+
241
+ { kind: "literal", value: decode_literal(value) }
242
+ end
243
+ end
244
+ end
245
+ end
246
+ end
@@ -0,0 +1,255 @@
1
+ module Hecks
2
+ module Bluebook
3
+ module MetaValidator
4
+ # S14, ADR 0026 — DISPATCHES THE LANGUAGE'S OWN GRAMMAR TABLE INTO
5
+ # ITSELF, once, so `Keyword`/`Argument`'s own `status` genuinely IS a
6
+ # lifecycle — checked at real dispatch time (the same admission/
7
+ # coercion/lifecycle-guard door every other command goes through),
8
+ # not merely declared and never exercised.
9
+ #
10
+ # THE SOURCE STAYS STATIC. Aggregate-local `KeywordSeed`/
11
+ # `ArgumentSeed` value objects (still hand-written `member` rows —
12
+ # now beside the concepts they spell) are what gets WRITTEN ; this
13
+ # is what turns them into what gets READ. `Judge`/`Reconstruction`
14
+ # already draw exactly this line everywhere else in the meta-domain
15
+ # (a chapter's own DECLARATIONS versus what gets DISPATCHED from
16
+ # them) — this runs the same distinction one level further out, for
17
+ # the language's own grammar table.
18
+ #
19
+ # A DEDICATED RUNTIME, not `MetaValidator.fresh_runtime`. That one
20
+ # is reserved for `Judge`'s own bootstrap (dispatching a CHAPTER's
21
+ # declarations INTO the meta-domain's grammar) — a different act
22
+ # from this one (dispatching the meta-domain's OWN grammar table
23
+ # data into a live "Bluebook" domain instance). Sharing the runtime
24
+ # would let one boot's own repository state leak into the other's.
25
+ #
26
+ # Usage:
27
+ #
28
+ # MetaValidator.syntax_table # => { keywords: [...], arguments: [...] }
29
+ #
30
+ # each row a plain Hash, string values, `status` included — the
31
+ # exact shape `ParserTable`/`syntax_conformance_spec` already read
32
+ # off the OLD closed-set members, so neither consumer had to change
33
+ # what it does with a row, only where the row comes from.
34
+ module SyntaxBoot
35
+ module_function
36
+
37
+ # Memoized the same way `MetaValidator.grammar_registry` itself
38
+ # is — the ~284-command dispatch sequence below is real work, not
39
+ # something three separate callers (ParserTable, syntax_
40
+ # conformance_spec, bin/reference) should each pay for on every
41
+ # call.
42
+ #
43
+ # KEYED BY THE GRAMMAR REGISTRY'S OWN CHAPTER SET, not by a
44
+ # "ready" flag. `boot` reads exactly two things: the registry's
45
+ # "Bluebook" chapter (the core seed rows) and every chapter in the
46
+ # registry that `attaches_to` a core context (Paging's rows). So
47
+ # the table is a pure function of which chapter OBJECTS the
48
+ # registry holds — and that is the cache key: the same chapters,
49
+ # by identity, mean the same table; a chapter replaced (the
50
+ # fixpoint judge swapping a raw chapter for its assembled self)
51
+ # or added (`load_attached_grammar_into`) means a fresh boot.
52
+ #
53
+ # WHY NOT THE EARLIER `grammar_registry_ready?` GUARD. That guard
54
+ # was right about the hazard — a snapshot taken mid-build, before
55
+ # Paging attached, would be missing limit/offset/cursor/nulls
56
+ # forever — and wrong about the cost of its cure. It refused to
57
+ # cache ANYTHING until the whole grammar registry had finished
58
+ # building, calling that window "narrow" and recomputing in it
59
+ # "cheap". Measured (hecks_ai_training, a six-entity chess domain,
60
+ # 2026-08-22): every `word_gate_dispatch` landing in that window
61
+ # re-ran the full ~284-dispatch boot at ~0.76s each — 42 times per
62
+ # process, 32 of a 35-second `Hecks.boot`, 95% of which had
63
+ # nothing to do with the domain being booted. Keying on the
64
+ # chapter set closes the same hazard by construction (a snapshot
65
+ # can only be served while the chapters it was read from are
66
+ # still the chapters the registry holds) and lets the window's
67
+ # own repeated, identical calls share one boot. A registry reset
68
+ # (fixpoint_spec's own `@grammar_registry = nil`) yields new
69
+ # chapter objects, so it invalidates this the same way it always
70
+ # invalidated `grammar_registry_ready?`.
71
+ #
72
+ # `equal?`, NOT `==`, on the chapters — identity is the fact being
73
+ # tracked. Holding the chapter objects themselves (not their ids)
74
+ # in the key also means a collected chapter can never hand its id
75
+ # to a newcomer behind this cache's back.
76
+ def call
77
+ chapters = MetaValidator.grammar_registry.bluebooks.to_a
78
+ return @call if @call && same_chapters?(@call_chapters, chapters)
79
+
80
+ result = boot
81
+ @call = result
82
+ @call_chapters = chapters
83
+ result
84
+ end
85
+
86
+ def same_chapters?(cached, current)
87
+ cached.size == current.size &&
88
+ cached.zip(current).all? { |(cached_name, cached_chapter), (name, chapter)| cached_name == name && cached_chapter.equal?(chapter) }
89
+ end
90
+
91
+ def boot
92
+ bluebook = MetaValidator.grammar_registry.bluebook("Bluebook")
93
+ # `MetaValidator.fresh_runtime`, not a brand-new `Runtime::
94
+ # Registry` — the grammar registry's own copy already has
95
+ # "Bluebook" registered (`grammar_registry`'s own boot already
96
+ # ran `registry.add_bluebook`) AND its adapter ports already
97
+ # loaded ; a standalone registry would need both wired by hand.
98
+ # `fresh_runtime` resets `@repositories` on the SAME registry —
99
+ # the identical isolation `Judge.new` already relies on for
100
+ # every real domain it judges, proven safe by every dispatch
101
+ # this session has ever made.
102
+ runtime = MetaValidator.fresh_runtime
103
+
104
+ declare_syntax(runtime, bluebook)
105
+ admit_keywords(runtime, bluebook)
106
+ admit_arguments(runtime, bluebook)
107
+
108
+ read_back(runtime, bluebook)
109
+ end
110
+
111
+ # An Integer stays an Integer — `position` is `Position`-typed
112
+ # (`attribute :value, Integer`), so stringifying it fails the type
113
+ # gate rather than feeding it, the same reading `Judge#v` gives.
114
+ def v(text)
115
+ return { value: text } if text.is_a?(Integer)
116
+
117
+ { value: text.to_s }
118
+ end
119
+
120
+ # ABSENT STAYS ABSENT. A seed row's own optional columns ("was",
121
+ # "at", "named", ...) are empty strings, not nil — `Literal`/CSV-
122
+ # shaped grammar data has no `nil` to write — so this is the one
123
+ # place that decides "" means "not given" for the purpose of an
124
+ # `optional: true` command argument, the same reading `Judge#v`
125
+ # makes for the meta-domain's own dispatches.
126
+ def optional(text)
127
+ return nil if text.nil? || text.to_s.empty?
128
+
129
+ v(text)
130
+ end
131
+
132
+ # `Syntax.Declare`'s own `reference_to Bluebook` names the CHAPTER
133
+ # it belongs to — the same fact every other top-level aggregate's
134
+ # own creating command carries (`ProcessManager.Declare`,
135
+ # `Policy.Declare`, ...). This fresh runtime holds no chapter
136
+ # record of its own yet (nothing else here needs one), so one is
137
+ # declared first, named after the real chapter, purely to satisfy
138
+ # the reference — its own vision/classification are never read
139
+ # by anything this boot does.
140
+ def declare_syntax(runtime, bluebook)
141
+ syntax = bluebook.aggregate("Syntax")
142
+ runtime.dispatch("Bluebook::Bluebook.Declare", name: v(bluebook.hecks_name),
143
+ vision: v("the language's own grammar table, dispatched into itself"),
144
+ classification: v("core"))
145
+ runtime.dispatch("Bluebook::Syntax.Declare", bluebook: bluebook.hecks_name, name: v(syntax.hecks_name))
146
+ end
147
+
148
+ # `to: "Syntax"` NAMES THE RECORD ALREADY OPENED BY `declare_syntax`
149
+ # ABOVE — an append onto an existing aggregate, not a second creation
150
+ # of it. This used to smuggle `name: v("Syntax")` into the payload
151
+ # instead, the pre-routing convention `Judge#appends` (the same
152
+ # append shape, for `ValueObject.Member`/`ProcessManager.Handler`)
153
+ # already left behind for `to:`/`with:` — carrying the receiver in
154
+ # the payload made `Syntax.Keyword`'s own `command.creates?` (true:
155
+ # it declares no `reference_to`) look like a fresh identity to mint,
156
+ # which collided with the very "Syntax" row `declare_syntax` had
157
+ # just opened.
158
+ def admit_keywords(runtime, bluebook)
159
+ all_rows(bluebook, "KeywordSeed").each_with_index do |row, index|
160
+ runtime.dispatch("Bluebook::Syntax.Keyword", to: "Syntax",
161
+ with: { position: v(index),
162
+ word: v(row[:word]), context: v(row[:context]), body: v(row[:body]),
163
+ inner: v(row[:inner]), opens: v(row[:opens]), fills: v(row[:fills]),
164
+ was: optional(row[:was]),
165
+ resolves_via: optional(row[:resolves_via]),
166
+ disambiguator: optional(row[:disambiguator]),
167
+ calls: optional(row[:calls]) })
168
+
169
+ next unless row[:status].to_s == "deprecated"
170
+
171
+ runtime.dispatch("Bluebook::Syntax.Keyword.Deprecate", to: { aggregate: "Syntax", entity: index.to_s })
172
+ end
173
+ end
174
+
175
+ def admit_arguments(runtime, bluebook)
176
+ all_rows(bluebook, "ArgumentSeed").each_with_index do |row, index|
177
+ runtime.dispatch("Bluebook::Syntax.Argument", to: "Syntax",
178
+ with: { position: v(index),
179
+ keyword: v(row[:keyword]), context: v(row[:context]),
180
+ at: optional(row[:at]), named: optional(row[:named]),
181
+ kind: v(row[:kind]), required: v(row[:required]), fills: v(row[:fills]),
182
+ selects: optional(row[:selects]), pair_key_fills: optional(row[:pair_key_fills]),
183
+ pair_value_fills: optional(row[:pair_value_fills]),
184
+ pairs_shape: optional(row[:pairs_shape]), variadic: optional(row[:variadic]),
185
+ minimum: optional(row[:minimum]),
186
+ coerce: optional(row[:coerce]), blank_message: optional(row[:blank_message]) })
187
+
188
+ next unless row[:status].to_s == "deprecated"
189
+
190
+ runtime.dispatch("Bluebook::Syntax.Argument.Deprecate", to: { aggregate: "Syntax", entity: index.to_s })
191
+ end
192
+ end
193
+
194
+ # EVERY AGGREGATE-LOCAL TABLE IN EVERY LOADED LANGUAGE CHAPTER.
195
+ # The table's presence is the registration: no chapter, aggregate,
196
+ # filename, or context catalog is maintained here. This finds the core
197
+ # Bluebook concepts, the sibling artifact languages (World, Hecksagon,
198
+ # Port, Adapter, Translation), and attached sub-languages alike.
199
+ # Concatenated into ONE sequence because `position` is minted from the
200
+ # walk index and must not collide across concepts.
201
+ def all_rows(bluebook, name)
202
+ seed_chapters(bluebook).flat_map { |chapter| rows(chapter, name) }
203
+ end
204
+
205
+ # Keep the core chapter first, then registry insertion order for every
206
+ # sibling/extension. The name rejection avoids reading the same core
207
+ # object twice without relying on object identity across fixpoint
208
+ # assembly.
209
+ def seed_chapters(bluebook)
210
+ [bluebook] + MetaValidator.grammar_registry.bluebooks.values.reject { |chapter| chapter.name == bluebook.name }
211
+ end
212
+
213
+ # The seed rows themselves — plain hashes, string values, read from
214
+ # every aggregate that owns a same-named local value object. Repeating
215
+ # the value-object shape is deliberate: each concept remains readable
216
+ # by itself, while this discovery is the only grouping mechanism.
217
+ def rows(bluebook, name)
218
+ bluebook.aggregates.flat_map do |aggregate|
219
+ value_object = aggregate.value_objects.find { |vo| vo.hecks_name == name }
220
+ next [] unless value_object
221
+
222
+ value_object.members.map { |row| row.to_h.transform_values(&:to_s) }
223
+ end
224
+ end
225
+
226
+ # Reads the dispatched result back into the SAME shape `rows`
227
+ # above hands the seed data in as — plain hashes, string values,
228
+ # `status` included — so `ParserTable`/`syntax_conformance_spec`
229
+ # need not know or care that a real dispatch happened in between.
230
+ def read_back(runtime, bluebook)
231
+ syntax = bluebook.aggregate("Syntax")
232
+ repository = runtime.registry.repository("Bluebook", syntax)
233
+ instance = repository.find(Naming.identity([syntax.hecks_name]))
234
+
235
+ {
236
+ keywords: Array(instance[:keywords]).map { |row| stringify(row) },
237
+ arguments: Array(instance[:arguments]).map { |row| stringify(row) }
238
+ }
239
+ end
240
+
241
+ def stringify(row)
242
+ row.to_h.each_with_object({}) do |(key, cell), out|
243
+ out[key] = scalar(cell).to_s
244
+ end
245
+ end
246
+
247
+ def scalar(cell)
248
+ return cell.to_h.values.first if cell.respond_to?(:to_h) && !cell.is_a?(String)
249
+
250
+ cell
251
+ end
252
+ end
253
+ end
254
+ end
255
+ end
@@ -0,0 +1,138 @@
1
+ module Hecks
2
+ module Bluebook
3
+ module MetaValidator
4
+ # Offers a built translation (a `translations/*.bluebook` edge,
5
+ # `Hecks.data_translation`'s own real, established file convention —
6
+ # there is no separate `.translation` extension) to the language
7
+ # that describes translations.
8
+ #
9
+ # A sibling of a bluebook again, the same shape PortJudge/
10
+ # AdapterJudge already are — its own file, its own door, judged
11
+ # through its own self-hosted language (translation.bluebook)
12
+ # rather than left as plain Ruby structs nothing checks. Walks the
13
+ # WHOLE built translation in one pass — the top-level Declare/
14
+ # Retire commands, then every nested aggregate's own Declare plus
15
+ # one Add* command per rule it carries — the same "one judge, every
16
+ # nested record" shape WorldJudge already gives Wiring. Field names
17
+ # throughout read lib/hecks/bluebook/translation.rb's own real
18
+ # struct/class definitions directly, not the DSL builder's own
19
+ # local parameter names (`move(old_path, to:)`'s `old_path` reads
20
+ # back as the struct's own `from`). Whole-project table-
21
+ # unification survey, item #13's remaining builders.
22
+ class TranslationJudge
23
+ attr_reader :refusals
24
+
25
+ def initialize(translation)
26
+ @translation = translation
27
+ @refusals = []
28
+ @runtime = MetaValidator.fresh_runtime
29
+ judge!
30
+ end
31
+
32
+ private
33
+
34
+ def v(text) = text.nil? ? nil : { value: text.to_s }
35
+
36
+ def args(pairs) = pairs.compact
37
+
38
+ # `Runtime::AlreadyExists` is rescued HERE but not by World/Port/
39
+ # Adapter's own sibling judges — found live, not by inspection.
40
+ # Those three each judge a SINGLE aggregate per build (Port/Adapter)
41
+ # or a Hash keyed by verb (World's own settings, deduped by
42
+ # construction), so a duplicate-identity Declare can never reach
43
+ # their own runtime. `TranslationBuilder#aggregate` appends every
44
+ # block to a plain Array (`@aggregates << builder.build`) with no
45
+ # dedup — two `aggregate "Account" do ... end` blocks in the same
46
+ # translation are syntactically legal and reach here for real. Left
47
+ # unrescued, the second Declare's `AlreadyExists` crashed straight
48
+ # through `call_translation` instead of becoming a clean refusal —
49
+ # confirmed via direct dispatch before this fix.
50
+ def offer(label)
51
+ yield
52
+ rescue Runtime::GivenNotMet, Runtime::InvariantViolation, Runtime::TypeMismatch,
53
+ Runtime::NotFound, Runtime::AlreadyExists => e
54
+ @refusals << "#{label}: #{e.message}"
55
+ rescue Runtime::UnknownVerb
56
+ nil
57
+ end
58
+
59
+ def send_to(verb, label, to: nil, with: {})
60
+ offer(label) { @runtime.dispatch(verb, to: to, with: args(with)) }
61
+ end
62
+
63
+ def judge!
64
+ t = @translation
65
+ send_to("Translation::Translation.Declare", t.domain,
66
+ with: { domain: v(t.domain), from: v(t.from), to: v(t.to) })
67
+
68
+ # `id:`, computed the SAME way — `Naming.identity([domain, from,
69
+ # to])` — Translation's own identity is COMPOSITE
70
+ # (`identified_by :domain, :from, :to`), the same reason
71
+ # `Wiring.Set`'s own self-reference dispatch (world.bluebook,
72
+ # `WorldJudge#judge_wiring`) needs a computed `id:` rather than
73
+ # a bare field value — a single-field identity (TranslationAggregate's
74
+ # own `name`) is the ONE case where the bare value itself IS the
75
+ # id, confirmed live via direct dispatch testing, not assumed.
76
+ translation_id = Naming.identity([t.domain, t.from, t.to])
77
+ Array(t.retired).each do |name|
78
+ send_to("Translation::Translation.Retire", t.domain, to: translation_id,
79
+ with: { value: v(name) })
80
+ end
81
+
82
+ Array(t.aggregates).each { |aggregate| judge_aggregate(t, aggregate) }
83
+ end
84
+
85
+ def judge_aggregate(translation, aggregate)
86
+ name = aggregate.name
87
+ parent = {
88
+ domain: v(translation.domain),
89
+ from: v(translation.from),
90
+ to: v(translation.to)
91
+ }
92
+ send_to("Translation::TranslationAggregate.Declare", name,
93
+ with: { translation_ref: parent, name: v(name), was: v(aggregate.was) })
94
+
95
+ Hash(aggregate.renames).each do |from, to|
96
+ send_to("Translation::TranslationAggregate.AddRename", name, to: name,
97
+ with: { from: v(from), to: v(to) })
98
+ end
99
+
100
+ Array(aggregate.moves).each do |move|
101
+ send_to("Translation::TranslationAggregate.AddMove", name, to: name,
102
+ with: { from: v(move.from), to: v(move.to) })
103
+ end
104
+
105
+ Array(aggregate.converts).each do |convert|
106
+ send_to("Translation::TranslationAggregate.AddConvert", name, to: name,
107
+ with: { from: v(convert.from), to: v(convert.to), values: v(convert.values.inspect) })
108
+ end
109
+
110
+ Array(aggregate.drops).each do |dropped|
111
+ send_to("Translation::TranslationAggregate.AddDrop", name, to: name,
112
+ with: { value: v(dropped) })
113
+ end
114
+
115
+ Array(aggregate.retypes).each do |retype|
116
+ send_to("Translation::TranslationAggregate.AddRetype", name, to: name,
117
+ with: { from: v(retype.from), to: v(retype.to) })
118
+ end
119
+
120
+ Array(aggregate.computes).each do |compute|
121
+ send_to("Translation::TranslationAggregate.AddCompute", name, to: name,
122
+ with: { from: v(compute.from), to: v(compute.to), sql: v(compute.sql) })
123
+ end
124
+
125
+ Array(aggregate.rekeys).each do |rekey|
126
+ send_to("Translation::TranslationAggregate.AddRekey", name, to: name,
127
+ with: { sql: v(rekey.sql) })
128
+ end
129
+
130
+ Array(aggregate.backfills).each do |backfill|
131
+ send_to("Translation::TranslationAggregate.AddBackfill", name, to: name,
132
+ with: { name: v(backfill.name), default: v(backfill.default.inspect) })
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end