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,98 @@
1
+ module Hecks
2
+ module Projections
3
+ module Model
4
+ # HOW THE MODEL'S SHAPE DIFFERS FROM THE LANGUAGE'S, and why.
5
+ #
6
+ # Every construct's `emits_ir` restates what the grammar declares —
7
+ # and the two legitimately differ, in seven ways. These lived only
8
+ # as prose in Ruby comments until spec/model_shape_conformance_spec
9
+ # made them checkable; they live HERE so the generator and the gate
10
+ # read one source rather than two that must agree.
11
+ #
12
+ # A reason is carried beside each entry because the generated file
13
+ # will CARRY it: a deviation is exactly the kind of thing whose
14
+ # explanation must survive regeneration, and the only way it can is
15
+ # to be emitted rather than typed into the output.
16
+ module Deviations
17
+ # The grammar is relational — a Command points UP at its
18
+ # Aggregate — where the model composes. An explicit `as:` still
19
+ # keeps its `_id` (Command's own `entity_id`, kept as data); the
20
+ # parent link itself mints bare now (ADR 0025) — `aggregate` or
21
+ # `bluebook`, whichever this category's creating command declares
22
+ # first — so both spellings are checked. Entity spells its own
23
+ # (separate, non-colliding) text twin of the parent link `owner`.
24
+ PARENT_REF = ->(field) { field.to_s.match?(/_id\z/) || %i[owner aggregate bluebook].include?(field) }
25
+
26
+ # The judge's own field, never the model's — contracts.rb already
27
+ # says so with `derived: { position: :walk }`.
28
+ JUDGE_ONLY = %i[position].freeze
29
+
30
+ # What the model holds that the grammar declares elsewhere: the
31
+ # containment edges, stated in syntax.bluebook's Keyword rows as
32
+ # `context` -> `opens`.
33
+ CONTAINED = {
34
+ "Bluebook" => %i[aggregates read_models policies process_managers],
35
+ "Aggregate" => %i[commands entities queries value_objects],
36
+ # S17, ADR 0026 — an entity may nest further entities now
37
+ # (`Dispatch`, inside `Handler`) — same containment edge as
38
+ # Aggregate's own `entities`, one level down.
39
+ "Entity" => %i[commands entities queries],
40
+ "ValueObject" => %i[members],
41
+ "ProcessManager" => %i[handlers]
42
+ }.freeze
43
+
44
+ # One model field gathered from several declared ones —
45
+ # contracts.rb's own `[:folded, ...]` shape, as the pair it is.
46
+ FOLDED = {
47
+ "Aggregate" => { lifecycle: %i[state_field state_start transitions] },
48
+ "Entity" => { lifecycle: %i[state_field state_start transitions] },
49
+ "Query" => { order_by: %i[order_field order_way] }
50
+ }.freeze
51
+
52
+ # The inverse of a fold, and it had no name at all: one declared
53
+ # field opening into several the model holds apart.
54
+ UNPACKED = {
55
+ "ReadModel" => { options: %i[wheres order_by limit] }
56
+ }.freeze
57
+
58
+ # Model-only, each for its own reason rather than by oversight.
59
+ COMPUTED = {
60
+ "Bluebook" => { ir_version: "the EMISSION's own version, not the domain's",
61
+ canonical_form: "the normalisation table every reader needs beside the IR" },
62
+ "ValueObject" => { closed_set: "an empty one_of and no one_of are otherwise indistinguishable" },
63
+ "Aggregate" => { ports: "declared in the hecksagon, attached after the aggregate exists" }
64
+ }.freeze
65
+
66
+ # DECLARED, AND DELIBERATELY NOT EMITTED. The category that had no
67
+ # home anywhere before — each of these was a comment and nothing
68
+ # more.
69
+ OFF_THE_WIRE = {
70
+ "Policy" => { aggregate: "the wire format is a pinned contract, and it does not carry " \
71
+ "where a policy was written before the builder hoisted it" },
72
+ "Bluebook" => { formerly_known_as: "a rename's old name is a fact about the source",
73
+ normalisations: "the normalisation table rides on canonical_form instead" },
74
+ "ValueObject" => { rows: "the language's name for a closed set's members; emitted as `members`" }
75
+ }.freeze
76
+
77
+ # Emitted by `to_h`'s own merge rather than by `emits_ir` — the
78
+ # two constructs whose shape is genuinely not fixed, because the
79
+ # query specification layer grew options after them.
80
+ DYNAMIC_TAIL = {
81
+ "Query" => %i[options],
82
+ "ReadModel" => %i[options group_by aggregate_heads count median_field]
83
+ }.freeze
84
+
85
+ module_function
86
+
87
+ # The tables that carry a reason answer with names only when the
88
+ # caller wants the set rather than the explanations.
89
+ def off_the_wire(name) = OFF_THE_WIRE.fetch(name, {}).keys
90
+ def computed(name) = COMPUTED.fetch(name, {}).keys
91
+ def contained(name) = CONTAINED.fetch(name, [])
92
+ def folded(name) = FOLDED.fetch(name, {})
93
+ def unpacked(name) = UNPACKED.fetch(name, {})
94
+ def dynamic_tail(name) = DYNAMIC_TAIL.fetch(name, [])
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,145 @@
1
+ require_relative "model/deviations"
2
+ require_relative "../projector"
3
+
4
+ module Hecks
5
+ module Projections
6
+ # THE MODEL CLASSES, PROJECTED FROM THE LANGUAGE THAT DECLARES THEM.
7
+ #
8
+ # A construct's HOLDING half — its readers, its emission, a
9
+ # constructor that assigns declared fields and hands off — restates
10
+ # what `bluebook.bluebook` already says, three times over in Ruby.
11
+ # This renders it instead.
12
+ #
13
+ # ONLY THE HOLDING HALF. `Behaviour::X` is hand-written and permanent,
14
+ # and `settle` is the seam: everything a declaration cannot state
15
+ # lives behind it, so regenerating can never be lossy. That property
16
+ # was established construct by construct before any of this was
17
+ # written — the chapter looked generatable and was not, until its
18
+ # `@hecks_root`, ports table and child stamping moved behind `settle`.
19
+ #
20
+ # THE HOST MANIFEST IS THE HONEST PART. The grammar states the fields;
21
+ # it does not state which constructs are Ruby CLASSES rather than
22
+ # instances, what a constructor's defaults are, or how a value is
23
+ # coerced on the way in. Those are facts about Ruby, not about
24
+ # bluebooks, so they are declared here rather than pretended into the
25
+ # language — and keeping them in one table is what would let a second
26
+ # host swap this file rather than edit thirteen.
27
+ module Model
28
+ extend Projector::Target
29
+
30
+ projects_as :model, declares: "Bluebook", emits: :files
31
+
32
+ # PER-CONSTRUCT RUBY FACTS. `coerce` is the only fiddly column: a
33
+ # declared field arrives as whatever the builder handed over, and
34
+ # each construct has always normalised its own on the way in.
35
+ HOST = {
36
+ "Policy" => {
37
+ construct: "Policy",
38
+ file: "policy.rb",
39
+ behaviour: "Behaviour::Policy",
40
+ readers: %i[name on_event trigger_command target_domain where for_each with_spec],
41
+ accessors: %i[aggregate],
42
+ defaults: { name: nil, on_event: "nil", trigger_command: "nil",
43
+ target_domain: "nil", where: "nil", for_each: "nil",
44
+ with_spec: "[]", aggregate: "nil" },
45
+ coerce: { name: ".to_s", aggregate: "&.to_s" },
46
+ # A LIST OF BINDINGS IS NOT A SCALAR ON THE WIRE. Every other
47
+ # field emits as itself; this one has to render the way
48
+ # `DispatchSpec`'s own `with_spec` does — keys to strings, and
49
+ # `render_value` KEEPING the leading colon on a Symbol, because
50
+ # a binding that reads an event field and one that supplies a
51
+ # literal string are otherwise indistinguishable once written
52
+ # down (see `MetaValidator::Readings`' own note on exactly that).
53
+ renders: { with_spec: "-> { with_spec.map { |key, value| [key.to_s, Bluebook.render_value(value)] } }" },
54
+ settles: false
55
+ }
56
+ }.freeze
57
+
58
+ module_function
59
+
60
+ def call(bluebook:, options: {})
61
+ HOST.to_h { |name, host| [host.fetch(:file), render(bluebook, name, host)] }
62
+ end
63
+
64
+ def render(bluebook, name, host)
65
+ <<~RUBY
66
+ # GENERATED — projected from the language's own #{name} aggregate.
67
+ # DO NOT EDIT: the holding half is rendered, and #{host.fetch(:behaviour)}
68
+ # is where anything hand-written belongs.
69
+ require_relative "behaviour/#{File.basename(host.fetch(:file), ".rb")}"
70
+
71
+ module Hecks
72
+ module Bluebook
73
+ class #{name}
74
+ include Hecks::IR
75
+ include #{host.fetch(:behaviour)}
76
+
77
+ #{indent(emits(bluebook, name), 6)}
78
+
79
+ #{indent(readers(host), 6)}
80
+
81
+ #{indent(constructor(host), 6)}
82
+ end
83
+ end
84
+ end
85
+ RUBY
86
+ end
87
+
88
+ # The emission, keyed as the model spells it and sourced as the
89
+ # language declares it.
90
+ def emits(bluebook, name)
91
+ fields = emitted_fields(bluebook, name)
92
+ renders = HOST.fetch(name).fetch(:renders, {})
93
+ width = fields.map { |f| f.to_s.length }.max.to_i
94
+
95
+ "emits_ir(\n" +
96
+ fields.map { |f| " #{"#{f}:".ljust(width + 1)} #{renders.fetch(f, ":#{f}")}" }.join(",\n") +
97
+ "\n)"
98
+ end
99
+
100
+ # WHAT THE CONSTRUCT EMITS: what the language declares, less every
101
+ # deviation the tables account for. The generator and
102
+ # spec/model_shape_conformance_spec compute this the same way, from
103
+ # the same tables, which is the point of the tables being in lib.
104
+ def emitted_fields(bluebook, name)
105
+ bluebook.aggregate(name).attributes.map(&:name)
106
+ .reject { |f| Deviations::PARENT_REF.call(f) } -
107
+ Deviations::JUDGE_ONLY -
108
+ Deviations.off_the_wire(name) -
109
+ Deviations.dynamic_tail(name) -
110
+ Deviations.folded(name).values.flatten -
111
+ Deviations.unpacked(name).keys
112
+ end
113
+
114
+ def readers(host)
115
+ lines = ["attr_reader #{host.fetch(:readers).map { |r| ":#{r}" }.join(', ')}"]
116
+ accessors = host.fetch(:accessors, [])
117
+ return lines.join("\n") if accessors.empty?
118
+
119
+ # A declared field the model deliberately does not emit still
120
+ # needs a reader, and the REASON it is off the wire is carried
121
+ # here rather than typed in — a comment that survives
122
+ # regeneration is one the generator writes.
123
+ reasons = Deviations::OFF_THE_WIRE.fetch(host.fetch(:construct, ""), {})
124
+ (lines + accessors.map { |a|
125
+ why = reasons[a]
126
+ (why ? "\n# #{a.upcase}, DECLARED AND DELIBERATELY OFF THE WIRE\n# #{wrap(why)}\n" : "") +
127
+ "attr_accessor :#{a}"
128
+ }).join("\n")
129
+ end
130
+
131
+ def constructor(host)
132
+ args = host.fetch(:defaults).map { |f, d| d ? "#{f}: #{d}" : "#{f}:" }.join(", ")
133
+ body = host.fetch(:defaults).keys.map { |f|
134
+ " @#{f} = #{f}#{host.fetch(:coerce, {})[f]}"
135
+ }
136
+ body << "\n settle" if host.fetch(:settles, true)
137
+
138
+ "def initialize(#{args})\n#{body.join("\n")}\nend"
139
+ end
140
+
141
+ def indent(text, by) = text.lines.map { |l| l.strip.empty? ? l : (" " * by) + l }.join
142
+ def wrap(text) = text.scan(/.{1,62}(?:\s|$)/).map(&:strip).join("\n# ")
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,110 @@
1
+ require_relative "../naming"
2
+ require_relative "../projector"
3
+
4
+ module Hecks
5
+ module Projections
6
+ # AN OIDC CLIENT/SCOPE MANIFEST derived from a domain's own IR: what
7
+ # an identity provider has to know about this domain before it can
8
+ # issue a token that means anything here.
9
+ #
10
+ # THE ARTIFACT HALF OF SOMETHING ALREADY HALF-BUILT.
11
+ # `spec/oidc_projection_spec.rb` covers the INTEGRATION half — verified
12
+ # claims in, `IdentityResolution.resolve` → `Authorization.holds_role?`
13
+ # → a dispatch scoped by `Hecks.as_caller(role:)`. That half enforces
14
+ # a role per command. It just had no way to say, up front and as data,
15
+ # WHICH role each command wants — every answer came from asking the
16
+ # live runtime one dispatch at a time.
17
+ #
18
+ # This is that catalogue, and the two are checked against each other
19
+ # rather than merely coexisting: `verb` here is spelled exactly as
20
+ # `Runtime::Dispatcher#dispatch` takes it and exactly as
21
+ # `Bluebook#verbs` lists it, so a scope can never name a command
22
+ # the domain does not have (spec/projections/oidc_projection_spec.rb
23
+ # holds the two lists equal), and `role` is the same string
24
+ # `Ports::Authorization.holds_role?` compares against a real
25
+ # `Governance::RoleAssignment`.
26
+ #
27
+ # ROLES COME FROM THE COMMANDS, NOT FROM GOVERNANCE. A command's own
28
+ # `role "Compliance officer"` is in the bluebook IR
29
+ # (`Command#role`), whereas `uses_framework "Governance"` is
30
+ # declared in the `.hecksagon` — which `call(bluebook:, options:)`
31
+ # cannot see at all. Reading the commands is both the only thing
32
+ # available here AND the more accurate source: it says what each
33
+ # command actually demands, not merely which role vocabulary the
34
+ # application happened to mount.
35
+ #
36
+ # A command with no declared role projects `"role" => nil` rather
37
+ # than being dropped — an unguarded command is a real fact about the
38
+ # domain, and silently omitting it would make the manifest read as
39
+ # though the command did not exist.
40
+ module OIDC
41
+ extend Projector::Target
42
+
43
+ projects_as :oidc
44
+
45
+ module_function
46
+
47
+ # `audience:` overrides the domain name, for the ordinary case
48
+ # where the IdP's registered audience is a URL rather than a bare
49
+ # chapter name.
50
+ def call(bluebook:, options: {})
51
+ scopes = scopes_for(bluebook)
52
+
53
+ {
54
+ "audience" => (options[:audience] || bluebook.name).to_s,
55
+ "scopes" => scopes,
56
+ "roles" => scopes.map { |scope| scope["role"] }.compact.uniq.sort
57
+ }
58
+ end
59
+
60
+ # Sorted by scope, not left in declaration order — a manifest is
61
+ # something two versions of get diffed, and the same precedent
62
+ # `StorageShape.project` sets for its own aggregate list.
63
+ #
64
+ # Recurses into entities, not just an aggregate's own direct
65
+ # commands — the exact same gap `Bluebook#verbs` closed for the
66
+ # same reason (`chapter.rb`'s own comment): a command reached
67
+ # through `Dispatcher#dispatch`'s dotted `Entity.Command` routing
68
+ # is real and callable, so a manifest that never names it can
69
+ # never grant a client a scope for it either.
70
+ def scopes_for(bluebook)
71
+ bluebook.aggregates.flat_map { |aggregate| aggregate_scopes(bluebook, aggregate) }
72
+ .sort_by { |scope| scope["scope"] }
73
+ end
74
+
75
+ def aggregate_scopes(bluebook, aggregate)
76
+ verb_prefix = "#{bluebook.name}::#{aggregate.hecks_name}"
77
+ scope_prefix = "#{Naming.snake(bluebook.name)}:#{Naming.snake(aggregate.hecks_name)}"
78
+
79
+ command_scopes(aggregate.commands, verb_prefix, scope_prefix) +
80
+ aggregate.entities.flat_map { |entity| entity_scopes(entity, verb_prefix, scope_prefix) }
81
+ end
82
+
83
+ # S17, ADR 0026 — an entity can nest further entities (`Dispatch`,
84
+ # inside `Handler`), so this recurses the same way `Chapter#verbs`
85
+ # now does. The verb and scope prefixes grow in lockstep, each
86
+ # `.`-joined the same way its own kind already was.
87
+ def entity_scopes(entity, verb_prefix, scope_prefix)
88
+ verb_prefix = "#{verb_prefix}.#{entity.hecks_name}"
89
+ scope_prefix = "#{scope_prefix}.#{Naming.snake(entity.hecks_name)}"
90
+
91
+ command_scopes(entity.commands, verb_prefix, scope_prefix) +
92
+ entity.entities.flat_map { |piece| entity_scopes(piece, verb_prefix, scope_prefix) }
93
+ end
94
+
95
+ # `banking:account.open` — the shape an OIDC scope is conventionally
96
+ # spelled in, and snake_cased through the SAME `Naming.snake` the
97
+ # facade uses to name a command's own door method, so a scope and
98
+ # the Ruby call that satisfies it cannot drift apart.
99
+ def command_scopes(commands, verb_prefix, scope_prefix)
100
+ commands.map do |command|
101
+ {
102
+ "scope" => "#{scope_prefix}.#{Naming.snake(command.hecks_name)}",
103
+ "verb" => "#{verb_prefix}.#{command.hecks_name}",
104
+ "role" => command.role
105
+ }
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,159 @@
1
+ require_relative "../projector"
2
+
3
+ module Hecks
4
+ module Projections
5
+ # THE RUST PARSER'S KEYWORD TABLE, projected from the chapter's own
6
+ # Syntax aggregate — "the parser's grammar knowledge is DERIVED from
7
+ # hecks's self-description, not hand-typed a second time", which
8
+ # is the anti-drift idea the whole Rust-parser plan rests on.
9
+ #
10
+ # A REGISTERED TARGET NOW, rather than a module living inside its own
11
+ # bin/ script. It was already a projection in everything but call
12
+ # shape; this only stops it being a fifth way of spelling one.
13
+ module ParserTable
14
+ extend Projector::Target
15
+ projects_as :parser_table, declares: "Syntax"
16
+
17
+ module_function
18
+
19
+ def call(bluebook:, options: {}) = render(bluebook)
20
+
21
+ module_function
22
+
23
+ # THE CHAPTER IS HANDED OVER, not reached for. This used to open the
24
+ # grammar registry itself, which meant the projection could only ever
25
+ # project one chapter — the language's own. Taking it as an argument is
26
+ # what the projector protocol asks for, and it costs nothing.
27
+ def syntax(bluebook) = bluebook.aggregate("Syntax")
28
+
29
+ # Every cell as text — exactly spec/syntax_conformance_spec.rb's own
30
+ # `rows` helper, reused rather than re-derived: a member's fields decode
31
+ # back through typed literal decoding on the way out of reconstruction,
32
+ # and this reads it back as what was written.
33
+ def rows(bluebook, name)
34
+ syntax(bluebook).value_objects.find { |vo| vo.hecks_name == name }
35
+ .members.map { |row| row.to_h.transform_values(&:to_s) }
36
+ end
37
+
38
+ def closed_set_members(bluebook, name) = rows(bluebook, name).map { |row| row[:name] }
39
+
40
+ KEYWORD_FIELDS = %i[word context body inner opens fills status was resolves_via disambiguator].freeze
41
+ ARGUMENT_FIELDS = %i[keyword context at named kind required fills selects
42
+ pair_key_fills pair_value_fills pairs_shape status variadic minimum].freeze
43
+
44
+ # A Rust string literal for one field's value — every field here is
45
+ # plain ASCII (a word, a context name, a digit, "true"/"false"), so this
46
+ # only has to be safe against the two characters Rust string literals
47
+ # themselves reserve.
48
+ def rust_string(value) = "\"#{value.to_s.gsub('\\', '\\\\\\\\').gsub('"', '\\"')}\""
49
+
50
+ def keyword_row(row)
51
+ fields = KEYWORD_FIELDS.map { |field| rust_string(row[field]) }
52
+ " KeywordRow { #{KEYWORD_FIELDS.zip(fields).map { |name, value| "#{name}: #{value}" }.join(', ')} },"
53
+ end
54
+
55
+ def argument_row(row)
56
+ fields = ARGUMENT_FIELDS.map { |field| rust_string(row[field]) }
57
+ " ArgumentRow { #{ARGUMENT_FIELDS.zip(fields).map { |name, value| "#{name}: #{value}" }.join(', ')} },"
58
+ end
59
+
60
+ def const_str_array(name, values)
61
+ lines = values.map { |value| " #{rust_string(value)}," }
62
+ "pub static #{name}: &[&str] = &[\n#{lines.join("\n")}\n];\n"
63
+ end
64
+
65
+ # S14, ADR 0026 — Keyword/Argument are genuine entities of Syntax
66
+ # now, dispatched (not merely declared) so their own `status`
67
+ # really is a lifecycle. `SyntaxBoot.call` reads the STILL-STATIC
68
+ # seed rows (`KeywordSeed`/`ArgumentSeed`), dispatches each one
69
+ # through the real admission/lifecycle door, and hands back the
70
+ # exact same shape `rows` used to read straight off the closed set
71
+ # — symbol keys, string values, `status` included — so nothing
72
+ # else in this file needed to change.
73
+ def render(bluebook)
74
+ table = Hecks::Bluebook::MetaValidator::SyntaxBoot.call
75
+ keywords = table[:keywords]
76
+ arguments = table[:arguments]
77
+
78
+ <<~RUST
79
+ // GENERATED by bin/project_parser_table from
80
+ // aggregate-local syntax tables under lib/hecks/language/ — DO NOT EDIT BY HAND.
81
+ //
82
+ // Re-run `bin/project_parser_table` after any syntax-table change.
83
+ // spec/parser_table_spec.rb regenerates this into memory and fails the
84
+ // normal `bundle exec rspec` suite the moment this file drifts from what
85
+ // the language currently declares — see that spec and
86
+ // bin/project_parser_table's own header for why this file is never
87
+ // hand-edited.
88
+
89
+ #[derive(Debug, Clone, Copy)]
90
+ pub struct KeywordRow {
91
+ pub word: &'static str,
92
+ pub context: &'static str,
93
+ pub body: &'static str,
94
+ pub inner: &'static str,
95
+ pub opens: &'static str,
96
+ pub fills: &'static str,
97
+ pub status: &'static str,
98
+ pub was: &'static str,
99
+ /// Which shared Hecks::Bluebook::DSL::RuleReference
100
+ /// primitive this word's own bare form resolves through,
101
+ /// once it has one ("hash_chain" / "owner_keyed" /
102
+ /// "sibling_scan") — "" for every word that only ever
103
+ /// declares.
104
+ pub resolves_via: &'static str,
105
+ /// Which disambiguator keyword argument a bare reference
106
+ /// may supply — "declared_by" today, "" otherwise.
107
+ pub disambiguator: &'static str,
108
+ }
109
+
110
+ #[derive(Debug, Clone, Copy)]
111
+ pub struct ArgumentRow {
112
+ pub keyword: &'static str,
113
+ pub context: &'static str,
114
+ pub at: &'static str,
115
+ pub named: &'static str,
116
+ pub kind: &'static str,
117
+ pub required: &'static str,
118
+ pub fills: &'static str,
119
+ pub selects: &'static str,
120
+ pub pair_key_fills: &'static str,
121
+ pub pair_value_fills: &'static str,
122
+ pub pairs_shape: &'static str,
123
+ pub status: &'static str,
124
+ pub variadic: &'static str,
125
+ pub minimum: &'static str,
126
+ }
127
+
128
+ impl KeywordRow {
129
+ /// An absent status reads as admitted — the grown-column convention
130
+ /// syntax_conformance_spec.rb's own `status_of`/`live?` already use.
131
+ pub fn live(&self) -> bool {
132
+ matches!(self.status, "" | "admitted" | "deprecated")
133
+ }
134
+ }
135
+
136
+ impl ArgumentRow {
137
+ pub fn live(&self) -> bool {
138
+ matches!(self.status, "" | "admitted" | "deprecated")
139
+ }
140
+ }
141
+
142
+ pub static KEYWORDS: &[KeywordRow] = &[
143
+ #{keywords.map { |row| keyword_row(row) }.join("\n")}
144
+ ];
145
+
146
+ pub static ARGUMENTS: &[ArgumentRow] = &[
147
+ #{arguments.map { |row| argument_row(row) }.join("\n")}
148
+ ];
149
+
150
+ #{const_str_array('CONTEXTS', closed_set_members(bluebook, 'Context'))}
151
+ #{const_str_array('BODIES', closed_set_members(bluebook, 'Body'))}
152
+ #{const_str_array('ARGUMENT_KINDS', closed_set_members(bluebook, 'ArgumentKind'))}
153
+ #{const_str_array('PAIRS_SHAPES', closed_set_members(bluebook, 'PairsShape'))}
154
+ #{const_str_array('STATUSES', closed_set_members(bluebook, 'Status'))}
155
+ RUST
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,38 @@
1
+ require_relative "../doc/reference"
2
+ require_relative "../projector"
3
+
4
+ module Hecks
5
+ module Projections
6
+ # THE DSL REFERENCE PAGES, projected from the chapter's own Syntax
7
+ # aggregate — the tables come from the declaration, the prose is
8
+ # preserved from whatever is already committed.
9
+ #
10
+ # THE FIRST PROJECTION THAT EMITS A TREE. `emits: :files` says so,
11
+ # and the framework writes the map rather than inferring one: a Hash
12
+ # of path => contents and a Hash that merely holds strings are the
13
+ # same object to Ruby, so only the projection can know which it
14
+ # meant. This is the case the output contract was designed for and
15
+ # deliberately left unimplemented until something real needed it.
16
+ #
17
+ # NOT PURE, and it cannot be. `Doc::Reference.pages` READS the
18
+ # committed pages to harvest their prose, so the existing directory
19
+ # is an input — passed as `from:` rather than assumed, so the
20
+ # projection never reaches for a path of its own choosing.
21
+ module Reference
22
+ extend Projector::Target
23
+
24
+ projects_as :reference, declares: "Syntax", emits: :files
25
+
26
+ module_function
27
+
28
+ def call(bluebook:, options: {})
29
+ from = options[:from] or
30
+ raise ArgumentError,
31
+ ":reference harvests prose from the committed pages, so it needs " \
32
+ "`from:` — the directory they live in"
33
+
34
+ Doc::Reference.pages(from)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,44 @@
1
+ require_relative "../ports/persistence"
2
+ require_relative "../projector"
3
+
4
+ module Hecks
5
+ module Projections
6
+ # THE STORAGE SHAPE of one bluebook — the same structural form
7
+ # `StorageShape.mint_hash` hashes to name an era, which is what makes
8
+ # a bump/no-bump question answerable by diffing two of these.
9
+ #
10
+ # THE RETROFIT, and the point of it: `Runtime::StorageShape.project`
11
+ # was written long before this framework existed, and it already
12
+ # takes exactly one bluebook, already returns a plain Hash, already
13
+ # touches no disk and no live runtime — it even already uses the
14
+ # word. Nothing about it was shaped to fit `call(bluebook:, options:)`.
15
+ #
16
+ # That it fits anyway, by a two-line keyword adapter and no change to
17
+ # `StorageShape` itself, is the evidence that the protocol is a real
18
+ # description of what projections do here rather than a shape fitted
19
+ # around its own first example (`:ir`, whose `call` is `to_h`, proves
20
+ # much less on its own).
21
+ module Shape
22
+ extend Projector::Target
23
+
24
+ projects_as :shape
25
+
26
+ module_function
27
+
28
+ # ADR 0033 — `Runtime::StorageShape` lives in the era persistence
29
+ # plugin now; the `:shape` projection itself stays registered
30
+ # unconditionally (the seam `spec/projector_seam_spec.rb` enforces
31
+ # is "every file here registers," not "every projection always
32
+ # succeeds") but refuses clearly if asked to run with the plugin
33
+ # unloaded, rather than raising on an undefined constant.
34
+ def call(bluebook:, options: {})
35
+ unless Ports::Persistence.plugin?(:era)
36
+ raise "the :shape projection needs the era persistence plugin loaded " \
37
+ "(require \"hecks/ports/persistence/plugins/era\")"
38
+ end
39
+
40
+ Runtime::StorageShape.project(bluebook)
41
+ end
42
+ end
43
+ end
44
+ end