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,144 @@
1
+ require "digest"
2
+ require "fileutils"
3
+ require "json"
4
+
5
+ module Hecks
6
+ module Adapters
7
+ module Driving
8
+ # Remembers the help a launcher prints, so a repeat `hecks` need not read the domain again.
9
+ #
10
+ # The help is a pure function of the domain's declarations and of the gem that projects them,
11
+ # and reading those is most of what a launcher costs. The answer is kept in a file named for a
12
+ # digest of everything it depends on (the domain's declaration files, the gem's judging code
13
+ # and adapters, the hecks and Ruby versions, the environment overlay, the command line and
14
+ # the program name),
15
+ # so any edit to one of them is simply a different entry: nothing is ever stale, only unused.
16
+ # Only an answer that ended well is kept, and any trouble with the cache itself is ignored
17
+ # and the help is worked out as if it did not exist.
18
+ #
19
+ # `HECKS_NO_USAGE_CACHE=1` turns it off; `HECKS_CACHE_DIR` moves it.
20
+ module UsageCache
21
+ # The variable that turns the cache off when set to anything but empty or `0`.
22
+ DISABLE_VARIABLE = "HECKS_NO_USAGE_CACHE".freeze
23
+ # The variable that names the directory the cache lives in.
24
+ DIRECTORY_VARIABLE = "HECKS_CACHE_DIR".freeze
25
+ # The declaration files whose contents shape a help text.
26
+ DECLARATIONS = "*.{bluebook,world,hecksagon,rb}".freeze
27
+ # How long an unused entry stays before the next write sweeps it away.
28
+ KEEP_SECONDS = 14 * 24 * 60 * 60
29
+
30
+ module_function
31
+
32
+ # Answers the cached help for this command line, or works it out with the block and
33
+ # remembers it.
34
+ #
35
+ # @param runtime [#directory] what `Hecks.describe` answered; anything else bypasses the
36
+ # cache
37
+ # @param argv [Array<String>] the command line
38
+ # @param program [String] how the caller was invoked
39
+ # @param audience [String] who the help is for, since the same line reads differently to
40
+ # each
41
+ # @yield works the answer out, loading the domain
42
+ # @yieldreturn [Array(String, Integer), nil] the text and status; nil if it runs a command
43
+ # @return [Array(String, Integer), nil] the block's answer, or the remembered one
44
+ def fetch(runtime, argv, program, audience: "")
45
+ directory = runtime.respond_to?(:directory) ? runtime.directory : nil
46
+ return yield unless directory && enabled?
47
+
48
+ file = entry_path(directory, argv, program, audience)
49
+ remembered = file && recall(file)
50
+ return remembered if remembered
51
+
52
+ answer = yield
53
+ remember(file, answer) if worth_keeping?(file, answer)
54
+ answer
55
+ end
56
+
57
+ # Whether an answer that ended well can be written to the entry's file.
58
+ def worth_keeping?(file, answer)
59
+ file && answer&.last&.zero?
60
+ end
61
+
62
+ # @return [Boolean] whether the cache is on
63
+ def enabled?
64
+ off = ENV[DISABLE_VARIABLE].to_s
65
+ off.empty? || off == "0"
66
+ end
67
+
68
+ # @return [String] the directory entries live in
69
+ def directory
70
+ ENV[DIRECTORY_VARIABLE] || File.join(ENV["XDG_CACHE_HOME"] || File.join(Dir.home, ".cache"), "hecks")
71
+ end
72
+
73
+ # The file that holds the answer for this command line, named by what it depends on.
74
+ #
75
+ # @return [String, nil] nil when the digest cannot be taken
76
+ def entry_path(domain, argv, program, audience = "")
77
+ digest = Digest::SHA256.new
78
+ key_parts(program, audience, argv).each { |part| digest << part.to_s << "\0" }
79
+ fingerprint_code(domain, digest)
80
+ File.join(directory, "usage-#{digest.hexdigest}.json")
81
+ rescue SystemCallError
82
+ nil
83
+ end
84
+
85
+ # @return [Array<String>] what the answer depends on besides the files: the gem and Ruby
86
+ # series, the environment overlay, the program and audience and the command line
87
+ def key_parts(program, audience, argv)
88
+ [Hecks::VERSION, Hecks::Bluebook::MetaValidator::VerdictCache.ruby_series,
89
+ ENV["HECKS_ENVIRONMENT"].to_s, program, audience, argv.join("\0")]
90
+ end
91
+
92
+ # Feeds the digest every declaration file under `root`: its path below `root` and its bytes,
93
+ # so a touch or a checkout elsewhere keeps the entry and an edit does not.
94
+ def fingerprint(root, digest)
95
+ Dir.glob(File.join(root, "**", DECLARATIONS)).each do |file|
96
+ digest << file.delete_prefix(root) << "\0" << File.binread(file) << "\0"
97
+ end
98
+ end
99
+
100
+ # Feeds the digest what shapes a help text: the domain's declarations, the judging trees of
101
+ # the gem (the same set the verdict cache keys on) and the adapters that render the text. An
102
+ # edit to the gem's projections, deploy or release code cannot change a help line.
103
+ def fingerprint_code(domain, digest)
104
+ fingerprint(domain, digest)
105
+ digest << Hecks::Bluebook::MetaValidator::VerdictCache.code_digest << "\0"
106
+ fingerprint(__dir__, digest)
107
+ end
108
+
109
+ # Reads the remembered answer and marks the entry as used, so a sweep keeps what is read.
110
+ #
111
+ # @return [Array(String, Integer), nil] the remembered answer, or nil when none can be
112
+ # trusted
113
+ def recall(file)
114
+ record = JSON.parse(File.read(file))
115
+ File.utime(nil, nil, file)
116
+ [record.fetch("text"), record.fetch("status")]
117
+ rescue SystemCallError, JSON::ParserError, KeyError
118
+ nil
119
+ end
120
+
121
+ # Writes the answer beside its name and sweeps entries nobody has read for a while.
122
+ def remember(file, answer)
123
+ FileUtils.mkdir_p(File.dirname(file))
124
+ scratch = "#{file}.#{Process.pid}"
125
+ File.write(scratch, JSON.generate("text" => answer.first, "status" => answer.last))
126
+ File.rename(scratch, file)
127
+ sweep(File.dirname(file))
128
+ rescue SystemCallError
129
+ nil
130
+ end
131
+
132
+ # Removes the entries older than `KEEP_SECONDS`.
133
+ def sweep(dir)
134
+ cutoff = Time.now - KEEP_SECONDS
135
+ Dir.glob(File.join(dir, "usage-*.json")).each do |entry|
136
+ File.delete(entry) if File.mtime(entry) < cutoff
137
+ end
138
+ rescue SystemCallError
139
+ nil
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
@@ -1,6 +1,9 @@
1
1
  module Hecks
