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,191 @@
1
+ module Hecks
2
+ module Bluebook
3
+ module DSL
4
+ # `identified_by` — SHARED by AggregateBuilder and EntityBuilder
5
+ # only (S9, ADR 0025 — "EntityBuilder's duplicate identified_by and
6
+ # resolve_pending_identity! go"): a piece's own identity cannot
7
+ # spell differently from its aggregate's, and until this slice it
8
+ # was hand-duplicated onto both rather than shared.
9
+ #
10
+ # A SEPARATE MODULE from AttributeCollector on purpose — every
11
+ # `attribute()`-taking builder (Command, Query, PortOperation,
12
+ # ValueObject, ...) `include`s that one too, and none of THEM
13
+ # declares an identity of its own ; folding `identified_by` in
14
+ # there made it answer for six builders that never earned the
15
+ # word (`syntax_conformance_spec.rb` catches exactly this — a
16
+ # builder answering a method the grammar never grants it).
17
+ #
18
+ # Requires its includer to ALSO `include AttributeCollector`
19
+ # (for `attributes`/`resolve_identity_field!`/`resolve_identity_
20
+ # type!`, still declared there since every includer of THAT
21
+ # module needs them) and to supply `identity_pool` (private) —
22
+ # the value-object list a bare field's own type resolves
23
+ # against: AggregateBuilder's own `@value_objects + closed_sets`,
24
+ # or EntityBuilder's OWNER's, since a piece mints none of its own.
25
+ module IdentityDeclaration
26
+ # There are three live forms, deliberately distinguishable at the
27
+ # declaration site:
28
+ #
29
+ # identified_by AccountNumber, as: :number # one identity concept
30
+ # identified_by do ... end # a bespoke concept
31
+ # identified_by :branch, :number # an existing compound key
32
+ #
33
+ # One symbol is retired: it cannot say whether the author means a
34
+ # value concept or a field-shaped database key. Frozen source still
35
+ # reaches the old interpretation through `legacy_identified_by`.
36
+ # RENAMED FROM `identified_by` — item #13's full metaprogrammed
37
+ # dispatch (slice 4c), same shared-mixin shape `attribute_impl`
38
+ # already proved in slice 3: ONE renamed method, both Aggregate
39
+ # and Entity Keyword rows name it in `calls:`. Bootstrap-
40
+ # reachable (every self-hosted aggregate/entity declares an
41
+ # identity), so in BOOTSTRAP_CALLS_FALLBACK for both contexts.
42
+ def identified_by_impl(*targets, as: nil, &definition)
43
+ return legacy_identified_by(*targets, as: as, &definition) if MetaValidator.shadow_parsing?
44
+
45
+ refuse_second_identity!
46
+
47
+ if definition
48
+ raise Malformed,
49
+ "#{@name}.identified_by cannot combine a value-object type with a block" unless targets.empty?
50
+
51
+ type_name = identity_value_object_name
52
+ value_object =
53
+ begin
54
+ ValueObjectBuilder.build(
55
+ type_name,
56
+ owner_value_objects: identity_pool,
57
+ &definition
58
+ )
59
+ rescue NameError => error
60
+ # A REMOVED SPELLING MUST REFUSE LOUDLY, not degrade into a raw
61
+ # Ruby error — the one contract `EraGuard.shadow_parse` leans
62
+ # on to know a normal parse genuinely could not read this text
63
+ # (only `Malformed` triggers its shadow-mode retry, era_guard.rb's
64
+ # own comment). The OLD `identified_by { name.value }` — a block
65
+ # whose text was NEVER CALLED, only extracted (legacy_
66
+ # identified_by, below) — is exactly this shape: read under the
67
+ # CURRENT grammar it is instead instance_eval'd as a value-object
68
+ # DEFINITION, and a bare identifier like `name` inside it resolves
69
+ # to nothing WordGate#method_missing recognizes, so Ruby itself
70
+ # raises NameError. Left uncaught, that NameError skipped
71
+ # shadow_parse's rescue entirely and reached callers as a raw
72
+ # crash instead of the frozen-era fallback that exists for
73
+ # precisely this text.
74
+ raise Malformed,
75
+ "#{@name}.identified_by do ... end could not be read as a value-object " \
76
+ "definition: #{error.message}"
77
+ end
78
+ if value_object.attributes.empty?
79
+ raise Malformed, "#{@name}.identified_by do declares no identity attributes"
80
+ end
81
+
82
+ install_identity_value_object!(value_object)
83
+ @identity_type_pending = [value_object, as || :identity, attributes.size]
84
+ return
85
+ end
86
+
87
+ raise Malformed, "#{@name}.identified_by names no identity" if targets.empty?
88
+
89
+ if targets.one? && identity_type?(targets.first)
90
+ @identity_type_pending = [targets.first, as, attributes.size]
91
+ return
92
+ end
93
+
94
+ if targets.one?
95
+ field = targets.first
96
+ raise Malformed,
97
+ "#{@name}.identified_by takes no as: — name the declared field itself" if as
98
+ # Transitional compatibility: the self-hosted language and live
99
+ # corpus still contain this form. Keep it readable until their
100
+ # exemplar-led migration is complete; the final lifecycle cutover
101
+ # replaces this assignment with the targeted refusal.
102
+ @identity_field_pending = field
103
+ return
104
+ end
105
+
106
+ unless targets.all? { |target| !identity_type?(target) }
107
+ raise Malformed,
108
+ "#{@name}.identified_by takes one value-object type or two or more attribute names, not both"
109
+ end
110
+ raise Malformed, "#{@name}.identified_by compound keys take no as:" if as
111
+
112
+ @identity_fields_pending = targets
113
+ end
114
+
115
+ private
116
+
117
+ # RESOLVES whichever of the three `identified_by` shapes is
118
+ # pending, against `identity_pool` — the includer's own private
119
+ # hook. Called at BUILD time, not at `identified_by`'s own call
120
+ # time — see `AttributeCollector#resolve_identity_field!`'s own
121
+ # comment on why.
122
+ def resolve_pending_identity!
123
+ if @identity_type_pending
124
+ type, as, insert_at = @identity_type_pending
125
+ @identity_paths = resolve_identity_type!(type, as, insert_at, identity_pool, @name)
126
+ elsif @identity_field_pending
127
+ @identity_paths = resolve_identity_field!(@identity_field_pending, identity_pool, @name)
128
+ elsif @identity_fields_pending
129
+ @identity_paths = @identity_fields_pending.flat_map do |field|
130
+ resolve_identity_field!(field, identity_pool, @name)
131
+ end
132
+ end
133
+ end
134
+
135
+ def identity_type?(target) = target.to_s.match?(/\A[A-Z]/)
136
+
137
+ def refuse_second_identity!
138
+ # During the staged migration a transitional one-symbol declaration
139
+ # may be replaced by the new declaration later in the same builder.
140
+ # This keeps existing builder fixtures/source readable while their
141
+ # exemplar is migrated. Once one-symbol identity is retired this
142
+ # compatibility branch disappears with it.
143
+ if @identity_field_pending && !@identity_type_pending && !@identity_fields_pending
144
+ @identity_field_pending = nil
145
+ return
146
+ end
147
+
148
+ return unless @identity_type_pending || @identity_field_pending || @identity_fields_pending ||
149
+ (@identity_paths && !@identity_paths.empty?)
150
+
151
+ raise Malformed, "#{@name} declares identified_by more than once"
152
+ end
153
+
154
+ # LEGACY — the two removed spellings (a value object + as:, and the
155
+ # multi-line block), kept alive ONLY for `EraGuard.shadow_parse`
156
+ # (S0a, ADR 0025) to still make sense of frozen era text that used
157
+ # them; unreachable outside `MetaValidator.shadow_parsing?`.
158
+ def legacy_identified_by(*targets, as:, &path)
159
+ target = targets.first
160
+ if target
161
+ raise Malformed, "#{@name}.identified_by takes a field name/value object or a block, not both" if path
162
+
163
+ if targets.size > 1
164
+ raise Malformed, "#{@name}.identified_by takes no as: with a compound key" if as
165
+
166
+ @identity_fields_pending = targets
167
+ return
168
+ end
169
+
170
+ if identity_type?(target)
171
+ @identity_type_pending = [target, as, attributes.size]
172
+ else
173
+ raise Malformed,
174
+ "#{@name}.identified_by :#{target} takes no as: — as: only applies to identified_by ValueObject" if as
175
+
176
+ @identity_field_pending = target
177
+ end
178
+ return
179
+ end
180
+
181
+ raise Malformed, "#{@name}.identified_by names no field" unless path
182
+
183
+ paths = Ports::Extraction.canonical(path).to_s.split(" ").reject(&:empty?)
184
+ raise Malformed, "#{@name}.identified_by names no field" if paths.empty?
185
+
186
+ @identity_paths = paths
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,44 @@
1
+ require_relative "word_gate"
2
+ module Hecks
3
+ module Bluebook
4
+ module DSL
5
+ class LifecycleBuilder
6
+ GRAMMAR_CONTEXT = "Lifecycle"
7
+
8
+ include WordGate
9
+
10
+ def initialize(field, default:)
11
+ @field = field
12
+ @default = default
13
+ @transitions = []
14
+ end
15
+
16
+ # RENAMED FROM `transition` — item #13's full metaprogrammed
17
+ # dispatch (slice 4c). Bootstrap-reachable (syntax.bluebook's
18
+ # own Keyword/Argument entities describe their `status`
19
+ # lifecycle with it), so in BOOTSTRAP_CALLS_FALLBACK.
20
+ def transition_impl(mapping)
21
+ mapping = mapping.dup
22
+ from = mapping.delete(:from)
23
+
24
+ mapping.each do |command, target|
25
+ @transitions << [
26
+ command.to_s,
27
+ StateTransition.new(target: target, from: from)
28
+ ]
29
+ end
30
+ end
31
+
32
+ def build
33
+ Lifecycle.new(field: @field, default: @default, transitions: @transitions)
34
+ end
35
+
36
+ def self.build(field, default:, &block)
37
+ builder = new(field, default: default)
38
+ builder.instance_eval(&block) if block
39
+ builder.build
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,7 @@
1
+ module Hecks
2
+ module Bluebook
3
+ module DSL
4
+ class Malformed < StandardError; end
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,120 @@
1
+ require_relative "word_gate"
2
+ module Hecks
3
+ module Bluebook
4
+ module DSL
5
+ class PolicyBuilder
6
+ GRAMMAR_CONTEXT = "Policy"
7
+
8
+ include WordGate
9
+
10
+ def initialize(name)
11
+ @name = name
12
+ end
13
+
14
+ # `on` — item #13's full metaprogrammed dispatch, slice 1
15
+ # (whole-project table-unification survey): a bare, kind-driven
16
+ # coerce-and-assign, now executed by `GenericDispatch`.
17
+
18
+ # `with:` — WHAT THE TRIGGER IS GIVEN, when the event's own shape
19
+ # is not it. Omitted, the whole event payload forwards verbatim,
20
+ # which is what every policy did before this existed.
21
+ #
22
+ # Same `key => value` shape a saga's own `dispatch ..., with:`
23
+ # takes, and read the same way at runtime: a Symbol names a field
24
+ # on the triggering event, anything else is a literal the policy
25
+ # supplies itself. The reason it exists is the reason a saga's
26
+ # does — a reaction crosses an aggregate boundary, and the event
27
+ # on one side is under no obligation to be shaped like the
28
+ # command on the other. Without it the target has to DECLARE
29
+ # every field the event happens to carry, whether it reads them
30
+ # or not.
31
+ # THE COMMAND ITSELF, NOT ITS NAME (ADR 0025, "events and
32
+ # reactions" — command references become first-class): `trigger
33
+ # Account::Debit`, a bare constant `ConstShim` resolves the same
34
+ # way `reference_to Account` always has, not a quoted verb string.
35
+ # Collapses the qualified/unqualified split this word and a
36
+ # saga's own `dispatch` used to disagree about — see
37
+ # `Naming.command_ref`'s own header for how the `::`/`.` rewrite
38
+ # works, and `SagaInterpreter#qualified` for why an unqualified
39
+ # form has always been enough (same-domain is the fallback, so
40
+ # `Account::Debit` and `Banking::Account::Debit` mean the same
41
+ # thing here).
42
+ #
43
+ # LEGACY UNDER SHADOW-PARSING (S0a's own bridge) — frozen era
44
+ # text still writes the quoted form.
45
+ #
46
+ # RENAMED FROM `trigger` — item #13's full metaprogrammed
47
+ # dispatch (slice 4), same reasoning as has_many_impl above: not
48
+ # bootstrap-reachable, reached through calls: with no fallback
49
+ # needed.
50
+ def trigger_impl(command_ref, with: nil)
51
+ if command_ref.is_a?(::String) && !MetaValidator.shadow_parsing?
52
+ raise Malformed,
53
+ "#{@name}'s trigger #{command_ref.inspect} is quoted text — give the bare command " \
54
+ "constant instead, e.g. trigger Account::Debit"
55
+ end
56
+
57
+ @trigger_command = Naming.command_ref(command_ref)
58
+ @with_spec = (with || {}).to_a
59
+ @projection_declared = !with.nil?
60
+ end
61
+
62
+ # `across` — item #13's full metaprogrammed dispatch, slice 1:
63
+ # same shape as `on`, above.
64
+
65
+ # THE GUARD — same extraction CommandBuilder#given/#ensures already
66
+ # use (Ports::Extraction reads the block's SOURCE ; the block itself
67
+ # is never called, here or at runtime — Runtime::PolicyInterpreter
68
+ # evaluates the extracted text through the same
69
+ # Bluebook::Expression::Evaluator a command's own given/ensures run
70
+ # through). No description argument the way given/ensures each
71
+ # carry one : a given's description becomes a GivenNotMet message,
72
+ # and a where that does not hold refuses nothing — it just means
73
+ # this policy does not apply to this event, exactly like an
74
+ # `event_qualifier` miss, which carries no message either.
75
+ #
76
+ # Evaluated against the triggering EVENT's OWN PAYLOAD, not a
77
+ # stored record — a policy reacts to what just happened, and has no
78
+ # aggregate instance of its own to read state from.
79
+ def where(&predicate)
80
+ canonical = Ports::Extraction.canonical(predicate)
81
+
82
+ if canonical.to_s.empty?
83
+ raise Malformed,
84
+ "#{@name}'s where did not survive extraction — its source " \
85
+ "could not be read, so no other runtime could ever evaluate it"
86
+ end
87
+
88
+ @where = canonical
89
+ end
90
+
91
+ # THE FAN-OUT SOURCE — a query verb, "Aggregate.query_name" or
92
+ # "Domain::Aggregate.query_name", the same qualified-or-not shape a
93
+ # saga's own `dispatch` command name already takes
94
+ # (SagaInterpreter#qualified). Runtime::PolicyInterpreter runs the
95
+ # named query against the triggering event's own payload and fires
96
+ # `trigger` once per row, instead of once for the event.
97
+ # `for_each` — item #13's full metaprogrammed dispatch, slice 1:
98
+ # same shape as `on`, above.
99
+
100
+ def build
101
+ Policy.new(
102
+ name: @name,
103
+ on_event: @on_event,
104
+ trigger_command: @trigger_command,
105
+ target_domain: @target_domain,
106
+ where: @where,
107
+ for_each: @for_each,
108
+ with_spec: @with_spec || []
109
+ ).tap { |policy| policy.instance_variable_set(:@projection_declared, !!@projection_declared) }
110
+ end
111
+
112
+ def self.build(name, &block)
113
+ builder = new(name)
114
+ builder.instance_eval(&block) if block
115
+ builder.build
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,30 @@
1
+ require_relative "word_gate"
2
+ module Hecks
3
+ module Bluebook
4
+ module DSL
5
+ class PortBuilder
6
+ GRAMMAR_CONTEXT = "Port"
7
+
8
+ include WordGate
9
+
10
+ def initialize(name)
11
+ @name = name
12
+ @signal = :reply
13
+ end
14
+
15
+ def verb(value) = @verb = value.to_s
16
+ def signal(value) = @signal = value.to_sym
17
+
18
+ def build
19
+ MetaValidator.call_port(Port.new(name: @name, verb: @verb, signal: @signal))
20
+ end
21
+
22
+ def self.build(name, &block)
23
+ builder = new(name)
24
+ builder.instance_eval(&block) if block
25
+ builder.build
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,142 @@
1
+ require_relative "word_gate"
2
+ module Hecks
3
+ module Bluebook
4
+ module DSL
5
+ class PortOperationBuilder
6
+ GRAMMAR_CONTEXT = "PortOperation"
7
+
8
+ include AttributeCollector
9
+ include WordGate
10
+
11
+ # `to:` — THE SANCTIONED REPLACEMENT for `reference_to` inside an
12
+ # operation body, added here rather than left as a documented-but-
13
+ # unbuilt promise: reference_to_impl's own refusal message has told
14
+ # authors to "pass the receiving aggregate in to:" since #335, but
15
+ # no `to:` argument existed anywhere in DomainPort's own grammar
16
+ # (domain_port.bluebook) for operation/tells/asks to receive it —
17
+ # confirmed by grep across every lib/hecks/language file, not
18
+ # assumed.
19
+ #
20
+ # GENUINE ROUTING METADATA, NOT AN ATTRIBUTE — matching
21
+ # rust/parser/src/parse/domain_port.rs's own header comment ("The
22
+ # receiving aggregate is routing metadata supplied by to:, not an
23
+ # operation attribute"), which anticipated this shape before either
24
+ # side actually built it. Stored separately (below, threaded to
25
+ # PortOperation as `to:`) rather than reusing reference_to_impl's
26
+ # own attribute-adding path — Dispatcher#port_invocation
27
+ # (lib/hecks/runtime/dispatcher.rb) is the ONE place that resolves
28
+ # routing at dispatch time; it gained a second, purely additive
29
+ # branch for this (falls back to a plain attribute NAMED for the
30
+ # owning aggregate's own identified_by field, the same "declare
31
+ # only external facts with attribute" the refusal message
32
+ # describes) rather than folding `to:` into identity_attribute's
33
+ # existing Reference-attribute scan, which every operation already
34
+ # in the corpus (Banking, pizzas, lifeadelics' own vendored
35
+ # PaymentGateway) still relies on unchanged.
36
+ def initialize(name, to: nil, owner: nil, direction: :inbound)
37
+ @name = name
38
+ @to = to && Naming.demodulise(to)
39
+ @owner = owner
40
+ @direction = direction
41
+ @emits = []
42
+ end
43
+
44
+ # ALWAYS an attribute, never the self-reference `CommandBuilder`
45
+ # spells — a port operation has no `creates?`/`acts_on` distinction
46
+ # to protect, so there is nothing for the self-reference branch to be
47
+ # FOR here. `reference_to Payment, as: :payment_id` reads the same
48
+ # even though the target happens to equal the owning aggregate.
49
+ # RENAMED FROM `reference_to` — item #13's full metaprogrammed
50
+ # dispatch (slice 4b). Bootstrap-reachable, in
51
+ # GenericDispatch::BOOTSTRAP_CALLS_FALLBACK.
52
+ #
53
+ # DISABLED, #335 — kept only for MetaValidator's own shadow-parsing
54
+ # pass (the self-hosted grammar's own KeywordSeed/ArgumentSeed rows
55
+ # for "reference_to" in this context are themselves declared using
56
+ # this construct, one level up — deleting the Ruby method would
57
+ # break the language describing itself, not just old domain
58
+ # authors). Every REAL domain author reaches `to:` instead, above —
59
+ # a genuinely different mechanism now, not a relocated spelling of
60
+ # this one (see `to:`'s own comment).
61
+ def reference_to_impl(type, as: nil)
62
+ unless MetaValidator.shadow_parsing?
63
+ raise Malformed,
64
+ "#{@name}.reference_to is behavioral routing, not retained domain state — " \
65
+ "pass the receiving aggregate in to: and declare only external facts with attribute"
66
+ end
67
+
68
+ add_reference!(type, as: as)
69
+ end
70
+
71
+ def emits(event_name) = @emits << event_name.to_s
72
+
73
+ # THE TWO HALVES OF AN `asks`. An outbound call has exactly two
74
+ # endings and the chapter names both — `answers` for what came back,
75
+ # `refuses` for what the outside said instead. They are separate words
76
+ # rather than two `emits` because a reader has to be able to tell them
77
+ # apart without reading the adapter: one is the thing you wanted, the
78
+ # other is the thing you have to handle.
79
+ # `answers`/`refuses` — item #13's full metaprogrammed dispatch,
80
+ # slice 1 (whole-project table-unification survey): both a bare,
81
+ # kind-driven coerce-and-assign with nothing else, now executed
82
+ # by `GenericDispatch`.
83
+
84
+ def build
85
+ outbound = @direction == :outbound
86
+ refuse_wrong_words!(outbound)
87
+
88
+ operation = PortOperation.new(
89
+ name: @name, attributes: attributes, emits: @emits,
90
+ direction: @direction, answers: @answers, refuses: @refuses, to: @to
91
+ )
92
+
93
+ # AN INBOUND OPERATION STILL HAS TO SAY SOMETHING. Only inbound: an
94
+ # `asks` says it with `answers`/`refuses` instead, and
95
+ # `refuse_wrong_words!` above has already insisted on both.
96
+ raise Malformed,
97
+ "#{@name} declares no emits — an operation with nothing to say " \
98
+ "afterward is a call into nothing" if !outbound && @emits.empty?
99
+
100
+ operation
101
+ end
102
+
103
+ private
104
+
105
+ # THE ONE PLACE a reference attribute actually gets added — both
106
+ # `to:` (initialize, above) and reference_to_impl's own shadow-
107
+ # parsing branch call this, so there is exactly one real
108
+ # implementation of "carry a Reference-typed external fact,"
109
+ # not two that could drift.
110
+ def add_reference!(type, as: nil)
111
+ target = Naming.demodulise(type)
112
+ attribute_impl(as || default_reference_name(target), Reference.new(target))
113
+ end
114
+
115
+ # EACH DIRECTION REFUSES THE OTHER'S WORDS. `emits` on an `asks` looks
116
+ # right and is not: it would name one ending and leave the other
117
+ # nowhere. `answers` on a `tells` is worse — there is no channel back
118
+ # to an inbound caller at all, so it would read as a promise the
119
+ # runtime cannot keep.
120
+ def refuse_wrong_words!(outbound)
121
+ if outbound
122
+ raise Malformed, "#{@name} is an asks and declares emits — name its two endings with " \
123
+ "answers and refuses instead" unless @emits.empty?
124
+ raise Malformed, "#{@name} declares no answers — an ask with no word for what came " \
125
+ "back cannot be reacted to" unless @answers
126
+ raise Malformed, "#{@name} declares no refuses — an ask that cannot fail is a call " \
127
+ "into a system you do not control, pretending otherwise" unless @refuses
128
+ else
129
+ raise Malformed, "#{@name} is a tells and declares #{@answers ? 'answers' : 'refuses'} — " \
130
+ "an inbound fact has no channel back to whoever sent it" if @answers || @refuses
131
+ end
132
+ end
133
+
134
+ def self.build(name, to: nil, owner: nil, direction: :inbound, &block)
135
+ builder = new(name, to: to, owner: owner, direction: direction)
136
+ builder.instance_eval(&block) if block
137
+ builder.build
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end