hecks 2.8.0 → 2.9.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 (404) hide show
  1. checksums.yaml +4 -4
  2. data/exe/hecks +4 -6
  3. data/lib/hecks/adapters/driven/claude_code.rb +4 -104
  4. data/lib/hecks/adapters/driven/d1.rb +44 -237
  5. data/lib/hecks/adapters/driven/folder.rb +17 -76
  6. data/lib/hecks/adapters/driven/google_authentication.rb +15 -49
  7. data/lib/hecks/adapters/driven/governance_authorization.rb +23 -70
  8. data/lib/hecks/adapters/driven/heki/journal.rb +8 -41
  9. data/lib/hecks/adapters/driven/heki/saga_store.rb +12 -54
  10. data/lib/hecks/adapters/driven/heki/snapshot.rb +4 -12
  11. data/lib/hecks/adapters/driven/heki.rb +13 -42
  12. data/lib/hecks/adapters/driven/identity_registry.rb +3 -14
  13. data/lib/hecks/adapters/driven/in_memory_ordering.rb +9 -26
  14. data/lib/hecks/adapters/driven/in_process_key_vault.rb +2 -14
  15. data/lib/hecks/adapters/driven/lambda/client.rb +10 -51
  16. data/lib/hecks/adapters/driven/lambda.rb +10 -84
  17. data/lib/hecks/adapters/driven/local_storage.rb +5 -40
  18. data/lib/hecks/adapters/driven/memory.rb +17 -98
  19. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +5 -21
  20. data/lib/hecks/adapters/driven/postgres/codec.rb +6 -28
  21. data/lib/hecks/adapters/driven/postgres/outbox.rb +3 -12
  22. data/lib/hecks/adapters/driven/postgres/reconnect.rb +4 -30
  23. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +34 -108
  24. data/lib/hecks/adapters/driven/postgres.rb +145 -134
  25. data/lib/hecks/adapters/driven/postgres_era.adapter +4 -15
  26. data/lib/hecks/adapters/driven/prism.rb +7 -30
  27. data/lib/hecks/adapters/driven/sql_query_builder.rb +21 -121
  28. data/lib/hecks/adapters/driven/sqlite/codec.rb +8 -40
  29. data/lib/hecks/adapters/driven/sqlite/projection.rb +26 -82
  30. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +40 -84
  31. data/lib/hecks/adapters/driven/sqlite.rb +128 -65
  32. data/lib/hecks/adapters/driven/tenant_provisioner.rb +5 -48
  33. data/lib/hecks/adapters/driven.rb +5 -36
  34. data/lib/hecks/adapters/driving/github_webhook.rb +8 -76
  35. data/lib/hecks/behaviors/dsl.rb +7 -25
  36. data/lib/hecks/behaviors/expectations.rb +31 -141
  37. data/lib/hecks/behaviors/ir.rb +7 -20
  38. data/lib/hecks/behaviors/rspec.rb +6 -17
  39. data/lib/hecks/behaviors/runner.rb +10 -34
  40. data/lib/hecks/behaviors.rb +4 -8
  41. data/lib/hecks/bluebook/aggregate.rb +9 -75
  42. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +9 -38
  43. data/lib/hecks/bluebook/assembly/build.rb +4 -15
  44. data/lib/hecks/bluebook/assembly/contract.rb +15 -136
  45. data/lib/hecks/bluebook/assembly/contracts.rb +54 -174
  46. data/lib/hecks/bluebook/assembly/marks.rb +31 -223
  47. data/lib/hecks/bluebook/assembly/specializer.rb +6 -52
  48. data/lib/hecks/bluebook/assembly.rb +6 -45
  49. data/lib/hecks/bluebook/attribute.rb +12 -61
  50. data/lib/hecks/bluebook/behaviour/aggregate.rb +10 -39
  51. data/lib/hecks/bluebook/behaviour/attribute.rb +3 -14
  52. data/lib/hecks/bluebook/behaviour/chapter.rb +14 -42
  53. data/lib/hecks/bluebook/behaviour/command.rb +14 -66
  54. data/lib/hecks/bluebook/behaviour/domain_port.rb +4 -20
  55. data/lib/hecks/bluebook/behaviour/entity.rb +7 -27
  56. data/lib/hecks/bluebook/behaviour/hexagon.rb +23 -41
  57. data/lib/hecks/bluebook/behaviour/lifecycle.rb +15 -40
  58. data/lib/hecks/bluebook/behaviour/policy.rb +23 -52
  59. data/lib/hecks/bluebook/behaviour/process_manager.rb +30 -57
  60. data/lib/hecks/bluebook/behaviour/read_model.rb +25 -31
  61. data/lib/hecks/bluebook/behaviour/traits.rb +27 -64
  62. data/lib/hecks/bluebook/behaviour/value_object.rb +5 -24
  63. data/lib/hecks/bluebook/capabilities.rb +17 -49
  64. data/lib/hecks/bluebook/chapter.rb +10 -48
  65. data/lib/hecks/bluebook/command.rb +23 -81
  66. data/lib/hecks/bluebook/domain_port.rb +14 -63
  67. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +50 -229
  68. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +50 -377
  69. data/lib/hecks/bluebook/dsl/attribute_collector.rb +33 -204
  70. data/lib/hecks/bluebook/dsl/binding_proxy.rb +16 -69
  71. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +86 -708
  72. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +56 -170
  73. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +1 -0
  74. data/lib/hecks/bluebook/dsl/command_builder.rb +74 -546
  75. data/lib/hecks/bluebook/dsl/const_shim.rb +8 -41
  76. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +12 -87
  77. data/lib/hecks/bluebook/dsl/entity_builder.rb +44 -330
  78. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +20 -269
  79. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +26 -194
  80. data/lib/hecks/bluebook/dsl/identity_declaration.rb +13 -88
  81. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +6 -20
  82. data/lib/hecks/bluebook/dsl/policy_builder.rb +16 -88
  83. data/lib/hecks/bluebook/dsl/port_builder.rb +2 -6
  84. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +22 -96
  85. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +31 -184
  86. data/lib/hecks/bluebook/dsl/query_builder.rb +11 -42
  87. data/lib/hecks/bluebook/dsl/read_model_builder.rb +75 -201
  88. data/lib/hecks/bluebook/dsl/rule_reference.rb +22 -163
  89. data/lib/hecks/bluebook/dsl/translation_builder.rb +28 -114
  90. data/lib/hecks/bluebook/dsl/value_object_builder.rb +10 -124
  91. data/lib/hecks/bluebook/dsl/word_gate.rb +16 -162
  92. data/lib/hecks/bluebook/dsl/world_builder.rb +19 -111
  93. data/lib/hecks/bluebook/entity.rb +13 -71
  94. data/lib/hecks/bluebook/expression/ast_json.rb +24 -190
  95. data/lib/hecks/bluebook/expression/ast_reader.rb +12 -54
  96. data/lib/hecks/bluebook/expression/canonical_form.rb +11 -88
  97. data/lib/hecks/bluebook/expression/evaluator.rb +47 -295
  98. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +30 -147
  99. data/lib/hecks/bluebook/expression/resolver.rb +37 -660
  100. data/lib/hecks/bluebook/hexagon.rb +5 -28
  101. data/lib/hecks/bluebook/lifecycle.rb +5 -16
  102. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -7
  103. data/lib/hecks/bluebook/meta_validator/judge.rb +111 -361
  104. data/lib/hecks/bluebook/meta_validator/plan.rb +33 -162
  105. data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -7
  106. data/lib/hecks/bluebook/meta_validator/readings.rb +56 -342
  107. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +24 -176
  108. data/lib/hecks/bluebook/meta_validator/shapes.rb +53 -267
  109. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +43 -305
  110. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +7 -43
  111. data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -18
  112. data/lib/hecks/bluebook/meta_validator.rb +79 -349
  113. data/lib/hecks/bluebook/model_check/client_profile.rb +6 -21
  114. data/lib/hecks/bluebook/model_check.rb +100 -597
  115. data/lib/hecks/bluebook/pattern_subset.rb +11 -69
  116. data/lib/hecks/bluebook/process_manager.rb +13 -83
  117. data/lib/hecks/bluebook/project_loader.rb +3 -39
  118. data/lib/hecks/bluebook/project_register.rb +12 -60
  119. data/lib/hecks/bluebook/query.rb +9 -48
  120. data/lib/hecks/bluebook/read_model.rb +57 -81
  121. data/lib/hecks/bluebook/reference.rb +7 -41
  122. data/lib/hecks/bluebook/smoke_test.rb +8 -98
  123. data/lib/hecks/bluebook/synthesizer.rb +13 -62
  124. data/lib/hecks/bluebook/translation.rb +26 -62
  125. data/lib/hecks/bluebook/value_object.rb +11 -36
  126. data/lib/hecks/bluebook.rb +5 -22
  127. data/lib/hecks/cache_dir.rb +4 -23
  128. data/lib/hecks/cli/document.rb +3 -21
  129. data/lib/hecks/cli/ir.rb +3 -5
  130. data/lib/hecks/cli/mcp.rb +2 -3
  131. data/lib/hecks/cli/mcp_door.rb +19 -114
  132. data/lib/hecks/cli/model_check.rb +18 -85
  133. data/lib/hecks/cli/project_cli.rb +7 -40
  134. data/lib/hecks/cli/project_diagrams.rb +0 -7
  135. data/lib/hecks/cli/run.rb +12 -58
  136. data/lib/hecks/cli/smoke_test.rb +2 -16
  137. data/lib/hecks/cli/stores.rb +1 -2
  138. data/lib/hecks/cli.rb +18 -24
  139. data/lib/hecks/construct.rb +8 -34
  140. data/lib/hecks/deploy/bluebook/deploy.bluebook +33 -142
  141. data/lib/hecks/deploy/bluebook/deploy.hecksagon +2 -9
  142. data/lib/hecks/embryonaut_bluebook/lock.rb +3 -35
  143. data/lib/hecks/embryonaut_bluebook/shape.rb +1 -17
  144. data/lib/hecks/embryonaut_bluebook/vendor.rb +8 -53
  145. data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +4 -19
  146. data/lib/hecks/embryonaut_bluebook.rb +4 -71
  147. data/lib/hecks/facade/cli_door.rb +24 -106
  148. data/lib/hecks/facade/cli_runner.rb +28 -172
  149. data/lib/hecks/facade/command_request.rb +13 -33
  150. data/lib/hecks/facade/handle.rb +34 -121
  151. data/lib/hecks/facade/json_door.rb +31 -146
  152. data/lib/hecks/facade/surface/aggregate_door.rb +39 -104
  153. data/lib/hecks/facade/surface/chapter.rb +10 -54
  154. data/lib/hecks/facade/surface.rb +8 -23
  155. data/lib/hecks/facade.rb +10 -25
  156. data/lib/hecks/forms/app.rb +15 -70
  157. data/lib/hecks/forms/command_form_renderer.rb +10 -29
  158. data/lib/hecks/forms/field_renderer.rb +17 -156
  159. data/lib/hecks/forms/field_shape.rb +30 -222
  160. data/lib/hecks/forms/html.rb +12 -47
  161. data/lib/hecks/forms/index_renderer.rb +1 -4
  162. data/lib/hecks/forms/page.rb +3 -9
  163. data/lib/hecks/forms/params.rb +14 -60
  164. data/lib/hecks/forms/port_argument.rb +2 -19
  165. data/lib/hecks/forms/query_form_renderer.rb +4 -79
  166. data/lib/hecks/forms/record_renderer.rb +8 -75
  167. data/lib/hecks/forms/record_table.rb +7 -50
  168. data/lib/hecks/forms/reference_options.rb +3 -30
  169. data/lib/hecks/forms/value_object_shape.rb +8 -35
  170. data/lib/hecks/forms.rb +5 -33
  171. data/lib/hecks/fqn.rb +5 -12
  172. data/lib/hecks/framework/bluebook/compliance.bluebook +16 -65
  173. data/lib/hecks/framework/bluebook/console_settings.bluebook +47 -134
  174. data/lib/hecks/framework/bluebook/framework.hecksagon +5 -14
  175. data/lib/hecks/framework/bluebook/governance.bluebook +13 -34
  176. data/lib/hecks/framework/bluebook/identity.bluebook +11 -26
  177. data/lib/hecks/framework/bluebook/privacy.bluebook +8 -33
  178. data/lib/hecks/framework.rb +11 -80
  179. data/lib/hecks/freezer.rb +5 -39
  180. data/lib/hecks/grammar/expression.bluebook +15 -40
  181. data/lib/hecks/grammar/translation.bluebook +3 -6
  182. data/lib/hecks/grammar.rb +16 -41
  183. data/lib/hecks/ir.rb +14 -83
  184. data/lib/hecks/language/adapter.bluebook +2 -7
  185. data/lib/hecks/language/bluebook/aggregate.bluebook +56 -221
  186. data/lib/hecks/language/bluebook/attaches/paging.bluebook +6 -15
  187. data/lib/hecks/language/bluebook/bluebook.bluebook +15 -83
  188. data/lib/hecks/language/bluebook/bluebook.hecksagon +3 -15
  189. data/lib/hecks/language/bluebook/command.bluebook +27 -144
  190. data/lib/hecks/language/bluebook/entity.bluebook +24 -97
  191. data/lib/hecks/language/bluebook/policy.bluebook +16 -67
  192. data/lib/hecks/language/bluebook/process_manager.bluebook +28 -151
  193. data/lib/hecks/language/bluebook/projection.bluebook +126 -60
  194. data/lib/hecks/language/bluebook/query.bluebook +12 -43
  195. data/lib/hecks/language/bluebook/shape.bluebook +23 -73
  196. data/lib/hecks/language/bluebook/syntax.bluebook +51 -262
  197. data/lib/hecks/language/bluebook/vocabulary.bluebook +82 -197
  198. data/lib/hecks/language/hecksagon/adapter_binding.bluebook +3 -10
  199. data/lib/hecks/language/hecksagon/domain_port.bluebook +4 -19
  200. data/lib/hecks/language/hecksagon/hecksagon.bluebook +6 -25
  201. data/lib/hecks/language/hecksagon/port_operation.bluebook +3 -14
  202. data/lib/hecks/language/oidc.json +35 -0
  203. data/lib/hecks/language/port.bluebook +4 -17
  204. data/lib/hecks/language/translation/translation.bluebook +3 -9
  205. data/lib/hecks/language/translation/translation_aggregate.bluebook +5 -24
  206. data/lib/hecks/language/world/wiring.bluebook +1 -3
  207. data/lib/hecks/language/world/world.bluebook +5 -7
  208. data/lib/hecks/literal.rb +8 -50
  209. data/lib/hecks/mcp_door_scope.rb +9 -90
  210. data/lib/hecks/mcp_stdio_guard.rb +4 -41
  211. data/lib/hecks/naming.rb +29 -137
  212. data/lib/hecks/ports/access_control.rb +7 -23
  213. data/lib/hecks/ports/agent/answers.rb +5 -23
  214. data/lib/hecks/ports/agent.rb +47 -172
  215. data/lib/hecks/ports/authentication.rb +12 -39
  216. data/lib/hecks/ports/authorization.rb +16 -43
  217. data/lib/hecks/ports/clock.rb +5 -45
  218. data/lib/hecks/ports/extraction.rb +7 -12
  219. data/lib/hecks/ports/identity_assignment.rb +8 -29
  220. data/lib/hecks/ports/identity_generation.rb +6 -29
  221. data/lib/hecks/ports/identity_resolution.rb +2 -10
  222. data/lib/hecks/ports/key_vault.rb +9 -45
  223. data/lib/hecks/ports/persistence/append_only.rb +61 -64
  224. data/lib/hecks/ports/persistence/binding_policy.rb +4 -14
  225. data/lib/hecks/ports/persistence/codec_boundary.rb +20 -80
  226. data/lib/hecks/ports/persistence/null_saga_store.rb +4 -20
  227. data/lib/hecks/ports/persistence/plugin.rb +13 -42
  228. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +38 -141
  229. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +15 -115
  230. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +16 -84
  231. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +6 -25
  232. data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +3 -22
  233. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +55 -181
  234. data/lib/hecks/ports/persistence/plugins/era/postgres_era/events.rb +77 -0
  235. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +12 -122
  236. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +28 -128
  237. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +73 -474
  238. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +31 -124
  239. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +45 -219
  240. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +13 -108
  241. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +18 -75
  242. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +9 -49
  243. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +6 -123
  244. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +23 -55
  245. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +14 -72
  246. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +7 -16
  247. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +14 -115
  248. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +13 -45
  249. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +127 -554
  250. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +13 -63
  251. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +2 -17
  252. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +3 -14
  253. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +13 -58
  254. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -17
  255. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +9 -26
  256. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +9 -25
  257. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +18 -124
  258. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +34 -123
  259. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +5 -12
  260. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +3 -9
  261. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -14
  262. data/lib/hecks/ports/persistence/plugins/era.rb +4 -23
  263. data/lib/hecks/ports/persistence/postgres_dump/connection.rb +7 -23
  264. data/lib/hecks/ports/persistence/postgres_dump.rb +4 -35
  265. data/lib/hecks/ports/persistence/remote_runtime.rb +2 -24
  266. data/lib/hecks/ports/persistence/repository_factory.rb +8 -27
  267. data/lib/hecks/ports/persistence/state_codec.rb +23 -103
  268. data/lib/hecks/ports/persistence.rb +7 -14
  269. data/lib/hecks/ports/projection.rb +42 -84
  270. data/lib/hecks/ports/query/in_memory.rb +10 -58
  271. data/lib/hecks/ports/query/ordering.rb +11 -32
  272. data/lib/hecks/ports/query.rb +14 -37
  273. data/lib/hecks/ports.rb +2 -8
  274. data/lib/hecks/projections/bootstrap_table.rb +11 -43
  275. data/lib/hecks/projections/deploy/fargate/assembly.rb +2 -15
  276. data/lib/hecks/projections/deploy/fargate/cdn.rb +1 -48
  277. data/lib/hecks/projections/deploy/fargate/cdn_default.rb +2 -11
  278. data/lib/hecks/projections/deploy/fargate/check.rb +5 -14
  279. data/lib/hecks/projections/deploy/fargate/containers.rb +3 -78
  280. data/lib/hecks/projections/deploy/fargate/extras.rb +3 -44
  281. data/lib/hecks/projections/deploy/fargate/monitoring.rb +0 -44
  282. data/lib/hecks/projections/deploy/fargate/settings.rb +2 -54
  283. data/lib/hecks/projections/deploy/fargate/yaml.rb +13 -58
  284. data/lib/hecks/projections/deploy/fargate.rb +35 -202
  285. data/lib/hecks/projections/deploy/lambda.rb +150 -678
  286. data/lib/hecks/projections/deploy/preview/access_yaml.rb +1 -33
  287. data/lib/hecks/projections/deploy/preview/containers.rb +6 -111
  288. data/lib/hecks/projections/deploy/preview/routing_yaml.rb +4 -56
  289. data/lib/hecks/projections/deploy/preview/script.rb +2 -14
  290. data/lib/hecks/projections/deploy/preview/settings.rb +3 -33
  291. data/lib/hecks/projections/deploy/preview/task_yaml.rb +2 -38
  292. data/lib/hecks/projections/deploy/preview/template.rb +2 -58
  293. data/lib/hecks/projections/deploy/preview.rb +2 -67
  294. data/lib/hecks/projections/deploy/scripts/settings.rb +3 -25
  295. data/lib/hecks/projections/deploy/scripts.rb +4 -57
  296. data/lib/hecks/projections/deploy/shared.rb +47 -146
  297. data/lib/hecks/projections/deploy/smoke.rb +4 -61
  298. data/lib/hecks/projections/deploy/template_diff/comparer.rb +3 -25
  299. data/lib/hecks/projections/deploy/template_diff/loader.rb +2 -11
  300. data/lib/hecks/projections/deploy/template_diff.rb +5 -57
  301. data/lib/hecks/projections/diagrams.rb +20 -582
  302. data/lib/hecks/projections/glossary/html.rb +14 -130
  303. data/lib/hecks/projections/glossary/markdown.rb +8 -69
  304. data/lib/hecks/projections/glossary/mermaid.rb +9 -66
  305. data/lib/hecks/projections/glossary/sensitivity.rb +5 -34
  306. data/lib/hecks/projections/glossary/sentences.rb +14 -159
  307. data/lib/hecks/projections/glossary.rb +30 -222
  308. data/lib/hecks/projections/ir.rb +3 -10
  309. data/lib/hecks/projections/model/deviations.rb +15 -42
  310. data/lib/hecks/projections/model.rb +12 -51
  311. data/lib/hecks/projections/oidc.rb +12 -56
  312. data/lib/hecks/projections/parser_table.rb +9 -28
  313. data/lib/hecks/projections/reference.rb +2 -15
  314. data/lib/hecks/projections/rust_vocabulary.rb +12 -250
  315. data/lib/hecks/projections/shape.rb +3 -29
  316. data/lib/hecks/projections/statements.rb +7 -86
  317. data/lib/hecks/projections/vocabulary.rb +6 -22
  318. data/lib/hecks/projections.rb +2 -16
  319. data/lib/hecks/projector/cli_projector.rb +60 -375
  320. data/lib/hecks/projector/docs_projector.rb +10 -201
  321. data/lib/hecks/projector/exporter.rb +42 -134
  322. data/lib/hecks/projector/ir_projector.rb +2 -9
  323. data/lib/hecks/projector/narrate_projector.rb +15 -189
  324. data/lib/hecks/projector/target.rb +13 -76
  325. data/lib/hecks/projector.rb +30 -121
  326. data/lib/hecks/query_specification/common/comparators.rb +5 -26
  327. data/lib/hecks/query_specification/common/comparison.rb +35 -143
  328. data/lib/hecks/query_specification/common/dsl.rb +8 -24
  329. data/lib/hecks/query_specification/common/null_policy.rb +20 -70
  330. data/lib/hecks/query_specification/common/options.rb +2 -6
  331. data/lib/hecks/query_specification/common/where_clause.rb +2 -10
  332. data/lib/hecks/query_specification/field_path.rb +37 -77
  333. data/lib/hecks/query_specification/hop_path.rb +12 -76
  334. data/lib/hecks/release/gem_pin.rb +2 -26
  335. data/lib/hecks/release/runner/ci_publisher.rb +3 -6
  336. data/lib/hecks/release/runner/commands.rb +2 -4
  337. data/lib/hecks/release/runner/gem_publisher.rb +2 -3
  338. data/lib/hecks/release/runner/npm_publisher.rb +6 -20
  339. data/lib/hecks/release/runner/preflight.rb +4 -19
  340. data/lib/hecks/release/runner/tagger.rb +2 -9
  341. data/lib/hecks/release/runner.rb +3 -32
  342. data/lib/hecks/rendering.rb +4 -26
  343. data/lib/hecks/router/namespace_installer.rb +12 -36
  344. data/lib/hecks/router.rb +4 -8
  345. data/lib/hecks/runtime/aggregate_lock.rb +8 -33
  346. data/lib/hecks/runtime/boot_gates.rb +3 -18
  347. data/lib/hecks/runtime/caller.rb +19 -59
  348. data/lib/hecks/runtime/capability_graph.rb +5 -21
  349. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +16 -78
  350. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +23 -160
  351. data/lib/hecks/runtime/command_interpreter.rb +124 -306
  352. data/lib/hecks/runtime/command_rules/admissibility.rb +95 -366
  353. data/lib/hecks/runtime/command_rules/arithmetic.rb +66 -196
  354. data/lib/hecks/runtime/command_rules/authorization.rb +13 -45
  355. data/lib/hecks/runtime/command_rules/emission.rb +8 -21
  356. data/lib/hecks/runtime/command_rules/references.rb +23 -202
  357. data/lib/hecks/runtime/dependency_planning.rb +30 -102
  358. data/lib/hecks/runtime/dispatcher.rb +82 -241
  359. data/lib/hecks/runtime/entity_element.rb +40 -462
  360. data/lib/hecks/runtime/entity_interpreter.rb +64 -219
  361. data/lib/hecks/runtime/errors.rb +32 -115
  362. data/lib/hecks/runtime/event.rb +3 -25
  363. data/lib/hecks/runtime/identity.rb +18 -154
  364. data/lib/hecks/runtime/instance.rb +18 -150
  365. data/lib/hecks/runtime/interpreting.rb +11 -70
  366. data/lib/hecks/runtime/invocation.rb +28 -164
  367. data/lib/hecks/runtime/loader.rb +48 -186
  368. data/lib/hecks/runtime/outbox.rb +38 -261
  369. data/lib/hecks/runtime/policy_interpreter.rb +38 -200
  370. data/lib/hecks/runtime/port_operation_interpreter.rb +26 -87
  371. data/lib/hecks/runtime/query_interpreter.rb +45 -187
  372. data/lib/hecks/runtime/reaction_invocation.rb +36 -138
  373. data/lib/hecks/runtime/read_model_interpreter.rb +117 -208
  374. data/lib/hecks/runtime/rebuild_sweep.rb +8 -54
  375. data/lib/hecks/runtime/reference_hop.rb +8 -81
  376. data/lib/hecks/runtime/refusal_wording.rb +8 -89
  377. data/lib/hecks/runtime/registry/saga_persistence.rb +10 -102
  378. data/lib/hecks/runtime/registry/verification.rb +52 -225
  379. data/lib/hecks/runtime/registry/world_defaults.rb +4 -53
  380. data/lib/hecks/runtime/registry.rb +99 -244
  381. data/lib/hecks/runtime/remote_dispatcher.rb +37 -152
  382. data/lib/hecks/runtime/routing.rb +4 -15
  383. data/lib/hecks/runtime/saga_interpreter/correlation.rb +14 -61
  384. data/lib/hecks/runtime/saga_interpreter.rb +79 -263
  385. data/lib/hecks/runtime/saga_pending_dispatch.rb +2 -43
  386. data/lib/hecks/runtime/tenant_check.rb +6 -68
  387. data/lib/hecks/runtime/tenant_scope.rb +3 -28
  388. data/lib/hecks/runtime/value/admission.rb +16 -67
  389. data/lib/hecks/runtime/value/coercion.rb +155 -641
  390. data/lib/hecks/runtime/value/entity_list_coercion.rb +26 -219
  391. data/lib/hecks/runtime/value.rb +12 -84
  392. data/lib/hecks/runtime.rb +17 -65
  393. data/lib/hecks/storehouse.rb +141 -568
  394. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +8 -32
  395. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +6 -23
  396. data/lib/hecks/vendoring/git_environment.rb +2 -8
  397. data/lib/hecks/vendoring/git_source.rb +4 -10
  398. data/lib/hecks/vendoring.rb +7 -46
  399. data/lib/hecks/version.rb +3 -14
  400. data/lib/hecks.rb +26 -81
  401. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -46
  402. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +11 -80
  403. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +10 -99
  404. metadata +3 -2
