hecks 0.2.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (419) hide show
  1. checksums.yaml +5 -5
  2. data/lib/hecks/adapters/driven/claude_code.adapter +3 -0
  3. data/lib/hecks/adapters/driven/claude_code.rb +127 -0
  4. data/lib/hecks/adapters/driven/d1.adapter +12 -0
  5. data/lib/hecks/adapters/driven/d1.rb +427 -0
  6. data/lib/hecks/adapters/driven/folder.adapter +3 -0
  7. data/lib/hecks/adapters/driven/folder.rb +199 -0
  8. data/lib/hecks/adapters/driven/google_authentication.adapter +3 -0
  9. data/lib/hecks/adapters/driven/google_authentication.rb +100 -0
  10. data/lib/hecks/adapters/driven/governance_authorization.adapter +3 -0
  11. data/lib/hecks/adapters/driven/governance_authorization.rb +101 -0
  12. data/lib/hecks/adapters/driven/heki/journal.rb +56 -0
  13. data/lib/hecks/adapters/driven/heki/saga_store.rb +99 -0
  14. data/lib/hecks/adapters/driven/heki/snapshot.rb +65 -0
  15. data/lib/hecks/adapters/driven/heki.adapter +4 -0
  16. data/lib/hecks/adapters/driven/heki.rb +179 -0
  17. data/lib/hecks/adapters/driven/identity_registry.adapter +3 -0
  18. data/lib/hecks/adapters/driven/identity_registry.rb +26 -0
  19. data/lib/hecks/adapters/driven/in_memory_ordering.rb +51 -0
  20. data/lib/hecks/adapters/driven/lambda/client.rb +63 -0
  21. data/lib/hecks/adapters/driven/lambda.adapter +4 -0
  22. data/lib/hecks/adapters/driven/lambda.rb +145 -0
  23. data/lib/hecks/adapters/driven/memory.adapter +3 -0
  24. data/lib/hecks/adapters/driven/memory.rb +103 -0
  25. data/lib/hecks/adapters/driven/mock_stripe_adapter.adapter +3 -0
  26. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +28 -0
  27. data/lib/hecks/adapters/driven/postgres/codec.rb +88 -0
  28. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +207 -0
  29. data/lib/hecks/adapters/driven/postgres.adapter +5 -0
  30. data/lib/hecks/adapters/driven/postgres.rb +438 -0
  31. data/lib/hecks/adapters/driven/postgres_era.adapter +17 -0
  32. data/lib/hecks/adapters/driven/prism.adapter +3 -0
  33. data/lib/hecks/adapters/driven/prism.rb +80 -0
  34. data/lib/hecks/adapters/driven/secure_random_identity.adapter +3 -0
  35. data/lib/hecks/adapters/driven/secure_random_identity.rb +14 -0
  36. data/lib/hecks/adapters/driven/sql_query_builder.rb +221 -0
  37. data/lib/hecks/adapters/driven/sqlite/codec.rb +80 -0
  38. data/lib/hecks/adapters/driven/sqlite/projection.rb +172 -0
  39. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +192 -0
  40. data/lib/hecks/adapters/driven/sqlite.adapter +9 -0
  41. data/lib/hecks/adapters/driven/sqlite.rb +306 -0
  42. data/lib/hecks/adapters/driven/system_clock.adapter +3 -0
  43. data/lib/hecks/adapters/driven/system_clock.rb +14 -0
  44. data/lib/hecks/adapters/driven.rb +54 -0
  45. data/lib/hecks/adapters.rb +6 -0
  46. data/lib/hecks/behaviors/dsl.rb +125 -0
  47. data/lib/hecks/behaviors/expectations.rb +340 -0
  48. data/lib/hecks/behaviors/ir.rb +31 -0
  49. data/lib/hecks/behaviors/rspec.rb +42 -0
  50. data/lib/hecks/behaviors/runner.rb +96 -0
  51. data/lib/hecks/behaviors.rb +25 -0
  52. data/lib/hecks/bluebook/aggregate.rb +108 -0
  53. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +134 -0
  54. data/lib/hecks/bluebook/assembly/build.rb +48 -0
  55. data/lib/hecks/bluebook/assembly/contract.rb +112 -0
  56. data/lib/hecks/bluebook/assembly/contracts.rb +438 -0
  57. data/lib/hecks/bluebook/assembly/marks.rb +228 -0
  58. data/lib/hecks/bluebook/assembly/specializer.rb +70 -0
  59. data/lib/hecks/bluebook/assembly.rb +91 -0
  60. data/lib/hecks/bluebook/attribute.rb +96 -0
  61. data/lib/hecks/bluebook/behaviour/aggregate.rb +83 -0
  62. data/lib/hecks/bluebook/behaviour/attribute.rb +27 -0
  63. data/lib/hecks/bluebook/behaviour/chapter.rb +72 -0
  64. data/lib/hecks/bluebook/behaviour/command.rb +116 -0
  65. data/lib/hecks/bluebook/behaviour/domain_port.rb +25 -0
  66. data/lib/hecks/bluebook/behaviour/entity.rb +59 -0
  67. data/lib/hecks/bluebook/behaviour/hexagon.rb +50 -0
  68. data/lib/hecks/bluebook/behaviour/lifecycle.rb +68 -0
  69. data/lib/hecks/bluebook/behaviour/policy.rb +52 -0
  70. data/lib/hecks/bluebook/behaviour/process_manager.rb +51 -0
  71. data/lib/hecks/bluebook/behaviour/query.rb +10 -0
  72. data/lib/hecks/bluebook/behaviour/read_model.rb +29 -0
  73. data/lib/hecks/bluebook/behaviour/traits.rb +81 -0
  74. data/lib/hecks/bluebook/behaviour/value_object.rb +33 -0
  75. data/lib/hecks/bluebook/chapter.rb +78 -0
  76. data/lib/hecks/bluebook/command.rb +124 -0
  77. data/lib/hecks/bluebook/domain_port.rb +102 -0
  78. data/lib/hecks/bluebook/dsl/adapter_builder.rb +34 -0
  79. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +1018 -0
  80. data/lib/hecks/bluebook/dsl/attribute_collector.rb +348 -0
  81. data/lib/hecks/bluebook/dsl/binding_proxy.rb +71 -0
  82. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +1087 -0
  83. data/lib/hecks/bluebook/dsl/command_builder.rb +714 -0
  84. data/lib/hecks/bluebook/dsl/const_shim.rb +81 -0
  85. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +79 -0
  86. data/lib/hecks/bluebook/dsl/entity_builder.rb +430 -0
  87. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +366 -0
  88. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +163 -0
  89. data/lib/hecks/bluebook/dsl/identity_declaration.rb +191 -0
  90. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +44 -0
  91. data/lib/hecks/bluebook/dsl/malformed.rb +7 -0
  92. data/lib/hecks/bluebook/dsl/policy_builder.rb +135 -0
  93. data/lib/hecks/bluebook/dsl/port_builder.rb +39 -0
  94. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +142 -0
  95. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +306 -0
  96. data/lib/hecks/bluebook/dsl/query_builder.rb +113 -0
  97. data/lib/hecks/bluebook/dsl/read_model_builder.rb +233 -0
  98. data/lib/hecks/bluebook/dsl/rule_reference.rb +173 -0
  99. data/lib/hecks/bluebook/dsl/translation_builder.rb +243 -0
  100. data/lib/hecks/bluebook/dsl/value_object_builder.rb +178 -0
  101. data/lib/hecks/bluebook/dsl/word_gate.rb +228 -0
  102. data/lib/hecks/bluebook/dsl/world_builder.rb +117 -0
  103. data/lib/hecks/bluebook/dsl.rb +45 -0
  104. data/lib/hecks/bluebook/entity.rb +103 -0
  105. data/lib/hecks/bluebook/expression/canonical_form.rb +123 -0
  106. data/lib/hecks/bluebook/expression/evaluator.rb +304 -0
  107. data/lib/hecks/bluebook/expression/projection.json +218 -0
  108. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +233 -0
  109. data/lib/hecks/bluebook/expression/resolver.rb +781 -0
  110. data/lib/hecks/bluebook/expression.rb +13 -0
  111. data/lib/hecks/bluebook/hexagon.rb +60 -0
  112. data/lib/hecks/bluebook/lifecycle.rb +42 -0
  113. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +54 -0
  114. data/lib/hecks/bluebook/meta_validator/judge.rb +621 -0
  115. data/lib/hecks/bluebook/meta_validator/plan.rb +332 -0
  116. data/lib/hecks/bluebook/meta_validator/port_judge.rb +51 -0
  117. data/lib/hecks/bluebook/meta_validator/readings.rb +360 -0
  118. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +351 -0
  119. data/lib/hecks/bluebook/meta_validator/shapes.rb +266 -0
  120. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +255 -0
  121. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +138 -0
  122. data/lib/hecks/bluebook/meta_validator/world_judge.rb +78 -0
  123. data/lib/hecks/bluebook/meta_validator.rb +542 -0
  124. data/lib/hecks/bluebook/model_check.rb +445 -0
  125. data/lib/hecks/bluebook/pattern_subset.rb +184 -0
  126. data/lib/hecks/bluebook/policy.rb +41 -0
  127. data/lib/hecks/bluebook/process_manager.rb +128 -0
  128. data/lib/hecks/bluebook/project_discovery.rb +30 -0
  129. data/lib/hecks/bluebook/project_loader.rb +40 -0
  130. data/lib/hecks/bluebook/project_register.rb +82 -0
  131. data/lib/hecks/bluebook/query.rb +61 -0
  132. data/lib/hecks/bluebook/read_model.rb +109 -0
  133. data/lib/hecks/bluebook/reference.rb +74 -0
  134. data/lib/hecks/bluebook/smoke_test.rb +166 -0
  135. data/lib/hecks/bluebook/synthesizer.rb +95 -0
  136. data/lib/hecks/bluebook/translation.rb +92 -0
  137. data/lib/hecks/bluebook/value_object.rb +66 -0
  138. data/lib/hecks/bluebook.rb +74 -0
  139. data/lib/hecks/codemod.rb +342 -0
  140. data/lib/hecks/construct.rb +71 -0
  141. data/lib/hecks/deploy/bluebook/deploy.bluebook +219 -0
  142. data/lib/hecks/deploy/bluebook/deploy.hecksagon +4 -0
  143. data/lib/hecks/deploy/oidc.json +18 -0
  144. data/lib/hecks/doc/reference.rb +410 -0
  145. data/lib/hecks/embryonaut_bluebook.rb +75 -0
  146. data/lib/hecks/facade/cli_door.rb +119 -0
  147. data/lib/hecks/facade/cli_runner.rb +190 -0
  148. data/lib/hecks/facade/command_request.rb +105 -0
  149. data/lib/hecks/facade/handle.rb +173 -0
  150. data/lib/hecks/facade/json_door.rb +166 -0
  151. data/lib/hecks/facade/surface/aggregate_door.rb +185 -0
  152. data/lib/hecks/facade/surface/chapter.rb +107 -0
  153. data/lib/hecks/facade/surface.rb +48 -0
  154. data/lib/hecks/facade.rb +44 -0
  155. data/lib/hecks/forms/app.rb +341 -0
  156. data/lib/hecks/forms/command_form_renderer.rb +113 -0
  157. data/lib/hecks/forms/examples/banking_console.bluebook +3 -0
  158. data/lib/hecks/forms/field_renderer.rb +177 -0
  159. data/lib/hecks/forms/field_shape.rb +232 -0
  160. data/lib/hecks/forms/html.rb +84 -0
  161. data/lib/hecks/forms/index_renderer.rb +35 -0
  162. data/lib/hecks/forms/page.rb +157 -0
  163. data/lib/hecks/forms/params.rb +161 -0
  164. data/lib/hecks/forms/port_argument.rb +46 -0
  165. data/lib/hecks/forms/query_form_renderer.rb +114 -0
  166. data/lib/hecks/forms/record_renderer.rb +119 -0
  167. data/lib/hecks/forms/record_table.rb +68 -0
  168. data/lib/hecks/forms/reference_options.rb +30 -0
  169. data/lib/hecks/forms/value_object_shape.rb +46 -0
  170. data/lib/hecks/forms.rb +54 -0
  171. data/lib/hecks/fqn.rb +94 -0
  172. data/lib/hecks/framework/bluebook/compliance.bluebook +1 -0
  173. data/lib/hecks/framework/bluebook/console_settings.bluebook +489 -0
  174. data/lib/hecks/framework/bluebook/framework.hecksagon +32 -0
  175. data/lib/hecks/framework/bluebook/governance.bluebook +145 -0
  176. data/lib/hecks/framework/bluebook/identity.bluebook +90 -0
  177. data/lib/hecks/framework/oidc.json +39 -0
  178. data/lib/hecks/framework.rb +90 -0
  179. data/lib/hecks/freezer.rb +67 -0
  180. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +527 -0
  181. data/lib/hecks/fuzzing/invalid_value_generator.rb +86 -0
  182. data/lib/hecks/fuzzing/isolated_boot.rb +286 -0
  183. data/lib/hecks/fuzzing/properties.rb +1192 -0
  184. data/lib/hecks/fuzzing/replay.rb +668 -0
  185. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +93 -0
  186. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +80 -0
  187. data/lib/hecks/fuzzing/sequence_generator/picker.rb +96 -0
  188. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +250 -0
  189. data/lib/hecks/fuzzing/sequence_generator.rb +131 -0
  190. data/lib/hecks/fuzzing/value_generator.rb +200 -0
  191. data/lib/hecks/fuzzing.rb +12 -0
  192. data/lib/hecks/grammar/evolve.rb +324 -0
  193. data/lib/hecks/grammar/expression.bluebook +420 -0
  194. data/lib/hecks/grammar/expression_operators.json +1648 -0
  195. data/lib/hecks/grammar/grammar.hecksagon +20 -0
  196. data/lib/hecks/grammar/oidc.json +69 -0
  197. data/lib/hecks/grammar/translation.bluebook +203 -0
  198. data/lib/hecks/grammar.rb +178 -0
  199. data/lib/hecks/ir.rb +126 -0
  200. data/lib/hecks/language/adapter.bluebook +116 -0
  201. data/lib/hecks/language/bluebook/aggregate.bluebook +581 -0
  202. data/lib/hecks/language/bluebook/attaches/paging.bluebook +76 -0
  203. data/lib/hecks/language/bluebook/bluebook.bluebook +256 -0
  204. data/lib/hecks/language/bluebook/bluebook.hecksagon +24 -0
  205. data/lib/hecks/language/bluebook/command.bluebook +471 -0
  206. data/lib/hecks/language/bluebook/entity.bluebook +392 -0
  207. data/lib/hecks/language/bluebook/policy.bluebook +189 -0
  208. data/lib/hecks/language/bluebook/process_manager.bluebook +381 -0
  209. data/lib/hecks/language/bluebook/projection.bluebook +267 -0
  210. data/lib/hecks/language/bluebook/query.bluebook +245 -0
  211. data/lib/hecks/language/bluebook/shape.bluebook +292 -0
  212. data/lib/hecks/language/bluebook/syntax.bluebook +448 -0
  213. data/lib/hecks/language/bluebook/vocabulary.bluebook +379 -0
  214. data/lib/hecks/language/hecksagon/adapter_binding.bluebook +51 -0
  215. data/lib/hecks/language/hecksagon/domain_port.bluebook +76 -0
  216. data/lib/hecks/language/hecksagon/hecksagon.bluebook +131 -0
  217. data/lib/hecks/language/hecksagon/port_operation.bluebook +102 -0
  218. data/lib/hecks/language/oidc.json +333 -0
  219. data/lib/hecks/language/port.bluebook +120 -0
  220. data/lib/hecks/language/translation/translation.bluebook +110 -0
  221. data/lib/hecks/language/translation/translation_aggregate.bluebook +267 -0
  222. data/lib/hecks/language/world/wiring.bluebook +62 -0
  223. data/lib/hecks/language/world/world.bluebook +84 -0
  224. data/lib/hecks/literal.rb +125 -0
  225. data/lib/hecks/naming.rb +174 -0
  226. data/lib/hecks/ports/access_control.port +9 -0
  227. data/lib/hecks/ports/access_control.rb +62 -0
  228. data/lib/hecks/ports/agent/answers.rb +104 -0
  229. data/lib/hecks/ports/agent.port +8 -0
  230. data/lib/hecks/ports/agent.rb +167 -0
  231. data/lib/hecks/ports/authentication.port +6 -0
  232. data/lib/hecks/ports/authentication.rb +50 -0
  233. data/lib/hecks/ports/authorization.port +7 -0
  234. data/lib/hecks/ports/authorization.rb +62 -0
  235. data/lib/hecks/ports/clock.port +5 -0
  236. data/lib/hecks/ports/clock.rb +62 -0
  237. data/lib/hecks/ports/extraction.port +5 -0
  238. data/lib/hecks/ports/extraction.rb +37 -0
  239. data/lib/hecks/ports/identity_assignment.port +5 -0
  240. data/lib/hecks/ports/identity_assignment.rb +45 -0
  241. data/lib/hecks/ports/identity_generation.port +5 -0
  242. data/lib/hecks/ports/identity_generation.rb +49 -0
  243. data/lib/hecks/ports/identity_resolution.port +5 -0
  244. data/lib/hecks/ports/identity_resolution.rb +40 -0
  245. data/lib/hecks/ports/loading.port +4 -0
  246. data/lib/hecks/ports/loading.rb +13 -0
  247. data/lib/hecks/ports/persistence/append_only.rb +138 -0
  248. data/lib/hecks/ports/persistence/binding_policy.rb +56 -0
  249. data/lib/hecks/ports/persistence/execution.rb +23 -0
  250. data/lib/hecks/ports/persistence/null_saga_store.rb +25 -0
  251. data/lib/hecks/ports/persistence/plugin.rb +54 -0
  252. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +169 -0
  253. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +128 -0
  254. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +161 -0
  255. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +61 -0
  256. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +304 -0
  257. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +171 -0
  258. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +190 -0
  259. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +478 -0
  260. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +166 -0
  261. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +314 -0
  262. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +168 -0
  263. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +170 -0
  264. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +134 -0
  265. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +137 -0
  266. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +89 -0
  267. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +85 -0
  268. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +43 -0
  269. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +132 -0
  270. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +74 -0
  271. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +758 -0
  272. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +120 -0
  273. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +31 -0
  274. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +46 -0
  275. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +102 -0
  276. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +46 -0
  277. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +70 -0
  278. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +72 -0
  279. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +120 -0
  280. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +183 -0
  281. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +41 -0
  282. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +28 -0
  283. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +30 -0
  284. data/lib/hecks/ports/persistence/plugins/era/translation.rb +11 -0
  285. data/lib/hecks/ports/persistence/plugins/era.rb +48 -0
  286. data/lib/hecks/ports/persistence/remote_runtime.rb +42 -0
  287. data/lib/hecks/ports/persistence/repository_factory.rb +30 -0
  288. data/lib/hecks/ports/persistence.port +4 -0
  289. data/lib/hecks/ports/persistence.rb +39 -0
  290. data/lib/hecks/ports/projection.port +4 -0
  291. data/lib/hecks/ports/projection.rb +96 -0
  292. data/lib/hecks/ports/query/in_memory.rb +60 -0
  293. data/lib/hecks/ports/query/ordering.rb +41 -0
  294. data/lib/hecks/ports/query.rb +36 -0
  295. data/lib/hecks/ports.rb +27 -0
  296. data/lib/hecks/projections/diagrams.rb +642 -0
  297. data/lib/hecks/projections/ir.rb +18 -0
  298. data/lib/hecks/projections/model/deviations.rb +98 -0
  299. data/lib/hecks/projections/model.rb +145 -0
  300. data/lib/hecks/projections/oidc.rb +110 -0
  301. data/lib/hecks/projections/parser_table.rb +159 -0
  302. data/lib/hecks/projections/reference.rb +38 -0
  303. data/lib/hecks/projections/shape.rb +44 -0
  304. data/lib/hecks/projections/statements.rb +110 -0
  305. data/lib/hecks/projections/vocabulary.rb +100 -0
  306. data/lib/hecks/projections.rb +32 -0
  307. data/lib/hecks/projector/cli_projector.rb +446 -0
  308. data/lib/hecks/projector/docs_projector.rb +321 -0
  309. data/lib/hecks/projector/exporter.rb +158 -0
  310. data/lib/hecks/projector/ir_projector.rb +18 -0
  311. data/lib/hecks/projector/narrate_projector.rb +243 -0
  312. data/lib/hecks/projector/target.rb +97 -0
  313. data/lib/hecks/projector.rb +186 -0
  314. data/lib/hecks/query_ir.rb +411 -0
  315. data/lib/hecks/query_specification/common/authorization_spec.rb +9 -0
  316. data/lib/hecks/query_specification/common/comparators.rb +25 -0
  317. data/lib/hecks/query_specification/common/comparison.rb +174 -0
  318. data/lib/hecks/query_specification/common/cursor_spec.rb +9 -0
  319. data/lib/hecks/query_specification/common/dsl.rb +58 -0
  320. data/lib/hecks/query_specification/common/inspection_spec.rb +9 -0
  321. data/lib/hecks/query_specification/common/limit_spec.rb +9 -0
  322. data/lib/hecks/query_specification/common/null_policy.rb +92 -0
  323. data/lib/hecks/query_specification/common/null_semantics.rb +11 -0
  324. data/lib/hecks/query_specification/common/offset_spec.rb +9 -0
  325. data/lib/hecks/query_specification/common/options.rb +32 -0
  326. data/lib/hecks/query_specification/common/order_by.rb +9 -0
  327. data/lib/hecks/query_specification/common/specification.rb +10 -0
  328. data/lib/hecks/query_specification/common/where_clause.rb +9 -0
  329. data/lib/hecks/query_specification/field_path.rb +107 -0
  330. data/lib/hecks/query_specification/hop_path.rb +132 -0
  331. data/lib/hecks/query_specification/read_model/specification.rb +18 -0
  332. data/lib/hecks/query_specification.rb +14 -0
  333. data/lib/hecks/rendering.rb +48 -0
  334. data/lib/hecks/router/namespace_installer.rb +157 -0
  335. data/lib/hecks/router.rb +70 -0
  336. data/lib/hecks/runtime/aggregate_lock.rb +45 -0
  337. data/lib/hecks/runtime/boot_gates.rb +41 -0
  338. data/lib/hecks/runtime/caller.rb +66 -0
  339. data/lib/hecks/runtime/capability_graph.rb +44 -0
  340. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +135 -0
  341. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +260 -0
  342. data/lib/hecks/runtime/command_interpreter.rb +497 -0
  343. data/lib/hecks/runtime/command_rules/admissibility.rb +357 -0
  344. data/lib/hecks/runtime/command_rules/arithmetic.rb +260 -0
  345. data/lib/hecks/runtime/command_rules/authorization.rb +65 -0
  346. data/lib/hecks/runtime/command_rules/emission.rb +34 -0
  347. data/lib/hecks/runtime/command_rules/references.rb +189 -0
  348. data/lib/hecks/runtime/command_rules.rb +28 -0
  349. data/lib/hecks/runtime/dependency_planning.rb +245 -0
  350. data/lib/hecks/runtime/dispatcher.rb +301 -0
  351. data/lib/hecks/runtime/entity_element.rb +253 -0
  352. data/lib/hecks/runtime/entity_interpreter.rb +299 -0
  353. data/lib/hecks/runtime/errors.rb +122 -0
  354. data/lib/hecks/runtime/event.rb +51 -0
  355. data/lib/hecks/runtime/identity.rb +156 -0
  356. data/lib/hecks/runtime/instance.rb +175 -0
  357. data/lib/hecks/runtime/interpreting.rb +90 -0
  358. data/lib/hecks/runtime/loader.rb +184 -0
  359. data/lib/hecks/runtime/policy_interpreter.rb +366 -0
  360. data/lib/hecks/runtime/port_operation_interpreter.rb +210 -0
  361. data/lib/hecks/runtime/query_interpreter.rb +286 -0
  362. data/lib/hecks/runtime/reaction_invocation.rb +253 -0
  363. data/lib/hecks/runtime/read_model_interpreter.rb +341 -0
  364. data/lib/hecks/runtime/rebuild_sweep.rb +74 -0
  365. data/lib/hecks/runtime/reference_hop.rb +99 -0
  366. data/lib/hecks/runtime/refusal_wording.rb +126 -0
  367. data/lib/hecks/runtime/registry/saga_persistence.rb +142 -0
  368. data/lib/hecks/runtime/registry/verification.rb +246 -0
  369. data/lib/hecks/runtime/registry.rb +279 -0
  370. data/lib/hecks/runtime/remote_dispatcher.rb +143 -0
  371. data/lib/hecks/runtime/routing.rb +96 -0
  372. data/lib/hecks/runtime/saga_interpreter/correlation.rb +97 -0
  373. data/lib/hecks/runtime/saga_interpreter.rb +503 -0
  374. data/lib/hecks/runtime/saga_pending_dispatch.rb +45 -0
  375. data/lib/hecks/runtime/tenant_check.rb +84 -0
  376. data/lib/hecks/runtime/tenant_scope.rb +55 -0
  377. data/lib/hecks/runtime/value/admission.rb +130 -0
  378. data/lib/hecks/runtime/value/coercion.rb +565 -0
  379. data/lib/hecks/runtime/value/invariant_violation.rb +5 -0
  380. data/lib/hecks/runtime/value.rb +125 -0
  381. data/lib/hecks/runtime.rb +107 -0
  382. data/lib/hecks/storehouse.rb +632 -0
  383. data/lib/hecks/version.rb +16 -0
  384. data/lib/hecks/vocabulary.rb +218 -0
  385. data/lib/hecks.rb +123 -6
  386. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +90 -0
  387. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +128 -0
  388. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +160 -0
  389. metadata +412 -222
  390. data/bin/hecks +0 -7
  391. data/bin/hecks-package +0 -65
  392. data/bin/hecks_console +0 -12
  393. data/bin/hecks_serverless +0 -6
  394. data/lib/cli/build.rb +0 -14
  395. data/lib/cli/command_runner.rb +0 -28
  396. data/lib/cli/console.rb +0 -10
  397. data/lib/cli/generate.rb +0 -37
  398. data/lib/cli/hecks-cli.rb +0 -27
  399. data/lib/cli/test.rb +0 -57
  400. data/lib/console/commands.rb +0 -8
  401. data/lib/console/hecks-console.rb +0 -1
  402. data/lib/packager/README.md +0 -0
  403. data/lib/packager/app_runner.rb +0 -21
  404. data/lib/packager/args.rb +0 -26
  405. data/lib/packager/compatibility/fixnum.rb +0 -6
  406. data/lib/packager/hecks.rb +0 -39
  407. data/lib/packager/query_runner.rb +0 -21
  408. data/lib/packager/resources/Dockerfile +0 -11
  409. data/lib/packager/resources/app_binary +0 -7
  410. data/lib/packager/resources/bundle_config +0 -3
  411. data/lib/packager/resources/traveling-ruby-20150715-2.2.2-linux-x86_64.tar.gz +0 -0
  412. data/lib/packager/resources/traveling-ruby-20150715-2.2.2-osx.tar.gz +0 -0
  413. data/lib/serverless/Domain +0 -32
  414. data/lib/serverless/cli.rb +0 -75
  415. data/lib/serverless/resources/command_name.js +0 -5
  416. data/lib/serverless/resources/environment.js +0 -7
  417. data/lib/serverless/resources/handler.js.tt +0 -28
  418. data/lib/serverless/resources/run_binary.js +0 -22
  419. data/lib/serverless/resources/serverless.yml +0 -20
