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
@@ -4,85 +4,23 @@ require "rack"
4
4
 
5
5
  module Hecks
6
6
  module Adapters
7
- # **The driving side** — code an outside caller reaches in through,
8
- # rather than code the domain reaches out through. Every existing
9
- # file under `adapters/driven/` is the latter: a store or reader a
10
- # `persisted_by`/`port` binding resolves to, called by this
11
- # framework's own runtime. Nothing under this repository has ever
12
- # been the mirror image before — code that receives a request from
13
- # the outside world and turns it into a dispatch — so this is the
14
- # first entry, and the directory itself is new.
7
+ # Driving adapters: code an outside caller reaches in through.
15
8
  module Driving
16
- # **A GitHub webhook receiver, transport only** — the same split
17
- # `Hecks::Adapters::GithubChecks` (qa/adapters/github_checks.rb,
18
- # this class's own pull-side sibling) already draws for itself:
19
- # this file owns proving a request really came from GitHub and
20
- # unwrapping GitHub's own webhook envelope (`X-GitHub-Event`, the
21
- # JSON body, GitHub's own automatic `ping` check) — never which
22
- # commands to dispatch about what it finds inside. That is exactly
23
- # as domain-specific as `GithubChecks#run` turning `check-runs`
24
- # JSON into green-or-raise, and lives exactly where that class's
25
- # own header explains such logic belongs: outside this library,
26
- # in `qa/adapters/github_ci_webhook.rb`, the subclass of this file
27
- # that actually knows what a `QualityControl::Clearance` is.
9
+ # A GitHub webhook receiver, transport only: a rack app that verifies the signature.
10
+ # A subclass implements `#handle_event(event, action, payload)` returning `[status, body]`.
28
11
  #
29
- # ## A plain rack app
30
- #
31
- # A plain rack app (`#call(env)`) — no Sinatra, no Rails — the same
32
- # shape `Hecks::Forms::App` (lib/hecks/forms/app.rb) already
33
- # established for the one other HTTP-facing surface this library
34
- # ships. `rack` is a lazy Gemfile dependency for exactly the reason
35
- # that file's own header gives: this file is never required by
36
- # `require "hecks"` (nothing under `adapters.rb`'s own eager
37
- # `adapters/driven` load names it — see that file's own header),
38
- # so a project that never mounts a driving adapter never needs
39
- # `rack` installed, the same "opt in by requiring the file at all"
40
- # contract `hecks/forms.rb` already has for `Forms::App`.
41
- #
42
- # ## Subclass responsibility
43
- #
44
- # Implement `#handle_event(event, action,
45
- # payload)`, returning `[http_status, response_body_hash]`. Called
46
- # only after the signature has verified and the body has parsed as
47
- # JSON — a subclass never has to re-check either. `event` is
48
- # GitHub's own `X-GitHub-Event` header value ("check_suite",
49
- # "check_run", "pull_request", ...); `action` is the payload's own
50
- # top-level `"action"` field when it has one (GitHub's webhooks
51
- # nearly all carry one — "completed", "requested", "opened", ...)
52
- # and nil when it does not. `ping` — GitHub's own automatic
53
- # connectivity check, sent once when a webhook is first saved in
54
- # repository settings — is answered here and never reaches a
55
- # subclass at all; there is nothing domain-specific to decide
56
- # about it.
12
+ # Not required by `require "hecks"`, so `rack` stays an opt-in dependency.
57
13
  class GithubWebhook
58
- # **Refused, loudly** — the same shape a domain refusal already takes
59
- # everywhere else in this codebase (`Runtime::DOMAIN_REFUSALS`,
60
- # `Forms::App`'s own `{error:, message:}` JSON body for a bad
61
- # command). A request that cannot prove it came from GitHub gets
62
- # a real 401 and a named reason, never a silent 200 that would
63
- # let a forged "CI passed" payload regress nothing while looking
64
- # exactly like success in a log nobody re-reads.
14
+ # Raised for a request that cannot prove it came from GitHub; answered with a 401.
65
15
  class InvalidSignature < StandardError; end
66
16
 
67
- # **The body did not even parse** — distinct from a signature refusal:
68
- # this body genuinely came from whoever signed it (checked
69
- # first, before parsing ever runs — see `#call`), and simply
70
- # is not JSON. Still refused, never guessed at.
17
+ # Raised when a correctly signed body is not JSON; answered with a 400.
71
18
  class MalformedPayload < StandardError; end
72
19
 
73
20
  SIGNATURE_HEADER = "HTTP_X_HUB_SIGNATURE_256".freeze