@@ -1,31 +1,13 @@
1
1
  module Hecks
2
2
  module Bluebook
3
3
  class Assembly
4
- # The leaf shapes an assembly reads, and the encodings it undoes.
5
- #
6
- # `to_h` spells things as text so the export stands on its own, and every
7
- # one of those spellings has to come back apart here. This is the same family
8
- # of work `MetaValidator::Shapes` does for the reconstruction — the difference
9
- # is that Shapes rebuilds hashes and this rebuilds objects, so it has to
10
- # recover types rather than just strings.
11
- #
12
- # Encoding losses are the largest family of bug in this codebase, and every
13
- # member has the same shape: reading an object where `to_h` holds a spelling.
14
- # So each method below names the spelling it inverts.
4
+ # Reads the leaf shapes of an assembly's `to_h` export back into IR objects.
5
+ # Each method inverts one textual spelling, so types are recovered, not just strings.
15
6
  module Marks
16
7
  module_function
17
8
 
18
9
  # `Attribute#to_h` spells a type with `to_s`, so a reference arrives as
19
- # "Reference<Customer>" and has to become an edge again. Everything else is
20
- # a name and stays one.
21
- #
22
- # @param field [Hash{Symbol => Object}] one attribute's declared row, keyed
23
- # `:type` (String, a plain type name or `"Reference<Target>"`), `:name`
24
- # (String), `:list` and `:optional` (read as truthy), `:default` (Object,
25
- # nil), `:pattern` (String, nil), `:admits` (String, nil) and
26
- # `:relationship` (String, nil), each the raw declared value
27
- # @return [Bluebook::Attribute] the attribute, with `type` resolved to a
28
- # `Bluebook::Reference` when the row spelled one
10
+ # "Reference<Customer>" and has to become an edge again.
29
11
  def attribute(field)
