hecks 2.9.0 → 3.0.1

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 (600) hide show
  1. checksums.yaml +4 -4
  2. data/exe/hecks +51 -6
  3. data/lib/hecks/adapters/driven/heki/journal.rb +1 -1
  4. data/lib/hecks/adapters/driven/lambda/client.rb +1 -1
  5. data/lib/hecks/adapters/driven/lambda.rb +1 -1
  6. data/lib/hecks/adapters/driven/memory.rb +16 -3
  7. data/lib/hecks/adapters/driven/postgres/reconnect.rb +6 -2
  8. data/lib/hecks/adapters/driven/postgres/shared_connection.rb +180 -0
  9. data/lib/hecks/adapters/driven/postgres.rb +13 -4
  10. data/lib/hecks/adapters/driven/prism.rb +41 -8
  11. data/lib/hecks/adapters/driven/tenant_provisioner.rb +73 -16
  12. data/lib/hecks/behaviors/expectations.rb +1 -1
  13. data/lib/hecks/behaviors.rb +1 -1
  14. data/lib/hecks/bench/cli.rb +62 -0
  15. data/lib/hecks/bench/environment.rb +71 -0
  16. data/lib/hecks/bench/postgres_probe.rb +30 -0
  17. data/lib/hecks/bench/report.rb +76 -0
  18. data/lib/hecks/bench/ruby_runner.rb +93 -0
  19. data/lib/hecks/bench/run.rb +55 -0
  20. data/lib/hecks/bench/rust_runner.rb +101 -0
  21. data/lib/hecks/bench/stats.rb +46 -0
  22. data/lib/hecks/bench/suite.rb +98 -0
  23. data/lib/hecks/bench/workload.rb +89 -0
  24. data/lib/hecks/bench.rb +17 -0
  25. data/lib/hecks/bluebook/assembly/contracts.rb +2 -0
  26. data/lib/hecks/bluebook/attribute.rb +1 -1
  27. data/lib/hecks/bluebook/behaviour/aggregate.rb +30 -1
  28. data/lib/hecks/bluebook/behaviour/domain_port.rb +7 -0
  29. data/lib/hecks/bluebook/chapter.rb +6 -2
  30. data/lib/hecks/bluebook/domain_port.rb +36 -5
  31. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +10 -0
  32. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +8 -0
  33. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -1
  34. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +31 -3
  35. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +122 -3
  36. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +1 -1
  37. data/lib/hecks/bluebook/dsl/query_builder.rb +27 -1
  38. data/lib/hecks/bluebook/dsl/rule_reference.rb +1 -1
  39. data/lib/hecks/bluebook/dsl/world_builder.rb +29 -2
  40. data/lib/hecks/bluebook/expression/canonical_form.rb +1 -1
  41. data/lib/hecks/bluebook/expression/resolver.rb +3 -2
  42. data/lib/hecks/bluebook/hexagon.rb +13 -5
  43. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +1 -0
  44. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +4 -1
  45. data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +246 -0
  46. data/lib/hecks/bluebook/meta_validator.rb +9 -4
  47. data/lib/hecks/bluebook/model_check.rb +23 -0
  48. data/lib/hecks/bluebook/pattern_subset.rb +38 -0
  49. data/lib/hecks/bluebook/query.rb +20 -5
  50. data/lib/hecks/bluebook/smoke_test.rb +1 -1
  51. data/lib/hecks/canonical_json.rb +30 -0
  52. data/lib/hecks/chapters.rb +102 -0
  53. data/lib/hecks/cli/behaviors.rb +61 -0
  54. data/lib/hecks/cli/canonicalise.rb +25 -0
  55. data/lib/hecks/cli/check_engine_agreement.rb +32 -0
  56. data/lib/hecks/cli/check_era.rb +88 -0
  57. data/lib/hecks/cli/console.rb +66 -0
  58. data/lib/hecks/cli/deploy_template_diff.rb +40 -0
  59. data/lib/hecks/cli/doc_coverage.rb +32 -0
  60. data/lib/hecks/cli/document.rb +2 -2
  61. data/lib/hecks/cli/expression_projection.rb +41 -0
  62. data/lib/hecks/cli/follow.rb +88 -0
  63. data/lib/hecks/cli/history.rb +44 -0
  64. data/lib/hecks/cli/ir.rb +1 -1
  65. data/lib/hecks/cli/mcp.rb +3 -3
  66. data/lib/hecks/cli/model_check.rb +15 -8
  67. data/lib/hecks/cli/pattern_cases.rb +47 -0
  68. data/lib/hecks/cli/present.rb +39 -0
  69. data/lib/hecks/cli/project_cli.rb +224 -32
  70. data/lib/hecks/cli/project_diagrams.rb +4 -3
  71. data/lib/hecks/cli/project_glossary.rb +44 -0
  72. data/lib/hecks/cli/reference.rb +46 -0
  73. data/lib/hecks/cli/refresh_projections.rb +44 -0
  74. data/lib/hecks/cli/refresh_rspec_runtime_baseline.rb +150 -0
  75. data/lib/hecks/cli/release.rb +70 -0
  76. data/lib/hecks/cli/release_gem.rb +59 -0
  77. data/lib/hecks/cli/rspec_io_parallel_files.rb +137 -0
  78. data/lib/hecks/cli/rspec_shard_files.rb +66 -0
  79. data/lib/hecks/cli/run.rb +3 -3
  80. data/lib/hecks/cli/seed_semantics_corpus.rb +71 -0
  81. data/lib/hecks/cli/serve_query_ir_mcp.rb +30 -0
  82. data/lib/hecks/cli/shape.rb +70 -0
  83. data/lib/hecks/cli/smoke_http.rb +230 -0
  84. data/lib/hecks/cli/smoke_test.rb +5 -3
  85. data/lib/hecks/cli/spec_example.rb +27 -0
  86. data/lib/hecks/cli/statements.rb +32 -0
  87. data/lib/hecks/cli/stores.rb +4 -5
  88. data/lib/hecks/cli/stress_concurrency_specs.rb +177 -0
  89. data/lib/hecks/cli.rb +201 -52
  90. data/lib/hecks/codemod.rb +324 -0
  91. data/lib/hecks/corpus.rb +423 -0
  92. data/lib/hecks/deploy/bluebook/deploy.bluebook +591 -23
  93. data/lib/hecks/deploy/bluebook/deploy.hecksagon +41 -4
  94. data/lib/hecks/deploy/oidc.json +82 -2
  95. data/lib/hecks/doc/reference.rb +351 -0
  96. data/lib/hecks/doors/cli_door.rb +196 -0
  97. data/lib/hecks/doors/cli_runner.rb +297 -0
  98. data/lib/hecks/{facade → doors}/command_request.rb +1 -1
  99. data/lib/hecks/{facade → doors}/handle.rb +2 -2
  100. data/lib/hecks/{facade → doors}/json_door.rb +4 -4
  101. data/lib/hecks/doors/launcher_options.rb +129 -0
  102. data/lib/hecks/{cli → doors}/mcp_door.rb +7 -7
  103. data/lib/hecks/doors/mcp_door_scope.rb +97 -0
  104. data/lib/hecks/{facade/surface → doors/ruby_door}/aggregate_door.rb +8 -3
  105. data/lib/hecks/{facade/surface → doors/ruby_door}/chapter.rb +3 -3
  106. data/lib/hecks/{facade/surface.rb → doors/ruby_door.rb} +36 -5
  107. data/lib/hecks/doors.rb +61 -0
  108. data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +1 -1
  109. data/lib/hecks/embryonaut_bluebook.rb +1 -1
  110. data/lib/hecks/engine_agreement.rb +156 -0
  111. data/lib/hecks/facade.rb +24 -37
  112. data/lib/hecks/forms/app.rb +6 -6
  113. data/lib/hecks/forms/banking_presentation.rb +50 -0
  114. data/lib/hecks/forms/field_shape.rb +2 -1
  115. data/lib/hecks/forms/port_argument.rb +1 -1
  116. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +393 -0
  117. data/lib/hecks/fuzzing/combination_miner.rb +86 -0
  118. data/lib/hecks/fuzzing/concurrent_dispatch.rb +214 -0
  119. data/lib/hecks/fuzzing/coverage_campaign.rb +143 -0
  120. data/lib/hecks/fuzzing/differential.rb +156 -0
  121. data/lib/hecks/fuzzing/domain_generator.rb +615 -0
  122. data/lib/hecks/fuzzing/era_boundary.rb +71 -0
  123. data/lib/hecks/fuzzing/form_census.rb +136 -0
  124. data/lib/hecks/fuzzing/generated_domain_check.rb +93 -0
  125. data/lib/hecks/fuzzing/invalid_value_generator.rb +64 -0
  126. data/lib/hecks/fuzzing/isolated_boot.rb +301 -0
  127. data/lib/hecks/fuzzing/nondeterministic.rb +51 -0
  128. data/lib/hecks/fuzzing/outside_world.rb +45 -0
  129. data/lib/hecks/fuzzing/persistence_parity.rb +102 -0
  130. data/lib/hecks/fuzzing/properties/corrections.rb +88 -0
  131. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +314 -0
  132. data/lib/hecks/fuzzing/properties/guards.rb +190 -0
  133. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +300 -0
  134. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +93 -0
  135. data/lib/hecks/fuzzing/properties/outbox.rb +89 -0
  136. data/lib/hecks/fuzzing/properties/querying.rb +175 -0
  137. data/lib/hecks/fuzzing/properties.rb +162 -0
  138. data/lib/hecks/fuzzing/qa_settings.rb +123 -0
  139. data/lib/hecks/fuzzing/replay.rb +505 -0
  140. data/lib/hecks/fuzzing/rotation_priority.rb +48 -0
  141. data/lib/hecks/fuzzing/rust_gap_manifest.rb +108 -0
  142. data/lib/hecks/fuzzing/self_consistency.rb +339 -0
  143. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +385 -0
  144. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +132 -0
  145. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +95 -0
  146. data/lib/hecks/fuzzing/sequence_generator/picker.rb +77 -0
  147. data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +109 -0
  148. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +237 -0
  149. data/lib/hecks/fuzzing/sequence_generator.rb +189 -0
  150. data/lib/hecks/fuzzing/shrinker.rb +166 -0
  151. data/lib/hecks/fuzzing/structural_skips.rb +33 -0
  152. data/lib/hecks/fuzzing/sweep_depth.rb +43 -0
  153. data/lib/hecks/fuzzing/target_capabilities.rb +163 -0
  154. data/lib/hecks/fuzzing/value_generator.rb +216 -0
  155. data/lib/hecks/fuzzing.rb +23 -0
  156. data/lib/hecks/grammar/evolve.rb +336 -0
  157. data/lib/hecks/hecks/adapters/codebase/codemods.rb +45 -0
  158. data/lib/hecks/hecks/adapters/codebase/conformance.rb +71 -0
  159. data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +202 -0
  160. data/lib/hecks/hecks/adapters/codebase/gem_registry.rb +80 -0
  161. data/lib/hecks/hecks/adapters/codebase/kernel_tables.rb +54 -0
  162. data/lib/hecks/hecks/adapters/codebase/language.rb +240 -0
  163. data/lib/hecks/hecks/adapters/codebase/npm_registry.rb +102 -0
  164. data/lib/hecks/hecks/adapters/codebase/publishing.rb +142 -0
  165. data/lib/hecks/hecks/adapters/codebase/regeneration.rb +67 -0
  166. data/lib/hecks/hecks/adapters/codebase/release_facts.rb +90 -0
  167. data/lib/hecks/hecks/adapters/codebase/ruby_child.rb +83 -0
  168. data/lib/hecks/hecks/adapters/codebase/secret_vault.rb +36 -0
  169. data/lib/hecks/hecks/adapters/codebase/source_tree.adapter +7 -0
  170. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +189 -0
  171. data/lib/hecks/hecks/adapters/codebase/sqlite_fixture.rb +71 -0
  172. data/lib/hecks/hecks/adapters/codebase/style.rb +171 -0
  173. data/lib/hecks/hecks/adapters/codebase/test_runner.rb +55 -0
  174. data/lib/hecks/hecks/adapters/codebase/test_suite.rb +165 -0
  175. data/lib/hecks/hecks/adapters/codebase/tree.rb +111 -0
  176. data/lib/hecks/hecks/adapters/console_capture.rb +79 -0
  177. data/lib/hecks/hecks/adapters/deploy_toolchain.adapter +7 -0
  178. data/lib/hecks/hecks/adapters/deploy_toolchain.rb +108 -0
  179. data/lib/hecks/hecks/adapters/git.adapter +7 -0
  180. data/lib/hecks/hecks/adapters/git.rb +102 -0
  181. data/lib/hecks/hecks/adapters/host_http.adapter +7 -0
  182. data/lib/hecks/hecks/adapters/host_http.rb +65 -0
  183. data/lib/hecks/hecks/adapters/in_process_boot.adapter +7 -0
  184. data/lib/hecks/hecks/adapters/in_process_boot.rb +197 -0
  185. data/lib/hecks/hecks/adapters/in_process_operations.rb +202 -0
  186. data/lib/hecks/hecks/adapters/journal_store/changes.rb +143 -0
  187. data/lib/hecks/hecks/adapters/journal_store/compaction.rb +145 -0
  188. data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +132 -0
  189. data/lib/hecks/hecks/adapters/journal_store/examination.rb +135 -0
  190. data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +163 -0
  191. data/lib/hecks/hecks/adapters/journal_store/readings.rb +187 -0
  192. data/lib/hecks/hecks/adapters/journal_store.adapter +7 -0
  193. data/lib/hecks/hecks/adapters/journal_store.rb +64 -0
  194. data/lib/hecks/hecks/adapters/local_files.adapter +7 -0
  195. data/lib/hecks/hecks/adapters/local_files.rb +46 -0
  196. data/lib/hecks/hecks/adapters/pg_admin.adapter +7 -0
  197. data/lib/hecks/hecks/adapters/pg_admin.rb +245 -0
  198. data/lib/hecks/hecks/adapters/process_pool.adapter +7 -0
  199. data/lib/hecks/hecks/adapters/process_pool.rb +119 -0
  200. data/lib/hecks/hecks/adapters/rust_toolchain.adapter +7 -0
  201. data/lib/hecks/hecks/adapters/rust_toolchain.rb +179 -0
  202. data/lib/hecks/hecks/adapters/rust_workspace.rb +150 -0
  203. data/lib/hecks/hecks/adapters/shell.adapter +7 -0
  204. data/lib/hecks/hecks/adapters/shell.rb +49 -0
  205. data/lib/hecks/hecks/adapters/terminal.adapter +7 -0
  206. data/lib/hecks/hecks/adapters/terminal.rb +74 -0
  207. data/lib/hecks/hecks/codebase.behaviors +172 -0
  208. data/lib/hecks/hecks/codebase.bluebook +2453 -0
  209. data/lib/hecks/hecks/context_map.hecksagon +13 -0
  210. data/lib/hecks/hecks/custodian.behaviors +179 -0
  211. data/lib/hecks/hecks/custodian.bluebook +1847 -0
  212. data/lib/hecks/hecks/deploy.behaviors +74 -0
  213. data/lib/hecks/hecks/deploy.world +12 -0
  214. data/lib/hecks/hecks/environments/memory.world +10 -0
  215. data/lib/hecks/hecks/governance.world +7 -0
  216. data/lib/hecks/hecks/hecks.behaviors +62 -0
  217. data/lib/hecks/hecks/hecks.bluebook +124 -0
  218. data/lib/hecks/hecks/hecks.hecksagon +390 -0
  219. data/lib/hecks/hecks/hecks.world +42 -0
  220. data/lib/hecks/kernel_capabilities.rb +194 -0
  221. data/lib/hecks/language/bluebook/bluebook.bluebook +11 -0
  222. data/lib/hecks/language/bluebook/query.bluebook +7 -0
  223. data/lib/hecks/language/bluebook/vocabulary.bluebook +4 -4
  224. data/lib/hecks/language/hecksagon/domain_port.bluebook +10 -0
  225. data/lib/hecks/language/hecksagon/hecksagon.bluebook +20 -0
  226. data/lib/hecks/mcp_stdio_guard.rb +2 -2
  227. data/lib/hecks/persistence_legacy_fixture/regenerate.rb +161 -0
  228. data/lib/hecks/persistence_legacy_fixture.rb +113 -0
  229. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +8 -5
  230. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +4 -1
  231. data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +15 -2
  232. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +1 -1
  233. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +1 -1
  234. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +1 -1
  235. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +32 -15
  236. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +15 -5
  237. data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +227 -0
  238. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +1 -1
  239. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +43 -24
  240. data/lib/hecks/ports/persistence/plugins/era/translation.rb +1 -0
  241. data/lib/hecks/ports/persistence/state_codec.rb +24 -5
  242. data/lib/hecks/projection_files.rb +285 -0
  243. data/lib/hecks/projections/bootstrap_table.rb +1 -1
  244. data/lib/hecks/projections/deploy/fargate.rb +5 -5
  245. data/lib/hecks/projections/deploy/lambda.rb +43 -32
  246. data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
  247. data/lib/hecks/projections/deploy/preview/template.rb +1 -1
  248. data/lib/hecks/projections/deploy/scripts/deploy-service.sh.tmpl +1 -1
  249. data/lib/hecks/projections/deploy/scripts/expected-era.header +1 -1
  250. data/lib/hecks/projections/deploy/scripts/hosting.mk.tmpl +3 -3
  251. data/lib/hecks/projections/deploy/scripts/smoke-after-deploy.sh.tmpl +1 -1
  252. data/lib/hecks/projections/deploy/shared.rb +1 -1
  253. data/lib/hecks/projections/diagrams.rb +3 -3
  254. data/lib/hecks/projections/model/deviations.rb +1 -1
  255. data/lib/hecks/projections/parser_table.rb +4 -4
  256. data/lib/hecks/projections/reference.rb +2 -3
  257. data/lib/hecks/projections/rust_vocabulary.rb +1 -1
  258. data/lib/hecks/projector/cli_projector.rb +106 -37
  259. data/lib/hecks/projector.rb +1 -1
  260. data/lib/hecks/quality_control/adapters/agent.adapter +7 -0
  261. data/lib/hecks/quality_control/adapters/agent.rb +58 -0
  262. data/lib/hecks/quality_control/adapters/angle_tools.adapter +7 -0
  263. data/lib/hecks/quality_control/adapters/angle_tools.rb +15 -0
  264. data/lib/hecks/quality_control/adapters/clearance_recorder.rb +36 -0
  265. data/lib/hecks/quality_control/adapters/clearance_tools.adapter +7 -0
  266. data/lib/hecks/quality_control/adapters/clearance_tools.rb +17 -0
  267. data/lib/hecks/quality_control/adapters/git_pr.adapter +7 -0
  268. data/lib/hecks/quality_control/adapters/git_pr.rb +226 -0
  269. data/lib/hecks/quality_control/adapters/github_checks.adapter +9 -0
  270. data/lib/hecks/quality_control/adapters/github_checks.rb +83 -0
  271. data/lib/hecks/quality_control/adapters/github_ci_webhook.rb +56 -0
  272. data/lib/hecks/quality_control/adapters/github_issues.adapter +7 -0
  273. data/lib/hecks/quality_control/adapters/github_issues.rb +49 -0
  274. data/lib/hecks/quality_control/adapters/qa_tool.rb +59 -0
  275. data/lib/hecks/quality_control/adapters/sweep_tools.adapter +7 -0
  276. data/lib/hecks/quality_control/adapters/sweep_tools.rb +51 -0
  277. data/lib/hecks/quality_control/adapters/target_tools.adapter +7 -0
  278. data/lib/hecks/quality_control/adapters/target_tools.rb +43 -0
  279. data/lib/hecks/quality_control/cli/child.rb +45 -0
  280. data/lib/hecks/quality_control/cli/qa_concurrency_racer.rb +45 -0
  281. data/lib/hecks/quality_control/cli/qa_discover_external_domains.rb +248 -0
  282. data/lib/hecks/quality_control/cli/qa_domain_novelty.rb +163 -0
  283. data/lib/hecks/quality_control/cli/qa_generated_domains.rb +450 -0
  284. data/lib/hecks/quality_control/cli/qa_log_bug.rb +195 -0
  285. data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +220 -0
  286. data/lib/hecks/quality_control/cli/qa_open_pr.rb +305 -0
  287. data/lib/hecks/quality_control/cli/qa_postgres_migrate.rb +154 -0
  288. data/lib/hecks/quality_control/cli/qa_postgres_role.rb +159 -0
  289. data/lib/hecks/quality_control/cli/qa_pr_check.rb +232 -0
  290. data/lib/hecks/quality_control/cli/qa_seed_angles.rb +142 -0
  291. data/lib/hecks/quality_control/cli/qa_seed_targets.rb +69 -0
  292. data/lib/hecks/quality_control/cli/qa_sweep/all_mode.rb +280 -0
  293. data/lib/hecks/quality_control/cli/qa_sweep/checks.rb +329 -0
  294. data/lib/hecks/quality_control/cli/qa_sweep/dials.rb +92 -0
  295. data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +117 -0
  296. data/lib/hecks/quality_control/cli/qa_sweep/parsing.rb +175 -0
  297. data/lib/hecks/quality_control/cli/qa_sweep/release_mode.rb +60 -0
  298. data/lib/hecks/quality_control/cli/qa_sweep.rb +611 -0
  299. data/lib/hecks/quality_control/cli/qa_tick.rb +274 -0
  300. data/lib/hecks/quality_control/quality_control.bluebook +2224 -0
  301. data/lib/hecks/quality_control/quality_control.hecksagon +13 -0
  302. data/lib/hecks/quality_control/quality_control.ports.hecksagon +54 -0
  303. data/lib/hecks/query_ir.rb +346 -0
  304. data/lib/hecks/query_ir_mcp.rb +198 -0
  305. data/lib/hecks/release/runner/ci_publisher.rb +2 -2
  306. data/lib/hecks/release/runner/clean_tree.rb +42 -0
  307. data/lib/hecks/release/runner/console.rb +6 -0
  308. data/lib/hecks/release/runner/gem_publisher.rb +14 -7
  309. data/lib/hecks/release/runner/npm_publisher.rb +6 -36
  310. data/lib/hecks/release/runner/preflight.rb +26 -2
  311. data/lib/hecks/release/runner/published.rb +8 -26
  312. data/lib/hecks/release/runner/tagger.rb +30 -15
  313. data/lib/hecks/release/runner.rb +57 -6
  314. data/lib/hecks/router/namespace_installer.rb +3 -3
  315. data/lib/hecks/runtime/adapter_lookup.rb +124 -0
  316. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +4 -0
  317. data/lib/hecks/runtime/dependency_planning.rb +20 -2
  318. data/lib/hecks/runtime/dispatcher.rb +53 -3
  319. data/lib/hecks/runtime/interpreting.rb +3 -0
  320. data/lib/hecks/runtime/loader.rb +65 -12
  321. data/lib/hecks/runtime/outbox.rb +1 -1
  322. data/lib/hecks/runtime/policy_interpreter.rb +5 -2
  323. data/lib/hecks/runtime/port_operation_interpreter.rb +2 -10
  324. data/lib/hecks/runtime/query_interpreter.rb +77 -0
  325. data/lib/hecks/runtime/reaction_outcome.rb +89 -0
  326. data/lib/hecks/runtime/refusal_wording.rb +23 -1
  327. data/lib/hecks/runtime/registry/verification.rb +163 -4
  328. data/lib/hecks/runtime/registry.rb +57 -3
  329. data/lib/hecks/runtime/remote_dispatcher.rb +31 -2
  330. data/lib/hecks/runtime/value/coercion.rb +23 -135
  331. data/lib/hecks/runtime/value/field_checks.rb +172 -0
  332. data/lib/hecks/runtime/value.rb +4 -1
  333. data/lib/hecks/runtime.rb +20 -6
  334. data/lib/hecks/rust_build/conformance.rb +158 -0
  335. data/lib/hecks/rust_build/conformance_fuzz.rb +71 -0
  336. data/lib/hecks/rust_build/coverage.rb +218 -0
  337. data/lib/hecks/rust_build/native_build.rb +124 -0
  338. data/lib/hecks/rust_build/project_rust/root_mod.rb +120 -0
  339. data/lib/hecks/rust_build/project_rust.rb +332 -0
  340. data/lib/hecks/rust_build/wasm.rb +105 -0
  341. data/lib/hecks/rust_build/wasm_browser.rb +113 -0
  342. data/lib/hecks/rust_build.rb +144 -0
  343. data/lib/hecks/storehouse.rb +16 -16
  344. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +2 -2
  345. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +1 -1
  346. data/lib/hecks/three_zero/forms.yml +127 -0
  347. data/lib/hecks/three_zero.rb +12 -0
  348. data/lib/hecks/tools/argument_gate_matrix.rb +316 -0
  349. data/lib/hecks/tools/comment_style.rb +1070 -0
  350. data/lib/hecks/tools/corpus_report.rb +87 -0
  351. data/lib/hecks/tools/deploy_recipe.rb +255 -0
  352. data/lib/hecks/tools/deploy_recipe_lint.rb +282 -0
  353. data/lib/hecks/tools/drop_implicit_append_fields.rb +95 -0
  354. data/lib/hecks/tools/era_projection_backfill.rb +94 -0
  355. data/lib/hecks/tools/era_reattest.rb +146 -0
  356. data/lib/hecks/tools/evolve_run.rb +189 -0
  357. data/lib/hecks/tools/fuzz_sweep.rb +373 -0
  358. data/lib/hecks/tools/heki_compaction.rb +100 -0
  359. data/lib/hecks/tools/hoist_local_givens.rb +246 -0
  360. data/lib/hecks/tools/journal_compaction.rb +89 -0
  361. data/lib/hecks/tools/oidc_manifests.rb +70 -0
  362. data/lib/hecks/tools/query_ir_run.rb +81 -0
  363. data/lib/hecks/tools/regeneration_run.rb +166 -0
  364. data/lib/hecks/tools/rust_comment_style.rb +642 -0
  365. data/lib/hecks/tools/sequence_script.rb +62 -0
  366. data/lib/hecks/tools/tail_merge.rb +117 -0
  367. data/lib/hecks/tools/tenant_provisioning.rb +142 -0
  368. data/lib/hecks/tools/translation_audit.rb +208 -0
  369. data/lib/hecks/tools/translation_scaffold.rb +131 -0
  370. data/lib/hecks/tools.rb +86 -0
  371. data/lib/hecks/version.rb +1 -1
  372. data/lib/hecks.rb +32 -15
  373. data/qa/settings.yml +66 -0
  374. data/rust/.cargo/config.toml +6 -0
  375. data/rust/.gitignore +2 -0
  376. data/rust/Cargo.lock +54 -0
  377. data/rust/Cargo.toml +61 -0
  378. data/rust/build/.cargo/config.toml +3 -0
  379. data/rust/build/.gitignore +1 -0
  380. data/rust/build/Cargo.lock +7 -0
  381. data/rust/build/Cargo.toml +19 -0
  382. data/rust/build/src/build_artifact.rs +47 -0
  383. data/rust/build/src/cargo_sync.rs +248 -0
  384. data/rust/build/src/json.rs +421 -0
  385. data/rust/build/src/lineage_pass.rs +285 -0
  386. data/rust/build/src/main.rs +51 -0
  387. data/rust/build/src/optional_pass.rs +150 -0
  388. data/rust/build/src/pipeline.rs +264 -0
  389. data/rust/build/src/reserved_names.rs +76 -0
  390. data/rust/build/src/resolve.rs +124 -0
  391. data/rust/build/src/root.rs +25 -0
  392. data/rust/build/src/sidecars.rs +73 -0
  393. data/rust/build/src/subprocess.rs +51 -0
  394. data/rust/build/src/tmp.rs +31 -0
  395. data/rust/codegen/.cargo/config.toml +3 -0
  396. data/rust/codegen/.gitignore +1 -0
  397. data/rust/codegen/Cargo.lock +7 -0
  398. data/rust/codegen/Cargo.toml +20 -0
  399. data/rust/codegen/src/attr.rs +32 -0
  400. data/rust/codegen/src/bridging.rs +481 -0
  401. data/rust/codegen/src/commands.rs +1226 -0
  402. data/rust/codegen/src/constraints.rs +109 -0
  403. data/rust/codegen/src/dependency_planning.rs +209 -0
  404. data/rust/codegen/src/domain_generator.rs +1288 -0
  405. data/rust/codegen/src/exemplar.rs +230 -0
  406. data/rust/codegen/src/expr_emitter.rs +87 -0
  407. data/rust/codegen/src/fielded.rs +192 -0
  408. data/rust/codegen/src/hecks_naming.rs +33 -0
  409. data/rust/codegen/src/json.rs +335 -0
  410. data/rust/codegen/src/json_codec.rs +697 -0
  411. data/rust/codegen/src/literal.rs +207 -0
  412. data/rust/codegen/src/main.rs +290 -0
  413. data/rust/codegen/src/manifest.rs +227 -0
  414. data/rust/codegen/src/mutations.rs +784 -0
  415. data/rust/codegen/src/naming.rs +286 -0
  416. data/rust/codegen/src/ports.rs +208 -0
  417. data/rust/codegen/src/prelude.rs +116 -0
  418. data/rust/codegen/src/queries.rs +786 -0
  419. data/rust/codegen/src/reactions.rs +532 -0
  420. data/rust/codegen/src/read_models.rs +697 -0
  421. data/rust/codegen/src/reference_specs.rs +40 -0
  422. data/rust/codegen/src/registry.rs +966 -0
  423. data/rust/codegen/src/reserved_names.rs +76 -0
  424. data/rust/codegen/src/shared.rs +93 -0
  425. data/rust/codegen/src/skip_reason.rs +25 -0
  426. data/rust/codegen/src/types.rs +362 -0
  427. data/rust/host/.cargo/config.toml +3 -0
  428. data/rust/host/.gitignore +1 -0
  429. data/rust/host/Cargo.lock +4297 -0
  430. data/rust/host/Cargo.toml +61 -0
  431. data/rust/host/HECKS_RELEASE +1 -0
  432. data/rust/host/rds-ca-bundle.pem +2736 -0
  433. data/rust/host/src/api.rs +1596 -0
  434. data/rust/host/src/approval.rs +652 -0
  435. data/rust/host/src/auth.rs +1287 -0
  436. data/rust/host/src/bin/expr_harness.rs +46 -0
  437. data/rust/host/src/bin/lineage_harness.rs +119 -0
  438. data/rust/host/src/bin/mint_harness.rs +135 -0
  439. data/rust/host/src/checkout.rs +265 -0
  440. data/rust/host/src/dispatch.rs +1443 -0
  441. data/rust/host/src/expr_json.rs +1117 -0
  442. data/rust/host/src/field_hints.rs +52 -0
  443. data/rust/host/src/ir.rs +733 -0
  444. data/rust/host/src/journal.rs +874 -0
  445. data/rust/host/src/lambda_client.rs +378 -0
  446. data/rust/host/src/log.rs +147 -0
  447. data/rust/host/src/main.rs +474 -0
  448. data/rust/host/src/mint.rs +1865 -0
  449. data/rust/host/src/payments/keystore.rs +330 -0
  450. data/rust/host/src/payments/tests.rs +1400 -0
  451. data/rust/host/src/payments.rs +767 -0
  452. data/rust/host/src/presentation.rs +698 -0
  453. data/rust/host/src/presentation_write.rs +1431 -0
  454. data/rust/host/src/query_step.rs +149 -0
  455. data/rust/host/src/rate_limit.rs +904 -0
  456. data/rust/host/src/reference_transform.rs +241 -0
  457. data/rust/host/src/reference_validate.rs +338 -0
  458. data/rust/host/src/resend.rs +296 -0
  459. data/rust/host/src/secrets.rs +106 -0
  460. data/rust/host/src/server.rs +663 -0
  461. data/rust/host/src/storage_shape.rs +175 -0
  462. data/rust/host/src/test_pg.rs +40 -0
  463. data/rust/host/src/ui_schema.rs +1351 -0
  464. data/rust/host/src/wasm_runner.rs +191 -0
  465. data/rust/host/src/web/newsletter/cooldown.rs +126 -0
  466. data/rust/host/src/web/newsletter/tests.rs +104 -0
  467. data/rust/host/src/web/newsletter.rs +310 -0
  468. data/rust/host/src/web/newsletter_send.rs +357 -0
  469. data/rust/host/src/web/registrations.rs +1505 -0
  470. data/rust/host/src/web.rs +2937 -0
  471. data/rust/lsp/.gitignore +1 -0
  472. data/rust/lsp/Cargo.lock +7 -0
  473. data/rust/lsp/Cargo.toml +16 -0
  474. data/rust/lsp/README.md +133 -0
  475. data/rust/lsp/src/diagnostics.rs +165 -0
  476. data/rust/lsp/src/json.rs +385 -0
  477. data/rust/lsp/src/main.rs +399 -0
  478. data/rust/lsp/src/outline.rs +177 -0
  479. data/rust/lsp/src/rpc.rs +67 -0
  480. data/rust/parser/.cargo/config.toml +5 -0
  481. data/rust/parser/.gitignore +1 -0
  482. data/rust/parser/Cargo.lock +7 -0
  483. data/rust/parser/Cargo.toml +21 -0
  484. data/rust/parser/src/build/closed_sets.rs +27 -0
  485. data/rust/parser/src/build/identity.rs +276 -0
  486. data/rust/parser/src/build/mod.rs +12 -0
  487. data/rust/parser/src/build/naming.rs +141 -0
  488. data/rust/parser/src/build/pattern_subset.rs +161 -0
  489. data/rust/parser/src/build/query_derive.rs +72 -0
  490. data/rust/parser/src/build/query_inference.rs +269 -0
  491. data/rust/parser/src/build/query_options.rs +100 -0
  492. data/rust/parser/src/build/read_model.rs +94 -0
  493. data/rust/parser/src/build/references.rs +32 -0
  494. data/rust/parser/src/canonical.rs +114 -0
  495. data/rust/parser/src/diag.rs +55 -0
  496. data/rust/parser/src/emit.rs +953 -0
  497. data/rust/parser/src/expr/ast_json.rs +147 -0
  498. data/rust/parser/src/expr/evaluator.rs +81 -0
  499. data/rust/parser/src/expr/mod.rs +84 -0
  500. data/rust/parser/src/expr/resolver.rs +497 -0
  501. data/rust/parser/src/ir.rs +387 -0
  502. data/rust/parser/src/keywords.rs +585 -0
  503. data/rust/parser/src/lex.rs +662 -0
  504. data/rust/parser/src/main.rs +252 -0
  505. data/rust/parser/src/parse/aggregate.rs +612 -0
  506. data/rust/parser/src/parse/chapter.rs +517 -0
  507. data/rust/parser/src/parse/command.rs +637 -0
  508. data/rust/parser/src/parse/domain_port.rs +127 -0
  509. data/rust/parser/src/parse/entity.rs +527 -0
  510. data/rust/parser/src/parse/file.rs +67 -0
  511. data/rust/parser/src/parse/hecksagon.rs +231 -0
  512. data/rust/parser/src/parse/lifecycle.rs +162 -0
  513. data/rust/parser/src/parse/mod.rs +1262 -0
  514. data/rust/parser/src/parse/policy.rs +87 -0
  515. data/rust/parser/src/parse/process_manager.rs +305 -0
  516. data/rust/parser/src/parse/query.rs +100 -0
  517. data/rust/parser/src/parse/read_model.rs +214 -0
  518. data/rust/parser/src/parse/value_object.rs +250 -0
  519. data/rust/parser/src/ruby_value.rs +422 -0
  520. data/rust/project/bridging.rb +257 -0
  521. data/rust/project/commands.rb +877 -0
  522. data/rust/project/constraints.rb +86 -0
  523. data/rust/project/dependency_planning.rb +178 -0
  524. data/rust/project/domain_generator.rb +980 -0
  525. data/rust/project/exemplar.rb +148 -0
  526. data/rust/project/expr_emitter.rb +72 -0
  527. data/rust/project/fielded.rb +141 -0
  528. data/rust/project/json_codec.rb +641 -0
  529. data/rust/project/mutations.rb +721 -0
  530. data/rust/project/naming.rb +205 -0
  531. data/rust/project/ports.rb +65 -0
  532. data/rust/project/queries.rb +493 -0
  533. data/rust/project/reactions.rb +317 -0
  534. data/rust/project/read_models.rb +690 -0
  535. data/rust/project/reference_specs.rb +27 -0
  536. data/rust/project/registry.rb +396 -0
  537. data/rust/project/skip_reason.rb +29 -0
  538. data/rust/project/types.rb +225 -0
  539. data/rust/project/write_if_changed.rb +83 -0
  540. data/rust/project.rb +30 -0
  541. data/rust/project_rust_pipeline.rb +383 -0
  542. data/rust/src/exemplar/commands.rs +279 -0
  543. data/rust/src/exemplar/constraints.rs +18 -0
  544. data/rust/src/exemplar/fielded.rs +280 -0
  545. data/rust/src/exemplar/json.rs +277 -0
  546. data/rust/src/exemplar/mod.rs +17 -0
  547. data/rust/src/exemplar/mutations.rs +80 -0
  548. data/rust/src/exemplar/queries.rs +33 -0
  549. data/rust/src/exemplar/reactions.rs +88 -0
  550. data/rust/src/exemplar/read_models.rs +48 -0
  551. data/rust/src/exemplar/registry.rs +166 -0
  552. data/rust/src/exemplar/types.rs +44 -0
  553. data/rust/src/kernel/attribute_shapes/composite.rs +88 -0
  554. data/rust/src/kernel/attribute_shapes/list.rs +18 -0
  555. data/rust/src/kernel/attribute_shapes/mod.rs +44 -0
  556. data/rust/src/kernel/attribute_shapes/optional.rs +9 -0
  557. data/rust/src/kernel/attribute_shapes/scalar.rs +39 -0
  558. data/rust/src/kernel/cli.rs +585 -0
  559. data/rust/src/kernel/dispatch.rs +1090 -0
  560. data/rust/src/kernel/expr.rs +458 -0
  561. data/rust/src/kernel/expression_operators/arithmetic.rs +128 -0
  562. data/rust/src/kernel/expression_operators/comparison.rs +48 -0
  563. data/rust/src/kernel/expression_operators/enumeration.rs +232 -0
  564. data/rust/src/kernel/expression_operators/logical.rs +13 -0
  565. data/rust/src/kernel/expression_operators/membership.rs +18 -0
  566. data/rust/src/kernel/expression_operators/mod.rs +68 -0
  567. data/rust/src/kernel/expression_operators/pattern_match.rs +73 -0
  568. data/rust/src/kernel/expression_operators/positional.rs +126 -0
  569. data/rust/src/kernel/expression_operators/presence.rs +101 -0
  570. data/rust/src/kernel/expression_operators/sign_test.rs +16 -0
  571. data/rust/src/kernel/expression_operators/sized.rs +35 -0
  572. data/rust/src/kernel/expression_operators/text.rs +79 -0
  573. data/rust/src/kernel/expression_operators/to_string.rs +18 -0
  574. data/rust/src/kernel/json.rs +779 -0
  575. data/rust/src/kernel/mod.rs +133 -0
  576. data/rust/src/kernel/named_query.rs +219 -0
  577. data/rust/src/kernel/orchestrate.rs +1810 -0
  578. data/rust/src/kernel/pattern.rs +307 -0
  579. data/rust/src/kernel/query_comparators.rs +206 -0
  580. data/rust/src/kernel/query_ordering.rs +103 -0
  581. data/rust/src/kernel/read_model.rs +914 -0
  582. data/rust/src/kernel/reference_lookup.rs +149 -0
  583. data/rust/src/kernel/repository.rs +468 -0
  584. data/rust/src/kernel/routing.rs +612 -0
  585. data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +118 -0
  586. data/rust/src/kernel/vocab/entity_dispatch_order.rs +114 -0
  587. data/rust/src/kernel/vocab/field_hint.rs +70 -0
  588. data/rust/src/kernel/vocab/mod.rs +20 -0
  589. data/rust/src/kernel/vocab/query_comparator.rs +65 -0
  590. data/rust/src/kernel/vocab/refusal_template.rs +1592 -0
  591. data/rust/src/lib.rs +8 -0
  592. data/rust/src/main.rs +16 -0
  593. data/rust/web/.gitignore +1 -0
  594. data/rust/web/Cargo.lock +119 -0
  595. data/rust/web/Cargo.toml +18 -0
  596. data/rust/web/src/lib.rs +8 -0
  597. metadata +477 -12
  598. data/lib/hecks/facade/cli_door.rb +0 -96
  599. data/lib/hecks/facade/cli_runner.rb +0 -127
  600. data/lib/hecks/mcp_door_scope.rb +0 -95
