hecks 2.7.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 (463) hide show
  1. checksums.yaml +4 -4
  2. data/exe/hecks +11 -0
  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 +18 -99
  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 +15 -37
  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 +32 -142
  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 +17 -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 +38 -26
  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 +17 -65
  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 -189
  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 -106
  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 +54 -268
  109. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +47 -308
  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 +10 -91
  114. data/lib/hecks/bluebook/model_check.rb +102 -600
  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 +84 -0
  128. data/lib/hecks/cli/document.rb +86 -0
  129. data/lib/hecks/cli/ir.rb +45 -0
  130. data/lib/hecks/cli/mcp.rb +31 -0
  131. data/lib/hecks/cli/mcp_door.rb +366 -0
  132. data/lib/hecks/cli/model_check.rb +203 -0
  133. data/lib/hecks/cli/project_cli.rb +90 -0
  134. data/lib/hecks/cli/project_diagrams.rb +36 -0
  135. data/lib/hecks/cli/run.rb +173 -0
  136. data/lib/hecks/cli/smoke_test.rb +45 -0
  137. data/lib/hecks/cli/stores.rb +53 -0
  138. data/lib/hecks/cli.rb +160 -0
  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 +98 -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 +95 -0
  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 +62 -65
  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 +7 -26
  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 +22 -122
  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 +151 -679
  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 +9 -16
  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 +46 -138
  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 +93 -0
  336. data/lib/hecks/release/runner/commands.rb +66 -0
  337. data/lib/hecks/release/runner/console.rb +52 -0
  338. data/lib/hecks/release/runner/gem_publisher.rb +43 -0
  339. data/lib/hecks/release/runner/git.rb +48 -0
  340. data/lib/hecks/release/runner/npm_publisher.rb +75 -0
  341. data/lib/hecks/release/runner/preflight.rb +104 -0
  342. data/lib/hecks/release/runner/published.rb +55 -0
  343. data/lib/hecks/release/runner/tagger.rb +80 -0
  344. data/lib/hecks/release/runner.rb +215 -0
  345. data/lib/hecks/rendering.rb +4 -26
  346. data/lib/hecks/router/namespace_installer.rb +12 -36
  347. data/lib/hecks/router.rb +4 -8
  348. data/lib/hecks/runtime/aggregate_lock.rb +8 -33
  349. data/lib/hecks/runtime/boot_gates.rb +3 -18
  350. data/lib/hecks/runtime/caller.rb +19 -59
  351. data/lib/hecks/runtime/capability_graph.rb +5 -21
  352. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +16 -78
  353. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +23 -160
  354. data/lib/hecks/runtime/command_interpreter.rb +125 -307
  355. data/lib/hecks/runtime/command_rules/admissibility.rb +95 -366
  356. data/lib/hecks/runtime/command_rules/arithmetic.rb +66 -196
  357. data/lib/hecks/runtime/command_rules/authorization.rb +13 -45
  358. data/lib/hecks/runtime/command_rules/emission.rb +8 -21
  359. data/lib/hecks/runtime/command_rules/references.rb +23 -202
  360. data/lib/hecks/runtime/dependency_planning.rb +30 -102
  361. data/lib/hecks/runtime/dispatcher.rb +82 -241
  362. data/lib/hecks/runtime/entity_element.rb +40 -462
  363. data/lib/hecks/runtime/entity_interpreter.rb +65 -220
  364. data/lib/hecks/runtime/errors.rb +32 -115
  365. data/lib/hecks/runtime/event.rb +3 -25
  366. data/lib/hecks/runtime/identity.rb +18 -154
  367. data/lib/hecks/runtime/instance.rb +18 -150
  368. data/lib/hecks/runtime/interpreting.rb +11 -70
  369. data/lib/hecks/runtime/invocation.rb +28 -164
  370. data/lib/hecks/runtime/loader.rb +48 -186
  371. data/lib/hecks/runtime/outbox.rb +38 -261
  372. data/lib/hecks/runtime/policy_interpreter.rb +38 -200
  373. data/lib/hecks/runtime/port_operation_interpreter.rb +26 -87
  374. data/lib/hecks/runtime/query_interpreter.rb +45 -187
  375. data/lib/hecks/runtime/reaction_invocation.rb +36 -138
  376. data/lib/hecks/runtime/read_model_interpreter.rb +139 -208
  377. data/lib/hecks/runtime/rebuild_sweep.rb +8 -54
  378. data/lib/hecks/runtime/reference_hop.rb +8 -81
  379. data/lib/hecks/runtime/refusal_wording.rb +8 -89
  380. data/lib/hecks/runtime/registry/saga_persistence.rb +10 -102
  381. data/lib/hecks/runtime/registry/verification.rb +52 -225
  382. data/lib/hecks/runtime/registry/world_defaults.rb +4 -53
  383. data/lib/hecks/runtime/registry.rb +103 -244
  384. data/lib/hecks/runtime/remote_dispatcher.rb +37 -152
  385. data/lib/hecks/runtime/routing.rb +4 -15
  386. data/lib/hecks/runtime/saga_interpreter/correlation.rb +14 -61
  387. data/lib/hecks/runtime/saga_interpreter.rb +79 -263
  388. data/lib/hecks/runtime/saga_pending_dispatch.rb +2 -43
  389. data/lib/hecks/runtime/tenant_check.rb +6 -68
  390. data/lib/hecks/runtime/tenant_scope.rb +3 -28
  391. data/lib/hecks/runtime/value/admission.rb +16 -67
  392. data/lib/hecks/runtime/value/coercion.rb +155 -641
  393. data/lib/hecks/runtime/value/entity_list_coercion.rb +26 -219
  394. data/lib/hecks/runtime/value.rb +12 -84
  395. data/lib/hecks/runtime.rb +17 -65
  396. data/lib/hecks/storehouse.rb +146 -567
  397. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +8 -32
  398. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +6 -23
  399. data/lib/hecks/vendoring/git_environment.rb +2 -8
  400. data/lib/hecks/vendoring/git_source.rb +4 -10
  401. data/lib/hecks/vendoring.rb +7 -46
  402. data/lib/hecks/version.rb +3 -14
  403. data/lib/hecks/vocabulary.rb +5 -0
  404. data/lib/hecks.rb +32 -82
  405. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -46
  406. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +11 -80
  407. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +10 -99
  408. metadata +30 -59
  409. data/lib/hecks/bench/cli.rb +0 -74
  410. data/lib/hecks/bench/environment.rb +0 -100
  411. data/lib/hecks/bench/postgres_probe.rb +0 -41
  412. data/lib/hecks/bench/report.rb +0 -116
  413. data/lib/hecks/bench/ruby_runner.rb +0 -151
  414. data/lib/hecks/bench/run.rb +0 -55
  415. data/lib/hecks/bench/rust_runner.rb +0 -165
  416. data/lib/hecks/bench/stats.rb +0 -75
  417. data/lib/hecks/bench/suite.rb +0 -172
  418. data/lib/hecks/bench/workload.rb +0 -137
  419. data/lib/hecks/bench.rb +0 -28
  420. data/lib/hecks/codemod.rb +0 -443
  421. data/lib/hecks/corpus.rb +0 -479
  422. data/lib/hecks/doc/reference.rb +0 -585
  423. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -684
  424. data/lib/hecks/fuzzing/combination_miner.rb +0 -178
  425. data/lib/hecks/fuzzing/concurrent_dispatch.rb +0 -469
  426. data/lib/hecks/fuzzing/coverage_campaign.rb +0 -161
  427. data/lib/hecks/fuzzing/differential.rb +0 -192
  428. data/lib/hecks/fuzzing/domain_generator.rb +0 -871
  429. data/lib/hecks/fuzzing/era_boundary.rb +0 -154
  430. data/lib/hecks/fuzzing/form_census.rb +0 -285
  431. data/lib/hecks/fuzzing/generated_domain_check.rb +0 -171
  432. data/lib/hecks/fuzzing/invalid_value_generator.rb +0 -125
  433. data/lib/hecks/fuzzing/isolated_boot.rb +0 -658
  434. data/lib/hecks/fuzzing/nondeterministic.rb +0 -79
  435. data/lib/hecks/fuzzing/persistence_parity.rb +0 -253
  436. data/lib/hecks/fuzzing/properties/corrections.rb +0 -125
  437. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +0 -688
  438. data/lib/hecks/fuzzing/properties/guards.rb +0 -398
  439. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +0 -399
  440. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +0 -137
  441. data/lib/hecks/fuzzing/properties/outbox.rb +0 -180
  442. data/lib/hecks/fuzzing/properties/querying.rb +0 -260
  443. data/lib/hecks/fuzzing/properties.rb +0 -267
  444. data/lib/hecks/fuzzing/qa_settings.rb +0 -164
  445. data/lib/hecks/fuzzing/replay.rb +0 -930
  446. data/lib/hecks/fuzzing/rotation_priority.rb +0 -114
  447. data/lib/hecks/fuzzing/rust_gap_manifest.rb +0 -139
  448. data/lib/hecks/fuzzing/self_consistency.rb +0 -825
  449. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +0 -532
  450. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +0 -171
  451. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +0 -127
  452. data/lib/hecks/fuzzing/sequence_generator/picker.rb +0 -116
  453. data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +0 -138
  454. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +0 -344
  455. data/lib/hecks/fuzzing/sequence_generator.rb +0 -311
  456. data/lib/hecks/fuzzing/shrinker.rb +0 -309
  457. data/lib/hecks/fuzzing/structural_skips.rb +0 -53
  458. data/lib/hecks/fuzzing/sweep_depth.rb +0 -61
  459. data/lib/hecks/fuzzing/target_capabilities.rb +0 -255
  460. data/lib/hecks/fuzzing/value_generator.rb +0 -340
  461. data/lib/hecks/fuzzing.rb +0 -23
  462. data/lib/hecks/grammar/evolve.rb +0 -524
  463. data/lib/hecks/query_ir.rb +0 -482