30
12
  type = field[:type].to_s
31
13
  target = type[/\AReference<(.+)>\z/, 1]
@@ -35,89 +17,39 @@ module Hecks
35
17
  type: target ? Reference.new(target) : type,
36
18
  list: field[:list] ? true : false,
37
19
  default: field[:default],
38
- # The last place optionality can be dropped, and the one that was
39
- # dropping it. Every bluebook in the registry is the round-trip
40
- # product — MetaValidator dispatches the declaration in and reads it
41
- # back — so a fact this constructor does not carry is a fact the
42
- # language cannot state about itself, however plainly the source
43
- # wrote it.
20
+ # Every registry bluebook is a round-trip product, so a fact dropped
21
+ # here is a fact the language cannot state about itself.
44
22
  optional: field[:optional] ? true : false,
45
23
  pattern: field[:pattern],
46
- # **The same lesson, one fact later**. `admits` is not on `to_h` — the
47
- # wire does not carry it, on purpose — but it must still survive the
48
- # round trip, because the grammar registry keeps the assembled graph
49
- # and downstream projections read the link off that. Dropped here, the
50
- # language could not say `admits` about itself no matter how plainly
51
- # the source wrote it, which is word for word what the note above
52
- # already learned about `optional`.
24
+ # Not on `to_h`, but the grammar registry keeps the assembled graph and
25
+ # downstream projections read the link off it, so it must survive.
53
26
  admits: field[:admits],