74
21
  EVENT_HEADER = "HTTP_X_GITHUB_EVENT".freeze
75
22
 
76
- # `secret:` has no default, on purpose — the same rule
77
- # `GoogleAuthentication`'s own header states for its own
78
- # `ENV.fetch`, restated here because the consequence is worse for
79
- # a webhook: an unverified signature check is not "half
80
- # configured", it is no verification at all, silently accepting
81
- # anything claiming to be GitHub. A caller passes the real
82
- # secret explicitly — from `ENV.fetch("GITHUB_WEBHOOK_SECRET")`
83
- # or wherever it keeps one — rather than this class reaching into
84
- # the environment itself and hiding that requirement inside a
85
- # default.
23
+ # `secret:` has no default: a missing secret would silently disable verification.
86
24
  # @param secret [String] the shared webhook secret (e.g. `ENV.fetch
87
25
  # ("GITHUB_WEBHOOK_SECRET")`), checked against each request's `X-Hub-Signature-256`
88
26
  # @raise [ArgumentError] if `secret` is nil or empty
@@ -120,13 +58,7 @@ module Hecks
120
58
 
121
59
  private
122
60
 
123
- # Constant-time compare, not `==`. A byte-by-byte `==` returns
124
- # the moment it finds the first mismatching byte, so how long
125
- # that took leaks how many leading bytes of a forged signature
126
- # were already right to anyone timing the response — GitHub's
127
- # own webhook documentation calls this out by name and recommends
128
- # exactly the constant-time compare `Rack::Utils.secure_compare`
129
- # already gives for free, reused rather than hand-rolled.
61
+ # Constant-time compare: `==` leaks how many leading bytes of a forged signature matched.
130
62
  def verify_signature!(request, body)
131
63
  header = request.get_header(SIGNATURE_HEADER)
132
64
  raise InvalidSignature, "missing #{SIGNATURE_HEADER.sub('HTTP_', '').tr('_', '-')} header" if header.to_s.empty?
@@ -1,20 +1,12 @@
1
1
  require_relative "ir"
2
2
 
3
- # The authoring surface for `Hecks.behaviors "Name" do ... end` — one
4
- # `test "description" do ... end` block per case, `tests`/`setup`/`input`/
5
- # `expect` inside. `instance_eval`-based, the same shape every other
6
- # hecks DSL builder uses (`WorldBuilder`, `HecksagonBuilder`).
3
+ # The authoring surface for `Hecks.behaviors "Name" do ... end`, built with `instance_eval`.
7
4
  module Hecks
8
5
  module Behaviors
9
- # Raised at `Hecks.behaviors` build time — a missing `vision` or
10
- # `loads` line names the fix directly rather than failing later, deep
11
- # inside a boot, over a suite that was never going to be scoped.
6
+ # Raised at build time, so a missing `vision` or `loads` fails with the fix named.
12
7
  class Malformed < StandardError; end
13
8
 
14
- # The `test "description" do ... end` block's own receiver — collects
15
- # `tests`/`setup`/`input`/`expect` calls and builds a `TestCase` (ir.rb).
16
- # `validate_expect!` (private, below) is where a malformed or empty
17
- # `expect` is refused at build time rather than silently passing later.
9
+ # The `test "description" do ... end` receiver; builds a `TestCase` (ir.rb).
18
10
  class TestCaseBuilder
19
11
  # @param description [String] the `test "description" do ... end` text
20
12
  def initialize(description)
@@ -82,14 +74,8 @@ module Hecks
82
74
 
83
75
  private
84
76
 
85
- # Closes the silent-pass paths a free-form `expect(**kwargs)` merge
86
- # otherwise leaves open: a test with no `expect` at all asserts
87
- # nothing and passes whenever dispatch doesn't raise; `count:` on a
88
- # command and `emits:` on a query are each read by neither runner
89
- # (Expectations#run_command/#run_query), so they're accepted here
90
- # and then silently ignored at run time. Checking both at build
91
- # time, not in the runners, makes them errors on the file that
92
- # wrote them rather than green checks nobody questions.
77
+ # Refuses expectations that would pass silently: an empty `expect`, `count:` on a command,
78
+ # or `emits:` on a query (neither runner reads those). Checked at build time.
93
79
  def validate_expect!
94
80
  if @expect.empty?
95
81
  raise Malformed, "test #{@description.inspect} has no `expect` — say what this " \
@@ -108,9 +94,7 @@ module Hecks
108
94
  end
109
95
  end
110
96
 
