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,221 @@
1
+ require_relative "generic_dispatch"
2
+ module Hecks
3
+ module Bluebook
4
+ module DSL
5
+ # THE RUBY-SIDE `word_gate` (`rust/parser/src/parse/mod.rs`'s own,
6
+ # read there first — this is the same job, one level up: Rust's own
7
+ # gate refuses a mistyped/inadmissible WORD directly, at the
8
+ # moment it reads a line of `.bluebook` SOURCE TEXT, before any
9
+ # per-construct parsing runs. Ruby never lexes source text — a
10
+ # `.bluebook` file IS Ruby, so `given("x")` is ALREADY a real
11
+ # method call by the time any of this code runs, and Ruby's own
12
+ # method dispatch already refuses an ARITY mismatch on an
13
+ # EXISTING method for free. What Ruby's own dispatch does NOT do
14
+ # is consult the self-hosted grammar table AT ALL — a mistyped
15
+ # word (`giv3n("x")`) or a word used in the wrong context
16
+ # (`identified_by` inside a `command` block) just raises Ruby's
17
+ # own generic `NoMethodError`, naming nothing about what the
18
+ # language actually admits. This module closes that gap.
19
+ #
20
+ # `method_missing`/`respond_to_missing?` ONLY — a word this
21
+ # builder class already answers with an ordinary `def` (or a
22
+ # shared mixin method like `AttributeCollector#attribute`) never
23
+ # reaches this module at all; Ruby's own method lookup finds it
24
+ # first, and item #13's later slices haven't removed every one of
25
+ # those yet. This WAS "zero behavior change for every currently-
26
+ # valid line in the entire corpus" in its own first slice — since
27
+ # item #13's full metaprogrammed dispatch (slice 1, whole-project
28
+ # table-unification survey) started REMOVING the hand-written
29
+ # methods this module's own admissibility check used to defer to,
30
+ # some words now execute for real here too, via `GenericDispatch`
31
+ # — see that module's own header for exactly which ones, and the
32
+ # full account of what was verified before each was migrated. A
33
+ # mistyped or wrongly-contexted word still gets the same real,
34
+ # helpful, table-driven refusal Rust's own `word_gate` already
35
+ # gives, instead of Ruby's own generic `NoMethodError` — that half
36
+ # is genuinely unchanged.
37
+ #
38
+ # `self.class::GRAMMAR_CONTEXT` — each including class names which
39
+ # row of the self-hosted `Context` closed set it corresponds to
40
+ # (`AggregateBuilder::GRAMMAR_CONTEXT = "Aggregate"`, etc.) — the
41
+ # SAME string `word_gate`'s own `context` parameter already is on
42
+ # the Rust side, read off the identical table.
43
+ #
44
+ # BOOTSTRAPPING GATED, the same reason `RuleReference#lookup`
45
+ # already is (`rule_reference.rb`'s own comment has the full
46
+ # story) — the meta-domain's own bootstrap calls dozens of
47
+ # keywords on itself before its own grammar table exists to
48
+ # check them against. For most words, this module steps aside
49
+ # entirely during bootstrap (`super`, Ruby's own ordinary
50
+ # `NoMethodError`) — the exact behavior every builder already had
51
+ # before this module existed, and (unlike `RuleReference`) there
52
+ # is deliberately no fallback covering the whole ~200-row table —
53
+ # that would defeat the entire point.
54
+ #
55
+ # ONE NARROW EXCEPTION, added in item #13's full metaprogrammed
56
+ # dispatch (slice 3, whole-project table-unification survey):
57
+ # `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`, a SMALL, EXPLICIT
58
+ # table naming the same (context, word) -> method pairs the real
59
+ # `calls:` column would say once it exists — used ONLY for words
60
+ # BOTH migrated to the `calls:` shape AND bootstrap-reachable
61
+ # (`attribute`, today). This is the one place in this whole arc a
62
+ # bootstrap fallback was worth building despite `RuleReference`'s
63
+ # own precedent against it: it duplicates NO logic, only a METHOD
64
+ # NAME — `attribute_impl`'s own real, hand-written body is called
65
+ # either way, bootstrap or not, so there is nothing here that can
66
+ # drift the way a full behavioral duplicate could.
67
+ #
68
+ # TWO FURTHER PIECES, both slice 5:
69
+ # - `word_gate_dispatch` is the same admission+dispatch logic
70
+ # `method_missing` below runs, factored out so a class with
71
+ # its OWN class-level `method_missing` (`HecksagonBuilder`'s/
72
+ # `WorldBuilder`'s genuinely open-ended verb vocabulary) can
73
+ # call it directly and fall back to its own open-verb handling
74
+ # only when this returns `NOT_ADMITTED` — a class-level `def`
75
+ # always wins over an included module's in Ruby's own method
76
+ # resolution, so their OWN `method_missing` is the only one
77
+ # that ever runs for them, and this is what lets a real,
78
+ # closed-set word (`port`/`realm`/`latest`) still reach
79
+ # `GenericDispatch` despite that.
80
+ # - the "Type"-position fallback inside `word_gate_dispatch`
81
+ # itself, for `one_of`/`list_of` — called inside an
82
+ # attribute's own type argument, with `self` as whatever
83
+ # builder is currently `instance_eval`ing, never a dedicated
84
+ # "Type" builder `self.class::GRAMMAR_CONTEXT` could ever
85
+ # name. See that method's own comment.
86
+ module WordGate
87
+ # A caller with its OWN class-level `method_missing`
88
+ # (`HecksagonBuilder`/`WorldBuilder` — see `word_gate_dispatch`'s
89
+ # own header) checks for this to tell "not a word the grammar
90
+ # admits at all" apart from every other outcome below (a real
91
+ # dispatch, or a raised refusal) — never raised itself, so a
92
+ # caller can fall through to its own open-ended handling instead.
93
+ NOT_ADMITTED = Object.new.freeze
94
+
95
+ # PRIVATE, matching Ruby's own convention for both (`Object`
96
+ # defines them private too) — and load-bearing here, not just
97
+ # style: `spec/syntax_conformance_spec.rb`'s "declares every
98
+ # word X answers" check walks `public_instance_methods`, and a
99
+ # public `method_missing`/`respond_to_missing?` would show up
100
+ # there as two more "answered words" no row of the grammar ever
101
+ # declares, on every builder this module touches.
102
+
103
+ private
104
+
105
+ def method_missing(word, *args, **kwargs, &block)
106
+ if MetaValidator.bootstrapping?
107
+ fallback = GenericDispatch::BOOTSTRAP_CALLS_FALLBACK
108
+ # Own context first, "Type" second — the same order the
109
+ # ordinary (non-bootstrapping) `word_gate_dispatch` path
110
+ # below checks them in, for the same reason: `list_of`/
111
+ # `one_of` used in an attribute's own type position never
112
+ # arrive with `self.class::GRAMMAR_CONTEXT == "Type"`.
113
+ target = fallback[[self.class::GRAMMAR_CONTEXT, word.to_s]] || fallback[["Type", word.to_s]]
114
+ return send(target, *args, **kwargs, &block) if target
115
+
116
+ return super
117
+ end
118
+
119
+ result = word_gate_dispatch(word, args, kwargs, block)
120
+ return super if result.equal?(NOT_ADMITTED)
121
+
122
+ result
123
+ end
124
+
125
+ # THE CORE ADMISSION+DISPATCH LOGIC, factored out of
126
+ # `method_missing` — item #13's full metaprogrammed dispatch
127
+ # (slice 5, whole-project table-unification survey) — so a
128
+ # class with its OWN class-level `method_missing`
129
+ # (`HecksagonBuilder`'s/`WorldBuilder`'s genuinely open-ended
130
+ # verb vocabulary, `persisted_by "Heki"`/`posted_by "Carrier"`,
131
+ # can never BE a closed table) can still route a word THE
132
+ # GRAMMAR ADMITS through here first, falling back to its own
133
+ # open-verb handling only for what this doesn't recognize at
134
+ # all — unblocking `Hecksagon#port`/`World#realm`/`World#latest`,
135
+ # each a real, closed-set word that happened to sit on a class
136
+ # whose own `method_missing` a class-level `def` always wins
137
+ # over Ruby's own module-inclusion order.
138
+ #
139
+ # NEVER RAISES "not admitted" — returns `NOT_ADMITTED` instead,
140
+ # so the caller (this module's own `method_missing`, or one of
141
+ # the two above) decides what that means for it. DOES still
142
+ # raise the richer, table-driven refusals below once a word is
143
+ # admitted-but-unimplemented, or admitted-somewhere-else-only —
144
+ # those are real, useful refusals regardless of which
145
+ # `method_missing` is asking.
146
+ def word_gate_dispatch(word, args, kwargs, block)
147
+ context = self.class::GRAMMAR_CONTEXT
148
+ rows = MetaValidator::SyntaxBoot.call
149
+ keywords = rows[:keywords]
150
+ admitted = keywords.select { |row| row[:context] == context && (row[:word] == word.to_s || row[:was] == word.to_s) }
151
+
152
+ # THE TYPE-POSITION FALLBACK — item #13's full metaprogrammed
153
+ # dispatch (slice 5). `one_of`/`list_of`, called inside an
154
+ # attribute's own type argument (`attribute :x,
155
+ # one_of("a","b")`), run with `self` as whatever builder is
156
+ # CURRENTLY instance_eval'ing — there is no dedicated "Type"
157
+ # builder of its own; every attribute()-taking builder answers
158
+ # these identically, through the shared `AttributeCollector`
159
+ # mixin. `self.class::GRAMMAR_CONTEXT` can never actually BE
160
+ # "Type", so a word admitted only there would otherwise look
161
+ # inadmissible everywhere. Checked only once THIS context has
162
+ # already come up empty, so a context with its own same-named
163
+ # row (`ValueObject`'s own `one_of`, the block-wrapper form)
164
+ # keeps using that instead, unaffected.
165
+ if admitted.empty?
166
+ type_admitted = keywords.select { |row| row[:context] == "Type" && row[:word] == word.to_s }
167
+ unless type_admitted.empty?
168
+ admitted = type_admitted
169
+ context = "Type"
170
+ end
171
+ end
172
+
173
+ return NOT_ADMITTED if admitted.empty? && !admitted_anywhere?(keywords, word)
174
+
175
+ if admitted.empty?
176
+ legal = keywords.select { |row| row[:context] == context }.map { |row| row[:word] }.uniq.sort
177
+ raise Malformed,
178
+ "'#{word}' is not a word #{context} admits — legal words here: #{legal.join(', ')}"
179
+ end
180
+
181
+ # ITEM #13's FULL METAPROGRAMMED DISPATCH — the word IS
182
+ # admitted here, and this builder has no hand-written method
183
+ # left for it; before falling through to the "not yet
184
+ # implemented" refusal every word without one still gets,
185
+ # offer it to GenericDispatch — the SAFE, verified subset of
186
+ # words whose whole behavior is now executed off this same
187
+ # table, not just checked against it.
188
+ dispatched = GenericDispatch.try(self, context, word.to_s, args, kwargs, block, rows)
189
+ return dispatched unless dispatched.equal?(GenericDispatch::NOT_HANDLED)
190
+
191
+ raise Malformed,
192
+ "'#{word}' is admitted by #{context}'s own grammar, but #{self.class} has no " \
193
+ "builder method for it yet — not yet implemented"
194
+ end
195
+
196
+ def respond_to_missing?(word, include_private = false)
197
+ return super if MetaValidator.bootstrapping?
198
+
199
+ context = self.class::GRAMMAR_CONTEXT
200
+ keywords = MetaValidator::SyntaxBoot.call[:keywords]
201
+ keywords.any? { |row| row[:context] == context && (row[:word] == word.to_s || row[:was] == word.to_s) } ||
202
+ keywords.any? { |row| row[:context] == "Type" && row[:word] == word.to_s } ||
203
+ super
204
+ end
205
+
206
+ # A word admitted SOMEWHERE, just not in THIS context, still
207
+ # falls through to Ruby's own `NoMethodError` rather than this
208
+ # module's own richer refusal — `method_missing` fires for
209
+ # every typo in the whole codebase (this class's own genuinely
210
+ # private helper methods included), not just DSL keyword calls;
211
+ # only raise the RICH, table-driven message when the word is at
212
+ # least SOMETHING the grammar knows about, anywhere, so an
213
+ # unrelated Ruby-level typo inside a builder's own private code
214
+ # keeps its own ordinary, unconfusing `NoMethodError`.
215
+ def admitted_anywhere?(rows, word)
216
+ rows.any? { |row| row[:word] == word.to_s || row[:was] == word.to_s }
217
+ end
218
+ end
219
+ end
220
+ end
221
+ end
@@ -0,0 +1,77 @@
1
+ require_relative "word_gate"
2
+ module Hecks
3
+ module Bluebook
4
+ module DSL
5
+ class SettingsCollector
6
+ def initialize = @values = {}
7
+
8
+ def method_missing(key, *args, &_block)
9
+ @values[key.to_sym] = args.size == 1 ? args.first : args
10
+ end
11
+
12
+ def respond_to_missing?(_name, _include_private = false) = true
13
+
14
+ def to_h = @values
15
+ end
16
+
17
+ class WorldBuilder
18
+ GRAMMAR_CONTEXT = "World"
19
+
20
+ include WordGate
21
+
22
+ def initialize(domain)
23
+ @domain = domain
24
+ @settings = {}
25
+ end
26
+
27
+ # RENAMED FROM `realm`/`latest` — item #13's full metaprogrammed
28
+ # dispatch (slice 5). Neither bootstrap-reachable (checked
29
+ # directly). Reached through `WordGate#method_missing`'s new
30
+ # `word_gate_dispatch`, called explicitly below since
31
+ # `WorldBuilder`'s own class-level `method_missing` (the
32
+ # open-verb catch-all beneath this) always wins over the
33
+ # module's — see `word_gate.rb`'s own header for the full
34
+ # mechanism.
35
+ def realm_impl(value)
36
+ @realm = required(value, "realm")
37
+ end
38
+
39
+ def latest_impl(value)
40
+ @latest = required(value, "latest version")
41
+ end
42
+
43
+ def method_missing(verb, *args, **kwargs, &block)
44
+ result = word_gate_dispatch(verb, args, kwargs, block)
45
+ return result unless result.equal?(WordGate::NOT_ADMITTED)
46
+
47
+ collector = SettingsCollector.new
48
+ collector.instance_eval(&block) if block
49
+ value = { adapter: args.first.to_s }.merge(kwargs).merge(collector.to_h)
50
+ @settings[verb.to_s] = value
51
+ @settings["#{verb}:#{args.first.to_s.downcase}"] = value
52
+ end
53
+
54
+ def respond_to_missing?(_name, _include_private = false) = true
55
+
56
+ def build
57
+ MetaValidator.call_world(
58
+ World.new(domain: @domain, realm: @realm, latest: @latest, settings: @settings)
59
+ )
60
+ end
61
+
62
+ def self.build(domain, &block)
63
+ builder = new(domain)
64
+ builder.instance_eval(&block) if block
65
+ builder.build
66
+ end
67
+
68
+ private
69
+
70
+ def required(value, label)
71
+ # moved to the language: Realm / Latest invariants, in world.bluebook
72
+ value.to_s
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,45 @@
1
+ module Hecks
2
+ module Bluebook
3
+ # The authoring surface — one builder per declaration kind, collecting
4
+ # what a .bluebook file says into the IR. Builders call the
5
+ # meta-validator at build time, not at load time, so their require
6
+ # order here is the readable one.
7
+ module DSL
8
+ end
9
+ end
10
+ end
11
+
12
+ # The query/read-model builders reach QuerySpecification at class-body
13
+ # level and cannot say so themselves (contents frozen) — the namespace
14
+ # file says it for them. The builders also build IR, at build time.
15
+ require_relative "../construct"
16
+ require_relative "../query_specification"
17
+ # The model the builders construct — `bluebook.rb` requires the whole
18
+ # set; this names the one file that declares the chapter class itself,
19
+ # which every builder returns into.
20
+ require_relative "chapter"
21
+
22
+ require_relative "dsl/malformed"
23
+ require_relative "dsl/const_shim"
24
+ require_relative "dsl/attribute_collector"
25
+ require_relative "dsl/identity_declaration"
26
+ require_relative "dsl/rule_reference"
27
+ require_relative "dsl/word_gate"
28
+ require_relative "dsl/value_object_builder"
29
+ require_relative "dsl/command_builder"
30
+ require_relative "dsl/lifecycle_builder"
31
+ require_relative "dsl/query_builder"
32
+ require_relative "dsl/read_model_builder"
33
+ require_relative "dsl/entity_builder"
34
+ require_relative "dsl/policy_builder"
35
+ require_relative "dsl/process_manager_builder"
36
+ require_relative "dsl/port_operation_builder"
37
+ require_relative "dsl/domain_port_builder"
38
+ require_relative "dsl/aggregate_builder"
39
+ require_relative "dsl/bluebook_builder"
40
+ require_relative "dsl/binding_proxy"
41
+ require_relative "dsl/hecksagon_builder"
42
+ require_relative "dsl/port_builder"
43
+ require_relative "dsl/adapter_builder"
44
+ require_relative "dsl/world_builder"
45
+ require_relative "dsl/translation_builder"
@@ -0,0 +1,103 @@
1
+ require_relative "behaviour/entity"
2
+
3
+ module Hecks
4
+ module Bluebook
5
+ # An entity, as a RUBY CLASS — a piece of an aggregate that has an identity
6
+ # of its own.
7
+ #
8
+ # Crossing over closes the OWNER CHAIN. An entity declares commands, and
9
+ # until now those commands had no owner that could state an identity: an
10
+ # entity was an IR object, not a construct, so `Construct#hecks_fqn` refused
11
+ # rather than answering "Deposit" and looking right. Four of banking's
12
+ # commands were in that state. They can say what they are now —
13
+ # `Banking::Account.Ledger.Deposit` — which is the id the judge already mints
14
+ # for them.
15
+ #
16
+ # NOT const_set, for the same reason a command is not: a name inside one
17
+ # aggregate can denote more than one kind of thing, so the constant tree
18
+ # cannot index it.
19
+ #
20
+ # It must stay STRUCTURALLY INTERCHANGEABLE with an aggregate — the runtime
21
+ # builds `Instance.new(aggregate: entity)` and `CommandRules` takes either as
22
+ # `declaring` — so it answers `hecks_name`, `attributes`, `attribute`,
23
+ # `identified_by` and `lifecycle` exactly as an aggregate does. And it must
24
+ # keep NOT answering `value_object`: `Value.for_attribute` sniffs for that
25
+ # method to tell a piece from a head.
26
+ class Entity
27
+ extend Construct
28
+ extend Hecks::IR
29
+ extend Behaviour::Entity
30
+
31
+ emits_ir(
32
+ name: :hecks_name,
33
+ description: :description,
34
+ identified_by: :identity_paths,
35
+ attributes: many(:attributes),
36
+ commands: many(:commands),
37
+ queries: many(:queries),
38
+ # S17, ADR 0026 — "That is what `entity` is for, and `entity` is
39
+ # declared by the language and used zero times in it" (the ADR's
40
+ # own words). Dispatch nests inside Handler, so an entity's own
41
+ # NESTED entities are part of its wire shape now, the same way
42
+ # an aggregate's always were — the field the ADR names as
43
+ # declared-but-unused until this slice.
44
+ entities: many(:entities),
45
+ # ADR 0028 — a piece's own `given`, the SAME shape
46
+ # `Aggregate#preconditions` already carries (its own `emits_ir`
47
+ # row, identical). A precondition shared across this piece's own
48
+ # commands, declared once — a command references it back by
49
+ # name; the resolved text still lands on EACH referencing
50
+ # command's own `givens` either way, so this field is read-only
51
+ # documentation of what the piece itself declared, the same
52
+ # relationship `Aggregate.preconditions` already has to its own
53
+ # commands.
54
+ preconditions: -> { preconditions.map { |rule| { description: rule.description, canonical: rule.canonical } } },
55
+ # A piece's OWN shape rule, checked against EVERY instance of
56
+ # this piece the aggregate holds (Admissibility#enforce_
57
+ # invariants' own recursive walk) — the SAME relationship
58
+ # `ValueObject#invariants` already has to its own instances,
59
+ # one level up the construct tree. Not a separate enforcement
60
+ # boundary; still checked at the SAME two points (after every
61
+ # mutation, before save) the aggregate's own invariants always
62
+ # were — see that method's own comment for why this does not
63
+ # contradict "there is no separate entity invariant."
64
+ invariants: -> { invariants.map { |rule| { description: rule.description, canonical: rule.canonical } } },
65
+ lifecycle: one(:lifecycle)
66
+ )
67
+
68
+ class << self
69
+ attr_reader :description, :identified_by, :identity_paths, :identity_heads,
70
+ :attributes, :commands, :queries, :entities, :preconditions, :invariants, :lifecycle
71
+
72
+ def declare(name:, description: nil, identified_by: nil, attributes: [],
73
+ commands: [], queries: [], entities: [], preconditions: [], invariants: [], lifecycle: nil)
74
+ piece = Class.new(self)
75
+ piece.hecks_name = name.to_s
76
+ piece.absorb(description: description, identified_by: identified_by,
77
+ attributes: attributes, commands: commands,
78
+ queries: queries, entities: entities, preconditions: preconditions,
79
+ invariants: invariants, lifecycle: lifecycle)
80
+ piece.stamp_children
81
+ piece
82
+ end
83
+
84
+ # Assigns what the language declares, then hands off to the
85
+ # behaviour's own `settle` — derived identity and the name
86
+ # indexes, neither of which the declaration states.
87
+ def absorb(description:, identified_by:, attributes:, commands:, queries:, entities:, preconditions:, invariants:, lifecycle:)
88
+ @description = description
89
+ @identified_by = identified_by
90
+ @attributes = attributes
91
+ @commands = commands
92
+ @queries = queries
93
+ @entities = entities
94
+ @preconditions = preconditions
95
+ @invariants = invariants
96
+ @lifecycle = lifecycle
97
+
98
+ settle
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,55 @@
1
+ require "json"
2
+ require_relative "../../vocabulary"
3
+
4
+ module Hecks
5
+ module Bluebook
6
+ module Expression
7
+ module CanonicalForm
8
+ Rule = Struct.new(:strategy, :source_token, :replacement, :boundary, :position, keyword_init: true)
9
+
10
+ STRATEGIES = Hecks::Vocabulary.fetch("NormalisationStrategy")
11
+
12
+ # READ, NOT RESTATED — the admitted normalisation rules, projected
13
+ # from the grammar chapter by bin/expression_projection exactly as
14
+ # the evaluator's operator table is. See Evaluator::PROJECTION for
15
+ # why a projection rather than a boot.
16
+ RULES = JSON.parse(
17
+ File.read(File.join(__dir__, "projection.json")), symbolize_names: true
18
+ ).fetch(:normalisations).map { |row| Rule.new(**row) }.freeze
19
+
20
+ module_function
21
+
22
+ def table
23
+ RULES.sort_by(&:position).map do |rule|
24
+ {
25
+ strategy: rule.strategy,
26
+ source_token: rule.source_token,
27
+ replacement: rule.replacement,
28
+ boundary: rule.boundary,
29
+ position: rule.position.to_s
30
+ }
31
+ end
32
+ end
33
+
34
+ def apply(source)
35
+ RULES.sort_by(&:position).reduce(source.to_s) { |text, rule| step(text, rule) }.strip
36
+ end
37
+
38
+ def step(text, rule)
39
+ case rule.strategy
40
+ when "collapse_whitespace" then text.gsub(/\s+/, " ")
41
+ when "replace" then replace(text, rule)
42
+ else
43
+ raise ArgumentError, "#{rule.strategy.inspect} is not a linked normalisation strategy"
44
+ end
45
+ end
46
+
47
+ def replace(text, rule)
48
+ return text.gsub(rule.source_token, rule.replacement) if rule.boundary == "none"
49
+
50
+ text.gsub(/#{Regexp.escape(rule.source_token)}(?![[:alnum:]_])/, rule.replacement)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end