54
27
  relationship: field[:relationship]
55
28
  )
56
29
  end
57
30
 
58
- # The same shape. Two names because a head's field and a verb's argument are
59
- # different things in the language even though the IR keeps one class for
60
- # both — `Aggregate.Attribute` and `Command.Argument` are separate verbs.
61
- #
62
- # @param field [Hash{Symbol => Object}] see `attribute`
63
- # @return [Bluebook::Attribute] see `attribute`
31
+ # A head's field and a verb's argument are separate verbs sharing one IR class.
64
32
  def shape_field(field) = attribute(field)
65
33
 
66
- # One part of an identity. It goes in as a row so the language can hold an
67
- # ordered list of them, and comes back out as the path it always was —
68
- # a String, because `identity_paths` splits paths and never symbols.
69
- #
70
- # @param part [Hash{Symbol => Object}] one identity-path row, keyed `:value`
71
- # (Object, read via `to_s`)
72
- # @return [String] the path segment
34
+ # One identity part, as the String path `identity_paths` splits.
73
35
  def identity_path(part) = part[:value].to_s
74
36
 
75
- # A member's fields — an open map, which is why Member is its own root in
76
- # the language and why the pairs arrive as a list rather than a value object.
77
- #
78
- # The values are unmarked, because `ValueObject#to_h` spells them with `to_s`
79
- # and the language stores them as text: `member code: "JPY", minor_units: 0`
80
- # came back with a minor_units of "0", and a closed set that admits the string
81
- # would refuse the number the caller passes.
82
- #
83
- # @param pairs [Array<Array(String, Object)>] one member's own field pairs,
84
- # each `[field_name, raw_value]` as declared
85
- # @return [Hash{Symbol => Object}] the member's fields, keyed by field name,
86
- # each value read back to its native type through `unmark_scalar`
37
+ # A member's fields, an open map, so the pairs arrive as a list.
38
+ # Values are unmarked because `ValueObject#to_h` spells them with `to_s`.
87
39
  def member(pairs)
