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
@@ -3,7 +3,7 @@ require_relative "../bluebook/model_check"
3
3
 
4
4
  module Hecks
5
5
  module CLI
6
- # The command behind `bin/model_check` and `hecks model_check`: static analysis
6
+ # The command behind `hecks model_check`: static analysis
7
7
  # over the IR, printed per domain, exiting non-zero when any error-severity
8
8
  # finding is left — unreachable lifecycle states, dead transitions, unreached
9
9
  # saga states, dispatches to a nonexistent command, handlers for an event
@@ -24,7 +24,8 @@ module Hecks
24
24
 
25
25
  # Runs the model check `argv` describes and exits with its verdict.
26
26
  #
27
- # @param argv [Array<String>] `--strict`, `--profile NAME` and domain paths
27
+ # @param argv [Array<String>] `--strict`, `--profile NAME`, `--wait` and domain paths;
28
+ # `--wait` is accepted for the launcher's sake, as the verdict is always the exit status
28
29
  # @param program [String] the name the usage message calls this command by
29
30
  # @param root [String, nil] the repository root, for the corpus sweep and the
30
31
  # Rust-target check; nil when there is no checkout
@@ -34,6 +35,7 @@ module Hecks
34
35
  def call(argv, program:, root: nil)
35
36
  argv = argv.dup
36
37
  strict = !argv.delete("--strict").nil?
38
+ argv.delete("--wait")
37
39
  profile = take_profile(argv)
38
40
  unless profile.nil? || Bluebook::ModelCheck::PROFILES.include?(profile)
39
41
  warn "unknown profile #{profile.inspect} (known: #{Bluebook::ModelCheck::PROFILES.join(', ')})"
@@ -105,11 +107,12 @@ module Hecks
105
107
 
106
108
  # A port attaches to its aggregate from the hecksagon, not the bluebook, so a
107
109
  # deaf-policy check that skipped it would miss a policy reacting to an event
108
- # nothing emits. Recording a bind builds IR only; no adapter resolves here.
110
+ # nothing emits. Recording a bind builds IR only; no adapter resolves here. A chapter
111
+ # that ships its ports beside its bluebook (`<name>.ports.hecksagon`) is read with them.
109
112
  hecksagons = if File.directory?(source)
110
113
  Dir.glob(File.join(source, "*.hecksagon"))
111
114
  else
112
- [source.sub(/\.bluebook\z/, ".hecksagon")]
115
+ [".hecksagon", ".ports.hecksagon"].map { |suffix| source.sub(/\.bluebook\z/, suffix) }
113
116
  end
114
117
  hecksagons.each { |hecksagon| Kernel.load(hecksagon) if File.exist?(hecksagon) }
115
118
  end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Hecks