2
- # The class-free public surface, installed per boot by `Runtime::Loader.bind_runtime`.
3
- module Doors
2
+ module Adapters
3
+ # Driving adapters: how an outside caller reaches in. The Ruby one is the class-free public
4
+ # surface, installed per boot by `Runtime::Loader.bind_runtime`.
5
+ module Driving
6
+ end
4
7
  end
5
8
 
6
9
  # Installs facade constants at top level, replacing only what it installed itself.
@@ -44,7 +47,8 @@ module Hecks
44
47
  end
45
48
  end
46
49
 
47
- require_relative "doors/handle"
48
- require_relative "doors/ruby_door"
49
- require_relative "doors/command_request"
50
- require_relative "doors/json_door"
50
+ require_relative "driving/admission"
51
+ require_relative "driving/handle"
52
+ require_relative "driving/ruby"
53
+ require_relative "driving/command_request"
54
+ require_relative "driving/json"
@@ -43,7 +43,7 @@ module Hecks
43
43
  # Memory-only-guarded runtime
44
44
  # @raise [Malformed] if any aggregate is bound to a non-Memory adapter
45
45
  def boot_and_guard(files)
46
- runtime = Hecks::Runtime::Loader.boot_files(files, install_doors: false)
46
+ runtime = Hecks::Runtime::Loader.boot_files(files, install_driving: false)
47
47
  guard_memory_only!(runtime)