88
40
  pairs.to_h { |key, value| [key.to_sym, unmark_scalar(value)] }
89
41
  end
90
42
 
91
- # A read model's gathered head. The keys must be symbols whichever way the
92
- # declaration arrived, and `as` must be one too: it names the reader the
93
- # projection answers to, and `ReadModel#to_h` spells it `to_s`.
94
- #
95
- # @param row [Hash{Symbol => Object}] one aggregate-head row, keyed
96
- # `:aggregate` (String), `:as` (String, the reader name) and `:many`
97
- # (Boolean)
98
- # @return [Hash{Symbol => Object}] the head, with every key a Symbol and
99
- # `:as` read back to a Symbol
43
+ # A read model's gathered head; `as` is a Symbol because `ReadModel#to_h` spells it `to_s`.
100
44
  def head(row)
101
45
  row.to_h { |key, value| [key.to_sym, key.to_sym == :as ? value.to_sym : value] }
102
46
  end
103
47
 
104
- # A group_by field's own name — the builder's native shape is already
105
- # `{field: :symbol}`, so this matches it rather than leaving `field`
106
- # as the String `Shapes#group_by_field` reads back.
107
- #
108
- # @param row [Hash{Symbol => String}] one group-by row, keyed `:field`
109
- # @return [Hash{field: Symbol}] the row with `:field` read back to a Symbol
48
+ # Matches the builder's native `{field: :symbol}` shape.
110
49
  def group_by_field(row) = { field: row[:field].to_sym }
111
50
 
112
- # A scalar that was written as itself rather than inspected — a member's
113
- # value, where the language holds text and the type has to be read back from
114
- # the shape of it. Unlike `read`, a bare word stays a String here, because
115
- # a closed set admits words far more often than symbols.
116
- #
117
- # @param value [Object] the raw declared scalar, read via `to_s`
118
- # @return [true, false, Integer, Float, String] `true`/`false` for those
119
- # literal words, a number when the text matches a bare integer or decimal
120
- # pattern, or the string itself otherwise
51
+ # Reads a member's scalar back from text. Unlike `read`, a bare word stays a
52
+ # String, because a closed set admits words far more often than symbols.
121
53
  def unmark_scalar(value)
122
54
  text = value.to_s
123
55
  return true if text == "true"
@@ -128,74 +60,34 @@ module Hecks
128
60
  text
129
61
  end
130
62
 
131
- # A saga's argument bindings. Each value rides Literal's spelling, which
132
- # marks a Symbol with a leading colon — lose it and an argument reads as a
133
- # string of the same name.
134
- #
135
- # @param with [Array<Array(String, String)>, nil] the declared bindings,
136
- # each `[argument_name, literal_spelling]` pair, or `nil` for none
137
- # @return [Hash{Symbol => Object}] the bindings, keyed by argument name,
138
- # each value read back through `Literal.read`
63
+ # A saga's argument bindings; Literal marks a Symbol with a leading colon.
139
64
  def bindings(with) = Array(with).to_h { |key, value| [key.to_sym, read(value)] }
140
65
 
141
66
  # Builds one aggregate or value object invariant from its declared row.
142
- #
143
- # @param rule [Hash{Symbol => String}] one invariant's declared row, keyed
144
- # `:description` and `:canonical` (the canonical-form rendering of the
145
- # rule's expression)
146
- # @return [Bluebook::Invariant] the invariant, with `predicate`/`ast` left
147
- # `nil` — `Expression::Evaluator` parses `canonical` on demand when
148
- # neither is set
67
+ # `Expression::Evaluator` parses `canonical` on demand.
149
68
  def invariant(rule)
150
69
  Invariant.new(description: rule[:description], canonical: rule[:canonical])
151
70
  end
152
71
 
153
72
  # Builds one command precondition from its declared row.
154
- #
155
- # @param rule [Hash{Symbol => String}] one given's declared row, keyed
156
- # `:description` and `:canonical` (the canonical-form rendering of the
157
- # rule's expression)
158
- # @return [Bluebook::Given] the given, with `predicate`/`ast` left `nil` —
159
- # `Expression::Evaluator` parses `canonical` on demand when neither is set
160
73
  def given(rule)
161
74
  Given.new(description: rule[:description], canonical: rule[:canonical])
162
75
  end
163
76
 
164
- # S12, ADR 0025 — `projects :name, from: :"reference.remote_field"`.
165
- # All three fields are identifiers, unlike Invariant/Given's own
166
- # free text, so — like `attribute`'s own `name`/`type` below —
167
- # they come back as Symbols.
168
- #
169
- # @param row [Hash{Symbol => String}] one projected-field's declared row,
170
- # keyed `:name`, `:reference` and `:remote_field` (each an identifier)
171
- # @return [Bluebook::ProjectedField] the projected field, with each
172
- # identifier read back to a Symbol
77
+ # All three fields are identifiers, unlike Invariant/Given's free text, so they
78
+ # come back as Symbols.
173
79
  def projected_field(row)
174
80
  ProjectedField.new(name: row[:name].to_sym, reference: row[:reference].to_sym,
175
81
  remote_field: row[:remote_field].to_sym)
176
82
  end
177
83
 
178
84
  # `Mutation#to_h` branches on the operation, so this does too.
