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
@@ -0,0 +1,213 @@
1
+ require_relative "../../runtime/errors"
2
+
3
+ module Hecks
4
+ module Adapters
5
+ module Driving
6
+ # What a spawned hecks mcp may do: every tool by default; only reader tools against named
7
+ # domains in reader mode; and in commands mode those plus `dispatch` of a closed list of
8
+ # commands — reach, not identity (ADR 0072, ADR 0089).
9
+ class McpScope
10
+ ENV_PREFIX = "HECKS_SERVER_".freeze
11
+ TOOLS_VARIABLE = "HECKS_SERVER_TOOLS".freeze
12
+ DOMAINS_VARIABLE = "HECKS_SERVER_DOMAINS".freeze
13
+ COMMANDS_VARIABLE = "HECKS_SERVER_COMMANDS".freeze
14
+ READERS = "readers".freeze
15
+ COMMANDS = "commands".freeze
16
+ MODES = [READERS, COMMANDS].freeze
17
+ EXIT_STATUS = 2
18
+
19
+ # The tools a reader server serves — each reads a domain or the server's own
20
+ # audit log and writes no domain state. `validate` still boots its
21
+ # domain, so it stays bound to the allowed set like every other tool.
22
+ READER_TOOLS = %w[query events state catalog describe validate domains history follow].freeze
23
+
24
+ # The tools a commands server serves: the reader tools, and `dispatch` for the allowed
25
+ # commands.
26
+ COMMAND_TOOLS = (READER_TOOLS + %w[dispatch]).freeze
27
+
28
+ # Argument names a restricted server never passes on, whatever command carries them: each
29
+ # one
30
+ # names a host or a URL, a binary to run, a place to write, a port to open, a store to
31
+ # switch to, or flips a command from a preview to a change of state.
32
+ DENIED_ARGUMENTS = %w[
33
+ adapter artifact confirm expected from gem_only header health_path host live no_wait npm_local
34
+ npm_only output path payload payload_file port rust_binary scheme stage_dir state_path stdio url
35
+ write
36
+ ].freeze
37
+
38
+ # Argument names whose values are paths (a comma-separated list for some). A restricted
39
+ # server
40
+ # passes them on only when each resolves, symlinks followed, inside `Storehouse::BOOT_ROOT`,
41
+ # and holds no colon: `host:repo` and `https://host/x` would otherwise pass as relative
42
+ # paths.
43
+ PATH_ARGUMENTS = %w[dir domain domains file fixture paths root script].freeze
44
+
45
+ # Argument names whose values are git refs: plain names only, so a value cannot read as an
46
+ # option to git.
47
+ REF_ARGUMENTS = %w[commit ref].freeze
48
+
49
+ # What a plain git ref looks like: no leading dash, no `..`, no spaces.
50
+ PLAIN_REF = %r{\A[A-Za-z0-9][A-Za-z0-9._/~^@-]*\z}
51
+
52
+ # Unrestricted when no HECKS_SERVER_* variable is set.
53
+ def self.from_env(env = ENV)
54
+ problems = EnvProblems.setting_problems(env)
55
+ raise ArgumentError, problems.join("\n") unless problems.empty?
56
+ return new(allowed_domains: nil) unless env[TOOLS_VARIABLE]
57
+
58
+ commands = env[TOOLS_VARIABLE] == COMMANDS ? EnvProblems.split_names(env[COMMANDS_VARIABLE]) : nil
59
+ new(allowed_domains: EnvProblems.split_paths(env[DOMAINS_VARIABLE]), allowed_commands: commands)
60
+ end
61
+
62
+ # Refuses to stderr, not stdout, which carries the MCP protocol.
63
+ def self.start!(server:, env: ENV, stderr: $stderr)
64
+ from_env(env)
65
+ rescue ArgumentError, Runtime::TypeMismatch => e
66
+ e.message.each_line { |line| stderr.puts("#{server}: refusing to start: #{line.chomp}") }
67
+ exit(EXIT_STATUS)
68
+ end
69
+
70
+ # The named domains, resolved against Storehouse::BOOT_ROOT; nil when unrestricted.
71
+ attr_reader :allowed_domains
72
+
73
+ # The command names a commands server dispatches, as the spawner wrote them; nil unless
74
+ # the server runs in commands mode.
75
+ attr_reader :allowed_commands
76
+
77
+ def initialize(allowed_domains:, allowed_commands: nil)
78
+ @allowed_domains = allowed_domains&.map { |path| Storehouse.confine!(path, DOMAINS_VARIABLE) }.freeze
79
+ @allowed_commands = allowed_commands&.dup&.freeze
80
+ end
81
+
82
+ def restricted?
83
+ !@allowed_domains.nil?
84
+ end
85
+
86
+ def commands_mode?
87
+ !@allowed_commands.nil?
88
+ end
89
+
90
+ def served_tools
91
+ commands_mode? ? COMMAND_TOOLS : READER_TOOLS
92
+ end
93
+
94
+ def permits_tool?(name)
95
+ !restricted? || served_tools.include?(name)
96
+ end
97
+
98
+ def refusal(name)
99
+ { ok: false,
100
+ error: "#{name.inspect} is refused: this server runs in #{mode_label}, " \
101
+ "which serves only #{served_tools.join(", ")}" }
102
+ end
103
+
104
+ # Checked before anything boots, so a path outside the allowed set never reaches
105
+ # Kernel.load.
106
+ def admit_domain!(domain)
107
+ return domain unless restricted?
108
+ return domain if @allowed_domains.include?(Storehouse.confine!(domain, "domain"))
109
+
110
+ raise Runtime::TypeMismatch,
111
+ "domain: #{domain.inspect} is refused: this server runs in #{mode_label} " \
112
+ "and reads only #{DOMAINS_VARIABLE}: #{@allowed_domains.join(", ")}"
113
+ end
114
+
115
+ # A commands server dispatches only the commands it was given. A requested name and each
116
+ # allowed name resolve through the alias map `dispatch` uses, and the resolved verbs are
117
+ # compared, so `complete` cannot reach a command of another aggregate that shares its short
118
+ # name, and an allowed name that resolves to nothing admits nothing.
119
+ #
120
+ # @param runtime [Runtime::Dispatcher] the booted domain
121
+ # @param command [String, nil] the command name the caller asked for
122
+ # @param args [Hash, nil] the arguments the caller gave it; see `admit_arguments!`
123
+ # @raise [Runtime::TypeMismatch] when the command is not on the list or an argument is
124
+ # refused
125
+ def admit_command!(runtime, command, args = nil)
126
+ return unless commands_mode?
127
+
128
+ verbs = Storehouse.verbs_for(runtime, [command, *allowed_commands])
129
+ unless verbs.first && verbs.drop(1).include?(verbs.first)
130
+ raise Runtime::TypeMismatch,
131
+ "command: #{command.to_s.inspect} is refused: this server runs in #{mode_label} " \
132
+ "and dispatches only #{COMMANDS_VARIABLE}: #{allowed_commands.join(", ")}"
133
+ end
134
+
135
+ admit_arguments!(args)
136
+ end
137
+
138
+ # Every step of a batch is admitted before any step runs.
139
+ #
140
+ # @param runtime [Runtime::Dispatcher] the booted domain
141
+ # @param steps [Array<Hash>] the batch, each `{"command" => ..., "args" => ...}`
142
+ # @raise [Runtime::TypeMismatch] when a step names a command off the list or a bad argument
143
+ def admit_steps!(runtime, steps)
144
+ Array(steps).each do |step|
145
+ step = {} unless step.is_a?(Hash)
146
+ admit_command!(runtime, step["command"], step["args"])
147
+ end
148
+ end
149
+
150
+ # The arguments a restricted server is about to pass to a command or a question. A command
151
+ # list admits commands, not values, so each argument is checked by its name: a denied name
152
+ # is refused, a path must resolve inside the root with symlinks followed, and a git ref must
153
+ # be a plain name. Other names pass; a domain with an argument that reaches outside its own
154
+ # files should name it in `DENIED_ARGUMENTS` or `PATH_ARGUMENTS` (ADR 0089).
155
+ #
156
+ # @param args [Hash, Array, nil] the arguments as the caller gave them, nested or not
157
+ # @raise [Runtime::TypeMismatch] on the first argument refused
158
+ def admit_arguments!(args)
159
+ return unless restricted?
160
+
161
+ ArgumentPolicy.admit!(args, mode_label)
162
+ end
163
+
164
+ # The one domain a restricted server serves, when it serves exactly one: a call may leave
165
+ # `domain:` out and get it. Nil for an unrestricted server and for one that names several.
166
+ #
167
+ # @return [String, nil] the domain path as `HECKS_SERVER_DOMAINS` named it, resolved
168
+ def default_domain
169
+ @allowed_domains.first if restricted? && @allowed_domains.size == 1
170
+ end
171
+
172
+ # The arguments with the default domain filled in when the caller left it out.
173
+ #
174
+ # @param args [Hash] a tool call's arguments
175
+ # @return [Hash] `args`, with `"domain"` set when the server has a default and none was
176
+ # given
177
+ def with_default_domain(args)
178
+ return args unless default_domain && args["domain"].to_s.strip.empty?
179
+
180
+ args.merge("domain" => default_domain)
181
+ end
182
+
183
+ # The tool as `tools/list` shows it; see `Presenter.present`.
184
+ #
185
+ # @param tool [Hash] a tool definition from `Mcp::TOOLS`
186
+ # @param guide [Array<String>, nil] one line per allowed command
187
+ # (`Storehouse.command_guide`)
188
+ # @return [Hash] the definition, narrowed for this server
189
+ def present(tool, guide = nil)
190
+ Presenter.present(self, tool, guide)
191
+ end
192
+
193
+ def notes
194
+ return [] unless restricted?
195
+
196
+ return Presenter.reader_notes(@allowed_domains) unless commands_mode?
197
+
198
+ Presenter.commands_notes(@allowed_domains, @allowed_commands)
199
+ end
200
+
201
+ private
202
+
203
+ def mode_label
204
+ commands_mode? ? "commands mode (#{TOOLS_VARIABLE}=#{COMMANDS})" : "reader mode (#{TOOLS_VARIABLE}=#{READERS})"
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end
210
+
211
+ require_relative "mcp_scope/env_problems"
212
+ require_relative "mcp_scope/argument_policy"
213
+ require_relative "mcp_scope/presenter"
@@ -0,0 +1,171 @@
1
+ require_relative "../../../bluebook/dsl/hecksagon_builder"
2
+ require_relative "../../../bluebook/dsl/world_builder"
3
+ require_relative "../../../bluebook/dsl/domain_port_builder"
4
+ require_relative "../../../bluebook/dsl/const_shim"
5
+ require_relative "../../../bluebook/dsl/binding_proxy"
6
+ require_relative "../../../bluebook/hecksagon"
7
+ require_relative "../handle"
8
+ require_relative "../../../naming"
9
+
10
+ module Hecks
11
+ module Adapters
12
+ module Driving
13
+ module Ruby
14
+ # One aggregate module: creating verbs and queries as module methods, CRUD
15
+ # delegation, and the `.hecksagon` binding/port hooks it lands on.
16
+ module AggregateModule
17
+ # Builds one aggregate module: creating-command methods, query methods,
18
+ # find/all/count/events, project/docs/narrate, and the port/binding hooks.
19
+ #
20
+ # Kept as one method: `:port`/`:method_missing`/`:const_missing` below
21
+ # cross-reference each other and would lose that thread if split up.
22
+ # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
23
+ def aggregate_module(dispatcher, domain, aggregate)
24
+ fqn = "#{domain}::#{aggregate.hecks_name}"
25
+ mod = Module.new
26
+
27
+ aggregate.attributes.each do |attribute|
28
+ next unless RESERVED.include?(attribute.name.to_sym)
29
+
30
+ warn "[hecks] #{aggregate.hecks_name}##{attribute.name} shadows a built-in — no reader defined"
31
+ end
32
+
33
+ # `!` marks a creating command (Ruby's mutate-and-may-refuse convention).
34
+ # Without it, a query sharing the same business name (e.g. Account's
35
+ # "Open") would collide with the creating command's own bare name.
36
+ aggregate.commands.select(&:creates?).each do |command|
37
+ mod.define_singleton_method("#{Naming.snake(command.hecks_name)}!") do |**args|
38
+ Handle.new(dispatcher: dispatcher, domain: domain, aggregate: aggregate,
39
+ instance: dispatcher.dispatch("#{fqn}.#{command.hecks_name}", with: args).instance)
40
+ end
41
+ end
42
+
43
+ # A query is a bare module method (no `!`): it just reads and returns
44
+ # the same raw row array `dispatcher.query` itself answers.
45
+ aggregate.queries.each do |query|
46
+ mod.define_singleton_method(Naming.snake(query.hecks_name)) do |**args|
47
+ dispatcher.query("#{fqn}.#{query.hecks_name}", **args)
48
+ end
49
+ end
50
+
51
+ mod.define_singleton_method(:fqn) { fqn }
52
+ mod.define_singleton_method(:ir) { aggregate }
53
+
54
+ # Every construct emits its own IR, so an aggregate is a legitimate
55
+ # `project` target too. A chapter-scoped target refuses here via
56
+ # `Projector.admits!` rather than answering a confidently empty result.
57
+ mod.define_singleton_method(:project) do |target, out: nil, **options|
58
+ key = Projector.key_for(target)
59
+ artifact = Projector.call(key, bluebook: aggregate, options: options)
60
+ return artifact unless out
61
+
62
+ Projector.write(artifact, out, as: Projector.emits_for(key))
63
+ end
64
+ mod.define_singleton_method(:repository) { dispatcher.registry.repository(domain, aggregate) }
65
+ mod.define_singleton_method(:commands) do
66
+ aggregate.commands.map { |c| "#{Naming.snake(c.hecks_name)}!" }.sort
67
+ end
68
+ mod.define_singleton_method(:queries) { aggregate.queries.map { |q| Naming.snake(q.hecks_name) }.sort }
69
+ # This chapter's own `:docs` projection, narrowed to this aggregate.
70
+ mod.define_singleton_method(:docs) do |**options|
71
+ Projector.call(:docs, bluebook: dispatcher.registry.bluebook(domain),
72
+ options: options.merge(aggregate: aggregate.hecks_name))
73
+ end
74
+ # Same narrowing as `:docs`, aimed at `:narrate`.
75
+ mod.define_singleton_method(:narrate) do |**options|
76
+ Projector.call(:narrate, bluebook: dispatcher.registry.bluebook(domain),
77
+ options: options.merge(aggregate: aggregate.hecks_name))
78
+ end
79
+ mod.define_singleton_method(:count) { dispatcher.registry.repository(domain, aggregate).count }
80
+ mod.define_singleton_method(:events) { dispatcher.events.select { |event| event.aggregate == fqn } }
81
+
82
+ mod.define_singleton_method(:find) do |id|
83
+ found = dispatcher.registry.repository(domain, aggregate).find(id)
84
+ found && Handle.new(dispatcher: dispatcher, domain: domain, aggregate: aggregate, instance: found)
85
+ end
86
+
87
+ mod.define_singleton_method(:all) do |**opts|
88
+ dispatcher.registry.repository(domain, aggregate).all(**opts).map do |instance|
89
+ Handle.new(dispatcher: dispatcher, domain: domain, aggregate: aggregate, instance: instance)
90
+ end
91
+ end
92
+
93
+ # Re-resolves via `Hecks.current_registry` rather than the closed-over
94
+ # `aggregate`: this module can be stale from an earlier boot, and its own
95
+ # `aggregate` would silently port a discarded one instead of the live one.
96
+ mod.define_singleton_method(:port) do |name, &block|
97
+ current = Hecks.current_registry&.bluebook(domain)&.aggregate(aggregate.hecks_name) or
98
+ raise Bluebook::DSL::Malformed, "#{fqn}.port(#{name.inspect}) called outside a boot"
99
+
100
+ built = Bluebook::DSL::ConstShim.with(->(const) { const }) do
101
+ Bluebook::DSL::DomainPortBuilder.build(name, owner: current.hecks_name, &block)
102
+ end
103
+
104
+ # A `verb`-shaped port is a plain `Port`, the same struct `Hecks.port` registers —
105
+ # it belongs to no aggregate IR the way an operations-shaped `DomainPort` does, so
106
+ # it takes the registry's `add_port` directly. Mirrors `DSL::BindingProxy#port`,
107
+ # which handles the identical shape on a domain's first in-process boot (before
108
+ # this facade constant exists); this method is what a repeat boot reaches instead.
109
+ if built.is_a?(Bluebook::Port)
110
+ Hecks.current_registry.add_port(built)
111
+ return self
112
+ end
113
+
114
+ built.operations.each do |operation|
115
+ operation.attributes.select(&:reference?).each { |attribute| attribute.type.declared_in = current }
116
+ end
117
+ current.add_port(built)
118
+ self
119
+ end
120
+
121
+ # `BindingProxy#ask_via` for a repeat boot, when this module stands where the proxy is.
122
+ mod.define_singleton_method(:ask_via) do |name, port:|
123
+ chapter = Hecks.current_registry&.bluebook(domain)
124
+ Bluebook::AskResolution.pick!(chapter, aggregate.hecks_name, name, port)
125
+ self
126
+ end
127
+
128
+ mod.define_singleton_method(:method_missing) do |verb, *args, **kwargs, &block|
129
+ # A bare call starts a Privacy marking chain (see
130
+ # `BindingProxy#method_missing`), reached here when the module is already
131
+ # installed (a second in-process boot) rather than at parse time.
132
+ return Bluebook::DSL::AttributePath.new(fqn, [verb.to_s]) if args.empty? && kwargs.empty? && !block
133
+
134
+ collector = Bluebook::DSL::HecksagonBuilder.collector
135
+ world = Bluebook::DSL::WorldBuilder.current
136
+ # Inside a `.world` block the same qualified spelling records a world setting.
137
+ return world.record_binding(verb, args, kwargs, block) if world && !collector
138
+ return super(verb, *args, **kwargs, &block) unless collector
139
+
140
+ collector << Bluebook::Bind.new(
141
+ aggregate: fqn,
142
+ verb: verb.to_s,
143
+ adapter: args.first.to_s,
144
+ role: kwargs[:role]&.to_s
145
+ )
146
+ block&.call
147
+ self
148
+ end
149
+
150
+ mod.define_singleton_method(:respond_to_missing?) do |name, include_private = false|
151
+ !Bluebook::DSL::HecksagonBuilder.collector.nil? || !Bluebook::DSL::WorldBuilder.current.nil? ||
152
+ super(name, include_private)
153
+ end
154
+
155
+ # Uses `aggregate.hecks_name`, not the qualified `fqn`: qualifying it here
156
+ # would make `Account::Debit`'s meaning depend on whether a stale module
157
+ # from an earlier boot happens to be sitting on this process.
158
+ mod.define_singleton_method(:const_missing) do |name|
159
+ resolver = Bluebook::DSL::ConstShim.resolver
160
+ return resolver.call("#{aggregate.hecks_name}::#{name}") if resolver
161
+
162
+ super(name)
163
+ end
164
+
165
+ mod
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,91 @@
1
+ require_relative "../../../bluebook/dsl/binding_proxy"
2
+ require_relative "../../../bluebook/dsl/const_shim"
3
+ require_relative "../../../bluebook/dsl/hecksagon_builder"
4
+
5
+ module Hecks
6
+ module Adapters
7
+ module Driving
8
+ module Ruby
9
+ # One chapter's module: vision and aggregate list on the singleton, one
10
+ # aggregate module per declared head, and a `const_missing` hook for undeclared names.
11
+ module Chapter
12
+ # Builds the anonymous module that stands for one booted chapter: `vision`,
13
+ # `aggregates`, `docs`, `narrate` and `project` as singleton methods, plus one
14
+ # nested constant per aggregate holding that aggregate module.
15
+ #
16
+ # @param dispatcher [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted
17
+ # dispatcher each aggregate module closes over
18
+ # @param bluebook [Bluebook::Chapter] the chapter to project into a module
19
+ # @return [Module] a fresh, unnamed module; `Ruby.install` gives it its
20
+ # top-level name
21
+ # @raise [NameError] if an aggregate's name is not a valid constant name
22
+ def chapter_module(dispatcher, bluebook)
23
+ chapter = Module.new
24
+ define_readers(chapter, bluebook)
25
+ define_projections(chapter, bluebook)
26
+
27
+ bluebook.aggregates.each do |aggregate|
28
+ chapter.const_set(aggregate.hecks_name, aggregate_module(dispatcher, bluebook.name, aggregate))
29
+ end
30
+
31
+ define_const_missing(chapter, bluebook)
32
+ chapter
33
+ end
34
+
35
+ private
36
+
37
+ # The chapter's vision and its aggregate names.
38
+ def define_readers(chapter, bluebook)
39
+ chapter.define_singleton_method(:vision) { bluebook.vision }
40
+ chapter.define_singleton_method(:aggregates) { bluebook.aggregates.map(&:name).sort }
41
+ end
42
+
43
+ # The chapter's documents and projections, each projected from its IR on every call so
44
+ # none can go stale.
45
+ def define_projections(chapter, bluebook)
46
+ chapter.define_singleton_method(:docs) do |**options|
47
+ Projector.call(:docs, bluebook: bluebook, options: options)
48
+ end
49
+
50
+ # The chapter read back in english, via `Projector::NarrateProjector`.
51
+ chapter.define_singleton_method(:narrate) do |**options|
52
+ Projector.call(:narrate, bluebook: bluebook, options: options)
53
+ end
54
+
55
+ define_project(chapter, bluebook)
56
+ end
57
+
58
+ # Projects the chapter's IR through a target; `out:` writes the artifact and
59
+ # the remaining options go to the projector (`audience:` for OIDC).
60
+ #
61
+ # Pizzas.project(Projections::OIDC)
62
+ # Pizzas.project(Projections::Shape, out: "shape.json")
63
+ def define_project(chapter, bluebook)
64
+ chapter.define_singleton_method(:project) do |target, out: nil, **options|
65
+ key = Projector.key_for(target)
66
+ artifact = Projector.call(key, bluebook: bluebook, options: options)
67
+ return artifact unless out
68
+
69
+ Projector.write(artifact, out, as: Projector.emits_for(key))
70
+ end
71
+ end
72
+
73
+ # Inside a hecksagon a name is a declaration: with a collector open it becomes
74
+ # a `BindingProxy`. Otherwise a scoped reference from a bluebook mid-declaration
75
+ # goes to the `ConstShim` resolver, so it resolves whether or not a facade exists.
76
+ def define_const_missing(chapter, bluebook)
77
+ chapter.define_singleton_method(:const_missing) do |name|
78
+ collector = Bluebook::DSL::HecksagonBuilder.collector
79
+ return Bluebook::DSL::BindingProxy.new("#{bluebook.name}::#{name}", collector) if collector
80
+
81
+ resolver = Bluebook::DSL::ConstShim.resolver
82
+ return resolver.call("#{bluebook.name}::#{name}") if resolver
83
+
84
+ super(name)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,83 @@
1
+ require_relative "../../chapters"
2
+ require_relative "ruby/chapter"
3
+ require_relative "ruby/aggregate_module"
4
+
5
+ module Hecks
6
+ module Adapters
7
+ module Driving
8
+ # The Ruby adapter without classes: anonymous per-boot modules whose singleton methods
9
+ # dispatch by FQN, installed by `Loader.bind_runtime` and replaced whole on the next boot.
10
+ #
11
+ # `persisted_by("Heki")` in a `.hecksagon` file lands on the module's `method_missing`,
12
+ # which records a `Bind` into the open `HecksagonBuilder.collector`.
13
+ module Ruby
14
+ # Names that would shadow the machinery a Handle runs on; a field with one gets no reader.
15
+ RESERVED = %i[id state events reload inspect to_h hash class].freeze
16
+
17
+ extend Chapter
18
+ extend AggregateModule
19
+
20
+ module_function
21
+
22
+ # Installs one top-level module per booted chapter, and one per aggregate, each
23
+ # closing over `dispatcher`, so `Pizzas::Pizza` and the bare `Pizza` both open the
24
+ # module of the boot that ran last.
25
+ #
26
+ # A gem chapter a hecksagon `attaches` installs nothing (`Query`, `Port` stay free).
27
+ # An aggregate sharing its chapter's name gets no second constant. A name that user
28
+ # code or the stdlib already owns is left alone with a warning (see `Namespace.install`).
29
+ #
30
+ # @param dispatcher [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted
31
+ # dispatcher every installed module dispatches and queries through
32
+ # @return [Runtime::Dispatcher, Runtime::RemoteDispatcher] the same `dispatcher`
33
+ # @raise [NameError] if a chapter or aggregate name is not a valid constant name
34
+ def install(dispatcher)
35
+ Admission.admit!(dispatcher.registry, "Ruby")
36
+ dispatcher.registry.bluebooks.each_value do |bluebook|
37
+ install_chapter(dispatcher, bluebook) unless attached_chapter?(dispatcher.registry, bluebook)
38
+ end
39
+ dispatcher
40
+ end
41
+
42
+ # Installs one chapter's module, and the top-level shortcut of each of its aggregates.
43
+ def install_chapter(dispatcher, bluebook)
44
+ chapter = chapter_module(dispatcher, bluebook)
45
+ Namespace.install(*placement(bluebook), chapter)
46
+ # A namespaced chapter keeps its aggregates inside its namespace: top-level
47
+ # shortcuts would bring back the collisions the namespace exists to avoid.
48
+ return if bluebook.namespace
49
+
50
+ bluebook.aggregates.each do |aggregate|
51
+ next if aggregate.hecks_name == bluebook.name
52
+
53
+ Namespace.install(Object, aggregate.hecks_name, chapter.const_get(aggregate.hecks_name, false))
54
+ end
55
+ end
56
+
57
+ # Whether `bluebook` is a gem chapter that a hecksagon attached rather than a domain's own.
58
+ #
59
+ # @param registry [Runtime::Registry] the booted registry
60
+ # @param bluebook [Bluebook::Chapter] the chapter under test
61
+ # @return [Boolean]
62
+ def attached_chapter?(registry, bluebook)
63
+ registry.bounded?(bluebook.name) && Hecks::Chapters.index.key?(bluebook.name)
64
+ end
65
+
66
+ # Where a chapter's module installs: `Object::<name>`, or the constant path its
67
+ # `namespace` names, creating any missing parent modules along the way.
68
+ #
69
+ # @param bluebook [Bluebook::Chapter] the chapter being installed
70
+ # @return [Array(Module, String)] the parent module and the constant name within it
71
+ def placement(bluebook)
72
+ return [Object, bluebook.name] unless bluebook.namespace
73
+
74
+ *parents, leaf = bluebook.namespace.split("::")
75
+ parent = parents.reduce(Object) do |outer, name|
76
+ outer.const_defined?(name, false) ? outer.const_get(name, false) : outer.const_set(name, Module.new)
77
+ end
78
+ [parent, leaf]
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end