hecks 2.9.0 → 3.0.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 (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 +4 -2
  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 +7 -4
  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 +6 -3
  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
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "json"
5
+ require "hecks/release/runner/commands"
6
+ require_relative "secret_vault"
7
+
8
+ module Hecks
9
+ module Adapters
10
+ module Codebase
11
+ # The `GemRegistry` adapter: what a release does with rubygems.org and the `gem` program.
12
+ #
13
+ # It asks the registry which versions are out (over `curl`, no credentials), builds the gem
14
+ # from the checkout's gemspec, and pushes it with the key the SecretVault holds. A built gem
15
+ # file is always deleted again, whether the push worked or not.
16
+ class GemRegistry
17
+ # Where RubyGems lists the published versions of the gem.
18
+ VERSIONS_URL = "https://rubygems.org/api/v1/versions/hecks.json"
19
+
20
+ # The env file `op run` reads the push key from, relative to the checkout.
21
+ ENV_FILE = "release/gem_push.env"
22
+
23
+ # @param root [String] the checkout's root
24
+ # @param commands [#capture, #run!] starts each process
25
+ # @param vault [SecretVault, nil] holds the push key; one over `commands` when nil
26
+ def initialize(root:, commands:, vault: nil)
27
+ @root = root
28
+ @commands = commands
29
+ @vault = vault || SecretVault.new(commands: commands)
30
+ end
31
+
32
+ # Says whether RubyGems lists the gem at a version.
33
+ #
34
+ # @param version [String] the version to look for
35
+ # @return [Boolean] true when it is published
36
+ # @raise [Hecks::Release::Runner::Refusal] when RubyGems cannot be reached or answers with
37
+ # something that is not a version list
38
+ def published?(version)
39
+ result = @commands.capture("curl", "-fsS", VERSIONS_URL, chdir: @root)
40
+ unless result.success?
41
+ raise Release::Runner::Refusal, "could not list published hecks versions (#{result.stderr.strip}); " \
42
+ "check the network and re-run"
43
+ end
44
+
45
+ JSON.parse(result.stdout).any? { |entry| entry["number"] == version }
46
+ rescue JSON::ParserError
47
+ raise Release::Runner::Refusal, "RubyGems answered with something other than a version list; " \
48
+ "re-run in a minute"
49
+ end
50
+
51
+ # Builds the gem, to prove it builds, and deletes it again.
52
+ #
53
+ # @param version [String] the version being released
54
+ # @return [void]
55
+ # @raise [Hecks::Release::Runner::CommandFailed] when the build fails
56
+ def build_only!(version)
57
+ @commands.run!("gem", "build", "hecks.gemspec", chdir: @root)
58
+ ensure
59
+ discard(version)
60
+ end
61
+
62
+ # Builds the gem and pushes it to rubygems.org, then deletes the file.
63
+ #
64
+ # @param version [String] the version being released
65
+ # @return [void]
66
+ # @raise [Hecks::Release::Runner::CommandFailed] when the build or the push fails
67
+ def push!(version)
68
+ @commands.run!("gem", "build", "hecks.gemspec", chdir: @root)
69
+ @vault.run!(ENV_FILE, "gem", "push", "hecks-#{version}.gem", chdir: @root)
70
+ ensure
71
+ discard(version)
72
+ end
73
+
74
+ private
75
+
76
+ def discard(version) = FileUtils.rm_f(File.join(@root, "hecks-#{version}.gem"))
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "tree"
4
+ require "hecks/kernel_capabilities"
5
+
6
+ module Hecks
7
+ module Adapters
8
+ module Codebase
9
+ # What Codebase's `KernelRun` asks of the working tree: projecting the capability enums the
10
+ # hand-written Rust kernel is matched against, and checking that each capability the grammar
11
+ # admits has its hand-written file.
12
+ #
13
+ # The projection is built in memory by `KernelCapabilities`, the code
14
+ # `hecks project_kernel_capabilities` runs, and compared with the tree; it writes only when
15
+ # confirmed. The coverage check reads the tree and writes nothing.
16
+ module KernelTables
17
+ # Every operation this family carries out.
18
+ OPERATIONS = %w[project_kernel_capabilities measure_kernel_coverage].freeze
19
+
20
+ module_function
21
+
22
+ # Carries out one operation.
23
+ #
24
+ # @param operation [String] one of `OPERATIONS`
25
+ # @param held [Hash] the `KernelRun` record's fields
26
+ # @param tree [Tree] the working tree, already known to be a hecks checkout
27
+ # @param shell [#capture] unused; the family starts no child process
28
+ # @return [String] the drift report, or the coverage report
29
+ # @raise [ConsoleCapture::Failure] when a capability has no kernel file
30
+ def call(operation, held, tree, shell: nil)
31
+ _ = shell
32
+ confirm = (held[:confirm].is_a?(Hash) ? held[:confirm][:value] : held[:confirm]) == true
33
+ return coverage(tree) if operation == "measure_kernel_coverage"
34
+
35
+ result = KernelCapabilities.build(root: tree.root)
36
+ tree.apply(result.content, stale: result.stale, confirm: confirm)
37
+ end
38
+
39
+ # Lists every capability the live grammar admits with whether its file is there.
40
+ #
41
+ # @param tree [Tree] the checkout
42
+ # @return [String] one line for each capability, and a verdict
43
+ # @raise [ConsoleCapture::Failure] when any capability has no file
44
+ def coverage(tree)
45
+ lines, verdict, complete = KernelCapabilities.coverage_report(root: tree.root)
46
+ report = [lines, "", verdict].join("\n")
47
+ return report if complete
48
+
49
+ raise ConsoleCapture::Failure, report
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,240 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require_relative "tree"
5
+ require_relative "../shell"
6
+ require "hecks/projection_files"
7
+ require "hecks/grammar/evolve"
8
+
9
+ module Hecks
10
+ module Adapters
11
+ module Codebase
12
+ # What Codebase's `LanguageRun` asks of the working tree: projecting the language's tables
13
+ # and walking a word through its lifecycle.
14
+ #
15
+ # A projection is built in memory by {ProjectionFiles} (the same code the `hecks project_*`
16
+ # scripts run) and compared with the tree; it writes only when confirmed. An evolution edits
17
+ # the syntax tables through {Grammar::Evolve}: unconfirmed it is rehearsed against an
18
+ # in-memory copy and reports the edit it would make; confirmed it regenerates the golden IR
19
+ # and holds the gates in a child process, and puts every file back if a gate refuses.
20
+ module Language
21
+ # The projection each operation runs.
22
+ PROJECTIONS = {
23
+ "project_model" => :model,
24
+ "project_vocabulary" => :vocabulary,
25
+ "project_rust_vocabulary" => :rust_vocabulary,
26
+ "project_reserved_names" => :reserved_names,
27
+ "project_parser_table" => :parser_table,
28
+ "project_bootstrap_table" => :bootstrap_table,
29
+ "project_field_hints" => :field_hints,
30
+ "project_expression_tables" => :expression_tables,
31
+ "project_reference" => :reference
32
+ }.freeze
33
+
34
+ # The status each word or argument operation moves a row to.
35
+ STATUSES = { "admit" => "admitted", "deprecate" => "deprecated", "retire" => "retired",
36
+ "admit_argument" => "admitted", "deprecate_argument" => "deprecated",
37
+ "retire_argument" => "retired" }.freeze
38
+
39
+ # Every operation this family carries out.
40
+ OPERATIONS = (PROJECTIONS.keys + %w[propose rename propose_argument] + STATUSES.keys).freeze
41
+
42
+ # The specs that hold a grammar edit: the syntax rows agree with the builder, the lifecycle
43
+ # holds, and the golden IR is the one the language produces.
44
+ GATES = %w[spec/syntax_conformance_spec.rb spec/syntax_lifecycle_spec.rb spec/ir_golden_spec.rb].freeze
45
+
46
+ # The golden IR a grammar edit regenerates.
47
+ GOLDEN = "spec/golden/ir/Bluebook.json"
48
+
49
+ # What the tool says after a proposal or a rename, which still needs hand work.
50
+ FOLLOW_UP = {
51
+ "propose" => "Proposed. It reaches no projection until admitted. Before `hecks admit`:\n " \
52
+ "1. teach the %<context>s builder the word (and its spec/dsl_spec example)",
53
+ "rename" => "Renamed. The old spelling keeps parsing — that is the point. Before this holds:\n " \
54
+ "1. alias the new word to the old in the %<context>s builder " \
55
+ "(alias_method :%<new_name>s, :%<word>s)\n " \
56
+ "2. add the identical-IR example to spec/dsl_spec.rb",
57
+ "propose_argument" => "Proposed. It reaches no projection until admitted. Before " \
58
+ "`hecks admit_argument`:\n " \
59
+ "1. teach the %<context>s builder's %<word>s method the argument"
60
+ }.freeze
61
+
62
+ module_function
63
+
64
+ # Carries out one operation.
65
+ #
66
+ # @param operation [String] one of `OPERATIONS`
67
+ # @param held [Hash] the `LanguageRun` record's fields
68
+ # @param tree [Tree] the working tree, already known to be a hecks checkout
69
+ # @param shell [#capture] starts the gates' child process
70
+ # @return [String] what was found, or done
71
+ # @raise [ConsoleCapture::Failure] when the language refuses the edit, or a gate does
72
+ def call(operation, held, tree, shell: Shell.new)
73
+ args = held.transform_values { |value| value.is_a?(Hash) ? value[:value] : value }
74
+ return project(operation, args, tree) if PROJECTIONS.key?(operation)
75
+
76
+ evolve(operation, args, tree, shell)
77
+ end
78
+
79
+ # Where every word stands: the rows not simply admitted, and the renamed ones.
80
+ #
81
+ # @return [String] a count, then a line for each row that is moving or renamed
82
+ def word_status
83
+ rows = Grammar::Evolve.keyword_rows
84
+ moving = rows.reject { |row| row[:status] == "admitted" }
85
+ renamed = rows.reject { |row| row[:was].to_s.empty? }
86
+ lines = ["#{rows.size} keyword rows; #{moving.size} not simply admitted; #{renamed.size} renamed"]
87
+ lines += moving.map { |row| " #{row[:status].ljust(10)} #{row[:context]}.#{row[:word]}" }
88
+ lines += renamed.map { |row| " renamed #{row[:context]}.#{row[:word]} (was #{row[:was]})" }
89
+ lines.join("\n")
90
+ end
91
+
92
+ # @param operation [String] a projection's operation
93
+ # @param args [Hash] the record's plain fields
94
+ # @param tree [Tree] the checkout
95
+ # @return [String] the projected text for `stdout`, or the drift report
96
+ # @raise [ConsoleCapture::Failure] when the language cannot be projected
97
+ def project(operation, args, tree)
98
+ result = ProjectionFiles.build(PROJECTIONS.fetch(operation), root: tree.root)
99
+ return result.content.values.first if args[:stdout]
100
+
101
+ tree.apply(result.content, stale: result.stale, confirm: args[:confirm] == true)
102
+ rescue ProjectionFiles::Refused => e
103
+ raise ConsoleCapture::Failure, e.message
104
+ end
105
+
106
+ # @param operation [String] a word or argument operation
107
+ # @param args [Hash] the record's plain fields
108
+ # @param tree [Tree] the checkout
109
+ # @param shell [#capture] starts the gates
110
+ # @return [String] the rehearsed edit, or the confirmed edit's outcome
111
+ # @raise [ConsoleCapture::Failure] when the language or a gate refuses
112
+ def evolve(operation, args, tree, shell)
113
+ change = -> { edit(operation, args) }
114
+ return rehearse(change, tree) unless args[:confirm] == true
115
+
116
+ guarded(operation, args, tree, shell, change)
117
+ rescue Grammar::Evolve::Refusal => e
118
+ raise ConsoleCapture::Failure, e.message
119
+ end
120
+
121
+ # @param edit [#call] the edit to make
122
+ # @param tree [Tree] the checkout
123
+ # @return [String] each file the edit would change, with its lines added and removed
124
+ def rehearse(edit, tree)
125
+ changed = Grammar::Evolve.rehearse(&edit)
126
+ lines = changed.map do |path, text|
127
+ before = File.read(path).lines
128
+ after = text.lines
129
+ " changed #{tree.relative(path)} (+#{(after - before).size} -#{(before - after).size} lines)"
130
+ end
131
+ ["dry run, #{changed.size} file changes (add --confirm to make them, regenerate the golden and hold the gates):",
132
+ *lines].join("\n")
133
+ end
134
+
135
+ # Makes the edit, regenerates the golden and holds the gates; puts every file back if the
136
+ # edit or a gate refuses.
137
+ #
138
+ # @param operation [String] the operation, for the follow-up it prints
139
+ # @param args [Hash] the record's plain fields
140
+ # @param tree [Tree] the checkout
141
+ # @param shell [#capture] starts the child processes
142
+ # @param edit [#call] the edit to make
143
+ # @return [String] what the gates held, and what is left to do by hand
144
+ # @raise [ConsoleCapture::Failure] when the edit or a gate refuses; nothing was changed
145
+ def guarded(operation, args, tree, shell, edit)
146
+ paths = Grammar::Evolve.syntax_paths + [tree.path(GOLDEN)]
147
+ snapshots = paths.to_h { |path| [path, File.read(path)] }
148
+ Grammar::Evolve.restore_on_raise(paths, &edit)
149
+ regenerate(tree, shell)
150
+ gates = shell.capture("bundle", "exec", "rspec", *GATES, chdir: tree.root)
151
+ unless gates.ok?
152
+ snapshots.each { |path, text| File.write(path, text) }
153
+ raise ConsoleCapture::Failure,
154
+ "RESTORED — the gates refused, and the failures are the checklist. Nothing was " \
155
+ "changed.\n#{[gates.out, gates.err].join.strip}"
156
+ end
157
+ ["#{args[:context]}.#{args[:word]} — the gates hold. Regenerated projections and the golden " \
158
+ "are in the tree.", follow_up(operation, args)].compact.join("\n")
159
+ end
160
+
161
+ # @param tree [Tree] the checkout
162
+ # @param shell [#capture] starts the child process
163
+ # @return [void]
164
+ def regenerate(tree, shell)
165
+ shell.capture("bundle", "exec", "rspec", "spec/ir_golden_spec.rb", env: { "GOLDEN" => "rewrite" },
166
+ chdir: tree.root)
167
+ end
168
+
169
+ # @param operation [String] the operation
170
+ # @param args [Hash] the record's plain fields
171
+ # @return [String, nil] what still needs hand work, if anything
172
+ def follow_up(operation, args)
173
+ text = FOLLOW_UP[operation] or return nil
174
+
175
+ format(text, context: args[:context], new_name: args[:new_name], word: args[:word])
176
+ end
177
+
178
+ # Makes one edit to the syntax tables.
179
+ #
180
+ # @param operation [String] a word or argument operation
181
+ # @param args [Hash] the record's plain fields
182
+ # @return [void]
183
+ # @raise [Grammar::Evolve::Refusal] when the tables refuse the edit
184
+ def edit(operation, args)
185
+ case operation
186
+ when "propose" then propose(args)
187
+ when "rename" then rename(args)
188
+ when "propose_argument" then propose_argument(args)
189
+ when "admit", "deprecate", "retire" then set_status(operation, args)
190
+ else set_argument_status(operation, args)
191
+ end
192
+ end
193
+
194
+ # @param args [Hash] the record's plain fields
195
+ # @return [void]
196
+ def propose(args)
197
+ Grammar::Evolve.propose(word: args[:word], context: args[:context], body: args[:body] || "none",
198
+ inner: args[:inner] || "", opens: args[:opens] || "", fills: args[:fills] || "")
199
+ end
200
+
201
+ # @param args [Hash] the record's plain fields
202
+ # @return [void]
203
+ # @raise [Grammar::Evolve::Refusal] when no new name was given
204
+ def rename(args)
205
+ to = args[:new_name] or refuse("a rename goes somewhere: new_name=")
206
+ Grammar::Evolve.rename(word: args[:word], context: args[:context], to: to)
207
+ end
208
+
209
+ # @param args [Hash] the record's plain fields
210
+ # @return [void]
211
+ def propose_argument(args)
212
+ Grammar::Evolve.propose_argument(keyword: args[:word], context: args[:context], kind: args[:kind],
213
+ required: args[:required] || "false", at: args[:at] || "",
214
+ named: args[:named] || "", fills: args[:fills] || "",
215
+ pairs_shape: args[:pairs_shape])
216
+ end
217
+
218
+ # @param operation [String] `admit`, `deprecate` or `retire`
219
+ # @param args [Hash] the record's plain fields
220
+ # @return [void]
221
+ def set_status(operation, args)
222
+ Grammar::Evolve.set_status(word: args[:word], context: args[:context], to: STATUSES.fetch(operation))
223
+ end
224
+
225
+ # @param operation [String] the argument form of `admit`, `deprecate` or `retire`
226
+ # @param args [Hash] the record's plain fields
227
+ # @return [void]
228
+ def set_argument_status(operation, args)
229
+ Grammar::Evolve.set_argument_status(keyword: args[:word], context: args[:context],
230
+ to: STATUSES.fetch(operation), at: args[:at] || "",
231
+ named: args[:named] || "")
232
+ end
233
+
234
+ # @param message [String] why the edit cannot be made
235
+ # @raise [Grammar::Evolve::Refusal] always
236
+ def refuse(message) = raise(Grammar::Evolve::Refusal, message)
237
+ end
238
+ end
239
+ end
240
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tempfile"
4
+ require "hecks/release/runner/commands"
5
+ require_relative "secret_vault"
6
+
7
+ module Hecks
8
+ module Adapters
9
+ module Codebase
10
+ # The `NpmRegistry` adapter: what a release does with npm and the `@hecks/client` package.
11
+ #
12
+ # It asks npm whether a version is out (no credentials), installs the package's
13
+ # dependencies, and publishes it, either as a dry run (a pack, no credentials) or with the
14
+ # token the SecretVault holds. The token bypasses 2FA, so the publish runs with
15
+ # `--auth-type=web` and its output is not captured: the approval link reaches the terminal.
16
+ class NpmRegistry
17
+ # The package, by name.
18
+ PACKAGE = "@hecks/client"
19
+
20
+ # Where the package stands, relative to the checkout.
21
+ PACKAGE_DIR = "packages/hecks-client"
22
+
23
+ # The env file `op run` reads the publish token from, relative to the checkout.
24
+ ENV_FILE = "release/npm_publish.env"
25
+
26
+ # The npmrc line that reads the token from the environment `op run` sets.
27
+ USERCONFIG_LINE = "//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}"
28
+
29
+ # @param root [String] the checkout's root
30
+ # @param commands [#capture, #run!] starts each process
31
+ # @param vault [SecretVault, nil] holds the publish token; one over `commands` when nil
32
+ def initialize(root:, commands:, vault: nil)
33
+ @root = root
34
+ @commands = commands
35
+ @vault = vault || SecretVault.new(commands: commands)
36
+ end
37
+
38
+ # Says whether npm lists the package at a version.
39
+ #
40
+ # @param version [String] the version to look for
41
+ # @return [Boolean] true when it is published
42
+ # @raise [Hecks::Release::Runner::Refusal] when npm fails for any reason other than the
43
+ # version not existing
44
+ def published?(version)
45
+ result = @commands.capture("npm", "view", "#{PACKAGE}@#{version}", "version", chdir: @root)
46
+ return result.stdout.strip == version if result.success?
47
+ return false if result.stderr.include?("E404")
48
+
49
+ raise Release::Runner::Refusal,
50
+ "could not check npm for #{PACKAGE}@#{version} (#{result.stderr.strip.lines.first}); " \
51
+ "check the network and re-run"
52
+ end
53
+
54
+ # @return [Boolean] whether the package's dependencies are already installed
55
+ def installed?
56
+ Dir.exist?(File.join(package_dir, "node_modules"))
57
+ end
58
+
59
+ # Installs the package's dependencies with `npm ci`.
60
+ #
61
+ # @return [void]
62
+ # @raise [Hecks::Release::Runner::CommandFailed] when it fails
63
+ def install!
64
+ @commands.run!("npm", "ci", chdir: package_dir)
65
+ end
66
+
67
+ # Packs the package and publishes nothing.
68
+ #
69
+ # @return [void]
70
+ # @raise [Hecks::Release::Runner::CommandFailed] when the pack fails
71
+ def dry_publish!
72
+ @commands.run!("npm", "publish", "--dry-run", "--access", "public", chdir: package_dir)
73
+ end
74
+
75
+ # Publishes the package with the token the vault holds.
76
+ #
77
+ # @return [void]
78
+ # @raise [Hecks::Release::Runner::CommandFailed] when the publish fails
79
+ def publish!
80
+ with_userconfig do |userconfig|
81
+ @vault.run!(File.join(@root, ENV_FILE), "npm", "publish", "--access", "public", "--auth-type=web",
82
+ "--userconfig", userconfig, chdir: package_dir)
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ def package_dir = File.join(@root, PACKAGE_DIR)
89
+
90
+ def with_userconfig
91
+ file = Tempfile.new(["hecks-npmrc", ".npmrc"])
92
+ file.chmod(0o600)
93
+ file.write("#{USERCONFIG_LINE}\n")
94
+ file.flush
95
+ yield file.path
96
+ ensure
97
+ file&.close!
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "stringio"
4
+ require "hecks/release/runner"
5
+ require_relative "tree"
6
+ require_relative "gem_registry"
7
+ require_relative "secret_vault"
8
+ require_relative "release_facts"
9
+ require_relative "../console_capture"
10
+
11
+ module Hecks
12
+ module Adapters
13
+ module Codebase
14
+ # What Codebase's `PublishingRun` asks of the working tree once the release is cleared:
15
+ # cutting the release, or pushing the gem alone.
16
+ #
17
+ # A release is `Hecks::Release::Runner`: it tags the release commit, publishes the gem and
18
+ # gets the client package published, skipping what a registry already lists. Its rules were
19
+ # judged before it starts (the givens of `Clear`), so it is handed the facts they cleared and
20
+ # checks only that its tools are installed. Unconfirmed it is the dry run: every check and
21
+ # build, and nothing tagged, pushed or published. The answer says which steps were really
22
+ # carried out, so the journal can advance the root `Release`: tagged, published, verified.
23
+ module Publishing
24
+ # Every operation this family carries out.
25
+ OPERATIONS = %w[publish publish_gem].freeze
26
+
27
+ class << self
28
+ # @return [#capture, #run!, nil] starts every process a release runs; the real ones
29
+ # (`Hecks::Release::Runner::Commands`) when nil. A spec replaces it, so nothing is
30
+ # tagged, pushed, published or fetched.
31
+ attr_accessor :commands
32
+
33
+ # @return [Hash] extra arguments for the release, such as `pause:` and `now:`, so a spec
34
+ # can wait for the registry without sleeping
35
+ attr_accessor :release_options
36
+ end
37
+
38
+ module_function
39
+
40
+ # The facts of a tree that is not a checkout: placeholders that satisfy the value objects,
41
+ # so the checkout rule is the one that refuses.
42
+ #
43
+ # @param operation [String] `publish` or `publish_gem`
44
+ # @return [Hash] every fact a release is judged by, none of them true
45
+ def no_facts(operation)
46
+ none = { value: "none" }
47
+ { operation: { value: operation }, version: { value: "0.0.0" }, client_version: { value: "0.0.0" },
48
+ ir_version: { value: "0.0.0" }, ships_from: { path: "none" }, branch: none, head: none,
49
+ tag_state: none, on_origin: { value: false }, clean: { value: false }, changelog: { value: false } }
50
+ end
51
+
52
+ # Carries out an accepted request.
53
+ #
54
+ # @param operation [String] `publish` or `publish_gem`
55
+ # @param held [Hash] the `PublishingRun` record's fields
56
+ # @param tree [Tree] the working tree, already known to be a hecks checkout
57
+ # @param shell [#capture, nil] unused; the release starts its own processes
58
+ # @return [Hash] `report`, `tagged`, `published` and `verified`, and `version` for the
59
+ # `Release` record
60
+ # @raise [ConsoleCapture::Failure] when the release refuses or a step fails, with its words
61
+ def call(operation, held, tree, shell: nil)
62
+ _ = shell
63
+ args = held.transform_values { |value| value.is_a?(Hash) && value.key?(:value) ? value[:value] : value }
64
+ commands = Publishing.commands || Release::Runner::Commands.new
65
+ if operation == "publish_gem"
66
+ publish_gem(args, tree, commands)
67
+ else
68
+ publish(args, tree, commands)
69
+ end
70
+ end
71
+
72
+ # @param args [Hash] the record's plain fields
73
+ # @param tree [Tree] the checkout
74
+ # @param commands [#capture, #run!] starts each process
75
+ # @return [Hash] what the release did
76
+ # @raise [ConsoleCapture::Failure] when the release stops before any real step; one that
77
+ # stops after a real step answers, with `succeeded` false, so the steps are recorded
78
+ def publish(args, tree, commands)
79
+ out = StringIO.new
80
+ err = StringIO.new
81
+ runner = release(args, tree, commands, out, err)
82
+ status = runner.call
83
+ text = [out.string, err.string].map(&:strip).reject(&:empty?).join("\n")
84
+ raise ConsoleCapture::Failure, text if !status.zero? && runner.steps.empty?
85
+
86
+ outcome(text, args, tagged: runner.steps.include?(:tagged), published: runner.steps.include?(:gem),
87
+ verified: runner.verified, succeeded: status.zero?)
88
+ end
89
+
90
+ # @return [Hecks::Release::Runner] the release, over the facts the run was cleared by
91
+ def release(args, tree, commands, out, err)
92
+ flag = ->(name) { args[name] == true }
93
+ options = Release::Runner::Options.new(dry_run: args[:confirm] != true, gem_only: flag.call(:gem_only),
94
+ npm_only: flag.call(:npm_only), npm_local: flag.call(:npm_local),
95
+ no_wait: flag.call(:no_wait), yes: true)
96
+ facts = Release::Runner::Preflight::Facts.new(version: args[:version], sha: args[:head])
97
+ Release::Runner.new(root: tree.root, options: options, commands: commands, input: StringIO.new, out: out,
98
+ err: err, facts: facts, **(Publishing.release_options || {}))
99
+ rescue ArgumentError => e
100
+ raise ConsoleCapture::Failure, e.message
101
+ end
102
+
103
+ # Pushes the gem alone, or builds it and deletes it again when unconfirmed.
104
+ #
105
+ # @param args [Hash] the record's plain fields
106
+ # @param tree [Tree] the checkout
107
+ # @param commands [#capture, #run!] starts each process
108
+ # @return [Hash] what was done
109
+ # @raise [ConsoleCapture::Failure] when the build or the push fails, or the vault is missing
110
+ def publish_gem(args, tree, commands)
111
+ registry = GemRegistry.new(root: tree.root, commands: commands)
112
+ version = args[:version]
113
+ return dry_gem(registry, version, args) unless args[:confirm] == true
114
+ raise ConsoleCapture::Failure, "1Password CLI (op) not found; install it: brew install 1password-cli" unless
115
+ SecretVault.new(commands: commands).installed?
116
+
117
+ registry.push!(version)
118
+ verified = registry.published?(version)
119
+ outcome("Released hecks #{version}.", args, tagged: false, published: true, verified: verified)
120
+ rescue Release::Runner::CommandFailed, Release::Runner::Refusal => e
121
+ raise ConsoleCapture::Failure, e.message
122
+ end
123
+
124
+ # @return [Hash] the answer of a gem push that only built the gem
125
+ def dry_gem(registry, version, args)
126
+ registry.build_only!(version)
127
+ outcome("dry run, built hecks-#{version}.gem and deleted it; nothing was pushed (add --confirm)",
128
+ args, tagged: false, published: false, verified: false)
129
+ rescue Release::Runner::CommandFailed => e
130
+ raise ConsoleCapture::Failure, e.message
131
+ end
132
+
133
+ # @return [Hash] the answer the journal records: a report, what was done, and the release
134
+ def outcome(text, args, tagged:, published:, verified:, succeeded: true)
135
+ { report: { value: text }, tagged: { value: tagged }, published: { value: published },
136
+ verified: { value: verified }, succeeded: { value: succeeded }, version: { value: args[:version] },
137
+ ir_version: { value: args[:ir_version] }, ships_from: args[:ships_from] }
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end