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
@@ -30,6 +30,7 @@ module Hecks
30
30
  ["Aggregate", "given"] => :given_impl,
31
31
  ["Aggregate", "projects"] => :projects_impl,
32
32
  ["Lifecycle", "transition"] => :transition_impl,
33
+ ["Lifecycle", "mark"] => :mark_impl,
33
34
  ["Bluebook", "attaches_to"] => :attaches_to_impl,
34
35
  ["Bluebook", "provides"] => :provides_impl,
35
36
  ["Bluebook", "aggregate"] => :aggregate_impl,
@@ -57,6 +58,7 @@ module Hecks
57
58
  ["Entity", "entity"] => :entity_impl,
58
59
  ["Policy", "on"] => :on_impl,
59
60
  ["Policy", "trigger"] => :trigger_impl,
61
+ ["Policy", "ask"] => :ask_impl,
60
62
  ["Policy", "across"] => :across_impl,
61
63
  ["ProcessManager", "starts_on"] => :starts_on_impl,
62
64
  ["ProcessManager", "ends_on"] => :ends_on_impl,
@@ -28,7 +28,7 @@ module Hecks
28
28
  # @raise [ArgumentError] on any other keyword
29
29
  def sets_impl(target, positional_to = UNSET, **ops)
30
30
  refuse_unknown_ops!(ops, KWARG_TO_OP.keys)
31
- ops = ops.merge(to: positional_to) unless ops.key?(:to) || positional_to.equal?(UNSET)
31
+ ops = source_of_to(ops, positional_to)
32
32
 
33
33
  # Only a Symbol can repeat the target's name; a literal value (`to: false`,
34
34
  # a String, ...) never has `.to_sym` to compare in the first place.
@@ -98,6 +98,13 @@ module Hecks
98
98
 
99
99
  private
100
100
 
101
+ # Folds a positional source into `to:`, and reads arithmetic written there
102
+ # (`to: paid * rate / 100`) as the source it spells.
103
+ def source_of_to(ops, positional_to)
104
+ ops = ops.merge(to: positional_to) unless ops.key?(:to) || positional_to.equal?(UNSET)
105
+ ops[:to].is_a?(Operands::Computation) ? ops.merge(to: ops[:to].to_source) : ops
106
+ end
107
+
101
108
  # A command's effects are one update set over the pre-dispatch state — a field
102
109
  # written twice would make declaration order silently significant.
103
110
  def refuse_duplicate_targets!