6
+ module CLI
7
+ # The command behind `hecks record_pattern_cases`: records the expected match results for
8
+ # `pattern:` cases, which `spec/pattern_subset_spec.rb` reads from
9
+ # `spec/corpus/fixtures/patterns.json`.
10
+ #
11
+ # The inputs cover embedded and trailing newlines and non-ASCII digits and word characters.
12
+ module PatternCases
13
+ # Each pattern with the inputs it is matched against.
14
+ CASES = [
15
+ ["^[A-Z]{3}-[0-9]{4}$", ["ABC-1234", "abc-1234", "xx\nABC-1234\nyy", "ABC-1234\n", "ABC-١٢٣٤"]],
16
+ ["^[0-9]{5}(-[0-9]{4})?$", ["12345", "12345-6789", "1234", "12345\nx", "١٢٣٤٥"]],
17
+ ['^[^@ ]+@[^@ ]+\.[^@ ]+$', ["a@b.co", "a b@c.co", "no-at-sign", "a@b.co\nevil", "\na@b.co", "café@x.co"]],
18
+ ["^(red|green|blue)$", ["red", "purple", "red\ngreen", "RED", "x\nred"]],
19
+ ["^[A-Za-z0-9_]+$", ["abc_1", "a b", "café", "abc\ndef"]],
20
+ ['^[ \t]*$', ["", " ", " ", "a", "\n", "\t"]],
21
+ ["^[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}$", ["12345678-1234-1234-1234-123456789abc", "nope"]],
22
+ ['^\+?[0-9 ()-]{7,20}$', ["+44 20 1234", "abc", "+44 20 1234\nx"]],
23
+ ['\Aonly\z', ["only", "only\n", "x\nonly"]],
24
+ ["^a.c$", ["abc", "a\nc", "a\tc", "aéc"]]
25
+ ].freeze
26
+
27
+ module_function
28
+
29
+ # @return [Array<Hash{String => Object}>] one row per pattern and input, with its match result
30
+ def rows
31
+ CASES.flat_map do |pattern, inputs|
32
+ regexp = Regexp.new(pattern)
33
+ inputs.map { |input| { "pattern" => pattern, "input" => input, "matches" => regexp.match?(input) } }
34
+ end
35
+ end
36
+
37
+ # Prints the rows as pretty JSON.
38
+ #
39
+ # @param out [IO] where the JSON goes
40
+ # @return [Integer] the exit status, always 0
41
+ def call(out: $stdout)
42
+ out.puts JSON.pretty_generate(rows)
43
+ 0
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../forms"
4
+ require_relative "../forms/banking_presentation"
5
+
6
+ module Hecks
7
+ module CLI
8
+ # The command behind `hecks present` and `hecks present`: serves the banking example's rendered
9
+ # forms and views, backed by the in-memory adapter
10
+ # (docs/command-form-and-query-form-bluebook.md).
11
+ #
12
+ # Then, for example, `http://localhost:4567/` lists every exposed chapter,
13
+ # `/Banking/Customer.html` every Customer plus Register, `/Banking/Account/Overdrawn.html` a
14
+ # query view, and `/Banking/Account/Debit` answers the command's shape as JSON.
15
+ module Present
16
+ ROOT = File.expand_path("../../..", __dir__)
17
+
18
+ module_function
19
+
20
+ # Starts a development server and blocks until it stops.
21
+ #
22
+ # @param argv [Array<String>] `[-p port]`
23
+ # @param program [String] the name the refusal calls this command by
24
+ # @param root [String] the checkout whose examples are served
25
+ # @return [void]
26
+ # @raise [SystemExit] when the port argument is refused
27
+ def call(argv, program: "hecks present", root: ROOT)
28
+ require "rackup"
29
+ port, error = Hecks::Forms::PortArgument.parse(argv)
30
+ abort "#{program}: #{error}" if error
31
+
32
+ app = Hecks::Forms::BankingPresentation.app(root: root)
33
+ puts "command_form.bluebook/query_form.bluebook — Banking, in memory — http://localhost:#{port}/"
34
+ puts "development server: no authentication, no CSRF protection, no caller identity — do not expose beyond localhost"
35
+ Rackup::Server.start(app: app, Port: port, server: "webrick")
36
+ end
37
+ end
38
+ end
39
+ end
@@ -3,35 +3,133 @@ require_relative "../../hecks"
3
3
 
4
4
  module Hecks
5
5
  module CLI
6
- # The command behind `bin/project_cli` and `hecks project_cli`: writes a
6
+ # The command behind `hecks project_cli` and `hecks project_cli`: writes a
7
7
  # command-line launcher beside each domain, named after its bluebook, so
8
8
  # `Hecks.bluebook "QualityControl"` in `qa/` becomes `qa/quality_control`.
9
9
  #
10
10
  # Each launcher is a dozen lines pinning one directory and handing over to
11
- # `Facade::CliRunner`; the surface itself is projected at the moment it runs,
11
+ # `Doors::CliRunner`; the surface itself is projected at the moment it runs,
12
12
  # so editing a chapter changes behaviour without regenerating anything.
13
+ #
14
+ # A chapter whose world's `launcher` setting names an `executable` gets its launcher
15
+ # there instead: the Hecks chapter's is the gem's `exe/hecks`.
13
16
  module ProjectCli
14
17
  # Build output and other trees under a root that hold no domain a caller means.