111
- # The top-level `Hecks.behaviors "Name" do ... end` receiver — collects
112
- # `vision`/`loads`/`test` calls and builds a `BehaviorsSuite` (ir.rb),
113
- # refusing to build one missing either `vision` or `loads` (`#build`).
97
+ # The top-level `Hecks.behaviors` receiver; builds a `BehaviorsSuite` (ir.rb).
114
98
  class BehaviorsBuilder
115
99
  # @param name [String] the suite's declared name
116
100
  # @param source_path [String] the `.behaviors` file's own path
@@ -129,9 +113,7 @@ module Hecks
129
113
  # @return [String] `text`, unchanged
130
114
  def vision(text) = @vision = text
131
115
 
132
- # Relative to this `.behaviors` file, never to the filesystem's cwd
133
- # or a same-stem convention — scope is a fact this file declares,
134
- # not one a runner infers.
116
+ # Paths are relative to this `.behaviors` file, never the cwd.
135
117
  #
136
118
  # @param paths [Array<String>] paths to the files this suite's domain boots
137
119
  # from, relative to the `.behaviors` file
@@ -5,45 +5,11 @@ require_relative "../runtime/value"
5
5
  require_relative "../runtime/reaction_invocation"
6
6
  require_relative "../ports/persistence/binding_policy"
7
7
 
8
- # Hecks::Behaviors::Expectations
9
- #
10
- # ## What it does
11
- #
12
- # One test case, start to finish: take the suite's own runtime (booted
13
- # once for exactly what its `loads` names, reset to nothing between
14
- # tests — `runtime_for`), replay `setup` dispatches, dispatch (or query)
15
- # the command under test, check `expect`. Split out of
16
- # runner.rb the same way the file-count/sweep concern is split from a
17
- # single test's own execution.
18
- #
19
- # ## Deviations from a prior port
20
- #
21
- # Two deliberate deviations from a prior port of this same idea (read
22
- # before writing this, not reinvented):
23
- #
24
- # 1. A `setup` refusal and a refusal from the command under test are
25
- # caught in separate rescue scopes. The prior port wrapped the whole
26
- # test body — setups included — in one `rescue *REFUSAL_CLASSES`, so a
27
- # broken setup could spuriously satisfy `expect refused: "..."` if its
28
- # own refusal happened to match the expected substring. Here, any
29
- # domain refusal during `setup` is unconditionally an error — the
30
- # example never got to the situation it claims to test.
31
- #
32
- # 2. `emits:` is read off a `registry.event_log` diff around the tested
33
- # dispatch, not off `Result#events` alone. `Result#events` only holds
34
- # the events the outermost dispatch announced; a policy's own cascade
35
- # reenters through the same `Dispatcher#dispatch` (`PolicyInterpreter
36
- # #deliver` → `door.reenter` → `dispatch`), and every dispatch's
37
- # events — outer and reentrant alike — land in the one shared
38
- # `registry.event_log`, in order (`CommandRules::Emission#emit`,
39
- # `PortOperationInterpreter#emit`/`#call`). Diffing that log's length
40
- # before/after the tested dispatch is the one read that actually sees
41
- # a cascade — this DSL has no `kind: :cascade` because cascades are
42
- # always on and `emits:` is expected to see them.
8
+ # Runs one behaviors test case: replays its setup dispatches, dispatches (or
9
+ # queries) the command under test, and checks the result against `expect`.
43
10
  module Hecks
44
11
  module Behaviors
45
- # See this file's own header above for what this module does and the
46
- # two deliberate deviations from the prior port it's built from.
12
+ # Runs one behaviors test case: replays its setup, dispatches the command, checks `expect`.
47
13
  module Expectations
48
14
  module_function
49
15
 
@@ -66,6 +32,8 @@ module Hecks
66
32
 
67
33
  current_setup = nil
68
34
  begin
35
+ # Caught separately from the tested command's own refusal, so a broken
36
+ # setup can't spuriously satisfy `expect refused: "..."` by coincidence.
69
37
  test.setups.each do |setup|
70
38
  current_setup = setup
71
39
  dispatch_command(runtime, qualify(setup.command, nil, bluebooks, kind: :command), setup.args)
@@ -79,20 +47,9 @@ module Hecks
79
47
  error_result(test, "#{e.class}: #{e.message}")
80
48
  end
81
49
 