@@ -0,0 +1,121 @@
1
+ module Hecks
2
+ module Bluebook
3
+ module DSL
4
+ class CommandBuilder
5
+ # Lets a bluebook write arithmetic where a `sets` takes its source: inside a command, a bare
6
+ # name that is one of its arguments or one of its owner's fields reads as an operand, and
7
+ # `+ - * /` over operands and numbers build a {Hecks::Computed}.
8
+ #
9
+ # sets :refund_cents, to: paid_cents * late_percent / 100
10
+ module Operands
11
+ # One node of the arithmetic a `sets` source spells: an operand name, a number, or two
12
+ # nodes joined by an operator. Renders the canonical text the expression grammar reads,
13
+ # parenthesised only where the written grouping needs it.
14
+ class Computation
15
+ # Binding strength of each operator; `*` and `/` bind tighter than `+` and `-`.
16
+ PRECEDENCE = { :+ => 1, :- => 1, :* => 2, :/ => 2 }.freeze
17
+
18
+ attr_reader :operator, :left, :right
19
+
20
+ # @param operator [Symbol, nil] the joining operator, or nil for a name or number
21
+ # @param left [Object] the operand name or number, or the left node
22
+ # @param right [Object, nil] the right node of a joined pair
23
+ def initialize(operator, left, right = nil)
24
+ @operator = operator
25
+ @left = left
26
+ @right = right
27
+ end
28
+
29
+ PRECEDENCE.each_key do |operator|
30
+ define_method(operator) { |other| Computation.new(operator, self, Computation.wrap(other)) }
31
+ end
32
+
33
+ # Lets `100 * paid` work: Ruby asks the right-hand operand to coerce the number.
34
+ #
35
+ # @return [Array<Computation>] the number and this node, as a left and right pair
36
+ def coerce(number) = [Computation.wrap(number), self]
37
+
38
+ # @return [Hecks::Computed, Symbol, Numeric] the source a `sets` records: the canonical
39
+ # text of joined arithmetic, or for a lone operand its argument name, or its number
40
+ def to_source
41
+ return Computed.new(to_text) if operator
42
+
43
+ return left if left.is_a?(Numeric)
44
+
45
+ path? ? Computed.new(left.to_s) : left.to_sym
46
+ end
47
+
48
+ # Reaches a field of the value object a name holds: `charged.cents` is the operand
49
+ # `charged` followed by its field `cents`, read by the same dotted path a `given` reads.
50
+ #
51
+ # @return [Computation] the operand extended by one path segment
52
+ def method_missing(field, *args, &block)
53
+ return super unless field_read?(args, block)
54
+
55
+ Computation.new(nil, "#{left}.#{field}")
56
+ end
57
+
58
+ def respond_to_missing?(field, include_private = false) = field_read?([], nil) || super
59
+
60
+ # @return [Boolean] whether this operand is a dotted path into a value object
61
+ def path? = left.to_s.include?(".")
62
+
63
+ # @return [String] the expression text, such as `paid * rate / 100`
64
+ def to_text
65
+ return operand_text unless operator
66
+
67
+ "#{side_text(left, false)} #{operator} #{side_text(right, true)}"
68
+ end
69
+
70
+ # @param value [Computation, Numeric] a node or a number
71
+ # @return [Computation] the node, or the number wrapped as one
72
+ # @raise [Bluebook::DSL::Malformed] if `value` is neither
73
+ def self.wrap(value)
74
+ return value if value.is_a?(Computation)
75
+ return new(nil, value) if value.is_a?(Numeric)
76
+
77
+ raise Malformed, "a sets expression joins argument names and numbers, not #{value.inspect}"
78
+ end
79
+
80
+ private
81
+
82
+ def operand_text = left.to_s
83
+
84
+ # Only a bare name (or an existing path) can be followed by a field.
85
+ def field_read?(args, block) = operator.nil? && !left.is_a?(Numeric) && args.empty? && block.nil?
86
+
87
+ # A joined side is parenthesised when it binds looser than this operator, or equally
88
+ # on the right, so the printed text parses back to the same grouping.
89
+ def side_text(side, right_side)
90
+ return side.to_text unless side.operator
91
+
92
+ looser = PRECEDENCE.fetch(side.operator) < PRECEDENCE.fetch(operator)
93
+ equal_on_right = right_side && PRECEDENCE.fetch(side.operator) == PRECEDENCE.fetch(operator)
94
+ looser || equal_on_right ? "(#{side.to_text})" : side.to_text
95
+ end
96
+ end
97
+
98
+ private
99
+
100
+ # A bare name the command declares as an argument, or its owner declares as a field,
101
+ # is an operand; anything else keeps the builder's own answer.
102
+ def method_missing(word, *args, **kwargs, &block)
103
+ return Computation.new(nil, word) if operand?(word, args, kwargs, block)
104
+
105
+ super
106
+ end
107
+
108
+ def respond_to_missing?(word, include_private = false)
109
+ operand?(word, [], {}, nil) || super
110
+ end
111
+
112
+ def operand?(word, args, kwargs, block)
113
+ return false unless args.empty? && kwargs.empty? && block.nil?
114
+
115
+ attributes.any? { |attr| attr.name == word } || @owner_attributes.any? { |attr| attr.name == word }
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -4,6 +4,7 @@ require_relative "command_builder/owner"
4
4
  require_relative "command_builder/rules"
5
5
  require_relative "command_builder/mutations"