@@ -1,60 +1,13 @@
1
1
  module Hecks
2
- # A named registry of "canonical IR in, external artifact out" tools —
3
- # §30 of docs/HECKS_IMPLEMENTATION_PLAN.md. Before this existed, every
4
- # such tool was its own thing with its own call shape: `Exporter`
5
- # (registry-wide, consumed directly by bin/ir, bin/project_rust, and
6
- # translation/audit's approval digest — untouched by this file, still
7
- # exactly what those three read), and `RustProjection::Projector`
8
- # (`rust/project.rb`, a whole separate Ruby program under a
9
- # confusingly-identical module name) are the two that already exist.
10
- # Neither is registered here — retrofitting either is real, separate
11
- # work (Rust's generator is a whole second toolchain; a UL/OIDC
12
- # projector doesn't exist yet at all) — but `:ir` is, as a genuine,
13
- # working, golden-tested example of the shape every future projector
14
- # (`:rust`, `:ul`, `:openid`, ...) is meant to follow.
2
+ # A named registry of "canonical IR in, external artifact out" tools, each
3
+ # taking one bluebook's IR and answering a derived artifact (`:ir`, `:docs`, ...).
15
4
  #
16
- # The unit is one bluebook's IR, not a whole booted registry — matching
17
- # every real projection target (Rust/UL/OIDC all project one domain at
18
- # a time), and deliberately narrower than `Exporter.call`'s own
19
- # multi-domain shape.
5
+ # Projector.call(:ir, bluebook: chapter)
20
6
  #