82
- # One boot per suite, not per test. The isolation a test needs is a
83
- # runtime with nothing in it — and a boot of the same files gives
84
- # exactly that back for the price of `Registry#reset_runtime_state!`
85
- # instead of ~2s of loading, verifying and era-checking the same
86
- # bluebook again (chess: 76 behaviours, 155s of which was booting
87
- # `chess.bluebook` 76 times). Keyed by the suite's own `loads` and
88
- # their mtimes, so an edited bluebook boots fresh on the next test
89
- # rather than running against a stale one — the property a watch
90
- # loop or a long rspec session actually relies on. `runtime:` lets
91
- # a caller that already holds a booted runtime (a spec, a REPL)
92
- # hand it in; it is reset the same way.
93
- # Not frozen — a real cache, mutated below (`RUNTIMES[key] ||=
94
- # boot_and_guard(files)`) and by #reset!. False positive for
95
- # Style/MutableConstant.
50
+ # Cached per suite, keyed by `loads`' file paths and mtimes, so a boot is
51
+ # reused across tests but an edited bluebook boots fresh on the next one.
52
+ # Mutated in place (`RUNTIMES[key] ||= boot_and_guard(files)`, and #reset!).
96
53
  # rubocop:disable-next Style/MutableConstant
97
54
  RUNTIMES = {}
98
55
  RUNTIMES_LOCK = Mutex.new
@@ -115,16 +72,8 @@ module Hecks
115
72
  end
116
73
  end
117
74
 
118
- # `reset_runtime_state!` only drops repository objects between
119
- # tests (registry.rb) — sufficient isolation for `Memory`, whose
120
- # `@records` is a plain per-instance ivar, so a fresh object really
121
- # is a fresh store. Against anything else (Sqlite, Postgres) the
122
- # rows themselves stay put: tests leak into each other, and a
123
- # suite booted against a domain's real hecksagon writes to a real
124
- # database. Refusing that wiring here, at boot, is the same shape
125
- # of guard `BindingPolicy` already applies to a missing bind — the
126
- # project's identity is refusing bad wiring up front, not
127
- # discovering it mid-suite.
75
+ # Refuses at boot when any aggregate binds to a non-Memory adapter —
76
+ # `reset_runtime_state!` only resets Memory's own per-instance store.
128
77
  #
129
78
  # @param files [Array<String>] absolute paths to the bluebook/hecksagon/world
130
79
  # files to boot
@@ -209,17 +158,8 @@ module Hecks
209
158
  check_ok(test) || check_fields(test, settled_state(runtime, verb, result)) || pass_result(test)
210
159
  end
211
160
 
212
- # A field expectation reads the aggregate as it stands once the
213
- # dispatch and its whole cascade have run — the same "cascades are
214
- # always on" reading `emits:` already commits to. `Result#state` is
215
- # the wrong source for that: it snapshots the instance the outer
216
- # dispatch saved, and a policy's own reentrant dispatch (a ply
217
- # advancing off a Moved event, a move count bumping) hydrates and
218
- # saves a fresh record afterward — so a field the cascade wrote
219
- # read back stale (found live: `expect move_count: 1` got 0 while
220
- # `emits:` saw MoveCountBumped in the same test). The repository
221
- # holds the settled record; read it back by the id the dispatch
222
- # itself answered with.
161
+ # Reads the aggregate back from the repository, not `Result#state` — a
162
+ # policy's own reentrant dispatch can re-save the record afterward.
223
163
  #
224
164
  # @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the suite's
225
165
  # booted runtime
@@ -228,7 +168,7 @@ module Hecks
228
168
  # the dispatch's own result
229
169
  # @return [Hash{Symbol => Object}] the settled record's current state, read
230
170
  # back from the repository; `result.state` (or `{}`) when the result has no
231
- # id or the repository no longer has that aggregate/record
171
+ # id or the repository does not have that aggregate/record
232
172
  def settled_state(runtime, verb, result)
233
173
  return result.state || {} unless result.respond_to?(:id) && result.id
234
174
 
@@ -241,44 +181,15 @@ module Hecks
241
181
  record ? record.state : (result.state || {})
242
182
  end
243
183
 
