hecks 2.8.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 (886) hide show
  1. checksums.yaml +4 -4
  2. data/exe/hecks +50 -7
  3. data/lib/hecks/adapters/driven/claude_code.rb +4 -104
  4. data/lib/hecks/adapters/driven/d1.rb +44 -237
  5. data/lib/hecks/adapters/driven/folder.rb +17 -76
  6. data/lib/hecks/adapters/driven/google_authentication.rb +15 -49
  7. data/lib/hecks/adapters/driven/governance_authorization.rb +23 -70
  8. data/lib/hecks/adapters/driven/heki/journal.rb +8 -41
  9. data/lib/hecks/adapters/driven/heki/saga_store.rb +12 -54
  10. data/lib/hecks/adapters/driven/heki/snapshot.rb +4 -12
  11. data/lib/hecks/adapters/driven/heki.rb +13 -42
  12. data/lib/hecks/adapters/driven/identity_registry.rb +3 -14
  13. data/lib/hecks/adapters/driven/in_memory_ordering.rb +9 -26
  14. data/lib/hecks/adapters/driven/in_process_key_vault.rb +2 -14
  15. data/lib/hecks/adapters/driven/lambda/client.rb +10 -51
  16. data/lib/hecks/adapters/driven/lambda.rb +10 -84
  17. data/lib/hecks/adapters/driven/local_storage.rb +5 -40
  18. data/lib/hecks/adapters/driven/memory.rb +32 -100
  19. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +5 -21
  20. data/lib/hecks/adapters/driven/postgres/codec.rb +6 -28
  21. data/lib/hecks/adapters/driven/postgres/outbox.rb +3 -12
  22. data/lib/hecks/adapters/driven/postgres/reconnect.rb +10 -32
  23. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +34 -108
  24. data/lib/hecks/adapters/driven/postgres/shared_connection.rb +180 -0
  25. data/lib/hecks/adapters/driven/postgres.rb +149 -136
  26. data/lib/hecks/adapters/driven/postgres_era.adapter +4 -15
  27. data/lib/hecks/adapters/driven/prism.rb +48 -38
  28. data/lib/hecks/adapters/driven/sql_query_builder.rb +21 -121
  29. data/lib/hecks/adapters/driven/sqlite/codec.rb +8 -40
  30. data/lib/hecks/adapters/driven/sqlite/projection.rb +26 -82
  31. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +40 -84
  32. data/lib/hecks/adapters/driven/sqlite.rb +128 -65
  33. data/lib/hecks/adapters/driven/tenant_provisioner.rb +76 -62
  34. data/lib/hecks/adapters/driven.rb +5 -36
  35. data/lib/hecks/adapters/driving/github_webhook.rb +8 -76
  36. data/lib/hecks/behaviors/dsl.rb +7 -25
  37. data/lib/hecks/behaviors/expectations.rb +32 -142
  38. data/lib/hecks/behaviors/ir.rb +7 -20
  39. data/lib/hecks/behaviors/rspec.rb +6 -17
  40. data/lib/hecks/behaviors/runner.rb +10 -34
  41. data/lib/hecks/behaviors.rb +4 -8
  42. data/lib/hecks/bench/cli.rb +62 -0
  43. data/lib/hecks/bench/environment.rb +71 -0
  44. data/lib/hecks/bench/postgres_probe.rb +30 -0
  45. data/lib/hecks/bench/report.rb +76 -0
  46. data/lib/hecks/bench/ruby_runner.rb +93 -0
  47. data/lib/hecks/bench/run.rb +55 -0
  48. data/lib/hecks/bench/rust_runner.rb +101 -0
  49. data/lib/hecks/bench/stats.rb +46 -0
  50. data/lib/hecks/bench/suite.rb +98 -0
  51. data/lib/hecks/bench/workload.rb +89 -0
  52. data/lib/hecks/bench.rb +17 -0
  53. data/lib/hecks/bluebook/aggregate.rb +9 -75
  54. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +9 -38
  55. data/lib/hecks/bluebook/assembly/build.rb +4 -15
  56. data/lib/hecks/bluebook/assembly/contract.rb +15 -136
  57. data/lib/hecks/bluebook/assembly/contracts.rb +56 -174
  58. data/lib/hecks/bluebook/assembly/marks.rb +31 -223
  59. data/lib/hecks/bluebook/assembly/specializer.rb +6 -52
  60. data/lib/hecks/bluebook/assembly.rb +6 -45
  61. data/lib/hecks/bluebook/attribute.rb +12 -61
  62. data/lib/hecks/bluebook/behaviour/aggregate.rb +39 -39
  63. data/lib/hecks/bluebook/behaviour/attribute.rb +3 -14
  64. data/lib/hecks/bluebook/behaviour/chapter.rb +14 -42
  65. data/lib/hecks/bluebook/behaviour/command.rb +14 -66
  66. data/lib/hecks/bluebook/behaviour/domain_port.rb +11 -20
  67. data/lib/hecks/bluebook/behaviour/entity.rb +7 -27
  68. data/lib/hecks/bluebook/behaviour/hexagon.rb +23 -41
  69. data/lib/hecks/bluebook/behaviour/lifecycle.rb +15 -40
  70. data/lib/hecks/bluebook/behaviour/policy.rb +23 -52
  71. data/lib/hecks/bluebook/behaviour/process_manager.rb +30 -57
  72. data/lib/hecks/bluebook/behaviour/read_model.rb +25 -31
  73. data/lib/hecks/bluebook/behaviour/traits.rb +27 -64
  74. data/lib/hecks/bluebook/behaviour/value_object.rb +5 -24
  75. data/lib/hecks/bluebook/capabilities.rb +17 -49
  76. data/lib/hecks/bluebook/chapter.rb +16 -50
  77. data/lib/hecks/bluebook/command.rb +23 -81
  78. data/lib/hecks/bluebook/domain_port.rb +49 -67
  79. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +50 -229
  80. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +50 -377
  81. data/lib/hecks/bluebook/dsl/attribute_collector.rb +33 -204
  82. data/lib/hecks/bluebook/dsl/binding_proxy.rb +16 -69
  83. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +96 -708
  84. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +63 -169
  85. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +3 -1
  86. data/lib/hecks/bluebook/dsl/command_builder.rb +74 -546
  87. data/lib/hecks/bluebook/dsl/const_shim.rb +8 -41
  88. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +42 -89
  89. data/lib/hecks/bluebook/dsl/entity_builder.rb +44 -330
  90. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +20 -269
  91. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +148 -197
  92. data/lib/hecks/bluebook/dsl/identity_declaration.rb +13 -88
  93. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +6 -20
  94. data/lib/hecks/bluebook/dsl/policy_builder.rb +16 -88
  95. data/lib/hecks/bluebook/dsl/port_builder.rb +2 -6
  96. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +22 -96
  97. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +31 -184
  98. data/lib/hecks/bluebook/dsl/query_builder.rb +38 -43
  99. data/lib/hecks/bluebook/dsl/read_model_builder.rb +75 -201
  100. data/lib/hecks/bluebook/dsl/rule_reference.rb +22 -163
  101. data/lib/hecks/bluebook/dsl/translation_builder.rb +28 -114
  102. data/lib/hecks/bluebook/dsl/value_object_builder.rb +10 -124
  103. data/lib/hecks/bluebook/dsl/word_gate.rb +16 -162
  104. data/lib/hecks/bluebook/dsl/world_builder.rb +46 -111
  105. data/lib/hecks/bluebook/entity.rb +13 -71
  106. data/lib/hecks/bluebook/expression/ast_json.rb +24 -190
  107. data/lib/hecks/bluebook/expression/ast_reader.rb +12 -54
  108. data/lib/hecks/bluebook/expression/canonical_form.rb +11 -88
  109. data/lib/hecks/bluebook/expression/evaluator.rb +47 -295
  110. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +30 -147
  111. data/lib/hecks/bluebook/expression/resolver.rb +39 -661
  112. data/lib/hecks/bluebook/hexagon.rb +18 -33
  113. data/lib/hecks/bluebook/lifecycle.rb +5 -16
  114. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -7
  115. data/lib/hecks/bluebook/meta_validator/judge.rb +111 -361
  116. data/lib/hecks/bluebook/meta_validator/plan.rb +33 -162
  117. data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -7
  118. data/lib/hecks/bluebook/meta_validator/readings.rb +56 -342
  119. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +25 -176
  120. data/lib/hecks/bluebook/meta_validator/shapes.rb +53 -267
  121. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +47 -306
  122. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +7 -43
  123. data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +246 -0
  124. data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -18
  125. data/lib/hecks/bluebook/meta_validator.rb +87 -352
  126. data/lib/hecks/bluebook/model_check/client_profile.rb +6 -21
  127. data/lib/hecks/bluebook/model_check.rb +122 -596
  128. data/lib/hecks/bluebook/pattern_subset.rb +49 -69
  129. data/lib/hecks/bluebook/process_manager.rb +13 -83
  130. data/lib/hecks/bluebook/project_loader.rb +3 -39
  131. data/lib/hecks/bluebook/project_register.rb +12 -60
  132. data/lib/hecks/bluebook/query.rb +27 -51
  133. data/lib/hecks/bluebook/read_model.rb +57 -81
  134. data/lib/hecks/bluebook/reference.rb +7 -41
  135. data/lib/hecks/bluebook/smoke_test.rb +9 -99
  136. data/lib/hecks/bluebook/synthesizer.rb +13 -62
  137. data/lib/hecks/bluebook/translation.rb +26 -62
  138. data/lib/hecks/bluebook/value_object.rb +11 -36
  139. data/lib/hecks/bluebook.rb +5 -22
  140. data/lib/hecks/cache_dir.rb +4 -23
  141. data/lib/hecks/canonical_json.rb +30 -0
  142. data/lib/hecks/chapters.rb +102 -0
  143. data/lib/hecks/cli/behaviors.rb +61 -0
  144. data/lib/hecks/cli/canonicalise.rb +25 -0
  145. data/lib/hecks/cli/check_engine_agreement.rb +32 -0
  146. data/lib/hecks/cli/check_era.rb +88 -0
  147. data/lib/hecks/cli/console.rb +66 -0
  148. data/lib/hecks/cli/deploy_template_diff.rb +40 -0
  149. data/lib/hecks/cli/doc_coverage.rb +32 -0
  150. data/lib/hecks/cli/document.rb +5 -23
  151. data/lib/hecks/cli/expression_projection.rb +41 -0
  152. data/lib/hecks/cli/follow.rb +88 -0
  153. data/lib/hecks/cli/history.rb +44 -0
  154. data/lib/hecks/cli/ir.rb +4 -6
  155. data/lib/hecks/cli/mcp.rb +5 -6
  156. data/lib/hecks/cli/model_check.rb +24 -88
  157. data/lib/hecks/cli/pattern_cases.rb +47 -0
  158. data/lib/hecks/cli/present.rb +39 -0
  159. data/lib/hecks/cli/project_cli.rb +223 -64
  160. data/lib/hecks/cli/project_diagrams.rb +4 -10
  161. data/lib/hecks/cli/project_glossary.rb +44 -0
  162. data/lib/hecks/cli/reference.rb +46 -0
  163. data/lib/hecks/cli/refresh_projections.rb +44 -0
  164. data/lib/hecks/cli/refresh_rspec_runtime_baseline.rb +150 -0
  165. data/lib/hecks/cli/release.rb +70 -0
  166. data/lib/hecks/cli/release_gem.rb +59 -0
  167. data/lib/hecks/cli/rspec_io_parallel_files.rb +137 -0
  168. data/lib/hecks/cli/rspec_shard_files.rb +66 -0
  169. data/lib/hecks/cli/run.rb +15 -61
  170. data/lib/hecks/cli/seed_semantics_corpus.rb +71 -0
  171. data/lib/hecks/cli/serve_query_ir_mcp.rb +30 -0
  172. data/lib/hecks/cli/shape.rb +70 -0
  173. data/lib/hecks/cli/smoke_http.rb +230 -0
  174. data/lib/hecks/cli/smoke_test.rb +7 -19
  175. data/lib/hecks/cli/spec_example.rb +27 -0
  176. data/lib/hecks/cli/statements.rb +32 -0
  177. data/lib/hecks/cli/stores.rb +4 -6
  178. data/lib/hecks/cli/stress_concurrency_specs.rb +177 -0
  179. data/lib/hecks/cli.rb +216 -73
  180. data/lib/hecks/codemod.rb +324 -0
  181. data/lib/hecks/construct.rb +8 -34
  182. data/lib/hecks/corpus.rb +423 -0
  183. data/lib/hecks/deploy/bluebook/deploy.bluebook +617 -158
  184. data/lib/hecks/deploy/bluebook/deploy.hecksagon +41 -11
  185. data/lib/hecks/deploy/oidc.json +82 -2
  186. data/lib/hecks/doc/reference.rb +351 -0
  187. data/lib/hecks/doors/cli_door.rb +196 -0
  188. data/lib/hecks/doors/cli_runner.rb +297 -0
  189. data/lib/hecks/{facade → doors}/command_request.rb +14 -34
  190. data/lib/hecks/doors/handle.rb +206 -0
  191. data/lib/hecks/doors/json_door.rb +132 -0
  192. data/lib/hecks/doors/launcher_options.rb +129 -0
  193. data/lib/hecks/{cli → doors}/mcp_door.rb +25 -120
  194. data/lib/hecks/doors/mcp_door_scope.rb +97 -0
  195. data/lib/hecks/doors/ruby_door/aggregate_door.rb +162 -0
  196. data/lib/hecks/doors/ruby_door/chapter.rb +72 -0
  197. data/lib/hecks/doors/ruby_door.rb +77 -0
  198. data/lib/hecks/doors.rb +61 -0
  199. data/lib/hecks/embryonaut_bluebook/lock.rb +3 -35
  200. data/lib/hecks/embryonaut_bluebook/shape.rb +1 -17
  201. data/lib/hecks/embryonaut_bluebook/vendor.rb +8 -53
  202. data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +4 -19
  203. data/lib/hecks/embryonaut_bluebook.rb +5 -72
  204. data/lib/hecks/engine_agreement.rb +156 -0
  205. data/lib/hecks/facade.rb +23 -51
  206. data/lib/hecks/forms/app.rb +21 -76
  207. data/lib/hecks/forms/banking_presentation.rb +50 -0
  208. data/lib/hecks/forms/command_form_renderer.rb +10 -29
  209. data/lib/hecks/forms/field_renderer.rb +17 -156
  210. data/lib/hecks/forms/field_shape.rb +31 -222
  211. data/lib/hecks/forms/html.rb +12 -47
  212. data/lib/hecks/forms/index_renderer.rb +1 -4
  213. data/lib/hecks/forms/page.rb +3 -9
  214. data/lib/hecks/forms/params.rb +14 -60
  215. data/lib/hecks/forms/port_argument.rb +2 -19
  216. data/lib/hecks/forms/query_form_renderer.rb +4 -79
  217. data/lib/hecks/forms/record_renderer.rb +8 -75
  218. data/lib/hecks/forms/record_table.rb +7 -50
  219. data/lib/hecks/forms/reference_options.rb +3 -30
  220. data/lib/hecks/forms/value_object_shape.rb +8 -35
  221. data/lib/hecks/forms.rb +5 -33
  222. data/lib/hecks/fqn.rb +5 -12
  223. data/lib/hecks/framework/bluebook/compliance.bluebook +16 -65
  224. data/lib/hecks/framework/bluebook/console_settings.bluebook +47 -134
  225. data/lib/hecks/framework/bluebook/framework.hecksagon +5 -14
  226. data/lib/hecks/framework/bluebook/governance.bluebook +13 -34
  227. data/lib/hecks/framework/bluebook/identity.bluebook +11 -26
  228. data/lib/hecks/framework/bluebook/privacy.bluebook +8 -33
  229. data/lib/hecks/framework.rb +11 -80
  230. data/lib/hecks/freezer.rb +5 -39
  231. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +393 -0
  232. data/lib/hecks/fuzzing/combination_miner.rb +86 -0
  233. data/lib/hecks/fuzzing/concurrent_dispatch.rb +214 -0
  234. data/lib/hecks/fuzzing/coverage_campaign.rb +143 -0
  235. data/lib/hecks/fuzzing/differential.rb +156 -0
  236. data/lib/hecks/fuzzing/domain_generator.rb +615 -0
  237. data/lib/hecks/fuzzing/era_boundary.rb +71 -0
  238. data/lib/hecks/fuzzing/form_census.rb +136 -0
  239. data/lib/hecks/fuzzing/generated_domain_check.rb +93 -0
  240. data/lib/hecks/fuzzing/invalid_value_generator.rb +64 -0
  241. data/lib/hecks/fuzzing/isolated_boot.rb +301 -0
  242. data/lib/hecks/fuzzing/nondeterministic.rb +51 -0
  243. data/lib/hecks/fuzzing/outside_world.rb +45 -0
  244. data/lib/hecks/fuzzing/persistence_parity.rb +102 -0
  245. data/lib/hecks/fuzzing/properties/corrections.rb +88 -0
  246. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +314 -0
  247. data/lib/hecks/fuzzing/properties/guards.rb +190 -0
  248. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +300 -0
  249. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +93 -0
  250. data/lib/hecks/fuzzing/properties/outbox.rb +89 -0
  251. data/lib/hecks/fuzzing/properties/querying.rb +175 -0
  252. data/lib/hecks/fuzzing/properties.rb +162 -0
  253. data/lib/hecks/fuzzing/qa_settings.rb +123 -0
  254. data/lib/hecks/fuzzing/replay.rb +505 -0
  255. data/lib/hecks/fuzzing/rotation_priority.rb +48 -0
  256. data/lib/hecks/fuzzing/rust_gap_manifest.rb +108 -0
  257. data/lib/hecks/fuzzing/self_consistency.rb +339 -0
  258. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +385 -0
  259. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +132 -0
  260. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +95 -0
  261. data/lib/hecks/fuzzing/sequence_generator/picker.rb +77 -0
  262. data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +109 -0
  263. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +237 -0
  264. data/lib/hecks/fuzzing/sequence_generator.rb +189 -0
  265. data/lib/hecks/fuzzing/shrinker.rb +166 -0
  266. data/lib/hecks/fuzzing/structural_skips.rb +33 -0
  267. data/lib/hecks/fuzzing/sweep_depth.rb +43 -0
  268. data/lib/hecks/fuzzing/target_capabilities.rb +163 -0
  269. data/lib/hecks/fuzzing/value_generator.rb +216 -0
  270. data/lib/hecks/fuzzing.rb +23 -0
  271. data/lib/hecks/grammar/evolve.rb +336 -0
  272. data/lib/hecks/grammar/expression.bluebook +15 -40
  273. data/lib/hecks/grammar/translation.bluebook +3 -6
  274. data/lib/hecks/grammar.rb +16 -41
  275. data/lib/hecks/hecks/adapters/codebase/codemods.rb +45 -0
  276. data/lib/hecks/hecks/adapters/codebase/conformance.rb +71 -0
  277. data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +202 -0
  278. data/lib/hecks/hecks/adapters/codebase/gem_registry.rb +80 -0
  279. data/lib/hecks/hecks/adapters/codebase/kernel_tables.rb +54 -0
  280. data/lib/hecks/hecks/adapters/codebase/language.rb +240 -0
  281. data/lib/hecks/hecks/adapters/codebase/npm_registry.rb +102 -0
  282. data/lib/hecks/hecks/adapters/codebase/publishing.rb +142 -0
  283. data/lib/hecks/hecks/adapters/codebase/regeneration.rb +67 -0
  284. data/lib/hecks/hecks/adapters/codebase/release_facts.rb +90 -0
  285. data/lib/hecks/hecks/adapters/codebase/ruby_child.rb +83 -0
  286. data/lib/hecks/hecks/adapters/codebase/secret_vault.rb +36 -0
  287. data/lib/hecks/hecks/adapters/codebase/source_tree.adapter +7 -0
  288. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +189 -0
  289. data/lib/hecks/hecks/adapters/codebase/sqlite_fixture.rb +71 -0
  290. data/lib/hecks/hecks/adapters/codebase/style.rb +171 -0
  291. data/lib/hecks/hecks/adapters/codebase/test_runner.rb +55 -0
  292. data/lib/hecks/hecks/adapters/codebase/test_suite.rb +165 -0
  293. data/lib/hecks/hecks/adapters/codebase/tree.rb +111 -0
  294. data/lib/hecks/hecks/adapters/console_capture.rb +79 -0
  295. data/lib/hecks/hecks/adapters/deploy_toolchain.adapter +7 -0
  296. data/lib/hecks/hecks/adapters/deploy_toolchain.rb +108 -0
  297. data/lib/hecks/hecks/adapters/git.adapter +7 -0
  298. data/lib/hecks/hecks/adapters/git.rb +102 -0
  299. data/lib/hecks/hecks/adapters/host_http.adapter +7 -0
  300. data/lib/hecks/hecks/adapters/host_http.rb +65 -0
  301. data/lib/hecks/hecks/adapters/in_process_boot.adapter +7 -0
  302. data/lib/hecks/hecks/adapters/in_process_boot.rb +197 -0
  303. data/lib/hecks/hecks/adapters/in_process_operations.rb +202 -0
  304. data/lib/hecks/hecks/adapters/journal_store/changes.rb +143 -0
  305. data/lib/hecks/hecks/adapters/journal_store/compaction.rb +145 -0
  306. data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +132 -0
  307. data/lib/hecks/hecks/adapters/journal_store/examination.rb +135 -0
  308. data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +163 -0
  309. data/lib/hecks/hecks/adapters/journal_store/readings.rb +187 -0
  310. data/lib/hecks/hecks/adapters/journal_store.adapter +7 -0
  311. data/lib/hecks/hecks/adapters/journal_store.rb +64 -0
  312. data/lib/hecks/hecks/adapters/local_files.adapter +7 -0
  313. data/lib/hecks/hecks/adapters/local_files.rb +46 -0
  314. data/lib/hecks/hecks/adapters/pg_admin.adapter +7 -0
  315. data/lib/hecks/hecks/adapters/pg_admin.rb +245 -0
  316. data/lib/hecks/hecks/adapters/process_pool.adapter +7 -0
  317. data/lib/hecks/hecks/adapters/process_pool.rb +119 -0
  318. data/lib/hecks/hecks/adapters/rust_toolchain.adapter +7 -0
  319. data/lib/hecks/hecks/adapters/rust_toolchain.rb +179 -0
  320. data/lib/hecks/hecks/adapters/rust_workspace.rb +150 -0
  321. data/lib/hecks/hecks/adapters/shell.adapter +7 -0
  322. data/lib/hecks/hecks/adapters/shell.rb +49 -0
  323. data/lib/hecks/hecks/adapters/terminal.adapter +7 -0
  324. data/lib/hecks/hecks/adapters/terminal.rb +74 -0
  325. data/lib/hecks/hecks/codebase.behaviors +172 -0
  326. data/lib/hecks/hecks/codebase.bluebook +2453 -0
  327. data/lib/hecks/hecks/context_map.hecksagon +13 -0
  328. data/lib/hecks/hecks/custodian.behaviors +179 -0
  329. data/lib/hecks/hecks/custodian.bluebook +1847 -0
  330. data/lib/hecks/hecks/deploy.behaviors +74 -0
  331. data/lib/hecks/hecks/deploy.world +12 -0
  332. data/lib/hecks/hecks/environments/memory.world +10 -0
  333. data/lib/hecks/hecks/governance.world +7 -0
  334. data/lib/hecks/hecks/hecks.behaviors +62 -0
  335. data/lib/hecks/hecks/hecks.bluebook +124 -0
  336. data/lib/hecks/hecks/hecks.hecksagon +390 -0
  337. data/lib/hecks/hecks/hecks.world +42 -0
  338. data/lib/hecks/ir.rb +14 -83
  339. data/lib/hecks/kernel_capabilities.rb +194 -0
  340. data/lib/hecks/language/adapter.bluebook +2 -7
  341. data/lib/hecks/language/bluebook/aggregate.bluebook +56 -221
  342. data/lib/hecks/language/bluebook/attaches/paging.bluebook +6 -15
  343. data/lib/hecks/language/bluebook/bluebook.bluebook +26 -83
  344. data/lib/hecks/language/bluebook/bluebook.hecksagon +3 -15
  345. data/lib/hecks/language/bluebook/command.bluebook +27 -144
  346. data/lib/hecks/language/bluebook/entity.bluebook +24 -97
  347. data/lib/hecks/language/bluebook/policy.bluebook +16 -67
  348. data/lib/hecks/language/bluebook/process_manager.bluebook +28 -151
  349. data/lib/hecks/language/bluebook/projection.bluebook +126 -60
  350. data/lib/hecks/language/bluebook/query.bluebook +19 -43
  351. data/lib/hecks/language/bluebook/shape.bluebook +23 -73
  352. data/lib/hecks/language/bluebook/syntax.bluebook +51 -262
  353. data/lib/hecks/language/bluebook/vocabulary.bluebook +83 -198
  354. data/lib/hecks/language/hecksagon/adapter_binding.bluebook +3 -10
  355. data/lib/hecks/language/hecksagon/domain_port.bluebook +14 -19
  356. data/lib/hecks/language/hecksagon/hecksagon.bluebook +26 -25
  357. data/lib/hecks/language/hecksagon/port_operation.bluebook +3 -14
  358. data/lib/hecks/language/oidc.json +35 -0
  359. data/lib/hecks/language/port.bluebook +4 -17
  360. data/lib/hecks/language/translation/translation.bluebook +3 -9
  361. data/lib/hecks/language/translation/translation_aggregate.bluebook +5 -24
  362. data/lib/hecks/language/world/wiring.bluebook +1 -3
  363. data/lib/hecks/language/world/world.bluebook +5 -7
  364. data/lib/hecks/literal.rb +8 -50
  365. data/lib/hecks/mcp_stdio_guard.rb +4 -41
  366. data/lib/hecks/naming.rb +29 -137
  367. data/lib/hecks/persistence_legacy_fixture/regenerate.rb +161 -0
  368. data/lib/hecks/persistence_legacy_fixture.rb +113 -0
  369. data/lib/hecks/ports/access_control.rb +7 -23
  370. data/lib/hecks/ports/agent/answers.rb +5 -23
  371. data/lib/hecks/ports/agent.rb +47 -172
  372. data/lib/hecks/ports/authentication.rb +12 -39
  373. data/lib/hecks/ports/authorization.rb +16 -43
  374. data/lib/hecks/ports/clock.rb +5 -45
  375. data/lib/hecks/ports/extraction.rb +7 -12
  376. data/lib/hecks/ports/identity_assignment.rb +8 -29
  377. data/lib/hecks/ports/identity_generation.rb +6 -29
  378. data/lib/hecks/ports/identity_resolution.rb +2 -10
  379. data/lib/hecks/ports/key_vault.rb +9 -45
  380. data/lib/hecks/ports/persistence/append_only.rb +61 -64
  381. data/lib/hecks/ports/persistence/binding_policy.rb +4 -14
  382. data/lib/hecks/ports/persistence/codec_boundary.rb +20 -80
  383. data/lib/hecks/ports/persistence/null_saga_store.rb +4 -20
  384. data/lib/hecks/ports/persistence/plugin.rb +13 -42
  385. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +44 -144
  386. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +15 -115
  387. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +20 -85
  388. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +6 -25
  389. data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +18 -24
  390. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +55 -181
  391. data/lib/hecks/ports/persistence/plugins/era/postgres_era/events.rb +77 -0
  392. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +12 -122
  393. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +28 -128
  394. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +73 -474
  395. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +31 -124
  396. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +45 -219
  397. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +13 -108
  398. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +19 -76
  399. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +9 -49
  400. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +6 -123
  401. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +23 -55
  402. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +14 -72
  403. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +7 -16
  404. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +43 -127
  405. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +13 -45
  406. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +131 -555
  407. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +13 -63
  408. data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +227 -0
  409. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +2 -17
  410. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +3 -14
  411. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +13 -58
  412. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -17
  413. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +9 -26
  414. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +50 -47
  415. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +18 -124
  416. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +34 -123
  417. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +5 -12
  418. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +3 -9
  419. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -14
  420. data/lib/hecks/ports/persistence/plugins/era/translation.rb +1 -0
  421. data/lib/hecks/ports/persistence/plugins/era.rb +4 -23
  422. data/lib/hecks/ports/persistence/postgres_dump/connection.rb +7 -23
  423. data/lib/hecks/ports/persistence/postgres_dump.rb +4 -35
  424. data/lib/hecks/ports/persistence/remote_runtime.rb +2 -24
  425. data/lib/hecks/ports/persistence/repository_factory.rb +8 -27
  426. data/lib/hecks/ports/persistence/state_codec.rb +47 -108
  427. data/lib/hecks/ports/persistence.rb +7 -14
  428. data/lib/hecks/ports/projection.rb +42 -84
  429. data/lib/hecks/ports/query/in_memory.rb +10 -58
  430. data/lib/hecks/ports/query/ordering.rb +11 -32
  431. data/lib/hecks/ports/query.rb +14 -37
  432. data/lib/hecks/ports.rb +2 -8
  433. data/lib/hecks/projection_files.rb +285 -0
  434. data/lib/hecks/projections/bootstrap_table.rb +12 -44
  435. data/lib/hecks/projections/deploy/fargate/assembly.rb +2 -15
  436. data/lib/hecks/projections/deploy/fargate/cdn.rb +1 -48
  437. data/lib/hecks/projections/deploy/fargate/cdn_default.rb +2 -11
  438. data/lib/hecks/projections/deploy/fargate/check.rb +5 -14
  439. data/lib/hecks/projections/deploy/fargate/containers.rb +3 -78
  440. data/lib/hecks/projections/deploy/fargate/extras.rb +3 -44
  441. data/lib/hecks/projections/deploy/fargate/monitoring.rb +0 -44
  442. data/lib/hecks/projections/deploy/fargate/settings.rb +2 -54
  443. data/lib/hecks/projections/deploy/fargate/yaml.rb +13 -58
  444. data/lib/hecks/projections/deploy/fargate.rb +40 -207
  445. data/lib/hecks/projections/deploy/lambda.rb +191 -708
  446. data/lib/hecks/projections/deploy/preview/access_yaml.rb +1 -33
  447. data/lib/hecks/projections/deploy/preview/containers.rb +6 -111
  448. data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
  449. data/lib/hecks/projections/deploy/preview/routing_yaml.rb +4 -56
  450. data/lib/hecks/projections/deploy/preview/script.rb +2 -14
  451. data/lib/hecks/projections/deploy/preview/settings.rb +3 -33
  452. data/lib/hecks/projections/deploy/preview/task_yaml.rb +2 -38
  453. data/lib/hecks/projections/deploy/preview/template.rb +3 -59
  454. data/lib/hecks/projections/deploy/preview.rb +2 -67
  455. data/lib/hecks/projections/deploy/scripts/deploy-service.sh.tmpl +1 -1
  456. data/lib/hecks/projections/deploy/scripts/expected-era.header +1 -1
  457. data/lib/hecks/projections/deploy/scripts/hosting.mk.tmpl +3 -3
  458. data/lib/hecks/projections/deploy/scripts/settings.rb +3 -25
  459. data/lib/hecks/projections/deploy/scripts/smoke-after-deploy.sh.tmpl +1 -1
  460. data/lib/hecks/projections/deploy/scripts.rb +4 -57
  461. data/lib/hecks/projections/deploy/shared.rb +48 -147
  462. data/lib/hecks/projections/deploy/smoke.rb +4 -61
  463. data/lib/hecks/projections/deploy/template_diff/comparer.rb +3 -25
  464. data/lib/hecks/projections/deploy/template_diff/loader.rb +2 -11
  465. data/lib/hecks/projections/deploy/template_diff.rb +5 -57
  466. data/lib/hecks/projections/diagrams.rb +22 -584
  467. data/lib/hecks/projections/glossary/html.rb +14 -130
  468. data/lib/hecks/projections/glossary/markdown.rb +8 -69
  469. data/lib/hecks/projections/glossary/mermaid.rb +9 -66
  470. data/lib/hecks/projections/glossary/sensitivity.rb +5 -34
  471. data/lib/hecks/projections/glossary/sentences.rb +14 -159
  472. data/lib/hecks/projections/glossary.rb +30 -222
  473. data/lib/hecks/projections/ir.rb +3 -10
  474. data/lib/hecks/projections/model/deviations.rb +16 -43
  475. data/lib/hecks/projections/model.rb +12 -51
  476. data/lib/hecks/projections/oidc.rb +12 -56
  477. data/lib/hecks/projections/parser_table.rb +13 -32
  478. data/lib/hecks/projections/reference.rb +4 -18
  479. data/lib/hecks/projections/rust_vocabulary.rb +13 -251
  480. data/lib/hecks/projections/shape.rb +3 -29
  481. data/lib/hecks/projections/statements.rb +7 -86
  482. data/lib/hecks/projections/vocabulary.rb +6 -22
  483. data/lib/hecks/projections.rb +2 -16
  484. data/lib/hecks/projector/cli_projector.rb +154 -400
  485. data/lib/hecks/projector/docs_projector.rb +10 -201
  486. data/lib/hecks/projector/exporter.rb +42 -134
  487. data/lib/hecks/projector/ir_projector.rb +2 -9
  488. data/lib/hecks/projector/narrate_projector.rb +15 -189
  489. data/lib/hecks/projector/target.rb +13 -76
  490. data/lib/hecks/projector.rb +30 -121
  491. data/lib/hecks/quality_control/adapters/agent.adapter +7 -0
  492. data/lib/hecks/quality_control/adapters/agent.rb +58 -0
  493. data/lib/hecks/quality_control/adapters/angle_tools.adapter +7 -0
  494. data/lib/hecks/quality_control/adapters/angle_tools.rb +15 -0
  495. data/lib/hecks/quality_control/adapters/clearance_recorder.rb +36 -0
  496. data/lib/hecks/quality_control/adapters/clearance_tools.adapter +7 -0
  497. data/lib/hecks/quality_control/adapters/clearance_tools.rb +17 -0
  498. data/lib/hecks/quality_control/adapters/git_pr.adapter +7 -0
  499. data/lib/hecks/quality_control/adapters/git_pr.rb +226 -0
  500. data/lib/hecks/quality_control/adapters/github_checks.adapter +9 -0
  501. data/lib/hecks/quality_control/adapters/github_checks.rb +83 -0
  502. data/lib/hecks/quality_control/adapters/github_ci_webhook.rb +56 -0
  503. data/lib/hecks/quality_control/adapters/github_issues.adapter +7 -0
  504. data/lib/hecks/quality_control/adapters/github_issues.rb +49 -0
  505. data/lib/hecks/quality_control/adapters/qa_tool.rb +59 -0
  506. data/lib/hecks/quality_control/adapters/sweep_tools.adapter +7 -0
  507. data/lib/hecks/quality_control/adapters/sweep_tools.rb +51 -0
  508. data/lib/hecks/quality_control/adapters/target_tools.adapter +7 -0
  509. data/lib/hecks/quality_control/adapters/target_tools.rb +43 -0
  510. data/lib/hecks/quality_control/cli/child.rb +45 -0
  511. data/lib/hecks/quality_control/cli/qa_concurrency_racer.rb +45 -0
  512. data/lib/hecks/quality_control/cli/qa_discover_external_domains.rb +248 -0
  513. data/lib/hecks/quality_control/cli/qa_domain_novelty.rb +163 -0
  514. data/lib/hecks/quality_control/cli/qa_generated_domains.rb +450 -0
  515. data/lib/hecks/quality_control/cli/qa_log_bug.rb +195 -0
  516. data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +220 -0
  517. data/lib/hecks/quality_control/cli/qa_open_pr.rb +305 -0
  518. data/lib/hecks/quality_control/cli/qa_postgres_migrate.rb +154 -0
  519. data/lib/hecks/quality_control/cli/qa_postgres_role.rb +159 -0
  520. data/lib/hecks/quality_control/cli/qa_pr_check.rb +232 -0
  521. data/lib/hecks/quality_control/cli/qa_seed_angles.rb +142 -0
  522. data/lib/hecks/quality_control/cli/qa_seed_targets.rb +69 -0
  523. data/lib/hecks/quality_control/cli/qa_sweep/all_mode.rb +280 -0
  524. data/lib/hecks/quality_control/cli/qa_sweep/checks.rb +329 -0
  525. data/lib/hecks/quality_control/cli/qa_sweep/dials.rb +92 -0
  526. data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +117 -0
  527. data/lib/hecks/quality_control/cli/qa_sweep/parsing.rb +175 -0
  528. data/lib/hecks/quality_control/cli/qa_sweep/release_mode.rb +60 -0
  529. data/lib/hecks/quality_control/cli/qa_sweep.rb +611 -0
  530. data/lib/hecks/quality_control/cli/qa_tick.rb +274 -0
  531. data/lib/hecks/quality_control/quality_control.bluebook +2224 -0
  532. data/lib/hecks/quality_control/quality_control.hecksagon +13 -0
  533. data/lib/hecks/quality_control/quality_control.ports.hecksagon +54 -0
  534. data/lib/hecks/query_ir.rb +346 -0
  535. data/lib/hecks/query_ir_mcp.rb +198 -0
  536. data/lib/hecks/query_specification/common/comparators.rb +5 -26
  537. data/lib/hecks/query_specification/common/comparison.rb +35 -143
  538. data/lib/hecks/query_specification/common/dsl.rb +8 -24
  539. data/lib/hecks/query_specification/common/null_policy.rb +20 -70
  540. data/lib/hecks/query_specification/common/options.rb +2 -6
  541. data/lib/hecks/query_specification/common/where_clause.rb +2 -10
  542. data/lib/hecks/query_specification/field_path.rb +37 -77
  543. data/lib/hecks/query_specification/hop_path.rb +12 -76
  544. data/lib/hecks/release/gem_pin.rb +2 -26
  545. data/lib/hecks/release/runner/ci_publisher.rb +5 -8
  546. data/lib/hecks/release/runner/clean_tree.rb +42 -0
  547. data/lib/hecks/release/runner/commands.rb +2 -4
  548. data/lib/hecks/release/runner/console.rb +6 -0
  549. data/lib/hecks/release/runner/gem_publisher.rb +15 -9
  550. data/lib/hecks/release/runner/npm_publisher.rb +9 -53
  551. data/lib/hecks/release/runner/preflight.rb +29 -20
  552. data/lib/hecks/release/runner/published.rb +8 -26
  553. data/lib/hecks/release/runner/tagger.rb +31 -23
  554. data/lib/hecks/release/runner.rb +60 -38
  555. data/lib/hecks/rendering.rb +4 -26
  556. data/lib/hecks/router/namespace_installer.rb +15 -39
  557. data/lib/hecks/router.rb +4 -8
  558. data/lib/hecks/runtime/adapter_lookup.rb +124 -0
  559. data/lib/hecks/runtime/aggregate_lock.rb +8 -33
  560. data/lib/hecks/runtime/boot_gates.rb +3 -18
  561. data/lib/hecks/runtime/caller.rb +19 -59
  562. data/lib/hecks/runtime/capability_graph.rb +5 -21
  563. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +16 -78
  564. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +27 -160
  565. data/lib/hecks/runtime/command_interpreter.rb +124 -306
  566. data/lib/hecks/runtime/command_rules/admissibility.rb +95 -366
  567. data/lib/hecks/runtime/command_rules/arithmetic.rb +66 -196
  568. data/lib/hecks/runtime/command_rules/authorization.rb +13 -45
  569. data/lib/hecks/runtime/command_rules/emission.rb +8 -21
  570. data/lib/hecks/runtime/command_rules/references.rb +23 -202
  571. data/lib/hecks/runtime/dependency_planning.rb +50 -104
  572. data/lib/hecks/runtime/dispatcher.rb +134 -243
  573. data/lib/hecks/runtime/entity_element.rb +40 -462
  574. data/lib/hecks/runtime/entity_interpreter.rb +64 -219
  575. data/lib/hecks/runtime/errors.rb +32 -115
  576. data/lib/hecks/runtime/event.rb +3 -25
  577. data/lib/hecks/runtime/identity.rb +18 -154
  578. data/lib/hecks/runtime/instance.rb +18 -150
  579. data/lib/hecks/runtime/interpreting.rb +14 -70
  580. data/lib/hecks/runtime/invocation.rb +28 -164
  581. data/lib/hecks/runtime/loader.rb +109 -194
  582. data/lib/hecks/runtime/outbox.rb +39 -262
  583. data/lib/hecks/runtime/policy_interpreter.rb +43 -202
  584. data/lib/hecks/runtime/port_operation_interpreter.rb +28 -97
  585. data/lib/hecks/runtime/query_interpreter.rb +122 -187
  586. data/lib/hecks/runtime/reaction_invocation.rb +36 -138
  587. data/lib/hecks/runtime/reaction_outcome.rb +89 -0
  588. data/lib/hecks/runtime/read_model_interpreter.rb +117 -208
  589. data/lib/hecks/runtime/rebuild_sweep.rb +8 -54
  590. data/lib/hecks/runtime/reference_hop.rb +8 -81
  591. data/lib/hecks/runtime/refusal_wording.rb +28 -87
  592. data/lib/hecks/runtime/registry/saga_persistence.rb +10 -102
  593. data/lib/hecks/runtime/registry/verification.rb +213 -227
  594. data/lib/hecks/runtime/registry/world_defaults.rb +4 -53
  595. data/lib/hecks/runtime/registry.rb +156 -247
  596. data/lib/hecks/runtime/remote_dispatcher.rb +68 -154
  597. data/lib/hecks/runtime/routing.rb +4 -15
  598. data/lib/hecks/runtime/saga_interpreter/correlation.rb +14 -61
  599. data/lib/hecks/runtime/saga_interpreter.rb +79 -263
  600. data/lib/hecks/runtime/saga_pending_dispatch.rb +2 -43
  601. data/lib/hecks/runtime/tenant_check.rb +6 -68
  602. data/lib/hecks/runtime/tenant_scope.rb +3 -28
  603. data/lib/hecks/runtime/value/admission.rb +16 -67
  604. data/lib/hecks/runtime/value/coercion.rb +162 -760
  605. data/lib/hecks/runtime/value/entity_list_coercion.rb +26 -219
  606. data/lib/hecks/runtime/value/field_checks.rb +172 -0
  607. data/lib/hecks/runtime/value.rb +15 -84
  608. data/lib/hecks/runtime.rb +35 -69
  609. data/lib/hecks/rust_build/conformance.rb +158 -0
  610. data/lib/hecks/rust_build/conformance_fuzz.rb +71 -0
  611. data/lib/hecks/rust_build/coverage.rb +218 -0
  612. data/lib/hecks/rust_build/native_build.rb +124 -0
  613. data/lib/hecks/rust_build/project_rust/root_mod.rb +120 -0
  614. data/lib/hecks/rust_build/project_rust.rb +332 -0
  615. data/lib/hecks/rust_build/wasm.rb +105 -0
  616. data/lib/hecks/rust_build/wasm_browser.rb +113 -0
  617. data/lib/hecks/rust_build.rb +144 -0
  618. data/lib/hecks/storehouse.rb +155 -582
  619. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +10 -34
  620. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +6 -23
  621. data/lib/hecks/three_zero/forms.yml +127 -0
  622. data/lib/hecks/three_zero.rb +12 -0
  623. data/lib/hecks/tools/argument_gate_matrix.rb +316 -0
  624. data/lib/hecks/tools/comment_style.rb +1070 -0
  625. data/lib/hecks/tools/corpus_report.rb +87 -0
  626. data/lib/hecks/tools/deploy_recipe.rb +255 -0
  627. data/lib/hecks/tools/deploy_recipe_lint.rb +282 -0
  628. data/lib/hecks/tools/drop_implicit_append_fields.rb +95 -0
  629. data/lib/hecks/tools/era_projection_backfill.rb +94 -0
  630. data/lib/hecks/tools/era_reattest.rb +146 -0
  631. data/lib/hecks/tools/evolve_run.rb +189 -0
  632. data/lib/hecks/tools/fuzz_sweep.rb +373 -0
  633. data/lib/hecks/tools/heki_compaction.rb +100 -0
  634. data/lib/hecks/tools/hoist_local_givens.rb +246 -0
  635. data/lib/hecks/tools/journal_compaction.rb +89 -0
  636. data/lib/hecks/tools/oidc_manifests.rb +70 -0
  637. data/lib/hecks/tools/query_ir_run.rb +81 -0
  638. data/lib/hecks/tools/regeneration_run.rb +166 -0
  639. data/lib/hecks/tools/rust_comment_style.rb +642 -0
  640. data/lib/hecks/tools/sequence_script.rb +62 -0
  641. data/lib/hecks/tools/tail_merge.rb +117 -0
  642. data/lib/hecks/tools/tenant_provisioning.rb +142 -0
  643. data/lib/hecks/tools/translation_audit.rb +208 -0
  644. data/lib/hecks/tools/translation_scaffold.rb +131 -0
  645. data/lib/hecks/tools.rb +86 -0
  646. data/lib/hecks/vendoring/git_environment.rb +2 -8
  647. data/lib/hecks/vendoring/git_source.rb +4 -10
  648. data/lib/hecks/vendoring.rb +7 -46
  649. data/lib/hecks/version.rb +3 -14
  650. data/lib/hecks.rb +52 -90
  651. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -46
  652. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +11 -80
  653. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +10 -99
  654. data/qa/settings.yml +66 -0
  655. data/rust/.cargo/config.toml +6 -0
  656. data/rust/.gitignore +2 -0
  657. data/rust/Cargo.lock +54 -0
  658. data/rust/Cargo.toml +61 -0
  659. data/rust/build/.cargo/config.toml +3 -0
  660. data/rust/build/.gitignore +1 -0
  661. data/rust/build/Cargo.lock +7 -0
  662. data/rust/build/Cargo.toml +19 -0
  663. data/rust/build/src/build_artifact.rs +47 -0
  664. data/rust/build/src/cargo_sync.rs +248 -0
  665. data/rust/build/src/json.rs +421 -0
  666. data/rust/build/src/lineage_pass.rs +285 -0
  667. data/rust/build/src/main.rs +51 -0
  668. data/rust/build/src/optional_pass.rs +150 -0
  669. data/rust/build/src/pipeline.rs +264 -0
  670. data/rust/build/src/reserved_names.rs +76 -0
  671. data/rust/build/src/resolve.rs +124 -0
  672. data/rust/build/src/root.rs +25 -0
  673. data/rust/build/src/sidecars.rs +73 -0
  674. data/rust/build/src/subprocess.rs +51 -0
  675. data/rust/build/src/tmp.rs +31 -0
  676. data/rust/codegen/.cargo/config.toml +3 -0
  677. data/rust/codegen/.gitignore +1 -0
  678. data/rust/codegen/Cargo.lock +7 -0
  679. data/rust/codegen/Cargo.toml +20 -0
  680. data/rust/codegen/src/attr.rs +32 -0
  681. data/rust/codegen/src/bridging.rs +481 -0
  682. data/rust/codegen/src/commands.rs +1226 -0
  683. data/rust/codegen/src/constraints.rs +109 -0
  684. data/rust/codegen/src/dependency_planning.rs +209 -0
  685. data/rust/codegen/src/domain_generator.rs +1288 -0
  686. data/rust/codegen/src/exemplar.rs +230 -0
  687. data/rust/codegen/src/expr_emitter.rs +87 -0
  688. data/rust/codegen/src/fielded.rs +192 -0
  689. data/rust/codegen/src/hecks_naming.rs +33 -0
  690. data/rust/codegen/src/json.rs +335 -0
  691. data/rust/codegen/src/json_codec.rs +697 -0
  692. data/rust/codegen/src/literal.rs +207 -0
  693. data/rust/codegen/src/main.rs +290 -0
  694. data/rust/codegen/src/manifest.rs +227 -0
  695. data/rust/codegen/src/mutations.rs +784 -0
  696. data/rust/codegen/src/naming.rs +286 -0
  697. data/rust/codegen/src/ports.rs +208 -0
  698. data/rust/codegen/src/prelude.rs +116 -0
  699. data/rust/codegen/src/queries.rs +786 -0
  700. data/rust/codegen/src/reactions.rs +532 -0
  701. data/rust/codegen/src/read_models.rs +697 -0
  702. data/rust/codegen/src/reference_specs.rs +40 -0
  703. data/rust/codegen/src/registry.rs +966 -0
  704. data/rust/codegen/src/reserved_names.rs +76 -0
  705. data/rust/codegen/src/shared.rs +93 -0
  706. data/rust/codegen/src/skip_reason.rs +25 -0
  707. data/rust/codegen/src/types.rs +362 -0
  708. data/rust/host/.cargo/config.toml +3 -0
  709. data/rust/host/.gitignore +1 -0
  710. data/rust/host/Cargo.lock +4297 -0
  711. data/rust/host/Cargo.toml +61 -0
  712. data/rust/host/HECKS_RELEASE +1 -0
  713. data/rust/host/rds-ca-bundle.pem +2736 -0
  714. data/rust/host/src/api.rs +1596 -0
  715. data/rust/host/src/approval.rs +652 -0
  716. data/rust/host/src/auth.rs +1287 -0
  717. data/rust/host/src/bin/expr_harness.rs +46 -0
  718. data/rust/host/src/bin/lineage_harness.rs +119 -0
  719. data/rust/host/src/bin/mint_harness.rs +135 -0
  720. data/rust/host/src/checkout.rs +265 -0
  721. data/rust/host/src/dispatch.rs +1443 -0
  722. data/rust/host/src/expr_json.rs +1117 -0
  723. data/rust/host/src/field_hints.rs +52 -0
  724. data/rust/host/src/ir.rs +733 -0
  725. data/rust/host/src/journal.rs +874 -0
  726. data/rust/host/src/lambda_client.rs +378 -0
  727. data/rust/host/src/log.rs +147 -0
  728. data/rust/host/src/main.rs +474 -0
  729. data/rust/host/src/mint.rs +1865 -0
  730. data/rust/host/src/payments/keystore.rs +330 -0
  731. data/rust/host/src/payments/tests.rs +1400 -0
  732. data/rust/host/src/payments.rs +767 -0
  733. data/rust/host/src/presentation.rs +698 -0
  734. data/rust/host/src/presentation_write.rs +1431 -0
  735. data/rust/host/src/query_step.rs +149 -0
  736. data/rust/host/src/rate_limit.rs +904 -0
  737. data/rust/host/src/reference_transform.rs +241 -0
  738. data/rust/host/src/reference_validate.rs +338 -0
  739. data/rust/host/src/resend.rs +296 -0
  740. data/rust/host/src/secrets.rs +106 -0
  741. data/rust/host/src/server.rs +663 -0
  742. data/rust/host/src/storage_shape.rs +175 -0
  743. data/rust/host/src/test_pg.rs +40 -0
  744. data/rust/host/src/ui_schema.rs +1351 -0
  745. data/rust/host/src/wasm_runner.rs +191 -0
  746. data/rust/host/src/web/newsletter/cooldown.rs +126 -0
  747. data/rust/host/src/web/newsletter/tests.rs +104 -0
  748. data/rust/host/src/web/newsletter.rs +310 -0
  749. data/rust/host/src/web/newsletter_send.rs +357 -0
  750. data/rust/host/src/web/registrations.rs +1505 -0
  751. data/rust/host/src/web.rs +2937 -0
  752. data/rust/lsp/.gitignore +1 -0
  753. data/rust/lsp/Cargo.lock +7 -0
  754. data/rust/lsp/Cargo.toml +16 -0
  755. data/rust/lsp/README.md +133 -0
  756. data/rust/lsp/src/diagnostics.rs +165 -0
  757. data/rust/lsp/src/json.rs +385 -0
  758. data/rust/lsp/src/main.rs +399 -0
  759. data/rust/lsp/src/outline.rs +177 -0
  760. data/rust/lsp/src/rpc.rs +67 -0
  761. data/rust/parser/.cargo/config.toml +5 -0
  762. data/rust/parser/.gitignore +1 -0
  763. data/rust/parser/Cargo.lock +7 -0
  764. data/rust/parser/Cargo.toml +21 -0
  765. data/rust/parser/src/build/closed_sets.rs +27 -0
  766. data/rust/parser/src/build/identity.rs +276 -0
  767. data/rust/parser/src/build/mod.rs +12 -0
  768. data/rust/parser/src/build/naming.rs +141 -0
  769. data/rust/parser/src/build/pattern_subset.rs +161 -0
  770. data/rust/parser/src/build/query_derive.rs +72 -0
  771. data/rust/parser/src/build/query_inference.rs +269 -0
  772. data/rust/parser/src/build/query_options.rs +100 -0
  773. data/rust/parser/src/build/read_model.rs +94 -0
  774. data/rust/parser/src/build/references.rs +32 -0
  775. data/rust/parser/src/canonical.rs +114 -0
  776. data/rust/parser/src/diag.rs +55 -0
  777. data/rust/parser/src/emit.rs +953 -0
  778. data/rust/parser/src/expr/ast_json.rs +147 -0
  779. data/rust/parser/src/expr/evaluator.rs +81 -0
  780. data/rust/parser/src/expr/mod.rs +84 -0
  781. data/rust/parser/src/expr/resolver.rs +497 -0
  782. data/rust/parser/src/ir.rs +387 -0
  783. data/rust/parser/src/keywords.rs +585 -0
  784. data/rust/parser/src/lex.rs +662 -0
  785. data/rust/parser/src/main.rs +252 -0
  786. data/rust/parser/src/parse/aggregate.rs +612 -0
  787. data/rust/parser/src/parse/chapter.rs +517 -0
  788. data/rust/parser/src/parse/command.rs +637 -0
  789. data/rust/parser/src/parse/domain_port.rs +127 -0
  790. data/rust/parser/src/parse/entity.rs +527 -0
  791. data/rust/parser/src/parse/file.rs +67 -0
  792. data/rust/parser/src/parse/hecksagon.rs +231 -0
  793. data/rust/parser/src/parse/lifecycle.rs +162 -0
  794. data/rust/parser/src/parse/mod.rs +1262 -0
  795. data/rust/parser/src/parse/policy.rs +87 -0
  796. data/rust/parser/src/parse/process_manager.rs +305 -0
  797. data/rust/parser/src/parse/query.rs +100 -0
  798. data/rust/parser/src/parse/read_model.rs +214 -0
  799. data/rust/parser/src/parse/value_object.rs +250 -0
  800. data/rust/parser/src/ruby_value.rs +422 -0
  801. data/rust/project/bridging.rb +257 -0
  802. data/rust/project/commands.rb +877 -0
  803. data/rust/project/constraints.rb +86 -0
  804. data/rust/project/dependency_planning.rb +178 -0
  805. data/rust/project/domain_generator.rb +980 -0
  806. data/rust/project/exemplar.rb +148 -0
  807. data/rust/project/expr_emitter.rb +72 -0
  808. data/rust/project/fielded.rb +141 -0
  809. data/rust/project/json_codec.rb +641 -0
  810. data/rust/project/mutations.rb +721 -0
  811. data/rust/project/naming.rb +205 -0
  812. data/rust/project/ports.rb +65 -0
  813. data/rust/project/queries.rb +493 -0
  814. data/rust/project/reactions.rb +317 -0
  815. data/rust/project/read_models.rb +690 -0
  816. data/rust/project/reference_specs.rb +27 -0
  817. data/rust/project/registry.rb +396 -0
  818. data/rust/project/skip_reason.rb +29 -0
  819. data/rust/project/types.rb +225 -0
  820. data/rust/project/write_if_changed.rb +83 -0
  821. data/rust/project.rb +30 -0
  822. data/rust/project_rust_pipeline.rb +383 -0
  823. data/rust/src/exemplar/commands.rs +279 -0
  824. data/rust/src/exemplar/constraints.rs +18 -0
  825. data/rust/src/exemplar/fielded.rs +280 -0
  826. data/rust/src/exemplar/json.rs +277 -0
  827. data/rust/src/exemplar/mod.rs +17 -0
  828. data/rust/src/exemplar/mutations.rs +80 -0
  829. data/rust/src/exemplar/queries.rs +33 -0
  830. data/rust/src/exemplar/reactions.rs +88 -0
  831. data/rust/src/exemplar/read_models.rs +48 -0
  832. data/rust/src/exemplar/registry.rs +166 -0
  833. data/rust/src/exemplar/types.rs +44 -0
  834. data/rust/src/kernel/attribute_shapes/composite.rs +88 -0
  835. data/rust/src/kernel/attribute_shapes/list.rs +18 -0
  836. data/rust/src/kernel/attribute_shapes/mod.rs +44 -0
  837. data/rust/src/kernel/attribute_shapes/optional.rs +9 -0
  838. data/rust/src/kernel/attribute_shapes/scalar.rs +39 -0
  839. data/rust/src/kernel/cli.rs +585 -0
  840. data/rust/src/kernel/dispatch.rs +1090 -0
  841. data/rust/src/kernel/expr.rs +458 -0
  842. data/rust/src/kernel/expression_operators/arithmetic.rs +128 -0
  843. data/rust/src/kernel/expression_operators/comparison.rs +48 -0
  844. data/rust/src/kernel/expression_operators/enumeration.rs +232 -0
  845. data/rust/src/kernel/expression_operators/logical.rs +13 -0
  846. data/rust/src/kernel/expression_operators/membership.rs +18 -0
  847. data/rust/src/kernel/expression_operators/mod.rs +68 -0
  848. data/rust/src/kernel/expression_operators/pattern_match.rs +73 -0
  849. data/rust/src/kernel/expression_operators/positional.rs +126 -0
  850. data/rust/src/kernel/expression_operators/presence.rs +101 -0
  851. data/rust/src/kernel/expression_operators/sign_test.rs +16 -0
  852. data/rust/src/kernel/expression_operators/sized.rs +35 -0
  853. data/rust/src/kernel/expression_operators/text.rs +79 -0
  854. data/rust/src/kernel/expression_operators/to_string.rs +18 -0
  855. data/rust/src/kernel/json.rs +779 -0
  856. data/rust/src/kernel/mod.rs +133 -0
  857. data/rust/src/kernel/named_query.rs +219 -0
  858. data/rust/src/kernel/orchestrate.rs +1810 -0
  859. data/rust/src/kernel/pattern.rs +307 -0
  860. data/rust/src/kernel/query_comparators.rs +206 -0
  861. data/rust/src/kernel/query_ordering.rs +103 -0
  862. data/rust/src/kernel/read_model.rs +914 -0
  863. data/rust/src/kernel/reference_lookup.rs +149 -0
  864. data/rust/src/kernel/repository.rs +468 -0
  865. data/rust/src/kernel/routing.rs +612 -0
  866. data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +118 -0
  867. data/rust/src/kernel/vocab/entity_dispatch_order.rs +114 -0
  868. data/rust/src/kernel/vocab/field_hint.rs +70 -0
  869. data/rust/src/kernel/vocab/mod.rs +20 -0
  870. data/rust/src/kernel/vocab/query_comparator.rs +65 -0
  871. data/rust/src/kernel/vocab/refusal_template.rs +1592 -0
  872. data/rust/src/lib.rs +8 -0
  873. data/rust/src/main.rs +16 -0
  874. data/rust/web/.gitignore +1 -0
  875. data/rust/web/Cargo.lock +119 -0
  876. data/rust/web/Cargo.toml +18 -0
  877. data/rust/web/src/lib.rs +8 -0
  878. metadata +478 -12
  879. data/lib/hecks/facade/cli_door.rb +0 -178
  880. data/lib/hecks/facade/cli_runner.rb +0 -271
  881. data/lib/hecks/facade/handle.rb +0 -293
  882. data/lib/hecks/facade/json_door.rb +0 -247
  883. data/lib/hecks/facade/surface/aggregate_door.rb +0 -222
  884. data/lib/hecks/facade/surface/chapter.rb +0 -116
  885. data/lib/hecks/facade/surface.rb +0 -61
  886. data/lib/hecks/mcp_door_scope.rb +0 -176
