hecks 3.10.0 → 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 (481) 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 +18 -2
  116. data/lib/hecks/doc/reference/rendering.rb +6 -3
  117. data/lib/hecks/forms/app/commands.rb +8 -7
  118. data/lib/hecks/forms/app/queries.rb +2 -1
  119. data/lib/hecks/forms/app.rb +1 -1
  120. data/lib/hecks/forms/record_renderer.rb +1 -1
  121. data/lib/hecks/framework/bluebook/privacy.bluebook +11 -0
  122. data/lib/hecks/fuzzing/concurrent_dispatch.rb +4 -1
  123. data/lib/hecks/fuzzing/failure_triage.rb +2 -1
  124. data/lib/hecks/fuzzing/mutation/run.rb +11 -4
  125. data/lib/hecks/fuzzing/properties/catalog.rb +6 -4
  126. data/lib/hecks/fuzzing/properties/guaranteed_by_construction.rb +18 -11
  127. data/lib/hecks/fuzzing/properties/guards.rb +1 -1
  128. data/lib/hecks/fuzzing/self_consistency/saga_redelivery.rb +1 -1
  129. data/lib/hecks/fuzzing/self_consistency.rb +6 -2
  130. data/lib/hecks/fuzzing/shrinker.rb +3 -1
  131. data/lib/hecks/fuzzing/value_generator.rb +19 -5
  132. data/lib/hecks/gate/stages.yml +4 -10
  133. data/lib/hecks/grammar/evolve.rb +1 -1
  134. data/lib/hecks/grammar/expression_operators.json +372 -0
  135. data/lib/hecks/grammar/operators.rb +3 -1
  136. data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +10 -10
  137. data/lib/hecks/hecks/adapters/codebase/inspection/rubycritic.rb +35 -0
  138. data/lib/hecks/hecks/adapters/codebase/inspection.rb +104 -0
  139. data/lib/hecks/hecks/adapters/codebase/publishing.rb +2 -1
  140. data/lib/hecks/hecks/adapters/codebase/release_facts.rb +12 -2
  141. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +2 -1
  142. data/lib/hecks/hecks/adapters/codebase/test_runner.rb +1 -1
  143. data/lib/hecks/hecks/adapters/cost_explorer.rb +8 -5
  144. data/lib/hecks/hecks/adapters/deploy_toolchain/statuses.rb +2 -1
  145. data/lib/hecks/hecks/adapters/finding_github.rb +13 -6
  146. data/lib/hecks/hecks/adapters/first_admin.rb +2 -2
  147. data/lib/hecks/hecks/adapters/github_rulesets.rb +6 -3
  148. data/lib/hecks/hecks/adapters/in_process_boot.rb +1 -1
  149. data/lib/hecks/hecks/adapters/journal_store/compaction.rb +1 -1
  150. data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +10 -20
  151. data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +28 -5
  152. data/lib/hecks/hecks/adapters/local_files.rb +2 -2
  153. data/lib/hecks/hecks/adapters/rehearsal/audit_report.rb +107 -0
  154. data/lib/hecks/hecks/adapters/rehearsal/census.rb +71 -0
  155. data/lib/hecks/hecks/adapters/rehearsal/declaration.rb +133 -0
  156. data/lib/hecks/hecks/adapters/rehearsal/environment.rb +83 -0
  157. data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +142 -0
  158. data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +96 -0
  159. data/lib/hecks/hecks/adapters/rehearsal/postgres_census.rb +100 -0
  160. data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +69 -0
  161. data/lib/hecks/hecks/adapters/rehearsal/postgres_store.rb +129 -0
  162. data/lib/hecks/hecks/adapters/rehearsal/rows.rb +41 -0
  163. data/lib/hecks/hecks/adapters/rehearsal/rust_audit.rb +83 -0
  164. data/lib/hecks/hecks/adapters/rehearsal/subject.rb +104 -0
  165. data/lib/hecks/hecks/adapters/rehearsal.rb +54 -0
  166. data/lib/hecks/hecks/adapters/rehearsal_store.adapter +7 -0
  167. data/lib/hecks/hecks/adapters/rehearsal_store.rb +81 -0
  168. data/lib/hecks/hecks/adapters/terminal.rb +13 -11
  169. data/lib/hecks/hecks/codebase.behaviors +13 -1
  170. data/lib/hecks/hecks/codebase.bluebook +239 -5
  171. data/lib/hecks/hecks/custodian.behaviors +24 -6
  172. data/lib/hecks/hecks/custodian.bluebook +6 -1227
  173. data/lib/hecks/hecks/deploy.behaviors +1 -1
  174. data/lib/hecks/hecks/deploy.world +1 -3
  175. data/lib/hecks/hecks/hecks.behaviors +1 -1
  176. data/lib/hecks/hecks/hecks.hecksagon +53 -18
  177. data/lib/hecks/hecks/hecks.world +10 -10
  178. data/lib/hecks/hecks/rehearsal.bluebook +477 -0
  179. data/lib/hecks/hecks/site.behaviors +1 -1
  180. data/lib/hecks/hecks/site.world +0 -1
  181. data/lib/hecks/hecks/tooling.bluebook +1238 -0
  182. data/lib/hecks/indifferent_key.rb +20 -0
  183. data/lib/hecks/language/adapter.bluebook +13 -3
  184. data/lib/hecks/language/bluebook/aggregate.bluebook +23 -0
  185. data/lib/hecks/language/bluebook/bluebook.bluebook +15 -0
  186. data/lib/hecks/language/bluebook/entity.bluebook +18 -0
  187. data/lib/hecks/language/bluebook/policy.bluebook +9 -1
  188. data/lib/hecks/language/bluebook/query.bluebook +1 -1
  189. data/lib/hecks/language/bluebook/vocabulary.bluebook +11 -4
  190. data/lib/hecks/language/hecksagon/domain_port.bluebook +1 -1
  191. data/lib/hecks/language/hecksagon/hecksagon.bluebook +22 -0
  192. data/lib/hecks/language/oidc.json +10 -0
  193. data/lib/hecks/language/port.bluebook +4 -4
  194. data/lib/hecks/literal.rb +12 -3
  195. data/lib/hecks/ports/key_vault.rb +7 -2
  196. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +21 -10
  197. data/lib/hecks/ports/persistence/plugins/era/postgres_era/boot.rb +4 -2
  198. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +2 -1
  199. data/lib/hecks/ports/persistence/repository_factory.rb +6 -3
  200. data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +29 -0
  201. data/lib/hecks/projections/deploy/box/templates/deploy-service.sh.tmpl +1 -1
  202. data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
  203. data/lib/hecks/projections/deploy/shared.rb +5 -2
  204. data/lib/hecks/projections/model/deviations.rb +2 -2
  205. data/lib/hecks/projections/model.rb +2 -2
  206. data/lib/hecks/projections/oidc.rb +13 -1
  207. data/lib/hecks/projections/shape.rb +5 -2
  208. data/lib/hecks/projections/site/cms_editor/attributes.rb +26 -6
  209. data/lib/hecks/projections/site/cms_editor/blocks.rb +157 -0
  210. data/lib/hecks/projections/site/cms_editor/checks.rb +14 -1
  211. data/lib/hecks/projections/site/cms_editor/clearing.rb +70 -0
  212. data/lib/hecks/projections/site/cms_editor/color.rb +124 -0
  213. data/lib/hecks/projections/site/cms_editor/composition.rb +42 -0
  214. data/lib/hecks/projections/site/cms_editor/destructive.rb +61 -0
  215. data/lib/hecks/projections/site/cms_editor/drafts.rb +102 -0
  216. data/lib/hecks/projections/site/cms_editor/hints.rb +74 -0
  217. data/lib/hecks/projections/site/cms_editor/icons.rb +86 -0
  218. data/lib/hecks/projections/site/cms_editor/key_kinds.rb +31 -0
  219. data/lib/hecks/projections/site/cms_editor/limits.rb +72 -0
  220. data/lib/hecks/projections/site/cms_editor/media.rb +1 -1
  221. data/lib/hecks/projections/site/cms_editor/moment.rb +34 -0
  222. data/lib/hecks/projections/site/cms_editor/palette.rb +115 -0
  223. data/lib/hecks/projections/site/cms_editor/pickers.rb +85 -0
  224. data/lib/hecks/projections/site/cms_editor/preview.rb +91 -0
  225. data/lib/hecks/projections/site/cms_editor/relations.rb +92 -0
  226. data/lib/hecks/projections/site/cms_editor/scale_checks.rb +66 -0
  227. data/lib/hecks/projections/site/cms_editor/scheduling.rb +150 -0
  228. data/lib/hecks/projections/site/cms_editor/schema.rb +96 -19
  229. data/lib/hecks/projections/site/cms_editor/setting.rb +52 -9
  230. data/lib/hecks/projections/site/cms_editor/targets.rb +86 -0
  231. data/lib/hecks/projections/site/cms_editor/templates/package.json.tmpl +18 -1
  232. data/lib/hecks/projections/site/cms_editor/templates/src/app.ts.tmpl +251 -57
  233. data/lib/hecks/projections/site/cms_editor/templates/src/assets.ts.tmpl +98 -0
  234. data/lib/hecks/projections/site/cms_editor/templates/src/auth/membership.ts.tmpl +32 -24
  235. data/lib/hecks/projections/site/cms_editor/templates/src/browser/app.css.tmpl +412 -0
  236. data/lib/hecks/projections/site/cms_editor/templates/src/browser/autosave.js.tmpl +144 -0
  237. data/lib/hecks/projections/site/cms_editor/templates/src/browser/blocks.js.tmpl +500 -0
  238. data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_doc.js.tmpl +175 -0
  239. data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_editor.js.tmpl +418 -0
  240. data/lib/hecks/projections/site/cms_editor/templates/src/browser/epoch.js.tmpl +61 -0
  241. data/lib/hecks/projections/site/cms_editor/templates/src/browser/forms.js.tmpl +210 -0
  242. data/lib/hecks/projections/site/cms_editor/templates/src/browser/lists.js.tmpl +40 -0
  243. data/lib/hecks/projections/site/cms_editor/templates/src/browser/main.js.tmpl +26 -0
  244. data/lib/hecks/projections/site/cms_editor/templates/src/browser/media_picker.js.tmpl +232 -0
  245. data/lib/hecks/projections/site/cms_editor/templates/src/browser/moments.js.tmpl +73 -0
  246. data/lib/hecks/projections/site/cms_editor/templates/src/browser/pickers.js.tmpl +152 -0
  247. data/lib/hecks/projections/site/cms_editor/templates/src/browser/preview.js.tmpl +98 -0
  248. data/lib/hecks/projections/site/cms_editor/templates/src/browser/shell.js.tmpl +140 -0
  249. data/lib/hecks/projections/site/cms_editor/templates/src/browser/toast.js.tmpl +66 -0
  250. data/lib/hecks/projections/site/cms_editor/templates/src/choices.ts.tmpl +99 -0
  251. data/lib/hecks/projections/site/cms_editor/templates/src/commands.ts.tmpl +78 -24
  252. data/lib/hecks/projections/site/cms_editor/templates/src/config.ts.tmpl +6 -0
  253. data/lib/hecks/projections/site/cms_editor/templates/src/flash.ts.tmpl +52 -0
  254. data/lib/hecks/projections/site/cms_editor/templates/src/host.ts.tmpl +19 -2
  255. data/lib/hecks/projections/site/cms_editor/templates/src/media/handler.ts.tmpl +31 -8
  256. data/lib/hecks/projections/site/cms_editor/templates/src/names.ts.tmpl +36 -0
  257. data/lib/hecks/projections/site/cms_editor/templates/src/schema.ts.tmpl +131 -2
  258. data/lib/hecks/projections/site/cms_editor/templates/src/ui/availability.ts.tmpl +15 -0
  259. data/lib/hecks/projections/site/cms_editor/templates/src/ui/block_list.js.tmpl +213 -0
  260. data/lib/hecks/projections/site/cms_editor/templates/src/ui/blocks.ts.tmpl +275 -0
  261. data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_model.js.tmpl +8 -5
  262. data/lib/hecks/projections/site/cms_editor/templates/src/ui/detail.ts.tmpl +88 -0
  263. data/lib/hecks/projections/site/cms_editor/templates/src/ui/dialogs.ts.tmpl +43 -0
  264. data/lib/hecks/projections/site/cms_editor/templates/src/ui/fields.ts.tmpl +229 -34
  265. data/lib/hecks/projections/site/cms_editor/templates/src/ui/html.ts.tmpl +40 -81
  266. data/lib/hecks/projections/site/cms_editor/templates/src/ui/icons.ts.tmpl +15 -0
  267. data/lib/hecks/projections/site/cms_editor/templates/src/ui/input.ts.tmpl +29 -12
  268. data/lib/hecks/projections/site/cms_editor/templates/src/ui/links.ts.tmpl +100 -0
  269. data/lib/hecks/projections/site/cms_editor/templates/src/ui/moments.ts.tmpl +44 -0
  270. data/lib/hecks/projections/site/cms_editor/templates/src/ui/outline.ts.tmpl +53 -17
  271. data/lib/hecks/projections/site/cms_editor/templates/src/ui/pages.ts.tmpl +178 -88
  272. data/lib/hecks/projections/site/cms_editor/templates/src/ui/paging.ts.tmpl +82 -0
  273. data/lib/hecks/projections/site/cms_editor/templates/src/ui/pickers.ts.tmpl +110 -0
  274. data/lib/hecks/projections/site/cms_editor/templates/src/ui/preview.ts.tmpl +87 -0
  275. data/lib/hecks/projections/site/cms_editor/templates/src/ui/related.ts.tmpl +97 -0
  276. data/lib/hecks/projections/site/cms_editor/templates/src/ui/schedule.ts.tmpl +160 -0
  277. data/lib/hecks/projections/site/cms_editor/templates/src/ui/shell.ts.tmpl +211 -0
  278. data/lib/hecks/projections/site/cms_editor/templates/src/ui/table.ts.tmpl +151 -0
  279. data/lib/hecks/projections/site/cms_editor/templates/src/ui/theme.js.tmpl +16 -0
  280. data/lib/hecks/projections/site/cms_editor/templates/src/ui/values.ts.tmpl +69 -0
  281. data/lib/hecks/projections/site/cms_editor/templates/src/ui/words.ts.tmpl +97 -0
  282. data/lib/hecks/projections/site/cms_editor/templates/src/ui/world.ts.tmpl +35 -0
  283. data/lib/hecks/projections/site/cms_editor/templates/tsconfig.json.tmpl +2 -1
  284. data/lib/hecks/projections/site/cms_editor/theme.rb +29 -0
  285. data/lib/hecks/projections/site/cms_editor/theme_checks.rb +57 -0
  286. data/lib/hecks/projections/site/cms_editor.rb +49 -23
  287. data/lib/hecks/projections/site/role_probe.rb +2 -0
  288. data/lib/hecks/projector/cli_audience.rb +2 -1
  289. data/lib/hecks/projector/cli_projector/listing.rb +1 -1
  290. data/lib/hecks/projector/cli_projector/option_specs.rb +1 -1
  291. data/lib/hecks/projector/docs_projector/closing.rb +1 -1
  292. data/lib/hecks/projector/docs_projector.rb +1 -1
  293. data/lib/hecks/projector/exporter/bindings.rb +40 -10
  294. data/lib/hecks/projector/exporter/capability_values.rb +55 -0
  295. data/lib/hecks/projector/narrate_projector/reactions.rb +1 -1
  296. data/lib/hecks/projector/target.rb +11 -0
  297. data/lib/hecks/projector.rb +32 -5
  298. data/lib/hecks/quality_control/adapters/github_checks.rb +12 -8
  299. data/lib/hecks/quality_control/adapters/github_issues.rb +8 -4
  300. data/lib/hecks/quality_control/cli/qa_open_pr.rb +2 -2
  301. data/lib/hecks/quality_control/cli/qa_sweep/checks/shrinking.rb +1 -1
  302. data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +1 -1
  303. data/lib/hecks/quality_control/cli/qa_sweep.rb +1 -1
  304. data/lib/hecks/quality_control/quality_control.bluebook +6 -2
  305. data/lib/hecks/release/approval.rb +64 -0
  306. data/lib/hecks/router/{door.rb → aggregate_reader.rb} +5 -4
  307. data/lib/hecks/router/namespace_installer.rb +16 -16
  308. data/lib/hecks/router.rb +1 -1
  309. data/lib/hecks/runtime/best_effort.rb +22 -0
  310. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +12 -2
  311. data/lib/hecks/runtime/command_interpreter.rb +3 -2
  312. data/lib/hecks/runtime/command_rules/emission.rb +1 -1
  313. data/lib/hecks/runtime/dispatcher.rb +4 -4
  314. data/lib/hecks/runtime/entity_element/location.rb +1 -1
  315. data/lib/hecks/runtime/event.rb +5 -0
  316. data/lib/hecks/runtime/identity.rb +7 -6
  317. data/lib/hecks/runtime/interpreting.rb +2 -1
  318. data/lib/hecks/runtime/invocation_builder.rb +1 -1
  319. data/lib/hecks/runtime/loader/boot_steps.rb +8 -5
  320. data/lib/hecks/runtime/loader.rb +10 -10
  321. data/lib/hecks/runtime/policy_interpreter/arguments.rb +7 -4
  322. data/lib/hecks/runtime/policy_interpreter/fan_out.rb +3 -3
  323. data/lib/hecks/runtime/policy_interpreter.rb +6 -6
  324. data/lib/hecks/runtime/port_operation_interpreter.rb +1 -1
  325. data/lib/hecks/runtime/reaction_invocation.rb +1 -1
  326. data/lib/hecks/runtime/rebuild_sweep.rb +11 -1
  327. data/lib/hecks/runtime/registry/ask_verification.rb +35 -0
  328. data/lib/hecks/runtime/registry/hecksagon_rules.rb +10 -0
  329. data/lib/hecks/runtime/registry/merging.rb +1 -1
  330. data/lib/hecks/runtime/registry/providers.rb +16 -0
  331. data/lib/hecks/runtime/registry/read_repository.rb +6 -4
  332. data/lib/hecks/runtime/registry/verification.rb +4 -1
  333. data/lib/hecks/runtime/registry/world_defaults.rb +29 -0
  334. data/lib/hecks/runtime/registry.rb +1 -1
  335. data/lib/hecks/runtime/saga_interpreter/compensation.rb +1 -1
  336. data/lib/hecks/runtime/saga_interpreter/delivery.rb +4 -4
  337. data/lib/hecks/runtime/saga_interpreter/transitions.rb +1 -1
  338. data/lib/hecks/runtime/saga_interpreter.rb +3 -3
  339. data/lib/hecks/runtime/tenant_check.rb +5 -4
  340. data/lib/hecks/runtime/value/coercion.rb +2 -2
  341. data/lib/hecks/runtime/value/validation.rb +16 -4
  342. data/lib/hecks/runtime.rb +9 -9
  343. data/lib/hecks/rust_build/project_rust/target_ir.rb +1 -1
  344. data/lib/hecks/rust_build/wasm.rb +1 -1
  345. data/lib/hecks/rust_build/wasm_browser.rb +1 -1
  346. data/lib/hecks/rust_build.rb +11 -0
  347. data/lib/hecks/served_options.rb +53 -0
  348. data/lib/hecks/site/bluebook/site.bluebook +1 -0
  349. data/lib/hecks/storehouse/audit.rb +1 -1
  350. data/lib/hecks/storehouse/dispatching.rb +8 -6
  351. data/lib/hecks/storehouse/introspection.rb +2 -2
  352. data/lib/hecks/storehouse/querying.rb +6 -4
  353. data/lib/hecks/storehouse.rb +4 -4
  354. data/lib/hecks/tools/comment_style/source_file.rb +4 -1
  355. data/lib/hecks/tools/deploy_recipe_lint/fixtures.rb +4 -1
  356. data/lib/hecks/tools/lanes/rows.rb +6 -0
  357. data/lib/hecks/tools/oidc_manifests.rb +4 -5
  358. data/lib/hecks/tools/regeneration_run.rb +11 -4
  359. data/lib/hecks/tools/site_routes/editor_files.rb +12 -5
  360. data/lib/hecks/tools/tenant_provisioning.rb +2 -2
  361. data/lib/hecks/tools/tools_doc.rb +4 -4
  362. data/lib/hecks/version.rb +1 -1
  363. data/lib/hecks/vocabulary.rb +4 -4
  364. data/lib/hecks.rb +15 -15
  365. data/lib/rubocop/cop/hecks/env_dump.rb +69 -0
  366. data/lib/rubocop/cop/hecks/monotonic_duration.rb +53 -0
  367. data/lib/rubocop/cop/hecks/no_wall_clock_in_runtime.rb +34 -0
  368. data/lib/rubocop/cop/hecks/raw_shell_interpolation.rb +79 -0
  369. data/lib/rubocop/cop/hecks/sleep_in_spec.rb +34 -0
  370. data/lib/rubocop/cop/hecks/swallowed_rescue.rb +37 -0
  371. data/lib/rubocop/cop/hecks/symbol_string_key_mix.rb +50 -0
  372. data/lib/rubocop/cop/hecks/untyped_raise.rb +37 -0
  373. data/prebuilt/05c358d38d661a3774f813d49c66f5b68939af94432d66257a56f49ee2598994.marshal +0 -0
  374. data/prebuilt/26c40c61f09b09b11651c64668eda3ceb180f83cf2a3802d3033d2fd99ac841b.marshal +0 -0
  375. data/prebuilt/verdicts-230ac18c25abfd3d92ede032bee2661a4c2cd2773ab0cf0e2ae52546ba2f7af9.json +1 -0
  376. data/rust/.gitignore +6 -0
  377. data/rust/Cargo.toml +6 -0
  378. data/rust/build/src/binding_pass.rs +103 -0
  379. data/rust/build/src/cargo_sync.rs +6 -0
  380. data/rust/build/src/lineage_pass.rs +3 -3
  381. data/rust/build/src/main.rs +17 -5
  382. data/rust/build/src/pipeline.rs +48 -5
  383. data/rust/build/src/seams_pass.rs +123 -0
  384. data/rust/build/src/translation_pass.rs +167 -0
  385. data/rust/codegen/src/asks.rs +103 -0
  386. data/rust/codegen/src/commands.rs +13 -8
  387. data/rust/codegen/src/constraints.rs +25 -0
  388. data/rust/codegen/src/domain_generator.rs +2 -0
  389. data/rust/codegen/src/expr_emitter.rs +13 -0
  390. data/rust/codegen/src/json_codec.rs +54 -15
  391. data/rust/codegen/src/main.rs +1 -0
  392. data/rust/codegen/src/mutations.rs +128 -3
  393. data/rust/codegen/src/queries.rs +11 -2
  394. data/rust/codegen/src/reactions.rs +1 -1
  395. data/rust/codegen/src/types.rs +166 -8
  396. data/rust/host/HECKS_RELEASE +1 -1
  397. data/rust/host/src/api.rs +4 -4
  398. data/rust/host/src/bin/validate_harness.rs +43 -0
  399. data/rust/host/src/boundary_fuzz/expr_json.rs +2 -2
  400. data/rust/host/src/boundary_fuzz/ir.rs +1 -0
  401. data/rust/host/src/boundary_fuzz/server.rs +1 -1
  402. data/rust/host/src/checkout.rs +35 -19
  403. data/rust/host/src/commerce_ir.rs +408 -2
  404. data/rust/host/src/expr_json.rs +118 -0
  405. data/rust/host/src/mint.rs +1 -1
  406. data/rust/host/src/payments/tests.rs +27 -1
  407. data/rust/host/src/payments.rs +12 -4
  408. data/rust/host/src/query_step.rs +22 -4
  409. data/rust/host/src/reference_validate.rs +653 -108
  410. data/rust/host/src/resend.rs +2 -0
  411. data/rust/host/src/server.rs +19 -6
  412. data/rust/host/src/ui_schema.rs +132 -5
  413. data/rust/host/src/web/newsletter.rs +7 -8
  414. data/rust/host/src/web/newsletter_send.rs +38 -13
  415. data/rust/host/src/web/registrations.rs +229 -26
  416. data/rust/host/src/web.rs +3 -3
  417. data/rust/parser/src/emit.rs +38 -6
  418. data/rust/parser/src/expr/ast_json.rs +7 -1
  419. data/rust/parser/src/expr/resolver.rs +171 -6
  420. data/rust/parser/src/ir.rs +7 -0
  421. data/rust/parser/src/keywords.rs +24 -0
  422. data/rust/parser/src/main.rs +3 -0
  423. data/rust/parser/src/parse/aggregate.rs +2 -1
  424. data/rust/parser/src/parse/chapter.rs +2 -2
  425. data/rust/parser/src/parse/command.rs +2 -0
  426. data/rust/parser/src/parse/hecksagon.rs +5 -2
  427. data/rust/parser/src/parse/lifecycle.rs +81 -0
  428. data/rust/parser/src/parse/mod.rs +15 -1
  429. data/rust/parser/src/parse/policy.rs +13 -2
  430. data/rust/parser/src/parse/value_object.rs +13 -8
  431. data/rust/parser/src/ruby_value.rs +1 -1
  432. data/rust/src/exemplar/commands.rs +4 -4
  433. data/rust/src/exemplar/constraints.rs +1 -1
  434. data/rust/src/exemplar/json.rs +43 -1
  435. data/rust/src/kernel/cli.rs +7 -5
  436. data/rust/src/kernel/dispatch.rs +2 -2
  437. data/rust/src/kernel/dispatch_guard_tests.rs +1 -1
  438. data/rust/src/kernel/expr.rs +66 -3
  439. data/rust/src/kernel/expression_operators/arithmetic.rs +130 -5
  440. data/rust/src/kernel/expression_operators/text.rs +26 -2
  441. data/rust/src/kernel/json.rs +15 -0
  442. data/rust/src/kernel/mod.rs +1 -1
  443. data/rust/src/kernel/orchestrate.rs +54 -5
  444. data/rust/src/kernel/orchestrate_guard_tests.rs +67 -0
  445. data/rust/src/kernel/pattern.rs +60 -0
  446. data/rust/src/kernel/reference_lookup.rs +90 -7
  447. metadata +140 -38
  448. data/lib/hecks/doors/cli_door/nesting.rb +0 -34
  449. data/lib/hecks/doors/cli_door/shorthand.rb +0 -85
  450. data/lib/hecks/doors/cli_door.rb +0 -124
  451. data/lib/hecks/doors/cli_runner/answers.rb +0 -138
  452. data/lib/hecks/doors/cli_runner/resolution.rb +0 -146
  453. data/lib/hecks/doors/cli_runner/settling.rb +0 -115
  454. data/lib/hecks/doors/cli_runner/streaming.rb +0 -90
  455. data/lib/hecks/doors/cli_runner/suggestions.rb +0 -43
  456. data/lib/hecks/doors/cli_runner.rb +0 -66
  457. data/lib/hecks/doors/command_request.rb +0 -119
  458. data/lib/hecks/doors/handle.rb +0 -208
  459. data/lib/hecks/doors/json_door.rb +0 -132
  460. data/lib/hecks/doors/launcher_options.rb +0 -247
  461. data/lib/hecks/doors/mcp_door/properties.rb +0 -47
  462. data/lib/hecks/doors/mcp_door/tool_calls.rb +0 -113
  463. data/lib/hecks/doors/mcp_door/tool_definitions/auditing.rb +0 -51
  464. data/lib/hecks/doors/mcp_door/tool_definitions/calling.rb +0 -69
  465. data/lib/hecks/doors/mcp_door/tool_definitions/reading.rb +0 -97
  466. data/lib/hecks/doors/mcp_door/tool_definitions.rb +0 -15
  467. data/lib/hecks/doors/mcp_door.rb +0 -156
  468. data/lib/hecks/doors/mcp_door_scope/argument_policy.rb +0 -92
  469. data/lib/hecks/doors/mcp_door_scope/env_problems.rb +0 -55
  470. data/lib/hecks/doors/mcp_door_scope/presenter.rb +0 -78
  471. data/lib/hecks/doors/mcp_door_scope.rb +0 -201
  472. data/lib/hecks/doors/mcp_guide_cache.rb +0 -75
  473. data/lib/hecks/doors/ruby_door/aggregate_door.rb +0 -162
  474. data/lib/hecks/doors/ruby_door/chapter.rb +0 -89
  475. data/lib/hecks/doors/ruby_door.rb +0 -80
  476. data/lib/hecks/doors/usage_cache.rb +0 -139
  477. data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_widget.js.tmpl +0 -282
  478. data/lib/hecks/projections/site/cms_editor/templates/src/ui/media_picker.js.tmpl +0 -109
  479. data/prebuilt/c28d6d9db14eea3e17161e9d9c262efee30b256cf97693a7cfd552bce24b2f7f.marshal +0 -0
  480. data/prebuilt/cb375cba61ca0dbe8ddb9739c306510fe05e5f4b7a9b6955caff8224c63ea0e0.marshal +0 -0
  481. data/prebuilt/verdicts-d6d584ad43b5f3cebf88aafbbdbf024c38574acf18e06adffbc555f7786b1c2f.json +0 -1