244
- # A behaviors test writes a dispatch the way the guide's own chess
245
- # examples do — receiver identity and command facts side by side
246
- # (`label: "g", id: "wn", to: { file: 2, rank: 2 }`) — and since
247
- # #335 the dispatcher's own `to:` keyword is the routing envelope,
248
- # so forwarding those kwargs loose collides the moment a domain
249
- # declares a command fact named `to` (chess does: every Move's own
250
- # destination). Found live: every such test failed with "to: does
251
- # not recognize file, rank" while this guide promised the spelling
252
- # works. `ReactionInvocation.build` is #335's own seam for turning
253
- # mixed facts into the strict envelope — identities lifted into
254
- # `to:`, declared facts into `with:` — so a behaviors dispatch now
255
- # goes through the exact same separation a policy's projection
256
- # does. A verb that names a port operation (checked explicitly,
257
- # below — "Pizzas::Order.PaymentGateway.Receive") keeps the loose
258
- # passthrough instead: its own input already spells the port
259
- # form's `to:`/`with:`, which the dispatcher's port branch reads
260
- # directly, and `ReactionInvocation.build`'s explicit envelope
261
- # expects a command's own declared attributes at the top level,
262
- # not a port operation's already-wrapped `to:`/`with:` shape.
184
+ # Splits a mixed dispatch (receiver identity plus declared facts, e.g.
185
+ # `to: { file: 2, rank: 2 }`) into the dispatcher's strict `to:`/`with:` envelope.
263
186
  #
264
- # Checks for a port operation directly rather than leaning on
265
- # `resolve_target` to raise `UnknownVerb` for one — a refusal that held
266
- # only so long as nothing else ever asked it to resolve a port-operation
267
- # verb. Now that a `policy` can legitimately `trigger` a port operation
268
- # (`ReactionInvocation#resolve_target`'s own port-operation branch),
269
- # that raise no longer happens, so this checks directly instead.
270
- #
271
- # @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the suite's
272
- # booted runtime
187
+ # @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the suite's booted runtime
273
188
  # @param verb [String] the command's dotted FQN, or a port operation's
274
- # @param args [Hash{Symbol => Object}] the command's (or setup's) facts, mixing
275
- # receiver identity and declared arguments
276
- # @return [Runtime::Dispatcher::Result, Runtime::RemoteDispatcher::Result] the
277
- # dispatch's own result
278
- # @raise [Runtime::UnknownVerb] if `verb` is not fully qualified or names an
279
- # undeclared domain, aggregate, command, entity or port operation
280
- # @raise [StandardError] any class in `Runtime::DOMAIN_REFUSALS` when the
281
- # domain refuses the call
189
+ # @param args [Hash{Symbol => Object}] the facts, mixing identity and declared arguments
190
+ # @return [Runtime::Dispatcher::Result, Runtime::RemoteDispatcher::Result] the dispatch result
191
+ # @raise [Runtime::UnknownVerb] if `verb` names an undeclared construct
192
+ # @raise [StandardError] any `Runtime::DOMAIN_REFUSALS` class, when the domain refuses
282
193
  def dispatch_command(runtime, verb, args)
283
194
  return runtime.dispatch_flat(verb, args) if port_operation?(runtime, verb)
284
195
 
@@ -297,10 +208,8 @@ module Hecks
297
208
  end
298
209
  end
299
210
 
300
- # The same "Head.Rest" shape `Dispatcher#dispatch` and
301
- # `ReactionInvocation#resolve_target` both already check — a bare
302
- # domain/aggregate lookup plus a port-name lookup, no command
303
- # resolution needed since all this asks is whether one exists.
211
+ # The same "Head.Rest" split `Dispatcher#dispatch` already uses — just a
212
+ # domain/aggregate and port-name lookup, no command resolution needed.
304
213
  #
305
214
  # @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the suite's
306
215
  # booted runtime
@@ -358,12 +267,8 @@ module Hecks
358
267
  test.expect.keys.any? { |key| !SPECIAL_KEYS.include?(key) }
359
268
  end
360
269
 
361
- # A field expectation on a query names one row's shape, so it only
362
- # makes sense once the query has settled on exactly one — the same
363
- # reason `expect status: "sold"` on a multi-row answer would be
364
- # ambiguous about which row it's describing. `check_fields` itself
365
- # stays row-shaped (it already is, for `run_command`'s settled
366
- # state); this picks which row it reads.
270
+ # A field expectation on a query only makes sense once it has settled on
271
+ # exactly one row; this picks that row (or fails naming the row count).
367
272
  #
368
273
  # @param test [Behaviors::TestCase] the test case, for the fail message
369
274
  # @param rows [Array<Hash>, Hash] the query's own result
@@ -437,12 +342,8 @@ module Hecks
437
342
  fail_result(test, "expected refused: #{expected.inspect}, got #{msg.inspect}")
438
343
  end
439
344
 
440
- # The real corpus this DSL is proven against writes VO-typed
441
- # `expect` values both ways — bare (`expect kind: "bishop"`) and
442
- # wrapped (`expect kind: { value: "bishop" }`). A live record's
443
- # field always comes back as a `Hecks::Runtime::Value`;
444
- # normalizing both sides to the same bare-scalar-or-plain-hash
445
- # shape is the one comparison that accepts either spelling.
345
+ # Normalizes both `expect` spellings (bare or `{value: ...}`) and a live
346
+ # `Hecks::Runtime::Value` field to the same bare-scalar-or-plain-hash shape.
446
347
  #