179
- #
180
- # An append binds several fields at once, each either an argument (a
181
- # Symbol, wearing its colon) or a literal — the distinction that is the
182
- # whole reason `append: { direction: "out" }` was once indistinguishable
183
- # from an argument named `out`.
184
- #
185
- # @param change [Hash{Symbol => Object}] one mutation's declared row, keyed
186
- # `:target` (String), `:op` (String, such as `"set"`, `"append"`,
187
- # `"delegate"` or `"corrects"`), `:fields` (Array, for the append-shaped
188
- # ops) and `:source` (Hash, for any other op)
189
- # @return [Bluebook::Mutation] the mutation, with `source` a
190
- # `Hash{Symbol => Object}` of field bindings for an append-shaped op, or
191
- # `classified`'s own result for any other
85
+ # An append binds several fields, each an argument (a Symbol) or a literal.
192
86
  def mutation(change)
193
87
  target = change[:target].to_sym
194
88
  op = change[:op].to_sym
195
89
 
196
- # `:delegate`/`:corrects` (CommandBuilder#delegates_to's and
197
- # #corrects_impl's own comments) ride the same multi-binding
198
- # shape `:append` does.
90
+ # `:delegate` and `:corrects` ride the same multi-binding shape as `:append`.
199
91
  return Mutation.new(target: target, op: op, source: appended(change[:fields])) if [:append, :delegate,
200
92
  :corrects].include?(op)
201
93
 
@@ -203,26 +95,11 @@ module Hecks
203
95
  end
204
96
 
205
97
  # Reads an append mutation's field bindings back off their declared rows.
206
- #
207
- # @param fields [Array<Array(String, String)>, nil] the append's declared
208
- # field bindings, each `[field_name, literal_spelling]` pair, or `nil`
209
- # for none
210
- # @return [Hash{Symbol => Object}] the bindings, keyed by field name, each
211
- # value read back through `Literal.read`
212
98
  def appended(fields)
213
99
  Array(fields).to_h { |field, source| [field.to_sym, read(source)] }
214
100
  end
215
101
 
216
- # A set reads one thing, and `classified_source` said which: an argument by
217
- # name, or a literal by value.
218
- #
219
- # @param source [Hash{Symbol => Object}, nil] the mutation's declared
220
- # source, keyed `:kind` (String, `"argument"`, `"state"` or a literal
221
- # kind) plus `:name` (String, for `"argument"`/`"state"`) or `:value`
222
- # (Object, for a literal), or `nil` for no source
223
- # @return [Symbol, Hecks::StateRef, Object, nil] the command argument name,
224
- # a state self-reference, the literal value, or `nil` when `source` is
225
- # `nil`
102
+ # A set reads one thing: an argument by name, or a literal by value.
226
103
  def classified(source)
227
104
  return nil if source.nil?
228
105
 
@@ -233,44 +110,13 @@ module Hecks
233
110
  end
234
111
  end
235
112
 
236
- # Every literal field on the wire, read back — one spelling, one reader.
237
- #
238
- # A where-clause value, a saga's argument bindings, an append binding, a
239
- # limit: all of them ride Literal's self-describing form, so all of them
240
- # come back through this one reader, never a second one that might
241
- # disagree about a quoted string or a number.
242
- #
243
- # An object literal is the one that bites hardest. A saga leg binds
244
- # `narrative: { text: "transfer out" }` — a value object's fields written
245
- # inline — and reading it as a plain string rather than through `Literal`
246
- # would reach the runtime as `"{:text=>\"transfer out\"}"`, which coercion
247
- # refuses: the debit leg is never delivered, and the whole settlement wire
248
- # stops — banking emits TransferRequested five times and TransferDebited
249
- # never. A whole-history replay gate is what catches this, because a saga
250
- # that silently does nothing looks exactly like a saga with nothing to do.
251
- #
252
- # @param value [String, Object] wire spelling produced by `Literal.render`,
253
- # or a bare word
254
- # @return [nil, true, false, Integer, Float, Symbol, Hecks::StateRef,
255
- # String, Hash, Array] the value read back — see `Literal.read`
113
+ # Reads every literal field on the wire through `Literal`, so no second reader
114
+ # can disagree about quoting. An object literal read as a plain string reaches
115
+ # the runtime as inspect text, which coercion refuses, and the saga leg stalls.
256
116
  def read(value) = Literal.read(value)
257
117
 
258
- # `target:` (ADR 0055) — read straight off the wire, unconverted:
259
- # it's already the bare aggregate-name string `WhereClause#to_h`/
260
- # `OrderBy#to_h`/`LimitSpec#to_h` wrote (`resolve_target`'s own
261
- # `Naming.demodulise` already ran once, at DSL-build time; this is
262
- # the replay path every real boot actually goes through, reading
263
- # that same wire shape back — see this class's own header). Absent
264
- # from `clause`/`declared` entirely on older wire data that never
265
- # declared `on:` — `clause[:target]`/`declared[:target]` reads
266
- # `nil` for a missing key exactly like an explicit `nil` would,
267
- # so this is additive, not a migration.
268
- #
269
- # @param clause [Hash{Symbol => Object}] one where-clause's declared row,
270
- # keyed `:field` (String), `:op` (String, a comparator name), `:value`
271
- # (String, Literal's spelling) and `:target` (String, `nil` when absent)
272
- # @return [QuerySpecification::Common::WhereClause] the clause, with `op`
273
- # read to a Symbol and `value` read back through `Literal.read`
118
+ # `target:` is read straight off the wire, unconverted. A missing key reads
119
+ # `nil` like an explicit `nil`, so older wire data needs no migration.
274
120
  def where_clause(clause)