@@ -0,0 +1,35 @@
1
+ require_relative "../../bluebook/ask_resolution"
2
+
3
+ module Hecks
4
+ module Runtime
5
+ class Registry
6
+ # The boot gate for `ask`: every policy's ask must resolve to one declared port operation,
7
+ # so a name the hecksagon never declared stops the boot instead of firing at nothing.
8
+ # Mixed into {Verification}.
9
+ module AskVerification
10
+ private
11
+
12
+ # A port operation a chapter provides, and an ask a policy makes, each name a port
13
+ # operation the hecksagon declared.
14
+ def refuse_unresolved_ports!
15
+ refuse_unresolved_port_operations!
16
+ refuse_unresolved_asks!
17
+ end
18
+
19
+ # Binds each ask to the operation it resolves to, refusing the first that resolves to none.
20
+ #
21
+ # @raise [WiringError] naming the policy, the ask and what the hecksagon would need
22
+ def refuse_unresolved_asks!
23
+ @declared.bluebooks.each_value do |chapter|
24
+ chapter.policies.select(&:asks?).each do |policy|
25
+ outcome = Bluebook::AskResolution.call(chapter, policy)
26
+ raise WiringError, outcome.detail unless outcome.resolved?
27
+
28
+ policy.resolve_ask!(outcome.verb)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -27,6 +27,16 @@ module Hecks
27
27
  "without that it is silent decoration, the exact defect this refusal exists to catch"