447
348
  # @param value [Object] a stored field's value, or an `expect` value to compare
448
349
  # it against
@@ -455,13 +356,8 @@ module Hecks
455
356
  value
456
357
  end
457
358
 
458
- # A bare `tests`/`setup` command name carries no domain — `loads`
459
- # can name more than one bluebook, so resolution searches every
460
- # aggregate across every bluebook the suite booted for the one that
461
- # actually declares the command. `on:` (when given, only ever on
462
- # the tested command — `setup` never receives it, see the DSL
463
- # contract) narrows the search to one aggregate by name instead of
464
- # searching all of them.
359
+ # Searches every aggregate of every bluebook the suite booted for the one
360
+ # declaring `command`; `on:` narrows the search to one aggregate by name.
465
361
  #
466
362
  # @param command [String, Symbol] a bare verb, or an already-dotted FQN
467
363
  # @param on_aggregate [String, Symbol, nil] the aggregate to search, or nil to
@@ -478,11 +374,8 @@ module Hecks
478
374
  disambiguate_qualified_name(candidates, command, kind, bluebooks)
479
375
  end
480
376
 
481
- # Finds every aggregate that could be what a bare command/query name refers to.
482
- #
483
- # **The search** — every (bluebook, aggregate) pair that declares a
484
- # command/query named `command`, narrowed to `on_aggregate` by name
485
- # when given.
377
+ # Finds every (bluebook, aggregate) pair that declares a command/query
378
+ # named `command`, narrowed to `on_aggregate` by name when given.
486
379
  #
487
380
  # @param command [String, Symbol] the bare verb to search for
488
381
  # @param on_aggregate [String, Symbol, nil] the aggregate to search, or nil to
@@ -502,11 +395,8 @@ module Hecks
502
395
  pairs.select { |_, agg| agg.public_send(members).any? { |m| m.hecks_name == command.to_s } }
503
396
  end
504
397
 
505
- # Resolves a search's candidates to exactly one dotted FQN, or refuses.
506
- #
507
- # **The report** — zero candidates and more-than-one candidates both
508
- # refuse (with a different message); exactly one resolves to its
509
- # dotted FQN.
398
+ # Resolves a search's candidates to exactly one dotted FQN; zero or more
399
+ # than one both refuse, each with a different message.
510
400
  #
511
401
  # @param candidates [Array<Array(Bluebook::Chapter, Bluebook::Aggregate)>] the
512
402
  # matching (chapter, aggregate) pairs found by `qualify_candidates`
@@ -1,29 +1,17 @@
1
1
  # Hecks::Behaviors::{TestSetup,TestCase,BehaviorsSuite}
2
2
  #
3
- # The `.behaviors` authoring surface's own IR — plain Structs, holding
4
- # exactly what `Hecks.behaviors "Name" do ... end` collected. Deliberately
5
- # not part of `Hecks::IR`/`emits_ir` — a behaviors suite is never
6
- # collected into a domain Registry (see `Hecks.behaviors`), never
7
- # dispatched through MetaValidator, and carries none of the self-hosted
8
- # round-trip machinery a real bluebook construct does. It is a test
9
- # artifact a runner reads on demand, not a domain a boot needs.
3
+ # Plain Structs holding what `Hecks.behaviors "Name" do ... end` collected; a suite is never
4
+ # collected into a domain Registry.
10
5
  module Hecks
11
- # The `.behaviors` toolkit: the DSL a `.behaviors` file is written against
12
- # (`Hecks.behaviors`, dsl.rb), the plain-Struct IR it builds (this file), the runner
13
- # that boots a suite's domain and checks each test (runner.rb, expectations.rb), and
14
- # an rspec shim for a consuming app's own suite (rspec.rb).
6
+ # The `.behaviors` toolkit: DSL, IR, runner and rspec shim.
15
7
  module Behaviors
16
8
  TestSetup = Struct.new(:command, :args, keyword_init: true)
17
9
 
18
10
  TestCase = Struct.new(:description, :tests_command, :on_aggregate, :kind,
19
11
  :setups, :input, :expect, keyword_init: true) do
20
- # Tells whether `tests_command` is already a fully qualified verb name.
12
+ # Whether `tests_command` is already a fully qualified verb name.
21
13
  #
