hecks 3.9.1 → 3.11.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 (504) hide show
  1. checksums.yaml +4 -4
  2. data/examples/pizzas/bluebook/pizzas.bluebook +29 -0
  3. data/examples/pizzas/bluebook/pizzas.hecksagon +7 -0
  4. data/exe/hecks +4 -4
  5. data/lib/hecks/adapters/driven/d1.rb +2 -10
  6. data/lib/hecks/adapters/driven/heki.adapter +1 -1
  7. data/lib/hecks/adapters/driven/heki.rb +2 -12
  8. data/lib/hecks/adapters/driven/lambda.adapter +1 -1
  9. data/lib/hecks/adapters/driven/lambda.rb +3 -18
  10. data/lib/hecks/adapters/driven/postgres.rb +4 -11
  11. data/lib/hecks/adapters/driven/sqlite.adapter +1 -1
  12. data/lib/hecks/adapters/driven/sqlite.rb +2 -10
  13. data/lib/hecks/adapters/driving/admission.rb +46 -0
  14. data/lib/hecks/adapters/driving/cli/nesting.rb +36 -0
  15. data/lib/hecks/adapters/driving/cli/shorthand.rb +88 -0
  16. data/lib/hecks/adapters/driving/cli.rb +127 -0
  17. data/lib/hecks/adapters/driving/cli_runner/answers.rb +160 -0
  18. data/lib/hecks/adapters/driving/cli_runner/resolution.rb +155 -0
  19. data/lib/hecks/adapters/driving/cli_runner/settling.rb +120 -0
  20. data/lib/hecks/adapters/driving/cli_runner/streaming.rb +93 -0
  21. data/lib/hecks/adapters/driving/cli_runner/suggestions.rb +47 -0
  22. data/lib/hecks/adapters/driving/cli_runner.rb +69 -0
  23. data/lib/hecks/adapters/driving/command_request.rb +121 -0
  24. data/lib/hecks/adapters/driving/handle.rb +214 -0
  25. data/lib/hecks/adapters/driving/json.rb +136 -0
  26. data/lib/hecks/adapters/driving/launcher_options.rb +267 -0
  27. data/lib/hecks/adapters/driving/mcp/properties.rb +49 -0
  28. data/lib/hecks/adapters/driving/mcp/tool_calls.rb +116 -0
  29. data/lib/hecks/adapters/driving/mcp/tool_definitions/auditing.rb +53 -0
  30. data/lib/hecks/adapters/driving/mcp/tool_definitions/calling.rb +71 -0
  31. data/lib/hecks/adapters/driving/mcp/tool_definitions/reading.rb +99 -0
  32. data/lib/hecks/adapters/driving/mcp/tool_definitions.rb +18 -0
  33. data/lib/hecks/adapters/driving/mcp.rb +159 -0
  34. data/lib/hecks/adapters/driving/mcp_guide_cache.rb +78 -0
  35. data/lib/hecks/adapters/driving/mcp_scope/argument_policy.rb +97 -0
  36. data/lib/hecks/adapters/driving/mcp_scope/env_problems.rb +69 -0
  37. data/lib/hecks/adapters/driving/mcp_scope/presenter.rb +83 -0
  38. data/lib/hecks/adapters/driving/mcp_scope.rb +213 -0
  39. data/lib/hecks/adapters/driving/ruby/aggregate_module.rb +171 -0
  40. data/lib/hecks/adapters/driving/ruby/chapter.rb +91 -0
  41. data/lib/hecks/adapters/driving/ruby.rb +83 -0
  42. data/lib/hecks/adapters/driving/usage_cache.rb +144 -0
  43. data/lib/hecks/{doors.rb → adapters/driving.rb} +10 -6
  44. data/lib/hecks/behaviors/expectations/boot.rb +1 -1
  45. data/lib/hecks/bench/ruby_runner.rb +1 -1
  46. data/lib/hecks/bluebook/ask_resolution.rb +130 -0
  47. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +8 -2
  48. data/lib/hecks/bluebook/assembly/contracts/rules.rb +4 -2
  49. data/lib/hecks/bluebook/assembly/contracts/structure.rb +3 -1
  50. data/lib/hecks/bluebook/assembly/marks.rb +1 -0
  51. data/lib/hecks/bluebook/attribute.rb +1 -1
  52. data/lib/hecks/bluebook/behaviour/command.rb +13 -8
  53. data/lib/hecks/bluebook/behaviour/policy.rb +30 -0
  54. data/lib/hecks/bluebook/capabilities/attribute_values.rb +72 -0
  55. data/lib/hecks/bluebook/capabilities.rb +91 -9
  56. data/lib/hecks/bluebook/domain_port.rb +16 -4
  57. data/lib/hecks/bluebook/dsl/adapter_builder.rb +18 -6
  58. data/lib/hecks/bluebook/dsl/binding_proxy.rb +13 -0
  59. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/computed_sources.rb +98 -0
  60. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/fan_out_rows.rb +42 -0
  61. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/projected_fields.rb +14 -2
  62. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/provisions.rb +69 -3
  63. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/with_projections.rb +17 -10
  64. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +5 -0
  65. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -0
  66. data/lib/hecks/bluebook/dsl/command_builder/mutations.rb +8 -1
  67. data/lib/hecks/bluebook/dsl/command_builder/operands.rb +121 -0
  68. data/lib/hecks/bluebook/dsl/command_builder.rb +2 -0
  69. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +10 -1
  70. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +45 -2
  71. data/lib/hecks/bluebook/dsl/policy_builder.rb +47 -10
  72. data/lib/hecks/bluebook/dsl/rule_reference.rb +7 -4
  73. data/lib/hecks/bluebook/dsl/world_builder.rb +1 -1
  74. data/lib/hecks/bluebook/expression/ast_json/emitters.rb +6 -0
  75. data/lib/hecks/bluebook/expression/ast_json.rb +9 -6
  76. data/lib/hecks/bluebook/expression/ast_reader.rb +14 -9
  77. data/lib/hecks/bluebook/expression/projection.json +36 -0
  78. data/lib/hecks/bluebook/expression/resolver/operations.rb +31 -0
  79. data/lib/hecks/bluebook/expression/resolver/parsing.rb +38 -5
  80. data/lib/hecks/bluebook/expression/resolver/scanning.rb +22 -5
  81. data/lib/hecks/bluebook/expression/resolver/values.rb +10 -0
  82. data/lib/hecks/bluebook/expression/resolver.rb +20 -1
  83. data/lib/hecks/bluebook/hecksagon.rb +23 -3
  84. data/lib/hecks/bluebook/lifecycle.rb +22 -3
  85. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +13 -1
  86. data/lib/hecks/bluebook/meta_validator/modes.rb +4 -1
  87. data/lib/hecks/bluebook/meta_validator/readings.rb +18 -1
  88. data/lib/hecks/bluebook/meta_validator/reconstruction/heads.rb +2 -1
  89. data/lib/hecks/bluebook/meta_validator/shapes.rb +5 -0
  90. data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +1 -1
  91. data/lib/hecks/bluebook/meta_validator.rb +12 -12
  92. data/lib/hecks/bluebook/model_check/ask_checks.rb +71 -0
  93. data/lib/hecks/bluebook/model_check/policy_checks.rb +2 -1
  94. data/lib/hecks/bluebook/model_check/reference_checks.rb +70 -0
  95. data/lib/hecks/bluebook/model_check.rb +8 -2
  96. data/lib/hecks/bluebook/policy.rb +4 -2
  97. data/lib/hecks/bluebook/smoke_test.rb +1 -1
  98. data/lib/hecks/bluebook/synthesizer.rb +2 -0
  99. data/lib/hecks/bluebook.rb +1 -0
  100. data/lib/hecks/cli/command_table.rb +1 -1
  101. data/lib/hecks/cli/console.rb +2 -1
  102. data/lib/hecks/cli/document.rb +1 -1
  103. data/lib/hecks/cli/launcher_words.rb +4 -4
  104. data/lib/hecks/cli/mcp.rb +4 -4
  105. data/lib/hecks/cli/project_cli/launcher_source.rb +2 -2
  106. data/lib/hecks/cli/project_cli/templates.rb +5 -5
  107. data/lib/hecks/cli/project_cli.rb +9 -6
  108. data/lib/hecks/cli/run.rb +2 -2
  109. data/lib/hecks/cli/smoke_http/checks.rb +4 -1
  110. data/lib/hecks/cli/smoke_http/transport.rb +3 -3
  111. data/lib/hecks/cli/smoke_http.rb +3 -0
  112. data/lib/hecks/codemod.rb +5 -2
  113. data/lib/hecks/corpus/routes.rb +7 -0
  114. data/lib/hecks/corpus/rust.rb +3 -2
  115. data/lib/hecks/deploy/bluebook/deploy.bluebook +124 -2
  116. data/lib/hecks/deploy/bluebook/deploy.hecksagon +8 -0
  117. data/lib/hecks/deploy/oidc.json +16 -3
  118. data/lib/hecks/doc/reference/rendering.rb +6 -3
  119. data/lib/hecks/forms/app/commands.rb +8 -7
  120. data/lib/hecks/forms/app/queries.rb +2 -1
  121. data/lib/hecks/forms/app.rb +1 -1
  122. data/lib/hecks/forms/record_renderer.rb +1 -1
  123. data/lib/hecks/framework/bluebook/privacy.bluebook +11 -0
  124. data/lib/hecks/fuzzing/concurrent_dispatch.rb +4 -1
  125. data/lib/hecks/fuzzing/failure_triage.rb +2 -1
  126. data/lib/hecks/fuzzing/mutation/run.rb +11 -4
  127. data/lib/hecks/fuzzing/properties/catalog.rb +6 -4
  128. data/lib/hecks/fuzzing/properties/guaranteed_by_construction.rb +18 -11
  129. data/lib/hecks/fuzzing/properties/guards.rb +1 -1
  130. data/lib/hecks/fuzzing/self_consistency/saga_redelivery.rb +1 -1
  131. data/lib/hecks/fuzzing/self_consistency.rb +6 -2
  132. data/lib/hecks/fuzzing/shrinker.rb +3 -1
  133. data/lib/hecks/fuzzing/value_generator.rb +19 -5
  134. data/lib/hecks/gate/stages.yml +4 -10
  135. data/lib/hecks/grammar/evolve.rb +1 -1
  136. data/lib/hecks/grammar/expression_operators.json +372 -0
  137. data/lib/hecks/grammar/operators.rb +3 -1
  138. data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +10 -10
  139. data/lib/hecks/hecks/adapters/codebase/inspection/rubycritic.rb +35 -0
  140. data/lib/hecks/hecks/adapters/codebase/inspection.rb +104 -0
  141. data/lib/hecks/hecks/adapters/codebase/publishing.rb +2 -1
  142. data/lib/hecks/hecks/adapters/codebase/release_facts.rb +12 -2
  143. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +2 -1
  144. data/lib/hecks/hecks/adapters/codebase/test_runner.rb +1 -1
  145. data/lib/hecks/hecks/adapters/cost_explorer.rb +8 -5
  146. data/lib/hecks/hecks/adapters/deploy_toolchain/clearing.rb +51 -0
  147. data/lib/hecks/hecks/adapters/deploy_toolchain/statuses.rb +2 -1
  148. data/lib/hecks/hecks/adapters/deploy_toolchain.rb +2 -0
  149. data/lib/hecks/hecks/adapters/finding_github.rb +13 -6
  150. data/lib/hecks/hecks/adapters/first_admin.rb +2 -2
  151. data/lib/hecks/hecks/adapters/github_rulesets.rb +6 -3
  152. data/lib/hecks/hecks/adapters/in_process_boot.rb +1 -1
  153. data/lib/hecks/hecks/adapters/journal_store/compaction.rb +1 -1
  154. data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +10 -20
  155. data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +28 -5
  156. data/lib/hecks/hecks/adapters/local_files.rb +2 -2
  157. data/lib/hecks/hecks/adapters/rehearsal/audit_report.rb +107 -0
  158. data/lib/hecks/hecks/adapters/rehearsal/census.rb +71 -0
  159. data/lib/hecks/hecks/adapters/rehearsal/declaration.rb +133 -0
  160. data/lib/hecks/hecks/adapters/rehearsal/environment.rb +83 -0
  161. data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +142 -0
  162. data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +96 -0
  163. data/lib/hecks/hecks/adapters/rehearsal/postgres_census.rb +100 -0
  164. data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +69 -0
  165. data/lib/hecks/hecks/adapters/rehearsal/postgres_store.rb +129 -0
  166. data/lib/hecks/hecks/adapters/rehearsal/rows.rb +41 -0
  167. data/lib/hecks/hecks/adapters/rehearsal/rust_audit.rb +83 -0
  168. data/lib/hecks/hecks/adapters/rehearsal/subject.rb +104 -0
  169. data/lib/hecks/hecks/adapters/rehearsal.rb +54 -0
  170. data/lib/hecks/hecks/adapters/rehearsal_store.adapter +7 -0
  171. data/lib/hecks/hecks/adapters/rehearsal_store.rb +81 -0
  172. data/lib/hecks/hecks/adapters/site_toolchain/role_prober.rb +78 -0
  173. data/lib/hecks/hecks/adapters/site_toolchain.rb +29 -4
  174. data/lib/hecks/hecks/adapters/terminal.rb +13 -11
  175. data/lib/hecks/hecks/adapters/world_clearing.rb +65 -0
  176. data/lib/hecks/hecks/codebase.behaviors +13 -1
  177. data/lib/hecks/hecks/codebase.bluebook +239 -5
  178. data/lib/hecks/hecks/custodian.behaviors +24 -6
  179. data/lib/hecks/hecks/custodian.bluebook +6 -1227
  180. data/lib/hecks/hecks/deploy.behaviors +1 -1
  181. data/lib/hecks/hecks/deploy.world +1 -3
  182. data/lib/hecks/hecks/hecks.behaviors +1 -1
  183. data/lib/hecks/hecks/hecks.hecksagon +61 -18
  184. data/lib/hecks/hecks/hecks.world +11 -10
  185. data/lib/hecks/hecks/rehearsal.bluebook +477 -0
  186. data/lib/hecks/hecks/site.behaviors +1 -1
  187. data/lib/hecks/hecks/site.world +0 -1
  188. data/lib/hecks/hecks/tooling.bluebook +1238 -0
  189. data/lib/hecks/indifferent_key.rb +20 -0
  190. data/lib/hecks/language/adapter.bluebook +13 -3
  191. data/lib/hecks/language/bluebook/aggregate.bluebook +23 -0
  192. data/lib/hecks/language/bluebook/bluebook.bluebook +15 -0
  193. data/lib/hecks/language/bluebook/entity.bluebook +18 -0
  194. data/lib/hecks/language/bluebook/policy.bluebook +9 -1
  195. data/lib/hecks/language/bluebook/query.bluebook +1 -1
  196. data/lib/hecks/language/bluebook/vocabulary.bluebook +11 -4
  197. data/lib/hecks/language/hecksagon/domain_port.bluebook +1 -1
  198. data/lib/hecks/language/hecksagon/hecksagon.bluebook +22 -0
  199. data/lib/hecks/language/oidc.json +10 -0
  200. data/lib/hecks/language/port.bluebook +4 -4
  201. data/lib/hecks/literal.rb +12 -3
  202. data/lib/hecks/ports/key_vault.rb +7 -2
  203. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +21 -10
  204. data/lib/hecks/ports/persistence/plugins/era/postgres_era/boot.rb +4 -2
  205. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +2 -1
  206. data/lib/hecks/ports/persistence/repository_factory.rb +6 -3
  207. data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +29 -0
  208. data/lib/hecks/projections/deploy/box/templates/deploy-service.sh.tmpl +1 -1
  209. data/lib/hecks/projections/deploy/fargate/settings/tuning.rb +15 -0
  210. data/lib/hecks/projections/deploy/fargate/settings.rb +3 -1
  211. data/lib/hecks/projections/deploy/fargate/stack/readers.rb +1 -0
  212. data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
  213. data/lib/hecks/projections/deploy/shared.rb +5 -2
  214. data/lib/hecks/projections/deploy/templates/fargate/Makefile.tmpl +7 -4
  215. data/lib/hecks/projections/model/deviations.rb +2 -2
  216. data/lib/hecks/projections/model.rb +2 -2
  217. data/lib/hecks/projections/oidc.rb +13 -1
  218. data/lib/hecks/projections/shape.rb +5 -2
  219. data/lib/hecks/projections/site/cms_editor/attributes.rb +95 -0
  220. data/lib/hecks/projections/site/cms_editor/blocks.rb +157 -0
  221. data/lib/hecks/projections/site/cms_editor/checks.rb +77 -0
  222. data/lib/hecks/projections/site/cms_editor/clearing.rb +70 -0
  223. data/lib/hecks/projections/site/cms_editor/color.rb +124 -0
  224. data/lib/hecks/projections/site/cms_editor/composition.rb +42 -0
  225. data/lib/hecks/projections/site/cms_editor/destructive.rb +61 -0
  226. data/lib/hecks/projections/site/cms_editor/drafts.rb +102 -0
  227. data/lib/hecks/projections/site/cms_editor/hints.rb +74 -0
  228. data/lib/hecks/projections/site/cms_editor/icons.rb +86 -0
  229. data/lib/hecks/projections/site/cms_editor/key_kinds.rb +31 -0
  230. data/lib/hecks/projections/site/cms_editor/limits.rb +72 -0
  231. data/lib/hecks/projections/site/cms_editor/media.rb +85 -0
  232. data/lib/hecks/projections/site/cms_editor/moment.rb +34 -0
  233. data/lib/hecks/projections/site/cms_editor/palette.rb +115 -0
  234. data/lib/hecks/projections/site/cms_editor/pickers.rb +85 -0
  235. data/lib/hecks/projections/site/cms_editor/pretty.rb +40 -0
  236. data/lib/hecks/projections/site/cms_editor/preview.rb +91 -0
  237. data/lib/hecks/projections/site/cms_editor/relations.rb +92 -0
  238. data/lib/hecks/projections/site/cms_editor/scale_checks.rb +66 -0
  239. data/lib/hecks/projections/site/cms_editor/scheduling.rb +150 -0
  240. data/lib/hecks/projections/site/cms_editor/schema.rb +161 -0
  241. data/lib/hecks/projections/site/cms_editor/setting.rb +130 -0
  242. data/lib/hecks/projections/site/cms_editor/targets.rb +86 -0
  243. data/lib/hecks/projections/site/cms_editor/templates/package.json.tmpl +36 -0
  244. data/lib/hecks/projections/site/cms_editor/templates/src/app.ts.tmpl +370 -0
  245. data/lib/hecks/projections/site/cms_editor/templates/src/assets.ts.tmpl +98 -0
  246. data/lib/hecks/projections/site/cms_editor/templates/src/auth/membership.ts.tmpl +87 -0
  247. data/lib/hecks/projections/site/cms_editor/templates/src/auth/session.ts.tmpl +36 -0
  248. data/lib/hecks/projections/site/cms_editor/templates/src/auth/sso.ts.tmpl +20 -0
  249. data/lib/hecks/projections/site/cms_editor/templates/src/browser/app.css.tmpl +412 -0
  250. data/lib/hecks/projections/site/cms_editor/templates/src/browser/autosave.js.tmpl +144 -0
  251. data/lib/hecks/projections/site/cms_editor/templates/src/browser/blocks.js.tmpl +500 -0
  252. data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_doc.js.tmpl +175 -0
  253. data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_editor.js.tmpl +418 -0
  254. data/lib/hecks/projections/site/cms_editor/templates/src/browser/epoch.js.tmpl +61 -0
  255. data/lib/hecks/projections/site/cms_editor/templates/src/browser/forms.js.tmpl +210 -0
  256. data/lib/hecks/projections/site/cms_editor/templates/src/browser/lists.js.tmpl +40 -0
  257. data/lib/hecks/projections/site/cms_editor/templates/src/browser/main.js.tmpl +26 -0
  258. data/lib/hecks/projections/site/cms_editor/templates/src/browser/media_picker.js.tmpl +232 -0
  259. data/lib/hecks/projections/site/cms_editor/templates/src/browser/moments.js.tmpl +73 -0
  260. data/lib/hecks/projections/site/cms_editor/templates/src/browser/pickers.js.tmpl +152 -0
  261. data/lib/hecks/projections/site/cms_editor/templates/src/browser/preview.js.tmpl +98 -0
  262. data/lib/hecks/projections/site/cms_editor/templates/src/browser/shell.js.tmpl +140 -0
  263. data/lib/hecks/projections/site/cms_editor/templates/src/browser/toast.js.tmpl +66 -0
  264. data/lib/hecks/projections/site/cms_editor/templates/src/choices.ts.tmpl +99 -0
  265. data/lib/hecks/projections/site/cms_editor/templates/src/commands.ts.tmpl +133 -0
  266. data/lib/hecks/projections/site/cms_editor/templates/src/config.ts.tmpl +29 -0
  267. data/lib/hecks/projections/site/cms_editor/templates/src/flash.ts.tmpl +52 -0
  268. data/lib/hecks/projections/site/cms_editor/templates/src/host.ts.tmpl +43 -0
  269. data/lib/hecks/projections/site/cms_editor/templates/src/media/handler.ts.tmpl +177 -0
  270. data/lib/hecks/projections/site/cms_editor/templates/src/media/multipart.ts.tmpl +53 -0
  271. data/lib/hecks/projections/site/cms_editor/templates/src/media/sniff.ts.tmpl +94 -0
  272. data/lib/hecks/projections/site/cms_editor/templates/src/media/storage.ts.tmpl +50 -0
  273. data/lib/hecks/projections/site/cms_editor/templates/src/names.ts.tmpl +36 -0
  274. data/lib/hecks/projections/site/cms_editor/templates/src/schema.ts.tmpl +214 -0
  275. data/lib/hecks/projections/site/cms_editor/templates/src/server.ts.tmpl +65 -0
  276. data/lib/hecks/projections/site/cms_editor/templates/src/ui/availability.ts.tmpl +15 -0
  277. data/lib/hecks/projections/site/cms_editor/templates/src/ui/block_list.js.tmpl +213 -0
  278. data/lib/hecks/projections/site/cms_editor/templates/src/ui/blocks.ts.tmpl +275 -0
  279. data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_model.js.tmpl +123 -0
  280. data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_parse.js.tmpl +320 -0
  281. data/lib/hecks/projections/site/cms_editor/templates/src/ui/detail.ts.tmpl +88 -0
  282. data/lib/hecks/projections/site/cms_editor/templates/src/ui/dialogs.ts.tmpl +43 -0
  283. data/lib/hecks/projections/site/cms_editor/templates/src/ui/fields.ts.tmpl +275 -0
  284. data/lib/hecks/projections/site/cms_editor/templates/src/ui/html.ts.tmpl +61 -0
  285. data/lib/hecks/projections/site/cms_editor/templates/src/ui/icons.ts.tmpl +15 -0
  286. data/lib/hecks/projections/site/cms_editor/templates/src/ui/input.ts.tmpl +92 -0
  287. data/lib/hecks/projections/site/cms_editor/templates/src/ui/links.ts.tmpl +100 -0
  288. data/lib/hecks/projections/site/cms_editor/templates/src/ui/moments.ts.tmpl +44 -0
  289. data/lib/hecks/projections/site/cms_editor/templates/src/ui/outline.ts.tmpl +63 -0
  290. data/lib/hecks/projections/site/cms_editor/templates/src/ui/pages.ts.tmpl +213 -0
  291. data/lib/hecks/projections/site/cms_editor/templates/src/ui/paging.ts.tmpl +82 -0
  292. data/lib/hecks/projections/site/cms_editor/templates/src/ui/pickers.ts.tmpl +110 -0
  293. data/lib/hecks/projections/site/cms_editor/templates/src/ui/preview.ts.tmpl +87 -0
  294. data/lib/hecks/projections/site/cms_editor/templates/src/ui/related.ts.tmpl +97 -0
  295. data/lib/hecks/projections/site/cms_editor/templates/src/ui/schedule.ts.tmpl +160 -0
  296. data/lib/hecks/projections/site/cms_editor/templates/src/ui/shell.ts.tmpl +211 -0
  297. data/lib/hecks/projections/site/cms_editor/templates/src/ui/table.ts.tmpl +151 -0
  298. data/lib/hecks/projections/site/cms_editor/templates/src/ui/theme.js.tmpl +16 -0
  299. data/lib/hecks/projections/site/cms_editor/templates/src/ui/values.ts.tmpl +69 -0
  300. data/lib/hecks/projections/site/cms_editor/templates/src/ui/words.ts.tmpl +97 -0
  301. data/lib/hecks/projections/site/cms_editor/templates/src/ui/world.ts.tmpl +35 -0
  302. data/lib/hecks/projections/site/cms_editor/templates/tsconfig.json.tmpl +17 -0
  303. data/lib/hecks/projections/site/cms_editor/theme.rb +29 -0
  304. data/lib/hecks/projections/site/cms_editor/theme_checks.rb +57 -0
  305. data/lib/hecks/projections/site/cms_editor.rb +133 -0
  306. data/lib/hecks/projections/site/role_probe.rb +74 -0
  307. data/lib/hecks/projector/cli_audience.rb +2 -1
  308. data/lib/hecks/projector/cli_projector/listing.rb +1 -1
  309. data/lib/hecks/projector/cli_projector/option_specs.rb +1 -1
  310. data/lib/hecks/projector/docs_projector/closing.rb +1 -1
  311. data/lib/hecks/projector/docs_projector.rb +1 -1
  312. data/lib/hecks/projector/exporter/bindings.rb +40 -10
  313. data/lib/hecks/projector/exporter/capability_values.rb +55 -0
  314. data/lib/hecks/projector/narrate_projector/reactions.rb +1 -1
  315. data/lib/hecks/projector/target.rb +11 -0
  316. data/lib/hecks/projector.rb +32 -5
  317. data/lib/hecks/quality_control/adapters/github_checks.rb +12 -8
  318. data/lib/hecks/quality_control/adapters/github_issues.rb +8 -4
  319. data/lib/hecks/quality_control/cli/qa_open_pr.rb +2 -2
  320. data/lib/hecks/quality_control/cli/qa_sweep/checks/shrinking.rb +1 -1
  321. data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +1 -1
  322. data/lib/hecks/quality_control/cli/qa_sweep.rb +1 -1
  323. data/lib/hecks/quality_control/quality_control.bluebook +6 -2
  324. data/lib/hecks/release/approval.rb +64 -0
  325. data/lib/hecks/router/{door.rb → aggregate_reader.rb} +5 -4
  326. data/lib/hecks/router/namespace_installer.rb +16 -16
  327. data/lib/hecks/router.rb +1 -1
  328. data/lib/hecks/runtime/best_effort.rb +22 -0
  329. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +12 -2
  330. data/lib/hecks/runtime/command_interpreter.rb +3 -2
  331. data/lib/hecks/runtime/command_rules/emission.rb +1 -1
  332. data/lib/hecks/runtime/dispatcher.rb +4 -4
  333. data/lib/hecks/runtime/entity_element/location.rb +1 -1
  334. data/lib/hecks/runtime/event.rb +5 -0
  335. data/lib/hecks/runtime/identity.rb +7 -6
  336. data/lib/hecks/runtime/interpreting.rb +2 -1
  337. data/lib/hecks/runtime/invocation_builder.rb +1 -1
  338. data/lib/hecks/runtime/loader/boot_steps.rb +8 -5
  339. data/lib/hecks/runtime/loader.rb +10 -10
  340. data/lib/hecks/runtime/policy_interpreter/arguments.rb +7 -4
  341. data/lib/hecks/runtime/policy_interpreter/fan_out.rb +3 -3
  342. data/lib/hecks/runtime/policy_interpreter.rb +6 -6
  343. data/lib/hecks/runtime/port_operation_interpreter.rb +1 -1
  344. data/lib/hecks/runtime/reaction_invocation.rb +1 -1
  345. data/lib/hecks/runtime/rebuild_sweep.rb +11 -1
  346. data/lib/hecks/runtime/registry/ask_verification.rb +35 -0
  347. data/lib/hecks/runtime/registry/hecksagon_rules.rb +10 -0
  348. data/lib/hecks/runtime/registry/merging.rb +1 -1
  349. data/lib/hecks/runtime/registry/providers.rb +16 -0
  350. data/lib/hecks/runtime/registry/read_repository.rb +6 -4
  351. data/lib/hecks/runtime/registry/verification.rb +4 -1
  352. data/lib/hecks/runtime/registry/world_defaults.rb +29 -0
  353. data/lib/hecks/runtime/registry.rb +1 -1
  354. data/lib/hecks/runtime/saga_interpreter/compensation.rb +1 -1
  355. data/lib/hecks/runtime/saga_interpreter/delivery.rb +4 -4
  356. data/lib/hecks/runtime/saga_interpreter/transitions.rb +1 -1
  357. data/lib/hecks/runtime/saga_interpreter.rb +3 -3
  358. data/lib/hecks/runtime/tenant_check.rb +5 -4
  359. data/lib/hecks/runtime/value/coercion.rb +2 -2
  360. data/lib/hecks/runtime/value/validation.rb +16 -4
  361. data/lib/hecks/runtime.rb +9 -9
  362. data/lib/hecks/rust_build/project_rust/target_ir.rb +1 -1
  363. data/lib/hecks/rust_build/wasm.rb +1 -1
  364. data/lib/hecks/rust_build/wasm_browser.rb +1 -1
  365. data/lib/hecks/rust_build.rb +11 -0
  366. data/lib/hecks/served_options.rb +53 -0
  367. data/lib/hecks/site/bluebook/site.bluebook +29 -1
  368. data/lib/hecks/site/bluebook/site.ports.hecksagon +6 -0
  369. data/lib/hecks/storehouse/audit.rb +1 -1
  370. data/lib/hecks/storehouse/dispatching.rb +8 -6
  371. data/lib/hecks/storehouse/introspection.rb +2 -2
  372. data/lib/hecks/storehouse/querying.rb +6 -4
  373. data/lib/hecks/storehouse.rb +4 -4
  374. data/lib/hecks/tools/comment_style/source_file.rb +4 -1
  375. data/lib/hecks/tools/deploy_recipe_lint/fixtures.rb +4 -1
  376. data/lib/hecks/tools/lanes/rows.rb +6 -0
  377. data/lib/hecks/tools/oidc_manifests.rb +4 -5
  378. data/lib/hecks/tools/regeneration_run.rb +11 -4
  379. data/lib/hecks/tools/site_routes/cli.rb +1 -1
  380. data/lib/hecks/tools/site_routes/editor_files.rb +45 -0
  381. data/lib/hecks/tools/site_routes.rb +14 -7
  382. data/lib/hecks/tools/tenant_provisioning.rb +2 -2
  383. data/lib/hecks/tools/tools_doc.rb +4 -4
  384. data/lib/hecks/version.rb +1 -1
  385. data/lib/hecks/vocabulary.rb +4 -4
  386. data/lib/hecks.rb +15 -15
  387. data/lib/rubocop/cop/hecks/env_dump.rb +69 -0
  388. data/lib/rubocop/cop/hecks/monotonic_duration.rb +53 -0
  389. data/lib/rubocop/cop/hecks/no_wall_clock_in_runtime.rb +34 -0
  390. data/lib/rubocop/cop/hecks/raw_shell_interpolation.rb +79 -0
  391. data/lib/rubocop/cop/hecks/sleep_in_spec.rb +34 -0
  392. data/lib/rubocop/cop/hecks/swallowed_rescue.rb +37 -0
  393. data/lib/rubocop/cop/hecks/symbol_string_key_mix.rb +50 -0
  394. data/lib/rubocop/cop/hecks/untyped_raise.rb +37 -0
  395. data/prebuilt/05c358d38d661a3774f813d49c66f5b68939af94432d66257a56f49ee2598994.marshal +0 -0
  396. data/prebuilt/26c40c61f09b09b11651c64668eda3ceb180f83cf2a3802d3033d2fd99ac841b.marshal +0 -0
  397. data/prebuilt/verdicts-230ac18c25abfd3d92ede032bee2661a4c2cd2773ab0cf0e2ae52546ba2f7af9.json +1 -0
  398. data/rust/.gitignore +6 -0
  399. data/rust/Cargo.toml +9 -1
  400. data/rust/build/src/binding_pass.rs +103 -0
  401. data/rust/build/src/cargo_sync.rs +6 -0
  402. data/rust/build/src/lineage_pass.rs +3 -3
  403. data/rust/build/src/main.rs +17 -5
  404. data/rust/build/src/pipeline.rs +48 -5
  405. data/rust/build/src/seams_pass.rs +123 -0
  406. data/rust/build/src/translation_pass.rs +167 -0
  407. data/rust/codegen/src/asks.rs +103 -0
  408. data/rust/codegen/src/bridging.rs +1 -1
  409. data/rust/codegen/src/commands.rs +13 -8
  410. data/rust/codegen/src/constraints.rs +25 -0
  411. data/rust/codegen/src/domain_generator.rs +11 -9
  412. data/rust/codegen/src/expr_emitter.rs +13 -0
  413. data/rust/codegen/src/json_codec.rs +54 -15
  414. data/rust/codegen/src/main.rs +2 -1
  415. data/rust/codegen/src/mutations.rs +174 -4
  416. data/rust/codegen/src/naming.rs +30 -0
  417. data/rust/codegen/src/queries.rs +11 -2
  418. data/rust/codegen/src/reactions.rs +1 -1
  419. data/rust/codegen/src/types.rs +166 -8
  420. data/rust/host/HECKS_RELEASE +1 -1
  421. data/rust/host/src/api.rs +4 -4
  422. data/rust/host/src/bin/validate_harness.rs +43 -0
  423. data/rust/host/src/boundary_fuzz/expr_json.rs +2 -2
  424. data/rust/host/src/boundary_fuzz/ir.rs +1 -0
  425. data/rust/host/src/boundary_fuzz/server.rs +1 -1
  426. data/rust/host/src/checkout.rs +35 -19
  427. data/rust/host/src/commerce_ir.rs +408 -2
  428. data/rust/host/src/expr_json.rs +118 -0
  429. data/rust/host/src/mint.rs +1 -1
  430. data/rust/host/src/payments/tests.rs +27 -1
  431. data/rust/host/src/payments.rs +12 -4
  432. data/rust/host/src/query_step.rs +22 -4
  433. data/rust/host/src/reference_validate.rs +653 -108
  434. data/rust/host/src/resend.rs +2 -0
  435. data/rust/host/src/server.rs +19 -6
  436. data/rust/host/src/ui_schema.rs +132 -5
  437. data/rust/host/src/web/newsletter.rs +7 -8
  438. data/rust/host/src/web/newsletter_send.rs +38 -13
  439. data/rust/host/src/web/registrations.rs +229 -26
  440. data/rust/host/src/web.rs +3 -3
  441. data/rust/parser/src/emit.rs +38 -6
  442. data/rust/parser/src/expr/ast_json.rs +7 -1
  443. data/rust/parser/src/expr/resolver.rs +171 -6
  444. data/rust/parser/src/ir.rs +7 -0
  445. data/rust/parser/src/keywords.rs +24 -0
  446. data/rust/parser/src/main.rs +3 -0
  447. data/rust/parser/src/parse/aggregate.rs +2 -1
  448. data/rust/parser/src/parse/chapter.rs +2 -2
  449. data/rust/parser/src/parse/command.rs +2 -0
  450. data/rust/parser/src/parse/hecksagon.rs +5 -2
  451. data/rust/parser/src/parse/lifecycle.rs +81 -0
  452. data/rust/parser/src/parse/mod.rs +15 -1
  453. data/rust/parser/src/parse/policy.rs +13 -2
  454. data/rust/parser/src/parse/value_object.rs +13 -8
  455. data/rust/parser/src/ruby_value.rs +1 -1
  456. data/rust/src/exemplar/commands.rs +4 -4
  457. data/rust/src/exemplar/constraints.rs +1 -1
  458. data/rust/src/exemplar/json.rs +43 -1
  459. data/rust/src/kernel/cli.rs +7 -5
  460. data/rust/src/kernel/dispatch.rs +2 -2
  461. data/rust/src/kernel/dispatch_guard_tests.rs +1 -1
  462. data/rust/src/kernel/expr.rs +73 -4
  463. data/rust/src/kernel/expression_operators/arithmetic.rs +130 -5
  464. data/rust/src/kernel/expression_operators/presence.rs +41 -3
  465. data/rust/src/kernel/expression_operators/text.rs +26 -2
  466. data/rust/src/kernel/json.rs +15 -0
  467. data/rust/src/kernel/mod.rs +1 -1
  468. data/rust/src/kernel/orchestrate.rs +54 -5
  469. data/rust/src/kernel/orchestrate_guard_tests.rs +67 -0
  470. data/rust/src/kernel/pattern.rs +60 -0
  471. data/rust/src/kernel/reference_lookup.rs +90 -7
  472. metadata +174 -36
  473. data/lib/hecks/doors/cli_door/nesting.rb +0 -34
  474. data/lib/hecks/doors/cli_door/shorthand.rb +0 -85
  475. data/lib/hecks/doors/cli_door.rb +0 -124
  476. data/lib/hecks/doors/cli_runner/answers.rb +0 -138
  477. data/lib/hecks/doors/cli_runner/resolution.rb +0 -146
  478. data/lib/hecks/doors/cli_runner/settling.rb +0 -115
  479. data/lib/hecks/doors/cli_runner/streaming.rb +0 -90
  480. data/lib/hecks/doors/cli_runner/suggestions.rb +0 -43
  481. data/lib/hecks/doors/cli_runner.rb +0 -66
  482. data/lib/hecks/doors/command_request.rb +0 -119
  483. data/lib/hecks/doors/handle.rb +0 -208
  484. data/lib/hecks/doors/json_door.rb +0 -132
  485. data/lib/hecks/doors/launcher_options.rb +0 -247
  486. data/lib/hecks/doors/mcp_door/properties.rb +0 -47
  487. data/lib/hecks/doors/mcp_door/tool_calls.rb +0 -113
  488. data/lib/hecks/doors/mcp_door/tool_definitions/auditing.rb +0 -51
  489. data/lib/hecks/doors/mcp_door/tool_definitions/calling.rb +0 -69
  490. data/lib/hecks/doors/mcp_door/tool_definitions/reading.rb +0 -97
  491. data/lib/hecks/doors/mcp_door/tool_definitions.rb +0 -15
  492. data/lib/hecks/doors/mcp_door.rb +0 -156
  493. data/lib/hecks/doors/mcp_door_scope/argument_policy.rb +0 -92
  494. data/lib/hecks/doors/mcp_door_scope/env_problems.rb +0 -55
  495. data/lib/hecks/doors/mcp_door_scope/presenter.rb +0 -78
  496. data/lib/hecks/doors/mcp_door_scope.rb +0 -201
  497. data/lib/hecks/doors/mcp_guide_cache.rb +0 -75
  498. data/lib/hecks/doors/ruby_door/aggregate_door.rb +0 -162
  499. data/lib/hecks/doors/ruby_door/chapter.rb +0 -89
  500. data/lib/hecks/doors/ruby_door.rb +0 -80
  501. data/lib/hecks/doors/usage_cache.rb +0 -139
  502. data/prebuilt/0ee7f83cba59ce7d1c45c65ba166b071779f70143449f584f33ff7989f64eccb.marshal +0 -0
  503. data/prebuilt/6e4a6bcf0aa880e2e6b056e1846b70e5a768090b9dc998bbadeaf5d6bcfd3db8.marshal +0 -0
  504. data/prebuilt/verdicts-91aaf93ce8981bd9a754948f075d95650dc2ec6435015376f5c60992b36a24b1.json +0 -1