48
48
  runtime
49
49
  end
@@ -35,7 +35,7 @@ module Hecks
35
35
  def boot(workload, adapter, schema)
36
36
  Hecks::Fuzzing::IsolatedBoot.call(workload.domain_path, adapter: adapter, database: DATABASE,
37
37
  schema: schema, scratch: { database: DATABASE, schema: schema }) do |copy|
38
- yield Hecks.boot(copy, install_doors: false)
38
+ yield Hecks.boot(copy, install_driving: false)
39
39
  end
40
40
  end
41
41
 
@@ -0,0 +1,130 @@
1
+ require_relative "../naming"
2
+
3
+ module Hecks
4
+ module Bluebook
5
+ # Maps a policy's `ask :name` onto the port operation the hecksagon declared for it.
6
+ #
7
+ # The match is by the event's aggregate and the ask's name against that aggregate's declared
8
+ # `asks`. When one aggregate has the same ask name on two ports, the operation the hecksagon
9
+ # marked with `ask_via` is the one; otherwise the ask is ambiguous.
10
+ module AskResolution
11
+ # One ask's outcome: the verb it resolves to, or why it resolves to none.
12
+ #
13
+ # @!attribute [r] verb
14
+ # @return [String, nil] `Aggregate::Port.Operation` when resolved, spelled as the
15
+ # `trigger Aggregate::Port::Operation` it stands for is
16
+ # @!attribute [r] problem
17
+ # @return [Symbol, nil] `:no_aggregate`, `:unmatched` or `:ambiguous` when not
18
+ # @!attribute [r] detail
19
+ # @return [String, nil] what the refusal says, naming the aggregate and candidates
20
+ Outcome = Struct.new(:verb, :problem, :detail, keyword_init: true) do
21
+ # @return [Boolean] whether the ask resolved to one operation
22
+ def resolved? = problem.nil?
23
+ end
24
+
25
+ module_function
26
+
27
+ # Resolves one ask policy against a chapter's aggregates and their declared ports.
28
+ #
29
+ # @param chapter [Bluebook::Chapter] the chapter the policy is declared in
30
+ # @param policy [Bluebook::Policy] a policy that `asks?`
31
+ # @return [Outcome] the verb, or the problem
32
+ def call(chapter, policy)
33
+ aggregate = event_aggregate(chapter, policy)
34
+ return no_aggregate(policy) unless aggregate
35
+
36
+ found = picked(candidates(aggregate, policy.ask))
37
+ return unmatched(aggregate, policy) if found.empty?
38
+ return ambiguous(aggregate, policy, found) if found.size > 1
39
+
40
+ port, operation = found.first
41
+ Outcome.new(verb: "#{aggregate.hecks_name}::#{port.name}.#{operation.hecks_name}")
42
+ end
43
+
44
+ # The operations a tie leaves once `ask_via` has spoken: the chosen ones, or all of them
45
+ # when the hecksagon chose none.
46
+ #
47
+ # @return [Array<Array(Bluebook::DomainPort, Bluebook::PortOperation)>]
48
+ def picked(found)
49
+ chosen = found.select { |_port, operation| operation.chosen? }
50
+ found.size > 1 && !chosen.empty? ? chosen : found
51
+ end
52
+
53
+ # Binds every ask policy of a chapter that resolves, leaving the rest for the boot gate and
54
+ # `model_check` to name.
55
+ #
56
+ # @param chapter [Bluebook::Chapter] the chapter whose policies to resolve
57
+ # @return [void]
58
+ def bind_resolved(chapter)
59
+ chapter.policies.select(&:asks?).each do |policy|
60
+ outcome = call(chapter, policy)
61
+ policy.resolve_ask!(outcome.verb) if outcome.resolved?
62
+ end
63
+ end
64
+
65
+ # Marks the named ask on the named port as the one policies reach, for `ask_via`, and
66
+ # re-binds every ask the pick settles.
67
+ #
68
+ # @param chapter [Bluebook::Chapter, nil] the chapter the aggregate belongs to
69
+ # @param aggregate_name [String] the aggregate declaring the port
70
+ # @param name [String, Symbol] the ask's declared name
71
+ # @param port [String, Symbol] the port whose ask is picked
72
+ # @return [void]
73
+ # @raise [Bluebook::DSL::Malformed] if no such ask is declared on that port yet
74
+ def pick!(chapter, aggregate_name, name, port)
75
+ held = chapter&.aggregate(aggregate_name)&.port(port)
76
+ operation = held&.operation(name)
77
+ unless operation
78
+ raise DSL::Malformed, "#{aggregate_name}.ask_via #{name.to_s.inspect}, port: #{port.to_s.inspect} " \
79
+ "names no declared ask — declare the port's `asks` before picking it"
80
+ end
81
+
82
+ operation.choose!
83
+ bind_resolved(chapter)
84
+ end
85
+
86
+ # The aggregate whose event the policy reacts to: the one the event is qualified with, or
87
+ # the one whose commands emit it when the event is written bare.
88
+ #
89
+ # @return [Bluebook::Aggregate, nil]
90
+ def event_aggregate(chapter, policy)
91
+ qualifier = policy.event_qualifier
92
+ return chapter.aggregate(Naming.demodulise(qualifier)) if qualifier
93
+
94
+ chapter.aggregates.find do |aggregate|
95
+ aggregate.commands.any? { |command| Array(command.emits).include?(policy.event_name) }
96
+ end
97
+ end
98
+
99
+ # @return [Array<Array(Bluebook::DomainPort, Bluebook::PortOperation)>] each outbound
100
+ # operation on the aggregate's ports the ask name spells
101
+ def candidates(aggregate, ask)
102
+ aggregate.ports.flat_map do |port|
103
+ port.operations.select { |op| op.outbound? && Naming.snake(op.hecks_name) == ask.to_s }
104
+ .map { |operation| [port, operation] }
105
+ end
106
+ end
107
+
108
+ def no_aggregate(policy)
109
+ Outcome.new(problem: :no_aggregate,
110
+ detail: "#{policy.name}'s ask #{policy.ask.to_sym.inspect} reacts to " \
111
+ "#{policy.on_event.inspect}, whose aggregate this chapter does not declare")
112
+ end
113
+
114
+ def unmatched(aggregate, policy)
115
+ Outcome.new(problem: :unmatched,
116
+ detail: "#{policy.name}'s ask #{policy.ask.to_sym.inspect} matches no `asks` declared on " \
117
+ "#{aggregate.hecks_name} — declare `asks \"#{Naming.pascal(policy.ask)}\"` " \
118
+ "on one of its ports in the hecksagon")
119
+ end
120
+
121
+ def ambiguous(aggregate, policy, found)
122
+ ports = found.map { |port, _operation| port.name.inspect }.join(" and ")
123
+ Outcome.new(problem: :ambiguous,
124
+ detail: "#{policy.name}'s ask #{policy.ask.to_sym.inspect} is asked on #{ports} of " \
125
+ "#{aggregate.hecks_name} — pick one in the hecksagon with " \
126
+ "`#{aggregate.hecks_name}.ask_via \"#{Naming.pascal(policy.ask)}\", port: \"...\"`")
127
+ end
128
+ end
129
+ end
130
+ end
@@ -75,7 +75,7 @@ module Hecks
75
75
  end
