hecks 3.10.0 → 3.12.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 (542) 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 +158 -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 +70 -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/newcomer.rb +50 -0
  40. data/lib/hecks/adapters/driving/ruby/aggregate_module.rb +171 -0
  41. data/lib/hecks/adapters/driving/ruby/chapter.rb +91 -0
  42. data/lib/hecks/adapters/driving/ruby.rb +83 -0
  43. data/lib/hecks/adapters/driving/usage_cache.rb +144 -0
  44. data/lib/hecks/{doors.rb → adapters/driving.rb} +10 -6
  45. data/lib/hecks/behaviors/expectations/boot.rb +1 -1
  46. data/lib/hecks/bench/ruby_runner.rb +1 -1
  47. data/lib/hecks/bluebook/ask_resolution.rb +130 -0
  48. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +8 -2
  49. data/lib/hecks/bluebook/assembly/contracts/read_models.rb +1 -1
  50. data/lib/hecks/bluebook/assembly/contracts/rules.rb +4 -2
  51. data/lib/hecks/bluebook/assembly/contracts/structure.rb +3 -1
  52. data/lib/hecks/bluebook/assembly/marks.rb +4 -15
  53. data/lib/hecks/bluebook/assembly/scalars.rb +39 -0
  54. data/lib/hecks/bluebook/attribute.rb +1 -1
  55. data/lib/hecks/bluebook/behaviour/command.rb +13 -8
  56. data/lib/hecks/bluebook/behaviour/policy.rb +30 -0
  57. data/lib/hecks/bluebook/capabilities/attribute_values.rb +72 -0
  58. data/lib/hecks/bluebook/capabilities.rb +91 -9
  59. data/lib/hecks/bluebook/domain_port.rb +16 -4
  60. data/lib/hecks/bluebook/dsl/adapter_builder.rb +18 -6
  61. data/lib/hecks/bluebook/dsl/binding_proxy.rb +13 -0
  62. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/computed_sources.rb +98 -0
  63. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/fan_out_rows.rb +42 -0
  64. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/projected_fields.rb +14 -2
  65. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/provisions.rb +69 -3
  66. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/with_projections.rb +17 -10
  67. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +5 -0
  68. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -0
  69. data/lib/hecks/bluebook/dsl/command_builder/mutations.rb +8 -1
  70. data/lib/hecks/bluebook/dsl/command_builder/operands.rb +121 -0
  71. data/lib/hecks/bluebook/dsl/command_builder.rb +2 -0
  72. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +10 -1
  73. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +45 -2
  74. data/lib/hecks/bluebook/dsl/policy_builder.rb +47 -10
  75. data/lib/hecks/bluebook/dsl/rule_reference.rb +7 -4
  76. data/lib/hecks/bluebook/dsl/world_builder.rb +1 -1
  77. data/lib/hecks/bluebook/expression/ast_json/emitters.rb +6 -0
  78. data/lib/hecks/bluebook/expression/ast_json.rb +9 -6
  79. data/lib/hecks/bluebook/expression/ast_reader.rb +14 -9
  80. data/lib/hecks/bluebook/expression/projection.json +36 -0
  81. data/lib/hecks/bluebook/expression/resolver/operations.rb +31 -0
  82. data/lib/hecks/bluebook/expression/resolver/parsing.rb +38 -5
  83. data/lib/hecks/bluebook/expression/resolver/scanning.rb +22 -5
  84. data/lib/hecks/bluebook/expression/resolver/values.rb +10 -0
  85. data/lib/hecks/bluebook/expression/resolver.rb +20 -1
  86. data/lib/hecks/bluebook/hecksagon.rb +23 -3
  87. data/lib/hecks/bluebook/lifecycle.rb +22 -3
  88. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +13 -1
  89. data/lib/hecks/bluebook/meta_validator/modes.rb +4 -1
  90. data/lib/hecks/bluebook/meta_validator/readings.rb +24 -1
  91. data/lib/hecks/bluebook/meta_validator/reconstruction/heads.rb +2 -1
  92. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +6 -3
  93. data/lib/hecks/bluebook/meta_validator/shapes.rb +5 -0
  94. data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +1 -1
  95. data/lib/hecks/bluebook/meta_validator.rb +12 -12
  96. data/lib/hecks/bluebook/model_check/ask_checks.rb +71 -0
  97. data/lib/hecks/bluebook/model_check/policy_checks.rb +2 -1
  98. data/lib/hecks/bluebook/model_check/reference_checks.rb +70 -0
  99. data/lib/hecks/bluebook/model_check.rb +8 -2
  100. data/lib/hecks/bluebook/policy.rb +4 -2
  101. data/lib/hecks/bluebook/smoke_test.rb +1 -1
  102. data/lib/hecks/bluebook/synthesizer.rb +2 -0
  103. data/lib/hecks/bluebook.rb +2 -0
  104. data/lib/hecks/cli/command_table.rb +1 -1
  105. data/lib/hecks/cli/console.rb +2 -1
  106. data/lib/hecks/cli/document.rb +1 -1
  107. data/lib/hecks/cli/launcher_words.rb +4 -4
  108. data/lib/hecks/cli/mcp.rb +4 -4
  109. data/lib/hecks/cli/project_cli/launcher_source.rb +2 -2
  110. data/lib/hecks/cli/project_cli/templates.rb +5 -5
  111. data/lib/hecks/cli/project_cli.rb +9 -6
  112. data/lib/hecks/cli/run.rb +2 -2
  113. data/lib/hecks/cli/smoke_http/checks.rb +4 -1
  114. data/lib/hecks/cli/smoke_http/transport.rb +3 -3
  115. data/lib/hecks/cli/smoke_http.rb +3 -0
  116. data/lib/hecks/codemod.rb +5 -2
  117. data/lib/hecks/corpus/routes.rb +7 -0
  118. data/lib/hecks/corpus/rust.rb +3 -2
  119. data/lib/hecks/deploy/bluebook/deploy.bluebook +34 -4
  120. data/lib/hecks/doc/reference/rendering.rb +6 -3
  121. data/lib/hecks/forms/app/commands.rb +8 -7
  122. data/lib/hecks/forms/app/queries.rb +2 -1
  123. data/lib/hecks/forms/app.rb +1 -1
  124. data/lib/hecks/forms/record_renderer.rb +1 -1
  125. data/lib/hecks/framework/bluebook/privacy.bluebook +11 -0
  126. data/lib/hecks/fuzzing/concurrent_dispatch.rb +4 -1
  127. data/lib/hecks/fuzzing/failure_triage.rb +2 -1
  128. data/lib/hecks/fuzzing/mutation/run.rb +11 -4
  129. data/lib/hecks/fuzzing/properties/catalog.rb +6 -4
  130. data/lib/hecks/fuzzing/properties/guaranteed_by_construction.rb +18 -11
  131. data/lib/hecks/fuzzing/properties/guards.rb +1 -1
  132. data/lib/hecks/fuzzing/self_consistency/saga_redelivery.rb +1 -1
  133. data/lib/hecks/fuzzing/self_consistency.rb +6 -2
  134. data/lib/hecks/fuzzing/shrinker.rb +3 -1
  135. data/lib/hecks/fuzzing/value_generator.rb +19 -5
  136. data/lib/hecks/gate/stages.yml +33 -7
  137. data/lib/hecks/grammar/evolve.rb +1 -1
  138. data/lib/hecks/grammar/expression_operators.json +372 -0
  139. data/lib/hecks/grammar/operators.rb +3 -1
  140. data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +10 -10
  141. data/lib/hecks/hecks/adapters/codebase/inspection/rubycritic.rb +35 -0
  142. data/lib/hecks/hecks/adapters/codebase/inspection.rb +104 -0
  143. data/lib/hecks/hecks/adapters/codebase/publishing.rb +2 -1
  144. data/lib/hecks/hecks/adapters/codebase/release_facts.rb +12 -2
  145. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +2 -1
  146. data/lib/hecks/hecks/adapters/codebase/test_runner.rb +1 -1
  147. data/lib/hecks/hecks/adapters/cost_explorer.rb +8 -5
  148. data/lib/hecks/hecks/adapters/deploy_toolchain/statuses.rb +2 -1
  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 +133 -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/terminal.rb +13 -11
  173. data/lib/hecks/hecks/codebase.behaviors +13 -1
  174. data/lib/hecks/hecks/codebase.bluebook +239 -5
  175. data/lib/hecks/hecks/custodian.behaviors +24 -6
  176. data/lib/hecks/hecks/custodian.bluebook +6 -1227
  177. data/lib/hecks/hecks/deploy.behaviors +1 -1
  178. data/lib/hecks/hecks/deploy.world +1 -3
  179. data/lib/hecks/hecks/hecks.behaviors +1 -1
  180. data/lib/hecks/hecks/hecks.hecksagon +53 -18
  181. data/lib/hecks/hecks/hecks.world +13 -10
  182. data/lib/hecks/hecks/rehearsal.bluebook +477 -0
  183. data/lib/hecks/hecks/site.behaviors +1 -1
  184. data/lib/hecks/hecks/site.world +0 -1
  185. data/lib/hecks/hecks/tooling.bluebook +1238 -0
  186. data/lib/hecks/indifferent_key.rb +20 -0
  187. data/lib/hecks/language/adapter.bluebook +13 -3
  188. data/lib/hecks/language/bluebook/aggregate.bluebook +23 -0
  189. data/lib/hecks/language/bluebook/bluebook.behaviors +23 -0
  190. data/lib/hecks/language/bluebook/bluebook.bluebook +15 -0
  191. data/lib/hecks/language/bluebook/entity.bluebook +18 -0
  192. data/lib/hecks/language/bluebook/policy.behaviors +19 -0
  193. data/lib/hecks/language/bluebook/policy.bluebook +9 -1
  194. data/lib/hecks/language/bluebook/query.bluebook +1 -1
  195. data/lib/hecks/language/bluebook/vocabulary.bluebook +63 -4
  196. data/lib/hecks/language/hecksagon/domain_port.bluebook +1 -1
  197. data/lib/hecks/language/hecksagon/hecksagon.bluebook +22 -0
  198. data/lib/hecks/language/oidc.json +10 -0
  199. data/lib/hecks/language/port.bluebook +4 -4
  200. data/lib/hecks/literal.rb +12 -3
  201. data/lib/hecks/ports/key_vault.rb +7 -2
  202. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +21 -10
  203. data/lib/hecks/ports/persistence/plugins/era/postgres_era/boot.rb +4 -2
  204. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +2 -1
  205. data/lib/hecks/ports/persistence/repository_factory.rb +6 -3
  206. data/lib/hecks/projection_files/expression_tables.rb +7 -2
  207. data/lib/hecks/projections/deploy/box/compose.rb +6 -4
  208. data/lib/hecks/projections/deploy/box/edge.rb +62 -0
  209. data/lib/hecks/projections/deploy/box/logging.rb +99 -0
  210. data/lib/hecks/projections/deploy/box/roll_scripts.rb +16 -8
  211. data/lib/hecks/projections/deploy/box/settings/cdn.rb +40 -0
  212. data/lib/hecks/projections/deploy/box/settings/logs.rb +40 -0
  213. data/lib/hecks/projections/deploy/box/settings/patterns.rb +11 -0
  214. data/lib/hecks/projections/deploy/box/settings/types.rb +16 -1
  215. data/lib/hecks/projections/deploy/box/settings.rb +6 -2
  216. data/lib/hecks/projections/deploy/box/stack_yaml.rb +5 -2
  217. data/lib/hecks/projections/deploy/box/templates/Makefile.tmpl +16 -0
  218. data/lib/hecks/projections/deploy/box/templates/box.yaml.tmpl +8 -4
  219. data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +32 -0
  220. data/lib/hecks/projections/deploy/box/templates/deploy-service.sh.tmpl +1 -1
  221. data/lib/hecks/projections/deploy/box/templates/edge.mk.tmpl +17 -0
  222. data/lib/hecks/projections/deploy/box/templates/edge.yaml.tmpl +65 -0
  223. data/lib/hecks/projections/deploy/box/templates/log-group.yaml.tmpl +12 -0
  224. data/lib/hecks/projections/deploy/box/templates/log-policy.yaml.tmpl +7 -0
  225. data/lib/hecks/projections/deploy/box/templates/render-compose-taskdef.sh.tmpl +5 -4
  226. data/lib/hecks/projections/deploy/box/templates/render-compose.sh.tmpl +5 -4
  227. data/lib/hecks/projections/deploy/box.rb +15 -3
  228. data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
  229. data/lib/hecks/projections/deploy/shared.rb +5 -2
  230. data/lib/hecks/projections/model/deviations.rb +2 -2
  231. data/lib/hecks/projections/model.rb +2 -2
  232. data/lib/hecks/projections/oidc.rb +13 -1
  233. data/lib/hecks/projections/shape.rb +5 -2
  234. data/lib/hecks/projections/site/cms_editor/attributes.rb +26 -6
  235. data/lib/hecks/projections/site/cms_editor/blocks.rb +157 -0
  236. data/lib/hecks/projections/site/cms_editor/checks.rb +14 -1
  237. data/lib/hecks/projections/site/cms_editor/clearing.rb +70 -0
  238. data/lib/hecks/projections/site/cms_editor/color.rb +124 -0
  239. data/lib/hecks/projections/site/cms_editor/composition.rb +42 -0
  240. data/lib/hecks/projections/site/cms_editor/destructive.rb +61 -0
  241. data/lib/hecks/projections/site/cms_editor/drafts.rb +102 -0
  242. data/lib/hecks/projections/site/cms_editor/hints.rb +74 -0
  243. data/lib/hecks/projections/site/cms_editor/icons.rb +86 -0
  244. data/lib/hecks/projections/site/cms_editor/key_kinds.rb +31 -0
  245. data/lib/hecks/projections/site/cms_editor/limits.rb +72 -0
  246. data/lib/hecks/projections/site/cms_editor/media.rb +1 -1
  247. data/lib/hecks/projections/site/cms_editor/moment.rb +34 -0
  248. data/lib/hecks/projections/site/cms_editor/palette.rb +115 -0
  249. data/lib/hecks/projections/site/cms_editor/pickers.rb +85 -0
  250. data/lib/hecks/projections/site/cms_editor/preview.rb +91 -0
  251. data/lib/hecks/projections/site/cms_editor/relations.rb +92 -0
  252. data/lib/hecks/projections/site/cms_editor/scale_checks.rb +66 -0
  253. data/lib/hecks/projections/site/cms_editor/scheduling.rb +150 -0
  254. data/lib/hecks/projections/site/cms_editor/schema.rb +96 -19
  255. data/lib/hecks/projections/site/cms_editor/setting.rb +52 -9
  256. data/lib/hecks/projections/site/cms_editor/targets.rb +86 -0
  257. data/lib/hecks/projections/site/cms_editor/templates/package.json.tmpl +18 -1
  258. data/lib/hecks/projections/site/cms_editor/templates/src/app.ts.tmpl +251 -57
  259. data/lib/hecks/projections/site/cms_editor/templates/src/assets.ts.tmpl +98 -0
  260. data/lib/hecks/projections/site/cms_editor/templates/src/auth/membership.ts.tmpl +32 -24
  261. data/lib/hecks/projections/site/cms_editor/templates/src/browser/app.css.tmpl +412 -0
  262. data/lib/hecks/projections/site/cms_editor/templates/src/browser/autosave.js.tmpl +144 -0
  263. data/lib/hecks/projections/site/cms_editor/templates/src/browser/blocks.js.tmpl +500 -0
  264. data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_doc.js.tmpl +175 -0
  265. data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_editor.js.tmpl +418 -0
  266. data/lib/hecks/projections/site/cms_editor/templates/src/browser/epoch.js.tmpl +61 -0
  267. data/lib/hecks/projections/site/cms_editor/templates/src/browser/forms.js.tmpl +210 -0
  268. data/lib/hecks/projections/site/cms_editor/templates/src/browser/lists.js.tmpl +40 -0
  269. data/lib/hecks/projections/site/cms_editor/templates/src/browser/main.js.tmpl +26 -0
  270. data/lib/hecks/projections/site/cms_editor/templates/src/browser/media_picker.js.tmpl +232 -0
  271. data/lib/hecks/projections/site/cms_editor/templates/src/browser/moments.js.tmpl +73 -0
  272. data/lib/hecks/projections/site/cms_editor/templates/src/browser/pickers.js.tmpl +152 -0
  273. data/lib/hecks/projections/site/cms_editor/templates/src/browser/preview.js.tmpl +98 -0
  274. data/lib/hecks/projections/site/cms_editor/templates/src/browser/shell.js.tmpl +140 -0
  275. data/lib/hecks/projections/site/cms_editor/templates/src/browser/toast.js.tmpl +66 -0
  276. data/lib/hecks/projections/site/cms_editor/templates/src/choices.ts.tmpl +99 -0
  277. data/lib/hecks/projections/site/cms_editor/templates/src/commands.ts.tmpl +78 -24
  278. data/lib/hecks/projections/site/cms_editor/templates/src/config.ts.tmpl +6 -0
  279. data/lib/hecks/projections/site/cms_editor/templates/src/flash.ts.tmpl +52 -0
  280. data/lib/hecks/projections/site/cms_editor/templates/src/host.ts.tmpl +19 -2
  281. data/lib/hecks/projections/site/cms_editor/templates/src/media/handler.ts.tmpl +31 -8
  282. data/lib/hecks/projections/site/cms_editor/templates/src/names.ts.tmpl +36 -0
  283. data/lib/hecks/projections/site/cms_editor/templates/src/schema.ts.tmpl +131 -2
  284. data/lib/hecks/projections/site/cms_editor/templates/src/ui/availability.ts.tmpl +15 -0
  285. data/lib/hecks/projections/site/cms_editor/templates/src/ui/block_list.js.tmpl +213 -0
  286. data/lib/hecks/projections/site/cms_editor/templates/src/ui/blocks.ts.tmpl +275 -0
  287. data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_model.js.tmpl +8 -5
  288. data/lib/hecks/projections/site/cms_editor/templates/src/ui/detail.ts.tmpl +88 -0
  289. data/lib/hecks/projections/site/cms_editor/templates/src/ui/dialogs.ts.tmpl +43 -0
  290. data/lib/hecks/projections/site/cms_editor/templates/src/ui/fields.ts.tmpl +229 -34
  291. data/lib/hecks/projections/site/cms_editor/templates/src/ui/html.ts.tmpl +40 -81
  292. data/lib/hecks/projections/site/cms_editor/templates/src/ui/icons.ts.tmpl +15 -0
  293. data/lib/hecks/projections/site/cms_editor/templates/src/ui/input.ts.tmpl +29 -12
  294. data/lib/hecks/projections/site/cms_editor/templates/src/ui/links.ts.tmpl +100 -0
  295. data/lib/hecks/projections/site/cms_editor/templates/src/ui/moments.ts.tmpl +44 -0
  296. data/lib/hecks/projections/site/cms_editor/templates/src/ui/outline.ts.tmpl +53 -17
  297. data/lib/hecks/projections/site/cms_editor/templates/src/ui/pages.ts.tmpl +178 -88
  298. data/lib/hecks/projections/site/cms_editor/templates/src/ui/paging.ts.tmpl +82 -0
  299. data/lib/hecks/projections/site/cms_editor/templates/src/ui/pickers.ts.tmpl +110 -0
  300. data/lib/hecks/projections/site/cms_editor/templates/src/ui/preview.ts.tmpl +87 -0
  301. data/lib/hecks/projections/site/cms_editor/templates/src/ui/related.ts.tmpl +97 -0
  302. data/lib/hecks/projections/site/cms_editor/templates/src/ui/schedule.ts.tmpl +160 -0
  303. data/lib/hecks/projections/site/cms_editor/templates/src/ui/shell.ts.tmpl +211 -0
  304. data/lib/hecks/projections/site/cms_editor/templates/src/ui/table.ts.tmpl +151 -0
  305. data/lib/hecks/projections/site/cms_editor/templates/src/ui/theme.js.tmpl +16 -0
  306. data/lib/hecks/projections/site/cms_editor/templates/src/ui/values.ts.tmpl +69 -0
  307. data/lib/hecks/projections/site/cms_editor/templates/src/ui/words.ts.tmpl +97 -0
  308. data/lib/hecks/projections/site/cms_editor/templates/src/ui/world.ts.tmpl +35 -0
  309. data/lib/hecks/projections/site/cms_editor/templates/tsconfig.json.tmpl +2 -1
  310. data/lib/hecks/projections/site/cms_editor/theme.rb +29 -0
  311. data/lib/hecks/projections/site/cms_editor/theme_checks.rb +57 -0
  312. data/lib/hecks/projections/site/cms_editor.rb +49 -23
  313. data/lib/hecks/projections/site/role_probe.rb +2 -0
  314. data/lib/hecks/projections/site/site_root/workflow.rb +5 -0
  315. data/lib/hecks/projector/cli_audience.rb +22 -1
  316. data/lib/hecks/projector/cli_projector/listing.rb +1 -1
  317. data/lib/hecks/projector/cli_projector/option_specs.rb +1 -1
  318. data/lib/hecks/projector/cli_projector/usage.rb +15 -4
  319. data/lib/hecks/projector/docs_projector/closing.rb +1 -1
  320. data/lib/hecks/projector/docs_projector.rb +1 -1
  321. data/lib/hecks/projector/exporter/bindings.rb +40 -10
  322. data/lib/hecks/projector/exporter/capability_values.rb +55 -0
  323. data/lib/hecks/projector/narrate_projector/reactions.rb +1 -1
  324. data/lib/hecks/projector/target.rb +11 -0
  325. data/lib/hecks/projector.rb +32 -5
  326. data/lib/hecks/quality_control/adapters/github_checks.rb +12 -8
  327. data/lib/hecks/quality_control/adapters/github_issues.rb +8 -4
  328. data/lib/hecks/quality_control/cli/qa_open_pr.rb +2 -2
  329. data/lib/hecks/quality_control/cli/qa_sweep/checks/shrinking.rb +1 -1
  330. data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +1 -1
  331. data/lib/hecks/quality_control/cli/qa_sweep.rb +1 -1
  332. data/lib/hecks/quality_control/quality_control.bluebook +6 -2
  333. data/lib/hecks/release/approval.rb +64 -0
  334. data/lib/hecks/router/{door.rb → aggregate_reader.rb} +5 -4
  335. data/lib/hecks/router/namespace_installer.rb +16 -16
  336. data/lib/hecks/router.rb +1 -1
  337. data/lib/hecks/runtime/best_effort.rb +22 -0
  338. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +12 -2
  339. data/lib/hecks/runtime/command_interpreter.rb +3 -2
  340. data/lib/hecks/runtime/command_rules/emission.rb +1 -1
  341. data/lib/hecks/runtime/dispatcher.rb +4 -4
  342. data/lib/hecks/runtime/entity_element/location.rb +1 -1
  343. data/lib/hecks/runtime/event.rb +5 -0
  344. data/lib/hecks/runtime/identity.rb +7 -6
  345. data/lib/hecks/runtime/interpreting.rb +2 -1
  346. data/lib/hecks/runtime/invocation_builder.rb +1 -1
  347. data/lib/hecks/runtime/loader/boot_steps.rb +8 -5
  348. data/lib/hecks/runtime/loader.rb +10 -10
  349. data/lib/hecks/runtime/policy_interpreter/arguments.rb +7 -4
  350. data/lib/hecks/runtime/policy_interpreter/fan_out.rb +3 -3
  351. data/lib/hecks/runtime/policy_interpreter.rb +6 -6
  352. data/lib/hecks/runtime/port_operation_interpreter.rb +1 -1
  353. data/lib/hecks/runtime/reaction_invocation.rb +1 -1
  354. data/lib/hecks/runtime/rebuild_sweep.rb +11 -1
  355. data/lib/hecks/runtime/registry/ask_verification.rb +35 -0
  356. data/lib/hecks/runtime/registry/hecksagon_rules.rb +10 -0
  357. data/lib/hecks/runtime/registry/merging.rb +1 -1
  358. data/lib/hecks/runtime/registry/providers.rb +16 -0
  359. data/lib/hecks/runtime/registry/read_repository.rb +6 -4
  360. data/lib/hecks/runtime/registry/verification.rb +4 -1
  361. data/lib/hecks/runtime/registry/world_defaults.rb +29 -0
  362. data/lib/hecks/runtime/registry.rb +1 -1
  363. data/lib/hecks/runtime/saga_interpreter/compensation.rb +1 -1
  364. data/lib/hecks/runtime/saga_interpreter/delivery.rb +4 -4
  365. data/lib/hecks/runtime/saga_interpreter/transitions.rb +1 -1
  366. data/lib/hecks/runtime/saga_interpreter.rb +3 -3
  367. data/lib/hecks/runtime/tenant_check.rb +5 -4
  368. data/lib/hecks/runtime/value/coercion.rb +2 -2
  369. data/lib/hecks/runtime/value/validation.rb +16 -4
  370. data/lib/hecks/runtime.rb +9 -9
  371. data/lib/hecks/rust_build/host.rb +11 -4
  372. data/lib/hecks/rust_build/prebuilt_binary.rb +101 -0
  373. data/lib/hecks/rust_build/project_rust/target_ir.rb +1 -1
  374. data/lib/hecks/rust_build/wasm.rb +1 -1
  375. data/lib/hecks/rust_build/wasm_browser.rb +1 -1
  376. data/lib/hecks/rust_build.rb +11 -0
  377. data/lib/hecks/served_options.rb +53 -0
  378. data/lib/hecks/site/bluebook/site.bluebook +1 -0
  379. data/lib/hecks/storehouse/audit.rb +1 -1
  380. data/lib/hecks/storehouse/dispatching.rb +8 -6
  381. data/lib/hecks/storehouse/introspection.rb +2 -2
  382. data/lib/hecks/storehouse/querying.rb +6 -4
  383. data/lib/hecks/storehouse.rb +4 -4
  384. data/lib/hecks/tools/ci_gate_decision.rb +35 -0
  385. data/lib/hecks/tools/ci_gates.rb +20 -1
  386. data/lib/hecks/tools/comment_style/source_file.rb +4 -1
  387. data/lib/hecks/tools/deploy_recipe_lint/fixtures.rb +4 -1
  388. data/lib/hecks/tools/lanes/files.rb +8 -0
  389. data/lib/hecks/tools/lanes/prebuilt_workflow.rb +81 -0
  390. data/lib/hecks/tools/lanes/rows.rb +6 -0
  391. data/lib/hecks/tools/lanes.rb +1 -0
  392. data/lib/hecks/tools/oidc_manifests.rb +4 -5
  393. data/lib/hecks/tools/regeneration_run.rb +11 -4
  394. data/lib/hecks/tools/release_only.rb +111 -0
  395. data/lib/hecks/tools/site_routes/editor_files.rb +12 -5
  396. data/lib/hecks/tools/tenant_provisioning.rb +2 -2
  397. data/lib/hecks/tools/tools_doc.rb +4 -4
  398. data/lib/hecks/version.rb +1 -1
  399. data/lib/hecks/vocabulary.rb +19 -5
  400. data/lib/hecks.rb +15 -15
  401. data/lib/rubocop/cop/hecks/env_dump.rb +69 -0
  402. data/lib/rubocop/cop/hecks/monotonic_duration.rb +53 -0
  403. data/lib/rubocop/cop/hecks/no_wall_clock_in_runtime.rb +34 -0
  404. data/lib/rubocop/cop/hecks/raw_shell_interpolation.rb +79 -0
  405. data/lib/rubocop/cop/hecks/sleep_in_spec.rb +34 -0
  406. data/lib/rubocop/cop/hecks/swallowed_rescue.rb +37 -0
  407. data/lib/rubocop/cop/hecks/symbol_string_key_mix.rb +50 -0
  408. data/lib/rubocop/cop/hecks/untyped_raise.rb +37 -0
  409. data/prebuilt/9ee54e2f0d4d6d3fe046827779b221e02de665fba911a048ae5dd8b8fbfb7d90.marshal +0 -0
  410. data/prebuilt/d74a00e996222c1d8b676d5959092effc46b4dc59e0555f3bf9554ac58e3094a.marshal +0 -0
  411. data/prebuilt/verdicts-df19f35868d3284a70f55e42dcef87ef4e65e6a678e5135f72e1f6cff4c1357b.json +1 -0
  412. data/rust/.gitignore +6 -0
  413. data/rust/Cargo.toml +6 -0
  414. data/rust/build/src/binding_pass.rs +103 -0
  415. data/rust/build/src/cargo_sync.rs +6 -0
  416. data/rust/build/src/lineage_pass.rs +3 -3
  417. data/rust/build/src/main.rs +17 -5
  418. data/rust/build/src/pipeline.rs +48 -5
  419. data/rust/build/src/seams_pass.rs +123 -0
  420. data/rust/build/src/translation_pass.rs +167 -0
  421. data/rust/codegen/src/asks.rs +103 -0
  422. data/rust/codegen/src/commands.rs +13 -8
  423. data/rust/codegen/src/constraints.rs +25 -0
  424. data/rust/codegen/src/domain_generator.rs +2 -0
  425. data/rust/codegen/src/expr_emitter.rs +13 -0
  426. data/rust/codegen/src/json_codec.rs +54 -15
  427. data/rust/codegen/src/main.rs +1 -0
  428. data/rust/codegen/src/mutations.rs +128 -3
  429. data/rust/codegen/src/queries.rs +11 -2
  430. data/rust/codegen/src/reactions.rs +1 -1
  431. data/rust/codegen/src/types.rs +166 -8
  432. data/rust/host/HECKS_RELEASE +1 -1
  433. data/rust/host/src/api.rs +4 -4
  434. data/rust/host/src/bin/validate_harness.rs +43 -0
  435. data/rust/host/src/boundary_fuzz/expr_json.rs +2 -2
  436. data/rust/host/src/boundary_fuzz/ir.rs +1 -0
  437. data/rust/host/src/boundary_fuzz/server.rs +1 -1
  438. data/rust/host/src/checkout.rs +35 -19
  439. data/rust/host/src/commerce_ir.rs +408 -2
  440. data/rust/host/src/expr_json.rs +118 -0
  441. data/rust/host/src/extension.rs +13 -1
  442. data/rust/host/src/lib.rs +1 -0
  443. data/rust/host/src/mint.rs +1 -1
  444. data/rust/host/src/payments/tests.rs +27 -1
  445. data/rust/host/src/payments.rs +12 -4
  446. data/rust/host/src/platform_signin/attack_tests.rs +1100 -0
  447. data/rust/host/src/platform_signin/config.rs +512 -0
  448. data/rust/host/src/platform_signin/cookie.rs +237 -0
  449. data/rust/host/src/platform_signin/extension.rs +190 -0
  450. data/rust/host/src/platform_signin/fetcher.rs +322 -0
  451. data/rust/host/src/platform_signin/handler.rs +251 -0
  452. data/rust/host/src/platform_signin/handler_tests.rs +554 -0
  453. data/rust/host/src/platform_signin/jti.rs +196 -0
  454. data/rust/host/src/platform_signin/jwks.rs +542 -0
  455. data/rust/host/src/platform_signin/jws.rs +249 -0
  456. data/rust/host/src/platform_signin/known_answer_tests.rs +237 -0
  457. data/rust/host/src/platform_signin/mapping_tests.rs +254 -0
  458. data/rust/host/src/platform_signin/mod.rs +75 -0
  459. data/rust/host/src/platform_signin/mode_tests.rs +382 -0
  460. data/rust/host/src/platform_signin/pages.rs +204 -0
  461. data/rust/host/src/platform_signin/query.rs +233 -0
  462. data/rust/host/src/platform_signin/session.rs +151 -0
  463. data/rust/host/src/platform_signin/settings.rs +385 -0
  464. data/rust/host/src/platform_signin/strict_json.rs +351 -0
  465. data/rust/host/src/platform_signin/test_support.rs +235 -0
  466. data/rust/host/src/platform_signin/ttl_tests.rs +94 -0
  467. data/rust/host/src/platform_signin/verify.rs +604 -0
  468. data/rust/host/src/platform_signin/wiring_support.rs +319 -0
  469. data/rust/host/src/query_step.rs +22 -4
  470. data/rust/host/src/reference_validate.rs +653 -108
  471. data/rust/host/src/resend.rs +2 -0
  472. data/rust/host/src/server.rs +19 -6
  473. data/rust/host/src/ui_schema.rs +132 -5
  474. data/rust/host/src/web/newsletter.rs +7 -8
  475. data/rust/host/src/web/newsletter_send.rs +38 -13
  476. data/rust/host/src/web/registrations.rs +229 -26
  477. data/rust/host/src/web.rs +53 -9
  478. data/rust/parser/src/emit.rs +38 -6
  479. data/rust/parser/src/expr/ast_json.rs +7 -1
  480. data/rust/parser/src/expr/resolver.rs +171 -6
  481. data/rust/parser/src/ir.rs +7 -0
  482. data/rust/parser/src/keywords.rs +24 -0
  483. data/rust/parser/src/main.rs +3 -0
  484. data/rust/parser/src/parse/aggregate.rs +2 -1
  485. data/rust/parser/src/parse/chapter.rs +2 -2
  486. data/rust/parser/src/parse/command.rs +2 -0
  487. data/rust/parser/src/parse/hecksagon.rs +5 -2
  488. data/rust/parser/src/parse/lifecycle.rs +81 -0
  489. data/rust/parser/src/parse/mod.rs +15 -1
  490. data/rust/parser/src/parse/policy.rs +13 -2
  491. data/rust/parser/src/parse/value_object.rs +34 -17
  492. data/rust/parser/src/ruby_value.rs +1 -1
  493. data/rust/src/exemplar/commands.rs +4 -4
  494. data/rust/src/exemplar/constraints.rs +1 -1
  495. data/rust/src/exemplar/json.rs +43 -1
  496. data/rust/src/kernel/cli.rs +7 -5
  497. data/rust/src/kernel/dispatch.rs +2 -2
  498. data/rust/src/kernel/dispatch_guard_tests.rs +1 -1
  499. data/rust/src/kernel/expr.rs +66 -3
  500. data/rust/src/kernel/expression_operators/arithmetic.rs +130 -5
  501. data/rust/src/kernel/expression_operators/text.rs +26 -2
  502. data/rust/src/kernel/json.rs +15 -0
  503. data/rust/src/kernel/mod.rs +1 -1
  504. data/rust/src/kernel/orchestrate.rs +54 -5
  505. data/rust/src/kernel/orchestrate_guard_tests.rs +67 -0
  506. data/rust/src/kernel/pattern.rs +60 -0
  507. data/rust/src/kernel/reference_lookup.rs +90 -7
  508. metadata +178 -38
  509. data/lib/hecks/doors/cli_door/nesting.rb +0 -34
  510. data/lib/hecks/doors/cli_door/shorthand.rb +0 -85
  511. data/lib/hecks/doors/cli_door.rb +0 -124
  512. data/lib/hecks/doors/cli_runner/answers.rb +0 -138
  513. data/lib/hecks/doors/cli_runner/resolution.rb +0 -146
  514. data/lib/hecks/doors/cli_runner/settling.rb +0 -115
  515. data/lib/hecks/doors/cli_runner/streaming.rb +0 -90
  516. data/lib/hecks/doors/cli_runner/suggestions.rb +0 -43
  517. data/lib/hecks/doors/cli_runner.rb +0 -66
  518. data/lib/hecks/doors/command_request.rb +0 -119
  519. data/lib/hecks/doors/handle.rb +0 -208
  520. data/lib/hecks/doors/json_door.rb +0 -132
  521. data/lib/hecks/doors/launcher_options.rb +0 -247
  522. data/lib/hecks/doors/mcp_door/properties.rb +0 -47
  523. data/lib/hecks/doors/mcp_door/tool_calls.rb +0 -113
  524. data/lib/hecks/doors/mcp_door/tool_definitions/auditing.rb +0 -51
  525. data/lib/hecks/doors/mcp_door/tool_definitions/calling.rb +0 -69
  526. data/lib/hecks/doors/mcp_door/tool_definitions/reading.rb +0 -97
  527. data/lib/hecks/doors/mcp_door/tool_definitions.rb +0 -15
  528. data/lib/hecks/doors/mcp_door.rb +0 -156
  529. data/lib/hecks/doors/mcp_door_scope/argument_policy.rb +0 -92
  530. data/lib/hecks/doors/mcp_door_scope/env_problems.rb +0 -55
  531. data/lib/hecks/doors/mcp_door_scope/presenter.rb +0 -78
  532. data/lib/hecks/doors/mcp_door_scope.rb +0 -201
  533. data/lib/hecks/doors/mcp_guide_cache.rb +0 -75
  534. data/lib/hecks/doors/ruby_door/aggregate_door.rb +0 -162
  535. data/lib/hecks/doors/ruby_door/chapter.rb +0 -89
  536. data/lib/hecks/doors/ruby_door.rb +0 -80
  537. data/lib/hecks/doors/usage_cache.rb +0 -139
  538. data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_widget.js.tmpl +0 -282
  539. data/lib/hecks/projections/site/cms_editor/templates/src/ui/media_picker.js.tmpl +0 -109
  540. data/prebuilt/c28d6d9db14eea3e17161e9d9c262efee30b256cf97693a7cfd552bce24b2f7f.marshal +0 -0
  541. data/prebuilt/cb375cba61ca0dbe8ddb9739c306510fe05e5f4b7a9b6955caff8224c63ea0e0.marshal +0 -0
  542. data/prebuilt/verdicts-d6d584ad43b5f3cebf88aafbbdbf024c38574acf18e06adffbc555f7786b1c2f.json +0 -1