28
28
  end
29
29
 
30
+ # `driven_by` names a driving adapter; a name none answers to would silently admit nothing.
31
+ def refuse_unknown_driving!(hecksagon)
32
+ unknown = hecksagon.driving - Bluebook::Hecksagon::DRIVING_ADAPTERS
33
+ return if unknown.empty?
34
+
35
+ raise WiringError,
36
+ "#{hecksagon.domain} is driven_by #{unknown.map(&:inspect).join(", ")}, which no driving " \
37
+ "adapter answers to — the adapters are #{Bluebook::Hecksagon::DRIVING_ADAPTERS.join(", ")}"
38
+ end
39
+
30
40
  # `attaches` loads a bounded context; the consumer must declare the sibling hecksagon
31
41
  # that is its ACL (Governance/Identity/Privacy already do). A vendored package only
32
42
  # loads its `.bluebook` files, so persistence, Governance and the `translates` ACL live
@@ -17,7 +17,7 @@ module Hecks
17
17
  def merge_hecksagons(base, overlay)
18
18
  # Order-independent: list facts uniq, so loading context_map before or after the
19
19
  # domain file yields the same merged hecksagon.
20
- lists = %i[subscriptions attachments translates].to_h do |facet|
20
+ lists = %i[subscriptions attachments translates driving].to_h do |facet|
21
21
  [facet, (base.public_send(facet) + overlay.public_send(facet)).uniq]