21
- # ## Three kinds of "project"
22
- #
23
- # Told apart by what they need as input. Only the first belongs in this
24
- # registry.
25
- #
26
- # a projection takes a chapter's declaration and answers something
27
- # that describes the domain: its IR, its storage shape, an OIDC scope
28
- # manifest, the parser's keyword table, the reference pages. Inert,
29
- # derived, and runnable against any chapter that carries what it
30
- # declares it needs. These are what `register` holds.
31
- #
32
- # an export takes a declaration and its bindings and answers
33
- # something that is the domain, running elsewhere — rust/project.rb's
34
- # generated crate, the WASM artifact, the CloudFormation template
35
- # `lib/hecks/projections/deploy` renders. It needs the
36
- # `.world`/`.hecksagon` a projection never looks at, because a
37
- # running system has to know how it is wired. A target that needs
38
- # this declares `needs_world: true` (`Target#projects_as`) and reads
39
- # `options.fetch(:world)`; `Projector.call`'s own `world:` keyword is
40
- # what carries it, merged into `options` before the target ever runs.
41
- #
42
- # a state projection takes records — a domain after dispatch — and is
43
- # a read-model question wearing the same word.
44
- # `bin/expression_projection` is the one of these: its operators are
45
- # not declared anywhere, they are what exists after
46
- # `Grammar.expression` replays expression_operators.json's ledger of
47
- # dispatches. Converting it into this registry would be a category
48
- # error, however much its name suggests otherwise.
49
- #
50
- # ## One word, three other meanings
51
- #
52
- # Worth naming too, because grepping "projection" turns all of these up
53
- # and none is the above:
54
- #
55
- # Ports::Projection read-model catch-up, events folded into state
56
- # bin/project forces that catch-up by hand
57
- # RustProjection rust/project.rb's own separate toolchain
7
+ # A projection is inert and derived from a chapter's declaration. An export
8
+ # (rust crate, WASM, CloudFormation) also needs `.world` bindings and declares
9
+ # `needs_world: true`. State projections (`bin/expression_projection`) and
10
+ # `Ports::Projection` read-model catch-up are different things and not registered.
58
11
  module Projector
59
12
  class UnknownProjector < StandardError; end
60
13
  # A chapter-scoped projector was handed something that is not a chapter.
@@ -62,14 +15,8 @@ module Hecks
62
15
 
63
16
  module_function
64
17
 
65
- # `projector` needs only to answer `call(bluebook:, options:)` — a
66
- # module, a class with a class method, or any object responding to
67
- # `call` all work. Re-registering a name replaces it outright,
68
- # deliberately unguarded: a spec re-registering a stub under the same
69
- # name between examples is the ordinary case, not a footgun to fence
70
- # against.
71
18
  # Adds `projector` to the registry under `name`, replacing anything
72
- # already registered there.
19
+ # already registered there (deliberately unguarded, so specs can stub).
73
20
  #
74
21
  # @param name [String, Symbol] the key `projector` is looked up by (converted to a symbol)
75
22
  # @param projector [Module, Class, #call] anything answering `call(bluebook:, options:)`
@@ -81,24 +28,14 @@ module Hecks
81
28
  # Runs the projector registered under `name` against `bluebook`,
82
29
  # after refusing a construct it does not admit.
83
30
  #
84
- # `bluebook:` is kept as the keyword because it is the shipped
85
- # spelling and every existing caller uses it — but what it accepts is
86
- # any construct that emits IR, and `admits!` is what decides whether
87
- # this target can actually take the one handed over.
88
- #
89
31
  # @param name [String, Symbol] the registered projector's key
90
- # @param bluebook [Bluebook::Behaviour::Chapter, Hecks::IR] the chapter or
91
- # IR-emitting construct to project
32
+ # @param bluebook [Bluebook::Behaviour::Chapter, Hecks::IR] the construct to project
92
33
  # @param options [Hash] projector-specific options, passed through unchanged
93
- # @param world [Bluebook::World, nil] the domain's `.world`/`.hecksagon` bindings,
94
- # required by an export (`needs_world: true`); nil for an ordinary projection.
95
- # Merged into `options[:world]` before the target runs — the target never
96
- # receives it as a separate argument.
97
- # @return [Object] whatever the projector's own `call` returns: typically a
98
- # `Hash`/`String` artifact, or a `Hash{String => String}` file tree
34
+ # @param world [Bluebook::World, nil] bindings an export needs (`needs_world: true`);
35
+ # merged into `options[:world]`
36
+ # @return [Object] whatever the projector's own `call` returns
99
37
  # @raise [UnknownProjector] if no projector is registered under `name`