@@ -0,0 +1,70 @@
1
+ module Hecks
2
+ module Bluebook
3
+ class Assembly
4
+ # THE FIRST SPECIALIZER — a projection of `contracts.rb`'s `fields:` table,
5
+ # derived from the language's own description of a category instead of
6
+ # hand-written beside it.
7
+ #
8
+ # `Plan` already reads `grammar_registry` to build the judge's walk ; this
9
+ # reads the same chapter to build the OTHER table this arc's own header
10
+ # names as duplication — "SPELLED AS THE IR SPELLS THEM," field for field,
11
+ # for every category simple enough to say so.
12
+ #
13
+ # ONE CASE, PROVEN, NOT THE WHOLE TABLE. A field this can speak for is
14
+ # scalar and not a reference — every other field (a list, a reference, a
15
+ # fold like Lifecycle) is exactly what `contracts.rb`'s `reads:`/`derived:`
16
+ # exist to say, and stays hand-written until a later projection learns to
17
+ # derive readers and folds too. Restricting the claim to what can be
18
+ # PROVEN CORRECT — checked in spec/specializer_spec.rb against two
19
+ # independent categories — is the same discipline `derived:` itself
20
+ # enforces : a claim needs a kind, and this one's kind is "plain, checked."
21
+ module Specializer
22
+ module_function
23
+
24
+ # `position` IS THE FIRST FOLD THIS RUNS INTO, and it is a universal
25
+ # one : every category declares `attribute :position, Position` — for
26
+ # the JUDGE's own walk, `order_by :position` on its `DeclaredIn` ask —
27
+ # but no `*` constructor takes it as an argument. `contracts.rb`
28
+ # already says so, in the language every other derived field speaks :
29
+ # `derived: { position: :walk }`. The language says a category HAS a
30
+ # position ; it does not say a category's OWN constructor is handed
31
+ # one, and that second fact is exactly what `fields:` needs to answer.
32
+ # So this is not silently special-cased — it is the one fold named
33
+ # here because it is the one fold that is not a lucky accident of
34
+ # Policy or Handler, but a fact true of every category this arc will
35
+ # ever reach.
36
+ DERIVED_EVERYWHERE = %i[position].freeze
37
+
38
+ # S17, ADR 0026 — `Handler` is a genuine entity now, nested under
39
+ # `ProcessManager`, so `.aggregate` alone no longer finds it —
40
+ # it hangs off some aggregate's own `.entities` instead
41
+ # (searched recursively, the same reason `Value::Coercion#find_
42
+ # entity` does: a NESTED entity, like `Dispatch` inside
43
+ # `Handler`, is not a direct child of any aggregate either).
44
+ def construct_for(chapter, name)
45
+ chapter.aggregate(name) || chapter.aggregates.filter_map { |a| find_entity(a, name) }.first
46
+ end
47
+
48
+ def find_entity(construct, name)
49
+ construct.entities.each do |candidate|
50
+ return candidate if candidate.hecks_name == name
51
+
52
+ found = find_entity(candidate, name)
53
+ return found if found
54
+ end
55
+ nil
56
+ end
57
+
58
+ def fields_for(category)
59
+ language = construct_for(MetaValidator.grammar_registry.bluebook("Bluebook"), category.to_s)
60
+ language.attributes.each_with_object({}) do |attribute, fields|
61
+ next if attribute.list? || attribute.reference?
62
+ next if DERIVED_EVERYWHERE.include?(attribute.name)
63
+
64
+ fields[attribute.name] = [attribute.name, :plain]
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,91 @@
1
+ module Hecks
2
+ module Bluebook
3
+ # A GRAPH BUILT FROM DECLARATIONS, rather than from DSL calls.
4
+ #
5
+ # This is the half that lets the language orchestrate. `Reconstruction` reads a
6
+ # chapter back out of the meta-domain, in declaration order, as plain
7
+ # declarations — the same shape `to_h` spells. This turns those declarations
8
+ # into the graph the runtime runs: IR aggregates with their verbs, value
9
+ # objects, entities and asks, owned by the chapter that declares them.
10
+ #
11
+ # It takes a HASH, not a runtime, on purpose. That makes it a pure inverse of
12
+ # `to_h` and testable without the meta-domain in the picture at all:
13
+ #
14
+ # Assembly.call(built.to_h).to_h == built.to_h
15
+ #
16
+ # which is the check `spec/assembly_spec` makes for every chapter in the tree.
17
+ # Feed it the reconstruction instead and the same code assembles what the
18
+ # LANGUAGE holds — the only difference being where the declarations came from.
19
+ #
20
+ # EVERY FIELD IS READ FROM ONE TABLE. There is no method per category here:
21
+ # `Contracts` names what the language cannot say about a construct, `Build`
22
+ # reads it, and the coverage gate holds the table to the language. The first
23
+ # draft of this file did have a method each, which is the shape the judge used
24
+ # to have — and the price of that shape was fourteen verbs the language declared
25
+ # and nothing ever offered.
26
+ #
27
+ # What stays hand-written is the CONTAINMENT: which construct holds which.
28
+ # That is not a field table. (The runtime SURFACE is no longer built here at
29
+ # all — the door is a per-boot projection, facade/surface.rb.)
30
+ class Assembly
31
+ def self.call(declaration) = new(declaration).bluebook
32
+
33
+ def initialize(declaration)
34
+ @declaration = declaration
35
+ end
36
+
37
+ def bluebook
38
+ aggregates = Array(@declaration[:aggregates]).map { |row| AggregateAssembly.new(row).aggregate }
39
+ models = Array(@declaration[:read_models]).map { |row| Build.call("ReadModel", row) }
40
+
41
+ chapter = Build.call(
42
+ "Bluebook", @declaration,
43
+ aggregates: aggregates,
44
+ read_models: models,
45
+ policies: reactions(aggregates),
46
+ process_managers: Array(@declaration[:process_managers]).map { |row| process_manager(row) }
47
+ )
48
+
49
+ chapter
50
+ end
51
+
52
+ private
53
+
54
+ # Every reaction the chapter holds, and each one also handed back to the head
55
+ # that declared it — the builder keeps a policy in BOTH places, hoisting it onto
56
+ # the chapter where the runtime reads it while the head keeps its own list. The
57
+ # language records which head, so the assembly can put it back.
58
+ def reactions(aggregates)
59
+ Array(@declaration[:policies]).map { |row| Build.call("Policy", row) }.each do |reaction|
60
+ next if reaction.aggregate.to_s.empty?
61
+
62
+ head = aggregates.find { |held| held.hecks_name == reaction.aggregate }
63
+ head&.policies&.push(reaction)
64
+ end
65
+ end
66
+
67
+ def process_manager(row)
68
+ Build.call("ProcessManager", row,
69
+ handlers: Array(row[:handlers]).map { |leg| handler(leg) })
70
+ end
71
+
72
+ def handler(row)
73
+ Build.call("Handler", row,
74
+ dispatches: Array(row[:dispatches]).map { |leg| dispatch(leg) })
75
+ end
76
+
77
+ # `compensates` — a PLAIN HASH on the declaration (`Reconstruction#
78
+ # dispatch`'s own comment for why), built into the real
79
+ # `DispatchSpec` its own field actually is by recursing through
80
+ # THIS SAME method, one level in — the identical move `handler`
81
+ # itself takes into `dispatches`, one level up. `nil` when there
82
+ # is nothing to compensate; `Build.call` never sees a
83
+ # `compensates:` key it does not know how to read either way, the
84
+ # same reason `compensates` never joined `Contract#fields` at all.
85
+ def dispatch(row)
86
+ compensates = row[:compensates] && dispatch(row[:compensates])
87
+ Build.call("Dispatch", row, compensates: compensates)
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,96 @@
1
+ require_relative "behaviour/attribute"
2
+ require_relative "../ir"
3
+ require_relative "../vocabulary"
4
+ require_relative "../naming"
5
+
6
+ module Hecks
7
+ module Bluebook
8
+ class Attribute
9
+ include Hecks::IR
10
+ include Behaviour::Attribute
11
+
12
+ emits_ir(
13
+ name: :name,
14
+ type: -> { @type.to_s },
15
+ list: :list?,
16
+ default: :default,
17
+ optional: :optional?,
18
+ pattern: :pattern,
19
+ admits: :admits,
20
+ relationship: :relationship
21
+ )
22
+
23
+ attr_reader :name, :type, :default, :pattern, :admits, :relationship
24
+
25
+ # A Reference is kept AS ITSELF. Every other type is still a name, and
26
+ # crosses over as its construct does.
27
+ #
28
+ # `admits` names an ALREADY-DECLARED closed set the value must belong
29
+ # to — `Vocabulary::QueryComparator` — spelled aggregate-qualified
30
+ # because the set is a value object INSIDE an aggregate, and the
31
+ # aggregate is the only thing `reference_to` can reach.
32
+ #
33
+ # ON THE WIRE, because it is a RULE and not only a typing hint.
34
+ #
35
+ # It began as neither. The link existed so a generator could type
36
+ # `WhereClause.op` as `WhereOp` — a typing convenience, not worth
37
+ # moving 710 attribute records across eight goldens for. Then `admits`
38
+ # grew teeth (coercion refuses a non-member) and the argument
39
+ # inverted: a rule the wire does not carry is one a reader of the IR
40
+ # cannot enforce, and a bluebook whose meaning depends on the reader
41
+ # means two things. Proved rather than assumed — the same domain
42
+ # refused "burnt" through one reading and emitted the event through
43
+ # another.
44
+ #
45
+ # The wire carries the NAME, not the members. A reader resolves it
46
+ # against the IR it holds, so the members are declared once and
47
+ # copied nowhere — which is the same reason `admits` exists at all.
48
+ def initialize(name:, type:, list: false, default: nil, optional: false, pattern: nil,
49
+ admits: nil, relationship: nil)
50
+ @name = name.to_sym
51
+ @type = spell(type)
52
+ @list = list
53
+ @default = default
54
+ @optional = optional
55
+ @pattern = pattern
56
+ @admits = admits&.to_s
57
+ @relationship = relationship&.to_s
58
+ end
59
+
60
+ # A BARE CONSTANT IN A BLUEBOOK IS A NAME, EVEN WHEN RUBY HAS HEARD OF IT.
61
+ #
62
+ # `BluebookBuilder.build` says exactly this and installs a `const_missing`
63
+ # resolver that hands back the symbol — `attribute :target, Target` becomes
64
+ # the name "Target" and nothing looks Target up. That works only while the
65
+ # lookup FAILS, and `Facade::Surface` installs every aggregate name as a
66
+ # TOP-LEVEL constant (its own comment, and `ConstShim`'s, both say so).
67
+ #
68
+ # So in one process: boot a domain with an aggregate named `Target`, then
69
+ # load a chapter whose own value object is called `Target`, and Ruby
70
+ # resolves the constant before the hook is ever asked. The chapter is then
71
+ # built against somebody else's aggregate — silently, with no refusal —
72
+ # and the attribute stops meaning what the file plainly says.
73
+ #
74
+ # DEMODULISED, so both paths spell it the same: `:Target` and
75
+ # `QualityControl::Target` are both "Target". A plain class stays itself —
76
+ # `String` demodulises to "String" — so the ordinary case is untouched.
77
+ # This does not undo the constant leak; it makes the leak unable to change
78
+ # what a chapter MEANS, which is the part that has to hold.
79
+ def spell(type)
80
+ return type if type.is_a?(Reference)
81
+ return Naming.demodulise(type) if type.is_a?(Module)
82
+
83
+ type.to_s
84
+ end
85
+ private :spell
86
+
87
+ # Held because a declared vocabulary pins it — spec/vocabulary_conformance
88
+ # holds `Primitive`'s members to this list. The `primitive?` predicate that
89
+ # used to read it had no caller anywhere and is gone.
90
+ PRIMITIVES = Hecks::Vocabulary.fetch("Primitive")
91
+
92
+ # `type` is spelled, never handed over. A Reference renders as
93
+ # "Reference<Customer>" here because that is the export's pinned spelling.
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,83 @@
1
+ require_relative "traits"
2
+
3
+ module Hecks
4
+ module Bluebook
5
+ module Behaviour
6
+ # WHAT AN AGGREGATE DOES, as opposed to what it holds.
7
+ #
8
+ # The holding half — the field list, the readers, the emission — is
9
+ # the same list the language already declares in
10
+ # `language/bluebook/aggregate.bluebook`, said a second and third
11
+ # time in Ruby. This half is not: derived identity, the name
12
+ # indexes, the owner stamping and the finders are decisions about
13
+ # HOW the declared shape is used, and no grammar states them.
14
+ #
15
+ # Split so the holding half can be generated from the language
16
+ # without any of this being in the blast radius of a regeneration.
17
+ # Everything here is hand-written and permanent.
18
+ #
19
+ # The three traits are shared with Entity (and, for `Indexed`,
20
+ # with Command and PortOperation) — see behaviour/traits.rb on why
21
+ # they are one module rather than four copies.
22
+ module Aggregate
23
+ include Identified
24
+ include Indexed
25
+ include Owns
26
+
27
+ # An aggregate is a MEMBER of its chapter's namespace — "Pizzas::Pizza" —
28
+ # where everything else is declared ON its owner and joins with ".".
29
+ def hecks_separator = "::"
30
+
31
+ # THE HOOK THE GENERATED CONSTRUCTOR CALLS once every declared
32
+ # field is assigned. Nothing here is derivable from the
33
+ # declaration, which is exactly why it is not generated.
34
+ def settle
35
+ derive_identity
36
+ index_declarations
37
+ # An entity stamps its own commands and queries when it is
38
+ # declared, so the chain closes downward from here.
39
+ stamp(@commands, @value_objects, @entities, @queries)
40
+ self
41
+ end
42
+
43
+ def index_declarations
44
+ index_attributes(@attributes)
45
+ @value_objects_by_name = index_by_hecks_name(@value_objects)
46
+ @commands_by_name = index_by_hecks_name(@commands)
47
+ @queries_by_name = index_by_hecks_name(@queries)
48
+ @ports_by_name = @ports.to_h { |port| [port.name, port] }
49
+ # S12, ADR 0025 — keyed by SYMBOL, the same convention
50
+ # `Indexed#attribute` already uses; `GuardState` asks for one
51
+ # by name at every dispatch, the rebuild sweep walks all of
52
+ # them once per pass.
53
+ @projected_fields_by_name = @projected_fields.to_h { |field| [field.name, field] }
54
+ end
55
+
56
+ def projected_field(named) = @projected_fields_by_name[named.to_sym]
57
+
58
+ # A value object is a CLASS now, so `name` is Ruby's answer (the constant
59
+ # path) and the declared name is `hecks_name`. This finder is on its way
60
+ # out — once an attribute's type IS the class there is nothing to find —
61
+ # but every consumer still asks by type string, so it stays until they
62
+ # stop.
63
+ def value_object(named) = @value_objects_by_name[named.to_s]
64
+ def port(named) = @ports_by_name[named.to_s]
65
+
66
+ # A PORT IS DECLARED IN THE HECKSAGON, NOT THE BLUEBOOK — the
67
+ # boundary between the domain and its adapters, in hexagonal terms,
68
+ # is exactly what a `.hecksagon` file already IS for every other
69
+ # port (persistence, projection, ...). So this attaches AFTER the
70
+ # aggregate already exists and is registered — `HecksagonBuilder`
71
+ # calls it once per `port` declaration, having already stamped each
72
+ # operation's reference attributes with `declared_in = self`, since
73
+ # nothing upstream of a hecksagon load does that for it.
74
+ def add_port(port)
75
+ @ports << port
76
+ @ports_by_name[port.name] = port
77
+ end
78
+
79
+ def storage_name = Naming.snake(@name)
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,27 @@
1
+ module Hecks
2
+ module Bluebook
3
+ module Behaviour
4
+ # WHAT AN ATTRIBUTE DOES. The declared half — name, type, list,
5
+ # default, optional, pattern, admits — is what the language states
6
+ # in `aggregate.bluebook`'s own `Field`. These are the questions
7
+ # readers ask ABOUT that shape, which no declaration states.
8
+ module Attribute
9
+ def list? = @list
10
+ def scalar? = !@list
11
+ def reference? = @type.is_a?(Reference)
12
+
13
+ # MAY THIS FACT BE LEFT OUT?
14
+ #
15
+ # Required is the default and by far the common case — a command takes
16
+ # the arguments it declares, and all of them — so the EXCEPTION is what
17
+ # gets marked. Marking the other way would annotate almost every
18
+ # attribute in the corpus to say nothing.
19
+ #
20
+ # Only a COMMAND enforces this. An aggregate's own attributes are filled
21
+ # by the commands that set them, and a value object's by its
22
+ # constructor ; neither is a payload anyone hands in.
23
+ def optional? = @optional
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,72 @@
1
+ require_relative "traits"
2
+
3
+ module Hecks
4
+ module Bluebook
5
+ module Behaviour
6
+ # WHAT A CHAPTER DOES. The declared half is the roll-call of what a
7
+ # bluebook holds; these are the finders over it, plus `verbs` — the
8
+ # chapter's own list of every dispatchable name, which is derived
9
+ # from the aggregates rather than declared anywhere.
10
+ module Chapter
11
+ include Owns
12
+
13
+ # THE HOOK THE GENERATED CONSTRUCTOR CALLS. Three things a
14
+ # declaration does not state: that a chapter is the ROOT of the
15
+ # owner chain (nothing declares it, it is what having no owner
16
+ # MEANS), the ports table — which a `.hecksagon` fills later, so
17
+ # the bluebook cannot declare it — and stamping its own children,
18
+ # the same act an Aggregate performs one level down.
19
+ def settle
20
+ @hecks_root = true
21
+ @ports = []
22
+ @ports_by_name = {}
23
+ stamp(@aggregates, @read_models)
24
+ self
25
+ end
26
+
27
+ def aggregate(named) = @aggregates.find { |a| a.name == named.to_s }
28
+ def read_model(named) = @read_models.find { |model| model.name == named.to_s || model.query_name == named.to_s }
29
+ def port(named) = @ports_by_name[named.to_s]
30
+
31
+ # A PORT IS DECLARED IN THE HECKSAGON, not the bluebook — so it
32
+ # attaches after the chapter already exists, the same way an
33
+ # aggregate's own ports do.
34
+ def add_port(port)
35
+ @ports << port
36
+ @ports_by_name[port.name] = port
37
+ end
38
+
39
+ # Every dispatchable name this chapter answers to, spelled exactly
40
+ # as Dispatcher#dispatch takes it. Derived from the aggregates,
41
+ # never declared — which is why Projections::OIDC can hold its own
42
+ # scope list equal to this and have that mean something.
43
+ #
44
+ # Recurses into entities, not just an aggregate's own direct
45
+ # commands — `Dispatcher#dispatch` already routes a dotted
46
+ # `Domain::Aggregate.Entity.Command` verb to `EntityInterpreter`
47
+ # (a command_name with a "." in it), so a verb this method left
48
+ # out was never "not a verb," only one this list forgot to name.
49
+ # Ported from the same recursive shape `spec/judge_coverage_spec.rb`
50
+ # already proved out for the meta-domain's own grammar (S17, ADR
51
+ # 0026) — entities nest arbitrarily deep (`Dispatch`, inside
52
+ # `Handler`), so one flat level isn't enough.
53
+ def verbs
54
+ @aggregates.flat_map { |agg| aggregate_verbs(agg) }
55
+ end
56
+
57
+ private
58
+
59
+ def aggregate_verbs(agg)
60
+ agg.commands.map { |cmd| "#{@name}::#{agg.hecks_name}.#{cmd.hecks_name}" } +
61
+ agg.entities.flat_map { |entity| entity_verbs("#{@name}::#{agg.hecks_name}", entity) }
62
+ end
63
+
64
+ def entity_verbs(prefix, entity)
65
+ dotted = "#{prefix}.#{entity.hecks_name}"
66
+ entity.commands.map { |cmd| "#{dotted}.#{cmd.hecks_name}" } +
67
+ entity.entities.flat_map { |piece| entity_verbs(dotted, piece) }
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,116 @@
1
+ require_relative "traits"
2
+
3
+ module Hecks
4
+ module Bluebook
5
+ module Behaviour
6
+ # WHAT A COMMAND DOES. EXTENDED, not included — a command is a
7
+ # CLASS, one per declared verb.
8
+ module Command
9
+ include Indexed
10
+
11
+ # Indexed once — attributes are final once absorbed, and every
12
+ # dispatch asks this finder by name.
13
+ def settle
14
+ index_attributes(@attributes)
15
+ self
16
+ end
17
+
18
+ # The construct this verb acts upon — the construct itself, not its name.
19
+ #
20
+ # A verb declared on an ENTITY always acts on that piece. It never
21
+ # self-references, because an element is addressed THROUGH its parent —
22
+ # which means `creates?` answers true for every one of them, and reading
23
+ # `acts_on` off `creates?` alone would report that `LedgerEntry.Amend`
24
+ # brings a ledger entry into being. Three of banking's commands were
25
+ # about to say exactly that, and nothing would have contradicted them.
26
+ #
27
+ # On an aggregate, a creating command acts on no existing root, so nil is
28
+ # the truth: there is nothing there yet.
29
+ def acts_on
30
+ # FULLY QUALIFIED, and it has to be: inside `module Behaviour`
31
+ # the bare name `Entity` resolves to Behaviour::Entity — this
32
+ # module's SIBLING — not to the construct. Comparing a Class to
33
+ # a Module answers nil, so the guard silently fell through and
34
+ # every entity verb reported acting on nothing.
35
+ return hecks_owner if hecks_owner.is_a?(Class) && hecks_owner < Bluebook::Entity
36
+
37
+ creates? ? nil : hecks_owner
38
+ end
39
+
40
+ def creates? = @references.nil?
41
+
42
+ # EVERY REASON THIS VERB CAN REFUSE ON A RULE — the descriptions of
43
+ # its givens AND its ensures, the exact text the runtime quotes
44
+ # after "refused — " when a guard is not met (see
45
+ # command_rules/admissibility.rb's GivenNotMet/EnsuresNotMet). A
46
+ # property that asks "did the runtime only ever refuse for a rule
47
+ # the language wrote" reads this rather than re-deriving the two
48
+ # collections; `compact` because a rule's description is optional
49
+ # (behavior.bluebook's Rule) and an unnamed one quotes nothing.
50
+ def guard_descriptions = (@givens + @ensures).map(&:description).compact
51
+
52
+ # THE ARGUMENT NAME THAT ADDRESSES an instance of `aggregate_name`
53
+ # for THIS command — the one fact `PolicyInterpreter`'s own
54
+ # `for_each` fan-out needs and, until this reading existed, had
55
+ # to guess at (see git blame: `Behaviour::Policy
56
+ # #fan_out_reference_key`, which hardcoded `<aggregate>_id`
57
+ # unconditionally and refused every dispatch to a self-
58
+ # addressing command as a result — `Account.Freeze`, addressed
59
+ # by `number`/`account`, not `account_id`).
60
+ #
61
+ # TWO SHAPES, the same two `CommandBuilder#reference_to` already
62
+ # tells apart at declare time (command_builder.rb's own comment
63
+ # on `cross_reference` — "`as:` MEANS a named attribute... a
64
+ # command can point at another instance of its OWN kind"):
65
+ #
66
+ # SELF-ADDRESSING — `references == aggregate_name` (this verb
67
+ # is declared ON the very aggregate it acts on, `reference_to
68
+ # Account` on a command Account itself owns). No attribute was
69
+ # minted for it at all; the SAME bare key
70
+ # `CommandInterpreter::ArgumentGate#reference_key` already
71
+ # accepts as "addressing, not describing" is reused here
72
+ # rather than re-derived — one door, not two.
73
+ #
74
+ # CROSS-REFERENCING — a real, declared reference-typed
75
+ # attribute whose OWN target is `aggregate_name` (`customer_id`
76
+ # on `Account.Open`, or whatever `as:` named it). Its NAME is
77
+ # the key, exactly as declared — never re-derived from the
78
+ # target's name, because an `as:` reference's name and its
79
+ # target's snake case can legitimately differ (`Transfer`'s own
80
+ # `source`/`destination`, both `Reference<Account>`).
81
+ #
82
+ # `nil` when neither shape matches — a CREATING command (nothing
83
+ # to address yet) or one that simply never references this
84
+ # aggregate at all. A caller minting a fan-out dispatch is
85
+ # expected to treat `nil` as "this command cannot be addressed by
86
+ # a row of this aggregate," not to fall back on a guess.
87
+ def addressing_key_for(aggregate_name)
88
+ return Naming.reference_key(aggregate_name) if references.to_s == aggregate_name.to_s
89
+
90
+ attributes.find { |attribute| attribute.reference? && attribute.type.target_name.to_s == aggregate_name.to_s }
91
+ &.name
92
+ end
93
+ end
94
+
95
+ # A MUTATION'S OWN READINGS. Included (not extended) — Mutation is
96
+ # a Struct, so these are instance methods.
97
+ module Mutation
98
+ # An APPEND binds several fields at once, each from either a command
99
+ # ARGUMENT (a Symbol) or a LITERAL. It used to spell the Symbol bare
100
+ # and inspect the rest, which is the opposite of what a where-clause
101
+ # did with the same two kinds — see Hecks::Literal.
102
+ def appended_fields = source.transform_values { |value| Literal.render(value) }
103
+
104
+ def classified_source
105
+ if source.is_a?(Symbol)
106
+ { kind: "argument", name: source.to_s }
107
+ elsif source.is_a?(StateRef)
108
+ { kind: "state", name: source.name.to_s }
109
+ else
110
+ { kind: "literal", value: source }
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,25 @@
1
+ require_relative "traits"
2
+
3
+ module Hecks
4
+ module Bluebook
5
+ module Behaviour
6
+ # WHAT ONE PORT OPERATION DOES.
7
+ module PortOperation
8
+ include Indexed
9
+
10
+ # An operation declares no reference of its own — the OWNER is
11
+ # what it acts for, and `identity_attribute` is how that is found.
12
+ def references = nil
13
+
14
+ def identity_attribute(owner_name)
15
+ @attributes.find { |attribute| attribute.reference? && attribute.type.target_name == owner_name.to_s }
16
+ end
17
+ end
18
+
19
+ # WHAT A PORT DOES — one finder over its declared operations.
20
+ module DomainPort
21
+ def operation(named) = @operations.find { |op| op.hecks_name == named.to_s }
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,59 @@
1
+ require_relative "traits"
2
+
3
+ module Hecks
4
+ module Bluebook
5
+ module Behaviour
6
+ # WHAT AN ENTITY DOES. EXTENDED, not included — an entity is a
7
+ # CLASS, so this is singleton behaviour.
8
+ #
9
+ # `settle` is the same seam an Aggregate has, reached from `absorb`
10
+ # rather than from a constructor because a declared entity is built
11
+ # by subclassing rather than by `new`. The traits are the same
12
+ # ones, which is the whole point of them being traits: an entity's
13
+ # identity is derived exactly as an aggregate's is, and was written
14
+ # out twice before anyone could see that.
15
+ module Entity
16
+ include Identified
17
+ include Indexed
18
+ include Owns
19
+
20
+ def settle
21
+ derive_identity
22
+ index_declarations
23
+ self
24
+ end
25
+
26
+ def index_declarations
27
+ index_attributes(@attributes)
28
+ @commands_by_name = index_by_hecks_name(@commands)
29
+ @queries_by_name = index_by_hecks_name(@queries)
30
+ end
31
+
32
+ # S17, ADR 0026 — `@entities`, now genuinely NESTED entities
33
+ # (Dispatch, inside Handler) rather than always `[]`. Kept as a
34
+ # real reader rather than a hardcoded empty list for two
35
+ # reasons at once: `Value::Coercion#for_attribute` calls
36
+ # `.entities` on WHATEVER OWNER it is handed — an aggregate's or
37
+ # an entity's own — the moment it meets ANY `list_of(...)`
38
+ # attribute (entity-typed or not — `hydrate_entity_list`'s own
39
+ # fallback, `return value unless entity`, only runs once
40
+ # `.entities` has already answered) ; and `EntityInterpreter`
41
+ # now walks a DOTTED chain of entities one level at a time
42
+ # (`walk_entity_chain`) exactly the way an aggregate's own
43
+ # `.entities` is walked for its direct children. Entity's own
44
+ # header comment already promises it stays "structurally
45
+ # interchangeable with an aggregate" for exactly this reason.
46
+ def entities = @entities || []
47
+
48
+ # A piece OWNS the verbs declared on it, so they can state an
49
+ # identity — `Banking::Account.Ledger.Deposit` rather than a
50
+ # command that cannot say what it belongs to. Separate from
51
+ # `settle` because `declare` stamps AFTER absorbing, once the
52
+ # subclass that will own them exists. `@entities` too now
53
+ # (S17, ADR 0026) — a nested entity states its own owner chain
54
+ # exactly the way a nested command does.
55
+ def stamp_children = stamp(@commands, @queries, @entities)
56
+ end
57
+ end
58
+ end
59
+ end