6
6
  require_relative "command_builder/implicit_attributes"
7
+ require_relative "command_builder/operands"
7
8
  module Hecks
8
9
  module Bluebook
9
10
  module DSL
@@ -17,6 +18,7 @@ module Hecks
17
18
  include WordGate
18
19
  include NeedWord
19
20
  include ImplicitAttributes
21
+ include Operands
20
22
 
21
23
  # Sentinel for "this keyword was never passed" — distinct from Ruby's own
22
24
  # nil/false, so `to: false` doesn't get treated as absent.
@@ -44,6 +44,7 @@ module Hecks
44
44
  @binds = []
45
45
  @subscriptions = []
46
46
  @attachments = []
47
+ @driving = []
47
48
  @bounded = false
48
49
  @translates = []
49
50
  end
@@ -57,6 +58,13 @@ module Hecks
57
58
  # Subscribes to an event from outside this domain's own bluebook.
58
59
  def subscribe(event) = @subscriptions << event.to_s
59
60
 
61
+ # Admits a driving adapter (`Ruby`, `Cli`, `Json` or `Mcp`) to reach this domain. Once any
62
+ # is declared, the adapters left out refuse the domain; none declared leaves it open.
63
+ #
64
+ # @param adapter [String, Symbol] the adapter's name
65
+ # @return [void]
66
+ def driven_by(adapter) = @driving << adapter.to_s
67
+
60
68
  # Attaches a chapter by name and loads it into the current registry, marked bounded.
61
69
  #
62
70
  # Without `from:` the name is a chapter the gem carries (a framework member such as
@@ -111,7 +119,8 @@ module Hecks
111
119
  # No ungoverned-role check here — it runs once on the merged result at verify! time.
112
120
  def build
113
121
  Hecksagon.new(domain: @domain, binds: @binds, subscriptions: @subscriptions,
114
- attachments: @attachments, bounded: @bounded, translates: @translates)
122
+ attachments: @attachments, bounded: @bounded, translates: @translates,
123
+ driving: @driving)
115
124
  end
116
125
 
117
126
  # Loads a chapter the gem carries and records it as attached from the gem.
@@ -14,6 +14,32 @@ module Hecks
14
14
  @field = field
15
15
  @default = default
16
16
  @transitions = []
17
+ @marks = {}
18
+ end
19
+
20
+ # Names a meaning and the states that carry it, such as
21
+ # `mark :holds_seat, "pending", "succeeded"`.
22
+ #
23
+ # Listed in `BOOTSTRAP_CALLS_FALLBACK` because syntax.bluebook uses it during boot.
24
+ #
25
+ # @param name [Symbol, String] a lowercase word (`holds_seat`)
26
+ # @param states [Array<String, Symbol>] one or more states, each named once
27
+ # @return [Array<String>] the states just recorded
28
+ # @raise [Bluebook::DSL::Malformed] for a name that is not a lowercase word, a name
29
+ # declared twice, no states, or a state named twice in one mark
30
+ def mark_impl(name, *states)
31
+ mark = name.to_s
32
+ unless mark.match?(/\A[a-z][a-z0-9_]*\z/)
33
+ raise Malformed, "lifecycle :#{@field} mark #{name.inspect} is not a lowercase word (such as :holds_seat)"
34
+ end
35
+ raise Malformed, "lifecycle :#{@field} declares mark :#{mark} twice" if @marks.key?(mark)
36
+ raise Malformed, "lifecycle :#{@field} mark :#{mark} names no states" if states.empty?
37
+
38
+ names = states.map(&:to_s)
39
+ twice = names.find { |state| names.count(state) > 1 }
40
+ raise Malformed, "lifecycle :#{@field} mark :#{mark} names state #{twice.inspect} twice" if twice
41
+
42
+ @marks[mark] = names
17
43
  end
18
44
 
19
45
  # Records one transition row per command in `mapping`, all sharing its `from:` guard.
@@ -40,10 +66,12 @@ module Hecks
40
66
  #