@@ -2,137 +2,77 @@ require_relative "../naming"
2
2
 
3
3
  module Hecks
4
4
  module Projector
5
- # A bluebook, projected as its own command-line surface.
5
+ # Projects a bluebook as its own command-line surface: the verb tree, argument spec
6
+ # and usage text. Nothing executes here; the generic runner (`hecks run`) parses against it.
6
7
  #
7
- # Every verb a domain declares is a subcommand; every argument is an
8
- # option whose type, whose admitted values and whose required-ness are
9
- # already stated in the chapter. A hand-written CLI restates all of it and
10
- # then drifts — and the first thing to drift is the help text, which is the
11
- # only part anybody reads.
12
- #
13
- # ## What is projected
14
- #
15
- # This answers the surface only — the verb tree, the argument spec, the
16
- # usage text — and nothing executes here. One small generic runner
17
- # (`bin/run`) boots a domain, asks for this, parses against it and
18
- # dispatches.
19
- #
20
- # ## Why project rather than generate
21
- #
22
- # The alternative was generating an executable per domain, which is what
23
- # `bin/project_rust` does for a whole runtime and would be the more
24
- # spectacular version of this. It was not taken: a generated program is a
25
- # second copy of the dispatch logic, and it needs regenerating on every
26
- # bluebook edit — a second tax beside the era gate, paid for a file nobody
27
- # reads. Projecting the surface keeps one dispatcher and a help text that
28
- # cannot be stale, because it is computed at the moment it is printed.
29
- #
30
- # ## The typing is the point
31
- #
32
- # A CLI hands everything over as a String. `sequence.value=99` has to
33
- # become the Integer 99 or the runtime refuses it, and the only honest
34
- # place to learn that is the value object's own declared field type. A CLI
35
- # that guessed — "it looks like a number" — would send 99 for a version
36
- # string of "99" and be wrong in a way nobody could see.
8
+ # Argument types come from the declared field types, never from guessing at the
9
+ # string: `sequence.value=99` must become 99, and a version "99" must stay a String.
37
10
  module CliProjector