275
121
  QuerySpecification::Common::WhereClause.new(
276
122
  field: clause[:field], op: clause[:op].to_sym, value: read(clause[:value]), target: clause[:target]
@@ -278,12 +124,6 @@ module Hecks
278
124
  end
279
125
 
280
126
  # Builds a query's declared ordering, if it declares one.
281
- #
282
- # @param declared [Hash{Symbol => Object}, nil] the declared order-by row,
283
- # keyed `:field` (String), `:direction` (String) and `:target` (String,
284
- # `nil` when absent), or `nil` when the construct declares no ordering
285
- # @return [QuerySpecification::Common::OrderBy, nil] the order-by, with
286
- # `direction` read to a Symbol, or `nil` when `declared` is `nil`
287
127
  def order_by(declared)
288
128
  return nil unless declared
289
129
 
@@ -293,25 +133,14 @@ module Hecks
293
133
  end
294
134
 
295
135
  # Builds a query's declared limit, if it declares one.
296
- #
297
- # @param declared [Hash{Symbol => Object}, nil] the declared limit row,
298
- # keyed `:value` (String, Literal's spelling) and `:target` (String,
299
- # `nil` when absent), or `nil` when the construct declares no limit
300
- # @return [QuerySpecification::Common::LimitSpec, nil] the limit, with
301
- # `value` read back through `Literal.read`, or `nil` when `declared` is
302
- # `nil`
303
136
  def limit(declared)
304
137
  return nil unless declared
305
138
 
306
139
  QuerySpecification::Common::LimitSpec.new(value: read(declared[:value]), target: declared[:target])
307
140
  end
308
141
 
309
- # Every other specification option, from one table.
310
- #
311
- # Each entry names the struct and which of its members carry a value that
312
- # rode Literal's spelling rather than plain text. A
313
- # ninth option is one row here and nothing else — the language already holds
314
- # it, because it holds options as an open map rather than a field each.
142
+ # Every other specification option, from one table: the struct, and which of
143
+ # its members carry a value that rode Literal's spelling.
315
144
  OPTIONS = {
316
145
  offset: [QuerySpecification::Common::OffsetSpec, %i[value]],
317
146
  cursor: [QuerySpecification::Common::CursorSpec, %i[value]],
@@ -320,24 +149,11 @@ module Hecks
320
149
  inspection: [QuerySpecification::Common::InspectionSpec, []]
321
150
  }.freeze
322
151
 
323
- # `mode` and `policy` are read as symbols because the DSL declares them that
324
- # way — `nulls :last`, `authorize :customer_access` — and `to_h` spells them
325
- # with `to_s`, so the colon is not there to strip.
152
+ # The DSL declares these as symbols (`nulls :last`) and `to_h` spells them with
153
+ # `to_s`, so they are converted back.
326
154
  SYMBOLIC = %i[mode policy tenant].freeze
327
155
 
328
156
  # Builds one query specification option from its declared row and `OPTIONS`.
329
- #
330
- # @param name [Symbol] the option's name, a key of `OPTIONS` (`:offset`,
331
- # `:cursor`, `:null_semantics`, `:authorization` or `:inspection`)
332
- # @param declared [Hash{Symbol => Object}, nil] the option's declared row,
333
- # or `nil` when the construct does not declare this option
334
- # @return [QuerySpecification::Common::OffsetSpec,
335
- # QuerySpecification::Common::CursorSpec,
336
- # QuerySpecification::Common::NullSemantics,
337
- # QuerySpecification::Common::AuthorizationSpec,
338
- # QuerySpecification::Common::InspectionSpec, nil] the option built
339
- # through the holder `OPTIONS` names for `name`, or `nil` when `declared`
340
- # is `nil`
341
157
  # @raise [KeyError] if `name` is not a key of `OPTIONS`
342
158
  def option(name, declared)
343
159
  return nil if declared.nil?
@@ -347,14 +163,6 @@ module Hecks
347
163
  end
348
164
 
349
165
  # Reads one option member's declared value back to its native type.
350
- #
351
- # @param key [Symbol] the option member's name being read
352
- # @param value [Object, nil] the declared value for that member
353
- # @param marked [Array<Symbol>] the members of this option that ride
354
- # Literal's spelling, from `OPTIONS`
355
- # @return [Object, nil] `value` read back through `Literal.read` when
356
- # `key` is marked, a Symbol when `key` is one of `SYMBOLIC`, the raw
357
- # `value` otherwise, or `nil` when `value` is `nil`
358
166
  def option_value(key, value, marked)
359
167
  return nil if value.nil?
360
168
  return read(value) if marked.include?(key)
@@ -1,49 +1,18 @@
1
1
  module Hecks
2
2
  module Bluebook
3
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."
4
+ # Projects `contracts.rb`'s `fields:` table from the language's own grammar,
5
+ # for fields simple enough to derive: scalar, non-reference, non-walked.
21
6
  module Specializer
22
7
  module_function
23
8
 
24
- # S17, ADR 0026 — `Handler` is a genuine entity, nested under
25
- # `ProcessManager`, so `.aggregate` alone does not find it —
26
- # it hangs off some aggregate's own `.entities` instead
27
- # (searched recursively, the same reason `Value::Coercion#find_
28
- # entity` does: a nested entity, like `Dispatch` inside
29
- # `Handler`, is not a direct child of any aggregate either).
30
- #
31
- # @param chapter [Bluebook::Chapter] the chapter to search
32
- # @param name [String, Symbol] the construct's declared name
33
- # @return [Bluebook::Aggregate, Class, nil] the aggregate, or the entity class
34
- # (a `Bluebook::Entity` subclass) nested anywhere under one, or `nil` if
35
- # `chapter` declares no construct by that name
9
+ # Finds a construct by name — an aggregate, or an entity nested anywhere
10
+ # under one (recursively, since an entity like Dispatch nests two deep).
36
11
  def construct_for(chapter, name)
37
12
  chapter.aggregate(name) || chapter.aggregates.filter_map { |a| find_entity(a, name) }.first
38
13
  end
39
14
 
40
15
  # Searches a construct's own entities, recursively, for one by name.
41
- #
42
- # @param construct [Bluebook::Aggregate, Class] the aggregate, or entity class,
43
- # to search under
44
- # @param name [String, Symbol] the entity's declared name
45
- # @return [Class, nil] the entity class (a `Bluebook::Entity` subclass), or `nil`
46
- # if none of `construct`'s nested entities has that name
47
16
  def find_entity(construct, name)
48
17
  construct.entities.each do |candidate|
49
18
  return candidate if candidate.hecks_name == name
@@ -54,23 +23,8 @@ module Hecks
54
23
  nil
55
24
  end
56
25
 
57
- # `position` is the first fold this runs into : a category declares
58
- # `attribute :position, Position` — for the judge's own walk,
59
- # `order_by :position` on its `DeclaredIn` ask — but no `*`
60
- # constructor takes it as an argument. `contracts.rb` already says so,
61
- # in the language every other derived field speaks :
62
- # `derived: { position: :walk }`. The language says a category has a
63
- # position ; it does not say a category's own constructor is handed
64
- # one, and that second fact is exactly what `fields:` needs to answer.
65
- # So the skip reads the category's own walk claims (`Contract#walked`)
66
- # rather than restating `position` here — Handler, which has no
67
- # walk-minted position, skips nothing.
68
- #
69
- # @param category [String, Symbol] the construct category's name, such as
70
- # `"Aggregate"` or `"Command"`
71
- # @return [Hash{Symbol => Array(Symbol, Symbol)}] a `contracts.rb`-shaped
72
- # `fields:` table, one `field => [field, :plain]` entry per scalar,
73
- # non-reference, non-walked attribute the language declares for `category`
26
+ # Builds `category`'s own `fields:` entries from the language's scalar,
27
+ # non-reference attributes, skipping any the contract already derives.
74
28
  def fields_for(category)
75
29
  language = construct_for(MetaValidator.grammar_registry.bluebook("Bluebook"), category.to_s)
76
30
  walked = Assembly.contract(category).walked
@@ -1,38 +1,7 @@
1
1
  module Hecks
2
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
- # ## Why a hash, not a runtime
12
- #
13
- # It takes a hash, not a runtime, on purpose. That makes it a pure inverse of
14
- # `to_h` and testable without the meta-domain in the picture at all:
15
- #
16
- # Assembly.call(built.to_h).to_h == built.to_h
17
- #
18
- # which is the check `spec/assembly_spec` makes for every chapter in the tree.
19
- # Feed it the reconstruction instead and the same code assembles what the
20
- # language holds — the only difference being where the declarations came from.
21
- #
22
- # ## One table, not a method per category
23
- #
24
- # Every field is read from one table. There is no method per category here:
25
- # `Contracts` names what the language cannot say about a construct, `Build`
26
- # reads it, and the coverage gate holds the table to the language. A method
27
- # per category can decorate a verb the language declares without ever
28
- # offering it; the table cannot, because `spec/assembly_spec` checks every
29
- # field the language declares against it.
30
- #
31
- # ## What stays hand-written
32
- #
33
- # The containment — which construct holds which — stays hand-written; it is
34
- # not a field table. The runtime surface is not built here at all — the
35
- # door is a per-boot projection, facade/surface.rb.
3
+ # Builds the runtime graph (aggregates, read models, process managers) from
4
+ # a chapter's declared hash — the pure inverse of `Chapter#to_h`.
36
5
  class Assembly
37
6
  # Builds the graph the runtime runs from one chapter's declared hash.
38
7
  #
@@ -67,10 +36,8 @@ module Hecks
67
36
 
68
37
  private
69
38
 
70
- # Every reaction the chapter holds, and each one also handed back to the head
71
- # that declared it — the builder keeps a policy in both places, hoisting it onto
72
- # the chapter where the runtime reads it while the head keeps its own list. The
73
- # language records which head, so the assembly can put it back.
39
+ # Hoists each reaction onto the chapter (for the runtime) and also back onto
40
+ # the aggregate head that declared it, per the language's own record of which.
74
41
  def reactions(aggregates)
75
42
  Array(@declaration[:policies]).map { |row| Build.call("Policy", row) }.each do |reaction|
76
43
  next if reaction.aggregate.to_s.empty?
@@ -90,14 +57,8 @@ module Hecks
90
57
  dispatches: Array(row[:dispatches]).map { |leg| dispatch(leg) })