41
67
  # @return [Bluebook::Lifecycle] the state machine: its field, default and transitions
42
68
  # @raise [Bluebook::DSL::Malformed] if two transitions for one command could both apply
43
- # from the same state (C5.3); skipped while shadow-parsing frozen era text
69
+ # from the same state (C5.3; skipped while shadow-parsing frozen era text), or a mark
70
+ # names a state the lifecycle does not have
44
71
  def build
45
72
  refuse_ambiguity!
46
- Lifecycle.new(field: @field, default: @default, transitions: @transitions)
73
+ refuse_unknown_mark_states!
74
+ Lifecycle.new(field: @field, default: @default, transitions: @transitions, marks: @marks)
47
75
  end
48
76
 
49
77
  # Evaluates a `lifecycle` block against a fresh builder and returns what it built.
@@ -62,6 +90,21 @@ module Hecks
62
90
 
63
91
  private
64
92
 
93
+ # A mark may name only the default or a transition target: anything else is a typo that
94
+ # would silently mark nothing. (A `from:` state is not enough; it is not reachable.)
95
+ def refuse_unknown_mark_states!
96
+ known = ([@default] + @transitions.map { |(_command, transition)| transition.target }).map(&:to_s)
97
+
98
+ @marks.each do |mark, states|
99
+ unknown = states - known
100
+ next if unknown.empty?
101
+
102
+ raise Malformed,
103
+ "lifecycle :#{@field} mark :#{mark} names #{unknown.map(&:inspect).join(", ")}, which " \
104
+ "is not a state of the lifecycle (states: #{known.uniq.join(", ")})"
105
+ end
106
+ end
107
+
65
108
  # C5.3 (docs/semantics/bluebook-semantics.md): overlapping `from:` sets for one command
66
109
  # are refused. Disjoint sets are legitimate. An undeclared `from:` state is left to
67
110
  # `hecks model_check`.
@@ -39,6 +39,8 @@ module Hecks
39
39
  # @return [void]
40
40
  # @raise [Bluebook::DSL::Malformed] if `command_ref` is quoted text outside shadow-parsing
41
41
  def trigger_impl(command_ref, with: nil)
42
+ raise Malformed, "#{@name} declares both a trigger and an ask — a policy does one" if @ask
43
+
42
44
  if command_ref.is_a?(::String) && !MetaValidator.shadow_parsing?
43
45
  raise Malformed,
44
46
  "#{@name}'s trigger #{command_ref.inspect} is quoted text — give the bare command " \
@@ -50,6 +52,26 @@ module Hecks
50
52
  @projection_declared = !with.nil?
51
53
  end
52
54
 
55
+ # Records the need this policy puts to the outside, in the domain's own words.
56
+ #
57
+ # The runtime resolves the name against the hecksagon's declared asks (the event's
58
+ # aggregate and the ask's name) and dispatches the port operation the match names, as
59
+ # `trigger` would have. `with:` projects the event onto the operation as it does for
60
+ # `trigger`.
61
+ #
62
+ # @param name [Symbol, String] the need, such as `:check`
63
+ # @param with [Hash{Symbol => Object}, nil] event payload to operation arguments;
64
+ # `nil` forwards the whole payload
65
+ # @return [void]
66
+ # @raise [Bluebook::DSL::Malformed] if the policy already triggers a command
67
+ def ask_impl(name, with: nil)
68
+ raise Malformed, "#{@name} declares both a trigger and an ask — a policy does one" if @trigger_command
69
+
70
+ @ask = name.to_s
71
+ @with_spec = (with || {}).to_a
72
+ @projection_declared = !with.nil?
73
+ end
74
+
53
75
  # Records the domain this policy's trigger reaches into.
54
76
  #
55
77
  # `expect_undelivered: true` declares that the target is expected never to be reached
@@ -96,16 +118,9 @@ module Hecks
96
118
  # @return [Bluebook::Policy] the built policy, carrying every field set by `on`,