38
11
  module_function
39
12
 
40
- # Two namespaces, not one — `{ verbs:, questions:, usage: }`.
41
- #
42
- # A chapter may legally declare a command and a query of one name: the
43
- # language namespaces them and `Banking::Account.Open` is both, in the
44
- # corpus, today. A single flat list of subcommands has to pick one, and
45
- # picking silently is how `Ticket.Filed` sat undetected in this
46
- # repository for a day.
47
- #
48
- # So a question is asked with `ask`: `bin/run ask account.open`. It is
49
- # this codebase own word — `Query::AskOption`, "the ask" — it is
50
- # shell-safe where a `?` suffix would be eaten by globbing, and it makes
51
- # the collision impossible rather than detected. It also reads as what it
52
- # is: everything under `ask` changes nothing.
13
+ # Projects the verb and question tables and the usage text. Commands and queries are
14
+ # separate namespaces (a chapter may declare both under one name); ask a query with `ask`.
53
15
  #
54
16
  # @param bluebook [Bluebook::Chapter] the booted domain to project
55
- # @param options [Hash{Symbol => Object}] `:program` (String, defaults to
56
- # `"bin/run"`) is echoed into the usage text; `:verb` (String) and `:ask`
57
- # (Boolean) select one verb's `--help` text instead of the full usage
58
- # @return [Hash{Symbol => Object}] `:verbs` and `:questions` map each projected
59
- # name to its spec hash; `:names` maps `:command`/`:question` to an alias table
60
- # (short and full spelling both keying the full name); `:usage` is the
61
- # pre-rendered help text
62
- # @raise [Bluebook::DSL::Malformed] if two verbs project to the same command-line
63
- # name
17
+ # @param options [Hash{Symbol => Object}] `:program` (default `"hecks run"`) for the usage
18
+ # text; `:verb` and `:ask` select one verb's `--help` text; `:names` maps a launcher
19
+ # name to the command it stands for (`{ "mcp" => "serve_mcp" }`); `:mint_run_keys` makes a
20
+ # creating verb's `run` key optional, since the launcher mints it
21
+ # @return [Hash{Symbol => Object}] `:verbs`, `:questions`, `:names` (alias tables) and
22
+ # `:usage` (pre-rendered help text)
23
+ # @raise [Bluebook::DSL::Malformed] if two verbs project to the same command-line name
64
24
  def call(bluebook:, options: {})
