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
data/lib/hecks/corpus.rb DELETED
@@ -1,479 +0,0 @@
1
- require_relative "fuzzing/target_capabilities"
2
-
3
- module Hecks
4
- # **The corpus, discovered** — every place in this repo that holds a real
5
- # domain, named once.
6
- #
7
- # One table here, rather than each consumer spelling out its own copy —
8
- # spec/corpus_spec.rb, spec/model_check_spec.rb, bin/model_check,
9
- # spec/parser_parity_spec.rb, bin/fuzz, Fuzzing::CombinationMiner all did,
10
- # and the copies had already drifted: only the model checker saw
11
- # `qa/bluebook`, only parser parity saw `spec/fixtures`, and nothing but
12
- # bin/fuzz's own sweep ever saw `qa/stress_domains`. Each consumer names
13
- # the `KINDS` it walks rather than re-deriving where those kinds live.
14
- #
15
- # Plain Dir/File only — bin/ scripts require this before (or without)
16
- # booting anything.
17
- module Corpus
18
- ROOT = File.expand_path("../..", __dir__).freeze
19
-
20
- Member = Struct.new(:stem, :kind, :path)
21
-
22
- # **One domain per directory** — its bluebooks sit in `<dir>/bluebook/`
23
- # or directly in `<dir>` (see `bluebook_files`). Stemmed by directory.
24
- DIRECTORY_KINDS = {
25
- example: "examples/*",
26
- stress: "qa/stress_domains/*",
27
- semantics: "spec/corpus/semantics/domains/*",
28
- # A PACKAGE VENDORED VIA `uses_embryonaut_bluebook` — same real
29
- # mechanism `uses_framework` is (`lib/hecks/embryonaut_bluebook.rb`'s
30
- # own header: "same shape as Framework"), one level further out: not
31
- # a chapter shipped inside THIS gem's own `lib/hecks/framework/
32
- # bluebook/` (the `:framework` FILE_KIND, below), but a directory
33
- # nested inside the CONSUMING example's own checkout
34
- # (`<domain>/vendor/embryonaut_bluebooks/<name>/bluebook/`,
35
- # EmbryonautBluebook.load!'s own resolution path) — hence its own
36
- # DIRECTORY_KIND rather than reuse of `:framework`'s shape. First
37
- # real member: docs/decisions/0058's own
38
- # examples/embryonaut_vendoring_demo/vendor/embryonaut_bluebooks/widgets.
39
- vendored: "examples/*/vendor/embryonaut_bluebooks/*"
40
- }.freeze
41
-
42
- # **One chapter per file**. Stemmed by the path below the glob's fixed
43
- # prefix, so a nested fixture keeps its subdirectory (`eras/base`)
44
- # and never collides with a same-named file elsewhere in the kind.
45
- FILE_KINDS = {
46
- grammar: "lib/hecks/grammar/*.bluebook",
47
- framework: "lib/hecks/framework/bluebook/*.bluebook",
48
- qa: "qa/bluebook/*.bluebook",
49
- language: "lib/hecks/language/**/*.bluebook",
50
- deploy: "lib/hecks/deploy/bluebook/*.bluebook",
51
- tenancy: "lib/hecks/tenancy/bluebook/*.bluebook",
52
- fixture: "spec/fixtures/**/*.bluebook"
53
- }.freeze
54
-
55
- KINDS = (DIRECTORY_KINDS.keys + FILE_KINDS.keys).freeze
56
-
57
- # Where a bluebook the sweep does not boot goes instead. Not a filter:
58
- # nothing leaves `sweepable_domains` without naming the check that owns
59
- # it, and spec/corpus_accounting_spec.rb proves each destination exists
60
- # and actually exercises what is routed to it.
61
- #
62
- # check: :named_in — `destination` names every routed file
63
- # (`names: :each_file`) or the given text
64
- # check: :gitignored — `destination` holds the ignore rule `names`,
65
- # and nothing matching is committed
66
- # check: :gap — no check exercises these yet. Listed so the
67
- # gap is visible; the accounting spec keeps it
68
- # pending and fails the moment one appears.
69
- #
70
- # Ordered — a bluebook belongs to the first route it matches, so a
71
- # specific destination sits above the catch-all for its shape.
72
- Route = Struct.new(:pattern, :check, :destination, :names, :why)
73
-
74
- ROUTES = [
75
- Route.new(%r{\Atmp/}, :gitignored, ".gitignore", "tmp/",
76
- "scratch output — generated and mined candidate domains, fuzz failures — never committed"),
77
- Route.new(%r{/\.aws-sam/}, :gitignored, ".gitignore", ".aws-sam/",
78
- "SAM build output — a vendored copy of real sources, never committed"),
79
- Route.new(%r{/data/eras/}, :gitignored, ".gitignore", "**/data/eras/",
80
- "era snapshots a file adapter writes at runtime, never committed"),
81
- Route.new(%r{\Arust/}, :named_in, "rust/parser/tests/gates.rs", :each_file,
82
- "the Rust parser's own fixtures, each loaded by its gate tests"),
83
- Route.new(%r{/translations/}, :named_in, "spec/translation/committed_edges_spec.rb", "bluebook/translations",
84
- "translation edges, not chapters; each must load, chain era to era, and end at the storage " \
85
- "shape its bluebook declares today"),
86
- Route.new(%r{\Alib/hecks/forms/examples/}, :named_in, "spec/forms/app_spec.rb", :each_file,
87
- "a Forms presentation config wearing the .bluebook extension"),
88
- Route.new(%r{\Aspec/fixtures/eras/}, :named_in, "spec/runtime/storage_shape_spec.rb", "fixtures/eras",
89
- "deliberately conflicting versions of one domain; each must classify as the verdict " \
90
- "its filename declares (bump_* / same_*)"),
91
- Route.new(%r{\Aspec/fixtures/model_check/}, :named_in, "spec/model_check_spec.rb", :each_file,
92
- "domains broken on purpose; each must produce exactly the finding kinds it is built to trigger"),
93
- # bin/fuzz sweeps it too, once Fuzzing::Replay coerces value-object
94
- # args before recomputing givens — today its oracle reads raw args
95
- # while dispatch coerces, so checkout_fixture's VO-reading given
96
- # reads as wrongly admitted.
97
- Route.new(%r{\Aspec/fixtures/rust_host/}, :named_in, "rust/host/src/web.rs", "checkout_fixture",
98
- "the Rust host's checkout fixture, pinned by its web and /api tests")
99
- ].freeze
100
-
101
- module_function
102
-
103
- # Lists corpus members of the given kinds (every kind, by default).
104
- #
105
- # @param kinds [Array<Symbol>] corpus kinds to include, from `KINDS`; every kind
106
- # when empty
107
- # @param root [String] repository root to search under
108
- # @return [Array<Member>] matching members
109
- # @raise [ArgumentError] if `kinds` names a kind not in `KINDS`
110
- def members(*kinds, root: ROOT)
111
- kinds = KINDS if kinds.empty?
112
- kinds.flat_map do |kind|
113
- if (glob = DIRECTORY_KINDS[kind])
114
- Dir.glob(File.join(root, glob)).select { |path| File.directory?(path) }.sort
115
- .map { |dir| Member.new(File.basename(dir), kind, dir) }
116
- elsif (glob = FILE_KINDS[kind])
117
- prefix = File.join(root, glob[/\A[^*]*/].chomp("/"))
118
- Dir.glob(File.join(root, glob))
119
- .map { |file| Member.new(file.delete_prefix("#{prefix}/").delete_suffix(".bluebook"), kind, file) }
120
- else
121
- raise ArgumentError, "unknown corpus kind #{kind.inspect} — known: #{KINDS.join(', ')}"
122
- end
123
- end
124
- end
125
-
126
- # What a boot loads for a member: a directory kind's bluebook
127
- # directory, a file kind's own file. `nil` for a directory holding no
128
- # bluebook at all.
129
- #
130
- # @param member [Member] the corpus member
131
- # @return [String, nil] the path to boot, or nil when a directory member holds
132
- # no bluebook
133
- def source_of(member)
134
- DIRECTORY_KINDS.key?(member.kind) ? bluebook_dir(member.path) : member.path
135
- end
136
-
137
- # Where a domain path keeps its bluebooks — `<domain>/bluebook/*.bluebook`
138
- # (every example and stress domain), or the directory itself
139
- # (`qa/bluebook`). `nil` when neither holds a bluebook.
140
- #
141
- # @param domain_path [String] path to a domain directory
142
- # @return [Array<String>, nil] `.bluebook` file paths found, or nil when none
143
- def bluebook_files(domain_path)
144
- [File.join(domain_path, "bluebook"), domain_path].each do |dir|
145
- files = Dir[File.join(dir, "*.bluebook")]
146
- return files unless files.empty?
147
- end
148
- nil
149
- end
150
-
151
- # The directory holding `domain_path`'s bluebooks.
152
- #
153
- # @param domain_path [String] path to a domain directory
154
- # @return [String, nil] directory of the first `.bluebook` file found, or nil
155
- # when `domain_path` holds none
156
- def bluebook_dir(domain_path)
157
- files = bluebook_files(domain_path)
158
- files && File.dirname(files.first)
159
- end
160
-
161
- # The route a repo-relative path takes instead of the sweep — the
162
- # first one it matches — or `nil` when the sweep boots it.
163
- #
164
- # @param relative_path [String] a corpus member's path, relative to the repo root
165
- # @return [Route, nil] the first matching route, or nil when the sweep boots it
166
- def route_for(relative_path)
167
- ROUTES.find { |route| route.pattern.match?(relative_path) }
168
- end
169
-
170
- # What bin/model_check and spec/model_check_spec.rb walk — every kind,
171
- # less the language (examined as one judged chapter, not file by file)
172
- # and deploy chapters (the SAM projector's own inputs), and less any
173
- # member a route already sends to a destination of its own: the
174
- # broken-on-purpose model_check fixtures must produce their findings
175
- # there, so a clean-corpus gate here would be the wrong check for them.
176
- MODEL_CHECK_KINDS = %i[example grammar framework vendored qa stress fixture].freeze
177
-
178
- # Every corpus member `bin/model_check` and `spec/model_check_spec.rb` walk.
179
- #
180
- # @param root [String] repository root to search under
181
- # @return [Array<Member>] members of `MODEL_CHECK_KINDS`, less any routed elsewhere
182
- def model_check_members(root: ROOT)
183
- members(*MODEL_CHECK_KINDS, root: root).reject { |member| route_for(member.path.delete_prefix("#{root}/")) }
184
- end
185
-
186
- # **The ledger sweeps itself** — its own chapter is a domain like any
187
- # other, and the one rotation member that is neither an example nor a
188
- # stress domain.
189
- ROTATION_LEDGER = { "quality_control" => "qa/bluebook" }.freeze
190
-
191
- # What the QA rotation is made of — every example and stress domain
192
- # this repository owns, plus the ledger, as `reference => repo-relative
193
- # path`: exactly the shape `Target.path` is stored in.
194
- #
195
- # Derived, because the hand-kept version silently went stale.
196
- # `bin/qa_seed_targets` carried a literal list naming three of the
197
- # thirteen stress domains; the other ten were authored, argued for in
198
- # their own NOTES.md, several promoted by `bin/qa_generated_domains
199
- # --promote` — and never swept once, because a `Target` row is what
200
- # puts a domain in the rotation and nothing tied that list to the
201
- # corpus. Promotion only ever printed the `target.identify` line for a
202
- # human to run.
203
- #
204
- # @param root [String] repository root to search under
205
- # @return [Hash{String => String}] each rotation member's stem/reference mapped
206
- # to its repo-relative path
207
- def rotation_targets(root: ROOT)
208
- members(:example, :stress, root: root)
209
- .to_h { |member| [member.stem, member.path.delete_prefix("#{root}/")] }
210
- .merge(ROTATION_LEDGER)
211
- end
212
-
213
- # Every bootable domain in the project, not a hand-kept list — any
214
- # directory holding a `.bluebook` no route sends elsewhere, a
215
- # `bluebook/` folder standing for the domain directory around it.
216
- #
217
- # @param root [String] repository root to search under
218
- # @return [Array<String>] absolute paths of every sweepable domain directory
219
- def sweepable_domains(root = ROOT)
220
- Dir.chdir(root) do
221
- Dir.glob("**/*.bluebook")
222
- .reject { |path| route_for(path) }
223
- .map { |path| File.dirname(path) }
224
- .map { |dir| File.basename(dir) == "bluebook" ? File.dirname(dir) : dir }
225
- .uniq.sort
226
- .map { |dir| File.join(root, dir) }
227
- end
228
- end
229
-
230
- # The domain directory a member stands for, spelled the way
231
- # `sweepable_domains` spells it: a `bluebook/` folder is its parent.
232
- #
233
- # @param member [Member] the corpus member
234
- # @return [String] the member's owning domain directory path
235
- def domain_dir_of(member)
236
- dir = File.directory?(member.path) ? member.path : File.dirname(member.path)
237
- File.basename(dir) == "bluebook" ? File.dirname(dir) : dir
238
- end
239
-
240
- # ── The Rust-facing corpus ─────────────────────────────────────────
241
- #
242
- # Every Rust-facing list (the fuzz bridge, the codegen drift check,
243
- # rust coverage, codegen parity) reads `rust/Cargo.toml`'s `[features]`
244
- # as its one source, rather than being typed out by hand per consumer
245
- # and drifting from it — hand-typed, fuzzing once saw only 8 of 20
246
- # features. The `rust/src/generated/` modules split into buckets, and
247
- # spec/corpus_rust_spec.rb proves every Cargo feature and every
248
- # generated module lands in exactly one of them:
249
- #
250
- # rust_domains an in-repo domain directory with a Cargo feature
251
- # of its own: fuzzed, regenerated, coverage- and
252
- # parity-checked
253
- # framework chapters no feature and no merged.rs; written as a side
254
- # effect of every `uses_framework` domain's regen
255
- # RUST_ELSEWHERE a feature with no in-repo domain directory, and
256
- # the check that owns it instead
257
- GENERATED_DIR = "rust/src/generated".freeze
258
- RUST_DOMAIN_KINDS = %i[example stress fixture].freeze
259
-
260
- RustDomain = Struct.new(:feature, :dir, :kind)
261
-
262
- # `check: :named_in` — `destination` must name `names`.
263
- Elsewhere = Struct.new(:check, :destination, :names, :why)
264
-
265
- RUST_ELSEWHERE = {
266
- "meta" => Elsewhere.new(:named_in, "spec/codegen_parity_spec.rb", "bluebook_language",
267
- "the self-hosted grammar (lib/hecks/language), not a domain directory — every " \
268
- "bin/project_rust run rewrites it (so the drift check diffs it), codegen parity " \
269
- "checks it as bluebook_language, and there is no directory to fuzz")
270
- }.freeze
271
-
272
- # Every place a Rust-facing domain's own `uses_framework`/
273
- # `uses_embryonaut_bluebook` attachment could be declared — every
274
- # in-repo Rust domain's own hecksagon files.
275
- #
276
- # @param root [String] repository root to search under
277
- # @return [String] every reachable hecksagon file's own text, joined by newlines
278
- def rust_attachment_hecksagon_text(root: ROOT)
279
- rust_domains(root: root).flat_map { |domain| Dir.glob(File.join(domain.dir, "**", "*.hecksagon")) }
280
- .map { |path| File.read(path) }.join("\n")
281
- end
282
-
283
- # **Shrink-only**. A generated module `bin/rust_coverage` still reports a
284
- # gap for. `bin/corpus --rust-coverage` requires each of these to
285
- # still fail, so an entry that starts passing breaks the build until
286
- # it is deleted here.
287
- RUST_COVERAGE_PENDING = {}.freeze
288
-
289
- # The Cargo `[features]` table's raw text.
290
- #
291
- # @param root [String] repository root to search under
292
- # @return [String] the table's text, or `""` when `rust/Cargo.toml` has none
293
- def cargo_features_table(root: ROOT)
294
- File.read(File.join(root, "rust/Cargo.toml"))[Fuzzing::TargetCapabilities::FEATURES_TABLE] || ""
295
- end
296
-
297
- # Every feature name Cargo declares with no dependency array of its own.
298
- #
299
- # @param root [String] repository root to search under
300
- # @return [Array<String>] feature names, in file order
301
- def cargo_features(root: ROOT)
302
- cargo_features_table(root: root).scan(/^(\w+)\s*=\s*\[\]/).flatten
303
- end
304
-
305
- # The feature `bin/project_rust` last wrote as Cargo's `default`.
306
- #
307
- # @param root [String] repository root to search under
308
- # @return [String, nil] the default feature's name, or nil when Cargo.toml
309
- # declares none
310
- def cargo_default(root: ROOT)
311
- cargo_features_table(root: root)[/^default\s*=\s*\["(\w+)"\]/, 1]
312
- end
313
-
314
- # Every in-repo domain directory whose name is a Cargo feature, sorted
315
- # by path. When the module is already generated, its metadata.rs stamp
316
- # decides which directory it came from — a directory name alone is not
317
- # enough: spec/fixtures/qa_discover_external_domains vendors a second
318
- # `examples/pizzas` that no Cargo feature was ever generated from.
319
- #
320
- # @param root [String] repository root to search under
321
- # @return [Array<RustDomain>] each Rust-facing domain, sorted by directory path
322
- def rust_domains(root: ROOT)
323
- features = cargo_features(root: root)
324
- members(*RUST_DOMAIN_KINDS, root: root)
325
- .map { |member| [domain_dir_of(member), member.kind] }
326
- .uniq(&:first)
327
- .select { |dir, _| rust_domain_dir?(dir, features, root) }
328
- .sort_by(&:first)
329
- .map { |dir, kind| RustDomain.new(File.basename(dir).downcase, dir, kind) }
330
- end
331
-
332
- # Tells whether `dir` is the real source directory for its Cargo feature.
333
- #
334
- # @param dir [String] a candidate domain directory
335
- # @param features [Array<String>] known Cargo feature names
336
- # @param root [String] repository root `dir` is rooted under
337
- # @return [Boolean]
338
- def rust_domain_dir?(dir, features, root)
339
- feature = File.basename(dir).downcase
340
- source = generated_source(feature, root: root)
341
- features.include?(feature) && (source.nil? || source == dir.delete_prefix("#{root}/"))
342
- end
343
-
344
- # Where a generated module came from, read off the stamp bin/project_rust
345
- # writes into its metadata.rs — `examples/pizzas`, `/abs/path/embryonaut`,
346
- # `the self-hosted language (lib/hecks/language/bluebook)`, with any
347
- # ` (uses_framework "X")` or ` (uses_embryonaut_bluebook "X")` suffix
348
- # dropped. `nil` when not generated.
349
- SOURCE_STAMP = %r{
350
- GENERATED\ by\ bin/project_rust\ —\ (.+?)
351
- (?:\ \((?:uses_framework|uses_embryonaut_bluebook)\ "\w+"\))?
352
- 's\ own\ canonical\ IR,
353
- }x
354
-
355
- # Where a generated module came from, read off the stamp `bin/project_rust`
356
- # writes into its metadata.rs.
357
- #
358
- # @param module_name [String] a generated module's name
359
- # @param root [String] repository root to search under
360
- # @return [String, nil] the source path the stamp names, or nil when the module
361
- # is not generated
362
- def generated_source(module_name, root: ROOT)
363
- metadata = File.join(root, GENERATED_DIR, module_name, "metadata.rs")
364
- File.file?(metadata) ? File.read(metadata)[SOURCE_STAMP, 1] : nil
365
- end
366
-
367
- # Every module directory under `rust/src/generated`.
368
- #
369
- # @param root [String] repository root to search under
370
- # @return [Array<String>] generated module names, sorted
371
- def generated_modules(root: ROOT)
372
- Dir.children(File.join(root, GENERATED_DIR)).select { |name| File.directory?(File.join(root, GENERATED_DIR, name)) }.sort
373
- end
374
-
375
- # Tells whether `feature` has already been generated (has a merged.rs).
376
- #
377
- # @param feature [String] a Cargo feature / domain name
378
- # @param root [String] repository root to search under
379
- # @return [Boolean]
380
- def generated?(feature, root: ROOT)
381
- File.file?(File.join(root, GENERATED_DIR, feature, "merged.rs"))
382
- end
383
-
384
- # Chapters attached via `kind` (`:framework` or `:vendored`) that
385
- # `bin/project_rust` writes a module for as a side effect of the
386
- # attaching domain — a module, no merged.rs of its own. Both kinds
387
- # share this shape: `bin/project_rust`'s own generated module name is
388
- # always `chapter_name.downcase` (rust/src/generated/mod.rs's own
389
- # convention), and for either kind that downcased name is always the
390
- # original lowercase directory stem again — confirmed live for a
391
- # framework member and for a vendored package alike (a vendored
392
- # package's own bluebook stem doesn't have to equal its declared
393
- # chapter name the way a framework member's does — `EmbryonautBluebook.
394
- # load!` resolves it as `Naming.pascal(dir_name)`, e.g. "widgets" ->
395
- # "Widgets" — but downcasing a `Naming.pascal`-derived name always
396
- # returns the original lowercase stem again).
397
- #
398
- # @param kind [Symbol] :framework or :vendored
399
- # @param root [String] repository root to search under
400
- # @return [Array<String>] stems with a generated module but no merged.rs
401
- # of their own
402
- def rust_side_chapters(kind, root: ROOT)
403
- modules = generated_modules(root: root)
404
- members(kind, root: root).map(&:stem)
405
- .select do |stem|
406
- module_name = Naming.pascal(stem).downcase
407
- modules.include?(module_name) && !generated?(module_name, root: root)
408
- end
409
- end
410
-
411
- # The generated module name a `:framework`/`:vendored` stem writes
412
- # under — `Naming.pascal(stem).downcase` (`bin/project_rust`'s own
413
- # convention, see `rust_side_chapters`'s header); a chapter whose
414
- # stem holds an underscore ("console_settings") writes to a
415
- # different, underscore-free module name ("consolesettings"), so a
416
- # bucket-membership check against `generated_modules` (a directory
417
- # name) needs this mapping rather than the raw stem
418
- # `rust_framework_chapters`/`rust_vendored_chapters` still return
419
- # (every other caller wants the stem, to build a bluebook file path).
420
- #
421
- # @param stem [String] a `:framework`/`:vendored` corpus member's stem
422
- # @return [String] the generated module's own directory name
423
- def rust_side_module_name(stem)
424
- Naming.pascal(stem).downcase
425
- end
426
-
427
- def rust_framework_chapters(root: ROOT)
428
- rust_side_chapters(:framework, root: root)
429
- end
430
-
431
- def rust_vendored_chapters(root: ROOT)
432
- rust_side_chapters(:vendored, root: root)
433
- end
434
-
435
- # Chapters an in-repo Rust domain carries beside its own bluebook (a
436
- # sibling `.bluebook` in the domain's directory) that `bin/project_rust`
437
- # writes a module for as a side effect — a module, no merged.rs of its
438
- # own. Today: `payments`, beside spec/fixtures/rust_host/checkout_fixture.
439
- #
440
- # @param root [String] repository root to search under
441
- # @return [Array<String>] generated module names with no merged.rs of their own
442
- def rust_sibling_chapters(root: ROOT)
443
- modules = generated_modules(root: root)
444
- rust_domains(root: root)
445
- .flat_map { |domain| Dir.glob(File.join(domain.dir, "bluebook", "*.bluebook")) }
446
- .map { |path| Naming.pascal(File.basename(path, ".bluebook")).downcase }
447
- .uniq
448
- .select { |name| modules.include?(name) && !generated?(name, root: root) }
449
- end
450
-
451
- # THE REGENERATION ORDER the drift check runs. Sorted by path, so
452
- # which domain runs last — and so wins Cargo's `default`, mod.rs's cfg
453
- # comments and the shared framework modules' attribution stamp — is a
454
- # fact of the sorted list, not a hand-picked order: today that is
455
- # `has_many_fixture`. spec/corpus_rust_spec.rb pins last == default.
456
- #
457
- # @param root [String] repository root to search under
458
- # @return [Array<RustDomain>] already-generated Rust domains, in regeneration order
459
- def rust_regen_order(root: ROOT)
460
- rust_domains(root: root).select { |domain| generated?(domain.feature, root: root) }
461
- end
462
-
463
- # The chapter a bluebook declares — `Hecks.bluebook "<Name>"`, read
464
- # off the file rather than guessed from its name (a grammar chapter's
465
- # file is named after its role, `aggregate.bluebook`, while its
466
- # chapter is always "Bluebook"). Scans the whole file: a framework
467
- # member's header comment can run past any fixed line cap.
468
- #
469
- # @param bluebook_path [String, Array<String>] a `.bluebook` file path, or an
470
- # array whose first element is used
471
- # @return [String, nil] the declared chapter name, or nil when the file never
472
- # declares one
473
- def chapter_name_of(bluebook_path)
474
- bluebook_path = Array(bluebook_path).first
475
- header = File.foreach(bluebook_path).find { |line| line =~ /\A\s*Hecks\.bluebook\s+"([^"]+)"/ }
476
- header && Regexp.last_match(1)
477
- end
478
- end
479
- end