97
119
  # `trigger`, `across`, `where`, `for_each`, and `with:`
98
120
  def build
99
- Policy.new(
100
- name: @name,
101
- on_event: @on_event,
102
- trigger_command: @trigger_command,
103
- target_domain: @target_domain,
104
- expect_undelivered: @expect_undelivered || false,
105
- where: @where,
106
- for_each: @for_each,
107
- with_spec: @with_spec || []
108
- ).tap { |policy| policy.instance_variable_set(:@projection_declared, !!@projection_declared) }
121
+ refuse_ask_across!
122
+ Policy.new(**reaction_fields, **guard_fields)
123
+ .tap { |policy| policy.instance_variable_set(:@projection_declared, !!@projection_declared) }
109
124
  end
110
125
 
111
126
  # Builds a `Policy` from a `policy "Name" do ... end` block.
@@ -118,6 +133,28 @@ module Hecks
118
133
  builder.instance_eval(&block) if block
119
134
  builder.build
120
135
  end
136
+
137
+ private
138
+
139
+ # What the policy reacts to and what it reaches.
140
+ def reaction_fields
141
+ { name: @name, on_event: @on_event, trigger_command: @trigger_command, ask: @ask,
142
+ target_domain: @target_domain }
143
+ end
144
+
145
+ # What decides whether it fires, and what it hands over.
146
+ def guard_fields
147
+ { expect_undelivered: @expect_undelivered || false, where: @where, for_each: @for_each,
148
+ with_spec: @with_spec || [] }
149
+ end
150
+
151
+ # An ask resolves against the ports of the domain it is declared in, so `across` has
152
+ # nothing to add to it.
153
+ def refuse_ask_across!
154
+ return unless @ask && @target_domain
155
+
156
+ raise Malformed, "#{@name} declares both an ask and across — an ask resolves in its own domain"
157
+ end
121
158
  end
122
159
  end
123
160
  end
@@ -6,6 +6,9 @@ module Hecks
6
6
  # The three "declared once, referenced by name" resolution primitives shared by
7
7
  # every given/invariant/ensures scope in this DSL (ADR 0025).
8
8
  module RuleReference
9
+ # Raised when the syntax table names a resolution this module cannot perform.
10
+ class Error < RuntimeError; end
11
+
9
12
  module_function
10
13
 
11
14
  # Extracts a predicate's source and builds the rule struct (Given or Invariant),
@@ -94,10 +97,10 @@ module Hecks
94
97
  actual = lookup(word, context)[:resolves_via]
95
98
  return if actual == expected_primitive
96
99
 
97
- raise "internal: syntax.bluebook says #{word}/#{context} resolves via " \
98
- "#{actual.inspect}, but #{word}'s own Ruby builder is about to use " \
99
- "#{expected_primitive.inspect} — the grammar table and the " \
100
- "implementation have drifted"
100
+ raise Error, "internal: syntax.bluebook says #{word}/#{context} resolves via " \
101
+ "#{actual.inspect}, but #{word}'s own Ruby builder is about to use " \
102
+ "#{expected_primitive.inspect} — the grammar table and the " \
103
+ "implementation have drifted"
101
104
  end
102
105
  end
103
106
  end
@@ -102,7 +102,7 @@ module Hecks
102
102
  end
103
103
 
104
104
  class << self
105
- # The builder whose block is being evaluated, so an aggregate door a facade already
105
+ # The builder whose block is being evaluated, so an aggregate module a facade already
106
106
  # installed (a repeat boot in one process) can record a qualified bind into it.
107
107
  #
108
108
  # @return [Bluebook::DSL::WorldBuilder, nil] nil outside a `.world` block
@@ -55,6 +55,9 @@ module Hecks
55
55
  # Segments, as `find.path` has, so a reader need not split a dotted string.
56
56
  Resolver::Lookup => ->(node) { { "op" => "lookup", "path" => node.path.split(".") } },