22
22
  end
23
23
  Bluebook::Hecksagon.new(domain: base.domain, binds: base.binds + overlay.binds,
@@ -101,6 +101,16 @@ module Hecks
101
101
  vendored_provider_for(domain, Bluebook::Capabilities::PAYMENT_CONNECTION)
102
102
  end
103
103
 
104
+ # The chapter that owns the checkout boundary for `domain` — resolved by what it
105
+ # declares (`provides "checkout"`). Nil when none does.
106
+ #
107
+ # @param domain [String, Symbol] the domain whose checkout chapter is being resolved
108
+ # @return [Bluebook::Chapter, nil] the chapter that owns `domain`'s checkout windows,
109
+ # or nil if none does
110
+ def checkout_provider_for(domain)
111
+ vendored_provider_for(domain, Bluebook::Capabilities::CHECKOUT)
112
+ end
113
+
104
114
  # The chapter that takes payments for `domain`: the domain's own chapter, a
105
115
  # framework member, or a vendored embryonaut bluebook it attaches, that
106
116
  # declares `provides "payments"`.
@@ -112,6 +122,12 @@ module Hecks
112
122
  vendored_provider_for(domain, Bluebook::Capabilities::PAYMENTS)
113
123
  end
114
124
 
125
+ # The first loaded chapter that declares `capability`, whichever domain asks.
126
+ #
127
+ # @param capability [String] a `Bluebook::Capabilities` name
128
+ # @return [Bluebook::Chapter, nil] the providing chapter, or nil if none loaded does
129
+ def provider_of(capability) = bluebooks.values.find { |chapter| chapter.provides?(capability) }
130
+
115
131
  # The chapter that provides `capability` for `domain`: the domain's own chapter,
116
132
  # a framework member its hecksagon attaches, or a vendored embryonaut bluebook
117
133
  # it attaches, that declares it.
@@ -1,3 +1,5 @@
1
+ require_relative "../best_effort"
2
+
1
3
  module Hecks
2
4
  module Runtime
3
5
  class Registry
@@ -37,10 +39,10 @@ module Hecks
37
39
  # `authoritative`, in the same order; false on any mismatch, or if comparing
38
40
  # them raises
39
41
  def projection_current?(projection, authoritative)
40
- entries_match?(projection.entries, authoritative.entries) &&
41
- sorted_rows(projection) == sorted_rows(authoritative)
42
- rescue StandardError
43
- false
42
+ BestEffort.call(false) do
43
+ entries_match?(projection.entries, authoritative.entries) &&
44
+ sorted_rows(projection) == sorted_rows(authoritative)
45
+ end
44
46
  end
45
47
 
46
48
  private
@@ -1,5 +1,6 @@
1
1
  require_relative "../../bluebook/hecksagon"
2
2
  require_relative "hecksagon_rules"
3
+ require_relative "ask_verification"
3
4
  require_relative "query_verification"
4
5
  require_relative "chapter_verification"
5
6
  require_relative "durability_warnings"
@@ -11,6 +12,7 @@ module Hecks
11
12
  # port's verb, and every world default is usable. `verify!` runs it all at boot.
12
13
  module Verification
13
14
  include HecksagonRules
15
+ include AskVerification
14
16
  include QueryVerification
15
17
  include ChapterVerification
16
18
  include DurabilityWarnings
@@ -24,7 +26,7 @@ module Hecks
24
26
  refuse_cross_package_bluebook_merge!
25
27
  refuse_reserved_chapter_names!
26
28
  refuse_membership_without_identity!
27
- refuse_unresolved_port_operations!
29
+ refuse_unresolved_ports!
28
30
  refuse_unanswerable_queries!
29
31
 
30
32
  @declared.hecksagons.each_value { |hecksagon| verify_hecksagon!(hecksagon) }
@@ -126,6 +128,7 @@ module Hecks
126
128
  # warnings for its chapter.
127
129
  def verify_hecksagon!(hecksagon)
128
130
  refuse_ungoverned_roles!(hecksagon)
131
+ refuse_unknown_driving!(hecksagon)
129
132
  refuse_unwired_attachments!(hecksagon)
130
133
  refuse_bounded_without_acl!(hecksagon)
131
134
  hecksagon.binds.each { |bind| verify_bind!(hecksagon, bind) }
@@ -28,8 +28,37 @@ module Hecks
28
28
  self
29
29
  end
30
30
 
31
+ # The defaults an adapter's `.adapter` declares for its fields, with each `{name}` or
32
+ # `{storage_name}` in a string filled from `aggregate`. A world's own setting always wins.
33
+ #
34
+ # @param adapter [String, Symbol] the adapter's name
35
+ # @param aggregate [Bluebook::Aggregate] the aggregate the adapter is built for
36
+ # @return [Hash{Symbol => Object}] the defaults, empty when the adapter declares none
37
+ def adapter_defaults(adapter, aggregate)
38
+ declared = @declared.adapters[adapter.to_s]&.declared_defaults || {}
39
+ declared.transform_values { |value| fill_placeholders(value, aggregate) }
40
+ end
41
+
42
+ # `settings` laid over the adapter's declared defaults. A field a world gives as `:default`
43
+ # asks for the declared default, so it is left out and the default shows through.
44
+ #
45
+ # @param adapter [String, Symbol] the adapter's name
46
+ # @param aggregate [Bluebook::Aggregate] the aggregate the adapter is built for
47
+ # @param settings [Hash{Symbol => Object}] the settings the world gave
48
+ # @return [Hash{Symbol => Object}] the defaults, overridden by the settings given
49
+ def with_adapter_defaults(adapter, aggregate, settings)
50
+ adapter_defaults(adapter, aggregate).merge(settings.reject { |_, value| value == :default })
51
+ end
52
+
31
53
  private
32
54
 
55
+ # A string default with its placeholders filled from the aggregate; any other value as is.
56
+ def fill_placeholders(value, aggregate)
57
+ return value unless value.is_a?(String)
58
+
59
+ value.gsub(/\{(name|storage_name)\}/) { aggregate.public_send(Regexp.last_match(1)) }
60
+ end
61
+
33
62
  # The chapter's own world answers first; the project's world fills the gap.
34
63
  def world_default(domain, field)
35
64
  world(domain)&.public_send(field) || project_world&.public_send(field)
@@ -45,7 +45,7 @@ module Hecks
45
45
  # against; nil for a registry with no such root
46
46
  def initialize(root: nil)
47
47
  @root = root
48
- # Recorded at hecksagon-build time (AggregateDoor#mark_sensitive), Loader.boot's
48
+ # Recorded at hecksagon-build time (AggregateModule#mark_sensitive), Loader.boot's
49
49
  # post-dispatch step turns each pending privacy marking into a real Privacy::Marking.Mark,
50
50
  # idempotently. `resolved_eras` and `superseded_eras` are eager, not lazy, so no two
51
51
  # dispatching threads race to create them; all writes still happen at boot,
@@ -7,7 +7,7 @@ module Hecks
7
7
  module Compensation
8
8
  private
9
9
 
10
- # Recorded before dispatching, not after `@door.reenter` returns —
10
+ # Recorded before dispatching, not after `@dispatcher.reenter` returns —
11
11
  # reenter can recursively re-enter this interpreter and refuse
12
12
  # before ever returning here, and recording only on success would
13
13
  # be too late for that nested refusal to see this leg's own
@@ -24,7 +24,7 @@ module Hecks
24
24
  record = { process_manager: process_manager.name, instance: correlation, dispatch: spec.command_name }
25
25
 
26
26
  log_dispatch_inputs(leg, spec, args) unless spec.with_spec.to_a.empty?
27
- return refuse_at_depth_ceiling(leg, record) if @door.reaction_depth_reached?
27
+ return refuse_at_depth_ceiling(leg, record) if @dispatcher.reaction_depth_reached?
28
28
 
29
29
  attempt_dispatch(leg, spec, args, record)
30
30
  end
@@ -46,7 +46,7 @@ module Hecks
46
46
  # it unwinds like a refusal rather than stranding the instance.
47
47
  def refuse_at_depth_ceiling(leg, record)
48
48
  @registry.saga_log << record.merge(delivered: false,
49
- reason: "reaction depth #{@door.max_reaction_depth} reached")
49
+ reason: "reaction depth #{@dispatcher.max_reaction_depth} reached")
50
50
  unwind(leg)
