hecks 3.10.0 → 3.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (481) hide show
  1. checksums.yaml +4 -4
  2. data/examples/pizzas/bluebook/pizzas.bluebook +29 -0
  3. data/examples/pizzas/bluebook/pizzas.hecksagon +7 -0
  4. data/exe/hecks +4 -4
  5. data/lib/hecks/adapters/driven/d1.rb +2 -10
  6. data/lib/hecks/adapters/driven/heki.adapter +1 -1
  7. data/lib/hecks/adapters/driven/heki.rb +2 -12
  8. data/lib/hecks/adapters/driven/lambda.adapter +1 -1
  9. data/lib/hecks/adapters/driven/lambda.rb +3 -18
  10. data/lib/hecks/adapters/driven/postgres.rb +4 -11
  11. data/lib/hecks/adapters/driven/sqlite.adapter +1 -1
  12. data/lib/hecks/adapters/driven/sqlite.rb +2 -10
  13. data/lib/hecks/adapters/driving/admission.rb +46 -0
  14. data/lib/hecks/adapters/driving/cli/nesting.rb +36 -0
  15. data/lib/hecks/adapters/driving/cli/shorthand.rb +88 -0
  16. data/lib/hecks/adapters/driving/cli.rb +127 -0
  17. data/lib/hecks/adapters/driving/cli_runner/answers.rb +160 -0
  18. data/lib/hecks/adapters/driving/cli_runner/resolution.rb +155 -0
  19. data/lib/hecks/adapters/driving/cli_runner/settling.rb +120 -0
  20. data/lib/hecks/adapters/driving/cli_runner/streaming.rb +93 -0
  21. data/lib/hecks/adapters/driving/cli_runner/suggestions.rb +47 -0
  22. data/lib/hecks/adapters/driving/cli_runner.rb +69 -0
  23. data/lib/hecks/adapters/driving/command_request.rb +121 -0
  24. data/lib/hecks/adapters/driving/handle.rb +214 -0
  25. data/lib/hecks/adapters/driving/json.rb +136 -0
  26. data/lib/hecks/adapters/driving/launcher_options.rb +267 -0
  27. data/lib/hecks/adapters/driving/mcp/properties.rb +49 -0
  28. data/lib/hecks/adapters/driving/mcp/tool_calls.rb +116 -0
  29. data/lib/hecks/adapters/driving/mcp/tool_definitions/auditing.rb +53 -0
  30. data/lib/hecks/adapters/driving/mcp/tool_definitions/calling.rb +71 -0
  31. data/lib/hecks/adapters/driving/mcp/tool_definitions/reading.rb +99 -0
  32. data/lib/hecks/adapters/driving/mcp/tool_definitions.rb +18 -0
  33. data/lib/hecks/adapters/driving/mcp.rb +159 -0
  34. data/lib/hecks/adapters/driving/mcp_guide_cache.rb +78 -0
  35. data/lib/hecks/adapters/driving/mcp_scope/argument_policy.rb +97 -0
  36. data/lib/hecks/adapters/driving/mcp_scope/env_problems.rb +69 -0
  37. data/lib/hecks/adapters/driving/mcp_scope/presenter.rb +83 -0
  38. data/lib/hecks/adapters/driving/mcp_scope.rb +213 -0
  39. data/lib/hecks/adapters/driving/ruby/aggregate_module.rb +171 -0
  40. data/lib/hecks/adapters/driving/ruby/chapter.rb +91 -0
  41. data/lib/hecks/adapters/driving/ruby.rb +83 -0
  42. data/lib/hecks/adapters/driving/usage_cache.rb +144 -0
  43. data/lib/hecks/{doors.rb → adapters/driving.rb} +10 -6
  44. data/lib/hecks/behaviors/expectations/boot.rb +1 -1
  45. data/lib/hecks/bench/ruby_runner.rb +1 -1
  46. data/lib/hecks/bluebook/ask_resolution.rb +130 -0
  47. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +8 -2
  48. data/lib/hecks/bluebook/assembly/contracts/rules.rb +4 -2
  49. data/lib/hecks/bluebook/assembly/contracts/structure.rb +3 -1
  50. data/lib/hecks/bluebook/assembly/marks.rb +1 -0
  51. data/lib/hecks/bluebook/attribute.rb +1 -1
  52. data/lib/hecks/bluebook/behaviour/command.rb +13 -8
  53. data/lib/hecks/bluebook/behaviour/policy.rb +30 -0
  54. data/lib/hecks/bluebook/capabilities/attribute_values.rb +72 -0
  55. data/lib/hecks/bluebook/capabilities.rb +91 -9
  56. data/lib/hecks/bluebook/domain_port.rb +16 -4
  57. data/lib/hecks/bluebook/dsl/adapter_builder.rb +18 -6
  58. data/lib/hecks/bluebook/dsl/binding_proxy.rb +13 -0
  59. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/computed_sources.rb +98 -0
  60. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/fan_out_rows.rb +42 -0
  61. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/projected_fields.rb +14 -2
  62. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/provisions.rb +69 -3
  63. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/with_projections.rb +17 -10
  64. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +5 -0
  65. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -0
  66. data/lib/hecks/bluebook/dsl/command_builder/mutations.rb +8 -1
  67. data/lib/hecks/bluebook/dsl/command_builder/operands.rb +121 -0
  68. data/lib/hecks/bluebook/dsl/command_builder.rb +2 -0
  69. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +10 -1
  70. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +45 -2
  71. data/lib/hecks/bluebook/dsl/policy_builder.rb +47 -10
  72. data/lib/hecks/bluebook/dsl/rule_reference.rb +7 -4
  73. data/lib/hecks/bluebook/dsl/world_builder.rb +1 -1
  74. data/lib/hecks/bluebook/expression/ast_json/emitters.rb +6 -0
  75. data/lib/hecks/bluebook/expression/ast_json.rb +9 -6
  76. data/lib/hecks/bluebook/expression/ast_reader.rb +14 -9
  77. data/lib/hecks/bluebook/expression/projection.json +36 -0
  78. data/lib/hecks/bluebook/expression/resolver/operations.rb +31 -0
  79. data/lib/hecks/bluebook/expression/resolver/parsing.rb +38 -5
  80. data/lib/hecks/bluebook/expression/resolver/scanning.rb +22 -5
  81. data/lib/hecks/bluebook/expression/resolver/values.rb +10 -0
  82. data/lib/hecks/bluebook/expression/resolver.rb +20 -1
  83. data/lib/hecks/bluebook/hecksagon.rb +23 -3
  84. data/lib/hecks/bluebook/lifecycle.rb +22 -3
  85. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +13 -1
  86. data/lib/hecks/bluebook/meta_validator/modes.rb +4 -1
  87. data/lib/hecks/bluebook/meta_validator/readings.rb +18 -1
  88. data/lib/hecks/bluebook/meta_validator/reconstruction/heads.rb +2 -1
  89. data/lib/hecks/bluebook/meta_validator/shapes.rb +5 -0
  90. data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +1 -1
  91. data/lib/hecks/bluebook/meta_validator.rb +12 -12
  92. data/lib/hecks/bluebook/model_check/ask_checks.rb +71 -0
  93. data/lib/hecks/bluebook/model_check/policy_checks.rb +2 -1
  94. data/lib/hecks/bluebook/model_check/reference_checks.rb +70 -0
  95. data/lib/hecks/bluebook/model_check.rb +8 -2
  96. data/lib/hecks/bluebook/policy.rb +4 -2
  97. data/lib/hecks/bluebook/smoke_test.rb +1 -1
  98. data/lib/hecks/bluebook/synthesizer.rb +2 -0
  99. data/lib/hecks/bluebook.rb +1 -0
  100. data/lib/hecks/cli/command_table.rb +1 -1
  101. data/lib/hecks/cli/console.rb +2 -1
  102. data/lib/hecks/cli/document.rb +1 -1
  103. data/lib/hecks/cli/launcher_words.rb +4 -4
  104. data/lib/hecks/cli/mcp.rb +4 -4
  105. data/lib/hecks/cli/project_cli/launcher_source.rb +2 -2
  106. data/lib/hecks/cli/project_cli/templates.rb +5 -5
  107. data/lib/hecks/cli/project_cli.rb +9 -6
  108. data/lib/hecks/cli/run.rb +2 -2
  109. data/lib/hecks/cli/smoke_http/checks.rb +4 -1
  110. data/lib/hecks/cli/smoke_http/transport.rb +3 -3
  111. data/lib/hecks/cli/smoke_http.rb +3 -0
  112. data/lib/hecks/codemod.rb +5 -2
  113. data/lib/hecks/corpus/routes.rb +7 -0
  114. data/lib/hecks/corpus/rust.rb +3 -2
  115. data/lib/hecks/deploy/bluebook/deploy.bluebook +18 -2
  116. data/lib/hecks/doc/reference/rendering.rb +6 -3
  117. data/lib/hecks/forms/app/commands.rb +8 -7
  118. data/lib/hecks/forms/app/queries.rb +2 -1
  119. data/lib/hecks/forms/app.rb +1 -1
  120. data/lib/hecks/forms/record_renderer.rb +1 -1
  121. data/lib/hecks/framework/bluebook/privacy.bluebook +11 -0
  122. data/lib/hecks/fuzzing/concurrent_dispatch.rb +4 -1
  123. data/lib/hecks/fuzzing/failure_triage.rb +2 -1
  124. data/lib/hecks/fuzzing/mutation/run.rb +11 -4
  125. data/lib/hecks/fuzzing/properties/catalog.rb +6 -4
  126. data/lib/hecks/fuzzing/properties/guaranteed_by_construction.rb +18 -11
  127. data/lib/hecks/fuzzing/properties/guards.rb +1 -1
  128. data/lib/hecks/fuzzing/self_consistency/saga_redelivery.rb +1 -1
  129. data/lib/hecks/fuzzing/self_consistency.rb +6 -2
  130. data/lib/hecks/fuzzing/shrinker.rb +3 -1
  131. data/lib/hecks/fuzzing/value_generator.rb +19 -5
  132. data/lib/hecks/gate/stages.yml +4 -10
  133. data/lib/hecks/grammar/evolve.rb +1 -1
  134. data/lib/hecks/grammar/expression_operators.json +372 -0
  135. data/lib/hecks/grammar/operators.rb +3 -1
  136. data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +10 -10
  137. data/lib/hecks/hecks/adapters/codebase/inspection/rubycritic.rb +35 -0
  138. data/lib/hecks/hecks/adapters/codebase/inspection.rb +104 -0
  139. data/lib/hecks/hecks/adapters/codebase/publishing.rb +2 -1
  140. data/lib/hecks/hecks/adapters/codebase/release_facts.rb +12 -2
  141. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +2 -1
  142. data/lib/hecks/hecks/adapters/codebase/test_runner.rb +1 -1
  143. data/lib/hecks/hecks/adapters/cost_explorer.rb +8 -5
  144. data/lib/hecks/hecks/adapters/deploy_toolchain/statuses.rb +2 -1
  145. data/lib/hecks/hecks/adapters/finding_github.rb +13 -6
  146. data/lib/hecks/hecks/adapters/first_admin.rb +2 -2
  147. data/lib/hecks/hecks/adapters/github_rulesets.rb +6 -3
  148. data/lib/hecks/hecks/adapters/in_process_boot.rb +1 -1
  149. data/lib/hecks/hecks/adapters/journal_store/compaction.rb +1 -1
  150. data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +10 -20
  151. data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +28 -5
  152. data/lib/hecks/hecks/adapters/local_files.rb +2 -2
  153. data/lib/hecks/hecks/adapters/rehearsal/audit_report.rb +107 -0
  154. data/lib/hecks/hecks/adapters/rehearsal/census.rb +71 -0
  155. data/lib/hecks/hecks/adapters/rehearsal/declaration.rb +133 -0
  156. data/lib/hecks/hecks/adapters/rehearsal/environment.rb +83 -0
  157. data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +142 -0
  158. data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +96 -0
  159. data/lib/hecks/hecks/adapters/rehearsal/postgres_census.rb +100 -0
  160. data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +69 -0
  161. data/lib/hecks/hecks/adapters/rehearsal/postgres_store.rb +129 -0
  162. data/lib/hecks/hecks/adapters/rehearsal/rows.rb +41 -0
  163. data/lib/hecks/hecks/adapters/rehearsal/rust_audit.rb +83 -0
  164. data/lib/hecks/hecks/adapters/rehearsal/subject.rb +104 -0
  165. data/lib/hecks/hecks/adapters/rehearsal.rb +54 -0
  166. data/lib/hecks/hecks/adapters/rehearsal_store.adapter +7 -0
  167. data/lib/hecks/hecks/adapters/rehearsal_store.rb +81 -0
  168. data/lib/hecks/hecks/adapters/terminal.rb +13 -11
  169. data/lib/hecks/hecks/codebase.behaviors +13 -1
  170. data/lib/hecks/hecks/codebase.bluebook +239 -5
  171. data/lib/hecks/hecks/custodian.behaviors +24 -6
  172. data/lib/hecks/hecks/custodian.bluebook +6 -1227
  173. data/lib/hecks/hecks/deploy.behaviors +1 -1
  174. data/lib/hecks/hecks/deploy.world +1 -3
  175. data/lib/hecks/hecks/hecks.behaviors +1 -1
  176. data/lib/hecks/hecks/hecks.hecksagon +53 -18
  177. data/lib/hecks/hecks/hecks.world +10 -10
  178. data/lib/hecks/hecks/rehearsal.bluebook +477 -0
  179. data/lib/hecks/hecks/site.behaviors +1 -1
  180. data/lib/hecks/hecks/site.world +0 -1
  181. data/lib/hecks/hecks/tooling.bluebook +1238 -0
  182. data/lib/hecks/indifferent_key.rb +20 -0
  183. data/lib/hecks/language/adapter.bluebook +13 -3
  184. data/lib/hecks/language/bluebook/aggregate.bluebook +23 -0
  185. data/lib/hecks/language/bluebook/bluebook.bluebook +15 -0
  186. data/lib/hecks/language/bluebook/entity.bluebook +18 -0
  187. data/lib/hecks/language/bluebook/policy.bluebook +9 -1
  188. data/lib/hecks/language/bluebook/query.bluebook +1 -1
  189. data/lib/hecks/language/bluebook/vocabulary.bluebook +11 -4
  190. data/lib/hecks/language/hecksagon/domain_port.bluebook +1 -1
  191. data/lib/hecks/language/hecksagon/hecksagon.bluebook +22 -0
  192. data/lib/hecks/language/oidc.json +10 -0
  193. data/lib/hecks/language/port.bluebook +4 -4
  194. data/lib/hecks/literal.rb +12 -3
  195. data/lib/hecks/ports/key_vault.rb +7 -2
  196. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +21 -10
  197. data/lib/hecks/ports/persistence/plugins/era/postgres_era/boot.rb +4 -2
  198. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +2 -1
  199. data/lib/hecks/ports/persistence/repository_factory.rb +6 -3
  200. data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +29 -0
  201. data/lib/hecks/projections/deploy/box/templates/deploy-service.sh.tmpl +1 -1
  202. data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
  203. data/lib/hecks/projections/deploy/shared.rb +5 -2
  204. data/lib/hecks/projections/model/deviations.rb +2 -2
  205. data/lib/hecks/projections/model.rb +2 -2
  206. data/lib/hecks/projections/oidc.rb +13 -1
  207. data/lib/hecks/projections/shape.rb +5 -2
  208. data/lib/hecks/projections/site/cms_editor/attributes.rb +26 -6
  209. data/lib/hecks/projections/site/cms_editor/blocks.rb +157 -0
  210. data/lib/hecks/projections/site/cms_editor/checks.rb +14 -1
  211. data/lib/hecks/projections/site/cms_editor/clearing.rb +70 -0
  212. data/lib/hecks/projections/site/cms_editor/color.rb +124 -0
  213. data/lib/hecks/projections/site/cms_editor/composition.rb +42 -0
  214. data/lib/hecks/projections/site/cms_editor/destructive.rb +61 -0
  215. data/lib/hecks/projections/site/cms_editor/drafts.rb +102 -0
  216. data/lib/hecks/projections/site/cms_editor/hints.rb +74 -0
  217. data/lib/hecks/projections/site/cms_editor/icons.rb +86 -0
  218. data/lib/hecks/projections/site/cms_editor/key_kinds.rb +31 -0
  219. data/lib/hecks/projections/site/cms_editor/limits.rb +72 -0
  220. data/lib/hecks/projections/site/cms_editor/media.rb +1 -1
  221. data/lib/hecks/projections/site/cms_editor/moment.rb +34 -0
  222. data/lib/hecks/projections/site/cms_editor/palette.rb +115 -0
  223. data/lib/hecks/projections/site/cms_editor/pickers.rb +85 -0
  224. data/lib/hecks/projections/site/cms_editor/preview.rb +91 -0
  225. data/lib/hecks/projections/site/cms_editor/relations.rb +92 -0
  226. data/lib/hecks/projections/site/cms_editor/scale_checks.rb +66 -0
  227. data/lib/hecks/projections/site/cms_editor/scheduling.rb +150 -0
  228. data/lib/hecks/projections/site/cms_editor/schema.rb +96 -19
  229. data/lib/hecks/projections/site/cms_editor/setting.rb +52 -9
  230. data/lib/hecks/projections/site/cms_editor/targets.rb +86 -0
  231. data/lib/hecks/projections/site/cms_editor/templates/package.json.tmpl +18 -1
  232. data/lib/hecks/projections/site/cms_editor/templates/src/app.ts.tmpl +251 -57
  233. data/lib/hecks/projections/site/cms_editor/templates/src/assets.ts.tmpl +98 -0
  234. data/lib/hecks/projections/site/cms_editor/templates/src/auth/membership.ts.tmpl +32 -24
  235. data/lib/hecks/projections/site/cms_editor/templates/src/browser/app.css.tmpl +412 -0
  236. data/lib/hecks/projections/site/cms_editor/templates/src/browser/autosave.js.tmpl +144 -0
  237. data/lib/hecks/projections/site/cms_editor/templates/src/browser/blocks.js.tmpl +500 -0
  238. data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_doc.js.tmpl +175 -0
  239. data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_editor.js.tmpl +418 -0
  240. data/lib/hecks/projections/site/cms_editor/templates/src/browser/epoch.js.tmpl +61 -0
  241. data/lib/hecks/projections/site/cms_editor/templates/src/browser/forms.js.tmpl +210 -0
  242. data/lib/hecks/projections/site/cms_editor/templates/src/browser/lists.js.tmpl +40 -0
  243. data/lib/hecks/projections/site/cms_editor/templates/src/browser/main.js.tmpl +26 -0
  244. data/lib/hecks/projections/site/cms_editor/templates/src/browser/media_picker.js.tmpl +232 -0
  245. data/lib/hecks/projections/site/cms_editor/templates/src/browser/moments.js.tmpl +73 -0
  246. data/lib/hecks/projections/site/cms_editor/templates/src/browser/pickers.js.tmpl +152 -0
  247. data/lib/hecks/projections/site/cms_editor/templates/src/browser/preview.js.tmpl +98 -0
  248. data/lib/hecks/projections/site/cms_editor/templates/src/browser/shell.js.tmpl +140 -0
  249. data/lib/hecks/projections/site/cms_editor/templates/src/browser/toast.js.tmpl +66 -0
  250. data/lib/hecks/projections/site/cms_editor/templates/src/choices.ts.tmpl +99 -0
  251. data/lib/hecks/projections/site/cms_editor/templates/src/commands.ts.tmpl +78 -24
  252. data/lib/hecks/projections/site/cms_editor/templates/src/config.ts.tmpl +6 -0
  253. data/lib/hecks/projections/site/cms_editor/templates/src/flash.ts.tmpl +52 -0
  254. data/lib/hecks/projections/site/cms_editor/templates/src/host.ts.tmpl +19 -2
  255. data/lib/hecks/projections/site/cms_editor/templates/src/media/handler.ts.tmpl +31 -8
  256. data/lib/hecks/projections/site/cms_editor/templates/src/names.ts.tmpl +36 -0
  257. data/lib/hecks/projections/site/cms_editor/templates/src/schema.ts.tmpl +131 -2
  258. data/lib/hecks/projections/site/cms_editor/templates/src/ui/availability.ts.tmpl +15 -0
  259. data/lib/hecks/projections/site/cms_editor/templates/src/ui/block_list.js.tmpl +213 -0
  260. data/lib/hecks/projections/site/cms_editor/templates/src/ui/blocks.ts.tmpl +275 -0
  261. data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_model.js.tmpl +8 -5
  262. data/lib/hecks/projections/site/cms_editor/templates/src/ui/detail.ts.tmpl +88 -0
  263. data/lib/hecks/projections/site/cms_editor/templates/src/ui/dialogs.ts.tmpl +43 -0
  264. data/lib/hecks/projections/site/cms_editor/templates/src/ui/fields.ts.tmpl +229 -34
  265. data/lib/hecks/projections/site/cms_editor/templates/src/ui/html.ts.tmpl +40 -81
  266. data/lib/hecks/projections/site/cms_editor/templates/src/ui/icons.ts.tmpl +15 -0
  267. data/lib/hecks/projections/site/cms_editor/templates/src/ui/input.ts.tmpl +29 -12
  268. data/lib/hecks/projections/site/cms_editor/templates/src/ui/links.ts.tmpl +100 -0
  269. data/lib/hecks/projections/site/cms_editor/templates/src/ui/moments.ts.tmpl +44 -0
  270. data/lib/hecks/projections/site/cms_editor/templates/src/ui/outline.ts.tmpl +53 -17
  271. data/lib/hecks/projections/site/cms_editor/templates/src/ui/pages.ts.tmpl +178 -88
  272. data/lib/hecks/projections/site/cms_editor/templates/src/ui/paging.ts.tmpl +82 -0
  273. data/lib/hecks/projections/site/cms_editor/templates/src/ui/pickers.ts.tmpl +110 -0
  274. data/lib/hecks/projections/site/cms_editor/templates/src/ui/preview.ts.tmpl +87 -0
  275. data/lib/hecks/projections/site/cms_editor/templates/src/ui/related.ts.tmpl +97 -0
  276. data/lib/hecks/projections/site/cms_editor/templates/src/ui/schedule.ts.tmpl +160 -0
  277. data/lib/hecks/projections/site/cms_editor/templates/src/ui/shell.ts.tmpl +211 -0
  278. data/lib/hecks/projections/site/cms_editor/templates/src/ui/table.ts.tmpl +151 -0
  279. data/lib/hecks/projections/site/cms_editor/templates/src/ui/theme.js.tmpl +16 -0
  280. data/lib/hecks/projections/site/cms_editor/templates/src/ui/values.ts.tmpl +69 -0
  281. data/lib/hecks/projections/site/cms_editor/templates/src/ui/words.ts.tmpl +97 -0
  282. data/lib/hecks/projections/site/cms_editor/templates/src/ui/world.ts.tmpl +35 -0
  283. data/lib/hecks/projections/site/cms_editor/templates/tsconfig.json.tmpl +2 -1
  284. data/lib/hecks/projections/site/cms_editor/theme.rb +29 -0
  285. data/lib/hecks/projections/site/cms_editor/theme_checks.rb +57 -0
  286. data/lib/hecks/projections/site/cms_editor.rb +49 -23
  287. data/lib/hecks/projections/site/role_probe.rb +2 -0
  288. data/lib/hecks/projector/cli_audience.rb +2 -1
  289. data/lib/hecks/projector/cli_projector/listing.rb +1 -1
  290. data/lib/hecks/projector/cli_projector/option_specs.rb +1 -1
  291. data/lib/hecks/projector/docs_projector/closing.rb +1 -1
  292. data/lib/hecks/projector/docs_projector.rb +1 -1
  293. data/lib/hecks/projector/exporter/bindings.rb +40 -10
  294. data/lib/hecks/projector/exporter/capability_values.rb +55 -0
  295. data/lib/hecks/projector/narrate_projector/reactions.rb +1 -1
  296. data/lib/hecks/projector/target.rb +11 -0
  297. data/lib/hecks/projector.rb +32 -5
  298. data/lib/hecks/quality_control/adapters/github_checks.rb +12 -8
  299. data/lib/hecks/quality_control/adapters/github_issues.rb +8 -4
  300. data/lib/hecks/quality_control/cli/qa_open_pr.rb +2 -2
  301. data/lib/hecks/quality_control/cli/qa_sweep/checks/shrinking.rb +1 -1
  302. data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +1 -1
  303. data/lib/hecks/quality_control/cli/qa_sweep.rb +1 -1
  304. data/lib/hecks/quality_control/quality_control.bluebook +6 -2
  305. data/lib/hecks/release/approval.rb +64 -0
  306. data/lib/hecks/router/{door.rb → aggregate_reader.rb} +5 -4
  307. data/lib/hecks/router/namespace_installer.rb +16 -16
  308. data/lib/hecks/router.rb +1 -1
  309. data/lib/hecks/runtime/best_effort.rb +22 -0
  310. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +12 -2
  311. data/lib/hecks/runtime/command_interpreter.rb +3 -2
  312. data/lib/hecks/runtime/command_rules/emission.rb +1 -1
  313. data/lib/hecks/runtime/dispatcher.rb +4 -4
  314. data/lib/hecks/runtime/entity_element/location.rb +1 -1
  315. data/lib/hecks/runtime/event.rb +5 -0
  316. data/lib/hecks/runtime/identity.rb +7 -6
  317. data/lib/hecks/runtime/interpreting.rb +2 -1
  318. data/lib/hecks/runtime/invocation_builder.rb +1 -1
  319. data/lib/hecks/runtime/loader/boot_steps.rb +8 -5
  320. data/lib/hecks/runtime/loader.rb +10 -10
  321. data/lib/hecks/runtime/policy_interpreter/arguments.rb +7 -4
  322. data/lib/hecks/runtime/policy_interpreter/fan_out.rb +3 -3
  323. data/lib/hecks/runtime/policy_interpreter.rb +6 -6
  324. data/lib/hecks/runtime/port_operation_interpreter.rb +1 -1
  325. data/lib/hecks/runtime/reaction_invocation.rb +1 -1
  326. data/lib/hecks/runtime/rebuild_sweep.rb +11 -1
  327. data/lib/hecks/runtime/registry/ask_verification.rb +35 -0
  328. data/lib/hecks/runtime/registry/hecksagon_rules.rb +10 -0
  329. data/lib/hecks/runtime/registry/merging.rb +1 -1
  330. data/lib/hecks/runtime/registry/providers.rb +16 -0
  331. data/lib/hecks/runtime/registry/read_repository.rb +6 -4
  332. data/lib/hecks/runtime/registry/verification.rb +4 -1
  333. data/lib/hecks/runtime/registry/world_defaults.rb +29 -0
  334. data/lib/hecks/runtime/registry.rb +1 -1
  335. data/lib/hecks/runtime/saga_interpreter/compensation.rb +1 -1
  336. data/lib/hecks/runtime/saga_interpreter/delivery.rb +4 -4
  337. data/lib/hecks/runtime/saga_interpreter/transitions.rb +1 -1
  338. data/lib/hecks/runtime/saga_interpreter.rb +3 -3
  339. data/lib/hecks/runtime/tenant_check.rb +5 -4
  340. data/lib/hecks/runtime/value/coercion.rb +2 -2
  341. data/lib/hecks/runtime/value/validation.rb +16 -4
  342. data/lib/hecks/runtime.rb +9 -9
  343. data/lib/hecks/rust_build/project_rust/target_ir.rb +1 -1
  344. data/lib/hecks/rust_build/wasm.rb +1 -1
  345. data/lib/hecks/rust_build/wasm_browser.rb +1 -1
  346. data/lib/hecks/rust_build.rb +11 -0
  347. data/lib/hecks/served_options.rb +53 -0
  348. data/lib/hecks/site/bluebook/site.bluebook +1 -0
  349. data/lib/hecks/storehouse/audit.rb +1 -1
  350. data/lib/hecks/storehouse/dispatching.rb +8 -6
  351. data/lib/hecks/storehouse/introspection.rb +2 -2
  352. data/lib/hecks/storehouse/querying.rb +6 -4
  353. data/lib/hecks/storehouse.rb +4 -4
  354. data/lib/hecks/tools/comment_style/source_file.rb +4 -1
  355. data/lib/hecks/tools/deploy_recipe_lint/fixtures.rb +4 -1
  356. data/lib/hecks/tools/lanes/rows.rb +6 -0
  357. data/lib/hecks/tools/oidc_manifests.rb +4 -5
  358. data/lib/hecks/tools/regeneration_run.rb +11 -4
  359. data/lib/hecks/tools/site_routes/editor_files.rb +12 -5
  360. data/lib/hecks/tools/tenant_provisioning.rb +2 -2
  361. data/lib/hecks/tools/tools_doc.rb +4 -4
  362. data/lib/hecks/version.rb +1 -1
  363. data/lib/hecks/vocabulary.rb +4 -4
  364. data/lib/hecks.rb +15 -15
  365. data/lib/rubocop/cop/hecks/env_dump.rb +69 -0
  366. data/lib/rubocop/cop/hecks/monotonic_duration.rb +53 -0
  367. data/lib/rubocop/cop/hecks/no_wall_clock_in_runtime.rb +34 -0
  368. data/lib/rubocop/cop/hecks/raw_shell_interpolation.rb +79 -0
  369. data/lib/rubocop/cop/hecks/sleep_in_spec.rb +34 -0
  370. data/lib/rubocop/cop/hecks/swallowed_rescue.rb +37 -0
  371. data/lib/rubocop/cop/hecks/symbol_string_key_mix.rb +50 -0
  372. data/lib/rubocop/cop/hecks/untyped_raise.rb +37 -0
  373. data/prebuilt/05c358d38d661a3774f813d49c66f5b68939af94432d66257a56f49ee2598994.marshal +0 -0
  374. data/prebuilt/26c40c61f09b09b11651c64668eda3ceb180f83cf2a3802d3033d2fd99ac841b.marshal +0 -0
  375. data/prebuilt/verdicts-230ac18c25abfd3d92ede032bee2661a4c2cd2773ab0cf0e2ae52546ba2f7af9.json +1 -0
  376. data/rust/.gitignore +6 -0
  377. data/rust/Cargo.toml +6 -0
  378. data/rust/build/src/binding_pass.rs +103 -0
  379. data/rust/build/src/cargo_sync.rs +6 -0
  380. data/rust/build/src/lineage_pass.rs +3 -3
  381. data/rust/build/src/main.rs +17 -5
  382. data/rust/build/src/pipeline.rs +48 -5
  383. data/rust/build/src/seams_pass.rs +123 -0
  384. data/rust/build/src/translation_pass.rs +167 -0
  385. data/rust/codegen/src/asks.rs +103 -0
  386. data/rust/codegen/src/commands.rs +13 -8
  387. data/rust/codegen/src/constraints.rs +25 -0
  388. data/rust/codegen/src/domain_generator.rs +2 -0
  389. data/rust/codegen/src/expr_emitter.rs +13 -0
  390. data/rust/codegen/src/json_codec.rs +54 -15
  391. data/rust/codegen/src/main.rs +1 -0
  392. data/rust/codegen/src/mutations.rs +128 -3
  393. data/rust/codegen/src/queries.rs +11 -2
  394. data/rust/codegen/src/reactions.rs +1 -1
  395. data/rust/codegen/src/types.rs +166 -8
  396. data/rust/host/HECKS_RELEASE +1 -1
  397. data/rust/host/src/api.rs +4 -4
  398. data/rust/host/src/bin/validate_harness.rs +43 -0
  399. data/rust/host/src/boundary_fuzz/expr_json.rs +2 -2
  400. data/rust/host/src/boundary_fuzz/ir.rs +1 -0
  401. data/rust/host/src/boundary_fuzz/server.rs +1 -1
  402. data/rust/host/src/checkout.rs +35 -19
  403. data/rust/host/src/commerce_ir.rs +408 -2
  404. data/rust/host/src/expr_json.rs +118 -0
  405. data/rust/host/src/mint.rs +1 -1
  406. data/rust/host/src/payments/tests.rs +27 -1
  407. data/rust/host/src/payments.rs +12 -4
  408. data/rust/host/src/query_step.rs +22 -4
  409. data/rust/host/src/reference_validate.rs +653 -108
  410. data/rust/host/src/resend.rs +2 -0
  411. data/rust/host/src/server.rs +19 -6
  412. data/rust/host/src/ui_schema.rs +132 -5
  413. data/rust/host/src/web/newsletter.rs +7 -8
  414. data/rust/host/src/web/newsletter_send.rs +38 -13
  415. data/rust/host/src/web/registrations.rs +229 -26
  416. data/rust/host/src/web.rs +3 -3
  417. data/rust/parser/src/emit.rs +38 -6
  418. data/rust/parser/src/expr/ast_json.rs +7 -1
  419. data/rust/parser/src/expr/resolver.rs +171 -6
  420. data/rust/parser/src/ir.rs +7 -0
  421. data/rust/parser/src/keywords.rs +24 -0
  422. data/rust/parser/src/main.rs +3 -0
  423. data/rust/parser/src/parse/aggregate.rs +2 -1
  424. data/rust/parser/src/parse/chapter.rs +2 -2
  425. data/rust/parser/src/parse/command.rs +2 -0
  426. data/rust/parser/src/parse/hecksagon.rs +5 -2
  427. data/rust/parser/src/parse/lifecycle.rs +81 -0
  428. data/rust/parser/src/parse/mod.rs +15 -1
  429. data/rust/parser/src/parse/policy.rs +13 -2
  430. data/rust/parser/src/parse/value_object.rs +13 -8
  431. data/rust/parser/src/ruby_value.rs +1 -1
  432. data/rust/src/exemplar/commands.rs +4 -4
  433. data/rust/src/exemplar/constraints.rs +1 -1
  434. data/rust/src/exemplar/json.rs +43 -1
  435. data/rust/src/kernel/cli.rs +7 -5
  436. data/rust/src/kernel/dispatch.rs +2 -2
  437. data/rust/src/kernel/dispatch_guard_tests.rs +1 -1
  438. data/rust/src/kernel/expr.rs +66 -3
  439. data/rust/src/kernel/expression_operators/arithmetic.rs +130 -5
  440. data/rust/src/kernel/expression_operators/text.rs +26 -2
  441. data/rust/src/kernel/json.rs +15 -0
  442. data/rust/src/kernel/mod.rs +1 -1
  443. data/rust/src/kernel/orchestrate.rs +54 -5
  444. data/rust/src/kernel/orchestrate_guard_tests.rs +67 -0
  445. data/rust/src/kernel/pattern.rs +60 -0
  446. data/rust/src/kernel/reference_lookup.rs +90 -7
  447. metadata +140 -38
  448. data/lib/hecks/doors/cli_door/nesting.rb +0 -34
  449. data/lib/hecks/doors/cli_door/shorthand.rb +0 -85
  450. data/lib/hecks/doors/cli_door.rb +0 -124
  451. data/lib/hecks/doors/cli_runner/answers.rb +0 -138
  452. data/lib/hecks/doors/cli_runner/resolution.rb +0 -146
  453. data/lib/hecks/doors/cli_runner/settling.rb +0 -115
  454. data/lib/hecks/doors/cli_runner/streaming.rb +0 -90
  455. data/lib/hecks/doors/cli_runner/suggestions.rb +0 -43
  456. data/lib/hecks/doors/cli_runner.rb +0 -66
  457. data/lib/hecks/doors/command_request.rb +0 -119
  458. data/lib/hecks/doors/handle.rb +0 -208
  459. data/lib/hecks/doors/json_door.rb +0 -132
  460. data/lib/hecks/doors/launcher_options.rb +0 -247
  461. data/lib/hecks/doors/mcp_door/properties.rb +0 -47
  462. data/lib/hecks/doors/mcp_door/tool_calls.rb +0 -113
  463. data/lib/hecks/doors/mcp_door/tool_definitions/auditing.rb +0 -51
  464. data/lib/hecks/doors/mcp_door/tool_definitions/calling.rb +0 -69
  465. data/lib/hecks/doors/mcp_door/tool_definitions/reading.rb +0 -97
  466. data/lib/hecks/doors/mcp_door/tool_definitions.rb +0 -15
  467. data/lib/hecks/doors/mcp_door.rb +0 -156
  468. data/lib/hecks/doors/mcp_door_scope/argument_policy.rb +0 -92
  469. data/lib/hecks/doors/mcp_door_scope/env_problems.rb +0 -55
  470. data/lib/hecks/doors/mcp_door_scope/presenter.rb +0 -78
  471. data/lib/hecks/doors/mcp_door_scope.rb +0 -201
  472. data/lib/hecks/doors/mcp_guide_cache.rb +0 -75
  473. data/lib/hecks/doors/ruby_door/aggregate_door.rb +0 -162
  474. data/lib/hecks/doors/ruby_door/chapter.rb +0 -89
  475. data/lib/hecks/doors/ruby_door.rb +0 -80
  476. data/lib/hecks/doors/usage_cache.rb +0 -139
  477. data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_widget.js.tmpl +0 -282
  478. data/lib/hecks/projections/site/cms_editor/templates/src/ui/media_picker.js.tmpl +0 -109
  479. data/prebuilt/c28d6d9db14eea3e17161e9d9c262efee30b256cf97693a7cfd552bce24b2f7f.marshal +0 -0
  480. data/prebuilt/cb375cba61ca0dbe8ddb9739c306510fe05e5f4b7a9b6955caff8224c63ea0e0.marshal +0 -0
  481. data/prebuilt/verdicts-d6d584ad43b5f3cebf88aafbbdbf024c38574acf18e06adffbc555f7786b1c2f.json +0 -1