57
57
  Resolver::Addition => binary("add", :emit_resolver),
58
+ Resolver::Subtraction => binary("sub", :emit_resolver),
59
+ Resolver::Multiplication => binary("mul", :emit_resolver),
60
+ Resolver::Division => binary("div", :emit_resolver),
58
61
  Resolver::SignTest => lambda { |node|
59
62
  { "op" => "sign_test", "cmp" => AstJson.emit_comparison(node.operator),
60
63
  "receiver" => AstJson.emit_resolver(node.receiver) }
@@ -81,6 +84,9 @@ module Hecks
81
84
  Resolver::Presence => receiver_with("presence", :negated),
82
85
  Resolver::Assignment => receiver_with("assignment", :negated),
83
86
  Resolver::Split => receiver_with("split", :separator),
87
+ Resolver::Strip => lambda { |node|
88
+ { "op" => "strip", "receiver" => AstJson.emit_resolver(node.receiver), "side" => node.side.to_s }
89
+ },
84
90
  Resolver::StartsWith => receiver_with("starts_with", :substring),
85
91
  Resolver::EndsWith => receiver_with("ends_with", :substring),
86
92
  Resolver::First => receiver_only("first"),
@@ -9,6 +9,9 @@ module Hecks
9
9
  # Lives in core because rust/host cannot link the kernel crate and interprets this from
10
10
  # ir.json.
11
11
  module AstJson
12
+ # Raised for an expression node that no JSON rendering exists for.
13
+ class UnhandledNode < RuntimeError; end
14
+
12
15
  module_function
13
16
 
14
17
  # The closed roster of `"op"` tags. A new node kind needs an entry here and an arm in
@@ -16,9 +19,9 @@ module Hecks
16
19
  OPS = %w[
17
20
  or and not compare include
18
21
  int float str bool nil array lookup
19
- add modulo sign_test empty size to_s
22
+ add sub mul div modulo sign_test empty size to_s
20
23
  block_predicate find first last
21
- matches_regex presence assignment split starts_with ends_with
24
+ matches_regex presence assignment split strip starts_with ends_with
22
25
  ].freeze
23
26
 
24
27
  # One rule row: description, canonical text, and the AST derived from that text.
@@ -119,8 +122,8 @@ module Hecks
119
122
  _, emitter = Emitters::BOOL.find { |klass, _| node.is_a?(klass) }
120
123
  return emitter.call(node) if emitter
121
124
 
122
- raise "unhandled evaluator node #{node.class} — no JSON rendering exists for it " \
123
- "(lib/hecks/bluebook/expression/ast_json.rb#emit_bool)"
125
+ raise UnhandledNode, "unhandled evaluator node #{node.class} — no JSON rendering exists for it " \
126
+ "(lib/hecks/bluebook/expression/ast_json.rb#emit_bool)"
124
127
  end
125
128
 
126
129
  # Emits the JSON form of one comparison operator.
@@ -161,8 +164,8 @@ module Hecks
161
164
  _, emitter = Emitters::RESOLVER.find { |klass, _| node.is_a?(klass) }
162
165
  return emitter.call(node) if emitter
163
166
 
164
- raise "unhandled resolver node #{node.class} — no JSON rendering exists for it " \
165
- "(lib/hecks/bluebook/expression/ast_json.rb#emit_resolver)"
167
+ raise UnhandledNode, "unhandled resolver node #{node.class} — no JSON rendering exists for it " \
168
+ "(lib/hecks/bluebook/expression/ast_json.rb#emit_resolver)"
166
169
  end
167
170
  end
168
171
  end
@@ -4,6 +4,9 @@ require_relative "resolver"
4
4
  module Hecks
5
5
  module Bluebook
6
6
  module Expression
7
+ # Raised for an operator `AstReader` has no arm for.
8
+ class UnhandledOp < RuntimeError; end
9
+
7
10
  # The inverse of `AstJson`: reads a rule row's `"op"`-tagged `ast` back into the
8
11
  # `Evaluator`/`Resolver` nodes, so dispatch evaluates a rule without re-parsing its text.
9
12
  module AstReader
@@ -33,9 +36,7 @@ module Hecks
33
36
  # @param klass [Class] `Evaluator::Or` or `Evaluator::And`
34
37
  # @param json [Hash] the node's `"left"` and `"right"` boolean children
35
38
  # @return [Object] the node `klass` builds over them
36
- def read_pair(klass, json)
37
- klass.new(left: read_bool(json["left"]), right: read_bool(json["right"]))
38
- end
39
+ def read_pair(klass, json) = klass.new(left: read_bool(json["left"]), right: read_bool(json["right"]))
39
40
 
40
41
  # @param json [Hash] a `"compare"` node
41
42
  # @return [Evaluator::Compare] its operator and both resolver sides
@@ -51,7 +52,7 @@ module Hecks
51
52
  op.compares_less_than == cmp.fetch("less_than") &&
52
53
  op.compares_equal == cmp.fetch("equal") &&
53
54
  op.negated == cmp.fetch("negated")
54
- end or raise "no comparison operator has the triple #{cmp.inspect}"
55
+ end or raise UnhandledOp, "no comparison operator has the triple #{cmp.inspect}"
55
56
  end
56
57
 
57
58
  # The readers `read_resolver` tries in turn; each answers `nil` for an op it does not own.
@@ -65,7 +66,7 @@ module Hecks
65
66
  node = public_send(reader, json)
66
67
  return node if node
67
68
  end
68
- raise "no reader handles op #{op.inspect} — add an arm before AstJson can emit it"
69
+ raise UnhandledOp, "no reader handles op #{op.inspect} — add an arm before AstJson can emit it"
69
70
  end
70
71
 
71
72
  # @param json [Hash] a resolver node
@@ -80,6 +81,8 @@ module Hecks
80
81
  end
81
82
  end
82
83
 
84
+ def read_binary(node_class, json) = node_class.new(left: read_resolver(json["left"]), right: read_resolver(json["right"]))
85
+
83
86
  # @param json [Hash] a resolver node
84
87
  # @return [Object, nil] the array or lookup it spells, or `nil` for any other op
85
88
  def read_collection(json)
@@ -93,7 +96,10 @@ module Hecks
93
96
  # @return [Object, nil] the sum, remainder or sign test it spells, or `nil` for any other op
94
97
  def read_arithmetic(json)
95
98
  case json["op"]
96
- when "add" then Resolver::Addition.new(left: read_resolver(json["left"]), right: read_resolver(json["right"]))
99
+ when "add" then read_binary(Resolver::Addition, json)
100
+ when "sub" then read_binary(Resolver::Subtraction, json)
101
+ when "mul" then read_binary(Resolver::Multiplication, json)
102
+ when "div" then read_binary(Resolver::Division, json)
97
103
  when "modulo" then Resolver::Modulo.new(receiver: receiver(json), divisor: read_resolver(json["divisor"]))
98
104
  when "sign_test"
99
105
  op = operator(json["cmp"])
@@ -110,6 +116,7 @@ module Hecks
110
116
  when "size" then Resolver::Size.new(receiver: receiver(json))
111
117
  when "first" then Resolver::First.new(receiver: receiver(json))
112
118
  when "last" then Resolver::Last.new(receiver: receiver(json))
119
+ when "strip" then Resolver::Strip.new(receiver: receiver(json), side: json["side"].to_sym)
113
120
  end
114
121
  end
115
122
 
@@ -152,9 +159,7 @@ module Hecks
152
159
 
153
160
  # `SignTest#test` is only refusal wording; recovering it keeps a rebuilt node's message
154
161
  # identical to the parsed one's.
155
- def sign_test_name(operator)
156
- Resolver::SIGN_TEST_OPERATORS.key(operator.symbol) || operator.symbol
157
- end
162
+ def sign_test_name(operator) = Resolver::SIGN_TEST_OPERATORS.key(operator.symbol) || operator.symbol
158
163
  end
159
164
  end
160
165
  end
@@ -209,6 +209,42 @@
209
209
  "category": "presence",
210
210
  "precedence": 7,
211
211
  "arity": 1
212
+ },
213
+ {
214
+ "symbol": ".strip",
215
+ "category": "text",
216
+ "precedence": 7,
217
+ "arity": 1
218
+ },
219
+ {
220
+ "symbol": ".lstrip",
221
+ "category": "text",
222
+ "precedence": 7,
223
+ "arity": 1
224
+ },
225
+ {
226
+ "symbol": ".rstrip",
227
+ "category": "text",
228
+ "precedence": 7,
229
+ "arity": 1
230
+ },
231
+ {
232
+ "symbol": "-",
233
+ "category": "arithmetic",
234
+ "precedence": 6,
235
+ "arity": 2
236
+ },
237
+ {
238
+ "symbol": "*",
239
+ "category": "arithmetic",
240
+ "precedence": 7,
241
+ "arity": 2
242
+ },
243
+ {
244
+ "symbol": "/",
245
+ "category": "arithmetic",
246
+ "precedence": 7,
247
+ "arity": 2
212
248
  }