15
18
  IGNORED = %r{\A(rust|deploy|tmp|coverage)/}
16
19
 
20
+ # What a chapter name, a domain path and a launcher verb may be made of.
21
+ NAME = /\A[A-Za-z][A-Za-z0-9_ ]*\z/
22
+ PATH = %r{\A[\w./-]+\z}
23
+ VERB = /\A[A-Za-z_][A-Za-z0-9_]*\z/
24
+
25
+ # Set first in an opted-in launcher, before anything reads a file.
26
+ ENCODING = "Encoding.default_external = Encoding::UTF_8\nEncoding.default_internal = Encoding::UTF_8\n\n".freeze
27
+
28
+ # How every launcher ends.
29
+ PLAIN_ENDING = "status.zero? ? puts(text) : abort(text)".freeze
30
+
31
+ # How an opted-in launcher ends: a failed `--wait` run still prints its settled record.
32
+ OPTED_ENDING = <<~LAUNCHER_TAIL.chomp.freeze
33
+ # A `--wait` run that failed answers its settled record on stdout, so `| jq` reads it
34
+ # either way, and says why on stderr.
35
+ if reason
36
+ puts text
37
+ warn reason
38
+ exit status
39
+ end
40
+ status.zero? ? puts(text) : abort(text)
41
+ LAUNCHER_TAIL
42
+
43
+ # The generator's name as an executable launcher's header states it, whoever ran it.
44
+ GENERATOR = "hecks project_cli".freeze
45
+
17
46
  module_function
18
47
 
19
- # `remove_stale_bin` deletes `root/bin/<name>`, where a launcher for the same
20
- # chapter would otherwise linger as a second front door.
48
+ # Writes a launcher for each named domain, or for every domain under `root`.
49
+ #
50
+ # `--check` writes nothing and exits 1 when a launcher differs from the one this would
51
+ # write. A domain that cannot boot has none to compare, so it fails too.
52
+ #
53
+ # @param argv [Array<String>] domain paths under `root`, and optionally `--check`
54
+ # @param program [String] how the caller was invoked, named in a launcher's header
55
+ # @param root [String] the directory launchers are written under
56
+ # @param remove_stale_bin [Boolean] delete `root/bin/<name>`, a second front door
57
+ # @return [void]
58
+ # @raise [SystemExit] with status 1 for a launcher out of date, an unreadable domain, a
59
+ # path outside `root` or an unknown flag
21
60
  def call(argv, program:, root:, remove_stale_bin: true)
22
- wanted = argv.empty? ? domains(root) : argv.map { |path| path.delete_prefix("#{root}/").chomp("/") }
61
+ flags, paths = argv.partition { |word| word.start_with?("-") }
62
+ unknown = flags - ["--check"]
63
+ abort "hecks project_cli: unknown option #{unknown.first} (the only option is --check)" unless unknown.empty?
23
64
 
24
- wanted.each do |path|
25
- name = bluebook_name(root, path) or next
26
- snake = Naming.snake(name)
27
- file = File.join(root, path, snake)
65
+ check = flags.include?("--check")
66
+ wanted = paths.empty? ? domains(root) : paths.map { |path| under_root(root, path) }
28
67
 