51
51
  end
52
52
 
@@ -95,14 +95,14 @@ module Hecks
95
95
  nil
96
96
  end
97
97
 
98
- # Dispatches `command_name` through the door, stamped with this saga's correlation.
98
+ # Dispatches `command_name` through the dispatcher, stamped with this saga's correlation.
99
99
  def reenter_command(leg, command_name, projected, explicit:, source_receiver:)
100
100
  head = leg.process_manager.correlation_head
101
101
  verb = qualified(command_name, leg.domain)
102
102
  invocation = ReactionInvocation.build(registry: @registry, verb: verb, projected: projected,
103
103
  explicit: explicit, passthrough: [head],
104
104
  source_receiver: source_receiver)
105
- @door.reenter(verb, saga_correlation: { head.to_s => leg.correlation }, **invocation)
105
+ @dispatcher.reenter(verb, saga_correlation: { head.to_s => leg.correlation }, **invocation)
106
106
  end
107
107
 
108
108
  # Unlike a refusal, a crash isn't a domain decision, so it
@@ -21,7 +21,7 @@ module Hecks
21
21
 
22
22
  # The mutex covers only the check-mutate-checkpoint step, never the
23
23
  # dispatch cascade that follows — `deliver_saga_dispatch`'s
24
- # `@door.reenter` can recursively re-enter this interpreter on the
24
+ # `@dispatcher.reenter` can recursively re-enter this interpreter on the
25
25
  # same thread, and `Mutex` is not reentrant.