76
76
  end
77
77
 
78
- # Folds `state_field`, `state_start` and `transitions` into the IR's one Lifecycle.
78
+ # Folds `state_field`, `state_start`, `transitions` and `marks` into the IR's one Lifecycle.
79
79
  def lifecycle_of(row)
80
80
  declared = row[:lifecycle]
81
81
  return nil unless declared
@@ -83,7 +83,8 @@ module Hecks
83
83
  Lifecycle.new(
84
84
  field: declared[:field],
85
85
  default: declared[:default],
86
- transitions: transitions(Array(declared[:transitions]))
86
+ transitions: transitions(Array(declared[:transitions])),
87
+ marks: marks(Array(declared[:marks]))
87
88
  )
88
89
  end
89
90
 
@@ -97,6 +98,11 @@ module Hecks
97
98
  end
98
99
  end
99
100
 
101
+ # Regroups the one-row-per-state marks back into `name => states`, in declared order.
102
+ def marks(rows)
103
+ rows.group_by { |held| held[:name].to_s }.transform_values { |held| held.map { |row| row[:state].to_s } }
104
+ end
105
+
100
106
  def from_of(froms)
101
107
  return nil if froms.empty?
102
108
  return froms.first if froms.size == 1
@@ -49,7 +49,7 @@ module Hecks
49
49
  # this entity rather than the aggregate's flat state.