@@ -1,138 +0,0 @@
1
- require "json"
2
-
3
- module Hecks
4
- module Doors
5
- module CliRunner
6
- # Runs one resolved command or question and shapes what it answers. Extended into
7
- # `CliRunner`.
8
- module Answers
9
- # What a dispatch needs beyond the spec: the parsed arguments, whether `--wait` was given,
10
- # and the chapter and launcher setting the command belongs to.
11
- Invocation = Struct.new(:args, :wait, :bluebook, :launcher)
12
-
13
- # Parses one resolved command's arguments, runs it as a query or command, and turns the
14
- # outcome, or the domain's refusal, into `[json_or_message, status]`. With `--wait`, a
15
- # question whose report names a gap (`LauncherOptions.gap_reported?`) answers status 1.
16
- #
17
- # @param bluebook [Bluebook::Chapter] the chapter the command belongs to
18
- # @param launcher [Hash, nil] the chapter's `launcher` world setting; nil when not opted in
19
- def dispatch(runtime, spec, name, rest, program, asking, bluebook: nil, launcher: nil) # rubocop:disable Metrics/ParameterLists -- the signature is what `call` passes
20
- args, wait = command_line(spec, rest, launcher)
21
- return answer_query(runtime, spec, args, wait) if spec[:kind] == :query
22
-
23
- run_command(runtime, spec, Invocation.new(args, wait, bluebook, launcher))
24
- rescue Runtime::NotFound, Runtime::TypeMismatch => e
25
- # A bad argument and a missing record both need the same next step: read the help.
26
- ["#{e.message}\n\n #{program} #{"query " if asking}#{name} --help", 1]
27
- rescue *Runtime::DOMAIN_REFUSALS => e
28
- # The refusal is the chapter's own sentence, verbatim.
29
- [e.message, 1]
30
- rescue StandardError => e
31
- # So is a wrapped tool's, when a question's adapter refuses to answer.
32
- raise unless tool_refusal?(e)
33
-
34
- [e.message, 1]
35
- end
36
-
37
- # The parsed arguments of a command line and whether to wait for the reactions.
38
- def command_line(spec, rest, launcher)
39
- rest, wait = LauncherOptions.take_wait(spec, rest) if launcher
40
- [CliDoor.arguments(spec, rest), wait || LauncherOptions.settled?(launcher, spec)]
41
- end
42
-
43
- # Whether the error is a wrapped tool's refusal. Matched by name: the adapters belong to the
44
- # Hecks chapter, which a client's runtime never loads.
45
- def tool_refusal?(error)
46
- error.class.ancestors.map(&:name).include?(CliRunner::TOOL_REFUSAL)
47
- end
48
-
49
- # Runs a command and answers its outcome, settled when `--wait` asks for it.
50
- def run_command(runtime, spec, invocation)
51
- args, minted = LauncherOptions.run_key(runtime, spec, invocation.args, invocation.launcher)
52
- handle = runtime.dispatch_flat(spec[:command], normalized_request(spec, args))
53
- extra = refusals_and_run(handle, minted)
54
- return settled(runtime, spec, handle, invocation.bluebook, invocation.launcher, extra) if invocation.wait
55
-
56
- [JSON.pretty_generate(outcome(handle, extra)), 0]
57
- end
58
-
59
- # The request a command takes from its parsed arguments.
60
- def normalized_request(spec, args)
61
- CommandRequest.normalize(args, receiver: spec[:receiver], legacy_receiver: spec[:legacy_receiver])
62
- end
63
-
64
- # The refused reactions of the dispatch, led by the minted run key when one was.
65
- def refusals_and_run(handle, minted)
66
- extra = refused_answer(handle)
67
- minted ? { run: minted }.merge(extra) : extra
68
- end
69
-
70
- # Answers with only this command's outcome; a full store dump is every record there is.
71
- def outcome(handle, extra)
72
- return answered(handle).merge(extra) if handle.state.nil?
73
-
74
- { id: handle.id, state: JsonDoor.materialize(handle.state), events: handle.events.map(&:name) }.merge(extra)
75
- end
76
-
77
- # Runs a question and answers its rows, or its text when an adapter answered in text.
78
- #
79
- # @param wait [Boolean, nil] whether `--wait` was given: a report naming a gap then fails
80
- # @return [Array(String, Integer)] the answer and the status
81
- def answer_query(runtime, spec, args, wait)
82
- rows = runtime.query(spec[:command], **args)
83
- text = text_answer(spec, rows)
84
- [text || JSON.pretty_generate(rows.map { |row| JsonDoor.materialize(row) }),
85
- wait && LauncherOptions.gap_reported?(text) ? 1 : 0]
86
- end
87
-
88
- # The text a query answered by a port gave, when that is the whole answer.
89
- #
90
- # A query declared `returns Document` (one value object of the single String attribute
91
- # `text`) answers one document; printing it raw keeps the document (JSON, Markdown,
92
- # sentences) readable and pipeable instead of quoted inside another JSON document. What a
93
- # query declares decides it, not how many rows came back: any other query prints JSON,
94
- # so a script reading it sees an array whether it held one row or two.
95
- #
96
- # @param spec [Hash] the question's projected spec
97
- # @param rows [Array<Hash>] the query's rows
98
- # @return [String, nil] the text, or nil when the query does not return a Document
99
- def text_answer(spec, rows)
100
- return unless spec[:returns] == "Document" && rows.length == 1 && rows.first.keys == [:text]
101
- return unless rows.first[:text].is_a?(String)
102
-
103
- rows.first[:text]
104
- end
105
-
106
- # The reactions one dispatch caused that the domain refused, as `refused_reactions:`.
107
- #
108
- # A policy's trigger that a `given` refuses is not the command's own refusal: the command
109
- # has already persisted. The dispatch result carries them (`Result#refused_reactions`);
110
- # without this the answer would say nothing of it. A defect (a crash) is shown apart, as
111
- # `reaction_defects:`, and fails `--wait`.
112
- #
113
- # @param handle [Runtime::Dispatcher::Result, Runtime::RemoteDispatcher::Result] the outcome
114
- # @return [Hash] `refused_reactions:` each with the `policy`, its `trigger`, the `reason`;
115
- # empty when every reaction was delivered (or a remote host sent no per-step log), plus
116
- # `reaction_defects:` when one crashed
117
- def refused_answer(handle)
118
- refused = handle.respond_to?(:refused_reactions) ? handle.refused_reactions : []
119
- defects = handle.respond_to?(:reaction_defects) ? handle.reaction_defects : []
120
- answer = refused.empty? ? {} : { refused_reactions: refused }
121
- defects.empty? ? answer : answer.merge(reaction_defects: defects)
122
- end
123
-
124
- # Shapes a port operation's outcome, which has no state: each event with its full payload.
125
- #
126
- # The answer lives only in the payloads, so names alone would drop it. Exit status stays 0
127
- # for refusal events too, since a healthy no is an answer, not a misuse.
128
- def answered(handle)
129
- # A port operation hydrates no instance, so the handle's `id` is nil by design.
130
- { id: handle.id || handle.events.first&.id,
131
- events: handle.events.map do |event|
132
- { name: event.name, payload: JsonDoor.materialize(event.payload) }
133
- end }
134
- end
135
- end
136
- end
137
- end
138
- end
@@ -1,146 +0,0 @@
1
- module Hecks
2
- module Doors
3
- module CliRunner
4
- # Parses a command line against the projection. Extended into `CliRunner`.
5
- module Resolution
6
- # The words that ask for the usage.
7
- HELP_WORDS = ["--help", "-h", "help"].freeze
8
-
9
- # Parses a command line against the projection: either the answer it gives without
10
- # running anything, or the command it would run.
11
- #
12
- # @return [Hash] `{answer: [text, status]}`, or `{spec:, name:, rest:, asking:, program:,
13
- # bluebook:, launcher:}` for a command to dispatch
14
- def resolve(runtime, argv, program)
15
- bluebook, argv, program = chapter_for(runtime, argv, program)
16
- launcher = LauncherOptions.settings(runtime, bluebook.name)
17
- options = LauncherOptions.projection(launcher, program)
18
- cli = Projector.call(:cli, bluebook: bluebook, options: options.merge(audience(runtime, launcher)))
19
- scope = { runtime: runtime, bluebook: bluebook, launcher: launcher, options: options,
20
- cli: cli, program: program }
21
- usage_answer(scope, argv.first) || command_plan(scope, argv)
22
- end
23
-
24
- # The answer to a line that is only a request for usage, or nil.
25
- def usage_answer(scope, name)
26
- return { answer: [scope[:cli][:usage], 0] } if name.nil? || HELP_WORDS.include?(name)
27
- return { answer: [all_usage(*scope.values_at(:runtime, :bluebook, :launcher, :options)), 0] } if name == "--all"
28
- return unless name == "--maintainer"
29
-
30
- { answer: [maintainer_usage(*scope.values_at(:runtime, :bluebook, :launcher, :options)), 0] }
31
- end
32
-
33
- # The command a line names, or the answer when it names none or asks for its help.
34
- def command_plan(scope, argv)
35
- cli = scope[:cli]
36
- asking, name, argv = entry_word(cli, argv)
37
- return { answer: [cli[:usage], 1] } if name.nil?
38
-
39
- spec = find_spec(cli, asking, name)
40
- return { answer: [unknown(cli, name, asking, scope[:program]), 1] } unless spec
41
-
42
- rest = argv[1..]
43
- return { answer: [command_help(scope, name, asking), 0] } if rest.include?("--help")
44
-
45
- { spec: spec, name: name, rest: rest, asking: asking, program: scope[:program],
46
- bluebook: scope[:bluebook], launcher: scope[:launcher] }
47
- end
48
-
49
- # The spec a name reaches; the alias map lets `create_pizza` and `order.create_pizza`
50
- # reach the same command.
51
- def find_spec(cli, asking, name)
52
- pool = asking ? cli[:questions] : cli[:commands]
53
- pool[cli[:names][asking ? :question : :command][name]]
54
- end
55
-
56
- # The usage of one command or question, which its `--help` asks for.
57
- def command_help(scope, name, asking)
58
- options = scope[:options].merge(command: name, ask: asking)
59
- Projector.call(:cli, bluebook: scope[:bluebook], options: options)[:usage]
60
- end
61
-
62
- # The usage with the internal commands and queries listed too, which `--all` asks for, and
63
- # with the maintainer's too.
64
- def all_usage(runtime, bluebook, launcher, options)
65
- shown = options.merge(audience(runtime, launcher, maintainer: true))
66
- Projector.call(:cli, bluebook: bluebook, options: shown.merge(all: true))[:usage]
67
- end
68
-
69
- # The usage a maintainer of the hecks checkout sees, which `--maintainer` asks for anywhere.
70
- def maintainer_usage(runtime, bluebook, launcher, options)
71
- shown = options.merge(audience(runtime, launcher, maintainer: true))
72
- Projector.call(:cli, bluebook: bluebook, options: shown)[:usage]
73
- end
74
-
75
- # What the help leaves out and points at for whoever typed the line: the maintainer's
76
- # aggregates only in a hecks checkout, and the chapters the chapter's launcher names, each
77
- # with the first sentence of what it is for.
78
- #
79
- # @return [Hash{Symbol => Object}] `:hide` and `:chapters` (pairs of word and summary)
80
- def audience(runtime, launcher, maintainer: LauncherOptions.maintainer?)
81
- shown = LauncherOptions.audience(launcher, maintainer)
82
- return shown unless shown[:chapters]
83
-
84
- shown.merge(chapters: chapter_summaries(runtime, shown[:chapters]))
85
- end
86
-
87
- # Each named chapter the domain attaches, as its word and the first sentence of its vision.
88
- def chapter_summaries(runtime, names)
89
- known = attached_chapters(runtime)
90
- names.filter_map do |name|
91
- next unless known.include?(name)
92
-
93
- [Naming.snake(name), Projector::CliProjector.first_sentence(runtime.registry.bluebook(name).vision)]
94
- end
95
- end
96
-
97
- # Names who the help is for, so the help remembered for one is not given to the other.
98
- def audience_key
99
- LauncherOptions.maintainer? ? "maintainer" : "project"
100
- end
101
-
102
- # Reads the entry words of a line: whether it asks a query, the bare name, and the words
103
- # from the name on.
104
- #
105
- # `query` gives queries their own namespace (`ask` is its older spelling); a chapter may
106
- # declare a command and a query of one name. A trailing `!` says the word is a command and
107
- # is not part of the name. A query answers to its bare name too; `query` is needed only when
108
- # a command shares it.
109
- #
110
- # @return [Array(Boolean, String, Array<String>)] `asking`, `name` (nil when absent), `argv`
111
- def entry_word(cli, argv)
112
- asking = CliRunner::QUERY_WORDS.include?(argv.first)
113
- argv = argv[1..] if asking
114
- name = argv.first
115
- return [asking, nil, argv] if name.nil?
116
-
117
- bang = name.end_with?("!")
118
- name = name.chomp("!")
119
- [asking || (!bang && query_only?(cli, name)), name, argv]
120
- end
121
-
122
- # Whether the name belongs to a query and to no command.
123
- def query_only?(cli, name)
124
- !cli[:names][:command].key?(name) && cli[:names][:question].key?(name)
125
- end
126
-
127
- # The chapter a command line speaks to: the booted domain's own, or, when the first word
128
- # names a chapter its hecksagon attaches or uses as a framework member (`deploy`,
129
- # `governance`), that chapter, with the word dropped and added to the program name.
130
- def chapter_for(runtime, argv, program)
131
- own = runtime.registry.bluebooks.values.first
132
- target = attached_chapters(runtime).find { |name| Naming.snake(name) == argv.first }
133
- return [own, argv, program] unless target
134
-
135
- [runtime.registry.bluebook(target), argv[1..], "#{program} #{argv.first}"]
136
- end
137
-
138
- # The chapters the booted domain's hecksagon attaches or uses as framework members.
139
- def attached_chapters(runtime)
140
- own = runtime.registry.bluebooks.values.first
141
- Array(runtime.registry.hecksagon(own.name)&.member_chapters)
142
- end
143
- end
144
- end
145
- end
146
- end
@@ -1,115 +0,0 @@
1
- require "json"
2
-
3
- module Hecks
4
- module Doors
5
- module CliRunner
6
- # The answer `--wait` gives once every reaction has run. Extended into `CliRunner`.
7
- module Settling
8
- # The answer `--wait` gives: the record re-read after every reaction has run, with all its
9
- # events, and a status of 1 when its lifecycle ended in a failure state, a refused reaction
10
- # blocks the run or a reaction crashed.
11
- #
12
- # A refusal blocks unless a sibling reaction delivered another command on the same
13
- # aggregate (`Runtime::ReactionOutcome`). The settled record is always the text, so a failed
14
- # run pipes like a passing one; the reason a human reads is a third element.
15
- #
16
- # @return [Array(String, Integer)] the JSON and the status, plus (Array(String, Integer,
17
- # String)) why the run failed when it did
18
- def settled(runtime, spec, handle, bluebook, launcher, extra) # rubocop:disable Metrics/ParameterLists -- pinned by the callers and specs
19
- why = reactions_failed(handle)
20
- return finish(stateless_json(handle, extra), why) if handle.state.nil?
21
-
22
- aggregate = aggregate_of(bluebook, spec)
23
- state = reread(runtime, bluebook, aggregate, handle) || handle.state
24
- answer = settled_answer(handle, state, settled_event_names(runtime, bluebook, aggregate, handle), extra)
25
- why += failure_reasons(aggregate, state, launcher)
26
- settled_result(answer, LauncherOptions.report?(launcher, spec), why)
27
- end
28
-
29
- # The settled answer as the launcher's report when it prints one, else as JSON.
30
- def settled_result(answer, report, why)
31
- report ? report_of(answer[:state], why) : finish(JSON.pretty_generate(answer), why)
32
- end
33
-
34
- # The JSON of a port operation's outcome, which has no record to re-read.
35
- def stateless_json(handle, extra)
36
- JSON.pretty_generate(answered(handle).merge(extra))
37
- end
38
-
39
- # The record, as the JSON answer holds it, with the names of its events.
40
- def settled_answer(handle, state, events, extra)
41
- { id: handle.id, state: JsonDoor.materialize(state), events: events }.merge(extra)
42
- end
43
-
44
- # The names of the events the record's run emitted, as the runtime now holds them.
45
- def settled_event_names(runtime, bluebook, aggregate, handle)
46
- fqn = "#{bluebook.name}::#{aggregate&.hecks_name}"
47
- events = runtime.events.select { |event| event.aggregate == fqn && event.id == handle.id }
48
- (events.empty? ? handle.events : events).map(&:name)
49
- end
50
-
51
- # The sentence for a record that ended in a failure state, as a list: empty otherwise.
52
- def failure_reasons(aggregate, state, launcher)
53
- return [] unless LauncherOptions.failed?(aggregate, state, launcher)
54
-
55
- [failure_sentence(aggregate, state, aggregate.lifecycle.field)]
56
- end
57
-
58
- # The answer of a command the launcher prints as its report: the text it recorded, with
59
- # the status 1 when the run failed, and nothing else.
60
- def report_of(state, why)
61
- text = state[why.empty? ? :output : :refusal]
62
- text = text[:value] if text.is_a?(Hash)
63
- text = why.join("\n") if text.to_s.strip.empty?
64
- [text.to_s, why.empty? ? 0 : 1]
65
- end
66
-
67
- # The sentence for a record that ended in a failure state: the state, then the record's own
68
- # `refusal` when it keeps one, without the class name an adapter's failure carries.
69
- def failure_sentence(aggregate, state, field)
70
- sentence = "#{aggregate.hecks_name} ended in the failure state #{state[field.to_sym].to_s.inspect}"
71
- refusal = state[:refusal]
72
- refusal = refusal.value if refusal.respond_to?(:value)
73
- refusal = refusal[:value] if refusal.is_a?(Hash)
74
- return sentence if refusal.to_s.strip.empty?
75
-
76
- "#{sentence}: #{refusal.to_s.strip.sub(/\A(\w+::)+\w+: /, "")}"
77
- end
78
-
79
- # The answer and its status: 0 when nothing failed, else 1 with the reasons joined.
80
- def finish(text, reasons)
81
- reasons.empty? ? [text, 0] : [text, 1, reasons.join("\n")]
82
- end
83
-
84
- # What the reactions of the run the handle reports did wrong: each refusal that blocks it
85
- # and each crash, as one sentence apiece.
86
- def reactions_failed(handle)
87
- blocking = handle.respond_to?(:blocking_reactions) ? handle.blocking_reactions : []
88
- crashed = handle.respond_to?(:reaction_defects) ? handle.reaction_defects : []
89
- blocking.map { |row| "reaction #{row[:policy]} was refused (#{row[:trigger]}): #{row[:reason]}" } +
90
- crashed.map { |row| "reaction #{row[:policy]} crashed (#{row[:error_class]}): #{row[:reason]}" }
91
- end
92
-
93
- # Whether a reaction the domain refused blocks the run the handle reports.
94
- def blocked?(handle)
95
- handle.respond_to?(:blocking_reactions) && !handle.blocking_reactions.empty?
96
- end
97
-
98
- # The aggregate a top-level command belongs to; nil for an entity command or a port.
99
- def aggregate_of(bluebook, spec)
100
- head = spec[:command].split("::", 2).last
101
- return if head.count(".") != 1
102
-
103
- bluebook.aggregates.find { |aggregate| aggregate.hecks_name == head.split(".").first }
104
- end
105
-
106
- # The record as its repository holds it now, or nil when it cannot be read.
107
- def reread(runtime, bluebook, aggregate, handle)
108
- return unless aggregate && runtime.respond_to?(:registry)
109
-
110
- runtime.registry.repository(bluebook.name, aggregate)&.find(handle.id)&.to_h
111
- end
112
- end
113
- end
114
- end
115
- end
@@ -1,90 +0,0 @@
1
- require "json"
2
-
3
- module Hecks
4
- module Doors
5
- module CliRunner
6
- # Tails a question: asks again from the cursor each answer gave, printing new entries as
7
- # JSON lines. Extended into `CliRunner`, so its methods are the runner's own.
8
- module Streaming
9
- # Tails a question: asks, prints each new entry as one JSON line, and asks again from the
10
- # cursor the answer gave, until the reader interrupts. Only for a question the `launcher`
11
- # setting lists under `streams`, given `--stream`; `from_now` applies to the first ask only.
12
- #
13
- # @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted domain
14
- # @param argv [Array<String>] as for `call`, with `--stream`
15
- # @param program [String] how the caller was invoked
16
- # @param out [IO] where each entry's line goes
17
- # @param err [IO] where a refusal goes
18
- # @param max_polls [Integer, nil] stop after this many asks; unbounded when nil
19
- # @return [Integer, nil] 0 or 1 for a stream, nil when `call` should run the line
20
- def stream(runtime:, argv:, program: "hecks run", out: $stdout, err: $stderr, max_polls: nil) # rubocop:disable Metrics/ParameterLists -- the keyword API is what callers pass
21
- plan = resolve(runtime, argv, program)
22
- words = stream_words(plan)
23
- return unless words
24
-
25
- run_stream(runtime, plan[:spec], words, out, max_polls)
26
- rescue Interrupt, Errno::EPIPE
27
- 0
28
- rescue Runtime::NotFound, Runtime::TypeMismatch => e
29
- refuse(err, "#{e.message}\n\n #{program} #{plan[:name]} --help")
30
- rescue *Runtime::DOMAIN_REFUSALS => e
31
- refuse(err, e.message)
32
- end
33
-
34
- # Tails the question the line names and answers 0, the status of a stream that ended.
35
- def run_stream(runtime, spec, words, out, max_polls)
36
- tail(runtime, spec, CliDoor.arguments(spec, words), out, max_polls)
37
- 0
38
- end
39
-
40
- # Prints a refusal and answers status 1.
41
- def refuse(err, text)
42
- err.puts(text)
43
- 1
44
- end
45
-
46
- # One ask after another, each from the cursor the last gave.
47
- def tail(runtime, spec, args, out, max_polls)
48
- args = args.merge(wait: { value: 30 }) unless args.key?(:wait)
49
- polls = 0
50
- cursor = nil
51
- loop do
52
- row = runtime.query(spec[:command], **next_ask(args, cursor)).first || {}
53
- print_entries(out, row[:events])
54
- cursor = row[:cursor]
55
- polls += 1
56
- break if cursor.nil? || (max_polls && polls >= max_polls)
57
- end
58
- end
59
-
60
- # An entry as a line: its payload, which the answer holds as JSON text, back as an object.
61
- def entry_line(event)
62
- line = JSON.parse(JSON.generate(event))
63
- line["payload"] = JSON.parse(line["payload"]) if line["payload"].is_a?(String)
64
- line
65
- rescue JSON::ParserError
66
- line
67
- end
68
-
69
- # The words to ask with when the line is a stream of a streamable question; nil otherwise.
70
- def stream_words(plan)
71
- return if plan[:answer] || !LauncherOptions.streams?(plan[:launcher], plan[:spec])
72
-
73
- words, streaming = LauncherOptions.take_stream(plan[:rest])
74
- words if streaming
75
- end
76
-
77
- # The arguments of the next ask: the first as given, later ones from the cursor.
78
- def next_ask(args, cursor)
79
- cursor ? args.except(:from_now).merge(since: { value: cursor }) : args
80
- end
81
-
82
- # Prints each event of an answer as one JSON line and flushes.
83
- def print_entries(out, events)
84
- Array(events).each { |event| out.puts(JSON.generate(entry_line(JsonDoor.materialize(event)))) }
85
- out.flush
86
- end
87
- end
88
- end
89
- end
90
- end
@@ -1,43 +0,0 @@
1
- module Hecks
2
- module Doors
3
- module CliRunner
4
- # Words the answer for a command or question the chapter does not declare. Extended into
5
- # `CliRunner`.
6
- module Suggestions
7
- # Words the answer for an unknown command or question, suggesting up to five near names.
8
- # Ranked by shared prefix, which survives a dropped letter where a substring would not.
9
- def unknown(cli, name, asking, program)
10
- near = near_names(cli, name, asking)
11
- lines = ["no such #{asking ? "query" : "command"}: #{name}"]
12
- lines += ["", "did you mean:", *near.first(5).map { |candidate| " #{candidate}" }] unless near.empty?
13
- lines += ["", " #{program}#{" query" if asking} for the full list"]
14
- lines.join("\n")
15
- end
16
-
17
- # The names worth suggesting: the bare name's homes first, then the names sharing a prefix.
18
- def near_names(cli, name, asking)
19
- # Both spellings are candidates: `order.create_piza` shares no prefix with `create_pizza`.
20
- pool = cli[:names][asking ? :question : :command].keys
21
- # A bare name is the commonest slip now that the aggregate is required: its homes first.
22
- homes = (pool + cli[:names][asking ? :command : :question].keys)
23
- .select { |candidate| candidate.split(".").last == name }.uniq
24
- homes + (similar_names(pool, name) - homes)
25
- end
26
-
27
- # The pool's names that share at least half of `name` (and three letters) as a prefix.
28
- def similar_names(pool, name)
29
- pool.map { |candidate| [shared_prefix(candidate, name), candidate] }
30
- .select { |shared, _| shared >= [name.length / 2, 3].max }
31
- .sort_by { |shared, candidate| [-shared, candidate] }
32
- .map(&:last)
33
- end
34
-
35
- # The length of the prefix two words share.
36
- def shared_prefix(one, other)
37
- length = [one.length, other.length].min
38
- (0...length).find { |index| one[index] != other[index] } || length
39
- end
40
- end
41
- end
42
- end
43
- end
@@ -1,66 +0,0 @@
1
- require "json"
2
- require_relative "cli_door"
3
- require_relative "command_request"
4
- require_relative "json_door"
5
- require_relative "launcher_options"
6
- require_relative "../projector"
7
- require_relative "../ports/clock"
8
- require_relative "cli_runner/answers"
9
- require_relative "cli_runner/resolution"
10
- require_relative "cli_runner/settling"
11
- require_relative "cli_runner/streaming"
12
- require_relative "cli_runner/suggestions"
13
-
14
- module Hecks
15
- module Doors
16
- # Parses and dispatches a command line against a `Projector::CliProjector` projection.
17
- # Does no IO: it answers `[text, status]` and leaves printing and exiting to its callers.
18
- module CliRunner
19
- # The class an adapter raises when the tool it wraps refuses.
20
- TOOL_REFUSAL = "Hecks::Adapters::ConsoleCapture::Failure".freeze
21
- # The words that open the query namespace; `ask` is the older spelling.
22
- QUERY_WORDS = %w[query ask].freeze
23
-
24
- extend Resolution
25
- extend Suggestions
26
- extend Answers
27
- extend Settling
28
- extend Streaming
29
-
30
- module_function
31
-
32
- # Runs one command line against a booted domain and answers the text to print.
33
- # The booted domain's own chapter is projected, or an attached one its first word names.
34
- #
35
- # @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted domain
36
- # @param argv [Array<String>] an optional `query` (or `ask`), the command or query name, then
37
- # `path=value` pairs; `--help`, `-h`, `help` or nothing answers usage
38
- # @param program [String] how the caller was invoked, echoed in usage and hints
39
- # @return [Array(String, Integer)] the text and the status: 0 answered, 1 refused or misused
40
- # @raise [Runtime::WiringError] if a repository or the clock adapter cannot be resolved
41
- # @raise [Runtime::StaleWrite] if concurrent writers beat the command through every retry
42
- def call(runtime:, argv:, program: "hecks run")
43
- plan = resolve(runtime, argv, program)
44
- return plan[:answer] if plan[:answer]
45
-
46
- dispatch(runtime, plan[:spec], plan[:name], plan[:rest], plan[:program], plan[:asking],
47
- bluebook: plan[:bluebook], launcher: plan[:launcher])
48
- end
49
-
50
- # Answers a command line that asks only for usage, from the projection alone.
51
- #
52
- # Needs a registry and no bound adapter, so a `Runtime::Loader::Described` serves: the
53
- # help text, the no-argument usage, a command's `--help` and an unknown one's hint all come
54
- # out byte-identical to `call`'s.
55
- #
56
- # @param runtime [#registry] a booted domain, or what `Hecks.describe` answers
57
- # @param argv [Array<String>] as for `call`
58
- # @param program [String] how the caller was invoked
59
- # @return [Array(String, Integer), nil] the text and status, or nil when the line would
60
- # run a command or question and so needs a booted domain
61
- def usage(runtime:, argv:, program: "hecks run")
62
- UsageCache.fetch(runtime, argv, program, audience: audience_key) { resolve(runtime, argv, program)[:answer] }
63
- end
64
- end
65
- end
66
- end