29
- File.write(file, launcher(path, name, program))
30
- FileUtils.chmod("+x", file)
31
- FileUtils.rm_f(File.join(root, "bin", snake)) if remove_stale_bin
68
+ outcomes = wanted.to_h { |path| [path, one(root, path, program, check, remove_stale_bin)] }
69
+ drifted = outcomes.select { |_, outcome| outcome == :drifted }.keys
70
+ failed = outcomes.select { |_, outcome| outcome == :failed }.keys
71
+ return if drifted.empty? && failed.empty?
72
+
73
+ warn "launcher out of date for: #{drifted.join(', ')}; run `hecks project_cli #{drifted.join(' ')}`" unless drifted.empty?
74
+ warn "no launcher could be checked or written for: #{failed.join(', ')}" unless failed.empty?
75
+ exit 1
76
+ end
77
+
78
+ # A domain path as a path under `root`.
79
+ #
80
+ # @param root [String] the directory launchers are written under
81
+ # @param path [String] the path as typed, relative to `root` or absolute
82
+ # @return [String] the path relative to `root`
83
+ # @raise [SystemExit] when the path is outside `root`
84
+ def under_root(root, path)
85
+ expanded = File.expand_path(path, root)
86
+ inside = expanded == root || expanded.start_with?("#{root}/")
87
+ abort "hecks project_cli: #{path.inspect} is outside #{root}" unless inside
88
+
89
+ relative = expanded.delete_prefix(root).delete_prefix("/")
90
+ relative.empty? ? "." : relative
91
+ end
92
+
93
+ # @api private
94
+ # @return [Symbol] `:current` when the launcher is (or was made) as generated, `:drifted`
95
+ # when `check` found it out of date, `:failed` when the domain could not be read or its
96
+ # launcher could not be made
97
+ def one(root, path, program, check, remove_stale_bin)
98
+ runtime = Hecks.boot(File.join(root, path), install_doors: false)
99
+ name = runtime.registry.bluebooks.keys.first or raise "it loads no bluebook"
100
+ setting = Doors::LauncherOptions.settings(runtime, name) || {}
101
+ snake = Naming.snake(name)
102
+ executable = setting[:executable]
103
+ label = executable || "#{path}/#{snake}"
104
+ file = File.join(root, label)
105
+ text = launcher(path, name, program, executable: executable, legacy: setting[:legacy],
106
+ opted: !setting.empty?)
107
+ return skip(path, "#{label} is a directory") if File.directory?(file)
32
108
 
33
- puts " #{path}/#{snake} -> #{name}"
109
+ return :drifted if check && !(File.file?(file) && File.read(file) == text)
110
+
111
+ unless check
112
+ File.write(file, text)
113
+ FileUtils.chmod("+x", file)
114
+ FileUtils.rm_f(File.join(root, "bin", snake)) if remove_stale_bin && !executable
34
115
  end
116
+ puts " #{label} -> #{name}"
117
+ :current
118
+ rescue StandardError => e
119
+ refuse(path, "cannot boot — #{e.message.lines.first.to_s.strip}")
120
+ end
121
+
122
+ # @api private
123
+ # @return [Symbol] `:current`: a directory where the launcher would go has nothing to check
124
+ def skip(path, reason)
125
+ warn " #{path}: #{reason}; no launcher written"
126
+ :current
127
+ end
128
+
129
+ # @api private
130
+ def refuse(path, reason)
131
+ warn " #{path}: #{reason}"
132
+ :failed
35
133
  end
36
134
 
37
135
  def domains(root)
@@ -44,18 +142,41 @@ module Hecks
44
142
  .uniq.sort
45
143
  end
46
144
 
47
- # Reads the name from `Hecks.bluebook "…"` rather than the directory, so a
48
- # domain using `formerly_known_as` gets a launcher under its current name.
49
- def bluebook_name(root, path)
50
- Hecks.boot(File.join(root, path), install_facade: false).registry.bluebooks.keys.first
51
- rescue StandardError => e
52
- warn " #{path}: cannot boot — #{e.message.lines.first.strip}"
53
- nil
54
- end
55
-
56
- def launcher(path, name, program)
145
+ # The source of one launcher; names and paths go in as they are, so each must be plain.
146
+ # An `opted` one also sets UTF-8 and prints a failed `--wait` record on stdout.
147
+ # @param path [String] the domain's directory under the root
148
+ # @param name [String] the chapter's name
149
+ # @param program [String] the generator's invocation, named in the header
150
+ # @param executable [String, nil] the file's path under the root when it is not beside the
151
+ # domain; its program name is then that file's basename, and its header names the generator
152
+ # @param legacy [Array<String>, nil] verbs an executable hands to `Hecks::CLI` first
153
+ # @param opted [Boolean] whether the chapter's world declares a `launcher` setting
154
+ # @return [String] the Ruby source
155
+ # @raise [ArgumentError] if a name, path, executable or legacy verb is not plain
156
+ def launcher(path, name, program, executable: nil, legacy: nil, opted: !executable.nil?)
157
+ plain!("chapter name", name, NAME)
158
+ plain!("domain path", path, PATH)
57
159
  snake = Naming.snake(name)