100
- # @raise [WrongConstruct] if `bluebook` lacks a capability or aggregate the
101
- # projector requires, or if the projector needs `world:` and none is given
38
+ # @raise [WrongConstruct] if `bluebook` lacks what the projector requires
102
39
  def call(name, bluebook:, options: {}, world: nil)
103
40
  projector = registry.fetch(name.to_sym) do
104
41
  raise UnknownProjector, "no projector registered for #{name.inspect} — registered: #{registered.sort.inspect}"
@@ -107,31 +44,17 @@ module Hecks
107
44
  projector.call(bluebook: bluebook, options: world ? options.merge(world: world) : options)
108
45
  end
109
46
 
110
- # Refuses `construct` if it lacks a capability or declared aggregate
111
- # `projector` requires. A projection names the capabilities it needs;
112
- # this is what enforces that, before the projector runs.
113
- #
114
- # One check covers both shapes. An ordinary construct includes its
115
- # capabilities and a class-shaped one — Command, Entity, ValueObject
116
- # — extends them, and `is_a?` consults the singleton chain, so it
117
- # answers for an extended module as readily as an included one. This
118
- # started as two checks on the assumption it would not; a spec
119
- # asserting the assumption failed, which is the only reason the
120
- # redundant half was noticed.
47
+ # Refuses `construct` if it lacks a capability or declared aggregate `projector` requires.
48
+ # One `is_a?` check covers included and extended capabilities alike.
121
49
  #
122
50
  # @param name [String, Symbol] the projector's registered key, used in the message
123
- # when refusing
124
- # @param projector [Module, Class, #call] the target being checked; consulted for
125
- # `projection_requires`, `projection_declares`, and `projection_needs_world?` when
126
- # it answers them
127
- # @param construct [Bluebook::Behaviour::Chapter, Hecks::IR] the chapter or
128
- # IR-emitting construct offered to the projector
129
- # @param world [Bluebook::World, nil] the `world:` given to `Projector.call`, checked
130
- # against the target's own `needs_world:` declaration
51
+ # @param projector [Module, Class, #call] the target; consulted for `projection_requires`,
52
+ # `projection_declares` and `projection_needs_world?` when it answers them
53
+ # @param construct [Bluebook::Behaviour::Chapter, Hecks::IR] the construct offered
54
+ # @param world [Bluebook::World, nil] the `world:` given to `Projector.call`
131
55
  # @return [void]
132
- # @raise [WrongConstruct] if `construct` lacks a required capability, the chapter it
133
- # is declares no aggregate the projector needs, or the projector needs `world:`
134
- # and `world` is nil
56
+ # @raise [WrongConstruct] if a required capability or aggregate is missing, or the
57
+ # projector needs `world:` and `world` is nil
135
58
  def admits!(name, projector, construct, world = nil)
136
59
  needed = projector.respond_to?(:projection_requires) ? projector.projection_requires : []
137
60
  missing = needed.reject { |capability| capable?(construct, capability) }
@@ -190,11 +113,8 @@ module Hecks
190
113
  @registry ||= {}
191
114
  end
192
115
 
193
- # A target may be addressed by the constant that implements it
194
- # (`Projections::OIDC`) or by the bare key it registered under
195
- # (`:oidc`). Both resolve here, so the constant form is added
196
- # surface rather than a replacement — every `Projector.call(:ir, ...)`
197
- # written before this existed keeps working untouched.
116
+ # Resolves a target given as a constant (`Projections::OIDC`) or a bare
117
+ # key (`:oidc`) to the key it is registered under.
198
118
  #
199
119
  # @param target [String, Symbol, #projection_key] a registered key, or a target
200
120
  # that declared its own key with `Target#projects_as`
@@ -205,15 +125,10 @@ module Hecks
205
125
  target
206
126
  end
207
127
 
208
- # Writing is the caller's choice, not the projector's. A projector
209
- # returns an artifact and never touches disk, which is what lets
210
- # spec/projector_spec.rb compare `:ir`'s output against a golden
211
- # fixture without a tmpdir. `out:` is the only thing that writes.
128
+ # Writes `artifact` to `out`; a projector itself never touches disk.
212
129
  #
213
- # `as:` comes from the projection's own `emits:` declaration rather
214
- # than from inspecting the artifact. A Hash of path => contents and a
215
- # Hash that simply happens to hold strings are the same object to
216
- # Ruby; only the projection knows which it meant.
130
+ # `as:` comes from the projection's `emits:` declaration, since a file tree
131
+ # and a Hash of strings are the same object to Ruby.
217
132
  #
218
133
  # @param artifact [Hash, String] the projector's output; a file tree when `as: :files`
219
134
  # @param out [String] the path to write to; a directory when `as: :files`
@@ -226,8 +141,7 @@ module Hecks
226
141
  out
227
142
  end
228
143
 
229
- # Answers the paths written, in the order given — so a caller can
230
- # report what happened without re-deriving it from the tree.
144
+ # Writes each `files` entry under `directory`, answering the paths written.
231
145
  #
