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,102 @@
1
+ require_relative "naming"
2
+ require_relative "runtime/registry"
3
+ require_relative "bluebook/meta_validator"
4
+
5
+ module Hecks
6
+ # The chapters the gem carries that a hecksagon can attach by name (ADR 0080): the language
7
+ # declared in itself, Expression, Tenancy, Deploy and QualityControl. Framework members stay in
8
+ # `Framework`.
9
+ #
10
+ # A chapter is named by the `Hecks.bluebook "Name"` header of its files, and may span several.
11
+ # Beside its bluebook a chapter may carry what every hecksagon attaching it needs, whatever
12
+ # its store: `<snake_name>.ports.hecksagon` (the ports it declares, as a `Hecks.hecksagon`
13
+ # block that merges into the attaching one) and `adapters/*.adapter` (the adapters binding them).
14
+ module Chapters
15
+ # Where attachable chapters live, relative to `lib/hecks/`.
16
+ GLOBS = %w[language/**/*.bluebook grammar/expression.bluebook tenancy/bluebook/*.bluebook
17
+ deploy/bluebook/*.bluebook quality_control/*.bluebook].freeze
18
+
19
+ # Every attachable chapter, by name, with the files that declare it.
20
+ #
21
+ # @return [Hash{String => Array<String>}] chapter name to its files' absolute paths, sorted
22
+ def self.index
23
+ @index ||= GLOBS.flat_map { |glob| Dir.glob(File.join(__dir__, glob)) }.sort
24
+ .group_by { |path| chapter_name(path) }
25
+ .reject { |name, _| name.nil? }
26
+ .freeze
27
+ end
28
+
29
+ # Loads a chapter's files into the current registry, unless it already holds the chapter.
30
+ #
31
+ # The files load together inside `MetaValidator.defer`, so a chapter spread over several files
32
+ # is judged once, whole. The chapter's ports file and adapters load after it, since a port
33
+ # names the chapter's aggregates.
34
+ #
35
+ # @param name [String] the chapter's name, such as `"Deploy"`
36
+ # @return [Boolean, nil] true when this call loaded the chapter, nil when it was already held
37
+ # @raise [Runtime::WiringError] if no attachable chapter has that name
38
+ def self.load!(name)
39
+ paths = index.fetch(name.to_s) do
40
+ raise Runtime::WiringError,
41
+ "no attachable chapter named #{name.inspect} — known: #{index.keys.join(', ')}"
42
+ end
43
+ registry = Hecks.current_registry or
44
+ raise Runtime::WiringError, "attaches #{name.to_s.inspect} outside a boot: no registry is open"
45
+ return refuse_own_chapter(registry, name.to_s) if registry.bluebook(name.to_s)
46
+
47
+ load_atomically(registry, name.to_s, paths)
48
+ true
49
+ end
50
+
51
+ # Loads a chapter whole or not at all: a raise anywhere leaves the registry as it was, so a
52
+ # retry loads the chapter again instead of finding a half-built one already held.
53
+ def self.load_atomically(registry, name, paths)
54
+ queued = Bluebook::MetaValidator.deferred_chapters.dup
55
+ ports = registry.ports.keys
56
+ adapters = registry.adapters.keys
57
+ Bluebook::MetaValidator.defer { paths.each { |path| Kernel.load(path) } }
58
+ Bluebook::MetaValidator.judge_deferred!(registry)
59
+ load_wiring(name, paths)
60
+ rescue Exception # rubocop:disable Lint/RescueException -- rolls back, then re-raises
61
+ registry.forget_chapter(name)
62
+ Bluebook::MetaValidator.deferred_chapters.replace(queued)
63
+ registry.ports.delete_if { |key, _| !ports.include?(key) }
64
+ registry.adapters.delete_if { |key, _| !adapters.include?(key) }
65
+ raise
66
+ end
67
+ private_class_method :load_atomically
68
+
69
+ # Returns nil for a chapter this gem already loaded; raises for a chapter of the same name
70
+ # that a user's own file declared, which `attaches` would otherwise silently take for the
71
+ # gem's.
72
+ def self.refuse_own_chapter(registry, name)
73
+ foreign = Array(registry.bluebook_sources[name]).compact
74
+ .reject { |path| File.expand_path(path).start_with?("#{__dir__}/") }
75
+ return if foreign.empty?
76
+
77
+ raise Runtime::WiringError,
78
+ "attaches #{name.inspect}, but a chapter named #{name.inspect} is already declared in " \
79
+ "#{foreign.join(', ')} — rename it, since the gem's own #{name} chapter cannot merge into it"
80
+ end
81
+ private_class_method :refuse_own_chapter
82
+
83
+ # Loads what a chapter ships beside its bluebook: its ports file, then its adapters.
84
+ #
85
+ # @param name [String] the chapter's name
86
+ # @param paths [Array<String>] the chapter's bluebook files
87
+ # @return [void]
88
+ def self.load_wiring(name, paths)
89
+ directory = File.dirname(paths.first)
90
+ ports = File.join(directory, "#{Naming.snake(name)}.ports.hecksagon")
91
+ Kernel.load(ports) if File.file?(ports)
92
+ Dir.glob(File.join(directory, "adapters", "*.adapter")).each { |adapter| Kernel.load(adapter) }
93
+ end
94
+ private_class_method :load_wiring
95
+
96
+ # The name a file's `Hecks.bluebook "Name"` header declares, or nil for a file without one.
97
+ def self.chapter_name(path)
98
+ File.foreach(path) { |line| return Regexp.last_match(1) if line =~ /\A\s*Hecks\.bluebook\s+"([^"]+)"/ }
99
+ nil
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,61 @@
1
+ require_relative "../../hecks"
2
+ require_relative "../behaviors"
3
+
4
+ module Hecks
5
+ module CLI
6
+ # The command behind `hecks run_behaviors` and Custodian's `Operation.RunBehaviors`: runs
7
+ # `.behaviors` files and reports pass, fail or error per test (docs/guides/behaviors.md).
8
+ module Behaviors
9
+ module_function
10
+
11
+ # Runs one `.behaviors` file, or every one under a directory, and exits with the verdict.
12
+ #
13
+ # @param argv [Array<String>] the file or directory to run
14
+ # @param program [String] the name the usage message calls this command by
15
+ # @return [void]
16
+ # @raise [SystemExit] always: 0 when every test passed, 1 when any failed, errored or did
17
+ # not parse, or when a directory holds no `.behaviors` file
18
+ def call(argv, program:)
19
+ target = argv.first or abort "usage: #{program} <file.behaviors | directory>"
20
+ abort "no such file or directory: #{target}" unless File.exist?(target)
21
+
22
+ if File.directory?(target)
23
+ sweep = Hecks::Behaviors.run_all(target)
24
+ abort "no .behaviors files under #{target}" if sweep.files_swept.zero?
25
+
26
+ sweep.files.each { |file| report_file(file) }
27
+ summary = sweep.summary
28
+ puts
29
+ puts "#{sweep.files_swept} file(s) swept — #{summary[:total]} test(s): " \
30
+ "#{summary[:passed]} passed, #{summary[:failed]} failed, #{summary[:errored]} errored, " \
31
+ "#{summary[:parse_errors]} file(s) failed to parse"
32
+ else
33
+ result = Hecks::Behaviors.run(target)
34
+ report_file(result)
35
+ summary = Hecks::Behaviors.summarize([result])
36
+ end
37
+ exit(bad?(summary) ? 1 : 0)
38
+ end
39
+
40
+ # @api private
41
+ def report_file(result)
42
+ puts result.path
43
+ if result.parse_error
44
+ puts " PARSE ERROR — #{result.parse_error}"
45
+ return
46
+ end
47
+
48
+ result.runs.each do |run|
49
+ marker = { pass: "ok", fail: "FAIL", error: "ERROR" }.fetch(run.status)
50
+ puts " #{marker.ljust(5)} #{run.description}"
51
+ puts " #{run.message}" if run.message
52
+ end
53
+ end
54
+
55
+ # @api private
56
+ def bad?(summary)
57
+ ((summary[:failed] || 0) + (summary[:errored] || 0) + (summary[:parse_errors] || 0)).positive?
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../canonical_json"
4
+
5
+ module Hecks
6
+ module CLI
7
+ # The command behind `hecks canonicalise`: prints a JSON document with every object's keys
8
+ # sorted, recursively. Key order is not semantics, so a diff a person reads should not have
9
+ # to notice it moved.
10
+ module Canonicalise
11
+ module_function
12
+
13
+ # Prints the document at the path in `argv`, canonicalised.
14
+ #
15
+ # @param argv [Array<String>] the path of the JSON file, first
16
+ # @param out [IO] where the document goes
17
+ # @return [Integer] the exit status, always 0
18
+ # @raise [IndexError] when no path is given
19
+ def call(argv, out: $stdout)
20
+ out.puts Hecks::CanonicalJson.pretty(File.read(argv.fetch(0)))
21
+ 0
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../hecks"
4
+ require_relative "../engine_agreement"
5
+
6
+ module Hecks
7
+ module CLI
8
+ # The command behind `hecks check_engine_agreement`: fails when a query engine re-grows its
9
+ # own comparator dispatch, or when a declared comparator lacks a shared `Comparison` case or
10
+ # a cross-engine agreement spec. Grep-based.
11
+ module CheckEngineAgreement
12
+ module_function
13
+
14
+ # Checks the tree and prints the verdict.
15
+ #
16
+ # @param root [String] the repository root to check
17
+ # @param out [IO] where a clean verdict goes
18
+ # @param err [IO] where the problems go
19
+ # @return [Integer] 0 when clean, 1 with problems
20
+ def call(root:, out: $stdout, err: $stderr)
21
+ finding = Hecks::EngineAgreement.check(root: root)
22
+ if finding.problems.empty?
23
+ out.puts Hecks::EngineAgreement.clean_line(finding)
24
+ return 0
25
+ end
26
+
27
+ err.puts Hecks::EngineAgreement.problem_lines(finding)
28
+ 1
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,88 @@
1
+ require_relative "../ports/persistence/plugins/era/expected_era"
2
+
3
+ module Hecks
4
+ module CLI
5
+ # The command behind `hecks check_era` and Custodian's `Host.CheckEra`: asks a running host
6
+ # which era it reports and compares it with an allow-list file. Read-only: one GET, no writes.
7
+ module CheckEra
8
+ USAGE = "usage: hecks check_era <url> <expected-era-file> [--timeout=<seconds>]".freeze
9
+
10
+ # What a check found.
11
+ #
12
+ # @!attribute [r] verdict
13
+ # @return [Runtime::EraCheck::ExpectedEra::Verdict] the era compared with the list
14
+ # @!attribute [r] version
15
+ # @return [String] the gem version the host reports, empty when it reports none
16
+ # @!attribute [r] line
17
+ # @return [String] the sentence `hecks check_era` prints for this finding
18
+ Finding = Struct.new(:verdict, :version, :line)
19
+
20
+ module_function
21
+
22
+ # Fetches the host's `/version` and compares its era with the allow-list file.
23
+ #
24
+ # @param url [String] the host's base URL, or its `/version` URL
25
+ # @param file [String] path of the allow-list file
26
+ # @param timeout [Numeric] seconds allowed for connecting and for reading
27
+ # @return [Finding] the comparison, whether or not the era is listed
28
+ # @raise [Errno::ENOENT, Errno::EACCES] if the file cannot be read
29
+ # @raise [Runtime::EraCheck::ExpectedEra::Unreachable] if the host cannot be reached
30
+ # @raise [Runtime::EraCheck::ExpectedEra::BadResponse] if it answers no era
31
+ def assess(url, file, timeout: 10)
32
+ expected = Runtime::EraCheck::ExpectedEra
33
+ allowed = expected.parse(File.read(file))
34
+ document = expected.fetch_version(url, timeout: timeout)
35
+ verdict = expected.verdict(document.fetch("era"), allowed)
36
+ Finding.new(verdict, document.fetch("version"), sentence(verdict, file))
37
+ end
38
+
39
+ # Runs `hecks check_era`: prints the finding and answers the exit status.
40
+ #
41
+ # @param argv [Array<String>] the url, the allow-list file and an optional `--timeout=N`
42
+ # @param out [IO] where a finding goes
43
+ # @param err [IO] where a refusal goes
44
+ # @return [Integer] 0 when the era is expected or unlisted, 1 on a mismatch, 2 on bad
45
+ # usage or an unreadable file, 3 when the host cannot be reached or answers no era
46
+ def run(argv, out: $stdout, err: $stderr)
47
+ timeout = 10
48
+ args = argv.reject do |arg|
49
+ next false unless arg.start_with?("--timeout=")
50
+
51
+ timeout = arg.delete_prefix("--timeout=").to_f
52
+ true
53
+ end
54
+ url, file = args
55
+ return usage(err) unless url && file && args.size == 2 && timeout.positive?
56
+
57
+ finding = assess(url, file, timeout: timeout)
58
+ (finding.verdict.ok? ? out : err).puts(finding.line)
59
+ finding.verdict.ok? ? 0 : 1
60
+ rescue Errno::ENOENT, Errno::EACCES => e
61
+ err.puts("cannot read #{file}: #{e.message}")
62
+ 2
63
+ rescue Runtime::EraCheck::ExpectedEra::Unreachable, Runtime::EraCheck::ExpectedEra::BadResponse => e
64
+ err.puts("era check failed: #{e.message}")
65
+ 3
66
+ end
67
+
68
+ # @param verdict [Runtime::EraCheck::ExpectedEra::Verdict] the comparison
69
+ # @param file [String] the allow-list file, named in the sentence
70
+ # @return [String] the one line that says what the comparison found
71
+ def sentence(verdict, file)
72
+ case verdict.status
73
+ when :match
74
+ "era #{verdict.era} is expected (#{verdict.allowed.join(', ')})"
75
+ when :unlisted
76
+ "host reports era #{verdict.era}; #{file} lists no era, so nothing was compared"
77
+ else
78
+ "host reports era #{verdict.era}, which is not in #{file} (expected: #{verdict.allowed.join(', ')})"
79
+ end
80
+ end
81
+
82
+ def usage(err)
83
+ err.puts(USAGE)
84
+ 2
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,66 @@
1
+ require_relative "../../hecks"
2
+ require "irb"
3
+ # The era/lineage subsystem is a persistence plugin core does not require by default (ADR 0033);
4
+ # a domain passed to the console may bind it.
5
+ require_relative "../ports/persistence/plugins/era"
6
+
7
+ module Hecks
8
+ module CLI
9
+ # The command behind `hecks console` and Custodian's `Operation.OpenConsole`: boots a domain
10
+ # (pizzas on Memory by default) and drops into IRB with its door installed, for dispatching a
11
+ # real command by hand. No argument needs no database (ADR 0073); a domain directory boots as
12
+ # it is wired, which may bind PostgresEra.
13
+ module Console
14
+ PIZZAS = File.expand_path("../../../examples/pizzas", __dir__)
15
+
16
+ DEFAULT_FILES = [
17
+ File.join(PIZZAS, "bluebook/pizzas.bluebook"),
18
+ File.join(PIZZAS, "pizzas_behaviors.hecksagon")
19
+ ].freeze
20
+
21
+ module_function
22
+
23
+ # Boots the domain, prints what it declares, and starts IRB; returns when the session ends.
24
+ #
25
+ # @param domain [String, nil] a domain directory; the bundled pizzas domain when nil
26
+ # @param launcher [#call] starts the interactive session; IRB unless a caller stands in
27
+ # @return [Runtime] the runtime the session ran against
28
+ def call(domain = nil, launcher: -> { IRB.start(__FILE__) })
29
+ runtime = domain ? Hecks.boot(domain) : Hecks.boot_files(DEFAULT_FILES)
30
+ puts overview(runtime)
31
+ puts banner
32
+ launcher.call
33
+ runtime
34
+ end
35
+
36
+ # @param runtime [Runtime] a booted domain
37
+ # @return [String] each chapter's vision and its aggregates' commands
38
+ def overview(runtime)
39
+ runtime.registry.bluebooks.each_value.map do |bluebook|
40
+ aggregates = bluebook.aggregates.map do |aggregate|
41
+ commands = aggregate.commands.map { |command| "#{Naming.snake(command.hecks_name)}!" }.sort.join(", ")
42
+ " #{aggregate.name}: #{commands}"
43
+ end
44
+
45
+ "\n #{bluebook.name} — #{bluebook.vision}\n#{aggregates.join("\n")}\n"
46
+ end.join
47
+ end
48
+
49
+ # @return [String] the version and a first session to try against the pizzas domain
50
+ def banner
51
+ <<~BANNER
52
+
53
+ hecks #{Hecks::VERSION}
54
+
55
+ try:
56
+ order = Order.create_pizza!(name: { value: "Margherita" }, pizza: { price_cents: { cents: 1200 }, size: { value: "large" } })
57
+ order.add_topping!(topping: { value: "Basil" }, amount: { value: 3 })
58
+ order.purchase!(customer_name: { value: "Chris" }, amount: { cents: 1200 })
59
+ order.status
60
+ order.events.last
61
+
62
+ BANNER
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+ require_relative "../projections/deploy/template_diff"
5
+
6
+ module Hecks
7
+ module CLI
8
+ # The command behind `hecks deploy diff` and `hecks deploy diff`: compares two
9
+ # CloudFormation templates offline and reports differences by logical id; `--strict` also
10
+ # counts cosmetic differences (comments, key order).
11
+ module DeployTemplateDiff
12
+ module_function
13
+
14
+ # Prints the comparison and answers the exit status.
15
+ #
16
+ # @param argv [Array<String>] `<before.yaml> <after.yaml> [--json] [--strict]`
17
+ # @param program [String] the name the usage message calls this command by
18
+ # @return [Integer] 0 when the templates match, 1 when they differ, 2 for bad input
19
+ # @raise [SystemExit] when there are not exactly two templates
20
+ def call(argv, program: "hecks deploy diff")
21
+ options = { json: false, strict: false }
22
+ parser = OptionParser.new do |opts|
23
+ opts.banner = "usage: #{program} <before.yaml> <after.yaml> [--json] [--strict]"
24
+ opts.on("--json", "write the report as JSON") { options[:json] = true }
25
+ opts.on("--strict", "count cosmetic differences too") { options[:strict] = true }
26
+ end
27
+ files = parser.parse(argv)
28
+ abort parser.banner unless files.size == 2
29
+
30
+ diff = Hecks::Projections::Deploy::TemplateDiff
31
+ report = diff.diff_files(files[0], files[1], strict: options[:strict])
32
+ puts(options[:json] ? diff.render_json(report) : diff.render(report))
33
+ report.different? ? 1 : 0
34
+ rescue ArgumentError => e
35
+ warn e.message
36
+ 2
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../hecks"
4
+ require_relative "../doc/reference"
5
+
6
+ module Hecks
7
+ module CLI
8
+ # The command behind `hecks measure_doc_coverage`: refuses unless every live word in
9
+ # `docs/implemented/reference/` has prose and a running example. It checks that an example
10
+ # exists; `spec/reference_doctest_spec.rb` checks that it passes. `ruby skip` fences and
11
+ # hidden `<!-- doctest:boot -->` blocks do not count as examples.
12
+ module DocCoverage
13
+ module_function
14
+
15
+ # Prints the coverage report.
16
+ #
17
+ # @param root [String] the repository root
18
+ # @param out [IO] where the report goes
19
+ # @param err [IO] where the refusal goes
20
+ # @return [Integer] 0 when every word is covered, 1 otherwise
21
+ def call(root:, out: $stdout, err: $stderr)
22
+ clean, report = Hecks::Doc::Reference.coverage_report(File.join(root, "docs/implemented/reference"))
23
+ out.puts report
24
+ return 0 if clean
25
+
26
+ err.puts "docs/implemented/reference/ is behind the language. " \
27
+ "Regenerate with hecks project_reference to see the words in place."
28
+ 1
29
+ end
30
+ end
31
+ end
32
+ end
@@ -2,7 +2,7 @@ require_relative "../../hecks"
2
2
 
3
3
  module Hecks
4
4
  module CLI
5
- # The command behind `bin/docs`, `bin/narrate`, `hecks docs` and `hecks narrate`:
5
+ # The command behind `hecks docs` and `hecks narrate`:
6
6
  # one domain's document, projected from its own bluebook, to stdout.
7
7
  #
8
8
  # Nothing is written for the caller: redirect it, so git decides whether it drifted.
@@ -31,7 +31,7 @@ module Hecks
31
31
  refuse_without_domain(program, root) unless path
32
32
 
33
33
  begin
34
- runtime = Hecks.boot(path, install_facade: false)
34
+ runtime = Hecks.boot(path, install_doors: false)
35
35
  rescue StandardError => e
36
36
  abort "cannot read #{path}: #{e.message.lines.first.strip}"
37
37
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../projection_files"
4
+
5
+ module Hecks
6
+ module CLI
7
+ # The command behind `hecks project_expression_tables`: projects the expression machinery's
8
+ # operator and normalisation tables from the grammar chapter into
9
+ # `lib/hecks/bluebook/expression/projection.json`.
10
+ #
11
+ # It never redirects with `>`: the shell would truncate the file the framework requires at
12
+ # boot. The default mode replaces the file atomically, and it refuses a projection that drops
13
+ # a self-bearing operator (one the chapter's own `status != "retired"` givens evaluate
14
+ # through), since that would wedge every later boot. Recover a broken file with
15
+ # `git checkout -- lib/hecks/bluebook/expression/projection.json`.
16
+ module ExpressionProjection
17
+ module_function
18
+
19
+ # Rewrites the projection file, or with `--stdout` prints it instead.
20
+ #
21
+ # @param argv [Array<String>] `--stdout` for the diff mode
22
+ # @param out [IO] where the result goes
23
+ # @return [Integer] the exit status, 0 once written or printed
24
+ # @raise [SystemExit] with the refusal when the projection drops a self-bearing operator
25
+ def call(argv, out: $stdout)
26
+ result = begin
27
+ Hecks::ProjectionFiles.build(:expression_tables)
28
+ rescue Hecks::ProjectionFiles::Refused => e
29
+ abort e.message
30
+ end
31
+
32
+ if argv.include?("--stdout")
33
+ out.print result.content.values.first
34
+ else
35
+ out.puts Hecks::ProjectionFiles.write(:expression_tables)
36
+ end
37
+ 0
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "optparse"
5
+ require_relative "../../hecks"
6
+
7
+ module Hecks
8
+ module CLI
9
+ # The command behind `hecks follow`: live-tails a domain's persisted event log as JSON lines.
10
+ #
11
+ # Events are one table per domain, so this polls the first repository that answers `:events`
12
+ # and diffs its size each tick, filtering client-side when `--aggregate` is given.
13
+ module Follow
14
+ module_function
15
+
16
+ # Tails until interrupted.
17
+ #
18
+ # @param argv [Array<String>] `<domain> [--aggregate name] [--interval seconds] [--from-now]`
19
+ # @param program [String] the name the usage message calls this command by
20
+ # @return [void]
21
+ # @raise [SystemExit] when no domain is named or no adapter persists an event log
22
+ def call(argv, program: "hecks follow")
23
+ argv = argv.dup
24
+ domain_name = argv.shift or abort "usage: #{program} <domain> [options]"
25
+ options = parse(argv, program)
26
+ repository = event_repository(Hecks.boot(domain_name), domain_name, program)
27
+ tail(repository, options)
28
+ end
29
+
30
+ # @param argv [Array<String>] the options after the domain; consumed in place
31
+ # @param program [String] the name the usage banner calls this command by
32
+ # @return [Hash] `:interval`, `:from_now` and, when given, `:aggregate`
33
+ def parse(argv, program)
34
+ options = { interval: 0.5, from_now: false }
35
+ OptionParser.new do |parser|
36
+ parser.banner = "usage: #{program} <domain> [options]"
37
+ parser.on("-a", "--aggregate=NAME", "Only follow events for this aggregate (e.g. Order)") do |name|
38
+ options[:aggregate] = name
39
+ end
40
+ parser.on("-i", "--interval=SECONDS", Float, "Poll interval in seconds (default: 0.5)") { |v| options[:interval] = v }
41
+ parser.on("--from-now", "Skip existing events; only print ones emitted from now on") { options[:from_now] = true }
42
+ end.parse!(argv)
43
+ options
44
+ end
45
+
46
+ # @param runtime [Runtime] a booted domain
47
+ # @param domain_name [String] its name, for the refusal
48
+ # @param program [String] the name the refusal calls this command by
49
+ # @return [Object] the first repository that answers `:events`
50
+ # @raise [SystemExit] when none does
51
+ def event_repository(runtime, domain_name, program)
52
+ repositories = runtime.registry.bluebooks.flat_map do |bluebook_domain, bluebook|
53
+ bluebook.aggregates.map { |aggregate| runtime.registry.repository(bluebook_domain, aggregate) }
54
+ end
55
+ repositories.find { |repo| repo.respond_to?(:events) } or
56
+ abort "#{program}: no adapter in #{domain_name} persists an event log"
57
+ end
58
+
59
+ # Says whether `event` passes the `--aggregate` filter: a bare name matched
60
+ # case-insensitively, or the exact name.
61
+ #
62
+ # @param event [Object] a logged event
63
+ # @param filter [String, nil] the aggregate name asked for
64
+ # @return [Boolean]
65
+ def matches?(event, filter)
66
+ filter.nil? || event.aggregate.to_s.split("::").last.casecmp?(filter) || event.aggregate == filter
67
+ end
68
+
69
+ # @param repository [Object] answers `events`
70
+ # @param options [Hash] the parsed options
71
+ # @return [void]
72
+ def tail(repository, options)
73
+ seen = options[:from_now] ? repository.events.size : 0
74
+ $stdout.sync = true
75
+ trap("INT") { exit }
76
+
77
+ loop do
78
+ events = repository.events
79
+ if events.size > seen
80
+ events[seen..].each { |event| puts JSON.generate(event.to_h) if matches?(event, options[:aggregate]) }
81
+ seen = events.size
82
+ end
83
+ sleep options[:interval]
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require_relative "../../hecks"
5
+
6
+ module Hecks
7
+ module CLI
8
+ # The command behind `hecks history` and `hecks history`: every journal entry a domain's
9
+ # append-only adapters hold, as JSON, the full write history rather than the current head.
10
+ module History
11
+ module_function
12
+
13
+ # Boots the domain `argv` names and prints its history as one JSON document.
14
+ #
15
+ # @param argv [Array<String>] the domain directory, first
16
+ # @param program [String] the name the usage message calls this command by
17
+ # @return [void]
18
+ # @raise [SystemExit] when `argv` is empty
19
+ def call(argv, program: "hecks history")
20
+ domain = argv.first or abort "usage: #{program} <domain>"
21
+ puts JSON.generate(document(Hecks.boot(domain).registry))
22
+ end
23
+
24
+ # @param registry [Runtime::Registry] a booted domain's registry
25
+ # @return [Hash{String => Array<Hash>}] each aggregate's storage name mapped to its entries
26
+ def document(registry)
27
+ registry.bluebooks.each_with_object({}) do |(name, bluebook), all|
28
+ bluebook.aggregates.each do |aggregate|
29
+ all[aggregate.storage_name] = entries(registry.repository(name, aggregate))
30
+ end
31
+ end
32
+ end
33
+
34
+ # @param repository [Object] an aggregate's repository
35
+ # @return [Array<Hash{String => Object}>] its journal entries; empty
36
+ # when it is not append-only
37
+ def entries(repository)
38
+ return [] unless repository.is_a?(Ports::Persistence::AppendOnly)
39
+
40
+ repository.entries.map { |entry| { "operation" => entry.operation, "id" => entry.id, "state" => entry.state } }
41
+ end
42
+ end
43
+ end
44
+ end
data/lib/hecks/cli/ir.rb CHANGED
@@ -5,7 +5,7 @@ require_relative "../ports/persistence/plugins/era"
5
5
 
6
6
  module Hecks
7
7
  module CLI
8
- # The command behind `bin/ir` and `hecks ir`: a booted domain's IR as JSON, the
8
+ # The command behind `hecks ir`: a booted domain's IR as JSON, the
9
9
  # same `to_h` the golden specs pin and `StorageShape` hashes into an era.
10
10
  module Ir
11
11
  module_function
data/lib/hecks/cli/mcp.rb CHANGED
@@ -2,7 +2,7 @@ require_relative "../mcp_stdio_guard"
2
2
 
3
3
  module Hecks
4
4
  module CLI
5
- # The command behind `bin/hecks_mcp_door` and `hecks mcp`: the MCP door onto the
5
+ # The command behind `hecks mcp`: the MCP door onto the
6
6
  # storehouse bus, over stdio only.
7
7
  #
8
8
  # This file loads nothing but `McpStdioGuard`, so the transport check runs
@@ -23,8 +23,8 @@ module Hecks
23
23
  # set up for stdio
24
24
  def call(argv)
25
25
  McpStdioGuard.enforce_stdio!(server: SERVER, argv: argv)
26
- require_relative "mcp_door"
27
- McpDoor.serve
26
+ require_relative "../doors/mcp_door"
27
+ Doors::McpDoor.serve
28
28
  end
29
29
  end
30
30
  end