58
- up = "../" * path.count("/").succ
160
+ if executable
161
+ plain!("launcher executable", executable, PATH)
162
+ if executable.split("/").include?("..")
163
+ raise ArgumentError, "launcher executable #{executable.inspect} must stay inside the root"
164
+ end
165
+
166
+ up = "../" * File.dirname(executable).split("/").reject { |part| part == "." }.length
167
+ boot = %(File.expand_path("#{up}#{path}", __dir__))
168
+ program = GENERATOR
169
+ where = executable
170
+ shown = File.basename(executable)
171
+ else
172
+ up = "../" * path.count("/").succ
173
+ boot = "__dir__"
174
+ where = shown = "#{path}/#{snake}"
175
+ end
176
+
177
+ handoff = legacy_handoff(Array(legacy)) if executable
178
+ encoding = opted ? ENCODING : ""
179
+ ending = opted ? OPTED_ENDING : PLAIN_ENDING
59
180
 
60
181
  <<~RUBY
61
182
  #!/usr/bin/env ruby
@@ -66,23 +187,94 @@ module Hecks
66
187
  # projected from the bluebook beside it every time this runs, so a change
67
188
  # to the chapter shows up here without re-minting anything.
68
189
  #
69
- # #{path}/#{snake} every verb, and every question
70
- # #{path}/#{snake} <verb> --help what it wants, and how it refuses
71
-
72
- $LOAD_PATH.unshift File.expand_path("#{up}lib", __dir__)
190
+ # #{where} every verb, and every question
191
+ # #{where} <verb> --help what it wants, and how it refuses
73
192
 
193
+ #{encoding}$LOAD_PATH.unshift File.expand_path("#{up}lib", __dir__)
194
+ #{handoff}
74
195
  require "hecks"
75
196
 
197
+ #{entry(name, boot, shown, opted)}
198
+ #{ending}
199
+ RUBY
200
+ end
201
+
202
+ # @api private
203
+ # @return [String] the launcher's middle: for an opted-in launcher, usage answered from the
204
+ # projection and a boot only for a line that runs a verb; for any other, the boot and
205
+ # dispatch every launcher has always had, byte for byte
206
+ def entry(name, boot, shown, opted)
207
+ opted ? described_entry(name, boot, shown) : plain_entry(name, boot, shown)
208
+ end
209
+
210
+ # @api private
211
+ # @return [String] the opted-in launcher's middle
212
+ def described_entry(name, boot, shown)
213
+ <<~RUBY.chomp
214
+ # Usage is answered from the projected chapter alone: no adapter is bound and no
215
+ # database is opened. Only a line that runs a verb boots the domain.
216
+ domain = #{boot}
217
+ program = "#{shown}"
218
+ described = begin
219
+ Hecks.describe(domain)
220
+ rescue StandardError => e
221
+ abort "cannot open #{name}: \#{e.message.lines.first.strip}"
222
+ end
223
+
224
+ text, status, reason = Hecks::Doors::CliRunner.usage(
225
+ runtime: described, argv: ARGV, program: program
226
+ )
227
+ unless text
228
+ runtime = begin
229
+ Hecks.boot(domain, install_doors: false)
230
+ rescue StandardError => e
231
+ abort "cannot open #{name}: \#{e.message.lines.first.strip}"
232
+ end
233
+ text, status, reason = Hecks::Doors::CliRunner.call(
234
+ runtime: runtime, argv: ARGV, program: program
235
+ )
236
+ end
237
+ RUBY
238
+ end
239
+
240
+ # @api private
241
+ # @return [String] the middle every launcher had before opted-in ones answered usage alone
242
+ def plain_entry(name, boot, shown)
243
+ <<~RUBY.chomp
76
244
  runtime = begin