232
146
  # @param files [Hash{String => String}] relative path => contents
233
147
  # @param directory [String] the directory to write the tree under
@@ -249,8 +163,7 @@ require "json"
249
163
  require_relative "projector/exporter"
250
164
  require_relative "projector/ir_projector"
251
165
  require_relative "projector/target"
252
- # Neither of these requires this file back — they need `Naming` and nothing
253
- # else — so unlike a target they are safe to pull in from here.
166
+ # These need only `Naming`, not this file, so they are safe to load from here.
254
167
  require_relative "projector/docs_projector"
255
168
  require_relative "projector/narrate_projector"
256
169
  require_relative "projector/cli_projector"
@@ -260,9 +173,5 @@ Hecks::Projector.register(:docs, Hecks::Projector::DocsProjector)
260
173
  Hecks::Projector.register(:narrate, Hecks::Projector::NarrateProjector)
261
174
  Hecks::Projector.register(:cli, Hecks::Projector::CliProjector)
262
175
 
263
- # The targets are required from lib/hecks.rb, immediately after this
264
- # file — deliberately not from here. A target requires this file (it
265
- # needs `Target` and the registry), so requiring them back from here
266
- # would close a genuine `circular require considered harmful` loop. Ruby
267
- # tolerates it; the warning is still right, and load order is the kind of
268
- # thing that only breaks once someone requires a target on its own.
176
+ # Targets are required from lib/hecks.rb, not here: each requires this file, so
177
+ # requiring them back would close a circular require.
@@ -2,37 +2,16 @@ require_relative "../../literal"
2
2
  require_relative "../../vocabulary"
3
3
 
4
4
  module Hecks
5
- # Reopened (see query_specification.rb for the namespace's own
6
- # summary) to add Common::COMPARATORS — the vendored comparator
7
- # vocabulary — and .render_value, the wire-rendering entry point
8
- # shared by every literal-bearing spec struct.
5
+ # Adds `Common::COMPARATORS` and `.render_value`, the wire renderer shared by spec structs.
9
6
  module QuerySpecification
10
- # The vocabulary every query-shaped construct shares — the clause structs
11
- # (`WhereClause`, `OrderBy`, `LimitSpec`...), the `DSL` mixin that parses
12
- # them, and the comparison and null rules every engine answers by. It is
13
- # its own namespace so a plain `Bluebook::Query` and a
14
- # `ReadModel::Specification` say one thing the same way rather than each
15
- # carrying a copy.
7
+ # The clause structs, DSL mixin and comparison rules shared by every query-shaped construct.
16
8
  module Common
17
- # `none_in_state`, vendored addition not (yet) upstream hecks
18
- # (migration plan task 4): a cross-aggregate anti-join comparator --
19
- # `where ref: { none_in_state: "Claim:held" }` holds true when no
20
- # record in the named aggregate, keyed by this record's own field
21
- # value, is currently in the named state. plan.bluebook's own
22
- # description: "a keyed point lookup (HashMap hit), never a scan" --
23
- # a real, deliberate, pre-existing feature (WhereOp::NoneInState in
24
- # the old Rust runtime), not invented here -- see Runtime::
25
- # QueryInterpreter#holds?'s own comment for the evaluation side.
9
+ # Includes `none_in_state`: `where ref: { none_in_state: "Claim:held" }` holds when no record
10
+ # in the named aggregate, keyed by this field's value, is in that state (a keyed lookup).
26
11
  COMPARATORS = Hecks::Vocabulary.symbols("QueryComparator")
27
12
  end
28
13
 
29
- # Renders a literal captured in a query clause as its self-describing
30
- # wire spelling, for a spec struct's `to_h`.
31
- #
32
- # The specification structs' own name for the one wire spelling — see
33
- # `Hecks::Literal`, which every other `to_h`-bound literal field
34
- # shares. Kept as a word here because the structs below read better
35
- # saying what they are doing than naming the module that does it.
14
+ # Renders a query-clause literal as its wire spelling; delegates to `Hecks::Literal.render`.
36
15
  #
37
16
  # @param value [nil, Symbol, String, StateRef, Boolean, Integer, Float, Hash, Array] the
38
17
  # literal as the bluebook author wrote it; Hash values and Array elements are
@@ -4,44 +4,16 @@ require_relative "../../runtime/value"
4
4
  module Hecks
5
5
  module QuerySpecification
6
6
  module Common
7
- # **The one comparator table**. There were two, near-identical copies of
8
- # this — `Ports::Query::InMemory` (the path a Memory- or Heki-backed
9
- # aggregate query actually runs) and `Runtime::QueryInterpreter`
10
- # (entity/sub-list queries, and any adapter implementing no `:query`)
11
- # — plus a third partial reading of `comparable` inside
12
- # `SqlQueryBuilder#query_value`.
13
- #
14
- # Two copies of one rule is not a style complaint here; it has cost
15
- # real bugs twice already, both recorded in the comments those files
16
- # carried. `none_in_state` was added to one copy only, so an ordinary
17
- # Memory query's `none_in_state` clause fell to the `else` branch and
18
- # silently excluded every row. And `comparable` diverged without
19
- # anyone noticing: one copy took the first numeric member of a value
20
- # object, the other only unwrapped when there was exactly one — so a
21
- # value object with two numeric members compared as a number on one
22
- # path and as a whole Hash on the other.
23
- #
24
- # The callers still differ in how they reach a value — one takes a
25
- # registry argument, the other closes over `@registry`; one digs the
26
- # field through `FieldPath`, the other is handed it — so they keep
27
- # their own resolution and share only the comparison itself.
7
+ # The one comparator table shared by `Ports::Query::InMemory` and
8
+ # `Runtime::QueryInterpreter`, so every engine reads a where-clause identically.
28
9
  module Comparison