@@ -2,15 +2,52 @@
2
2
  Hecks.hecksagon "Deploy" do
3
3
  persisted_by "Memory"
4
4
 
5
- # `bin/project_tenant` dispatches this after `Tenant.Declare` validates; `directory` is a real
6
- # filesystem path, which `Tenant`'s declared `domain` name cannot supply.
5
+ # `Tenant.Provision` and `Tenant.Reprovision` dispatch this through their policies; `directory` is a real filesystem path, which `Tenant`'s
6
+ # declared `domain` name cannot supply. `adapter` may be left to the record, which leaves it to
7
+ # the adapter's PostgresEra.
7
8
  Deploy::Tenant.port "TenantProvisioning" do
8
- asks "Provision", to: Tenant do
9
+ asks "WriteOverlay", to: Tenant do
9
10
  attribute :database, DatabaseName
10
- attribute :adapter, AdapterName
11
+ attribute :adapter, AdapterName, optional: true
11
12
  attribute :directory, DirectoryPath
12
13
  answers "TenantProvisioned"
13
14
  refuses "ProvisioningRefused"
14
15
  end
15
16
  end
17
+
18
+ # Each run below asks the DeployToolchain adapter, which the Hecks domain supplies
19
+ # (lib/hecks/hecks/adapters/deploy_toolchain.rb): the outside a recipe, a lint, a comparison or
20
+ # a manifest reaches. Every ask is answered with the record's own arguments or refused with
21
+ # a reason.
22
+ Deploy::Recipe.port "DeployToolchain" do
23
+ asks "Generate", to: Recipe do
24
+ attribute :run, RunKey
25
+ answers "RecipeAnswered"
26
+ refuses "RecipeRefused"
27
+ end
28
+ end
29
+
30
+ Deploy::MakefileCheck.port "DeployToolchain" do
31
+ asks "Scan", to: MakefileCheck do
32
+ attribute :run, RunKey
33
+ answers "LintAnswered"
34
+ refuses "LintRefused"
35
+ end
36
+ end
37
+
38
+ Deploy::TemplateComparison.port "DeployToolchain" do
39
+ asks "Compare", to: TemplateComparison do
40
+ attribute :run, RunKey
41
+ answers "ComparisonAnswered"
42
+ refuses "ComparisonUnreadable"
43
+ end
44
+ end
45
+
46
+ Deploy::OidcManifest.port "DeployToolchain" do
47
+ asks "Manifest", to: OidcManifest do
48
+ attribute :run, RunKey
49
+ answers "ManifestAnswered"
50
+ refuses "ManifestRefused"
51
+ end
52
+ end
16
53
  end