65
25
  verbs = {}
66
26
  questions = {}
67
27
 
68
- bluebook.aggregates.each do |aggregate|
69
- aggregate.commands.each { |c| claim(verbs, name_for(aggregate, c), command_spec(bluebook, aggregate, nil, c)) }
70
- aggregate.queries.each { |q| claim(questions, name_for(aggregate, q), query_spec(bluebook, aggregate, nil, q)) }
71
-
72
- aggregate.entities.each do |entity|
73
- entity.commands.each do |c|
74
- claim(verbs, name_for(aggregate, c, entity), command_spec(bluebook, aggregate, entity, c))
75
- end
76
- entity.queries.each do |q|
77
- claim(questions, name_for(aggregate, q, entity), query_spec(bluebook, aggregate, entity, q))
78
- end
79
- end
80
-
81
- # A port is a verb too, and leaving it off the map was a real gap
82
- # rather than a tasteful omission. The runtime has always dispatched
83
- # a port operation by exactly the same name as a command — the
84
- # projection simply never listed one, so `run_specs` and `file`
85
- # answered "no such verb" while working perfectly through Ruby.
86
- #
87
- # It matters most for the caller with no other door. An agent that
88
- # may not shell out reaches this domain only through the projected
89
- # CLI, and a port it cannot see is a capability it does not have.
90
- aggregate.ports.each do |port|
91
- port.operations.each { |o| claim(verbs, name_for(aggregate, o), port_spec(bluebook, aggregate, port, o)) }
92
- end
93
- end
28
+ bluebook.aggregates.each { |aggregate| claim_aggregate(verbs, questions, bluebook, aggregate) }
94
29
 