77
- Hecks.boot(__dir__, install_facade: false)
245
+ Hecks.boot(#{boot}, install_doors: false)
78
246
  rescue StandardError => e
79
247
  abort "cannot open #{name}: \#{e.message.lines.first.strip}"
80
248
  end
81
249
 
82
- text, status = Hecks::Facade::CliRunner.call(
83
- runtime: runtime, argv: ARGV, program: "#{path}/#{snake}"
250
+ text, status = Hecks::Doors::CliRunner.call(
251
+ runtime: runtime, argv: ARGV, program: "#{shown}"
84
252
  )
85
- status.zero? ? puts(text) : abort(text)
253
+ RUBY
254
+ end
255
+
256
+ # @api private
257
+ def plain!(what, value, pattern)
258
+ return if value.to_s.match?(pattern)
259
+
260
+ raise ArgumentError, "#{what} #{value.to_s.inspect} is not a plain word or path"
261
+ end
262
+
263
+ # @api private
264
+ # @return [String] the lines that hand the legacy verbs to `Hecks::CLI`, or nothing
265
+ def legacy_handoff(legacy)
266
+ return "" if legacy.empty?
267
+
268
+ legacy.each { |verb| plain!("legacy verb", verb, VERB) }
269
+
270
+ <<~RUBY
271
+
272
+ # The names the gem has always shipped keep their positional forms.
273
+ LEGACY = %w[#{legacy.join(' ')}].freeze
274
+ if LEGACY.include?(ARGV.first)
275
+ require "hecks/cli"
276
+ exit Hecks::CLI.start(ARGV) unless Hecks::CLI.launcher_form?(ARGV)
277
+ end
86
278
  RUBY
87
279
  end
88
280
  end
@@ -2,10 +2,11 @@ require_relative "../../hecks"
2
2
 
3
3
  module Hecks
4
4
  module CLI
5
- # The command behind `bin/project_diagrams` and `hecks project_diagrams`: boots a
5
+ # The command behind `hecks project_diagrams`: boots a
6
6
  # domain, finds the chapter it names, and writes its Mermaid diagrams
7
7
  # (`Projections::Diagrams`) under `docs/generated/diagrams/<chapter>/` below the
8
- # root.
8
+ # root. Only this positional form writes; the launcher's `domain=… chapter=…` question
9
+ # prints the same files' text and writes nothing.
9
10
  module ProjectDiagrams
10
11
  module_function
11
12
 
@@ -15,7 +16,7 @@ module Hecks
15
16
  chapter_name = argv[1] or abort usage
16
17
 
17
18
  runtime = Hecks.boot(File.expand_path(domain_path, Dir.pwd))
18
- chapter = runtime.registry.bluebook(chapter_name)
19
+ chapter = runtime.registry.bluebook(chapter_name) or abort "no chapter named #{chapter_name}"
19
20
  hecksagon = runtime.registry.hecksagon(chapter_name)
20
21
  directory = File.expand_path("docs/generated/diagrams/#{Naming.snake(chapter_name)}", root)
21
22
 
@@ -0,0 +1,44 @@
1
+ require_relative "../../hecks"
2
+
3
+ module Hecks
4
+ module CLI
5
+ # The command behind `hecks glossary`: boots a domain and projects one chapter into its
6
+ # Ubiquitous Language glossary, `glossary.md` plus `html/index.html` rendered from it, in the
7
+ # domain's own `glossary/` folder.
8
+ #
9
+ # A field the deployment marks sensitive (`has_phi(readable_by:)` in the domain's `.hecksagon`)
10
+ # is tagged in the glossary with its category. spec/glossary_spec.rb re-projects every one and
11
+ # refuses a diff, so a committed glossary never lags the bluebook it describes.
12
+ module ProjectGlossary
13
+ module_function
14
+
15
+ # Writes the chapter's glossary and prints each file written.
16
+ #
17
+ # @param argv [Array<String>] the domain path, then the chapter's name
18
+ # @param program [String] the name the usage line shows
19
+ # @return [void]
20
+ # @raise [SystemExit] with the usage line when an argument is missing
21
+ def call(argv, program: "hecks glossary")
22
+ usage = "usage: #{program} <domain-path> <ChapterName>"
23
+ domain_path = argv[0] or abort usage
24
+ chapter_name = argv[1] or abort usage
25
+
26
+ domain = File.expand_path(domain_path, Dir.pwd)
27
+ runtime = Hecks.boot(domain)
28
+ chapter = runtime.registry.bluebook(chapter_name)
29
+
30
+ markings = runtime.registry.pending_privacy_markings.select do |marking|
31
+ marking[:domain].to_s.start_with?("#{chapter_name}::")
32
+ end
33
+
34
+ written = Projector.write(
35
+ Projector.call(:glossary, bluebook: chapter, options: { markings: markings }),
36
+ File.join(domain, "glossary"), as: :files
37
+ )
38
+
39
+ puts "wrote #{written.size} file(s):"
40
+ written.each { |path| puts " #{path}" }
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../hecks"
4
+ require_relative "../projection_files"
5
+
6
+ module Hecks
7
+ module CLI
8
+ # The command behind `hecks project_reference`: regenerates `docs/implemented/reference/` from
9
+ # the language's Syntax chapter. Tables come from the declaration; prose is kept from the
10
+ # committed pages.
11
+ module Reference
12
+ module_function
13
+
14
+ # Writes the pages and reports both coverage gates together (`DocCoverage` asks the same
15
+ # as an exit status).
16
+ #
17
+ # @param root [String] the repository root
18
+ # @param out [IO] where the report goes
19
+ # @return [Integer] the exit status, always 0
20
+ def call(root:, out: $stdout)
21
+ directory = File.join(root, "docs/implemented/reference")
22
+ written = Hecks::ProjectionFiles.write(:reference, root: root)
23
+ out.puts "wrote #{written.size - 1} pages to docs/implemented/reference/, " \
24
+ "and regenerated README.md's own indexes"
25
+ { "prose" => Hecks::Doc::Reference.undocumented(directory),
26
+ "a running example" => Hecks::Doc::Reference.unexemplified(directory) }.each do |owed, missing|
27
+ report(out, owed, missing)
28
+ end
29
+ 0
30
+ end
31
+
32
+ # @param out [IO] where the line goes
33
+ # @param owed [String] what each word owes: prose, or a running example
34
+ # @param missing [Array<String>] the words that lack it
35
+ # @return [void]
36
+ def report(out, owed, missing)
37
+ if missing.empty?
38
+ out.puts "every live word carries #{owed}."
39
+ else
40
+ out.puts "#{missing.size} word(s) still carry no #{owed}:"
41
+ missing.each { |word| out.puts " #{word}" }
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,44 @@
1
+ require_relative "../../hecks"
2
+
3
+ module Hecks
4
+ module CLI
5
+ # The logic behind `hecks refresh_projections` and Custodian's `Operation.RefreshProjections`:
6
+ # forces every
7
+ # read-model projection a booted domain declares to catch up now, the same catch-up a boot
8
+ # runs lazily on first read.
9
+ module RefreshProjections
10
+ module_function
11
+
12
+ # Boots the domain `argv` names and refreshes its projections, as `hecks refresh_projections`
13
+ # does.
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 [Integer] 0
18
+ # @raise [SystemExit] when `argv` is empty
19
+ def run(argv, program: "hecks refresh_projections")
20
+ domain = argv.first or abort "usage: #{program} <domain>"
21
+ call(Hecks.boot(domain))
22
+ 0
23
+ end
24
+
25
+ # Refreshes every projection of every chapter in a booted runtime.
26
+ #
27
+ # @param runtime [Runtime] a booted domain
28
+ # @return [Integer] how many projections were caught up
29
+ def call(runtime)
30
+ refreshed = 0
31
+ runtime.registry.bluebooks.each do |domain, bluebook|
32
+ bluebook.aggregates.each do |aggregate|
33
+ worker = Ports::Projection.worker(runtime.registry, domain, aggregate, policy: :refresh)
34
+ next unless worker
35
+
36
+ worker.catch_up!
37
+ refreshed += 1
38
+ end
39
+ end
40
+ refreshed
41
+ end
42
+ end
43
+ end
44
+ end