91
58
  end
92
59
 
93
- # `compensates` — a plain hash on the declaration (`Reconstruction#
94
- # dispatch`'s own comment for why), built into the real
95
- # `DispatchSpec` its own field actually is by recursing through
96
- # this same method, one level in — the identical move `handler`
97
- # itself takes into `dispatches`, one level up. `nil` when there
98
- # is nothing to compensate; `Build.call` never sees a
99
- # `compensates:` key it does not know how to read either way, the
100
- # same reason `compensates` never joined `Contract#fields` at all.
60
+ # `compensates` recurses through this same method (as `handler` does for
61
+ # `dispatches`) since it's itself a nested `DispatchSpec`-shaped hash.
101
62
  def dispatch(row)
102
63
  compensates = row[:compensates] && dispatch(row[:compensates])
103
64
  Build.call("Dispatch", row, compensates: compensates)
@@ -5,12 +5,8 @@ require_relative "../naming"
5
5
 
6
6
  module Hecks
7
7
  module Bluebook
8
- # One declared field on a construct — name, type (a primitive, a
9
- # Reference, or another construct's name, always spelled via `#spell`),
10
- # whether it's a list, its default/optional/pattern, and the closed set
11
- # it `admits`. What `attribute :x, Type` (or `identified_by`,
12
- # `reference_to`, etc.) actually mints, on an Aggregate, Entity, Command,
13
- # ValueObject, Query or PortOperation alike.
8
+ # One declared field on a construct: name, type, list, default, optional, pattern
9
+ # and the closed set it `admits`.
14
10
  class Attribute
15
11
  include Hecks::IR
16
12
  include Behaviour::Attribute
@@ -28,44 +24,17 @@ module Hecks
28
24
 
29
25
  attr_reader :name, :type, :default, :pattern, :admits, :relationship
30
26
 
31
- # A Reference is kept as itself. Every other type is still a name, and
32
- # crosses over as its construct does.
33
- #
34
- # `admits` names an already-declared closed set the value must belong
35
- # to — `Vocabulary::QueryComparator` — spelled aggregate-qualified
36
- # because the set is a value object inside an aggregate, and the
37
- # aggregate is the only thing `reference_to` can reach.
38
- #
39
- # On the wire, because it is a rule and not only a typing hint.
40
- #
41
- # It began as neither. The link existed so a generator could type
42
- # `WhereClause.op` as `WhereOp` — a typing convenience, not worth
43
- # moving 710 attribute records across eight goldens for. Then `admits`
44
- # grew teeth (coercion refuses a non-member) and the argument
45
- # inverted: a rule the wire does not carry is one a reader of the IR
46
- # cannot enforce, and a bluebook whose meaning depends on the reader
47
- # means two things. Proved rather than assumed — the same domain
48
- # refused "burnt" through one reading and emitted the event through
49
- # another.
50
- #
51
- # The wire carries the name, not the members. A reader resolves it
52
- # against the IR it holds, so the members are declared once and
53
- # copied nowhere — which is the same reason `admits` exists at all.
27
+ # A Reference is kept as itself; every other type is a name.
54
28
  #
55
29
  # @param name [Symbol, String] the attribute's name
56
- # @param type [Module, Bluebook::Reference, String, Symbol] the bare constant naming
57
- # a primitive or a value object, a `reference_to`-built Reference, or already-spelled
58
- # text
30
+ # @param type [Module, Bluebook::Reference, String, Symbol] a bare constant, a Reference,
31
+ # or already-spelled text
59
32
  # @param list [Boolean] whether this attribute holds a list of values rather than one
60
33
  # @param default [Object, nil] the value a new record starts with when none is given
61
34
  # @param optional [Boolean] whether a command may omit this attribute
62
- # @param pattern [String, nil] a regex source the value must match; shared-engine
63
- # subset only (`PatternSubset`)
64
- # @param admits [String, nil] an already-declared closed set's aggregate-qualified
65
- # name, such as `"Vocabulary::QueryComparator"`, that the value must belong to
66
- # @param relationship [Symbol, nil] the DSL word that minted this attribute
67
- # (`:reference_to`, `:has_many`, `:has_one` or `:belongs_to`), or `nil` for an
68
- # ordinary attribute
35
+ # @param pattern [String, nil] a regex source the value must match (`PatternSubset`)
36
+ # @param admits [String, nil] an aggregate-qualified closed-set name for the value
37
+ # @param relationship [Symbol, nil] the DSL word that minted this attribute, or `nil`
69
38
  def initialize(name:, type:, list: false, default: nil, optional: false, pattern: nil,
70
39
  admits: nil, relationship: nil)
71
40
  @name = name.to_sym
@@ -80,23 +49,9 @@ module Hecks
80
49
 
81
50
  # A bare constant in a bluebook is a name, even when Ruby has heard of it.
82
51
  #
83
- # `BluebookBuilder.build` says exactly this and installs a `const_missing`
84
- # resolver that hands back the symbol — `attribute :target, Target` becomes
85
- # the name "Target" and nothing looks Target up. That works only while the
86
- # lookup fails, and `Facade::Surface` installs every aggregate name as a
87
- # top-level constant (its own comment, and `ConstShim`'s, both say so).
88
- #
89
- # So in one process: boot a domain with an aggregate named `Target`, then
90
- # load a chapter whose own value object is called `Target`, and Ruby
91
- # resolves the constant before the hook is ever asked. The chapter is then
92
- # built against somebody else's aggregate — silently, with no refusal —
93
- # and the attribute stops meaning what the file plainly says.
94
- #
95
- # Demodulised, so both paths spell it the same: `:Target` and
96
- # `QualityControl::Target` are both "Target". A plain class stays itself —
97
- # `String` demodulises to "String" — so the ordinary case is untouched.
98
- # This does not undo the constant leak; it makes the leak unable to change
99
- # what a chapter means, which is the part that has to hold.
52
+ # `Facade::Surface` installs aggregate names as top-level constants, which would win
53
+ # over the `const_missing` resolver and silently rebind the attribute. Demodulising
54
+ # spells `:Target` and `QualityControl::Target` the same.
100
55
  def spell(type)
101
56
  return type if type.is_a?(Reference)
102
57
  return Naming.demodulise(type) if type.is_a?(Module)
@@ -105,12 +60,8 @@ module Hecks
105
60
  end
106
61
  private :spell
107
62
 
108
- # Held because a declared vocabulary pins it — spec/vocabulary_conformance
109
- # holds `Primitive`'s members to this list.
63
+ # Pinned by spec/vocabulary_conformance, which holds `Primitive`'s members to this list.
110
64
  PRIMITIVES = Hecks::Vocabulary.fetch("Primitive")
111
-
112
- # `type` is spelled, never handed over. A Reference renders as
113
- # "Reference<Customer>" here because that is the export's pinned spelling.
114
65
  end
115
66
  end
116
67
  end