95
- # A report is a question too, and leaving it off was the same gap the
96
- # ports had: `Dispatcher#query` has always answered `Domain.ReportName`,
97
- # the projection simply never listed one — so the composed reads worked
98
- # from Ruby and did not exist for anybody whose only door is the command
99
- # line.
100
- #
101
- # It matters most for exactly what a report is for. Every other question
102
- # here answers with rows and leaves the arithmetic to the reader; a
103
- # `group_by` report is the one that counts. An agent that cannot reach
104
- # it can list bugs all day and never answer "how are we doing".
105
- #
106
- # **One dot, not two** — a report belongs to the chapter rather than to any
107
- # aggregate (that is what rootless means), so it is addressed
108
- # `QualityControl.BugsByStatus` where a query is
109
- # `QualityControl::Bug.Queue`. `Dispatcher#query` splits on precisely
110
- # that difference.
30
+ # A report belongs to the chapter, not an aggregate, so it is addressed
31
+ # `Chapter.Report` (one dot) where a query is `Chapter::Aggregate.Query`.
111
32
  bluebook.read_models.each do |model|
112
33
  claim(questions, Naming.snake(model.hecks_name), report_spec(bluebook, model))
113
34
  end
114
35
 
115
- # The short spelling, where it cannot be ambiguous. `pizzas
116
- # create_pizza` rather than `pizzas order.create_pizza` — the
117
- # aggregate is worth typing only when two of them declare the same
118
- # verb, and in a one-aggregate domain it never is. Both spellings are
119
- # always accepted; `names` maps every accepted one to its canonical
120
- # key, and the display name is the shortest that is unambiguous.
121
- shorten(verbs)
122
- shorten(questions)
36
+ mint_run_keys(verbs) if options[:mint_run_keys]
37
+
38
+ # The display name is the shortest unambiguous one; both spellings are accepted.
39
+ display_names([verbs, questions], options[:names])
123
40
 