22
- # An already-dotted tests_command is a literal FQN; otherwise `on:`
23
- # composes with the domain name resolved once setups/the tested
24
- # dispatch actually run (see Expectations — the domain isn't known
25
- # until `loads` is booted, so it's a runtime concern, not a field
26
- # here).
14
+ # @return [Boolean] true when `tests_command` contains a `.`
27
15
  #
28
16
  # @return [Boolean] true when `tests_command` contains a `.`
29
17
  def dotted? = tests_command.to_s.include?(".")
@@ -34,9 +22,8 @@ module Hecks
34
22
  def query? = kind == :query
35
23
  end
36
24
 
37
- # `loads` — absolute paths, already resolved against the `.behaviors`
38
- # file's own directory by the DSL (`BehaviorsBuilder#loads`). `path` is
39
- # the `.behaviors` file itself, kept for error messages.
25
+ # `loads` holds absolute paths resolved against the `.behaviors` file's directory;
26
+ # `path` is that file, kept for error messages.
40
27
  BehaviorsSuite = Struct.new(:name, :vision, :loads, :tests, :path, keyword_init: true)
41
28
  end
42
29
  end
@@ -1,13 +1,8 @@
1
1
  require_relative "../behaviors"
2
2
 
3
- # Hecks::Behaviors::RSpec.describe_file(path) — the shim a consumer's
4
- # `bundle exec rspec` uses to run `.behaviors` files as ordinary examples,
5
- # one `it` per test, named by the test's own description string. Same
6
- # shape `spec/guides_spec.rb` uses for doctested guides: the file is
7
- # parsed at collection time (cheap — `Behaviors.parse`, no test actually
8
- # run yet, just enough to know the `it` names), and each test's own
9
- # `Expectations.run_one` runs lazily inside its own `it`, exactly when
10
- # rspec actually executes it.
3
+ # Hecks::Behaviors::RSpec.describe_file(path) registers one `it` per test in a `.behaviors` file,
4
+ # so a consumer's `bundle exec rspec` runs it. The file is parsed at collection time; each test
5
+ # runs lazily inside its own `it`.
11
6
  #
12
7
  # require "hecks/behaviors/rspec"
13
8
  #
@@ -16,19 +11,13 @@ require_relative "../behaviors"
16
11
  # end
17
12
  module Hecks
18
13
  module Behaviors
19
- # See this file's own header above for what `describe_file` does and
20
- # how a consumer wires it into their own `bundle exec rspec` run.
14
+ # Registers one example per test in a `.behaviors` file so a consumer's suite runs it.
21
15
  module RSpec
22
16
  module_function
23
17
 
24
- # Parses one `.behaviors` file and registers an rspec example group for it, one
25
- # `it` per test named by the test's own description.
18
+ # Registers an rspec example group for one `.behaviors` file, one `it` per test.
26
19
  #
27
- # @param path [String] the `.behaviors` file's path
28
- # @return [void]
29
- # @raise [RuntimeError] not raised by this call itself; wraps `parsed.parse_error`'s
30
- # message and is raised only when rspec later runs the generated "loads without a
31
- # parse error" example, so it surfaces as that example's failure
20
+ # A parse error surfaces as a failing "loads without a parse error" example.
32
21
  def describe_file(path)
33
22
  parsed = Behaviors.parse(path)
34
23
 
@@ -2,14 +2,9 @@ require_relative "expectations"
2
2
 
3
3
  # Hecks::Behaviors.run(path) / .run_all(dir)
4
4
  #
5
- # Discovery + aggregation. `run_one` (expectations.rb) is the impure edge
6
- # that actually touches a live runtime; this file finds `.behaviors`
7
- # files, loads them, and maps their tests through it.
5
+ # Finds `.behaviors` files, loads them, and runs their tests through `Expectations.run_one`.
8
6
  module Hecks
9
- # See this file's own header above for `.run`/`.run_all`/`.parse` — the
10
- # discovery-and-aggregation half of the `.behaviors` authoring surface;
11
- # `Expectations` (expectations.rb) is the impure edge that actually runs
12
- # one test against a live runtime.
7
+ # The `.behaviors` toolkit: DSL, IR, runner and rspec shim.
13
8
  module Behaviors
14
9
  FileResult = Struct.new(:path, :parse_error, :runs, keyword_init: true)
15
10
  SweepResult = Struct.new(:root, :files_swept, :files, :summary, keyword_init: true)
@@ -18,33 +13,16 @@ module Hecks
18
13
  class LoadOutsideRunner < StandardError; end
19
14
 
20
15
  class << self