@@ -12,8 +12,88 @@
12
12
  "role": null
13
13
  },
14
14
  {
15
- "scope": "deploy:tenant.declare",
16
- "verb": "Deploy::Tenant.Declare",
15
+ "scope": "deploy:makefile_check.flag",
16
+ "verb": "Deploy::MakefileCheck.Flag",
17
+ "role": null
18
+ },
19
+ {
20
+ "scope": "deploy:makefile_check.lint",
21
+ "verb": "Deploy::MakefileCheck.Lint",
22
+ "role": null
23
+ },
24
+ {
25
+ "scope": "deploy:makefile_check.pass",
26
+ "verb": "Deploy::MakefileCheck.Pass",
27
+ "role": null
28
+ },
29
+ {
30
+ "scope": "deploy:oidc_manifest.complete",
31
+ "verb": "Deploy::OidcManifest.Complete",
32
+ "role": null
33
+ },
34
+ {
35
+ "scope": "deploy:oidc_manifest.fault",
36
+ "verb": "Deploy::OidcManifest.Fault",
37
+ "role": null
38
+ },
39
+ {
40
+ "scope": "deploy:oidc_manifest.project_oidc",
41
+ "verb": "Deploy::OidcManifest.ProjectOidc",
42
+ "role": null
43
+ },
44
+ {
45
+ "scope": "deploy:recipe.complete",
46
+ "verb": "Deploy::Recipe.Complete",
47
+ "role": null
48
+ },
49
+ {
50
+ "scope": "deploy:recipe.fault",
51
+ "verb": "Deploy::Recipe.Fault",
52
+ "role": null
53
+ },
54
+ {
55
+ "scope": "deploy:recipe.project",
56
+ "verb": "Deploy::Recipe.Project",
57
+ "role": null
58
+ },
59
+ {
60
+ "scope": "deploy:template_comparison.diff",
61
+ "verb": "Deploy::TemplateComparison.Diff",
62
+ "role": null
63
+ },
64
+ {
65
+ "scope": "deploy:template_comparison.drift",
66
+ "verb": "Deploy::TemplateComparison.Drift",
67
+ "role": null
68
+ },
69
+ {
70
+ "scope": "deploy:template_comparison.match",
71
+ "verb": "Deploy::TemplateComparison.Match",
72
+ "role": null
73
+ },
74
+ {
75
+ "scope": "deploy:template_comparison.refuse",
76
+ "verb": "Deploy::TemplateComparison.Refuse",
77
+ "role": null
78
+ },
79
+ {
80
+ "scope": "deploy:tenant.abandon",
81
+ "verb": "Deploy::Tenant.Abandon",
82
+ "role": null
83
+ },
84
+ {
85
+ "scope": "deploy:tenant.complete",
86
+ "verb": "Deploy::Tenant.Complete",
87
+ "role": null
88
+ },
89
+ {
90
+ "scope": "deploy:tenant.provision",
91
+ "verb": "Deploy::Tenant.Provision",
92
+ "role": null
93
+ },
94
+ {
95
+ "scope": "deploy:tenant.reprovision",
96
+ "verb": "Deploy::Tenant.Reprovision",
17
97
  "role": null
18
98
  }