29
10
  module_function
30
11
 
31
12
  # Unwraps a value object to the one scalar a comparison can mean.
32
13
  #
33
- # A value object compared as a scalar, when which scalar is meant
34
- # is not in doubt. Exactly one numeric member is unambiguous; a
35
- # single-member value object is unambiguous whatever its type.
36
- # Anything else is returned unchanged rather than guessed at — two
37
- # numeric members give no reason to prefer either, and picking the
38
- # first silently compares a field the author never named.
39
- #
40
- # A declaration naming an ambiguous value object is refused when
41
- # the bluebook loads (`AggregateBuilder`'s own query-field seal),
42
- # so this branch is a backstop rather than the primary guard: which
43
- # member is meant is knowable at declaration time, and a refusal
44
- # naming the candidates is worth more than any runtime reading.
14
+ # Only a sole numeric member, or a sole member of any type, is unambiguous; anything
15
+ # else is returned unchanged rather than guessed at. Ambiguous declarations are refused
16
+ # at load, so this is a backstop.
45
17
  #
46
18
  # @param value [Runtime::Value, Hash, Object, nil] a held or wanted value; a
47
19
  # `Runtime::Value` is read through its `to_h`
@@ -58,9 +30,8 @@ module Hecks
58
30
  value
59
31
  end
60
32
 
61
- # Lists the members a value object offers a scalar comparison, for a
62
- # refusal that can name them. Empty when the value object is
63
- # unambiguous — nothing to report.
33
+ # Lists the members a value object offers a scalar comparison, for a refusal
34
+ # that can name them. Empty when the value object is unambiguous.
64
35
  #
65
36
  # @param value_object [Class<Bluebook::ValueObject>] the declared shape (a subclass
66
37
  # minted by `Bluebook::ValueObject.declare`, closed sets included) a query field names
@@ -76,34 +47,19 @@ module Hecks
76
47
 
77
48
  NUMERIC_TYPES = %w[Integer Float Numeric].freeze
78
49
 
79
- # Decides whether one where-clause comparison holds between the value
80
- # a record holds and the value the query wants.
81
- #
82
- # A `case` over a closed, declared set (`Vocabulary::QueryComparator`,
83
- # held equal to this list by spec/vocabulary_table_spec — see the
84
- # `else` branch's own comment) is the whole point of the one
85
- # comparator table this file's header describes: one place naming
86
- # every comparator, not one method per comparator scattered across
87
- # a module.
50
+ # Decides whether one where-clause comparison holds between the value a record
51
+ # holds and the value the query wants.
88
52
  #
89
- # @param operation [Symbol, String] the comparator name: `eq`, `ne`, `lt`, `lte`,
90
- # `gt`, `gte`, `in`, `contains` or `none_in_state`
91
- # @param held [Object, nil] the record's own value for the field; `nil` against a
92
- # non-nil `want` satisfies no comparator except `none_in_state` (see
93
- # `NullPolicy.unmatchable?`)
94
- # @param want [Object, nil] the value compared against; for `in` an Array or a
95
- # comma-separated String, for `none_in_state` an `"Aggregate:state"` String
96
- # @param registry [Runtime::Registry, nil] used only by `none_in_state` to look the
97
- # target aggregate up; `nil` makes that comparator hold
98
- # @return [Boolean] whether the comparison holds; an ordered comparator over a
99
- # non-`Numeric` operand is `false`
100
- # @raise [Runtime::WiringError] if `operation` names no comparator in this table, or
101
- # `none_in_state`'s target aggregate has no repository that can be wired
53
+ # @param operation [Symbol, String] `eq`, `ne`, `lt`, `lte`, `gt`, `gte`, `in`,
54
+ # `contains` or `none_in_state`
55
+ # @param held [Object, nil] the record's own value for the field
56
+ # @param want [Object, nil] the value compared against
57
+ # @param registry [Runtime::Registry, nil] used only by `none_in_state`
58
+ # @return [Boolean] whether the comparison holds
59
+ # @raise [Runtime::WiringError] if `operation` names no comparator in this table
102
60
  # rubocop:disable-next Metrics/CyclomaticComplexity
103
61
  def holds?(operation, held, want, registry: nil)
104
- # A NULL satisfies no comparison — NullPolicy.unmatchable? owns
105
- # the rule and the reasoning, including why `none_in_state` is
106
- # exempt from it.
62
+ # A NULL satisfies no comparison (`none_in_state` is exempt); see NullPolicy.
107
63
  return false if NullPolicy.unmatchable?(operation, held, want)
108
64
 
109
65
  case operation.to_s
@@ -117,24 +73,15 @@ module Hecks
117
73
  when "contains" then contains?(held, want)
118
74
  when "none_in_state" then none_in_state?(held, want, registry)
119
75
  else