213
249
  ],
214
250
  "normalisations": [
@@ -24,6 +24,7 @@ module Hecks
24
24
  Presence => ->(node, value, _state, _attrs) { Resolver.presence_of(node, value) },
25
25
  Assignment => ->(node, value, _state, _attrs) { Resolver.assignment_of(node, value) },
26
26
  Split => ->(node, value, _state, _attrs) { Resolver.split_value(value, node.separator) },
27
+ Strip => ->(node, value, _state, _attrs) { Resolver.strip_value(value, node.side) },
27
28
  Last => ->(_node, value, _state, _attrs) { Resolver.last_of(value) },
28
29
  First => ->(_node, value, _state, _attrs) { Resolver.first_of(value) },
29
30
  Find => ->(node, value, state, attrs) { Resolver.found_of(node, value, state, attrs) },
@@ -46,6 +47,36 @@ module Hecks
46
47
  raise EvaluationError, "addition overflowed: #{lhs} + #{rhs} is not a finite number"
47
48
  end
48
49
 
50
+ def subtract(left, right)
51
+ checked_arithmetic("subtraction", "-", left, right) { |lhs, rhs| lhs - rhs }
52
+ end
53
+
54
+ def multiply(left, right)
55
+ checked_arithmetic("multiplication", "*", left, right) { |lhs, rhs| lhs * rhs }
56
+ end
57
+
58
+ # Integer operands floor toward negative infinity; a zero divisor faults like `.modulo`.
59
+ def divide(left, right)
60
+ raise EvaluationError, "divided by 0" if require_number(right, "division").zero?
61
+
62
+ checked_arithmetic("division", "/", left, right) { |lhs, rhs| lhs / rhs }
63
+ end
64
+
65
+ # Applies `operation` to two numbers, refusing an Integer outside 64 bits or a Float that
66
+ # is not finite, as `add` does.
67
+ def checked_arithmetic(name, symbol, left, right)
68
+ lhs = require_number(left, name)
69
+ rhs = require_number(right, name)
70
+ result = yield(lhs, rhs)
71
+ return result if representable?(result)
72
+
73
+ raise EvaluationError, "#{name} overflowed: #{lhs} #{symbol} #{rhs} #{overflow_reason(result)}"
74
+ end
75
+
76
+ def representable?(number) = number.is_a?(Integer) ? INT64_RANGE.cover?(number) : number.finite?
77
+
78
+ def overflow_reason(number) = number.is_a?(Integer) ? "does not fit in a 64-bit integer" : "is not a finite number"
79
+
49
80
  def apply_sign_test(node, value)
50
81
  number = numeric(value)
51
82
  raise EvaluationError, "#{node.test} expects a number, got #{describe(value)}" unless number