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,581 @@
1
+ Hecks.bluebook "Bluebook" do
2
+ aggregate "Aggregate" do
3
+ description "One aggregate inside a chapter — the head a domain's declarations attach to."
4
+
5
+ reference_to Bluebook
6
+
7
+ # THE CHAPTER IT IS IN, AND ITS NAME. `Order` names nothing on its own — two
8
+ # chapters may both declare one — so the chapter is part of the identity, not
9
+ # context around it. This is the derivation `MetaValidator::Judge#identify`
10
+ # spelled by hand as "#{parent_id}::#{name}" for as long as the language could
11
+ # not say it.
12
+ identified_by :bluebook, :name
13
+
14
+ # SPELLED AS THE IR SPELLS THEM — see the note on Command's fields.
15
+ attribute :name, AggregateName
16
+ attribute :description, Description
17
+ attribute :provenance, LiteralText
18
+ # SEVERAL PATHS, because the identity is their JOIN. One is the ordinary case ;
19
+ # more than one is what anything named BENEATH another thing needs, and the
20
+ # language's own chapters are all of that kind — a command is not named by
21
+ # `Declare`, which every aggregate here spells, but by the aggregate it belongs
22
+ # to AND that name. Held as a list for the same reason attributes are : the
23
+ # order is a fact about the source, and the join reads in it.
24
+ attribute :identified_by, list_of(IdentityPath)
25
+ attribute :attributes, list_of(Field)
26
+ attribute :value_objects, list_of(ValueName)
27
+ attribute :state_field, IdentityField, optional: true
28
+ attribute :state_start, IdentityField, optional: true
29
+ attribute :transitions, list_of(Transition)
30
+ # THE AGGREGATE BOUNDARY (S10, ADR 0025 — "Rules") — checked after
31
+ # every command, before save, the SAME Rule shape Command's own
32
+ # givens/ensures already carry (description+canonical), declared
33
+ # again here rather than shared: a closed set/value object is
34
+ # aggregate-scoped in this self-hosting domain, the same reason
35
+ # CommandText and LiteralText stay two separate, identically-
36
+ # shaped value objects rather than one reused across chapters.
37
+ attribute :invariants, list_of(Rule)
38
+ # A PRECONDITION SHARED ACROSS COMMANDS, DECLARED ONCE (S10, ADR
39
+ # 0025) — the SAME Rule shape, the aggregate's own named `given`s
40
+ # that a referencing command's own (already-resolved) `given`
41
+ # entry came from.
42
+ attribute :preconditions, list_of(Rule)
43
+ # A FIELD READ THROUGH A REFERENCE, HELD LOCALLY (S12, ADR 0025 —
44
+ # "Consistency across aggregate boundaries") — the local half of
45
+ # `projects :name, from: :"reference.remote_field"`. Deliberately
46
+ # NOT folded into `attributes` — see the comment on
47
+ # `Aggregate#emits_ir`'s own `projected_fields:` entry for why.
48
+ attribute :projected_fields, list_of(ProjectedField)
49
+
50
+ value_object "AggregateName" do
51
+ attribute :value, String
52
+ invariant("an aggregate is named") { !value.to_s.empty? }
53
+ end
54
+
55
+ value_object "Description" do
56
+ attribute :value, String
57
+ invariant("a description says something") { !value.to_s.empty? }
58
+ end
59
+
60
+ value_object "IdentityField" do
61
+ attribute :value, String
62
+ end
63
+
64
+ # ONE PART OF AN IDENTITY — a path down to the SCALAR that carries it, like
65
+ # "number.value". A part on its own is not an identity ; the identity is every
66
+ # part joined, in the order they were declared.
67
+ #
68
+ # Its field is `value`, like every other one-field value object in this
69
+ # language, because that is the name the judge wraps a cell under. Spelling it
70
+ # `path` made `path.path` read nothing, and a given that reads nothing does not
71
+ # fail — it refuses everything, which is a far quieter way to be wrong.
72
+ value_object "IdentityPath" do
73
+ attribute :value, String
74
+ end
75
+
76
+ value_object "Field" do
77
+ attribute :name, String
78
+ attribute :type, String
79
+ attribute :list, String
80
+ attribute :optional, String, optional: true
81
+ attribute :pattern, String, optional: true
82
+ # Held as a LITERAL, written so its type survives — 0.0 rather than "0.0".
83
+ # Without this an aggregate attribute's default was simply dropped, and the
84
+ # reconstruction hardcoded nil, which happened to match every corpus member
85
+ # and would have lied about the first one that declared a default.
86
+ attribute :default, String
87
+ # THE CLOSED SET THIS VALUE MUST BELONG TO, named where it was already
88
+ # declared — `Vocabulary::QueryComparator`. Qualified by the aggregate that
89
+ # holds it, because a closed set is a value object INSIDE an aggregate and
90
+ # the aggregate is the only thing a reference can reach ; `one_of` is the
91
+ # other word and it can only ever SYNTHESISE a new set, never name one
92
+ # twice. Absent for almost every attribute, so it is optional.
93
+ attribute :admits, String, optional: true
94
+ # `reference_to`, `belongs_to`, `has_one`, or `has_many`; absent on
95
+ # ordinary value/entity fields. This keeps relationship language through
96
+ # the self-hosted round trip instead of collapsing it to a reference.
97
+ attribute :relationship, String, optional: true
98
+ end
99
+
100
+ value_object "ValueName" do
101
+ attribute :name, String
102
+ end
103
+
104
+ value_object "Transition" do
105
+ attribute :command, String
106
+ attribute :from_state, String
107
+ attribute :to_state, String
108
+ end
109
+
110
+ # THE SAME SHAPE Command's own "Rule" value object carries — see
111
+ # behavior.bluebook — declared again here rather than shared, per
112
+ # that value object's own comment.
113
+ value_object "Rule" do
114
+ attribute :description, String
115
+ attribute :canonical, String
116
+ end
117
+
118
+ # S12, ADR 0025 — `name` is this aggregate's own field being
119
+ # declared; `reference` names the local `reference_to` attribute to
120
+ # read through; `remote_field` names the scalar on the target. All
121
+ # three are plain identifiers, the same shape `Field#name`/`#type`
122
+ # already are.
123
+ value_object "ProjectedField" do
124
+ attribute :name, String
125
+ attribute :reference, String
126
+ attribute :remote_field, String
127
+ end
128
+
129
+ command "Declare" do
130
+ role "Language"
131
+ goal "Open an aggregate inside a chapter"
132
+
133
+ reference_to Bluebook
134
+ attribute :name, AggregateName
135
+ attribute :description, Description, optional: true
136
+ # See Command.Declare's own `provenance` — identical shape, one
137
+ # level up. LiteralText, not a dedicated value object: the field
138
+ # rides the same self-describing `inspect` encoding `default:`
139
+ # already uses, so it needs no invariant of its own.
140
+ attribute :provenance, LiteralText, optional: true
141
+
142
+ attribute :position, Position, optional: true
143
+
144
+ sets :bluebook
145
+ sets :name
146
+ sets :description
147
+ sets :provenance
148
+ sets :position
149
+ emits "RootDeclared"
150
+ end
151
+
152
+ # ONE PART OF THE IDENTITY, attached the way one attribute is. It was an
153
+ # argument on Declare while an identity was a single field ; a list cannot be
154
+ # carried by one argument, and every other list in this language is filled by
155
+ # a verb of its own, so this is the shape that was already here.
156
+ command "Identify" do
157
+ role "Language"
158
+ goal "Attach one part of the aggregate's identity"
159
+
160
+ attribute :path, IdentityPath
161
+
162
+ # AN IDENTITY PART NAMES SOMETHING. This rule used to sit on Seal reading
163
+ # `empty? || == "id" || includes(".")` — two escapes and a check. The escapes
164
+ # were the whole of it : `empty?` let an aggregate say nothing and be given a
165
+ # minted `id`, and `== "id"` let it name that slot outright. Both are gone,
166
+ # so what is left is the rule itself, and it now runs on every part rather
167
+ # than on whichever one a single field happened to hold.
168
+ #
169
+ # THREE WAYS TO REACH A SCALAR (ADR 0025, "Identity"), and this given no
170
+ # longer distinguishes them — it used to accept only a dotted path
171
+ # (`number.value`, a value object's own field) or an `_id`-suffixed
172
+ # reference, and REFUSED a bare scalar field (`sku`), which is legal
173
+ # too : a plain primitive attribute IS already a scalar, nothing to
174
+ # open. That distinction belongs to the BUILDER, which resolves each
175
+ # spelling before an identity part ever reaches here
176
+ # (`AttributeCollector#resolve_identity_field!` — the single place
177
+ # that decides dotted-unwrap vs. reference vs. bare-scalar, and the
178
+ # only place that can, since it alone holds the attribute's declared
179
+ # type). What is left for THIS rule to say, once the builder has
180
+ # already resolved a real shape, is only that a part was actually
181
+ # given a name at all.
182
+ given("an identity part names something") { !path.value.to_s.empty? }
183
+
184
+ sets :identified_by, append: { value: :path }
185
+ emits "RootIdentified"
186
+ end
187
+
188
+ # The way back, one level down: the aggregates DECLARED IN a chapter, found by
189
+ # the chapter that declared them. The parent reference IS the query.
190
+ #
191
+ # `bluebook_id` is an IdentityField, not an AggregateName — it holds the id of
192
+ # the chapter above, and calling that a name for aggregates was sloppiness that
193
+ # would read as a lie the first time somebody looked.
194
+ # WHERE IT SITS AMONG ITS SIBLINGS. Declaration order is a FACT ABOUT THE
195
+ # SOURCE — the IR is a contract read straight from this file,
196
+ # field for field AND INDEX FOR INDEX. So the language HOLDS it, instead of
197
+ # inheriting it from whatever order a store happens to iterate: an ask that
198
+ # does not say how it is ordered is answered in identity order, which is
199
+ # right for every other ask and fatal for the one that is the SOURCE.
200
+ value_object "Position" do
201
+ attribute :value, Integer
202
+ end
203
+
204
+ attribute :position, Position
205
+
206
+ query "DeclaredIn" do
207
+ attribute :bluebook, IdentityField
208
+ where(bluebook: :bluebook)
209
+ order_by :position
210
+ end
211
+
212
+ command "Attribute" do
213
+ role "Language"
214
+ goal "Attach one attribute to the aggregate"
215
+
216
+ # The TYPE is a reference to the value object it names. An attribute
217
+ # naming an undeclared value object then fails REFERENCE RESOLUTION —
218
+ # "no Shape with id …" — with no predicate at all.
219
+ #
220
+ # This is the rule "attributes must use value-object types", which could
221
+ # not be a `given` : it needs, for each element of a list, a check against
222
+ # another list, and the sublanguage has no quantifier and cannot see
223
+ # inside a list element. As a reference it stops being a predicate and
224
+ # becomes a consequence of the model.
225
+ reference_to ValueObject, as: :type
226
+ attribute :name, FieldName
227
+ attribute :list, ListFlag
228
+ attribute :optional, ListFlag, optional: true
229
+ # A PATTERN IS A REGEX, not a flag — see the note on ListFlag below.
230
+ attribute :pattern, LiteralText, optional: true
231
+ attribute :default, LiteralText, optional: true
232
+ # FREE TEXT, not a reference. A closed set is a value object INSIDE an
233
+ # aggregate, and `reference_to` reaches heads only — so the qualified name
234
+ # is carried as text and checked where it is READ, by the reader, which
235
+ # already raises for the other things the language cannot say about itself.
236
+ attribute :admits, LiteralText, optional: true
237
+ attribute :relationship, LiteralText, optional: true
238
+
239
+ sets :attributes, append: { name: :name, type: :type, list: :list, default: :default, optional: :optional, pattern: :pattern, admits: :admits, relationship: :relationship }
240
+
241
+ emits "AttributeAttached"
242
+ end
243
+
244
+ # An attribute that points at ANOTHER aggregate's head.
245
+ #
246
+ # `Attribute` types its type as `reference_to ValueObject`, which is what makes
247
+ # "attributes use value-object types" a consequence of the model rather than a
248
+ # predicate. A cross-aggregate reference cannot go through it: its type is
249
+ # `Reference<Customer>`, which is not a value object and never will be. So the
250
+ # judge SKIPPED every one of them, and the meta-domain did not hold
251
+ # Account#customer_id at all — the way back was lossy for exactly one
252
+ # construct, and the comment excusing it said "the language does not model
253
+ # reference-typed attributes yet".
254
+ command "Reference" do
255
+ role "Language"
256
+ goal "Attach an attribute that points at another aggregate's head"
257
+
258
+ # POINTS AT a declared head, and that is the whole rule.
259
+ #
260
+ # This carried `attribute :type, ReferenceType` — a STRING — with an invariant
261
+ # apologising for itself, because the shape that actually tells a reference
262
+ # from a value object (`Reference<Something>`) is unsayable in the predicate
263
+ # sublanguage. That was never a sublanguage gap; a string was simply the wrong
264
+ # model. As a reference it stops being a predicate and becomes a consequence
265
+ # of the model, exactly as Attribute's type does — and it is STRONGER than the
266
+ # prefix match, which only ever checked that the text looked right.
267
+ reference_to Aggregate, as: :points_at
268
+ attribute :name, FieldName
269
+ attribute :list, ListFlag
270
+ attribute :optional, ListFlag, optional: true
271
+ # A PATTERN IS A REGEX, not a flag — see the note on ListFlag below.
272
+ attribute :pattern, LiteralText, optional: true
273
+ attribute :default, LiteralText, optional: true
274
+ attribute :admits, LiteralText, optional: true
275
+ attribute :relationship, LiteralText, optional: true
276
+
277
+ sets :attributes, append: { name: :name, type: :points_at, list: :list, default: :default, optional: :optional, pattern: :pattern, admits: :admits, relationship: :relationship }
278
+
279
+ emits "ReferenceAttached"
280
+ end
281
+
282
+ # An attribute whose type names a PIECE this aggregate holds.
283
+ #
284
+ # `Account#ledger` is a list of LedgerEntry — neither a value object nor another
285
+ # aggregate's head — so it fitted no verb and the walk skipped it, the last
286
+ # construct the way back could not carry. Same trick as Reference: point at the
287
+ # entity's own declaration, and "an aggregate holds a piece it declared" costs
288
+ # no predicate at all. Entities are declared before attributes are offered, so
289
+ # the reference resolves.
290
+ command "Holds" do
291
+ role "Language"
292
+ goal "Attach an attribute whose type is a piece of this aggregate"
293
+
294
+ reference_to Entity, as: :holds
295
+ attribute :name, FieldName
296
+ attribute :list, ListFlag
297
+ attribute :optional, ListFlag, optional: true
298
+ # A PATTERN IS A REGEX, not a flag — see the note on ListFlag below.
299
+ attribute :pattern, LiteralText, optional: true
300
+ attribute :default, LiteralText, optional: true
301
+ attribute :admits, LiteralText, optional: true
302
+ attribute :relationship, LiteralText, optional: true
303
+
304
+ sets :attributes, append: { name: :name, type: :holds, list: :list, default: :default, optional: :optional, pattern: :pattern, admits: :admits, relationship: :relationship }
305
+
306
+ emits "PieceHeld"
307
+ end
308
+
309
+ # THE ONLY RULE THAT CAN REFUSE AN UNNAMED ATTRIBUTE, and it lives here.
310
+ #
311
+ # Attribute, Reference and Holds each carried `given("… is named")` beside
312
+ # this, and not one of them could ever fire: an EMPTY name builds the value
313
+ # object and this invariant refuses first, while an ABSENT name builds no
314
+ # value object and the given did not refuse at all — probed, not reasoned.
315
+ # Three rules that read as rules and were decoration. A predicate cannot
316
+ # guard what a type already guards.
317
+ value_object "FieldName" do
318
+ attribute :value, String
319
+ invariant("an attribute is named") { !value.to_s.empty? }
320
+ end
321
+
322
+ value_object "TypeName" do
323
+ attribute :value, String
324
+ invariant("a type is named") { !value.to_s.empty? }
325
+ end
326
+
327
+ # A literal written the way it can be read back — see Readings#encode_literal.
328
+ value_object "LiteralText" do
329
+ attribute :value, String
330
+ end
331
+
332
+ # TRUE OR FALSE, AND NOTHING ELSE. `list` and `optional` are the two fields
333
+ # this is about — they are the answers to a yes/no question, written as text
334
+ # because that is what the wire carries.
335
+ #
336
+ # `pattern` was typed with it too, which is not a loose typing but a FALSE
337
+ # one: a pattern holds a regex, and this invariant admits exactly two strings
338
+ # neither of which is one. It never fired only because no aggregate-level or
339
+ # entity-level attribute in the corpus declares a pattern — banking's one
340
+ # `pattern:` sits on a VALUE OBJECT field, which is built by
341
+ # `ValueObject.Field` and so went down a different command entirely. The
342
+ # first head that declared a pattern would have been refused by a rule about
343
+ # lists. It is `LiteralText` now, which is what the other four element
344
+ # builders already say a pattern is: free text.
345
+ value_object "ListFlag" do
346
+ attribute :value, String
347
+ invariant("list is true or false") { value.to_s == "true" || value.to_s == "false" }
348
+ end
349
+
350
+ command "Lifecycle" do
351
+ role "Language"
352
+ goal "Name the field this aggregate's state machine runs on"
353
+
354
+ attribute :state_field, FieldName
355
+ attribute :state_start, FieldName
356
+
357
+ sets :state_field
358
+ sets :state_start
359
+
360
+ emits "LifecycleNamed"
361
+ end
362
+
363
+ command "Transition" do
364
+ role "Language"
365
+ goal "Attach one transition to the state machine"
366
+
367
+ attribute :command, FieldName
368
+ attribute :from_state, FieldName, optional: true
369
+ attribute :to_state, FieldName
370
+
371
+ sets :transitions, append: { command: :command, from_state: :from_state, to_state: :to_state }
372
+
373
+ emits "TransitionAttached"
374
+ end
375
+
376
+ command "Seal" do
377
+ role "Language"
378
+ goal "Close an aggregate — the checks that need every declaration present"
379
+
380
+ # The builders validate at `build`, once everything is collected. A given
381
+ # fires at DISPATCH, when later declarations do not exist yet. So the
382
+ # whole-document rules live on a command dispatched after the aggregate
383
+ # is fully declared. This is the shape the tier-3 rules need ; whether
384
+ # the sublanguage can express their CONTENT is the open question.
385
+
386
+ # THE RULE THAT WAS HERE WAS NOT TRUE, and nothing could tell us while Seal
387
+ # went undispatched. `given("an aggregate declares at least one attribute")`
388
+ # was invented for this command — it is not one of the rules ported from the
389
+ # builders, so no bluebook was ever written against it. The first time it
390
+ # ran it refused twenty-five of them, including every aggregate whose state
391
+ # is a `lifecycle` rather than an `attribute` : a state machine declares no
392
+ # attribute and is perfectly well formed.
393
+ #
394
+ # Seal stays. It is the right SHAPE for a whole-document rule — dispatched
395
+ # once every declaration is present — and it is now actually dispatched, so
396
+ # the next rule put here will be seen refusing instead of sitting decorative.
397
+ # The rule waiting for it is "a mutation names a field the aggregate has",
398
+ # which lives in AggregateBuilder today because saying it here needs to reach
399
+ # a list on another root.
400
+ # AN AGGREGATE SAYS WHAT IT IS KNOWN BY. Nothing falls back to `id` any more.
401
+ #
402
+ # The fallback was never a default — it was a MINT. An aggregate that said
403
+ # nothing got an id invented for it, so the same declaration named a
404
+ # different record on the next run, and nothing outside the runtime could
405
+ # name a record at all. The rule that stood here permitted it in as many
406
+ # words (`empty? || == "id"`), and the comment named exactly who was
407
+ # leaning on it : the language's own chapters, all fourteen of them.
408
+ #
409
+ # They derive now, like everything else does. What each PART must look like
410
+ # is Identify's rule ; this one only asks that there be one.
411
+ given("an aggregate says what it is known by") { !identified_by.empty? }
412
+
413
+ emits "RootSealed"
414
+ end
415
+
416
+ command "Value" do
417
+ role "Language"
418
+ goal "Declare a value object belonging to this aggregate"
419
+
420
+ attribute :name, FieldName
421
+
422
+ sets :value_objects, append: { name: :name }
423
+
424
+ emits "ValueDeclared"
425
+ end
426
+
427
+ # THE AGGREGATE BOUNDARY (S10, ADR 0025 — "Rules") — the same shape
428
+ # Command.Rule attaches a given with, one level up.
429
+ command "Invariant" do
430
+ role "Language"
431
+ goal "Attach one invariant to the aggregate"
432
+
433
+ attribute :description, LiteralText, optional: true
434
+ attribute :canonical, LiteralText
435
+
436
+ given("a rule says what it means") { !description.value.to_s.empty? }
437
+ given("a rule survives extraction") { !canonical.value.to_s.empty? }
438
+
439
+ sets :invariants, append: { description: :description, canonical: :canonical }
440
+
441
+ emits "InvariantAttached"
442
+ end
443
+
444
+ # A PRECONDITION SHARED ACROSS COMMANDS, DECLARED ONCE (S10, ADR
445
+ # 0025) — the aggregate's own named `given`, referenced back by a
446
+ # command rather than re-typed.
447
+ command "Precondition" do
448
+ role "Language"
449
+ goal "Declare one precondition commands can reference by name"
450
+
451
+ attribute :description, LiteralText, optional: true
452
+ attribute :canonical, LiteralText
453
+
454
+ given("a rule says what it means") { !description.value.to_s.empty? }
455
+ given("a rule survives extraction") { !canonical.value.to_s.empty? }
456
+
457
+ sets :preconditions, append: { description: :description, canonical: :canonical }
458
+
459
+ emits "PreconditionAttached"
460
+ end
461
+
462
+ # THE LOCAL HALF OF A CROSS-AGGREGATE READ (S12, ADR 0025) — the
463
+ # target's own existence and shape are checked elsewhere
464
+ # (BluebookBuilder#validate_projected_fields!, once every aggregate
465
+ # in the chapter is real), the same two-tier split a query's own
466
+ # hop already uses.
467
+ command "Projects" do
468
+ role "Language"
469
+ goal "Declare a field read locally through a reference"
470
+
471
+ attribute :name, FieldName
472
+ attribute :reference, FieldName
473
+ attribute :remote_field, FieldName
474
+
475
+ given("a projected field is named") { !name.value.to_s.empty? }
476
+ given("a projected field reads through a reference") { !reference.value.to_s.empty? }
477
+ given("a projected field names a remote field") { !remote_field.value.to_s.empty? }
478
+
479
+ sets :projected_fields, append: { name: :name, reference: :reference, remote_field: :remote_field }
480
+
481
+ emits "ProjectedFieldAttached"
482
+ end
483
+
484
+ # HOW AGGREGATE IS SPELLED. These rows live with the concept
485
+ # they describe; SyntaxBoot discovers every aggregate-local seed table.
486
+ # Contexts represented here: Aggregate, Lifecycle.
487
+ value_object "KeywordSeed" do
488
+ attribute :word, String
489
+ attribute :context, String, admits: "Syntax::Context"
490
+ attribute :body, String, admits: "Syntax::Body"
491
+ attribute :inner, String
492
+ attribute :opens, String
493
+ attribute :fills, String
494
+ attribute :status, String, default: "admitted", admits: "Syntax::Status"
495
+ attribute :was, String
496
+ attribute :resolves_via, String, default: "", admits: "Syntax::ResolutionScope"
497
+ attribute :disambiguator, String, default: "", admits: "Syntax::Disambiguator"
498
+
499
+ member word: "description", context: "Aggregate", body: "none", inner: "", opens: "", fills: "description"
500
+ member word: "provenance", context: "Aggregate", body: "none", inner: "", opens: "", fills: "provenance", calls: "provenance_impl"
501
+ member word: "identified_by", context: "Aggregate", body: "keywords", inner: "ValueObject", opens: "ValueObject", fills: "identified_by", calls: "identified_by_impl"
502
+ member word: "identified_by", context: "Aggregate", body: "none", inner: "", opens: "", fills: "identified_by", calls: "identified_by_impl"
503
+ member word: "reference_to", context: "Aggregate", body: "none", inner: "", opens: "", fills: "attributes", calls: "reference_to_impl"
504
+ member word: "has_many", context: "Aggregate", body: "none", inner: "", opens: "", fills: "attributes", calls: "has_many_impl"
505
+ member word: "has_one", context: "Aggregate", body: "none", inner: "", opens: "", fills: "attributes", calls: "has_one_impl"
506
+ member word: "belongs_to", context: "Aggregate", body: "none", inner: "", opens: "", fills: "attributes", calls: "belongs_to_impl"
507
+ member word: "lifecycle", context: "Aggregate", body: "keywords", inner: "Lifecycle", opens: "", fills: "state_field", calls: "lifecycle_impl"
508
+ member word: "entity", context: "Aggregate", body: "keywords", inner: "Entity", opens: "Entity", fills: "", calls: "entity_impl"
509
+ member word: "query", context: "Aggregate", body: "keywords", inner: "Query", opens: "Query", fills: "", calls: "query_impl"
510
+ member word: "policy", context: "Aggregate", body: "keywords", inner: "Policy", opens: "Policy", fills: "", calls: "policy_impl"
511
+ member word: "value_object", context: "Aggregate", body: "keywords", inner: "ValueObject", opens: "ValueObject", fills: "value_objects"
512
+ member word: "command", context: "Aggregate", body: "keywords", inner: "Command", opens: "Command", fills: "", calls: "command_impl"
513
+ member word: "attribute", context: "Aggregate", body: "none", inner: "", opens: "", fills: "attributes", calls: "attribute_impl"
514
+ member word: "invariant", context: "Aggregate", body: "source", inner: "", opens: "", fills: "invariants", calls: "invariant_impl"
515
+ member word: "given", context: "Aggregate", body: "source", inner: "", opens: "", fills: "preconditions", resolves_via: "owner_keyed", disambiguator: "declared_by", calls: "given_impl"
516
+ member word: "projects", context: "Aggregate", body: "none", inner: "", opens: "", fills: "projected_fields", calls: "projects_impl"
517
+ member word: "transition", context: "Lifecycle", body: "none", inner: "", opens: "", fills: "transitions", calls: "transition_impl"
518
+
519
+ end
520
+
521
+ value_object "ArgumentSeed" do
522
+ attribute :keyword, String
523
+ attribute :context, String, admits: "Syntax::Context"
524
+ attribute :at, String
525
+ attribute :named, String
526
+ attribute :kind, String, admits: "Syntax::ArgumentKind"
527
+ attribute :required, String
528
+ attribute :fills, String
529
+ attribute :selects, String
530
+ attribute :pair_key_fills, String
531
+ attribute :pair_value_fills, String
532
+ attribute :pairs_shape, String, admits: "Syntax::PairsShape"
533
+ attribute :status, String, default: "admitted", admits: "Syntax::Status"
534
+ attribute :variadic, String
535
+ attribute :minimum, String
536
+ attribute :coerce, String
537
+ attribute :blank_message, String
538
+
539
+ member keyword: "description", context: "Aggregate", at: "1", named: "", kind: "text", required: "true", fills: "description", coerce: "false"
540
+ member keyword: "provenance", context: "Aggregate", at: "", named: "from", kind: "literal", required: "true", fills: "provenance"
541
+ member keyword: "identified_by", context: "Aggregate", at: "1", named: "", kind: "symbol", required: "false", fills: "identified_by", variadic: "true", minimum: "2"
542
+ member keyword: "identified_by", context: "Aggregate", at: "1", named: "", kind: "constant", required: "false", fills: "type"
543
+ member keyword: "identified_by", context: "Aggregate", at: "", named: "as", kind: "symbol", required: "false", fills: "name"
544
+ member keyword: "reference_to", context: "Aggregate", at: "1", named: "", kind: "constant", required: "true", fills: "type"
545
+ member keyword: "reference_to", context: "Aggregate", at: "", named: "as", kind: "symbol", required: "false", fills: "name"
546
+ member keyword: "reference_to", context: "Aggregate", at: "", named: "optional", kind: "flag", required: "false", fills: "optional"
547
+ member keyword: "has_many", context: "Aggregate", at: "1", named: "", kind: "constant", required: "true", fills: "type"
548
+ member keyword: "has_many", context: "Aggregate", at: "", named: "as", kind: "symbol", required: "false", fills: "name"
549
+ member keyword: "has_one", context: "Aggregate", at: "1", named: "", kind: "constant", required: "true", fills: "type"
550
+ member keyword: "has_one", context: "Aggregate", at: "", named: "as", kind: "symbol", required: "false", fills: "name"
551
+ member keyword: "has_one", context: "Aggregate", at: "", named: "optional", kind: "flag", required: "false", fills: "optional"
552
+ member keyword: "belongs_to", context: "Aggregate", at: "1", named: "", kind: "constant", required: "true", fills: "type"
553
+ member keyword: "belongs_to", context: "Aggregate", at: "", named: "as", kind: "symbol", required: "false", fills: "name"
554
+ member keyword: "belongs_to", context: "Aggregate", at: "", named: "optional", kind: "flag", required: "false", fills: "optional"
555
+ member keyword: "lifecycle", context: "Aggregate", at: "1", named: "", kind: "symbol", required: "true", fills: "state_field"
556
+ member keyword: "lifecycle", context: "Aggregate", at: "", named: "default", kind: "literal", required: "true", fills: "state_start"
557
+ member keyword: "entity", context: "Aggregate", at: "1", named: "", kind: "text", required: "true", fills: "name"
558
+ member keyword: "query", context: "Aggregate", at: "1", named: "", kind: "text", required: "true", fills: "name"
559
+ member keyword: "policy", context: "Aggregate", at: "1", named: "", kind: "text", required: "true", fills: "name"
560
+ member keyword: "value_object", context: "Aggregate", at: "1", named: "", kind: "text", required: "true", fills: "name"
561
+ member keyword: "command", context: "Aggregate", at: "1", named: "", kind: "text", required: "true", fills: "name"
562
+ member keyword: "command", context: "Aggregate", at: "", named: "from", kind: "literal", required: "false", fills: "from"
563
+ member keyword: "attribute", context: "Aggregate", at: "1", named: "", kind: "symbol", required: "true", fills: "name"
564
+ member keyword: "attribute", context: "Aggregate", at: "2", named: "", kind: "constant", required: "true", fills: "type"
565
+ member keyword: "attribute", context: "Aggregate", at: "", named: "default", kind: "literal", required: "false", fills: "default"
566
+ member keyword: "attribute", context: "Aggregate", at: "", named: "optional", kind: "flag", required: "false", fills: "optional"
567
+ member keyword: "attribute", context: "Aggregate", at: "", named: "pattern", kind: "text", required: "false", fills: "pattern"
568
+ member keyword: "attribute", context: "Aggregate", at: "", named: "admits", kind: "text", required: "false", fills: "admits"
569
+ member keyword: "attribute", context: "Aggregate", at: "", named: "one_of", kind: "list", required: "false", fills: "one_of"
570
+ member keyword: "invariant", context: "Aggregate", at: "1", named: "", kind: "text", required: "true", fills: "description"
571
+ member keyword: "given", context: "Aggregate", at: "1", named: "", kind: "text", required: "true", fills: "description"
572
+ member keyword: "given", context: "Aggregate", at: "", named: "declared_by", kind: "constant", required: "false", fills: "declared_by"
573
+ member keyword: "projects", context: "Aggregate", at: "1", named: "", kind: "symbol", required: "true", fills: "name"
574
+ member keyword: "projects", context: "Aggregate", at: "", named: "from", kind: "symbol", required: "true", fills: "from"
575
+ member keyword: "transition", context: "Lifecycle", at: "1", named: "", kind: "pairs", required: "true", fills: "", pair_key_fills: "command", pair_value_fills: "to_state", pairs_shape: "fields"
576
+ member keyword: "transition", context: "Lifecycle", at: "", named: "from", kind: "text", required: "false", fills: "from_state"
577
+ member keyword: "transition", context: "Lifecycle", at: "", named: "from", kind: "list", required: "false", fills: "from_state"
578
+
579
+ end
580
+ end
581
+ end
@@ -0,0 +1,76 @@
1
+ Hecks.bluebook "Paging" do
2
+ vision "The four words a page of results is read through — limit, offset, cursor, nulls — held as their own sub-language rather than as four more rows in the core's own grammar table. ADR 0026's own worked example: the core grammar does not name its extension points, so this chapter names ITSELF onto them, with `attaches_to`."
3
+ generic
4
+ attaches_to "Query", "ReadModel"
5
+
6
+ # A PLACE TO HOLD ROWS, AND NOTHING ELSE — the same shape `Vocabulary`
7
+ # (vocabulary.bluebook) already is: no commands, because nothing ever
8
+ # dispatches this aggregate for real. `SyntaxBoot`'s own generic
9
+ # discovery reads `KeywordSeed`/`ArgumentSeed`'s rows straight off the
10
+ # declared IR, the exact way it already reads the core's own — and
11
+ # dispatches them through the CORE's `Bluebook::Syntax.Keyword`/
12
+ # `.Argument` verbs, which is why these rows need no entity, no
13
+ # lifecycle, no creating command of their own here: the verb they ride
14
+ # already exists, and a second copy of it would be the core's own
15
+ # machinery duplicated for no reason.
16
+ aggregate "Syntax" do
17
+ description "The rows this sub-language contributes to the core's own grammar table."
18
+ identified_by PagingSyntaxName, as: :name
19
+
20
+
21
+ value_object "PagingSyntaxName" do
22
+ attribute :value, String
23
+ invariant("a syntax table is named") { !value.to_s.empty? }
24
+ end
25
+
26
+ # THE SAME EIGHT ROWS syntax.bluebook held directly until now — moved,
27
+ # not rewritten. `limit`/`offset`/`cursor`/`nulls`, on both `Query`
28
+ # and `ReadModel`. See syntax.bluebook's own comment at the old site
29
+ # for why `limit` fills a dedicated field while the other three land
30
+ # on the open `options` map.
31
+ value_object "KeywordSeed" do
32
+ attribute :word, String
33
+ attribute :context, String
34
+ attribute :body, String
35
+ attribute :inner, String
36
+ attribute :opens, String
37
+ attribute :fills, String
38
+ attribute :was, String, optional: true
39
+
40
+ member word: "limit", context: "Query", body: "none", inner: "", opens: "", fills: "limit", calls: "limit_impl"
41
+ member word: "offset", context: "Query", body: "none", inner: "", opens: "", fills: "options", calls: "offset_impl"
42
+ member word: "cursor", context: "Query", body: "none", inner: "", opens: "", fills: "options"
43
+ member word: "nulls", context: "Query", body: "none", inner: "", opens: "", fills: "options"
44
+
45
+ member word: "limit", context: "ReadModel", body: "none", inner: "", opens: "", fills: "options", calls: "limit_impl"
46
+ member word: "offset", context: "ReadModel", body: "none", inner: "", opens: "", fills: "options", calls: "offset_impl"
47
+ member word: "cursor", context: "ReadModel", body: "none", inner: "", opens: "", fills: "options"
48
+ member word: "nulls", context: "ReadModel", body: "none", inner: "", opens: "", fills: "options"
49
+ end
50
+
51
+ value_object "ArgumentSeed" do
52
+ attribute :keyword, String
53
+ attribute :context, String
54
+ attribute :at, String, optional: true
55
+ attribute :named, String, optional: true
56
+ attribute :kind, String
57
+ attribute :required, String
58
+ attribute :fills, String, optional: true
59
+ attribute :selects, String, optional: true
60
+ attribute :pair_key_fills, String, optional: true
61
+ attribute :pair_value_fills, String, optional: true
62
+ attribute :pairs_shape, String, optional: true
63
+ attribute :variadic, String, optional: true
64
+
65
+ member keyword: "limit", context: "Query", at: "1", named: "", kind: "number", required: "true", fills: "limit"
66
+ member keyword: "offset", context: "Query", at: "1", named: "", kind: "number", required: "true", fills: "value"
67
+ member keyword: "cursor", context: "Query", at: "1", named: "", kind: "symbol", required: "true", fills: "value"
68
+ member keyword: "nulls", context: "Query", at: "1", named: "", kind: "symbol", required: "true", fills: "mode"
69
+
70
+ member keyword: "limit", context: "ReadModel", at: "1", named: "", kind: "number", required: "true", fills: "value"
71
+ member keyword: "offset", context: "ReadModel", at: "1", named: "", kind: "number", required: "true", fills: "value"
72
+ member keyword: "cursor", context: "ReadModel", at: "1", named: "", kind: "symbol", required: "true", fills: "value"
73
+ member keyword: "nulls", context: "ReadModel", at: "1", named: "", kind: "symbol", required: "true", fills: "mode"
74
+ end
75
+ end
76
+ end