120
- # Every declared comparator has a `when` above (Vocabulary::
121
- # QueryComparator, held equal to this list by
122
- # spec/vocabulary_table_spec) — this is not a real runtime
123
- # path today, only a backstop against the day a tenth
124
- # comparator reaches the grammar and this method doesn't
125
- # grow to match. Silently reading an unrecognized comparator
126
- # as `eq` is exactly the failure this table already shipped
127
- # once (see this file's own header) — an operator this
128
- # method can't evaluate must refuse, not guess.
76
+ # Backstop: an unrecognized comparator must refuse, never read as `eq`.
129
77
  raise Runtime::WiringError, "no comparator handles #{operation.to_s.inspect} — add one before declaring it"
130
78
  end
131
79
  end
132
80
 
133
81
  # Checks that both operands of an ordered comparison are numbers.
134
82
  #
135
- # gt/gte/lt/lte are numeric-only and silently false otherwise — a
136
- # where-clause never raises the way a given does, and that contract
137
- # predates the extraction (lt was already exactly this permissive).
83
+ # Ordered comparators are numeric-only and silently false otherwise; a where-clause
84
+ # never raises.
138
85
  #
139
86
  # @param held [Object, nil] the record's own value for the field
140
87
  # @param want [Object, nil] the value compared against
@@ -143,12 +90,8 @@ module Hecks
143
90
 
144
91
  # Reads a comparator's list operand as the Strings membership is tested against.
145
92
  #
146
- # `in` reads a comma-separated list — a real Array survives
147
- # untouched (a bluebook's own in-process value, before any wire
148
- # serialisation), each element unwrapped the same way a scalar
149
- # field is. This is `in`'s reading of its argument (a caller may
150
- # legitimately pass "a,b,c" meaning "any of these") — unrelated to
151
- # `contains`, which reads the stored field. See `contains?`.
93
+ # `in` accepts an Array or a comma-separated String; `contains` reads the stored
94
+ # field instead (see `contains?`).
152
95
  #
153
96
  # @param value [Array, String, Object, nil] an Array of elements, or anything whose
154
97
  # `to_s` is a comma-separated list such as `"a, b,c"`
@@ -164,12 +107,6 @@ module Hecks
164
107
  # Answers `in`: whether the held value, or any element of a held Array,
165
108
  # occurs in the wanted list.
166
109
  #
167
- # A folded reference hop asks whether the locally-held identity is
168
- # among the matching target identities. A has_many relationship holds
169
- # several identities, so the same question becomes an intersection:
170
- # does any held identity occur in the wanted set? Scalar `in` retains
171
- # its existing one-candidate behavior.
172
- #
173
110
  # @param held [Array, Object] the record's own value; an Array contributes each
174
111
  # element as a candidate, anything else is the single candidate
175
112
  # @param want [Array, String, Object] the wanted set, read through `members`
@@ -182,19 +119,10 @@ module Hecks
182
119
  candidates.any? { |candidate| wanted.include?(comparable(candidate).to_s) }
183
120
  end
184
121
 
185
- # Answers `contains`: element membership for a held Array, substring
186
- # for anything else.
122
+ # Answers `contains`: element membership for a held Array, substring for anything else.
187
123
  #
188
- # `contains` means two different things depending on what is held —
189
- # real element membership for a `list_of` field (a genuine Array
190
- # arrives already, one element one member, nothing to split), and
191
- # plain substring for anything else. The scalar case deliberately
192
- # stays out of `members`' comma-split: that silently reads a
193
- # free-text field's own comma as a separator — which the SQL
194
- # side's `instr`/`position` never does, so the two disagree the
195
- # moment a scalar's real content holds a comma. Matching SQL's
196
- # substring reading here keeps every engine answering `contains`
197
- # identically for the same declared field.
124
+ # A scalar is not comma-split, so free text containing a comma matches the way
125
+ # SQL's `instr`/`position` does.
198
126
  #
199
127
  # @param held [Array, Object] the record's own value; anything but an Array is
200
128
  # read through `to_s`
@@ -206,29 +134,17 @@ module Hecks
206
134
  held.to_s.include?(want.to_s)
207
135
  end
208
136
 
209
- # Answers `none_in_state` by looking the held identity up in another
210
- # aggregate's repository and reading that record's state.
137
+ # Answers `none_in_state` by looking the held identity up in another aggregate's
138
+ # repository and reading that record's state.
211
139
  #
212
- # **A cross-aggregate anti-join** — `where ref: { none_in_state:
213
- # "Claim:held" }` holds when no record in the named aggregate,
214
- # keyed by this record's own field value, is in the named state.
215
- # No registry — no way to look the target up — reads as "not
216
- # excluded", the same graceful default a missing record already
217
- # falls back to. The target is searched by bare name across every
218
- # loaded domain; ambiguity (two domains declaring one name) picks
219
- # the first match rather than refusing, since a where-clause never
220
- # raises (see `ordered?`).
140
+ # Holds when no such record is in the named state. No registry, aggregate or record
141
+ # reads as "not excluded". The target is found by bare name; the first match wins.
221
142
  #
