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,161 +0,0 @@
1
- module Hecks
2
- module Fuzzing
3
- # What one sweep has already reached, and where the next seed starts.
4
- #
5
- # `SequenceGenerator` steers within one sequence (an unexercised verb is
6
- # weighted up — picker.rb's `steer`), but without this, every seed of a
7
- # sweep would start from nothing and forget everything the seeds before
8
- # it reached. Roughly half of the QA ledger's bugs are refusal-kind splits (BUG#2–4,
9
- # 7, 8, 14–16, 19–21, 23, 27, 28, 36–38, 41, 54, 56): a particular verb,
10
- # in a particular state, with a particular malformed argument, refused
11
- # one way on Ruby and another on Rust. BUG#11 was found by hand because
12
- # a 25-step random walk never reached `Annotate` at all. Uniform seeds
13
- # reach those corners by luck; this makes the sweep remember.
14
- #
15
- # ## The coverage unit
16
- #
17
- # A tuple, not a verb — `verb | step kind | lifecycle state before |
18
- # adversarial mutation | outcome` (see `SequenceGenerator#coverage_tuple`).
19
- # "Renew refused LifecycleRefused from free" and "Renew ok from held" are
20
- # different places a runtime can be wrong, where a verb-level count
21
- # calls them the same.
22
- #
23
- # ## Two levers
24
- #
25
- # Both expressed as plain generator arguments so a seed stays
26
- # reproducible from one call:
27
- #
28
- # 1. Splicing (`prefix:`). A sequence that reached a new tuple joins
29
- # the corpus, cut at its last new tuple. A later seed may start by
30
- # re-generating a random-length prefix of a corpus entry — the same
31
- # seed, favor and nested prefix, so the same steps, dispatched for
32
- # real, reaching the same state — and then carries on with its own
33
- # seed's randomness from there. Deep state gets reached once and
34
- # explored many times, instead of re-rolled per seed.
35
- # 2. Favor (`favor:`). The verbs this sweep has hit least often —
36
- # including declared verbs it has never hit at all — are weighted
37
- # up for the next seed.
38
- #
39
- # ## Reproducibility
40
- #
41
- # Nothing here is random at the campaign's own level beyond
42
- # `Random.new(seed)` per plan, and a plan is printed with every finding
43
- # (`bin/qa_sweep`'s `reproduce:` line), so no finding depends on
44
- # re-running the whole campaign to get back to it.
45
- class CoverageCampaign
46
- # One seed's generation instructions, printable and reproducible.
47
- Plan = Struct.new(:prefix, :favor, keyword_init: true) do
48
- # Renders this plan as the keyword arguments a generator call accepts.
49
- #
50
- # @return [Hash{Symbol => Object}] `{ prefix:, favor: }`, the keyword arguments
51
- # `SequenceGenerator.generate` and `.trace` accept for this plan
52
- def generator_options = { prefix: prefix, favor: favor }
53
-
54
- # Answers whether this plan starts from a spliced corpus prefix.
55
- #
56
- # @return [Boolean] whether this plan starts from a spliced corpus prefix
57
- def spliced? = !prefix.nil?
58
- end
59
-
60
- attr_reader :corpus
61
-
62
- # @param splice_probability [Float, Integer] chance, per seed with a non-empty
63
- # corpus, of splicing a corpus entry's prefix instead of generating fresh
64
- # @param favor_count [Integer, Float] how many rare verbs to favor per plan; 0
65
- # or non-positive disables favoring entirely
66
- # @param corpus_limit [Integer] maximum corpus entries kept; the oldest is
67
- # dropped once this is exceeded
68
- # @param max_prefix_depth [Integer] maximum nested-prefix depth a new corpus
69
- # entry may reach before splicing stops extending it further
70
- def initialize(splice_probability:, favor_count:, corpus_limit: 64, max_prefix_depth: 6)
71
- @splice_probability = splice_probability.to_f
72
- @favor_count = favor_count.to_i
73
- @corpus_limit = corpus_limit
74
- @max_prefix_depth = max_prefix_depth
75
- @corpus = []
76
- @seen = Set.new
77
- @verb_hits = Hash.new(0)
78
- @declared_verbs = Set.new
79
- @seeds = 0
80
- @spliced = 0
81
- @seeds_with_new = 0
82
- end
83
-
84
- # Builds this seed's generation plan — a possible splice, plus which
85
- # verbs to favor.
86
- #
87
- # @param seed [Integer] the seed this plan is for; drives the splice-or-not
88
- # coin flip and, when splicing, which corpus entry and prefix length are drawn
89
- # @return [Fuzzing::CoverageCampaign::Plan] the plan `SequenceGenerator.generate`
90
- # or `.trace` should be run with for this seed
91
- def plan(seed)
92
- random = Random.new(seed)
93
- prefix = nil
94
- if !@corpus.empty? && random.rand < @splice_probability
95
- entry = @corpus[random.rand(@corpus.size)]
96
- prefix = entry[:spec].merge("steps" => random.rand(1..entry[:attempts]))
97
- end
98
- Plan.new(prefix: prefix, favor: rare_verbs)
99
- end
100
-
101
- # Folds one seed's generation results into the campaign's running
102
- # coverage, and admits its corpus entry if it reached anything new.
103
- #
104
- # @param seed [Integer] the seed this trace was generated from
105
- # @param plan [Fuzzing::CoverageCampaign::Plan] the plan that seed was generated with
106
- # @param trace [Fuzzing::SequenceGenerator::Trace] `SequenceGenerator.trace`'s answer:
107
- # `coverage` is `[[attempt_index, tuple], ...]`, `verbs` every verb the booted
108
- # catalog offered
109
- # @return [void]
110
- def record(seed, plan, trace)
111
- @seeds += 1
112
- @spliced += 1 if plan.spliced?
113
- @declared_verbs.merge(trace.verbs)
114
-
115
- new_at = trace.coverage.filter_map do |index, tuple|
116
- @verb_hits[tuple.split(" | ").first] += 1
117
- index if @seen.add?(tuple)
118
- end
119
- return if new_at.empty?
120
-
121
- @seeds_with_new += 1
122
- admit(seed, plan, new_at.max + 1)
123
- end
124
-
125
- # Counts the distinct coverage tuples this campaign has reached so far.
126
- #
127
- # @return [Integer] the number of distinct coverage tuples seen
128
- def tuples_seen = @seen.size
129
-
130
- # Renders the campaign's running totals as one human-readable line.
131
- #
132
- # @return [String] a one-line summary of tuples seen, seeds run, seeds that
133
- # reached something new, and how many were spliced from the corpus
134
- def summary
135
- "coverage: #{@seen.size} distinct (verb, kind, state, mutation, outcome) tuple(s) over #{@seeds} seed(s); " \
136
- "#{@seeds_with_new} seed(s) reached something new; #{@spliced} spliced from a corpus of #{@corpus.size}"
137
- end
138
-
139
- private
140
-
141
- def admit(seed, plan, attempts)
142
- return if depth(plan.prefix) + 1 > @max_prefix_depth
143
-
144
- spec = { "seed" => seed, "favor" => plan.favor }
145
- spec["prefix"] = plan.prefix if plan.prefix
146
- @corpus << { spec: spec, attempts: attempts }
147
- @corpus.shift while @corpus.size > @corpus_limit
148
- end
149
-
150
- def depth(prefix) = prefix.nil? ? 0 : 1 + depth(prefix["prefix"])
151
-
152
- # Never-hit declared verbs first (hits 0), then the least-hit, ties
153
- # broken by name so a plan is a pure function of what was recorded.
154
- def rare_verbs
155
- return [] unless @favor_count.positive?
156
-
157
- (@declared_verbs | @verb_hits.keys).min_by(@favor_count) { |verb| [@verb_hits[verb], verb] }
158
- end
159
- end
160
- end
161
- end
@@ -1,192 +0,0 @@
1
- require "json"
2
- require "open3"
3
- require_relative "replay"
4
- require_relative "properties"
5
- require_relative "self_consistency"
6
- require_relative "rust_gap_manifest"
7
- require_relative "nondeterministic"
8
-
9
- module Hecks
10
- module Fuzzing
11
- # The Ruby-vs-Rust comparison of one generated sequence — moved here,
12
- # unchanged, out of `bin/qa_sweep`'s own `diff_ruby_vs_rust` so a second
13
- # caller (`bin/qa_generated_domains --rust`, which compares domains the
14
- # generator wrote rather than ones on the rotation) runs the same
15
- # comparison rather than a re-derived one. `bin/qa_sweep` delegates to
16
- # it; its own comments on the known-gap filtering, the wire-precision
17
- # reduction and the deep copy taken before `strip_emitted_flags!` apply
18
- # here word for word.
19
- #
20
- # `differ` is anything answering `RustConformanceHelpers`' comparison
21
- # helpers (spec/support/rust_conformance_helpers.rb) plus a
22
- # `structural_skips` set — duck-typed, so lib never requires spec/.
23
- # Which query verbs may diverge is not the differ's call: it is read
24
- # off the binary's own manifest.json (`manifest_partition`, below).
25
- #
26
- # Returns one divergence list per active mode — `{ differential: [...],
27
- # self_consistency: [...], properties_in_differential: [...],
28
- # adapter_parity_sqlite: [...] }`, keys present only for the modes in
29
- # `modes:` (`differential` always). `adapter_parity_sqlite:` is the
30
- # caller's own callable, because which two adapters that mode pairs is
31
- # the caller's dial, not this module's.
32
- module Differential
33
- module_function
34
-
35
- # The one place a Ruby/Rust query divergence may be tolerated — and
36
- # only for a verb `gaps` (a `RustGapManifest`) declares not generated.
37
- # Rust refuses such a verb outright while Ruby answers it for real
38
- # (or refuses it for its own business reason), so both sides' rows for
39
- # that verb leave the comparison. Everything else stays in: a refusal
40
- # the manifest doesn't account for — however its message is worded —
41
- # is a real divergence.
42
- #
43
- # `skipped` is every tolerated verb this history actually reached (for
44
- # the sweep's structural_skip_report). `stale` is a divergence per
45
- # tolerated verb Rust nonetheless answered with a query row: the
46
- # manifest says "not generated" but the binary disagrees, so the
47
- # tolerance itself is wrong and must not silently hold.
48
- # @param gaps [Hecks::Fuzzing::RustGapManifest] the compiled binary's manifest
49
- # @param ruby_refusals [Array<Hash>] Ruby's own refusal rows for this history
50
- # @param rust_refusals [Array<Hash>] the Rust binary's own refusal rows
51
- # @param ruby_queries [Array<Hash>] Ruby's own query rows for this history
52
- # @param rust_queries [Array<Hash>] the Rust binary's own query rows
53
- # @return [Hash] `ruby_refusals:`/`rust_refusals:`/`ruby_queries:`/
54
- # `rust_queries:` (each with tolerated verbs removed), `skipped:` (a
55
- # `Set<String>` of every tolerated verb this history actually reached),
56
- # and `stale:` (an `Array<Hash>` divergence per tolerated verb the Rust
57
- # binary nonetheless answered)
58
- def manifest_partition(gaps, ruby_refusals:, rust_refusals:, ruby_queries:, rust_queries:)
59
- verb_of = ->(row) { row.key?("verb") ? row["verb"] : row["query"] }
60
- declared = ->(row) { gaps.not_generated?(verb_of.call(row)) }
61
- stale = rust_queries.select(&declared).map do |row|
62
- { field: "manifest", verb: row["query"],
63
- detail: "#{row['query']} is declared generated: false in manifest.json " \
64
- "(#{gaps.not_generated(row['query']).values_at('gap_class', 'construct').join('/')}), " \
65
- "but the Rust binary answered it — regenerate with bin/project_rust" }
66
- end
67
- reached = (ruby_refusals + rust_refusals + ruby_queries).select(&declared)
68
- { ruby_refusals: ruby_refusals.reject(&declared), rust_refusals: rust_refusals.reject(&declared),
69
- ruby_queries: ruby_queries.reject(&declared), rust_queries: rust_queries.reject(&declared),
70
- skipped: reached.to_set(&verb_of), stale: stale }
71
- end
72
-
73
- # Runs the standard property battery and reports every one that failed.
74
- #
75
- # @param history [Hash] a replayed history as returned by `Replay.call`
76
- # @return [Array<Hash>] one `{field:, detail:}` entry per failed property
77
- def property_divergences(history)
78
- Properties.check(history).reject { |_, result| result == true }
79
- .map { |name, message| { field: name.to_s, detail: message } }
80
- end
81
-
82
- # Flattens `history[:self_consistency]`'s own per-check divergence lists.
83
- #
84
- # @param history [Hash] a replayed history as returned by `Replay.call`,
85
- # optionally carrying `:self_consistency` (`Replay.call`'s own
86
- # `self_consistency: true`)
87
- # @return [Array<Hash>] every self-consistency divergence recorded; empty if
88
- # `history` carries no `:self_consistency`
89
- def self_consistency_divergences(history)
90
- return [] unless history[:self_consistency]
91
-
92
- history[:self_consistency].values.flatten(1)
93
- end
94
-
95
- # rubocop:disable-next Metrics/AbcSize
96
- # rubocop:disable-next Metrics/CyclomaticComplexity
97
- # rubocop:disable-next Metrics/PerceivedComplexity
98
- # rubocop:disable-next Metrics/MethodLength
99
- # @param differ [Object] duck-typed comparison helper answering
100
- # `RustConformanceHelpers`' interface (adapter-defined)
101
- # @param domain_path [String] path to the domain directory to replay
102
- # @param steps [Array<Hash>] the step sequence to replay on both engines
103
- # @param binary [String] path to the compiled Rust conformance binary to run
104
- # @param modes [Array<Symbol>] which comparisons to run — any of
105
- # `:differential` (always run), `:self_consistency`,
106
- # `:properties_in_differential`, `:adapter_parity_sqlite`
107
- # @param adapter_parity_sqlite [Proc, nil] callable answering the
108
- # `:adapter_parity_sqlite` mode's own comparison, or `nil` to skip it
109
- # even when named in `modes`
110
- # @return [Hash{Symbol => Array<Hash>}] one divergence list per active mode,
111
- # keyed by the mode name; `:differential` is always present
112
- def diff(differ, domain_path, steps, binary, modes:, adapter_parity_sqlite: nil)
113
- self_consistency = modes.include?(:self_consistency)
114
- ruby_result = Replay.call(domain_path, steps, self_consistency: self_consistency)
115
- ruby_instances = JSON.parse(JSON.generate(ruby_result[:instances]))
116
- ruby_events = JSON.parse(JSON.generate(ruby_result[:events]))
117
- ruby_refusals = ruby_result[:refusals].map do |r|
118
- { "verb" => r[:verb].to_s, "kind" => r[:kind].to_s.split("::").last, "error" => r[:error] }
119
- end
120
- ruby_queries = JSON.parse(JSON.generate(ruby_result[:queries].map { |row| Nondeterministic.strip(row, :query_row) }))
121
- ruby_sagas = JSON.parse(JSON.generate(ruby_result[:sagas]))
122
- ruby_dry_runs = ruby_result[:dry_runs].map { |d| { "verb" => d[:verb].to_s, "ok" => d[:ok] } }
123
-
124
- outcomes = {}
125
- outcomes[:properties_in_differential] = property_divergences(ruby_result) if modes.include?(:properties_in_differential)
126
- if modes.include?(:adapter_parity_sqlite) && adapter_parity_sqlite
127
- outcomes[:adapter_parity_sqlite] = adapter_parity_sqlite.call
128
- end
129
-
130
- stdout, status = Open3.capture2(binary, stdin_data: JSON.generate({ "steps" => steps }))
131
- unless status.success?
132
- return outcomes.merge(differential: [{ field: "process",
133
- detail: "rust binary exited #{status.exitstatus}: #{stdout}" }])
134
- end
135
-
136
- rust_output = JSON.parse(stdout)
137
- rust_live_instances = JSON.parse(JSON.generate(rust_output["instances"])) if self_consistency
138
- differ.strip_emitted_flags!(rust_output["instances"])
139
- differ.strip_emitted_flags!(rust_output["queries"])
140
- differ.strip_occurred_at!(rust_output["events"])
141
-
142
- divergences = []
143
- divergences << { field: "instances", ruby: ruby_instances, rust: rust_output["instances"] } \
144
- unless rust_output["instances"] == ruby_instances
145
- divergences << { field: "events", ruby: ruby_events, rust: rust_output["events"] } \
146
- unless rust_output["events"] == ruby_events
147
-
148
- kept = manifest_partition(RustGapManifest.for_binary(binary),
149
- ruby_refusals: ruby_refusals, rust_refusals: rust_output["refusals"],
150
- ruby_queries: ruby_queries, rust_queries: rust_output["queries"])
151
- differ.structural_skips.merge(kept[:skipped])
152
- divergences.concat(kept[:stale])
153
-
154
- by_kind = ->(r) { r.slice("verb", "kind") }
155
- rust_refusals = kept[:rust_refusals].map(&by_kind)
156
- kept_ruby_refusals = kept[:ruby_refusals].map(&by_kind)
157
- divergences << { field: "refusals", ruby: kept_ruby_refusals, rust: rust_refusals } \
158
- unless rust_refusals == kept_ruby_refusals
159
-
160
- wordless = ->(row) { differ.reduce_to_wire_precision(row.except("error", "reference_error")) }
161
- rust_queries = kept[:rust_queries].map(&wordless)
162
- kept_ruby_queries = kept[:ruby_queries].map(&wordless)
163
- divergences << { field: "queries", ruby: kept_ruby_queries, rust: rust_queries } \
164
- unless rust_queries == kept_ruby_queries
165
-
166
- divergences << { field: "sagas", ruby: ruby_sagas, rust: rust_output["sagas"] } \
167
- unless rust_output["sagas"] == ruby_sagas
168
-
169
- cross_domain = differ.cross_domain_policy_names(rust_output)
170
- kept_ruby_reactions = JSON.parse(JSON.generate(ruby_result[:reactions]))
171
- .reject { |r| cross_domain.include?(r["policy"]) }
172
- rust_reactions = rust_output.fetch("reactions")
173
- divergences << { field: "reactions", ruby: kept_ruby_reactions, rust: rust_reactions } \
174
- unless rust_reactions == kept_ruby_reactions
175
-
176
- rust_dry_runs = Array(rust_output["dry_runs"]).map { |d| d.slice("verb", "ok") }
177
- divergences << { field: "dry_runs", ruby: ruby_dry_runs, rust: rust_dry_runs } \
178
- unless rust_dry_runs == ruby_dry_runs
179
- outcomes[:differential] = divergences
180
-
181
- if self_consistency
182
- outcomes[:self_consistency] =
183
- self_consistency_divergences(ruby_result) +
184
- SelfConsistency.check_rust_rehydration(binary, differ, rust_live_instances) +
185
- SelfConsistency.check_rust_idempotency(binary, differ, rust_live_instances)
186
- end
187
-
188
- outcomes
189
- end
190
- end
191
- end
192
- end