124
41
  { verbs: verbs, questions: questions,
125
42
  names: { command: aliases(verbs), question: aliases(questions) },
126
43
  usage: usage(bluebook, verbs, questions, options) }
127
44
  end
128
45
 
129
- # A last segment is claimed only if exactly one verb ends in it. Two
130
- # aggregates declaring `Close` keep `customer.close` and `account.close`,
131
- # which is the honest answer — a CLI that picked one would be choosing
132
- # for the caller.
46
+ # Claims the verbs and questions of one aggregate and its entities and ports.
47
+ #
48
+ # @param verbs [Hash{String => Hash}] the verb map, mutated in place
49
+ # @param questions [Hash{String => Hash}] the question map, mutated in place
50
+ # @param bluebook [Bluebook::Chapter] the booted domain
51
+ # @param aggregate [Bluebook::Aggregate] the aggregate to claim
52
+ # @return [void]
53
+ def claim_aggregate(verbs, questions, bluebook, aggregate)
54
+ aggregate.commands.each { |c| claim(verbs, name_for(aggregate, c), command_spec(bluebook, aggregate, nil, c)) }
55
+ aggregate.queries.each { |q| claim(questions, name_for(aggregate, q), query_spec(bluebook, aggregate, nil, q)) }
56
+
57
+ aggregate.entities.each do |entity|
58
+ entity.commands.each do |c|
59
+ claim(verbs, name_for(aggregate, c, entity), command_spec(bluebook, aggregate, entity, c))
60
+ end
61
+ entity.queries.each do |q|
62
+ claim(questions, name_for(aggregate, q, entity), query_spec(bluebook, aggregate, entity, q))
63
+ end
64
+ end
65
+
66
+ # Port operations dispatch by the same name as a command, so they are verbs too.
67
+ aggregate.ports.each do |port|
68
+ port.operations.each { |o| claim(verbs, name_for(aggregate, o), port_spec(bluebook, aggregate, port, o)) }
69
+ end
70
+ end
71
+
72
+ # Sets `:short` on each spec: the last segment when exactly one verb ends in it,
73
+ # else the full name (two aggregates both declaring `Close` keep both spellings).
133
74
  #
134
- # @param specs [Hash{String => Hash}] the verb or question map being projected,
135
- # mutated in place: each spec hash gains a `:short` key
75
+ # @param specs [Hash{String => Hash}] the verb or question map, mutated in place
136
76
  # @return [void]
137
77
  def shorten(specs)
138
78
  tails = specs.keys.group_by { |name| name.split(".").last }
@@ -142,30 +82,68 @@ module Hecks
142
82
  end
143
83
  end
144
84
 
145
- # Builds the alias table `CliRunner` resolves a typed name against.
85
+ # Marks the `run` key of each creating verb `minted`: optional, and never filled by a bare
86
+ # word, since the launcher makes one when it is left out.
87
+ def mint_run_keys(verbs)
88
+ verbs.each_value do |spec|
89
+ next unless spec[:creates]
90
+
91
+ spec[:arguments] = spec[:arguments].map do |argument|
92
+ next argument unless argument[:path] == "run.value"
93
+
94
+ argument.merge(required: false, minted: true, note: "minted when omitted")
95
+ end
96
+ end
97
+ end
98
+
99
+ # Sets the display name of every spec in each map: shortened, then renamed by `names`.
146
100
  #
147
- # @param specs [Hash{String => Hash}] a verb or question map whose specs already
148
- # carry `:short` (set by `shorten`)
149
- # @return [Hash{String => String}] every accepted spelling — the full name and
150
- # its `:short` form — mapped to the full name
101
+ # @param maps [Array<Hash{String => Hash}>] the verb and question maps, mutated in place
102
+ # @param names [Hash, nil] the chapter's launcher names
103
+ # @return [void]
104
+ def display_names(maps, names)
105
+ maps.each do |specs|
106
+ shorten(specs)
107
+ rename(specs, names)
108
+ end
109
+ end
110
+
111
+ # Gives a command the launcher name a chapter's `names` table assigns it.
112
+ #
113
+ # The alias replaces the short name in help, so it is listed once; the spelling it
114
+ # replaced keeps working (`:short_was`, read by `aliases`). A table entry naming no
115
+ # command in `specs` belongs to the other namespace and is skipped.
116
+ #
117
+ # @param specs [Hash{String => Hash}] the verb or question map, mutated in place
118
+ # @param names [Hash{String, Symbol => String, Symbol}, nil] launcher name to command name
119
+ # @return [void]
120
+ # @raise [Bluebook::DSL::Malformed] if a launcher name is already another command's name
121
+ def rename(specs, names)
122
+ Hash(names).each do |launcher_name, command|
123
+ found = specs.find { |key, spec| [key, spec[:short]].include?(command.to_s) }
124
+ next unless found
125
+
126
+ taken = specs.values.any? { |spec| spec[:short] == launcher_name.to_s }
127
+ raise Bluebook::DSL::Malformed, "launcher name #{launcher_name.to_s.inspect} is already a command" if taken
128
+
129
+ found.last[:short_was] = found.last[:short]
130
+ found.last[:short] = launcher_name.to_s
131
+ end
132
+ end
133
+
134
+ # Maps every accepted spelling (full name, `:short` and a renamed command's old short
135
+ # name) to the full name.
151
136
  def aliases(specs)
152
137
  specs.each_with_object({}) do |(name, spec), map|
153
138
  map[name] = name
154
139
  map[spec[:short]] = name
140
+ map[spec[:short_was]] = name if spec[:short_was]
155
141
  end
156
142
  end
157
143
 
158
- # A name is claimed once. A command and a query of one name are legal in
159
- # a chapter — the language namespaces them — and ambiguous as
160
- # subcommands. Refused here rather than silently resolving to whichever
161
- # was walked first, which is how `Ticket.Filed` (a command) and
162
- # `Ticket.Filed` (a query) sat undetected until something flattened them.
144
+ # Stores `spec` under `name`, refusing a second claim rather than silently
145
+ # keeping whichever verb was walked first.
163
146
  #
164
- # @param verbs [Hash{String => Hash}] the verb or question map being built,
165
- # mutated in place
166
- # @param name [String] the command-line name this spec claims
167
- # @param spec [Hash{Symbol => Object}] the spec hash to store under `name`
168
- # @return [void]
169
147
  # @raise [Bluebook::DSL::Malformed] if `name` is already claimed by another verb
170
148
  def claim(verbs, name, spec)
171
149
  if verbs.key?(name)
@@ -177,15 +155,7 @@ module Hecks
177
155
  verbs[name] = spec
178
156
  end
179
157
 
180
- # Builds the dotted command-line name for one verb: `aggregate[.entity].verb`,
181
- # snake-cased.
182
- #
183
- # @param aggregate [Bluebook::Aggregate] the verb's owning aggregate
184
- # @param verb [Bluebook::Command, Bluebook::Query, Bluebook::PortOperation]
185
- # the command, query or port operation being named
186
- # @param entity [Bluebook::Entity, nil] the entity the verb is declared on, or `nil`
187
- # for an aggregate-level verb
188
- # @return [String] the dotted, snake-cased command-line name
158
+ # The dotted command-line name, `aggregate[.entity].verb`, snake-cased.
189
159
  def name_for(aggregate, verb, entity = nil)
190
160
  parts = [Naming.snake(aggregate.hecks_name)]
191
161
  parts << Naming.snake(entity.hecks_name) if entity
@@ -193,39 +163,15 @@ module Hecks
193
163
  parts.join(".")
194
164
  end
195
165
 
196
- # Builds the verb's fully-qualified language name, `Bluebook::Aggregate[.Entity].Verb`,
197
- # for display in help text.
198
- #
199
- # @param bluebook [Bluebook::Chapter] the verb's owning chapter
200
- # @param aggregate [Bluebook::Aggregate] the verb's owning aggregate
201
- # @param verb [Bluebook::Command, Bluebook::Query, Bluebook::PortOperation]
202
- # the command, query or port operation being named
203
- # @param entity [Bluebook::Entity, nil] the entity the verb is declared on, or `nil`
204
- # for an aggregate-level verb
205
- # @return [String] the fully-qualified name, as the language spells it
166
+ # The verb's language name, `Chapter::Aggregate[.Entity].Verb`, for help text.
206
167
  def fqn(bluebook, aggregate, verb, entity = nil)
207
168
  [bluebook.name, "::", aggregate.hecks_name, ".",
208
169
  entity ? "#{entity.hecks_name}." : "", verb.hecks_name].join
209
170
  end
210
171
 