@@ -0,0 +1,1238 @@
1
+ Hecks.bluebook "Hecks" do
2
+ # Tooling: developer tooling for working with a domain, in the Maintenance module (ADR 0096).
3
+ # A client runs these while building a domain, not while operating one; the operating half is
4
+ # in custodian.bluebook.
5
+
6
+ aggregate "Introspection" do
7
+ description "A look at a booted domain, answered by the runtime without changing anything."
8
+
9
+ attribute :domain, DomainPath
10
+ identified_by :domain
11
+
12
+ value_object "DomainPath" do
13
+ attribute :value, String, pattern: '[^\t\n\r]'
14
+
15
+ invariant("a domain is named by its path") { !value.to_s.empty? }
16
+ end
17
+
18
+ value_object "ChapterName" do
19
+ attribute :value, String, pattern: '[^ \t\n\r]'
20
+
21
+ invariant("a chapter is named") { !value.to_s.empty? }
22
+ end
23
+
24
+ value_object "AggregateName" do
25
+ attribute :value, String, pattern: '[^ \t\n\r]'
26
+
27
+ invariant("an aggregate is named") { !value.to_s.empty? }
28
+ end
29
+
30
+ # Each query below is answered by the DomainRuntime port's adapter: the domain is booted
31
+ # in-process and the answer is the exact text the matching bin script printed.
32
+
33
+ # An answer that is one document of text, exactly as the tool that produced it wrote it.
34
+ value_object "Document" do
35
+ attribute :text, String
36
+ end
37
+
38
+ query "Ir" do
39
+ description "The IR a domain's boot produces, as JSON: what golden specs pin and an era hashes."
40
+ attribute :domain, DomainPath, optional: true
41
+ attribute :translations, Boolean, optional: true
42
+ attribute :meta, Boolean, optional: true
43
+ returns Document
44
+ end
45
+
46
+ query "Shape" do
47
+ description "The storage shape an era's label is minted from: a bluebook file or a directory of them."
48
+ attribute :domain, DomainPath
49
+ returns Document
50
+ end
51
+
52
+ query "Stores" do
53
+ description "Every aggregate's current records: the head, not the journal."
54
+ attribute :domain, DomainPath
55
+ returns Document
56
+ end
57
+
58
+ query "History" do
59
+ description "Every journal entry a domain's append-only adapters hold: the whole write history."
60
+ attribute :domain, DomainPath
61
+ returns Document
62
+ end
63
+
64
+ query "Statements" do
65
+ description "A chapter's declared facts, as plain English sentences."
66
+ attribute :domain, DomainPath
67
+ attribute :chapter, ChapterName
68
+ returns Document
69
+ end
70
+
71
+ query "Narrate" do
72
+ description "A domain read back in English, whole or for one aggregate."
73
+ attribute :domain, DomainPath, optional: true
74
+ attribute :aggregate, AggregateName, optional: true
75
+ returns Document
76
+ end
77
+
78
+ query "Docs" do
79
+ description "A domain's usage document, whole or for one aggregate."
80
+ attribute :domain, DomainPath, optional: true
81
+ attribute :aggregate, AggregateName, optional: true
82
+ returns Document
83
+ end
84
+
85
+ # One file a projection would have written, held as its name and its text.
86
+ value_object "ProjectedFile" do
87
+ attribute :name, String
88
+ attribute :text, String
89
+ end
90
+
91
+ query "ProjectDiagrams" do
92
+ description "A chapter's Mermaid diagrams, as file names mapped to their text; nothing is written."
93
+ attribute :domain, DomainPath
94
+ attribute :chapter, ChapterName
95
+ returns list_of(ProjectedFile)
96
+ end
97
+
98
+ query "Glossary" do
99
+ description "A chapter's Ubiquitous Language glossary, as file names mapped to their text; nothing is written."
100
+ attribute :domain, DomainPath
101
+ attribute :chapter, ChapterName
102
+ returns list_of(ProjectedFile)
103
+ end
104
+ end
105
+
106
+ # A journaled command whose work happens outside the runtime (ADR 0080, section 11). Every
107
+ # command below that runs something follows this one shape, so later aggregates copy it:
108
+ #
109
+ # 1. A creating command records the request as a record in state "requested" and emits.
110
+ # 2. A policy on that event asks for the work by name (`ask :check`); the hecksagon says which
111
+ # port answers. The runtime hands the port's adapter the whole record, so the adapter does
112
+ # all the IO, and only there.
113
+ # 3. The adapter answers (a hash named like a command's arguments) or raises (its message
114
+ # is the refusal). One policy per outcome re-enters the record with a command that stores it.
115
+ #
116
+ # The ports are declared in hecks.hecksagon; each adapter is in adapters/.
117
+
118
+ aggregate "ModelCheckRun" do
119
+ description "One static analysis over domains' models, and what it found: a check's history."
120
+
121
+ attribute :run, RunKey
122
+ attribute :domains, DomainList, optional: true
123
+ attribute :profile, ProfileName, optional: true
124
+ attribute :strict, Switch, optional: true
125
+ attribute :report, Report, optional: true
126
+ attribute :checked, Checked, optional: true
127
+ attribute :refusal, Report, optional: true
128
+ identified_by :run
129
+
130
+ value_object "RunKey" do
131
+ attribute :value, String, pattern: '[^ \t\n\r]'
132
+
133
+ invariant("a run is named") { !value.to_s.empty? }
134
+ end
135
+
136
+ value_object "DomainList" do
137
+ attribute :value, String, pattern: '[^\t\n\r]'
138
+ end
139
+
140
+ value_object "ProfileName" do
141
+ attribute :value, String, pattern: '^client$'
142
+ end
143
+
144
+ value_object "Switch" do
145
+ attribute :value, Boolean
146
+ end
147
+
148
+ value_object "Report" do
149
+ attribute :value, String
150
+ end
151
+
152
+ # How many domains the report examined, counted from its headers so a reader need not.
153
+ value_object "Checked" do
154
+ attribute :value, Integer
155
+
156
+ invariant("a count is not negative") { value >= 0 }
157
+ end
158
+
159
+ lifecycle :status, default: "requested" do
160
+ mark :failure, "flagged"
161
+ transition "Pass" => "clean", from: "requested"
162
+ transition "Flag" => "flagged", from: "requested"
163
+ end
164
+
165
+ command "ModelCheck" do
166
+ role "Operator"
167
+ goal "Check domains for dead states and unreachable steps, and keep the verdict"
168
+
169
+ attribute :run, RunKey
170
+ attribute :domains, DomainList, optional: true
171
+ attribute :profile, ProfileName, optional: true
172
+ attribute :strict, Switch, optional: true
173
+
174
+ sets :run
175
+ sets :domains
176
+ sets :profile
177
+ sets :strict
178
+
179
+ emits ModelCheckRequested
180
+ end
181
+
182
+ command "Pass" do
183
+ role "System"
184
+ goal "Record that the analysis left no error"
185
+
186
+ reference_to ModelCheckRun
187
+ attribute :report, Report
188
+ attribute :checked, Checked
189
+
190
+ sets :report
191
+ sets :checked
192
+
193
+ emits ModelCheckPassed
194
+ end
195
+
196
+ command "Flag" do
197
+ role "System"
198
+ goal "Record that the analysis found something, or could not run, and what it said"
199
+
200
+ reference_to ModelCheckRun
201
+ attribute :refusal, Report
202
+
203
+ sets :refusal
204
+
205
+ emits ModelCheckFlagged
206
+ end
207
+
208
+ # `hecks model_check` answers once the request is recorded, so the verdict is read here.
209
+ query "Verdict" do
210
+ description "What one check found: its status, and the report or the reason it could not run."
211
+ attribute :run, RunKey
212
+ where(run: :run)
213
+ order_by :run
214
+ end
215
+
216
+ query "Flagged" do
217
+ description "Every check that left a finding or could not run."
218
+ where(status: "flagged")
219
+ order_by :run
220
+ end
221
+ end
222
+
223
+ policy "AnalyseWhenRequested" do
224
+ on "ModelCheckRun.ModelCheckRequested"
225
+ ask :check, with: { run: :run }
226
+ end
227
+
228
+ policy "RecordTheClean" do
229
+ on "ModelCheckRun.ModelCheckAnswered"
230
+ trigger ModelCheckRun::Pass, with: { report: :report, checked: :checked }
231
+ end
232
+
233
+ policy "RecordTheFlag" do
234
+ on "ModelCheckRun.ModelCheckRefused"
235
+ trigger ModelCheckRun::Flag, with: { refusal: :refusal }
236
+ end
237
+
238
+ aggregate "Package" do
239
+ description "One package of the bluebook registry, vendored into a project and pinned."
240
+
241
+ attribute :package, PackageRef
242
+ attribute :from, Location, optional: true
243
+ attribute :root, Location, optional: true
244
+ attribute :report, Report, optional: true
245
+ attribute :refusal, Report, optional: true
246
+ identified_by :package
247
+
248
+ # `name` for the newest release, `name@1.2.0` for one release, `name@9f2c1ab` for a commit.
249
+ value_object "PackageRef" do
250
+ attribute :value, String, pattern: '^[a-z][a-z0-9_]*(@[^ \t\n\r]+)?$'
251
+
252
+ invariant("a package is named") { !value.to_s.empty? }
253
+ end
254
+
255
+ # A directory: the source repository (`from`), or the project the files land in (`root`).
256
+ value_object "Location" do
257
+ attribute :value, String, pattern: '[^\t\n\r]'
258
+ end
259
+
260
+ value_object "Report" do
261
+ attribute :value, String
262
+ end
263
+
264
+ # An answer that is one document of text, exactly as the tool that produced it wrote it.
265
+ value_object "Document" do
266
+ attribute :text, String
267
+ end
268
+
269
+ lifecycle :status, default: "requested" do
270
+ mark :failure, "refused"
271
+ transition "Revendor" => "requested", from: %w[vendored refused]
272
+ transition "Complete" => "vendored", from: "requested"
273
+ transition "Abandon" => "refused", from: "requested"
274
+ end
275
+
276
+ # `hecks vendor <package[@version]> [from=path] [root=path]`. The record is named by what was
277
+ # asked for, so a release is one record; vendoring the same spelling again is `hecks revendor`.
278
+ # `from` defaults to $EMBRYONAUT_BLUEBOOKS_SRC and `root` to the current directory, both read by
279
+ # the adapter.
280
+ command "Vendor" do
281
+ role "Operator"
282
+ goal "Pin a package of the bluebook registry into this project, at a release or a commit"
283
+
284
+ attribute :package, PackageRef
285
+ attribute :from, Location, optional: true
286
+ attribute :root, Location, optional: true
287
+
288
+ sets :package
289
+ sets :from
290
+ sets :root
291
+
292
+ emits VendorRequested
293
+ end
294
+
295
+ command "Revendor" do
296
+ role "Operator"
297
+ goal "Pin a package that was vendored before again, for the newest release or the same one"
298
+
299
+ reference_to Package
300
+ attribute :from, Location, optional: true
301
+ attribute :root, Location, optional: true
302
+
303
+ sets :from
304
+ sets :root
305
+
306
+ emits RevendorRequested
307
+ end
308
+
309
+ command "Complete" do
310
+ role "System"
311
+ goal "Record that the package was pinned, and what changed"
312
+
313
+ reference_to Package
314
+ attribute :report, Report
315
+
316
+ sets :report
317
+
318
+ emits PackageVendored
319
+ end
320
+
321
+ command "Abandon" do
322
+ role "System"
323
+ goal "Record that the package could not be pinned, and why"
324
+
325
+ reference_to Package
326
+ attribute :refusal, Report
327
+
328
+ sets :refusal
329
+
330
+ emits VendoringRefused
331
+ end
332
+
333
+ query "Pinning" do
334
+ description "What vendoring one package did: its status, and the report or the refusal."
335
+ attribute :package, PackageRef
336
+ where(package: :package)
337
+ order_by :package
338
+ end
339
+
340
+ query "Unpinned" do
341
+ description "Every package the last vendoring of which was refused."
342
+ where(status: "refused")
343
+ order_by :package
344
+ end
345
+
346
+ # `hecks package.verify [root=path]`: a read of the files, so it keeps no record. The manifest
347
+ # is the answer; a package that disagrees with its lock refuses it, naming each one.
348
+ query "Verify" do
349
+ description "Check every vendored package against its bluebook.lock, then describe them as the manifest a client image carries."
350
+ attribute :root, Location, optional: true
351
+ returns Document
352
+ end
353
+ end
354
+
355
+ policy "PinWhenVendored" do
356
+ on "Package.VendorRequested"
357
+ ask :pin, with: { package: :package }
358
+ end
359
+
360
+ policy "PinWhenRevendored" do
361
+ on "Package.RevendorRequested"
362
+ ask :pin, with: { package: :package }
363
+ end
364
+
365
+ policy "RecordThePinning" do
366
+ on "Package.PackagePinned"
367
+ trigger Package::Complete, with: { report: :report }
368
+ end
369
+
370
+ policy "RecordTheRefusedPin" do
371
+ on "Package.PinRefused"
372
+ trigger Package::Abandon, with: { refusal: :refusal }
373
+ end
374
+
375
+ aggregate "Registry" do
376
+ description "A bluebook registry repository: packages versioned in it and released as tags."
377
+
378
+ attribute :run, RegistryKey
379
+ attribute :package, PackageName, optional: true
380
+ attribute :root, Location, optional: true
381
+ attribute :report, Report, optional: true
382
+ attribute :refusal, Report, optional: true
383
+ identified_by :run
384
+
385
+ value_object "RegistryKey" do
386
+ attribute :value, String, pattern: '[^ \t\n\r]'
387
+
388
+ invariant("a release is named") { !value.to_s.empty? }
389
+ end
390
+
391
+ value_object "PackageName" do
392
+ attribute :value, String, pattern: '^[a-z][a-z0-9_]*$'
393
+ end
394
+
395
+ # The registry repository: the current directory when absent.
396
+ value_object "Location" do
397
+ attribute :value, String, pattern: '[^\t\n\r]'
398
+ end
399
+
400
+ value_object "Report" do
401
+ attribute :value, String
402
+ end
403
+
404
+ # An answer that is one document of text, exactly as the tool that produced it wrote it.
405
+ value_object "Document" do
406
+ attribute :text, String
407
+ end
408
+
409
+ lifecycle :status, default: "requested" do
410
+ mark :failure, "refused"
411
+ transition "Complete" => "tagged", from: "requested"
412
+ transition "Abandon" => "refused", from: "requested"
413
+ end
414
+
415
+ # `hecks package.release <package> [root=path]`. Tags the package's current version as
416
+ # `<package>-vX.Y.Z` in the local repository and never pushes; the report ends in the command
417
+ # that does. A refusal says which rule the package broke.
418
+ command "Release" do
419
+ role "Operator"
420
+ goal "Tag a package of this registry at the version its bluebook.yml carries"
421
+
422
+ attribute :run, RegistryKey
423
+ attribute :package, PackageName
424
+ attribute :root, Location, optional: true
425
+
426
+ sets :run
427
+ sets :package
428
+ sets :root
429
+
430
+ emits ReleaseRequested
431
+ end
432
+
433
+ command "Complete" do
434
+ role "System"
435
+ goal "Record that the release was tagged, and the command that publishes it"
436
+
437
+ reference_to Registry
438
+ attribute :report, Report
439
+
440
+ sets :report
441
+
442
+ emits ReleaseRecorded
443
+ end
444
+
445
+ command "Abandon" do
446
+ role "System"
447
+ goal "Record that the release was refused, and why"
448
+
449
+ reference_to Registry
450
+ attribute :refusal, Report
451
+
452
+ sets :refusal
453
+
454
+ emits ReleaseAbandoned
455
+ end
456
+
457
+ # `hecks package.check [root=path]`: a read of the repository's files and tags, so it keeps no
458
+ # record. A package that changed since its latest release without a version bump and a changelog
459
+ # entry refuses it, naming each one.
460
+ query "Check" do
461
+ description "Judge every package against its latest release: a changed package needs a newer version and a changelog entry."
462
+ attribute :root, Location, optional: true
463
+ returns Document
464
+ end
465
+
466
+ # `hecks package.digest <package> [root=path]`: a read of the package's files, so it keeps no
467
+ # record. The digest is `Lock.digest_of` over `<package>/bluebook/*.bluebook` only (the
468
+ # `bluebook.lock` digest line), then the package's shape labels; `root` is the registry, or a
469
+ # project that vendors the package.
470
+ query "Digest" do
471
+ description "Print a package's content digest and shape labels, as its bluebook.lock records them."
472
+ attribute :package, PackageName
473
+ attribute :root, Location, optional: true
474
+ returns Document
475
+ end
476
+
477
+ query "Releasing" do
478
+ description "What releasing one run did: its status, and the report or the refusal."
479
+ attribute :run, RegistryKey
480
+ where(run: :run)
481
+ order_by :run
482
+ end
483
+
484
+ query "Refused" do
485
+ description "Every release that was refused."
486
+ where(status: "refused")
487
+ order_by :run
488
+ end
489
+ end
490
+
491
+ policy "TagWhenReleased" do
492
+ on "Registry.ReleaseRequested"
493
+ ask :tag, with: { run: :run }
494
+ end
495
+
496
+ policy "RecordTheTag" do
497
+ on "Registry.ReleaseTagged"
498
+ trigger Registry::Complete, with: { report: :report }
499
+ end
500
+
501
+ policy "RecordTheRefusedTag" do
502
+ on "Registry.ReleaseRefused"
503
+ trigger Registry::Abandon, with: { refusal: :refusal }
504
+ end
505
+
506
+ # A run record for one launch onto the framework, not an adapter: the driving adapters live in `Hecks::Adapters::Driving`.
507
+ aggregate "Launch" do
508
+ description "A way into the framework opened for a project: its launchers, or an MCP server."
509
+
510
+ attribute :run, LaunchKey
511
+ attribute :domains, DomainList, optional: true
512
+ attribute :stdio, Switch, optional: true
513
+ attribute :name, DomainName, optional: true
514
+ attribute :adapter, AdapterName, optional: true
515
+ attribute :dir, TargetDir, optional: true
516
+ attribute :expert, ExpertName, optional: true
517
+ attribute :no_ai, Switch, optional: true
518
+ attribute :output, Note, optional: true
519
+ attribute :refusal, Note, optional: true
520
+ identified_by :run
521
+
522
+ value_object "LaunchKey" do
523
+ attribute :value, String, pattern: '[^ \t\n\r]'
524
+
525
+ invariant("a launch is named") { !value.to_s.empty? }
526
+ end
527
+
528
+ # Domain directories under the project, comma separated.
529
+ value_object "DomainList" do
530
+ attribute :value, String, pattern: '[^\t\n\r]'
531
+ end
532
+
533
+ value_object "Switch" do
534
+ attribute :value, Boolean
535
+ end
536
+
537
+ # What a new domain is called, as its bluebook spells it: a capital and then letters and digits.
538
+ value_object "DomainName" do
539
+ attribute :value, String, pattern: '[A-Z][A-Za-z0-9]*'
540
+ end
541
+
542
+ # A persistence adapter a stub can be bound to; `Hecks::CLI::DomainStub` names the ones it knows.
543
+ value_object "AdapterName" do
544
+ attribute :value, String, pattern: '[A-Za-z]+'
545
+ end
546
+
547
+ # Where the new domain goes; the domain's snake-cased name under the current directory when absent.
548
+ value_object "TargetDir" do
549
+ attribute :value, String, pattern: '[^\t\n\r]'
550
+ end
551
+
552
+ # Who is interviewed; asked for at the terminal when absent.
553
+ value_object "ExpertName" do
554
+ attribute :value, String, pattern: '[^\t\n\r]'
555
+ end
556
+
557
+ value_object "Note" do
558
+ attribute :value, String
559
+ end
560
+
561
+ lifecycle :status, default: "requested" do
562
+ mark :failure, "stopped"
563
+ transition "Finish" => "finished", from: "requested"
564
+ transition "Stop" => "stopped", from: "requested"
565
+ end
566
+
567
+ # Writes a launcher beside each domain. It replaces the generated launcher already there and
568
+ # deletes nothing, so it runs without `confirm` (`hecks project_cli` never asked for one).
569
+ command "ProjectCli" do
570
+ role "Operator"
571
+ goal "Write a command-line launcher beside each domain of this project"
572
+
573
+ attribute :run, LaunchKey
574
+ attribute :domains, DomainList, optional: true
575
+
576
+ sets :run
577
+ sets :domains
578
+
579
+ emits LaunchersRequested
580
+ end
581
+
582
+ # Writes the stub files of a new domain (ADR 0087). It asks nothing and replaces nothing: a
583
+ # directory that already holds any file it would write is refused, with that file named.
584
+ command "Init" do
585
+ role "Operator"
586
+ goal "Write the stub files of a new domain"
587
+
588
+ attribute :run, LaunchKey
589
+ attribute :name, DomainName
590
+ attribute :adapter, AdapterName, optional: true
591
+ attribute :dir, TargetDir, optional: true
592
+
593
+ sets :run
594
+ sets :name
595
+ sets :adapter
596
+ sets :dir
597
+
598
+ emits StubRequested
599
+ end
600
+
601
+ # Holds an interview with a subject matter expert and drafts a first domain from it (ADR 0088).
602
+ # The Terminal adapter runs the conversation, as it runs a console; the journal records that one
603
+ # was held and how it ended, not what was said. `--no-ai` leaves the model out.
604
+ command "Interview" do
605
+ role "Operator"
606
+ goal "Hold an interview with a subject matter expert and draft a first domain from it"
607
+
608
+ attribute :run, LaunchKey
609
+ attribute :name, DomainName
610
+ attribute :adapter, AdapterName, optional: true
611
+ attribute :dir, TargetDir, optional: true
612
+ attribute :expert, ExpertName, optional: true
613
+ attribute :no_ai, Switch, optional: true
614
+
615
+ sets :run
616
+ sets :name
617
+ sets :adapter
618
+ sets :dir
619
+ sets :expert
620
+ sets :no_ai
621
+
622
+ emits InterviewRequested
623
+ end
624
+
625
+ # A stdio server runs until its client closes stdin, so it cannot be asked and answered
626
+ # quickly like the others. The command records that the launch was started; the Terminal
627
+ # adapter hands the process over to the launch and answers when it closes, as OpenConsole
628
+ # does for a console. The launcher spells it `serve_mcp`; the `mcp` spelling exe/hecks ships
629
+ # today stays with its router until the launcher is generated.
630
+ command "ServeMcp" do
631
+ role "Operator"
632
+ goal "Serve the storehouse bus to an MCP client over stdio until it closes"
633
+
634
+ attribute :run, LaunchKey
635
+ attribute :stdio, Switch, optional: true
636
+
637
+ sets :run
638
+ sets :stdio
639
+
640
+ emits McpRequested
641
+ end
642
+
643
+ command "Finish" do
644
+ role "System"
645
+ goal "Record that the launch did its work, or was closed, and what it said"
646
+
647
+ reference_to Launch
648
+ attribute :output, Note
649
+
650
+ sets :output
651
+
652
+ emits LaunchFinished
653
+ end
654
+
655
+ command "Stop" do
656
+ role "System"
657
+ goal "Record that the launch could not be opened, and why"
658
+
659
+ reference_to Launch
660
+ attribute :refusal, Note
661
+
662
+ sets :refusal
663
+
664
+ emits LaunchStopped
665
+ end
666
+
667
+ query "Ended" do
668
+ description "How one launch ended: its status, and what it said or why it stopped."
669
+ attribute :run, LaunchKey
670
+ where(run: :run)
671
+ order_by :run
672
+ end
673
+
674
+ query "Stopped" do
675
+ description "Every launch that could not be opened."
676
+ where(status: "stopped")
677
+ order_by :run
678
+ end
679
+ end
680
+
681
+ policy "WriteWhenRequested" do
682
+ on "Launch.LaunchersRequested"
683
+ ask :write, with: { run: :run }
684
+ end
685
+
686
+ policy "ScaffoldWhenRequested" do
687
+ on "Launch.StubRequested"
688
+ ask :scaffold, with: { run: :run }
689
+ end
690
+
691
+ policy "ConverseWhenRequested" do
692
+ on "Launch.InterviewRequested"
693
+ ask :converse, with: { run: :run }
694
+ end
695
+
696
+ policy "ServeWhenRequested" do
697
+ on "Launch.McpRequested"
698
+ ask :serve, with: { run: :run }
699
+ end
700
+
701
+ policy "RecordTheLaunch" do
702
+ on "Launch.LaunchAnswered"
703
+ trigger Launch::Finish, with: { output: :output }
704
+ end
705
+
706
+ policy "RecordTheStoppedLaunch" do
707
+ on "Launch.LaunchRefused"
708
+ trigger Launch::Stop, with: { refusal: :refusal }
709
+ end
710
+
711
+ aggregate "Build" do
712
+ description "One build of a domain for the Rust host, or a check of that build against Ruby."
713
+
714
+ attribute :run, BuildKey
715
+ attribute :domain, DomainPath, optional: true
716
+ attribute :script, ScriptPath, optional: true
717
+ attribute :artifact, Artifact, optional: true
718
+ attribute :target, TargetTriple, optional: true
719
+ attribute :stage_dir, StagePath, optional: true
720
+ attribute :seeds, Count, optional: true
721
+ attribute :steps, Count, optional: true
722
+ attribute :output, Report, optional: true
723
+ attribute :refusal, Report, optional: true
724
+ identified_by :run
725
+
726
+ value_object "BuildKey" do
727
+ attribute :value, String, pattern: '[^ \t\n\r]'
728
+
729
+ invariant("a build is named") { !value.to_s.empty? }
730
+ end
731
+
732
+ # A domain directory. Its last part is also a Rust module and a Cargo feature, so it is a
733
+ # lowercase identifier; the toolchain refuses the reserved words the pattern cannot.
734
+ value_object "DomainPath" do
735
+ attribute :value, String, pattern: '(^|/)[a-z][a-z0-9_]*/?$'
736
+ end
737
+
738
+ # A generated module: the directory name a domain was built as.
739
+ value_object "ModuleName" do
740
+ attribute :value, String, pattern: '^[a-z][a-z0-9_]*$'
741
+ end
742
+
743
+ # A JSON file of steps for a domain to replay.
744
+ value_object "ScriptPath" do
745
+ attribute :value, String, pattern: '[^\t\n\r]'
746
+ end
747
+
748
+ # What a build is replayed through: `native`, `build` (the domain's own feature), a `.wasm`
749
+ # module, or a binary.
750
+ value_object "Artifact" do
751
+ attribute :value, String, pattern: '[^ \t\n\r]'
752
+ end
753
+
754
+ # A Rust target triple: `<arch>-<vendor>-<os>` with an optional `-<env>`. The toolchain
755
+ # refuses the triples a host cannot be built for (a wasm target) and the ones not installed.
756
+ value_object "TargetTriple" do
757
+ attribute :value, String, pattern: '^[a-z0-9_]+(-[a-z0-9_.]+){2,3}$'
758
+ end
759
+
760
+ # The directory a host build stages its files in.
761
+ value_object "StagePath" do
762
+ attribute :value, String, pattern: '[^\t\n\r]'
763
+ end
764
+
765
+ value_object "Count" do
766
+ attribute :value, Integer
767
+
768
+ invariant("a count is positive") { value > 0 }
769
+ end
770
+
771
+ value_object "CodegenName" do
772
+ attribute :value, String, pattern: '^(ruby|rust)$'
773
+ end
774
+
775
+ value_object "Report" do
776
+ attribute :value, String
777
+ end
778
+
779
+ lifecycle :status, default: "requested" do
780
+ mark :failure, "faulted"
781
+ transition "Complete" => "completed", from: "requested"
782
+ transition "Fault" => "faulted", from: "requested"
783
+ end
784
+
785
+ # Writes the domain's Rust source and a Cargo feature for it into the build workspace: the
786
+ # checkout's own `rust/`, or a copy of the packaged workspace under `.hecks/rust/<version>/`.
787
+ # Nothing is ever written into the installed gem.
788
+ command "ProjectRust" do
789
+ role "Operator"
790
+ goal "Generate a domain's Rust source for the Rust host"
791
+
792
+ attribute :domain, DomainPath
793
+ attribute :run, BuildKey
794
+
795
+ sets :domain
796
+ sets :run
797
+
798
+ emits RustProjectionRequested
799
+ end
800
+
801
+ command "BuildWasm" do
802
+ role "Operator"
803
+ goal "Compile a domain to a WASI module a runtime such as wasmtime can run"
804
+
805
+ attribute :domain, DomainPath
806
+ attribute :run, BuildKey
807
+
808
+ sets :domain
809
+ sets :run
810
+
811
+ emits WasmBuildRequested
812
+ end
813
+
814
+ # Compiles the host binary from the installed gem's workspace for the target and stages it with
815
+ # the domain's `.wasm` and `.ir.json` (in `.hecks/host/<target>/` unless `stage_dir` names a
816
+ # directory), the files a container image copies in.
817
+ command "BuildHost" do
818
+ role "Operator"
819
+ goal "Build the Rust host for a domain, with its wasm and IR, ready to copy into an image"
820
+
821
+ attribute :domain, DomainPath
822
+ attribute :run, BuildKey
823
+ attribute :target, TargetTriple, optional: true
824
+ attribute :stage_dir, StagePath, optional: true
825
+
826
+ sets :domain
827
+ sets :run
828
+ sets :target
829
+ sets :stage_dir
830
+
831
+ emits HostBuildRequested
832
+ end
833
+
834
+ command "BuildBrowserWasm" do
835
+ role "Operator"
836
+ goal "Compile a domain to an ES module a browser can import"
837
+
838
+ attribute :domain, DomainPath
839
+ attribute :run, BuildKey
840
+
841
+ sets :domain
842
+ sets :run
843
+
844
+ emits BrowserWasmBuildRequested
845
+ end
846
+
847
+ # Without an artifact it answers Ruby's own result, which saved to a file becomes a pinned
848
+ # expectation; with one it compares Ruby's result with that artifact's.
849
+ command "CheckConformance" do
850
+ role "Operator"
851
+ goal "Replay a script through Ruby and a Rust build of the domain and compare what each did"
852
+
853
+ attribute :domain, DomainPath
854
+ attribute :script, ScriptPath
855
+ attribute :run, BuildKey
856
+ attribute :artifact, Artifact, optional: true
857
+
858
+ sets :domain
859
+ sets :script
860
+ sets :run
861
+ sets :artifact
862
+
863
+ emits ConformanceRequested
864
+ end
865
+
866
+ command "FuzzConformance" do
867
+ role "Operator"
868
+ goal "Replay generated sequences through Ruby and a Rust build of the domain and compare them"
869
+
870
+ attribute :domain, DomainPath
871
+ attribute :artifact, Artifact
872
+ attribute :run, BuildKey
873
+ attribute :seeds, Count, optional: true
874
+ attribute :steps, Count, optional: true
875
+
876
+ sets :domain
877
+ sets :artifact
878
+ sets :run
879
+ sets :seeds
880
+ sets :steps
881
+
882
+ emits ConformanceFuzzRequested
883
+ end
884
+
885
+ command "CheckCoverageAllowlist" do
886
+ role "Operator"
887
+ goal "Check that every rule excusing a Rust coverage gap still excuses a real one"
888
+
889
+ attribute :run, BuildKey
890
+
891
+ sets :run
892
+
893
+ emits CoverageAllowlistRequested
894
+ end
895
+
896
+ command "Complete" do
897
+ role "System"
898
+ goal "Record that the build or check ended well, and what it reported"
899
+
900
+ reference_to Build
901
+ attribute :output, Report
902
+
903
+ sets :output
904
+
905
+ emits BuildCompleted
906
+ end
907
+
908
+ command "Fault" do
909
+ role "System"
910
+ goal "Record that the build or check was refused or found a difference, and what it said"
911
+
912
+ reference_to Build
913
+ attribute :refusal, Report
914
+
915
+ sets :refusal
916
+
917
+ emits BuildFaulted
918
+ end
919
+
920
+ query "Result" do
921
+ description "How one build or check ended: its status, and what it reported or why it failed."
922
+ attribute :run, BuildKey
923
+ where(run: :run)
924
+ order_by :run
925
+ end
926
+
927
+ query "Faulted" do
928
+ description "Every build or check that was refused or found a difference."
929
+ where(status: "faulted")
930
+ order_by :run
931
+ end
932
+
933
+ # An answer that is one document of text, exactly as the tool that produced it wrote it.
934
+ value_object "Document" do
935
+ attribute :text, String
936
+ end
937
+
938
+ query "RustCoverage" do
939
+ description "Whether each construct of a generated module has a routed Rust implementation."
940
+ attribute :module_name, ModuleName
941
+ attribute :codegen, CodegenName, optional: true
942
+ returns Document
943
+ end
944
+ end
945
+
946
+ policy "GenerateWhenRequested" do
947
+ on "Build.RustProjectionRequested"
948
+ ask :generate, with: { run: :run }
949
+ end
950
+
951
+ policy "CompileWasmWhenRequested" do
952
+ on "Build.WasmBuildRequested"
953
+ ask :wasm, with: { run: :run }
954
+ end
955
+
956
+ policy "BuildHostWhenRequested" do
957
+ on "Build.HostBuildRequested"
958
+ ask :host, with: { run: :run }
959
+ end
960
+
961
+ policy "CompileBrowserWasmWhenRequested" do
962
+ on "Build.BrowserWasmBuildRequested"
963
+ ask :browser_wasm, with: { run: :run }
964
+ end
965
+
966
+ policy "ConformWhenRequested" do
967
+ on "Build.ConformanceRequested"
968
+ ask :conform, with: { run: :run }
969
+ end
970
+
971
+ policy "ReplayWhenRequested" do
972
+ on "Build.ConformanceFuzzRequested"
973
+ ask :replay, with: { run: :run }
974
+ end
975
+
976
+ policy "AuditWhenRequested" do
977
+ on "Build.CoverageAllowlistRequested"
978
+ ask :audit, with: { run: :run }
979
+ end
980
+
981
+ policy "RecordTheBuild" do
982
+ on "Build.BuildAnswered"
983
+ trigger Build::Complete, with: { output: :output }
984
+ end
985
+
986
+ policy "RecordTheFault" do
987
+ on "Build.BuildRefused"
988
+ trigger Build::Fault, with: { refusal: :refusal }
989
+ end
990
+
991
+ # Named for the run, as ModelCheckRun is: `Hecks::Fuzzing` is the fuzzing toolkit's own module,
992
+ # so a hecksagon could not spell an aggregate of that name.
993
+ aggregate "FuzzRun" do
994
+ description "One fuzz sweep or benchmark, and how it ended; and the generated sequences it replays."
995
+
996
+ attribute :domain, DomainPath, optional: true
997
+ attribute :run, FuzzKey
998
+ attribute :seeds, Count, optional: true
999
+ attribute :steps, Count, optional: true
1000
+ attribute :workers, Count, optional: true
1001
+ attribute :budget, Count, optional: true
1002
+ attribute :adapter, Persistence, optional: true
1003
+ attribute :persist_regressions, RegressionSwitch, optional: true
1004
+ attribute :domains, NameList, optional: true
1005
+ attribute :targets, NameList, optional: true
1006
+ attribute :iterations, Count, optional: true
1007
+ attribute :warmup, Warmup, optional: true
1008
+ attribute :runs, Count, optional: true
1009
+ attribute :rust_binary, BinaryPath, optional: true
1010
+ attribute :format, Format, optional: true
1011
+ attribute :output, OutputPath, optional: true
1012
+ attribute :report, Report, optional: true
1013
+ attribute :refusal, Report, optional: true
1014
+ identified_by :run
1015
+
1016
+ value_object "FuzzKey" do
1017
+ attribute :value, String, pattern: '[^ \t\n\r]'
1018
+
1019
+ invariant("a sweep is named") { !value.to_s.empty? }
1020
+ end
1021
+
1022
+ # A domain directory; a sweep with none covers every domain of a hecks checkout.
1023
+ value_object "DomainPath" do
1024
+ attribute :value, String, pattern: '[^\t\n\r]'
1025
+
1026
+ invariant("a domain is named by its path") { !value.to_s.empty? }
1027
+ end
1028
+
1029
+ value_object "Count" do
1030
+ attribute :value, Integer
1031
+
1032
+ invariant("a count is positive") { value > 0 }
1033
+ end
1034
+
1035
+ value_object "Warmup" do
1036
+ attribute :value, Integer
1037
+
1038
+ invariant("a warmup is not negative") { value >= 0 }
1039
+ end
1040
+
1041
+ # Which persistence every ephemeral boot of a sweep uses.
1042
+ value_object "Persistence" do
1043
+ attribute :value, String, pattern: '^(memory|sqlite|postgres)$'
1044
+ end
1045
+
1046
+ # Whether each distinct finding's minimized repro is also kept under `spec/corpus/regressions/`.
1047
+ value_object "RegressionSwitch" do
1048
+ attribute :value, Boolean
1049
+ end
1050
+
1051
+ # Names, comma separated: benchmark domains, or the targets to measure.
1052
+ value_object "NameList" do
1053
+ attribute :value, String, pattern: '^[^ \t\n\r,]+(,[^ \t\n\r,]+)*$'
1054
+ end
1055
+
1056
+ value_object "BinaryPath" do
1057
+ attribute :value, String, pattern: '[^\t\n\r]'
1058
+ end
1059
+
1060
+ value_object "Format" do
1061
+ attribute :value, String, pattern: '^(markdown|json)$'
1062
+ end
1063
+
1064
+ value_object "OutputPath" do
1065
+ attribute :value, String, pattern: '[^\t\n\r]'
1066
+ end
1067
+
1068
+ value_object "Report" do
1069
+ attribute :value, String
1070
+ end
1071
+
1072
+ lifecycle :status, default: "requested" do
1073
+ mark :failure, "halted"
1074
+ transition "Conclude" => "concluded", from: "requested"
1075
+ transition "Halt" => "halted", from: "requested"
1076
+ end
1077
+
1078
+ # A sweep replays generated command and query sequences and checks properties over each.
1079
+ # A finding is shrunk and saved under `tmp/fuzz-failures/`, and ends the run as halted, with
1080
+ # what was found. Sweeping every domain of a checkout needs a hecks checkout; a client names
1081
+ # the domain. The sweep runs in child processes started through the ProcessPool adapter, so an
1082
+ # interrupt reaches the workers.
1083
+ command "Fuzz" do
1084
+ role "Operator"
1085
+ goal "Fuzz a domain with generated sequences and keep whether anything broke a property"
1086
+
1087
+ attribute :domain, DomainPath, optional: true
1088
+ attribute :run, FuzzKey
1089
+ attribute :seeds, Count, optional: true
1090
+ attribute :steps, Count, optional: true
1091
+ attribute :workers, Count, optional: true
1092
+ attribute :adapter, Persistence, optional: true
1093
+ attribute :persist_regressions, RegressionSwitch, optional: true
1094
+
1095
+ sets :domain
1096
+ sets :run
1097
+ sets :seeds
1098
+ sets :steps
1099
+ sets :workers
1100
+ sets :adapter
1101
+ sets :persist_regressions
1102
+
1103
+ emits FuzzRequested
1104
+ end
1105
+
1106
+ # Mutation-tests a domain's own checks: changes its bluebooks in small ways and keeps which
1107
+ # changes the fuzz properties, the corpus script and the domain's behaviors tests let through.
1108
+ # A survivor is a hole in those checks. `budget` caps how many mutants are tried. The run is a
1109
+ # child process started through the ProcessPool adapter, as a sweep is.
1110
+ command "Mutate" do
1111
+ role "Operator"
1112
+ goal "Mutate a domain's rules and keep whether its checks noticed each change"
1113
+
1114
+ attribute :domain, DomainPath
1115
+ attribute :run, FuzzKey
1116
+ attribute :seeds, Count, optional: true
1117
+ attribute :steps, Count, optional: true
1118
+ attribute :budget, Count, optional: true
1119
+
1120
+ sets :domain
1121
+ sets :run
1122
+ sets :seeds
1123
+ sets :steps
1124
+ sets :budget
1125
+
1126
+ emits MutateRequested
1127
+ end
1128
+
1129
+ # Measures each persistence adapter, and the Rust binary when there is one to build or use.
1130
+ # The report is kept in this record, which the Hecks domain binds to Memory: keeping a
1131
+ # domain's benchmark history beside it, in Sqlite under the project's `.hecks/`, is decided
1132
+ # with the persistence binding for Custodian, not here. `output` also writes the full JSON.
1133
+ command "Bench" do
1134
+ role "Operator"
1135
+ goal "Measure command throughput and latency for each target, and keep the report"
1136
+
1137
+ attribute :run, FuzzKey
1138
+ attribute :domains, NameList, optional: true
1139
+ attribute :targets, NameList, optional: true
1140
+ attribute :iterations, Count, optional: true
1141
+ attribute :warmup, Warmup, optional: true
1142
+ attribute :runs, Count, optional: true
1143
+ attribute :rust_binary, BinaryPath, optional: true
1144
+ attribute :format, Format, optional: true
1145
+ attribute :output, OutputPath, optional: true
1146
+
1147
+ sets :run
1148
+ sets :domains
1149
+ sets :targets
1150
+ sets :iterations
1151
+ sets :warmup
1152
+ sets :runs
1153
+ sets :rust_binary
1154
+ sets :format
1155
+ sets :output
1156
+
1157
+ emits BenchRequested
1158
+ end
1159
+
1160
+ command "Conclude" do
1161
+ role "System"
1162
+ goal "Record that the sweep or benchmark ended well, and what it reported"
1163
+
1164
+ reference_to FuzzRun
1165
+ attribute :report, Report
1166
+
1167
+ sets :report
1168
+
1169
+ emits FuzzConcluded
1170
+ end
1171
+
1172
+ command "Halt" do
1173
+ role "System"
1174
+ goal "Record that the sweep found something or the benchmark could not run, and what it said"
1175
+
1176
+ reference_to FuzzRun
1177
+ attribute :refusal, Report
1178
+
1179
+ sets :refusal
1180
+
1181
+ emits FuzzHalted
1182
+ end
1183
+
1184
+ query "Conclusion" do
1185
+ description "How one sweep or benchmark ended: its status, and what it reported or found."
1186
+ attribute :run, FuzzKey
1187
+ where(run: :run)
1188
+ order_by :run
1189
+ end
1190
+
1191
+ query "Halted" do
1192
+ description "Every sweep that found something and every benchmark that could not run."
1193
+ where(status: "halted")
1194
+ order_by :run
1195
+ end
1196
+
1197
+ # An answer that is one document of text, exactly as the tool that produced it wrote it.
1198
+ value_object "Document" do
1199
+ attribute :text, String
1200
+ end
1201
+
1202
+ # The generator `hecks fuzz` draws from, on its own: one valid dispatch sequence as a
1203
+ # replayable script. `adversarial` is the fraction of steps mutated to be refused; 0 is off.
1204
+ query "GenerateSequence" do
1205
+ description "One generated, valid dispatch sequence for a domain, as a script `hecks run` replays."
1206
+ attribute :domain, DomainPath
1207
+ attribute :seed, Integer, optional: true
1208
+ attribute :steps, Integer, optional: true
1209
+ attribute :adversarial, Float, optional: true
1210
+ returns Document
1211
+ end
1212
+ end
1213
+
1214
+ policy "SweepWhenRequested" do
1215
+ on "FuzzRun.FuzzRequested"
1216
+ ask :sweep, with: { run: :run }
1217
+ end
1218
+
1219
+ policy "MutateWhenRequested" do
1220
+ on "FuzzRun.MutateRequested"
1221
+ ask :probe, with: { run: :run }
1222
+ end
1223
+
1224
+ policy "MeasureWhenRequested" do
1225
+ on "FuzzRun.BenchRequested"
1226
+ ask :measure, with: { run: :run }
1227
+ end
1228
+
1229
+ policy "RecordTheSweep" do
1230
+ on "FuzzRun.FuzzAnswered"
1231
+ trigger FuzzRun::Conclude, with: { report: :report }
1232
+ end
1233
+
1234
+ policy "RecordTheFinding" do
1235
+ on "FuzzRun.FuzzRefused"
1236
+ trigger FuzzRun::Halt, with: { refusal: :refusal }
1237
+ end
1238
+ end