26
26
  def remembered_leg_taken?(leg)
27
27
  leg.instance = @registry.saga_instances[leg.process_manager.name][leg.correlation]
@@ -55,11 +55,11 @@ module Hecks
55
55
 
56
56
  # @param registry [Runtime::Registry] the booted registry whose declared
57
57
  # process managers and saga persistence this interpreter runs against
58
- # @param door [Runtime::Dispatcher] the dispatcher a saga leg's own dispatch
58
+ # @param dispatcher [Runtime::Dispatcher] the dispatcher a saga leg's own dispatch
59
59
  # re-enters through
60
- def initialize(registry, door:)
60
+ def initialize(registry, dispatcher:)
61
61
  @registry = registry
62
- @door = door
62
+ @dispatcher = dispatcher
63
63
  end
64
64
 
65
65
  # Runs `domain`'s declared process managers against `event`: begins,
@@ -1,4 +1,5 @@
1
1
  require_relative "../ports/persistence/binding_policy"
2
+ require_relative "best_effort"
2
3
 
3
4
  module Hecks
4
5
  module Runtime
@@ -37,10 +38,10 @@ module Hecks
37
38
  # Same defensive shape as EraCheck#lineage_capable? — a class that
38
39
  # doesn't respond at all, or whose lookup raises, is false, not an error.
39
40
  def tenant_capable?(registry, adapter_name)
40
- adapter_class = registry.adapters[adapter_name] && registry.adapter_class(adapter_name)
41
- adapter_class.respond_to?(:tenant_capable?) && adapter_class.tenant_capable?
42
- rescue StandardError
43
- false
41
+ BestEffort.call(false) do
42
+ adapter_class = registry.adapters[adapter_name] && registry.adapter_class(adapter_name)
43
+ adapter_class.respond_to?(:tenant_capable?) && adapter_class.tenant_capable?
44
+ end
44
45
  end
45
46
  end
46
47
  end
@@ -37,9 +37,9 @@ module Hecks
37
37
  # Coerces `value` for a single, already-resolved `attribute`, branching on
38
38
  # its declared shape (list, reference, composite, or bare scalar).
39
39
  #
40
- # `boundary: false` is the query door, where a declared type documents the
40
+ # `boundary: false` is the query entry point, where a declared type documents the
41
41
  # argument for callers/generators rather than naming a shape to enforce.
42
- # `argument: true` is the command/entity/port dispatch door, where a nil for
42
+ # `argument: true` is the command/entity/port dispatch entry point, where a nil for
43
43
  # a required attribute is a left-empty argument (C3.7), not ordinary state nil.
44
44
  #
45
45
  # @raise [Runtime::TypeMismatch] if `value` cannot be coerced, or a required
@@ -34,7 +34,11 @@ module Hecks
34
34
  fields
35
35
  end
36
36
 
37
- # Fills every declared attribute `fields` lacks with its own `default:`.
37
+ # Fills every declared attribute `fields` lacks with its own `default:`, and every required
38
+ # list it lacks with an empty one: a list left out reads as no members, the one shape the
39
+ # Rust runtime (a `Vec`) can hold, so a value object built from nothing (a required
40
+ # argument left empty, C3.7) is `{blocks: []}` on both sides rather than `{}` here.
41
+ # An optional list stays absent, and an offered list, empty or not, is kept as offered.
38
42
  #
39
43
  # @param value_object [Class] the `Bluebook::ValueObject` subclass whose
40
44
  # declared defaults are read
@@ -42,11 +46,11 @@ module Hecks
42
46
  # @return [Hash{Symbol => Object}] `fields`, with defaults filled in
43
47
  def apply_defaults(value_object, fields)
44
48
  value_object.attributes.each_with_object(fields) do |attribute, completed|