211
- # The arguments a receiver adds, before any verb-specific one. Shared by
212
- # `command_spec` and `port_spec` — a port operation always addresses an
213
- # aggregate record (`port_spec` passes `receiver: :aggregate`, never
214
- # `:entity` or `nil`, because a port is declared on an aggregate, never
215
- # an entity), and this is exactly the same "how do I name the record"
216
- # question a command with a receiver already answers, so it is answered
217
- # once, here, rather than duplicated.
218
- #
219
- # `nil` — a creating command — takes none: there is no existing record
220
- # yet for `to=` to name.
221
- #
222
- # @param receiver [Symbol, nil] `:entity`, `:aggregate`, or `nil` for a creating
223
- # command that names no existing record
224
- # @param aggregate [Bluebook::Aggregate] the aggregate the record belongs to
225
- # @param entity [Bluebook::Entity, nil] the entity to name, required when
226
- # `receiver` is `:entity`
227
- # @return [Array<Hash{Symbol => Object}>] the `to`/`to.aggregate`+`to.entity` option
228
- # specs this receiver needs, or `[]` for `nil`
172
+ # The options that name the record a verb acts on, before its own arguments.
173
+ # `nil` (a creating command) takes none: there is no record yet.
174
+ # Ports always pass `:aggregate`, since a port is declared on an aggregate.
229
175
  def receiver_options(receiver, aggregate, entity)
230
176
  case receiver
231
177
  when :entity
@@ -243,20 +189,6 @@ module Hecks
243
189
  end
244
190
  end
245
191
 
246
- # ── one verb ──────────────────────────────────────────────────────
247
-
248
- # Projects one command into the spec `verb_help` and `CliRunner` both read.
249
- #
250
- # @param bluebook [Bluebook::Chapter] the command's owning chapter
251
- # @param aggregate [Bluebook::Aggregate] the command's owning aggregate
252
- # @param entity [Bluebook::Entity, nil] the entity the command is declared on, or
253
- # `nil` for an aggregate-level command
254
- # @param command [Bluebook::Command] the command being projected
255
- # @return [Hash{Symbol => Object}] `:verb` (fully-qualified name), `:kind` (`:command`),
256
- # `:summary`, `:role` (String, nil if the command declares none), `:role_gated`
257
- # (Boolean), `:creates` (Boolean), `:receiver` (`:entity`, `:aggregate` or `nil`),
258
- # `:legacy_receiver` (`:id` or `nil`), `:legacy_arguments`, `:refusals`
259
- # (see `refusals`) and `:arguments` (see `options_for`)
260
192
  def command_spec(bluebook, aggregate, entity, command)
261
193
  holder = entity || aggregate
262
194
  arguments = command.attributes.flat_map { |a| options_for(a, holder, aggregate) }
@@ -266,16 +198,9 @@ module Hecks
266
198
  (command.creates? ? nil : :aggregate)
267
199
  end
268
200
 
269
- # The receiver is not a command argument. An aggregate command names
270
- # its record through to; an entity command needs both the aggregate
271
- # record and the entity element within it. Keeping those paths in the
272
- # projected option list makes the human-facing request complete while
273
- # CommandRequest can remove them before it builds with: from the
274
- # command's declared facts.
275
- #
276
- # Existing aggregate scripts may still spell the receiver id=... .
277
- # That alias is deliberately hidden from help and recorded separately
278
- # as legacy_arguments; new help and examples teach only to=... .
201
+ # The receiver paths stay in the projected option list so the request is complete;
202
+ # CommandRequest strips them before building `with:`. `id=...` is still accepted
203
+ # for aggregate receivers but hidden from help, which teaches only `to=...`.
279
204
  arguments = receiver_options(receiver, aggregate, entity) + arguments
280
205
  legacy_arguments = receiver == :aggregate ? [{ path: "id", type: "String", required: true }] : []
281
206
 
@@ -284,55 +209,22 @@ module Hecks
284
209
  creates: command.creates?,
285
210
  receiver: receiver, legacy_receiver: (receiver == :aggregate ? :id : nil),
286
211
  legacy_arguments: legacy_arguments,
287
- refusals: refusals(command, holder), arguments: arguments }
212
+ refusals: refusals(command, holder), requirements: requirements(command),
213
+ arguments: arguments }
288
214
  end
289
215
 
290
- # A port operation reads as a verb but reports as a boundary.
291
- #
292
- # `creates: false` because it makes no record, and `refusals: []`
293
- # because it has none in the sense every other verb means: a command's
294
- # refusals are sentences the chapter will say back to you, and an
295
- # outbound operation's failure is somebody else's sentence, unknowable
296
- # from here.
297
- #
298
- # The summary names both endings, which is the one thing a caller most
299
- # needs and cannot infer. `run_specs` looks like it either works or
300
- # errors; what it actually does is answer `SpecsCompleted` even when the
301
- # suite is red, and refuse only when rspec could not run. Somebody
302
- # reading `--help` should not have to open the hecksagon to find that
303
- # out.
304
- #
305
- # @param bluebook [Bluebook::Chapter] the port's owning chapter
306
- # @param aggregate [Bluebook::Aggregate] the aggregate the port is declared on
307
- # @param port [Bluebook::DomainPort] the port the operation is declared on
308
- # @param operation [Bluebook::PortOperation] the operation being projected
309
- # @return [Hash{Symbol => Object}] `:verb` (the `Aggregate.port.operation` name),
310
- # `:kind` (`:command`), `:creates` (`false`), `:receiver` (`:aggregate`),
311
- # `:refusals` (`[]`), `:role` (String, who calls whom through the port),
312
- # `:role_gated` (`false`), `:summary` (see `port_summary`) and `:arguments`
313
- # (see `options_for`)
216
+ # A port operation reads as a verb but reports as a boundary: it creates no
217
+ # record, and an outbound failure is the other side's sentence, so `refusals` is empty.
314
218
  def port_spec(bluebook, aggregate, port, operation)
315
219
  arguments = receiver_options(:aggregate, aggregate, nil) +
316
220
  operation.attributes.flat_map { |a| options_for(a, aggregate, aggregate) }
317
221
 
318
- # The wire name carries the port, the typed name does not.
319
- #
320
- # `Dispatcher#dispatch` splits a verb into head and sub and looks the
321
- # head up as a port, so a port operation is addressed
322
- # `Aggregate.Port.Operation` — three parts where a command has two.
323
- # But nobody should have to type `sweep.toolchain.run_specs`: which
324
- # port a verb goes out through is wiring, and the caller's business is
325
- # what they want done. So the projection spells the verb in full and
326
- # names it short, which is the same split `shorten` already makes.
222
+ # `Dispatcher#dispatch` looks the head up as a port, so the wire name is
223
+ # `Aggregate.Port.Operation`; the short name (see `shorten`) hides the port.
327
224
  { verb: [fqn(bluebook, aggregate, operation).sub(/\.[^.]+\z/, ""), port.name, operation.hecks_name].join("."),
328
225
  kind: :command, creates: false, receiver: :aggregate, refusals: [],
329
- # `role:` here is descriptive text, not an authorization gate —
330
- # who calls whom through the port, for `--help`/`verb_help`'s
331
- # "issued by" line. A port operation never reaches
332
- # `CommandRules::Authorization#refuse_role_mismatch` (only
333
- # `CommandInterpreter`/`EntityInterpreter` call it, never the port
334
- # dispatch path), so `role_gated: false` always, unlike
335
- # `command_spec` where the same key name means a real one.
226
+ # `role:` is help text only; port dispatch never reaches the role check,
227
+ # so `role_gated` is always false here.
336
228
  role: if operation.outbound?
337
229
  "#{aggregate.hecks_name} asking #{port.name}"
338
230
  else
@@ -342,26 +234,16 @@ module Hecks
342
234
  summary: port_summary(port, operation), arguments: arguments }
343
235
  end
344
236
 
345
- # Names both endings a port operation can have: what an inbound `tells` records,
346
- # or what an outbound `asks` came back with and what it said instead.
347
- #
348
- # @param port [Bluebook::DomainPort] the operation's owning port
349
- # @param operation [Bluebook::PortOperation] the operation being summarized
350
- # @return [String] one line, worded for `--help`
237
+ # Names both endings of a port operation, since `--help` is where a caller
238
+ # learns that e.g. a spec run answers `SpecsCompleted` even when the suite is red.
351
239
  def port_summary(port, operation)
352
240
  return "#{port.name} reports it; emits #{operation.emits.join(', ')}" unless operation.outbound?
353
241
 
354
242
  "Ask #{port.name} — answers #{operation.answers}, refuses #{operation.refuses}"
355
243
  end
356
244
 
357
- # A rootless report takes nothing; a rooted one takes the id of the
358
- # record it is a view of, under the name the model gave that reference.
359
- #
360
- # @param bluebook [Bluebook::Chapter] the report's owning chapter
361
- # @param model [Bluebook::ReadModel] the report being projected
362
- # @return [Hash{Symbol => Object}] `:verb` (`Chapter.ReportName`), `:kind`
363
- # (`:query`), `:summary` and `:arguments` (`[]`, or one required id option
364
- # named after the model's `reference_name` when it declares a `reference_target`)
245
+ # A rootless report takes nothing; a rooted one takes the id of the record it
246
+ # is a view of, under the name the model gave that reference.
365
247
  def report_spec(bluebook, model)
366
248
  arguments =
367
249
  if model.reference_target
@@ -375,15 +257,6 @@ module Hecks
375
257
  summary: model.description, arguments: arguments }
376
258
  end
377
259
 
378
- # Projects one query into the spec `verb_help` and `CliRunner` both read.
379
- #
380
- # @param bluebook [Bluebook::Chapter] the query's owning chapter
381
- # @param aggregate [Bluebook::Aggregate] the query's owning aggregate
382
- # @param entity [Bluebook::Entity, nil] the entity the query is declared on, or
383
- # `nil` for an aggregate-level query
384
- # @param query [Bluebook::Query] the query being projected
385
- # @return [Hash{Symbol => Object}] `:verb` (fully-qualified name), `:kind`
386
- # (`:query`), `:summary` and `:arguments` (see `options_for`)
387
260
  def query_spec(bluebook, aggregate, entity, query)
388
261
  arguments = Array(query.to_h[:attributes]).flat_map do |declared|
389
262
  attribute = query.attributes.find { |a| a.name.to_s == declared[:name].to_s }
@@ -391,38 +264,19 @@ module Hecks
391
264
  end
392
265
 
393
266
  { verb: fqn(bluebook, aggregate, query, entity), kind: :query,
394
- summary: query.description, arguments: arguments }
267
+ summary: query.description, arguments: arguments, returns: query.returns }
395
268
  end
396
269
 
397
- # ── one argument, flattened ───────────────────────────────────────
398
-
399
- # A value object becomes one option per field, dotted. `commit` typed
400
- # `CommitRef` is `--commit.value`, because that is the shape the runtime
401
- # wants and a flat `--commit` would have to guess which field it meant.
402
- # Single-field value objects — almost all of them — read fine either way,
403
- # and the runner accepts the short form for exactly those.
404
- # Recursive, and it has to be. A value object may hold another one —
405
- # pizzas' `Pizza` holds a `Price` and a `Size` — so stopping after one
406
- # level produced `pizza.price_cents=1500` and sent the string "1500"
407
- # where `{ cents: 1500 }` belonged.
408
- #
409
- # The runtime took it. `qa/FINDINGS.md` #2 is exactly that gap —
410
- # `Value::Coercion.build` does not validate nested value objects — so a
411
- # one-level CLI is not merely inconvenient, it is a machine for writing
412
- # malformed records into a real store, which is what it did on its first
413
- # run against the pizzas database.
270
+ # Flattens an attribute into one option per leaf field. A value object becomes
271
+ # dotted options (`--commit.value`), recursing into nested value objects so
272
+ # `{ cents: 1500 }` is never sent as the string "1500".
414
273
  #
415
274
  # @param attribute [Bluebook::Attribute] the field being projected
416
- # @param holder [Bluebook::Aggregate, Bluebook::Entity, Class] the construct
417
- # `attribute` is declared on; a `Bluebook::ValueObject` subclass on a recursive call
275
+ # @param holder [Bluebook::Aggregate, Bluebook::Entity, Class] what declares `attribute`
418
276
  # @param aggregate [Bluebook::Aggregate] the top-level aggregate, kept across recursion
419
- # so a nested value object can still be found
420
- # @param prefix [String, nil] the dotted path built so far, `nil` at the top level
421
- # @param optional [Boolean, nil] whether an enclosing field already makes this one
422
- # optional; `nil` defers to `attribute.optional?`
423
- # @return [Array<Hash{Symbol => Object}>] one option spec per leaf field, each with
424
- # `:path`, `:type`, `:required`, and optionally `:note`, `:enum`, `:pattern`,
425
- # `:default`, `:list`
277
+ # @param prefix [String, nil] the dotted path built so far
278
+ # @param optional [Boolean, nil] whether an enclosing field already makes this one optional
279
+ # @return [Array<Hash{Symbol => Object}>] one option spec per leaf field
426
280
  def options_for(attribute, holder, aggregate, prefix = nil, optional = nil)
427
281
  path = [prefix, attribute.name].compact.join(".")
428
282
  optional ||= attribute.optional?
@@ -431,19 +285,8 @@ module Hecks
431
285
  value_object = value_object_for(attribute, holder, aggregate)