@@ -1,156 +0,0 @@
1
- require "json"
2
- require_relative "../../hecks"
3
- require_relative "../cli/mcp"
4
- require_relative "mcp_door/tool_definitions"
5
- require_relative "mcp_door/tool_calls"
6
-
7
- module Hecks
8
- module Doors
9
- # The MCP door onto the storehouse bus: hand-rolled JSON-RPC over stdio, one
10
- # server per booted domain. All logic and the JSON shape answered with live in
11
- # `Storehouse`; this is plumbing only. A `role:`/`actor_id:` a caller sends is
12
- # asserted, not verified, unless Governance is attached.
13
- #
14
- # There is no authentication: `domain:` boots real Ruby confined to
15
- # `Storehouse::BOOT_ROOT`, so this is a code execution surface for anyone who
16
- # can write to stdin (ADR 0062). Spawning with `HECKS_DOOR_TOOLS=readers` and
17
- # `HECKS_DOOR_DOMAINS=<dir>[:<dir>...]` narrows a door to read-only tools and
18
- # named domains (`McpDoorScope`); this limits what one agent can reach and is
19
- # not itself authentication.
20
- module McpDoor
21
- PROTOCOL_VERSION = "2024-11-05".freeze
22
-
23
- TOOLS = ToolDefinitions::TOOLS
24
-
25
- NOTES = [
26
- "Identity is self-asserted: role:/actor_id: are claims made by the caller and are not verified.",
27
- "state, events, history, follow, describe, catalog and query run with no role check at all.",
28
- "domain: boots real Ruby under #{Storehouse::BOOT_ROOT}; anyone who can write to stdin can run it."
29
- ].freeze
30
-
31
- # The method that answers each JSON-RPC method that has a response.
32
- REPLIES = {
33
- "initialize" => :initialize_result,
34
- "tools/list" => :tools_list,
35
- "tools/call" => :tools_call,
36
- "ping" => :ping_result
37
- }.freeze
38
-
39
- module_function
40
-
41
- # Read once, so a bad setting refuses to start the door before anything is
42
- # written to stdout.
43
- def scope
44
- @scope ||= McpDoorScope.start!(server: CLI::Mcp::SERVER)
45
- end
46
-
47
- # The standing notes, then what this door's scope says about `domain:`.
48
- def warning_notes
49
- NOTES[0, 2] + (scope.restricted? ? scope.notes : [NOTES[2]])
50
- end
51
-
52
- def serve
53
- McpStdioGuard.warn!(server: CLI::Mcp::SERVER, notes: warning_notes)
54
- $stdout.sync = true
55
-
56
- $stdin.each_line { |line| answer_line(line.strip) }
57
- end
58
-
59
- # Answers one request line; a line that is not JSON is a parse error, not a crash.
60
- def answer_line(line)
61
- return if line.empty?
62
-
63
- handle_safely(JSON.parse(line))
64
- rescue JSON::ParserError => e
65
- send_error(nil, -32_700, "Parse error: #{e.message}")
66
- end
67
-
68
- # Handles a request, answering an internal error rather than ending the door.
69
- def handle_safely(request)
70
- handle(request)
71
- rescue StandardError => e
72
- send_error(request["id"], -32_603, "Internal error: #{e.class}: #{e.message}")
73
- end
74
-
75
- # `domain:` is confined to `Storehouse::BOOT_ROOT`, and further to a restricted
76
- # door's `HECKS_DOOR_DOMAINS`, before anything boots. An unrestricted door boots the
77
- # domain afresh on every call; a restricted door lives for one spawner's session and
78
- # keeps each named domain booted until the files of its directory change.
79
- def boot(domain)
80
- path = Storehouse.confine!(scope.admit_domain!(domain), "domain")
81
- return Hecks.boot(path, install_doors: false) unless scope.restricted?
82
-
83
- resident_boot(path)
84
- end
85
-
86
- # The runtime a restricted door keeps for `path`, booted again once its files change.
87
- def resident_boot(path)
88
- resident = (@resident ||= {})
89
- fingerprint = Storehouse.fingerprint(path)
90
- return resident[path][:runtime] if resident[path] && resident[path][:fingerprint] == fingerprint
91
-
92
- resident[path] = { fingerprint: fingerprint, runtime: Hecks.boot(path, install_doors: false) }
93
- resident[path][:runtime]
94
- end
95
-
96
- def answer_tool(name, args)
97
- return scope.refusal(name) unless scope.permits_tool?(name)
98
-
99
- ToolCalls.call(name, scope.with_default_domain(args))
100
- end
101
-
102
- # One line per allowed command: what it takes and the role it declares, read from the booted
103
- # domain the door serves. Nil when the door serves no single domain or it will not boot, so
104
- # `tools/list` still answers, with the commands as a bare list.
105
- def command_guide
106
- return unless scope.commands_mode? && scope.default_domain
107
-
108
- McpGuideCache.remember(scope.default_domain, scope.allowed_commands) do
109
- Storehouse.command_guide(boot(scope.default_domain), scope.allowed_commands)
110
- end
111
- rescue StandardError
112
- nil
113
- end
114
-
115
- def tool_result(payload)
116
- { content: [{ type: "text", text: JSON.pretty_generate(payload) }], isError: payload[:ok] == false }
117
- end
118
-
119
- def send_response(id, result)
120
- puts JSON.generate({ jsonrpc: "2.0", id: id, result: result })
121
- end
122
-
123
- def send_error(id, code, message)
124
- puts JSON.generate({ jsonrpc: "2.0", id: id, error: { code: code, message: message } })
125
- end
126
-
127
- # Answers one JSON-RPC request. A notification has no id and gets no response.
128
- def handle(request)
129
- id = request["id"]
130
- method = request["method"]
131
- reply = REPLIES[method]
132
- return send_response(id, public_send(reply, request["params"] || {})) if reply
133
-
134
- send_error(id, -32_601, "Method not found: #{method}") unless id.nil? || method == "notifications/initialized"
135
- end
136
-
137
- def initialize_result(_params)
138
- { protocolVersion: PROTOCOL_VERSION, capabilities: { tools: {} },
139
- serverInfo: { name: CLI::Mcp::SERVER, version: "1.1.0" } }
140
- end
141
-
142
- # The tools this door's scope permits, each presented with the command guide.
143
- def tools_list(_params)
144
- served = TOOLS.select { |tool| scope.permits_tool?(tool[:name]) }
145
- guide = command_guide
146
- { tools: served.map { |tool| scope.present(tool, guide) } }
147
- end
148
-
149
- def tools_call(params)
150
- tool_result(answer_tool(params["name"], params["arguments"] || {}))
151
- end
152
-
153
- def ping_result(_params) = {}
154
- end
155
- end
156
- end
@@ -1,92 +0,0 @@
1
- require_relative "../../runtime/errors"
2
-
3
- module Hecks
4
- module Doors
5
- class McpDoorScope
6
- # What a restricted door lets an argument say: a denied name is refused, a path must resolve
7
- # inside the root with symlinks followed, and a git ref must be a plain name.
8
- module ArgumentPolicy
9
- module_function
10
-
11
- # Checks every argument in `args`, nested or not, and refuses the first one the policy
12
- # forbids.
13
- #
14
- # @param args [Hash, Array, nil] the arguments as the caller gave them
15
- # @param label [String] how the door describes its mode, for the refusal
16
- # @raise [Runtime::TypeMismatch] on the first argument refused
17
- def admit!(args, label)
18
- each_argument(args) do |name, value|
19
- if DENIED_ARGUMENTS.include?(name)
20
- refuse!(name, label, "never passes it on: it names a host, a binary, an output or a change of state")
21
- end
22
- admit_path!(name, value, label) if PATH_ARGUMENTS.include?(name)
23
- admit_ref!(name, value, label) if REF_ARGUMENTS.include?(name)
24
- end
25
- end
26
-
27
- # Yields every argument name with its value, descending into nested objects and lists, so
28
- # `{"file" => {"value" => "x"}}` is checked the way `{"file" => "x"}` is.
29
- def each_argument(args, &visit)
30
- case args
31
- when Hash
32
- args.each do |name, value|
33
- yield(name.to_s, value)
34
- each_argument(value, &visit)
35
- end
36
- when Array
37
- args.each { |item| each_argument(item, &visit) }
38
- end
39
- end
40
-
41
- # A value written as an object of one field (`{"value" => "x"}`) is that field's value.
42
- def scalar(value)
43
- return value unless value.is_a?(Hash)
44
-
45
- value.fetch("value") { value.fetch(:value, value) }
46
- end
47
-
48
- def refuse!(name, label, why)
49
- raise Runtime::TypeMismatch, "argument: #{name.inspect} is refused: this door runs in #{label} and #{why}"
50
- end
51
-
52
- def admit_path!(name, value, label)
53
- Array(scalar(value)).flat_map { |item| item.to_s.split(",") }.reject(&:empty?).each do |path|
54
- next if !path.include?(":") && inside_root?(path)
55
-
56
- refuse!(name, label, "passes a path only when it resolves inside #{Storehouse::BOOT_ROOT} " \
57
- "and holds no colon: #{path.inspect}")
58
- end
59
- end
60
-
61
- def admit_ref!(name, value, label)
62
- ref = scalar(value).to_s
63
- return if ref.match?(PLAIN_REF) && !ref.include?("..")
64
-
65
- refuse!(name, label, "passes only a plain git ref: #{ref.inspect}")
66
- end
67
-
68
- def inside_root?(path)
69
- root = resolve_real(Storehouse::BOOT_ROOT)
70
- resolved = resolve_real(File.expand_path(path, Storehouse::BOOT_ROOT))
71
- resolved == root || resolved.start_with?("#{root}#{File::SEPARATOR}")
72
- end
73
-
74
- # The real path of `path`, symlinks followed: of the whole path when it exists, else of its
75
- # deepest existing parent with the missing names put back, so a link above a file that does
76
- # not exist yet still shows where the file would land.
77
- def resolve_real(path)
78
- missing = []
79
- current = File.expand_path(path)
80
- until File.exist?(current)
81
- parent = File.dirname(current)
82
- break if parent == current
83
-
84
- missing.unshift(File.basename(current))
85
- current = parent
86
- end
87
- File.join(File.realpath(current), *missing)
88
- end
89
- end
90
- end
91
- end
92
- end
@@ -1,55 +0,0 @@
1
- module Hecks
2
- module Doors
3
- class McpDoorScope
4
- # What is wrong with the `HECKS_DOOR_*` variables a door was spawned with, as the sentences
5
- # that refuse to start it.
6
- module EnvProblems
7
- module_function
8
-
9
- # Every problem in `env`: a variable the door does not take, then those of the mode.
10
- def setting_problems(env)
11
- known = [TOOLS_VARIABLE, DOMAINS_VARIABLE, COMMANDS_VARIABLE]
12
- problems = (env.keys.select { |name| name.start_with?(ENV_PREFIX) } - known).map do |name|
13
- "environment #{name} is not accepted; the door takes only #{known.join(", ")}"
14
- end
15
- problems + mode_problems(env[TOOLS_VARIABLE], env[DOMAINS_VARIABLE], env[COMMANDS_VARIABLE])
16
- end
17
-
18
- def mode_problems(tools, domains, commands)
19
- return stray_problems(domains, commands) if tools.nil?
20
- unless MODES.include?(tools)
21
- return ["#{TOOLS_VARIABLE}=#{tools.inspect} is not accepted; the values are #{MODES.map(&:inspect).join(" and ")}"]
22
- end
23
-
24
- problems = []
25
- if split_paths(domains.to_s).empty?
26
- problems << "#{TOOLS_VARIABLE}=#{tools} needs #{DOMAINS_VARIABLE}, the domains this door may read"
27
- end
28
- problems + command_problems(tools, commands)
29
- end
30
-
31
- def stray_problems(domains, commands)
32
- stray = { DOMAINS_VARIABLE => domains, COMMANDS_VARIABLE => commands }.compact.keys
33
- return [] if stray.empty?
34
-
35
- ["#{stray.join(" and ")} set without #{TOOLS_VARIABLE}=#{READERS} or #{TOOLS_VARIABLE}=#{COMMANDS}; " \
36
- "they only narrow a restricted door"]
37
- end
38
-
39
- def command_problems(tools, commands)
40
- if tools == COMMANDS && split_names(commands).empty?
41
- ["#{TOOLS_VARIABLE}=#{COMMANDS} needs #{COMMANDS_VARIABLE}, the commands this door may dispatch"]
42
- elsif tools == READERS && !commands.nil?
43
- ["#{COMMANDS_VARIABLE} is set in reader mode; it only narrows a commands door"]
44
- else
45
- []
46
- end
47
- end
48
-
49
- def split_paths(value) = value.to_s.split(File::PATH_SEPARATOR).reject(&:empty?)
50
-
51
- def split_names(value) = value.to_s.split(",").map(&:strip).reject(&:empty?).uniq
52
- end
53
- end
54
- end
55
- end
@@ -1,78 +0,0 @@
1
- module Hecks
2
- module Doors
3
- class McpDoorScope
4
- # A tool definition as `tools/list` shows it to one door, and the notes the door starts with.
5
- module Presenter
6
- module_function
7
-
8
- # The tool as `tools/list` shows it. A door that serves one domain makes `domain:` optional.
9
- # On a commands door, `dispatch` also names the commands it serves as an enum, so a caller
10
- # sees them as typed choices instead of finding out by being refused, and says what each
11
- # takes and which role it declares.
12
- #
13
- # @param scope [McpDoorScope] the door's scope
14
- # @param tool [Hash] a tool definition from `McpDoor::TOOLS`
15
- # @param guide [Array<String>, nil] one line per allowed command
16
- # (`Storehouse.command_guide`)
17
- # @return [Hash] the definition, narrowed for this door
18
- def present(scope, tool, guide)
19
- tool = domain_optional(tool, scope.default_domain) if scope.default_domain
20
- return tool unless scope.commands_mode? && tool[:name] == "dispatch"
21
-
22
- narrow_dispatch(tool, guide, scope.allowed_commands)
23
- end
24
-
25
- # `dispatch`, with the commands the door serves as typed choices.
26
- def narrow_dispatch(tool, guide, commands)
27
- properties = tool[:inputSchema][:properties]
28
- steps = properties[:steps]
29
- narrowed = properties.merge(command: with_enum(properties[:command], commands),
30
- steps: steps.merge(items: step_items(steps[:items], commands)))
31
- tool.merge(description: dispatch_description(tool[:description], guide, commands),
32
- inputSchema: tool[:inputSchema].merge(properties: narrowed))
33
- end
34
-
35
- def domain_optional(tool, default)
36
- schema = tool[:inputSchema]
37
- return tool unless schema[:properties].key?(:domain)
38
-
39
- note = "Optional: this door serves one domain, #{default}, and uses it when you leave this out."
40
- properties = schema[:properties].merge(domain: schema[:properties][:domain].merge(description: note))
41
- tool.merge(inputSchema: schema.merge(properties: properties, required: schema[:required] - ["domain"]))
42
- end
43
-
44
- def dispatch_description(base, guide, commands)
45
- intro = "#{base} This door dispatches only the commands below, each with the role it declares: pass " \
46
- "that role as `role`. A command that takes `run` takes a key you choose, and the call answers " \
47
- "the record as it stands once its reactions have run; an argument marked * is required."
48
- entries = Array(guide).empty? ? commands : guide
49
- ([intro] + entries.map { |entry| "- #{entry}" }).join("\n")
50
- end
51
-
52
- def with_enum(property, commands)
53
- property.merge(enum: commands)
54
- end
55
-
56
- def step_items(items, commands)
57
- items.merge(properties: items[:properties].merge(command: with_enum(items[:properties][:command], commands)))
58
- end
59
-
60
- # What a reader door says about itself when it starts.
61
- def reader_notes(domains)
62
- ["Reader mode (#{TOOLS_VARIABLE}=#{READERS}): serves #{READER_TOOLS.join(", ")}; " \
63
- "refuses dispatch (with dry_run and steps), behaviors and every other tool.",
64
- "domain: boots only #{DOMAINS_VARIABLE}: #{domains.join(", ")}.",
65
- "Reader mode limits reach and identifies no one; it is not authentication."]
66
- end
67
-
68
- # What a commands door says about itself when it starts.
69
- def commands_notes(domains, commands)
70
- ["Commands mode (#{TOOLS_VARIABLE}=#{COMMANDS}): serves #{READER_TOOLS.join(", ")} and dispatch of only " \
71
- "#{COMMANDS_VARIABLE}: #{commands.join(", ")}; refuses behaviors and every other tool.",
72
- "domain: boots only #{DOMAINS_VARIABLE}: #{domains.join(", ")}.",
73
- "Commands mode limits reach and identifies no one; role: stays self-asserted and it is not authentication."]
74
- end
75
- end
76
- end
77
- end
78
- end
@@ -1,201 +0,0 @@
1
- require_relative "../runtime/errors"
2
-
3
- module Hecks
4
- module Doors
5
- # What a spawned hecks mcp may do: every tool by default; only reader tools against named
6
- # domains in reader mode; and in commands mode those plus `dispatch` of a closed list of
7
- # commands — reach, not identity (ADR 0072, ADR 0089).
8
- class McpDoorScope
9
- ENV_PREFIX = "HECKS_DOOR_".freeze
10
- TOOLS_VARIABLE = "HECKS_DOOR_TOOLS".freeze
11
- DOMAINS_VARIABLE = "HECKS_DOOR_DOMAINS".freeze
12
- COMMANDS_VARIABLE = "HECKS_DOOR_COMMANDS".freeze
13
- READERS = "readers".freeze
14
- COMMANDS = "commands".freeze
15
- MODES = [READERS, COMMANDS].freeze
16
- EXIT_STATUS = 2
17
-
18
- # The tools a reader door serves — each reads a domain or the door's own
19
- # audit log and writes no domain state. `validate` still boots its
20
- # domain, so it stays bound to the allowed set like every other tool.
21
- READER_TOOLS = %w[query events state catalog describe validate domains history follow].freeze
22
-
23
- # The tools a commands door serves: the reader tools, and `dispatch` for the allowed commands.
24
- COMMAND_TOOLS = (READER_TOOLS + %w[dispatch]).freeze
25
-
26
- # Argument names a restricted door never passes on, whatever command carries them: each one
27
- # names a host or a URL, a binary to run, a place to write, a port to open, a store to
28
- # switch to, or flips a command from a preview to a change of state.
29
- DENIED_ARGUMENTS = %w[
30
- adapter artifact confirm expected from gem_only header health_path host live no_wait npm_local
31
- npm_only output path payload payload_file port rust_binary scheme stage_dir state_path stdio url
32
- write
33
- ].freeze
34
-
35
- # Argument names whose values are paths (a comma-separated list for some). A restricted door
36
- # passes them on only when each resolves, symlinks followed, inside `Storehouse::BOOT_ROOT`,
37
- # and holds no colon: `host:repo` and `https://host/x` would otherwise pass as relative paths.
38
- PATH_ARGUMENTS = %w[dir domain domains file fixture paths root script].freeze
39
-
40
- # Argument names whose values are git refs: plain names only, so a value cannot read as an
41
- # option to git.
42
- REF_ARGUMENTS = %w[commit ref].freeze
43
-
44
- # What a plain git ref looks like: no leading dash, no `..`, no spaces.
45
- PLAIN_REF = %r{\A[A-Za-z0-9][A-Za-z0-9._/~^@-]*\z}
46
-
47
- # Unrestricted when no HECKS_DOOR_* variable is set.
48
- def self.from_env(env = ENV)
49
- problems = EnvProblems.setting_problems(env)
50
- raise ArgumentError, problems.join("\n") unless problems.empty?
51
- return new(allowed_domains: nil) unless env[TOOLS_VARIABLE]
52
-
53
- commands = env[TOOLS_VARIABLE] == COMMANDS ? EnvProblems.split_names(env[COMMANDS_VARIABLE]) : nil
54
- new(allowed_domains: EnvProblems.split_paths(env[DOMAINS_VARIABLE]), allowed_commands: commands)
55
- end
56
-
57
- # Refuses to stderr, not stdout, which carries the MCP protocol.
58
- def self.start!(server:, env: ENV, stderr: $stderr)
59
- from_env(env)
60
- rescue ArgumentError, Runtime::TypeMismatch => e
61
- e.message.each_line { |line| stderr.puts("#{server}: refusing to start: #{line.chomp}") }
62
- exit(EXIT_STATUS)
63
- end
64
-
65
- # The named domains, resolved against Storehouse::BOOT_ROOT; nil when unrestricted.
66
- attr_reader :allowed_domains
67
-
68
- # The command names a commands door dispatches, as the spawner wrote them; nil unless
69
- # the door runs in commands mode.
70
- attr_reader :allowed_commands
71
-
72
- def initialize(allowed_domains:, allowed_commands: nil)
73
- @allowed_domains = allowed_domains&.map { |path| Storehouse.confine!(path, DOMAINS_VARIABLE) }.freeze
74
- @allowed_commands = allowed_commands&.dup&.freeze
75
- end
76
-
77
- def restricted?
78
- !@allowed_domains.nil?
79
- end
80
-
81
- def commands_mode?
82
- !@allowed_commands.nil?
83
- end
84
-
85
- def served_tools
86
- commands_mode? ? COMMAND_TOOLS : READER_TOOLS
87
- end
88
-
89
- def permits_tool?(name)
90
- !restricted? || served_tools.include?(name)
91
- end
92
-
93
- def refusal(name)
94
- { ok: false,
95
- error: "#{name.inspect} is refused: this door runs in #{mode_label}, " \
96
- "which serves only #{served_tools.join(", ")}" }
97
- end
98
-
99
- # Checked before anything boots, so a path outside the allowed set never reaches Kernel.load.
100
- def admit_domain!(domain)
101
- return domain unless restricted?
102
- return domain if @allowed_domains.include?(Storehouse.confine!(domain, "domain"))
103
-
104
- raise Runtime::TypeMismatch,
105
- "domain: #{domain.inspect} is refused: this door runs in #{mode_label} " \
106
- "and reads only #{DOMAINS_VARIABLE}: #{@allowed_domains.join(", ")}"
107
- end
108
-
109
- # A commands door dispatches only the commands it was given. A requested name and each
110
- # allowed name resolve through the alias map `dispatch` uses, and the resolved verbs are
111
- # compared, so `complete` cannot reach a command of another aggregate that shares its short
112
- # name, and an allowed name that resolves to nothing admits nothing.
113
- #
114
- # @param runtime [Runtime::Dispatcher] the booted domain
115
- # @param command [String, nil] the command name the caller asked for
116
- # @param args [Hash, nil] the arguments the caller gave it; see `admit_arguments!`
117
- # @raise [Runtime::TypeMismatch] when the command is not on the list or an argument is refused
118
- def admit_command!(runtime, command, args = nil)
119
- return unless commands_mode?
120
-
121
- verbs = Storehouse.verbs_for(runtime, [command, *allowed_commands])
122
- unless verbs.first && verbs.drop(1).include?(verbs.first)
123
- raise Runtime::TypeMismatch,
124
- "command: #{command.to_s.inspect} is refused: this door runs in #{mode_label} " \
125
- "and dispatches only #{COMMANDS_VARIABLE}: #{allowed_commands.join(", ")}"
126
- end
127
-
128
- admit_arguments!(args)
129
- end
130
-
131
- # Every step of a batch is admitted before any step runs.
132
- #
133
- # @param runtime [Runtime::Dispatcher] the booted domain
134
- # @param steps [Array<Hash>] the batch, each `{"command" => ..., "args" => ...}`
135
- # @raise [Runtime::TypeMismatch] when a step names a command off the list or a bad argument
136
- def admit_steps!(runtime, steps)
137
- Array(steps).each do |step|
138
- step = {} unless step.is_a?(Hash)
139
- admit_command!(runtime, step["command"], step["args"])
140
- end
141
- end
142
-
143
- # The arguments a restricted door is about to pass to a command or a question. A command
144
- # list admits commands, not values, so each argument is checked by its name: a denied name
145
- # is refused, a path must resolve inside the root with symlinks followed, and a git ref must
146
- # be a plain name. Other names pass; a domain with an argument that reaches outside its own
147
- # files should name it in `DENIED_ARGUMENTS` or `PATH_ARGUMENTS` (ADR 0089).
148
- #
149
- # @param args [Hash, Array, nil] the arguments as the caller gave them, nested or not
150
- # @raise [Runtime::TypeMismatch] on the first argument refused
151
- def admit_arguments!(args)
152
- return unless restricted?
153
-
154
- ArgumentPolicy.admit!(args, mode_label)
155
- end
156
-
157
- # The one domain a restricted door serves, when it serves exactly one: a call may leave
158
- # `domain:` out and get it. Nil for an unrestricted door and for one that names several.
159
- #
160
- # @return [String, nil] the domain path as `HECKS_DOOR_DOMAINS` named it, resolved
161
- def default_domain
162
- @allowed_domains.first if restricted? && @allowed_domains.size == 1
163
- end
164
-
165
- # The arguments with the default domain filled in when the caller left it out.
166
- #
167
- # @param args [Hash] a tool call's arguments
168
- # @return [Hash] `args`, with `"domain"` set when the door has a default and none was given
169
- def with_default_domain(args)
170
- return args unless default_domain && args["domain"].to_s.strip.empty?
171
-
172
- args.merge("domain" => default_domain)
173
- end
174
-
175
- # The tool as `tools/list` shows it; see `Presenter.present`.
176
- #
177
- # @param tool [Hash] a tool definition from `McpDoor::TOOLS`
178
- # @param guide [Array<String>, nil] one line per allowed command (`Storehouse.command_guide`)
179
- # @return [Hash] the definition, narrowed for this door
180
- def present(tool, guide = nil)
181
- Presenter.present(self, tool, guide)
182
- end
183
-
184
- def notes
185
- return [] unless restricted?
186
-
187
- commands_mode? ? Presenter.commands_notes(@allowed_domains, @allowed_commands) : Presenter.reader_notes(@allowed_domains)
188
- end
189
-
190
- private
191
-
192
- def mode_label
193
- commands_mode? ? "commands mode (#{TOOLS_VARIABLE}=#{COMMANDS})" : "reader mode (#{TOOLS_VARIABLE}=#{READERS})"
194
- end
195
- end
196
- end
197
- end
198
-
199
- require_relative "mcp_door_scope/env_problems"
200
- require_relative "mcp_door_scope/argument_policy"
201
- require_relative "mcp_door_scope/presenter"
@@ -1,75 +0,0 @@
1
- require "digest"
2
- require "fileutils"
3
- require "json"
4
- require_relative "../cache_dir"
5
- require_relative "../storehouse"
6
- require_relative "../bluebook/meta_validator/verdict_cache"
7
-
8
- module Hecks
9
- module Doors
10
- # Remembers the command guide a commands door builds for `tools/list`, so a door that starts
11
- # again does not boot its domain to describe its commands. A client waits only a moment for a
12
- # server's tools, and a boot at that point costs more than the wait.
13
- #
14
- # An entry is keyed by the domain directory's files, a digest of the hecks code, and the
15
- # allowed commands, so a change to any of them builds a new guide. The file is JSON, read only
16
- # from a directory and a file that only this user can write (the verdict cache's own check),
17
- # and only when it is a list of strings: a guide is text an agent reads as instructions.
18
- module McpGuideCache
19
- module_function
20
-
21
- # The guide for a domain and its allowed commands, from the cache or else from the block,
22
- # which is then remembered. A cache that cannot be written is not an error: the next start
23
- # builds the guide again.
24
- #
25
- # @param domain [String] the domain directory the guide describes
26
- # @param commands [Array<String>] the allowed command names
27
- # @yieldreturn [Array<String>, nil] the guide, built when none is remembered
28
- # @return [Array<String>, nil] the guide
29
- def remember(domain, commands)
30
- file = File.join(dir, "#{key(domain, commands)}.json")
31
- cached = read(file)
32
- return cached if cached
33
-
34
- guide = yield
35
- write(file, guide) if guide.is_a?(Array) && guide.any?
36
- guide
37
- end
38
-
39
- # @return [String] the directory entries live in
40
- def dir = Hecks::CacheDir.path("mcp_door_guides")
41
-
42
- # @param domain [String] the domain directory
43
- # @param commands [Array<String>] the allowed command names
44
- # @return [String] a hex SHA-256 over the domain's files, the hecks code and the commands
45
- def key(domain, commands)
46
- parts = [Storehouse.fingerprint(domain), Bluebook::MetaValidator::VerdictCache.code_digest,
47
- commands.sort.join(",")]
48
- Digest::SHA256.hexdigest(parts.join("\0"))
49
- end
50
-
51
- # @param file [String] an entry's path
52
- # @return [Array<String>, nil] the guide, or nil when absent, untrusted or malformed
53
- def read(file)
54
- return unless File.file?(file) && Bluebook::MetaValidator::VerdictCache.trusted?(file)
55
-
56
- guide = JSON.parse(File.read(file))
57
- guide if guide.is_a?(Array) && guide.all?(String)
58
- rescue StandardError
59
- nil
60
- end
61
-
62
- # @param file [String] an entry's path
63
- # @param guide [Array<String>] the guide to remember
64
- # @return [void]
65
- def write(file, guide)
66
- FileUtils.mkdir_p(dir, mode: 0o700)
67
- temp = "#{file}.#{Process.pid}.tmp"
68
- File.write(temp, JSON.generate(guide), perm: 0o600)
69
- File.rename(temp, file)
70
- rescue StandardError
71
- FileUtils.rm_f(temp) if temp
72
- end
73
- end
74
- end
75
- end