50
50
  invariants: [:invariants, [:each, :invariant]]
51
51
  },
52
- rows: { transitions: :transition_rows, identified_by: :identity_rows },
52
+ rows: { transitions: :transition_rows, marks: :mark_rows, identified_by: :identity_rows },
53
53
  reads: { identified_by: [:each, :identity_path], attributes: [:each, :shape_field],
54
54
  preconditions: [:each, :rule], invariants: [:each, :rule] },
55
55
  derived: {
@@ -57,7 +57,8 @@ module Hecks
57
57
  owner: :parent,
58
58
  state_field: [:folded, :lifecycle, :field],
59
59
  state_start: [:folded, :lifecycle, :default],
60
- transitions: [:folded, :lifecycle, :transitions]
60
+ transitions: [:folded, :lifecycle, :transitions],
61
+ marks: [:folded, :lifecycle, :marks]
61
62
  }
62
63
  ),
63
64
 
@@ -68,6 +69,7 @@ module Hecks
68
69
  aggregate: [:aggregate, :plain],
69
70
  on_event: [:on_event, :plain],
70
71
  trigger_command: [:trigger_command, :plain],
72
+ ask: [:ask, :plain],
71
73
  target_domain: [:target_domain, :plain],
72
74
  expect_undelivered: [:expect_undelivered, :plain],
73
75
  where: [:where, :plain],
@@ -34,7 +34,8 @@ module Hecks
34
34
  projected_fields: [:projected_fields, [:each, :projected_field]],
35
35
  provenance: [:provenance, :plain]
36
36
  },
37
- rows: { transitions: :transition_rows, value_objects: :value_object_names, identified_by: :identity_rows },
37
+ rows: { transitions: :transition_rows, marks: :mark_rows, value_objects: :value_object_names,
38
+ identified_by: :identity_rows },
38
39
  reads: { identified_by: [:each, :identity_path], attributes: [:each_with_id, :attribute],
39
40
  invariants: [:each, :rule], preconditions: [:each, :rule],
40
41
  projected_fields: [:each, :projected_field] },
@@ -43,6 +44,7 @@ module Hecks
43
44
  state_field: [:folded, :lifecycle, :field],
44
45
  state_start: [:folded, :lifecycle, :default],
45
46
  transitions: [:folded, :lifecycle, :transitions],
47
+ marks: [:folded, :lifecycle, :marks],
46
48
  value_objects: :children
47
49
  }
48
50
  ),