432
286
  return [scalar_option(path, attribute, optional)] unless value_object
433
287
 
434
- # A list says so, all the way down to its leaves.
435
- #
436
- # Without this a `list_of(Tag)` projected exactly like a single Tag:
437
- # one option, `tags.value`, indistinguishable from a scalar. So the
438
- # help said to pass one, `CliDoor#bury` overwrote the leaf each time,
439
- # and passing two tags stored the second and lost the first without
440
- # saying anything. A missing argument is refused loudly; a forgotten
441
- # one is not, which makes it the more expensive of the two by far.
442
- #
443
- # The flag is carried on the leaf rather than kept beside the
444
- # attribute because the leaf is all `CliDoor` ever sees — it is handed
445
- # a path and a spec, and reuniting them with the attribute that
446
- # produced them would be a lookup that exists only to answer this.
288
+ # The list flag rides on each leaf: without it a repeated flag overwrote the
289
+ # leaf silently, and CliDoor only ever sees a path and a spec.
447
290
  fields = value_object.attributes.flat_map do |field|
448
291
  nested = value_object_for(field, value_object, aggregate)
449
292
  next options_for(field, value_object, aggregate, path, optional) if nested
@@ -457,59 +300,31 @@ module Hecks
457
300
  fields.map { |option| option.merge(list: true, note: [option[:note], "repeatable"].compact.join("; ")) }
458
301
  end
459
302
 
460
- # Projects a `reference_to` attribute as the id of the record it points at.
461
- #
462
- # @param attribute [Bluebook::Attribute] a `reference?` attribute
463
- # @return [Hash{Symbol => Object}] `:path`, `:type` (`"String"`), `:required` and
464
- # `:note`
465
303
  def reference_option(attribute)
466
304
  { path: attribute.name.to_s, type: "String", required: !attribute.optional?,
467
305
  note: "id of a #{attribute.type.target_name}" }
468
306
  end
469
307
 
470
- # Projects one leaf field — a plain attribute, or one field of a flattened
471
- # value object.
472
- #
473
- # @param path [String] the dotted option path this field is reached at
474
- # @param field [Bluebook::Attribute] the field being projected
475
- # @param optional [Boolean] whether this option may be omitted
476
- # @param enum [Array<Object>] the closed set of values this field admits, `[]` for
477
- # an open field
478
- # @return [Hash{Symbol => Object}] `:path`, `:type`, `:required`, plus `:enum` when
479
- # `enum` is non-empty, `:pattern` when `field` declares one, and `:default` when
480
- # `field` declares a non-nil one
308
+ # A `list_of` scalar carries `list: true, words: true`: the launcher reads a comma-separated
309
+ # value or a repeated name as the list's elements, since the runtime refuses a lone scalar.
481
310
  def scalar_option(path, field, optional, enum: [])
482
311
  option = { path: path, type: field.type.to_s, required: !optional }
312
+ option.merge!(list: true, words: true, note: "list: comma-separated or repeated") if field.list?
483
313
  option[:enum] = enum unless enum.empty?
484
314
  option[:pattern] = field.pattern if field.respond_to?(:pattern) && field.pattern
485
315
  option[:default] = field.default if field.respond_to?(:default) && !field.default.nil?
486
316
  option
487
317
  end
488
318
 
489
- # The values a `one_of` value object's field is closed to, for the option's
490
- # `:enum`.
491
- #
492
- # @param value_object [Class] a `Bluebook::ValueObject` subclass
493
- # @param field [Bluebook::Attribute] the value object's field being projected
494
- # @return [Array<Object>] the distinct values every declared member gives this
495
- # field, or `[]` when the value object declares no closed set
319
+ # The values a `one_of` value object's field is closed to, or `[]` for an open one.
496
320
  def closed_members(value_object, field)
497
321
  return [] unless value_object.closed_set?
498
322
 
499
323
  value_object.members.filter_map { |member| member[field.name] }.uniq
500
324
  end
501
325
 
502
- # Finds the value object type an attribute declares, if any — the fork between
503
- # `scalar_option` (one option) and recursing into `options_for` (one option per
504
- # field).
505
- #
506
- # @param attribute [Bluebook::Attribute] the field whose type is being resolved
507
- # @param holder [Bluebook::Aggregate, Bluebook::Entity, Class, nil] the construct
508
- # `attribute` is declared on, searched first
509
- # @param aggregate [Bluebook::Aggregate, nil] the top-level aggregate, searched when
510
- # `holder` does not declare the value object itself
511
- # @return [Class, nil] the matching `Bluebook::ValueObject` subclass, or `nil` if
512
- # `attribute`'s type names no value object in scope
326
+ # The `Bluebook::ValueObject` subclass an attribute's type names, searching
327
+ # `holder` then `aggregate`, or `nil` for a plain scalar.
513
328
  def value_object_for(attribute, holder, aggregate)
514
329
  [holder, aggregate].compact.each do |scope|
515
330
  next unless scope.respond_to?(:value_objects)
@@ -520,15 +335,8 @@ module Hecks
520
335
  nil
521
336
  end
522
337
 
523
- # Every way this verb can say no, in the chapter's own words — printed
524
- # by `--help` before the caller spends a dispatch finding out.
525
- #
526
- # @param command [Bluebook::Command] the command whose refusals are being projected
527
- # @param holder [Bluebook::Aggregate, Bluebook::Entity] the aggregate or entity the
528
- # command's lifecycle guard, if any, is checked against
529
- # @return [Array<String>] one sentence per way the command can refuse: a lifecycle
530
- # state mismatch, a missing referenced record per `reference_to` attribute, and
531
- # each declared `given`'s description, in that order
338
+ # The conditions that refuse the verb when they hold, in the chapter's own words: a
339
+ # lifecycle state mismatch and a missing referenced record per `reference_to`.
532
340
  def refusals(command, holder)
533
341
  out = []
534
342
  lifecycle = holder.lifecycle
@@ -537,28 +345,21 @@ module Hecks
537
345
  end.flatten.uniq
538
346
  out << "#{lifecycle.field} is not #{froms.join(' or ')}" if froms && !froms.empty?
539
347
  out += command.attributes.select(&:reference?).map { |r| "no #{r.type.target_name} has that #{r.name}" }
540
- out + command.givens.map(&:description)
348
+ out
541
349
  end
542
350
 
543
- # ── the help ──────────────────────────────────────────────────────
351
+ # The conditions the verb needs: each `given` states what must hold, so the verb is
352
+ # refused unless it does.
353
+ def requirements(command) = command.givens.map(&:description)
544
354
 
545
355
  # Renders the full verb/question table, or one verb's `--help` text when
546
356
  # `options[:verb]` names one.
547
- #
548
- # @param bluebook [Bluebook::Chapter] the projected chapter, for its name and vision
549
- # line
550
- # @param verbs [Hash{String => Hash}] the projected, `shorten`ed command map
551
- # @param questions [Hash{String => Hash}] the projected, `shorten`ed query map
552
- # @param options [Hash{Symbol => Object}] `:program` (String), `:verb` (String, nil)
553
- # and `:ask` (Boolean) — see `call`
554
- # @return [String] the rendered help text
555
357
  def usage(bluebook, verbs, questions, options)
556
- program = options[:program] || "bin/run"
358
+ program = options[:program] || "hecks run"
557
359
  only = options[:verb]
558
360
 
559
- # Which namespace, when both hold the name. `options[:ask]` says so;
560
- # without it a `--help` for a question would print the command that
561
- # shares its name, which banking has and which is how this was found.
361
+ # `options[:ask]` picks the namespace when both hold the name; without it a
362
+ # question's `--help` would print the command sharing its name.
562
363
  if only
563
364
  pool = options[:ask] ? questions : verbs
564
365
  key = aliases(pool)[only] || (options[:ask] ? nil : aliases(questions)[only])
@@ -582,40 +383,19 @@ module Hecks
582
383
  out.join("\n")
583
384
  end
584
385
 
585
- # Picks one verb whose full spelling differs from its short one, to show the
586
- # caller both forms exist.
587
- #
588
- # @param verbs [Hash{String => Hash}] the projected, `shorten`ed command map
589
- # @return [String] `"<short> is also <full>"`, or `""` if every verb's short form
590
- # is its full name (a one-aggregate domain, where no name ever collides)
386
+ # One verb whose full spelling differs from its short one, or `""` when none does.
591
387
  def example_qualified(verbs)
592
388
  name, spec = verbs.find { |key, value| key != value[:short] } || verbs.first
593
389
  name ? "#{spec[:short]} is also #{name}" : ""
594
390
  end
595
391
 
596
- # A query's `description` is written as a paragraph — it argues for why
597
- # the list is worth reading. A verb table wants the first sentence of
598
- # that argument; `--help` still prints the whole thing.
599
- #
600
- # @param text [String, nil] the full description, or `nil`
601
- # @return [String] the text up to and including its first `.`, or `""` for `nil`
392
+ # The verb table wants a query description's first sentence; `--help` prints all.
602
393
  def first_sentence(text)
603
394
  text.to_s.split(/(?<=\.)\s/).first.to_s
604
395
  end
605
396
 
606
- # Four text blocks, in fixed display order — meta (name/kind/role),
607
- # invocation, arguments, refusals. Each block is independent of the
608
- # others' content (only the output order is fixed, and stays fixed
609
- # below), so each is its own method returning the lines it
610
- # contributes — `[]` when it contributes none — concatenated in the
611
- # same order the original inline version built them in.
612
- #
613
- # @param program [String] how the caller was invoked, echoed in the invocation line
614
- # @param name [String] the verb's short display name
615
- # @param spec [Hash{Symbol => Object}] the verb or question's projected spec (see
616
- # `command_spec`, `port_spec`, `report_spec` or `query_spec`)
617
- # @param ask [Boolean] whether this is a question, reached through `ask`
618
- # @return [String] the verb's full `--help` text
397
+ # Four independent blocks, concatenated in fixed order: meta, invocation,
398
+ # arguments, refusals.
619
399
  def verb_help(program, name, spec, ask: false)
620
400
  out = verb_help_meta_lines(name, spec)
621
401
  out.concat(verb_help_invocation_lines(program, name, spec, ask))
@@ -624,12 +404,6 @@ module Hecks
624
404
  out.join("\n")
625
405
  end
626
406
 
627
- # Renders the name/summary heading, plus the dispatched or read verb and issuing
628
- # role when the spec declares them.
629
- #
630
- # @param name [String] the verb's short display name
631
- # @param spec [Hash{Symbol => Object}] the verb or question's projected spec
632
- # @return [Array<String>] the meta block's lines
633
407
  def verb_help_meta_lines(name, spec)
634
408
  out = ["#{name} — #{spec[:summary]}", ""]
635
409
  out << "dispatches #{spec[:verb]}" if spec[:kind] == :command
@@ -638,26 +412,11 @@ module Hecks
638
412
  out
639
413
  end
640
414
 
641
- # Renders the one-line example invocation, with every argument's path as a
642
- # placeholder.
643
- #
644
- # @param program [String] how the caller was invoked
645
- # @param name [String] the verb's short display name
646
- # @param spec [Hash{Symbol => Object}] the verb or question's projected spec, read
647
- # for `:arguments`
648
- # @param ask [Boolean] whether to show the `ask` form of the invocation
649
- # @return [Array<String>] the invocation block's lines
650
415
  def verb_help_invocation_lines(program, name, spec, ask)
651
416
  invocation = ask ? "#{program} ask #{name}" : "#{program} #{name}"
652
417
  ["", " #{invocation}#{spec[:arguments].map { |a| " #{a[:path]}=…" }.join}", ""]
653
418
  end
654
419
 
655
- # Renders one line per declared argument: its type, admitted values, pattern,
656
- # default and whether it is optional.
657
- #
658
- # @param spec [Hash{Symbol => Object}] the verb or question's projected spec, read
659
- # for `:arguments`
660
- # @return [Array<String>] the argument block's lines, `[]` if the verb takes none
661
420
  def verb_help_argument_lines(spec)
662
421
  return [] if spec[:arguments].empty?
663
422
 
@@ -675,15 +434,10 @@ module Hecks
675
434
  lines << ""
676
435
  end
677
436
 
678
- # Renders the "refused when:" block, one line per way the verb can say no.
679
- #
680
- # @param spec [Hash{Symbol => Object}] the verb or question's projected spec, read
681
- # for `:refusals`
682
- # @return [Array<String>] the refusal block's lines, `[]` if the verb declares none
683
437
  def verb_help_refusal_lines(spec)
684
- return [] if Array(spec[:refusals]).empty?
685
-
686
- ["refused when:", *spec[:refusals].map { |refusal| " #{refusal}" }, ""]
438
+ [["refused when:", spec[:refusals]], ["refused unless:", spec[:requirements]]].flat_map do |heading, items|
439
+ Array(items).empty? ? [] : [heading, *items.map { |item| " #{item}" }, ""]
440
+ end
687
441
  end
688
442
  end
689
443
  end