21
- # The `.behaviors` file currently being `Kernel.load`ed, if any —
22
- # bound only for the duration of that load (`run`'s own `ensure`),
23
- # never left set. `Hecks.behaviors` (lib/hecks/behaviors.rb)
24
- # reads this to resolve its own `loads` line relative to the right
25
- # file, and to refuse a `.behaviors` file loaded any other way.
16
+ # The `.behaviors` file being `Kernel.load`ed; `Hecks.behaviors` reads it to resolve
17
+ # `loads` and to refuse a file loaded any other way.
26
18
  attr_accessor :loading_path
27
19
 
28
- # The suite the most recent `Hecks.behaviors` call built — read
29
- # once, right after `Kernel.load`, by `run` below, which resets it
30
- # to nil before every load so a file that loads without calling
31
- # `Hecks.behaviors` at all is never confused for a stale previous
32
- # suite (a real bug in a prior port of this idea: compared only
33
- # against nil, so after the first successful file in a sweep it
34
- # stayed non-nil forever).
20
+ # The suite the latest `Hecks.behaviors` call built. `parse` resets it before each load so
21
+ # a file that never calls `Hecks.behaviors` is not mistaken for the previous suite.
35
22
  attr_accessor :last_suite
36
23
 
37
- # `Kernel.load`s one `.behaviors` file and returns its suite, with
38
- # no test actually executed yet — the cheap half, split out so a
39
- # caller that only needs to know what tests exist (the rspec shim,
40
- # naming its `it`s at collection time) doesn't pay for running them
41
- # until it actually wants to. `Behaviors.loading_path` is bound only
42
- # for the duration of the load, and `last_suite` is reset to nil
43
- # before it — a file that loads without ever calling
44
- # `Hecks.behaviors` is unambiguously a parse error, never a stale
45
- # suite from whatever loaded before it in a sweep (a real bug in a
46
- # prior port of this idea: compared only against nil, so after the
47
- # first successful file in a sweep it stayed non-nil forever).
24
+ # `Kernel.load`s one `.behaviors` file and returns its suite without running any test.
25
+ #
48
26
  # @param path [String] the `.behaviors` file's path
49
27
  # @return [Behaviors::ParseResult] `suite` holding the built `BehaviorsSuite` and
50
28
  # `parse_error` nil on success; `suite` nil and `parse_error` a String describing
@@ -83,10 +61,8 @@ module Hecks
83
61
  FileResult.new(path: parsed.path, parse_error: nil, runs: runs)
84
62
  end
85
63
 
86
- # Sweeps every `.behaviors` file under `dir`. Reports the file count
87
- # it actually found — a sweep that goes green without saying how
88
- # much it looked at is indistinguishable from one that found
89
- # nothing to look at.
64
+ # Sweeps every `.behaviors` file under `dir`, reporting the file count so a green sweep
65
+ # that found nothing is distinguishable.
90
66
  #
91
67
  # @param dir [String] the directory to search, recursively, for `.behaviors` files
92
68
  # @return [Behaviors::SweepResult] `files_swept` the count found, `files` one
@@ -2,17 +2,13 @@ require_relative "behaviors/dsl"
2
2
  require_relative "behaviors/expectations"
3
3
  require_relative "behaviors/runner"
4
4
 
5
- # The `.behaviors` toolkit `bin/behaviors` and a consumer's own rspec shim
6
- # (`hecks/behaviors/rspec`) drive. Not required by lib/hecks.rb on
7
- # purpose — a booted domain never needs it, the same reason `Fuzzing` (see
5
+ # The `.behaviors` toolkit driven by `bin/behaviors` and `hecks/behaviors/rspec`.
6
+ # Opt-in: not required by lib/hecks.rb, like `Fuzzing`.
8
7
  # lib/hecks/fuzzing.rb, the shape this file mirrors) is opt-in too.
9
8
  module Hecks
10
9
  class << self
11
- # `Hecks.behaviors "Name" do ... end` — deliberately not routed
12
- # through `collect` the way `bluebook`/`hecksagon`/`world` are: a
13
- # behaviors suite is a test artifact a runner reads on demand, never
14
- # a thing a live domain boot needs, so it has no business landing in
15
- # `Runtime.current_registry`.
10
+ # `Hecks.behaviors "Name" do ... end`. Not routed through `collect`: a behaviors suite is a
11
+ # test artifact, so it stays out of `Runtime.current_registry`.
16
12
  #
17
13
  # @param name [String] the suite's declared name
18
14
  # @yield the suite's body, evaluated against a `Behaviors::BehaviorsBuilder`