@@ -110,6 +110,7 @@ module Hecks
110
110
  case source[:kind].to_s
111
111
  when "argument" then source[:name].to_sym
112
112
  when "state" then StateRef.new(source[:name].to_sym)
113
+ when "expression" then Computed.new(source[:text])
113
114
  else source[:value]
114
115
  end
115
116
  end
@@ -52,7 +52,7 @@ module Hecks
52
52
 
53
53
  # A bare constant in a bluebook is a name, even when Ruby has heard of it.
54
54
  #
55
- # `Doors::RubyDoor` installs aggregate names as top-level constants, which would win
55
+ # `Adapters::Driving::Ruby` installs aggregate names as top-level constants, which would win
56
56
  # over the `const_missing` resolver and silently rebind the attribute. Demodulising
57
57
  # spells `:Target` and `QualityControl::Target` the same.
58
58
  def spell(type)
@@ -78,15 +78,20 @@ module Hecks
78
78
  #
79
79
  # @return [Hash{Symbol => Object}] `{kind: "argument", name:}` for a
80
80
  # command argument, `{kind: "state", name:}` for a state
81
- # self-reference, or `{kind: "literal", value:}` for a literal value
81
+ # self-reference, `{kind: "expression", text:, ast:}` for a computed value, or
82
+ # `{kind: "literal", value:}` for a literal value
82
83
  def classified_source
83
- if source.is_a?(Symbol)
84
- { kind: "argument", name: source.to_s }
85
- elsif source.is_a?(StateRef)
86
- { kind: "state", name: source.name.to_s }
87
- else
88
- { kind: "literal", value: source }
89
- end
84
+ return { kind: "argument", name: source.to_s } if source.is_a?(Symbol)
85
+ return { kind: "state", name: source.name.to_s } if source.is_a?(StateRef)
86
+ return computed_source if source.is_a?(Computed)
87
+
88
+ { kind: "literal", value: source }
89
+ end
90
+
91
+ # A computed value on the wire: its canonical text, and the AST the Rust side reads.
92
+ def computed_source
93
+ tree = Expression::Resolver.parse(source.text)
94
+ { kind: "expression", text: source.text, ast: Expression::AstJson.emit_resolver(tree) }
90
95
  end
91
96
  end
92
97
  end
@@ -18,6 +18,36 @@ module Hecks
18
18
  # @return [String]
19
19
  def event_name = Naming.unqualified(@on_event)
20
20
 
21
+ # Whether this policy puts a need to the outside by name rather than triggering a command.
22
+ #
23
+ # @return [Boolean]
24
+ def asks? = !@ask.to_s.empty?
25
+
26
+ # What the policy reaches, as a reader names it: the trigger, or the ask while no
27
+ # hecksagon has bound it to a port operation.
28
+ #
29
+ # @return [String]
30
+ def reaches = @trigger_command.to_s.empty? && asks? ? "ask :#{@ask}" : @trigger_command.to_s
31
+
32
+ # Binds an `ask` to the verb the hecksagon's declared ask resolves to, so everything
33
+ # that reads `trigger_command` reads the port operation the old spelling named.
34
+ #
35
+ # @param verb [String] the resolved `Aggregate::Port.Operation`
36
+ # @return [String] the verb
37
+ def resolve_ask!(verb) = @trigger_command = verb
38
+
39
+ # The wire form: an ask policy carries `ask` where a trigger policy carries
40
+ # `trigger_command`, and a trigger policy carries no `ask` key at all, so the pinned
41
+ # shape of every existing policy does not move.
42
+ #
43
+ # @return [Hash] the policy's IR
44
+ def to_h
45
+ shape = super
46
+ return shape.except(:ask) unless asks?
47
+
48
+ shape.except(:ask).to_h { |key, value| key == :trigger_command ? [:ask, @ask] : [key, value] }
49
+ end
50
+
21
51
  # Whether `for_each` names a query, turning one reaction into a dispatch per row.