19
99
  ],
@@ -0,0 +1,351 @@
1
+ require_relative "../bluebook/meta_validator"
2
+ require_relative "../naming"
3
+
4
+ module Hecks
5
+ module Doc
6
+ # The DSL reference, projected from the language's own Syntax chapter so
7
+ # its tables can never drift from the live builders. Regenerate with `hecks project_reference`.
8
+ module Reference
9
+ GENERATED_END = "<!-- generated:end -->".freeze
10
+ TODO_SENTINEL = "<!-- TODO: document this word -->".freeze
11
+
12
+ # A page's hand-written opening, keyed under a Symbol so it can never
13
+ # collide with a word (words are always Strings off the Syntax chapter).
14
+ PREAMBLE = :preamble
15
+
16
+ module_function
17
+
18
+ # The marker opening one word's generated region.
19
+ def generated_begin(word) = "<!-- generated:begin word=#{word} -->"
20
+
21
+ # Same marker convention as generated_begin, keyed by region id for
22
+ # parts of a page not about one word (a lede, README's indexes).
23
+ def region_begin(id) = "<!-- generated:begin id=#{id} -->"
24
+
25
+ # The language's own Syntax aggregate, read off the judged grammar chapter.
26
+ def syntax
27
+ meta = Bluebook::MetaValidator.grammar_registry.bluebook("Bluebook")
28
+ meta.aggregates.find { |aggregate| aggregate.hecks_name == "Syntax" }
29
+ end
30
+
31
+ # Reads one closed-set value object's declared members off the Syntax
32
+ # aggregate, as string-valued Hashes.
33
+ def rows(name)
34
+ syntax.value_objects.find { |vo| vo.hecks_name == name }
35
+ .members.map { |row| row.to_h.transform_values(&:to_s) }
36
+ end
37
+
38
+ # Delegates to SyntaxBoot.call's own cache instead of memoizing here —
39
+ # a second cache could lock in a stale set with no way to invalidate (ADR 0026).
40
+ def keywords = Bluebook::MetaValidator::SyntaxBoot.call[:keywords]
41
+
42
+ # Every declared Argument row.
43
+ def arguments = Bluebook::MetaValidator::SyntaxBoot.call[:arguments]
44
+
45
+ # Reads a row's declared status, defaulting to "admitted" when it declared none.
46
+ def status_of(row) = row[:status].to_s.empty? ? "admitted" : row[:status].to_s
47
+
48
+ # Whether a row is still current enough to appear in the reference.
49
+ def live?(row) = %w[admitted deprecated].include?(status_of(row))
50
+
51
+ # Every distinct context a keyword is declared in.
52
+ def contexts = keywords.map { |row| row[:context] }.uniq
53
+
54
+ # Derives a context's reference page filename.
55
+ def page_name(context) = "#{Naming.snake(context)}.md"
56
+
57
+ # Every reference page, rendered fresh: prose carried over from the
58
+ # committed pages, new words seeded with the sentinel, orphaned prose refused.
59
+ def pages(directory)
60
+ contexts.each_with_object({}) do |context, pages|
61
+ path = File.join(directory, page_name(context))
62
+ prose = File.exist?(path) ? harvest(File.read(path)) : {}
63
+ pages[page_name(context)] = render_page(context, prose, path)
64
+ end.merge("index.md" => render_index)
65
+ end
66
+
67
+ # Renders one context's page, grouped by word (not per form) so a
68
+ # two-form word doesn't repeat its heading and prose.
69
+ def render_page(context, prose, path)
70
+ words = keywords.select { |row| row[:context] == context }.group_by { |row| row[:word] }
71
+ orphans = prose.keys - words.keys - [PREAMBLE]
72
+ unless orphans.empty?
73
+ raise "#{path} carries prose for #{orphans.join(', ')}, which the language no longer " \
74
+ "declares in #{context} — deleting writing is a human's decision, so decide"
75
+ end
76
+
77
+ sections = words.map { |word, forms| render_word(forms, prose[word]) }
78
+ preamble = prose[PREAMBLE].to_s.strip
79
+ <<~PAGE
80
+ # #{context}
81
+
82
+ #{region_begin('page')}
83
+ #{context_lede(context)}
84
+
85
+ *The tables on this page are generated from the language's own
86
+ aggregate-local syntax tables (`lib/hecks/language/**/*.bluebook`)
87
+ by `hecks project_reference` — do not edit inside the markers. The prose
88
+ between them is hand-written and survives regeneration.*
89
+ #{GENERATED_END}
90
+ #{"\n#{preamble}\n" unless preamble.empty?}
91
+ #{sections.join("\n")}
92
+ PAGE
93
+ end
94
+
95
+ # The one-line description of where a context's words are typed.
96
+ def context_lede(context)
97
+ openers = keywords.select { |row| row[:opens] == context }
98
+ return "Words available at the top of a file." if context == "File"
99
+ return "Words available in the type position of an `attribute`." if context == "Type"
100
+
101
+ inside = openers.map { |row| "`#{row[:word]} do ... end`" }.uniq.join(" / ")
102
+ inside.empty? ? "Words available in the #{context} body." : "Words available inside #{inside}."
103
+ end
104
+
105
+ # One section per word, built off forms.first — spelling is the only
106
+ # column that differs between a word's two forms.
107
+ def render_word(forms, prose)
108
+ row = forms.first
109
+ table = argument_table(row)
110
+ facts = []
111
+ facts << "opens a `#{row[:opens]}` body" unless row[:opens].to_s.empty?
112
+ facts << "fills `#{row[:fills]}`" unless row[:fills].to_s.empty?
113
+ facts << "**status: #{status_of(row)}**" unless status_of(row) == "admitted"
114
+ facts << "was `#{row[:was]}`" unless row[:was].to_s.empty?
115
+ spellings = forms.map { |form| "`#{signature(form)}`" }.join(" / ")
116
+
117
+ <<~WORD
118
+ ## #{row[:word]}
119
+
120
+ #{generated_begin(row[:word])}
121
+ #{spellings}#{" — #{facts.join(', ')}" unless facts.empty?}
122
+ #{table}#{GENERATED_END}
123
+
124
+ #{prose_or_sentinel(prose)}
125
+ WORD
126
+ end
127
+
128
+ # Falls back to the TODO sentinel when a word has no prose yet.
129
+ def prose_or_sentinel(prose)
130
+ text = prose.to_s.strip
131
+ text.empty? ? TODO_SENTINEL : text
132
+ end
133
+
134
+ # The Argument rows declared for one Keyword row's word, in its context.
135
+ def word_arguments(row)
136
+ arguments.select { |arg| arg[:keyword] == row[:word] && arg[:context] == row[:context] }
137
+ end
138
+
139
+ # Builds the call spelling shown for one word: positional arguments
140
+ # then named ones, with a trailing `do ... end` unless its body is "none".
141
+ def signature(row)
142
+ positional = word_arguments(row).reject { |arg| arg[:at].to_s.empty? }
143
+ .sort_by { |arg| arg[:at].to_i }
144
+ .map { |arg| arg[:fills].to_s.empty? ? arg[:kind] : arg[:fills] }
145
+ named = word_arguments(row).select { |arg| arg[:at].to_s.empty? }
146
+ .map { |arg| "#{arg[:named]}:" }
147
+ parts = positional + named
148
+ base = parts.empty? ? row[:word] : "#{row[:word]} #{parts.join(', ')}"
149
+ row[:body].to_s == "none" ? base : "#{base} do ... end"
150
+ end
151
+
152
+ # Renders one word's arguments as a Markdown table, or "" if it declares none.
153
+ def argument_table(row)
154
+ args = word_arguments(row)
155
+ return "" if args.empty?
156
+
157
+ lines = ["", "| argument | kind | required | fills |", "|---|---|---|---|"]
158
+ args.each do |arg|
159
+ name = arg[:at].to_s.empty? ? "`#{arg[:named]}:`" : "positional #{arg[:at]}"
160
+ lines << "| #{name} | #{arg[:kind]} | #{arg[:required]} | #{arg[:fills]} |"
161
+ end
162
+ "#{lines.join("\n")}\n"
163
+ end
164
+
165
+ # Renders the reference index page, one linked entry per context.
166
+ def render_index
167
+ listed = contexts.map do |context|
168
+ count = keywords.select { |row| row[:context] == context }.map { |row| row[:word] }.uniq.size
169
+ "- [#{context}](#{page_name(context)}) — #{count} #{count == 1 ? 'word' : 'words'}"
170
+ end
171
+ <<~INDEX
172
+ # The DSL reference
173
+
174
+ One page per context — the place in a file where a word may be
175
+ typed. Generated by `hecks project_reference` from the Syntax chapter;
176
+ the prose between the generated markers is hand-written and
177
+ survives regeneration.
178
+
179
+ #{listed.join("\n")}
180
+ INDEX
181
+ end
182
+
183
+ # Prose keyed by word: everything between a section's generated region
184
+ # and the next `## ` heading. Starts collecting under `PREAMBLE` (not nil)
185
+ # so a page written before that region existed still parses unchanged.
186
+ # rubocop:disable-next Metrics/PerceivedComplexity
187
+ def harvest(text)
188
+ prose = {}
189
+ current = PREAMBLE
190
+ collecting = false
191
+ buffer = []
192
+
193
+ # A `## ` line inside a fence is not a heading — it's a comment in
194
+ # the runnable example, not a section break.
195
+ in_fence = false
196
+
197
+ text.each_line do |line|
198
+ in_fence = !in_fence if line.start_with?("```")
199
+
200
+ if !in_fence && (match = line.match(/\A## (\S+)\s*\z/))
201
+ prose[current] = buffer.join.strip if current && collecting
202
+ current = match[1]
203
+ collecting = false
204
+ buffer = []
205
+ elsif line.include?(GENERATED_END)
206
+ collecting = true
207
+ elsif collecting
208
+ buffer << line
209
+ end
210
+ end
211
+ prose[current] = buffer.join.strip if current && collecting
212
+ prose.reject { |_word, text_| text_.empty? || text_ == TODO_SENTINEL }
213
+ end
214
+
215
+ # Renders every reference page and writes each to `directory`.
216
+ def write!(directory)
217
+ FileUtils.mkdir_p(directory)
218
+ pages(directory).each do |name, content|
219
+ File.write(File.join(directory, name), content)
220
+ end
221
+ end
222
+
223
+ # README's own generated regions, keyed by region id instead of a word.
224
+ def readme_regions(root)
225
+ {
226
+ "guides" => guide_index(root),
227
+ "reference" => reference_index(root),
228
+ "corpus" => corpus_roster(root),
229
+ "diagrams" => diagram_showcase(root)
230
+ }
231
+ end
232
+
233
+ # Lists every committed guide, linked and titled by its own heading.
234
+ def guide_index(root)
235
+ paths = Dir.glob(File.join(root, "docs/implemented/guides/*.md"))
236
+ .reject { |p| %w[AUTHORING.md].include?(File.basename(p)) }
237
+ lines = paths.map do |path|
238
+ heading = File.foreach(path).find { |line| line.start_with?("# ") }
239
+ title = heading ? heading.sub(/\A#\s*/, "").strip : File.basename(path)
240
+ "- [#{title}](docs/implemented/guides/#{File.basename(path)})"
241
+ end
242
+ lines.join("\n")
243
+ end
244
+
245
+ # Links the reference index, with its context count.
246
+ def reference_index(_root)
247
+ count = contexts.size
248
+ "[The DSL reference](docs/implemented/reference/index.md) — #{count} contexts, generated from " \
249
+ "the aggregate-local tables under `lib/hecks/language/` and held to them by " \
250
+ "`spec/reference_golden_spec.rb`."
251
+ end
252
+
253
+ # Quotes the real, committed diagram file rather than re-deriving one,
254
+ # so this can't drift from `spec/diagrams_spec.rb`'s own check.
255
+ def diagram_showcase(root)
256
+ lifecycle = File.read(File.join(root, "docs/generated/diagrams/pizzas/Order_lifecycle.mmd")).strip
257
+ <<~MARKDOWN.strip
258
+ `hecks project_diagrams` reads a booted domain's own declaration and draws it as Mermaid — nine kinds so far: `<Name>_lifecycle.mmd`, `relationships.mmd`, `dispatch.mmd`, `roles.mmd`, `ports.mmd`, `read_models.mmd`, `<Name>_surface.mmd` (what a command does, and what it writes), `<Name>_saga.mmd`, and `frameworks.mmd`. Nothing hand-drawn — the same reason a domain is data at all. Order's own lifecycle, straight off the bluebook above:
259
+
260
+ ```mermaid
261
+ #{lifecycle}
262
+ ```
263
+
264
+ The full set for every domain in this checkout — `examples/pizzas`, `examples/banking` — lives in [`docs/generated/diagrams/`](docs/generated/diagrams/), held to the declaration by `spec/diagrams_spec.rb` the same drift-refusing way this page is held to its own source.
265
+ MARKDOWN
266
+ end
267
+
268
+ # Lists every example domain with a `.bluebook` file, with its own declared vision.
269
+ def corpus_roster(root)
270
+ dirs = Dir.glob(File.join(root, "examples/*/"))
271
+ lines = dirs.filter_map do |dir|
272
+ name = File.basename(dir.chomp("/"))
273
+ bluebooks = Dir.glob(File.join(dir, "bluebook/*.bluebook"))
274
+ bluebooks = Dir.glob(File.join(dir, "*.bluebook")) if bluebooks.empty?
275
+ next if bluebooks.empty?
276
+
277
+ vision = bluebooks.filter_map { |bluebook| File.read(bluebook)[/vision\s+"([^"]*)"/, 1] }.first
278
+ "- **#{name}** — #{vision}"
279
+ end
280
+ lines.join("\n")
281
+ end
282
+
283
+ # Replaces every generated region inside `text` with its freshly rendered
284
+ # content, leaving the hand-written parts of README untouched.
285
+ def render_readme(root, text)
286
+ readme_regions(root).reduce(text) do |current, (id, content)|
287
+ pattern = /#{Regexp.escape(region_begin(id))}.*?#{Regexp.escape(GENERATED_END)}/m
288
+ current.sub(pattern) { "#{region_begin(id)}\n#{content}\n#{GENERATED_END}" }
289
+ end
290
+ end
291
+
292
+ # Regenerates README's generated regions in place.
293
+ def write_readme!(root)
294
+ path = File.join(root, "README.md")
295
+ File.write(path, render_readme(root, File.read(path)))
296
+ end
297
+
298
+ # Matches a runnable `ruby`/`ruby bluebook`/`ruby boot` fenced example;
299
+ # `ruby skip` and hidden boot blocks are setup, not an example, and don't count.
300
+ EXAMPLE_FENCE = /^```ruby(?: bluebook| boot)?[ \t]*$/
301
+
302
+ # Whether a word's prose carries a runnable example.
303
+ def exemplified?(prose) = prose.to_s.match?(EXAMPLE_FENCE)
304
+
305
+ # Every live word paired with its prose; shared by both coverage gates
306
+ # so their page-walks can't drift apart.
307
+ def live_words(directory)
308
+ rows = contexts.flat_map do |context|
309
+ path = File.join(directory, page_name(context))
310
+ prose = File.exist?(path) ? harvest(File.read(path)) : {}
311
+ keywords.select { |row| row[:context] == context && live?(row) }
312
+ .map { |row| [row[:word], context, prose[row[:word]]] }
313
+ end
314
+ rows.uniq { |word, context, _| [word, context] }
315
+ end
316
+
317
+ # Disambiguates a word by the context it is declared in.
318
+ def name_of(word, context) = "#{word} (#{context})"
319
+
320
+ # The coverage gate's question: every live word with no prose yet.
321
+ def undocumented(directory)
322
+ live_words(directory).reject { |_word, _context, prose| prose }
323
+ .map { |word, context, _| name_of(word, context) }
324
+ end
325
+
326
+ # The second coverage gate: prose alone can go stale or describe
327
+ # unwired behavior; a runnable example is the only doc that can go red.
328
+ def unexemplified(directory)
329
+ live_words(directory).reject { |_word, _context, prose| exemplified?(prose) }
330
+ .map { |word, context, _| name_of(word, context) }
331
+ end
332
+
333
+ # Both coverage gates, worded for a person: each gap section lists the words that owe it,
334
+ # and a word missing both kinds is listed twice, one repair each.
335
+ #
336
+ # @param directory [String] the reference pages
337
+ # @return [Array(Boolean, String)] whether every live word is covered, and what to print
338
+ def coverage_report(directory)
339
+ gaps = [[undocumented(directory), "no prose — write their sections"],
340
+ [unexemplified(directory), "no running example — write one in each word's own section"]]
341
+ sections = gaps.reject { |words, _| words.empty? }.map do |words, owed|
342
+ head = "#{words.size} live #{words.size == 1 ? 'word carries' : 'words carry'} #{owed}:\n"
343
+ "#{head}#{words.map { |word| " #{word}\n" }.join}\n"
344
+ end
345
+ return [true, "every live word carries prose and a running example."] if sections.empty?
346
+
347
+ [false, sections.join]
348
+ end
349
+ end
350
+ end
351
+ end