222
- # @param held [Object, nil] this record's own field value, used as the target
223
- # record's identity
224
- # @param want [String, Symbol] `"Aggregate:state"` — the target aggregate's bare
225
- # name and the excluded state, split on the first colon
226
- # @param registry [Runtime::Registry, nil] the booted registry to find the target
227
- # aggregate and its repository in
228
- # @return [Boolean] `false` only when the target record exists and its lifecycle
229
- # field (or `:state`, absent a lifecycle) equals the named state; `true` when
230
- # there is no registry, no such aggregate or no such record
231
- # @raise [Runtime::WiringError] if the target aggregate's repository cannot be wired
143
+ # @param held [Object, nil] this record's own field value, the target record's identity
144
+ # @param want [String, Symbol] `"Aggregate:state"`, split on the first colon
145
+ # @param registry [Runtime::Registry, nil] the booted registry
146
+ # @return [Boolean] `false` only when the target record is in the named state
147
+ # @raise [Runtime::WiringError] if the target's repository cannot be wired
232
148
  def none_in_state?(held, want, registry)
233
149
  return true unless registry
234
150
 
@@ -240,31 +156,7 @@ module Hecks
240
156
  record = registry.repository(target_domain, target_ir).find(held)
241
157
  return true unless record
242
158
 
243
- # The field a state lives on, read from the target's own
244
- # declaration — not assumed to be literally named `state`. Every
245
- # `none_in_state` fixture this comparator originally shipped
246
- # with (spec/query_none_in_state_*_spec.rb) happens to declare a
247
- # plain `attribute :state, ...` rather than a real `lifecycle`,
248
- # which is how the previous hardcoded `record.state[:state]`
249
- # passed every one of them while being wrong for the shape this
250
- # whole comparator exists to answer about: a real state machine.
251
- # `lifecycle :field, default: ... do ... end` stores its state
252
- # under `field` (`Instance#assign_creation_attributes`'s own
253
- # `state[aggregate.lifecycle.field.to_sym] = ...`), and this
254
- # codebase's own convention overwhelmingly names that field
255
- # `status`, not `state` (`QualityControl::Clearance`'s own
256
- # `lifecycle :status` among many others) — so the hardcoded key
257
- # silently read `nil` from every real lifecycle-backed target,
258
- # comparable(nil) != state was true unconditionally, and
259
- # `none_in_state` against any lifecycle aggregate answered
260
- # "not excluded" for every row, always, no matter its actual
261
- # state. Found chasing `QualityControl::Bug.AwaitingClearance`
262
- # (qa/bluebook/quality_control.bluebook), which is exactly this
263
- # shape: `Clearance:green`/`Clearance:red` against a `lifecycle
264
- # :status` aggregate. Falls back to `:state` when the target
265
- # declares no lifecycle at all, so every existing fixture (a
266
- # plain attribute literally named `state`) keeps answering
267
- # exactly as before.
159
+ # The state lives on the target's lifecycle field, else on `:state`.
268
160
  field = target_ir.lifecycle&.field || :state
269
161
  comparable(record.state[field]) != state
270
162
  end
@@ -11,33 +11,17 @@ require_relative "where_clause"
11
11
  module Hecks
12
12
  module QuerySpecification
13
13
  module Common
14
- # Shared builder-instance methods for the `where`/`order_by`/
15
- # `limit`/`offset`/`cursor`/`authorize`/`nulls`/`inspect_query`
16
- # bluebook DSL words, mixed into both QueryBuilder and
17
- # ReadModelBuilder so the two specification kinds parse the same
18
- # clauses identically rather than each carrying its own copy.
14
+ # Builder methods for the `where`/`order_by`/`limit`/`offset`/`cursor`/`authorize`/
15
+ # `nulls`/`inspect_query` DSL words, shared by QueryBuilder and ReadModelBuilder.
19
16
  module DSL
20
- # Records one `WhereClause` per `field => value` pair — what the
21
- # `where` DSL word forwards to.
17
+ # Records one `WhereClause` per `field => value` pair (the `where` DSL word).
22
18
  #
23
- # `where`/`order_by`/`limit`/`offset`/`authorize` (all below) carry
24
- # an `_impl` name because the words themselves are dispatched by
25
- # the grammar table rather than defined as methods: both the Query
26
- # and the ReadModel Keyword rows name the `_impl` method in
27
- # `calls:`, the same shape `attribute_impl` has, and a shared mixin
28
- # means one method each. `where`/`order_by` are bootstrap-reachable
29
- # (every core chapter's own `read_model` filters its roster with
30
- # them), so they must resolve through
31
- # `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK` while the grammar
32
- # table is still being built.
19
+ # `where`/`order_by` resolve through `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`
20
+ # while the grammar table is being built.
33
21
  #
34
- # @param clauses [Hash{Symbol => Object}] field name (a dotted or slashed path is
35
- # one Symbol) to either a literal, a Symbol naming a query argument, or a
36
- # one-pair Hash `{ comparator => operand }` such as `{ gte: :minimum }`; a bare
37
- # value means `eq`
38
- # @return [void]
39
- # @raise [ArgumentError] if a Hash value does not have exactly one pair, or names a
40
- # comparator outside `COMPARATORS`
22
+ # @param clauses [Hash{Symbol => Object}] field name to a literal, a query argument
23
+ # Symbol, or a one-pair Hash such as `{ gte: :minimum }`; a bare value means `eq`
24
+ # @raise [ArgumentError] if a Hash value is not one known `COMPARATORS` pair
41
25
  def where_impl(clauses)
42
26
  @wheres ||= []
43
27
  clauses.each do |field, value|