45
- completed[attribute.name] = attribute.default unless completed.key?(attribute.name) || attribute.default.nil?
49
+ fill_absent(completed, attribute) unless completed.key?(attribute.name)
46
50
  end
47
51
  end
48
52
 
49
- # The full door a value object's own fields pass through — shared by
53
+ # The full entry point a value object's own fields pass through — shared by
50
54
  # `build` and `normalize_composite_fields`, so a nested field refuses
51
55
  # exactly like the same type declared directly on a command.
52
56
  #
@@ -85,6 +89,13 @@ module Hecks
85
89
 
86
90
  private
87
91
 
92
+ # The default an absent attribute declares, else an empty list for a required list.
93
+ def fill_absent(fields, attribute)
94
+ return fields[attribute.name] = attribute.default unless attribute.default.nil?
95
+
96
+ fields[attribute.name] = [] if attribute.list? && !attribute.optional?
97
+ end
98
+
88
99
  def normalize_composite_field(aggregate, attribute, fields)
89
100
  # A list member hydrates the same as a top-level list, so a value
90
101
  # read back from the store matches the shape a live dispatch wrote.
@@ -109,7 +120,8 @@ module Hecks
109
120
  validate_input_list(aggregate, attribute, fields)
110
121
  end
111
122
 
112
- # Runs each value-object member of an input list through the same door a nested single
123
+ # Runs each value-object member of an input list through the same entry point a nested
124
+ # single
113
125
  # value goes through, so a member's own invariants and nested lists refuse on dispatch
114
126
  # exactly as they do when the list sits directly on a command. The members stay as offered.
115
127
  def validate_input_list(aggregate, attribute, fields)
data/lib/hecks/runtime.rb CHANGED
@@ -42,12 +42,12 @@ module Hecks
42
42
  #
43
43
  # @param path [String] path to a domain directory, or a file inside one
44
44
  # @param shared [String, nil] a shared-root override
45
- # @param install_doors [Boolean] whether to install the Ruby facade constants
45
+ # @param install_driving [Boolean] whether to install the Ruby facade constants
46
46
  # @param environment [String, nil] the environment name for `Adapters::Folder#load_domain`
47
47
  # @return [Runtime::Dispatcher, Runtime::RemoteDispatcher] the dispatcher bound
48
48
  # to the booted domain
49
- def boot(path, shared: nil, install_doors: true, environment: Runtime::Loader::FROM_ENV)
50
- Loader.boot(path, shared: shared, install_doors: install_doors, environment: environment)
49
+ def boot(path, shared: nil, install_driving: true, environment: Runtime::Loader::FROM_ENV)
50
+ Loader.boot(path, shared: shared, install_driving: install_driving, environment: environment)
51
51
  end
52
52
 
53
53
  # Loads a domain's declarations without binding any adapter. See Loader.describe.
@@ -63,23 +63,23 @@ module Hecks
63
63
  # Finishes a boot from declarations `describe` already loaded. See Loader.boot_described.
64
64
  #
65
65
  # @param described [Runtime::Loader::Described] what `describe` answered
66
- # @param install_doors [Boolean] whether to install the Ruby facade constants
66
+ # @param install_driving [Boolean] whether to install the Ruby facade constants
67
67
  # @return [Runtime::Dispatcher, Runtime::RemoteDispatcher] the dispatcher bound
68
68
  # to the booted domain
69
- def boot_described(described, install_doors: true)
70
- Loader.boot_described(described, install_doors: install_doors)
69
+ def boot_described(described, install_driving: true)
70
+ Loader.boot_described(described, install_driving: install_driving)
71
71
  end
72
72
 
73
73
  # Loads only the given files of a domain; otherwise like `boot`. See Loader.boot_files.
74
74
  #
75
75
  # @param paths [String, Array<String>] one or more file paths within the domain
76
76
  # @param shared [String, nil] a shared-root override
77
- # @param install_doors [Boolean] whether to install the Ruby facade constants
77
+ # @param install_driving [Boolean] whether to install the Ruby facade constants
78
78
  # @param environment [String, nil] the environment name for the selected-file loader
79
79
  # @return [Runtime::Dispatcher, Runtime::RemoteDispatcher] the dispatcher bound
80
80
  # to the booted domain
81
- def boot_files(paths, shared: nil, install_doors: true, environment: Runtime::Loader::FROM_ENV)
82
- Loader.boot_files(paths, shared: shared, install_doors: install_doors, environment: environment)
81
+ def boot_files(paths, shared: nil, install_driving: true, environment: Runtime::Loader::FROM_ENV)
82
+ Loader.boot_files(paths, shared: shared, install_driving: install_driving, environment: environment)
83
83
  end
84
84
 
85
85
  # Bind the ambient registry for the duration of the block, restoring
@@ -11,7 +11,7 @@ module Hecks
11
11
  # The optional attachments `rust/host` reads from `ir.json`, each omitted when nothing
12
12
  # attached provides it: exporter method, then the key it is written under.
13
13
  SEAMS = %i[authorization membership identity newsletter newsletter_issues payments
14
- registrations payment_connection].freeze
14
+ registrations payment_connection checkout].freeze
15
15
 
16
16
  # Round-trips a value through JSON so generators see the string-keyed shape a real
17
17
  # `ir.json` carries, never live Ruby symbols.
@@ -60,7 +60,7 @@ module Hecks
60
60
  end
61
61
 
62
62
  def require_target!
63
- return if system("rustup target list --installed 2>/dev/null | grep -qx #{TARGET}")
63
+ return if RustBuild.target_installed?(TARGET)
64
64
 
65
65
  raise Failure, <<~MSG
66
66
  #{TARGET} isn't installed for this toolchain. Install it once with:
@@ -52,7 +52,7 @@ module Hecks
52
52
  end
53
53
 
54
54
  def require_target!
55
- return if system("rustup target list --installed 2>/dev/null | grep -qx #{TARGET}")
55
+ return if RustBuild.target_installed?(TARGET)
56
56
 
57
57
  raise Failure, <<~MSG
58
58
  #{TARGET} isn't installed for this toolchain. Install it once with:
@@ -83,6 +83,17 @@ module Hecks
83
83
  raise Failure, "`#{command.join(" ")}` failed#{" in #{chdir}" if chdir}"
84
84
  end
85
85
 
86
+ # Asks rustup whether a compile target is installed for the active toolchain.
87
+ #
88
+ # @param target [String] the target triple, such as `wasm32-unknown-unknown`
89
+ # @return [Boolean] false when rustup is missing or does not list the target
90
+ def target_installed?(target)
91
+ listed = IO.popen(["rustup", "target", "list", "--installed", { err: File::NULL }], &:read)
92
+ listed.split("\n").include?(target)
93
+ rescue SystemCallError
94
+ false
95
+ end
96
+
86
97
  # Runs a tool and holds what it printed.
87
98
  #
88
99
  # @param tool [String] a key of `TOOLS`