22
52
  #
23
53
  # @return [Boolean]
@@ -0,0 +1,72 @@
1
+ module Hecks
2
+ module Bluebook
3
+ module Capabilities
4
+ # Reads the value a capability entry names ("Aggregate.attribute"): whole seconds, text,
5
+ # or the attribute's own name. Extended onto {Capabilities}, whose singleton methods
6
+ # these become.
7
+ module AttributeValues
8
+ # The whole seconds a duration attribute's `default:` holds: a bare integer, or the
9
+ # `{ value: N }` fill of a one-field value object (how a bluebook types a number).
10
+ #
11
+ # @param default [Object] an attribute's `default:`
12
+ # @return [Integer, nil] the positive whole seconds, or `nil` when it is not that
13
+ def duration_seconds(default)
14
+ seconds = filled_value(default)
15
+ seconds if seconds.is_a?(Integer) && seconds.positive?
16
+ end
17
+
18
+ # The seconds a `:duration` verb ("Aggregate.attribute") resolves to in `chapter`.
19
+ #
20
+ # @param chapter [Bluebook::Chapter] the chapter that declares the aggregate
21
+ # @param verb [String] the entry, spelled "Aggregate.attribute"
22
+ # @return [Integer, nil] the positive whole seconds, or `nil` when the verb names no such
23
+ # attribute or its default is not whole seconds
24
+ def duration_of(chapter, verb)
25
+ duration_seconds(attribute_default(chapter, verb))
26
+ end
27
+
28
+ # The `default:` of the attribute a verb ("Aggregate.attribute") names in `chapter`.
29
+ #
30
+ # @param chapter [Bluebook::Chapter] the chapter that declares the aggregate
31
+ # @param verb [String] the entry, spelled "Aggregate.attribute"
32
+ # @return [Object, nil] that attribute's `default:`, or `nil` when there is none
33
+ def attribute_default(chapter, verb)
34
+ aggregate_name, attribute_name = verb.split(".", 2)
35
+ chapter.aggregate(aggregate_name)&.attributes&.find { |a| a.name.to_s == attribute_name }&.default
36
+ end
37
+
38
+ # A default's value: the bare value, or the `{ value: x }` fill of a one-field value object.
39
+ #
40
+ # @param default [Object] an attribute's `default:`
41
+ # @return [Object] the bare value
42
+ def filled_value(default)
43
+ default.is_a?(Hash) ? default.fetch(:value) { default["value"] } : default
44
+ end
45
+
46
+ # The string a `:text` verb ("Aggregate.attribute") resolves to in `chapter`: the
47
+ # attribute's `default:`, bare or as the `{ value: "..." }` fill of a one-field value
48
+ # object (ADR 0099).
49
+ #
50
+ # @param chapter [Bluebook::Chapter] the chapter that declares the aggregate
51
+ # @param verb [String] the entry, spelled "Aggregate.attribute"
52
+ # @return [String, nil] the non-empty text, or `nil` when the verb names no such attribute
53
+ # or its default is not text
54
+ def text_of(chapter, verb)
55
+ text = filled_value(attribute_default(chapter, verb))
56
+ text if text.is_a?(String) && !text.empty?
57
+ end
58
+
59
+ # The attribute name an `:attribute` verb ("Aggregate.attribute") resolves to in `chapter`.
60
+ #
61
+ # @param chapter [Bluebook::Chapter] the chapter that declares the aggregate
62
+ # @param verb [String] the entry, spelled "Aggregate.attribute"
63
+ # @return [String, nil] the attribute's name, or `nil` when the aggregate lacks it
64
+ def attribute_of(chapter, verb)
65
+ aggregate_name, attribute_name = verb.split(".", 2)
66
+ found = chapter.aggregate(aggregate_name)&.attributes&.any? { |a| a.name.to_s == attribute_name }
67
+ attribute_name if found
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end