@@ -0,0 +1,53 @@
1
+ require "rack"
2
+
3
+ module Hecks
4
+ # What a domain's world file says it is served by: each `served_by("Adapter") { ... }` line
5
+ # names an inbound adapter and the per-environment values it needs. The route is derived,
6
+ # never written: a driving adapter answers at `/webhooks/<adapter name, snake-cased>` unless
7
+ # its `path` says otherwise, so a world holds only what the domain cannot know (secret,
8
+ # path override) and the same domain can be served differently in each environment.
9
+ #
10
+ # A secret is named by the environment variable that holds it, never inline.
11
+ module ServedOptions
12
+ VERB = "served_by".freeze
13
+
14
+ module_function
15
+
16
+ # @param world [Bluebook::World, nil] a domain's judged world
17
+ # @return [Array<Hash>] one entry per `served_by` line: `adapter`, `path` and its settings
18
+ def entries(world)
19
+ return [] unless world
20
+
21
+ world.settings.filter_map do |key, values|
22
+ next unless key.to_s.start_with?("#{VERB}:")
23
+
24
+ adapter = values.fetch(:adapter)
25
+ values.merge(adapter: adapter, path: values.fetch(:path) { "/webhooks/#{snake(adapter)}" })
26
+ end
27
+ end
28
+
29
+ # A Rack app mounting every `served_by` adapter the world names at its derived path.
30
+ #
31
+ # @param world [Bluebook::World, nil]
32
+ # @param adapters [Hash{String => Class}] driving adapter classes by the name a world uses;
33
+ # each is built with `secret:` read from the environment variable the line names
34
+ # @param env [#fetch] where secrets are read from (`ENV` in production)
35
+ # @return [Rack::URLMap]
36
+ # @raise [KeyError] when a line names an adapter no class answers for
37
+ # @raise [ArgumentError] when a line names a secret variable the environment does not hold
38
+ def rack_app(world, adapters:, env: ENV)
39
+ routes = entries(world).to_h do |entry|
40
+ klass = adapters.fetch(entry[:adapter])
41
+ [entry[:path], klass.new(secret: secret_for(entry, env))]
42
+ end
43
+ Rack::URLMap.new(routes)
44
+ end
45
+
46
+ def secret_for(entry, env)
47
+ name = entry.fetch(:secret) { raise ArgumentError, "#{entry[:adapter]} names no secret" }
48
+ env.fetch(name) { raise ArgumentError, "#{entry[:adapter]}: environment variable #{name} is not set" }
49
+ end
50
+
51
+ def snake(name) = name.to_s.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
52
+ end
53
+ end
@@ -167,6 +167,7 @@ Hecks.bluebook "Site" do
167
167
  end
168
168
 
169
169
  lifecycle :status, default: "requested" do
170
+ mark :failure, "faulted"
170
171
  transition "Complete" => "projected", from: "requested"
171
172
  transition "Fault" => "faulted", from: "requested"
172
173
  end
@@ -33,7 +33,7 @@ module Hecks
33
33
  end
34
34
 
35
35
  # Tails this bus's own dispatch/query/state audit log — a pull-based
36
- # substitute for a push subscription, since a stdio door answers one
36
+ # substitute for a push subscription, since a stdio server answers one
37
37
  # request at a time with no channel to push through.
38
38
  #
39
39
  # @param runtime [Runtime::Registry] the booted domain to tail
@@ -34,9 +34,9 @@ module Hecks
34
34
 
35
35
  # :nodoc:
36
36
  def command_envelope(args, spec)
37
- Doors::CommandRequest.normalize(Doors::JsonDoor.deep_symbolize(args),
38
- receiver: spec[:receiver],
39
- legacy_receiver: spec[:legacy_receiver])
37
+ Adapters::Driving::CommandRequest.normalize(Adapters::Driving::Json.deep_symbolize(args),
38
+ receiver: spec[:receiver],
39
+ legacy_receiver: spec[:legacy_receiver])
40
40
  end
41
41
 
42
42
  # :nodoc:
@@ -52,7 +52,9 @@ module Hecks
52
52
  ok(summary: summary,
53
53
  id: result.id,
54
54
  state: settled_state(runtime, spec, result),
55
- events: result.events.map { |event| { name: event.name, payload: Doors::JsonDoor.materialize(event.payload) } })
55
+ events: result.events.map do |event|
56
+ { name: event.name, payload: Adapters::Driving::Json.materialize(event.payload) }
57
+ end)
56
58
  end
57
59
 
58
60
  # The record as its repository holds it once every reaction has run, which is the state the
@@ -65,7 +67,7 @@ module Hecks
65
67
  def settled_state(runtime, spec, result)
66
68
  return if result.state.nil?
67
69
 
68
- Doors::JsonDoor.materialize(settled_record(runtime, spec, result) || result.state)
70
+ Adapters::Driving::Json.materialize(settled_record(runtime, spec, result) || result.state)
69
71
  end
70
72
 
71
73
  # :nodoc:
@@ -141,7 +143,7 @@ module Hecks
141
143
 
142
144
  # :nodoc:
143
145
  def batch_step(runtime, raw, summary, options)
144
- step = Doors::JsonDoor.deep_symbolize(raw)
146
+ step = Adapters::Driving::Json.deep_symbolize(raw)
145
147
  dispatch(runtime: runtime, command: step[:command], args: step[:args] || {}, summary: summary, **options)
146
148
  end
147
149
  end
@@ -68,7 +68,7 @@ module Hecks
68
68
  # @return [Hash] {ok: true, domain:, valid: true} plus :findings when deep;
69
69
  # {ok: false, domain:, valid: false, error:} if the boot itself failed
70
70
  def validate(domain:, deep: false)
71
- runtime = Hecks.boot(confine!(domain, "domain"), install_doors: false)
71
+ runtime = Hecks.boot(confine!(domain, "domain"), install_driving: false)
72
72
  result = { ok: true, domain: domain, valid: true }
73
73
  result[:findings] = model_findings(runtime) if deep
74
74
  result
@@ -103,7 +103,7 @@ module Hecks
103
103
 
104
104
  # :nodoc:
105
105
  def journal_entries(repository)
106
- repository.entries.map { |entry| { operation: entry.operation, id: entry.id, state: Doors::JsonDoor.materialize(entry.state) } }
106
+ repository.entries.map { |entry| { operation: entry.operation, id: entry.id, state: Adapters::Driving::Json.materialize(entry.state) } }
107
107
  end
108
108
 
109
109
  # Runs a domain's hand-curated .behaviors examples and reports the results.
@@ -27,12 +27,14 @@ module Hecks
27
27
  spec = resolve!(cli_for(request.bluebook), request.name, asking: true)
28
28
  rows = with_caller(request.role, request.actor_id) { answer(request, spec) }
29
29
 
30
- ok(summary: request.summary, rows: rows.map { |row| Doors::JsonDoor.materialize(row) }).merge(verb: spec[:command])
30
+ ok(summary: request.summary, rows: rows.map do |row|
31
+ Adapters::Driving::Json.materialize(row)
32
+ end).merge(verb: spec[:command])
31
33
  end
32
34
 
33
35
  # :nodoc:
34
36
  def answer(request, spec)
35
- request.runtime.query(spec[:command], **Doors::JsonDoor.deep_symbolize(request.args))
37
+ request.runtime.query(spec[:command], **Adapters::Driving::Json.deep_symbolize(request.args))
36
38
  end
37
39
 
38
40
  # Reads one aggregate's stored records directly, bypassing any declared
@@ -63,7 +65,7 @@ module Hecks
63
65
  repository = runtime.registry.repository(bluebook.name, ir)
64
66
  return ok(summary: summary, record: single_record(repository, ir, id)) if id
65
67
 
66
- records = repository.all.map { |instance| Doors::JsonDoor.materialize(instance.to_h) }
68
+ records = repository.all.map { |instance| Adapters::Driving::Json.materialize(instance.to_h) }
67
69
  ok(summary: summary, count: records.length, records: records)
68
70
  end
69
71
 
@@ -71,7 +73,7 @@ module Hecks
71
73
  def single_record(repository, aggregate_ir, identity)
72
74
  instance = repository.find(identity) or
73
75
  raise Runtime::NotFound, "no #{aggregate_ir.hecks_name} found for id #{identity.inspect}"
74
- Doors::JsonDoor.materialize(instance.to_h)
76
+ Adapters::Driving::Json.materialize(instance.to_h)
75
77
  end
76
78
  end
77
79
  end