hecks 3.10.0 → 3.12.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 (542) 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 +158 -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 +70 -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/newcomer.rb +50 -0
  40. data/lib/hecks/adapters/driving/ruby/aggregate_module.rb +171 -0
  41. data/lib/hecks/adapters/driving/ruby/chapter.rb +91 -0
  42. data/lib/hecks/adapters/driving/ruby.rb +83 -0
  43. data/lib/hecks/adapters/driving/usage_cache.rb +144 -0
  44. data/lib/hecks/{doors.rb → adapters/driving.rb} +10 -6
  45. data/lib/hecks/behaviors/expectations/boot.rb +1 -1
  46. data/lib/hecks/bench/ruby_runner.rb +1 -1
  47. data/lib/hecks/bluebook/ask_resolution.rb +130 -0
  48. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +8 -2
  49. data/lib/hecks/bluebook/assembly/contracts/read_models.rb +1 -1
  50. data/lib/hecks/bluebook/assembly/contracts/rules.rb +4 -2
  51. data/lib/hecks/bluebook/assembly/contracts/structure.rb +3 -1
  52. data/lib/hecks/bluebook/assembly/marks.rb +4 -15
  53. data/lib/hecks/bluebook/assembly/scalars.rb +39 -0
  54. data/lib/hecks/bluebook/attribute.rb +1 -1
  55. data/lib/hecks/bluebook/behaviour/command.rb +13 -8
  56. data/lib/hecks/bluebook/behaviour/policy.rb +30 -0
  57. data/lib/hecks/bluebook/capabilities/attribute_values.rb +72 -0
  58. data/lib/hecks/bluebook/capabilities.rb +91 -9
  59. data/lib/hecks/bluebook/domain_port.rb +16 -4
  60. data/lib/hecks/bluebook/dsl/adapter_builder.rb +18 -6
  61. data/lib/hecks/bluebook/dsl/binding_proxy.rb +13 -0
  62. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/computed_sources.rb +98 -0
  63. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/fan_out_rows.rb +42 -0
  64. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/projected_fields.rb +14 -2
  65. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/provisions.rb +69 -3
  66. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/with_projections.rb +17 -10
  67. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +5 -0
  68. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -0
  69. data/lib/hecks/bluebook/dsl/command_builder/mutations.rb +8 -1
  70. data/lib/hecks/bluebook/dsl/command_builder/operands.rb +121 -0
  71. data/lib/hecks/bluebook/dsl/command_builder.rb +2 -0
  72. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +10 -1
  73. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +45 -2
  74. data/lib/hecks/bluebook/dsl/policy_builder.rb +47 -10
  75. data/lib/hecks/bluebook/dsl/rule_reference.rb +7 -4
  76. data/lib/hecks/bluebook/dsl/world_builder.rb +1 -1
  77. data/lib/hecks/bluebook/expression/ast_json/emitters.rb +6 -0
  78. data/lib/hecks/bluebook/expression/ast_json.rb +9 -6
  79. data/lib/hecks/bluebook/expression/ast_reader.rb +14 -9
  80. data/lib/hecks/bluebook/expression/projection.json +36 -0
  81. data/lib/hecks/bluebook/expression/resolver/operations.rb +31 -0
  82. data/lib/hecks/bluebook/expression/resolver/parsing.rb +38 -5
  83. data/lib/hecks/bluebook/expression/resolver/scanning.rb +22 -5
  84. data/lib/hecks/bluebook/expression/resolver/values.rb +10 -0
  85. data/lib/hecks/bluebook/expression/resolver.rb +20 -1
  86. data/lib/hecks/bluebook/hecksagon.rb +23 -3
  87. data/lib/hecks/bluebook/lifecycle.rb +22 -3
  88. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +13 -1
  89. data/lib/hecks/bluebook/meta_validator/modes.rb +4 -1
  90. data/lib/hecks/bluebook/meta_validator/readings.rb +24 -1
  91. data/lib/hecks/bluebook/meta_validator/reconstruction/heads.rb +2 -1
  92. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +6 -3
  93. data/lib/hecks/bluebook/meta_validator/shapes.rb +5 -0
  94. data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +1 -1
  95. data/lib/hecks/bluebook/meta_validator.rb +12 -12
  96. data/lib/hecks/bluebook/model_check/ask_checks.rb +71 -0
  97. data/lib/hecks/bluebook/model_check/policy_checks.rb +2 -1
  98. data/lib/hecks/bluebook/model_check/reference_checks.rb +70 -0
  99. data/lib/hecks/bluebook/model_check.rb +8 -2
  100. data/lib/hecks/bluebook/policy.rb +4 -2
  101. data/lib/hecks/bluebook/smoke_test.rb +1 -1
  102. data/lib/hecks/bluebook/synthesizer.rb +2 -0
  103. data/lib/hecks/bluebook.rb +2 -0
  104. data/lib/hecks/cli/command_table.rb +1 -1
  105. data/lib/hecks/cli/console.rb +2 -1
  106. data/lib/hecks/cli/document.rb +1 -1
  107. data/lib/hecks/cli/launcher_words.rb +4 -4
  108. data/lib/hecks/cli/mcp.rb +4 -4
  109. data/lib/hecks/cli/project_cli/launcher_source.rb +2 -2
  110. data/lib/hecks/cli/project_cli/templates.rb +5 -5
  111. data/lib/hecks/cli/project_cli.rb +9 -6
  112. data/lib/hecks/cli/run.rb +2 -2
  113. data/lib/hecks/cli/smoke_http/checks.rb +4 -1
  114. data/lib/hecks/cli/smoke_http/transport.rb +3 -3
  115. data/lib/hecks/cli/smoke_http.rb +3 -0
  116. data/lib/hecks/codemod.rb +5 -2
  117. data/lib/hecks/corpus/routes.rb +7 -0
  118. data/lib/hecks/corpus/rust.rb +3 -2
  119. data/lib/hecks/deploy/bluebook/deploy.bluebook +34 -4
  120. data/lib/hecks/doc/reference/rendering.rb +6 -3
  121. data/lib/hecks/forms/app/commands.rb +8 -7
  122. data/lib/hecks/forms/app/queries.rb +2 -1
  123. data/lib/hecks/forms/app.rb +1 -1
  124. data/lib/hecks/forms/record_renderer.rb +1 -1
  125. data/lib/hecks/framework/bluebook/privacy.bluebook +11 -0
  126. data/lib/hecks/fuzzing/concurrent_dispatch.rb +4 -1
  127. data/lib/hecks/fuzzing/failure_triage.rb +2 -1
  128. data/lib/hecks/fuzzing/mutation/run.rb +11 -4
  129. data/lib/hecks/fuzzing/properties/catalog.rb +6 -4
  130. data/lib/hecks/fuzzing/properties/guaranteed_by_construction.rb +18 -11
  131. data/lib/hecks/fuzzing/properties/guards.rb +1 -1
  132. data/lib/hecks/fuzzing/self_consistency/saga_redelivery.rb +1 -1
  133. data/lib/hecks/fuzzing/self_consistency.rb +6 -2
  134. data/lib/hecks/fuzzing/shrinker.rb +3 -1
  135. data/lib/hecks/fuzzing/value_generator.rb +19 -5
  136. data/lib/hecks/gate/stages.yml +33 -7
  137. data/lib/hecks/grammar/evolve.rb +1 -1
  138. data/lib/hecks/grammar/expression_operators.json +372 -0
  139. data/lib/hecks/grammar/operators.rb +3 -1
  140. data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +10 -10
  141. data/lib/hecks/hecks/adapters/codebase/inspection/rubycritic.rb +35 -0
  142. data/lib/hecks/hecks/adapters/codebase/inspection.rb +104 -0
  143. data/lib/hecks/hecks/adapters/codebase/publishing.rb +2 -1
  144. data/lib/hecks/hecks/adapters/codebase/release_facts.rb +12 -2
  145. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +2 -1
  146. data/lib/hecks/hecks/adapters/codebase/test_runner.rb +1 -1
  147. data/lib/hecks/hecks/adapters/cost_explorer.rb +8 -5
  148. data/lib/hecks/hecks/adapters/deploy_toolchain/statuses.rb +2 -1
  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 +133 -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/terminal.rb +13 -11
  173. data/lib/hecks/hecks/codebase.behaviors +13 -1
  174. data/lib/hecks/hecks/codebase.bluebook +239 -5
  175. data/lib/hecks/hecks/custodian.behaviors +24 -6
  176. data/lib/hecks/hecks/custodian.bluebook +6 -1227
  177. data/lib/hecks/hecks/deploy.behaviors +1 -1
  178. data/lib/hecks/hecks/deploy.world +1 -3
  179. data/lib/hecks/hecks/hecks.behaviors +1 -1
  180. data/lib/hecks/hecks/hecks.hecksagon +53 -18
  181. data/lib/hecks/hecks/hecks.world +13 -10
  182. data/lib/hecks/hecks/rehearsal.bluebook +477 -0
  183. data/lib/hecks/hecks/site.behaviors +1 -1
  184. data/lib/hecks/hecks/site.world +0 -1
  185. data/lib/hecks/hecks/tooling.bluebook +1238 -0
  186. data/lib/hecks/indifferent_key.rb +20 -0
  187. data/lib/hecks/language/adapter.bluebook +13 -3
  188. data/lib/hecks/language/bluebook/aggregate.bluebook +23 -0
  189. data/lib/hecks/language/bluebook/bluebook.behaviors +23 -0
  190. data/lib/hecks/language/bluebook/bluebook.bluebook +15 -0
  191. data/lib/hecks/language/bluebook/entity.bluebook +18 -0
  192. data/lib/hecks/language/bluebook/policy.behaviors +19 -0
  193. data/lib/hecks/language/bluebook/policy.bluebook +9 -1
  194. data/lib/hecks/language/bluebook/query.bluebook +1 -1
  195. data/lib/hecks/language/bluebook/vocabulary.bluebook +63 -4
  196. data/lib/hecks/language/hecksagon/domain_port.bluebook +1 -1
  197. data/lib/hecks/language/hecksagon/hecksagon.bluebook +22 -0
  198. data/lib/hecks/language/oidc.json +10 -0
  199. data/lib/hecks/language/port.bluebook +4 -4
  200. data/lib/hecks/literal.rb +12 -3
  201. data/lib/hecks/ports/key_vault.rb +7 -2
  202. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +21 -10
  203. data/lib/hecks/ports/persistence/plugins/era/postgres_era/boot.rb +4 -2
  204. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +2 -1
  205. data/lib/hecks/ports/persistence/repository_factory.rb +6 -3
  206. data/lib/hecks/projection_files/expression_tables.rb +7 -2
  207. data/lib/hecks/projections/deploy/box/compose.rb +6 -4
  208. data/lib/hecks/projections/deploy/box/edge.rb +62 -0
  209. data/lib/hecks/projections/deploy/box/logging.rb +99 -0
  210. data/lib/hecks/projections/deploy/box/roll_scripts.rb +16 -8
  211. data/lib/hecks/projections/deploy/box/settings/cdn.rb +40 -0
  212. data/lib/hecks/projections/deploy/box/settings/logs.rb +40 -0
  213. data/lib/hecks/projections/deploy/box/settings/patterns.rb +11 -0
  214. data/lib/hecks/projections/deploy/box/settings/types.rb +16 -1
  215. data/lib/hecks/projections/deploy/box/settings.rb +6 -2
  216. data/lib/hecks/projections/deploy/box/stack_yaml.rb +5 -2
  217. data/lib/hecks/projections/deploy/box/templates/Makefile.tmpl +16 -0
  218. data/lib/hecks/projections/deploy/box/templates/box.yaml.tmpl +8 -4
  219. data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +32 -0
  220. data/lib/hecks/projections/deploy/box/templates/deploy-service.sh.tmpl +1 -1
  221. data/lib/hecks/projections/deploy/box/templates/edge.mk.tmpl +17 -0
  222. data/lib/hecks/projections/deploy/box/templates/edge.yaml.tmpl +65 -0
  223. data/lib/hecks/projections/deploy/box/templates/log-group.yaml.tmpl +12 -0
  224. data/lib/hecks/projections/deploy/box/templates/log-policy.yaml.tmpl +7 -0
  225. data/lib/hecks/projections/deploy/box/templates/render-compose-taskdef.sh.tmpl +5 -4
  226. data/lib/hecks/projections/deploy/box/templates/render-compose.sh.tmpl +5 -4
  227. data/lib/hecks/projections/deploy/box.rb +15 -3
  228. data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
  229. data/lib/hecks/projections/deploy/shared.rb +5 -2
  230. data/lib/hecks/projections/model/deviations.rb +2 -2
  231. data/lib/hecks/projections/model.rb +2 -2
  232. data/lib/hecks/projections/oidc.rb +13 -1
  233. data/lib/hecks/projections/shape.rb +5 -2
  234. data/lib/hecks/projections/site/cms_editor/attributes.rb +26 -6
  235. data/lib/hecks/projections/site/cms_editor/blocks.rb +157 -0
  236. data/lib/hecks/projections/site/cms_editor/checks.rb +14 -1
  237. data/lib/hecks/projections/site/cms_editor/clearing.rb +70 -0
  238. data/lib/hecks/projections/site/cms_editor/color.rb +124 -0
  239. data/lib/hecks/projections/site/cms_editor/composition.rb +42 -0
  240. data/lib/hecks/projections/site/cms_editor/destructive.rb +61 -0
  241. data/lib/hecks/projections/site/cms_editor/drafts.rb +102 -0
  242. data/lib/hecks/projections/site/cms_editor/hints.rb +74 -0
  243. data/lib/hecks/projections/site/cms_editor/icons.rb +86 -0
  244. data/lib/hecks/projections/site/cms_editor/key_kinds.rb +31 -0
  245. data/lib/hecks/projections/site/cms_editor/limits.rb +72 -0
  246. data/lib/hecks/projections/site/cms_editor/media.rb +1 -1
  247. data/lib/hecks/projections/site/cms_editor/moment.rb +34 -0
  248. data/lib/hecks/projections/site/cms_editor/palette.rb +115 -0
  249. data/lib/hecks/projections/site/cms_editor/pickers.rb +85 -0
  250. data/lib/hecks/projections/site/cms_editor/preview.rb +91 -0
  251. data/lib/hecks/projections/site/cms_editor/relations.rb +92 -0
  252. data/lib/hecks/projections/site/cms_editor/scale_checks.rb +66 -0
  253. data/lib/hecks/projections/site/cms_editor/scheduling.rb +150 -0
  254. data/lib/hecks/projections/site/cms_editor/schema.rb +96 -19
  255. data/lib/hecks/projections/site/cms_editor/setting.rb +52 -9
  256. data/lib/hecks/projections/site/cms_editor/targets.rb +86 -0
  257. data/lib/hecks/projections/site/cms_editor/templates/package.json.tmpl +18 -1
  258. data/lib/hecks/projections/site/cms_editor/templates/src/app.ts.tmpl +251 -57
  259. data/lib/hecks/projections/site/cms_editor/templates/src/assets.ts.tmpl +98 -0
  260. data/lib/hecks/projections/site/cms_editor/templates/src/auth/membership.ts.tmpl +32 -24
  261. data/lib/hecks/projections/site/cms_editor/templates/src/browser/app.css.tmpl +412 -0
  262. data/lib/hecks/projections/site/cms_editor/templates/src/browser/autosave.js.tmpl +144 -0
  263. data/lib/hecks/projections/site/cms_editor/templates/src/browser/blocks.js.tmpl +500 -0
  264. data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_doc.js.tmpl +175 -0
  265. data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_editor.js.tmpl +418 -0
  266. data/lib/hecks/projections/site/cms_editor/templates/src/browser/epoch.js.tmpl +61 -0
  267. data/lib/hecks/projections/site/cms_editor/templates/src/browser/forms.js.tmpl +210 -0
  268. data/lib/hecks/projections/site/cms_editor/templates/src/browser/lists.js.tmpl +40 -0
  269. data/lib/hecks/projections/site/cms_editor/templates/src/browser/main.js.tmpl +26 -0
  270. data/lib/hecks/projections/site/cms_editor/templates/src/browser/media_picker.js.tmpl +232 -0
  271. data/lib/hecks/projections/site/cms_editor/templates/src/browser/moments.js.tmpl +73 -0
  272. data/lib/hecks/projections/site/cms_editor/templates/src/browser/pickers.js.tmpl +152 -0
  273. data/lib/hecks/projections/site/cms_editor/templates/src/browser/preview.js.tmpl +98 -0
  274. data/lib/hecks/projections/site/cms_editor/templates/src/browser/shell.js.tmpl +140 -0
  275. data/lib/hecks/projections/site/cms_editor/templates/src/browser/toast.js.tmpl +66 -0
  276. data/lib/hecks/projections/site/cms_editor/templates/src/choices.ts.tmpl +99 -0
  277. data/lib/hecks/projections/site/cms_editor/templates/src/commands.ts.tmpl +78 -24
  278. data/lib/hecks/projections/site/cms_editor/templates/src/config.ts.tmpl +6 -0
  279. data/lib/hecks/projections/site/cms_editor/templates/src/flash.ts.tmpl +52 -0
  280. data/lib/hecks/projections/site/cms_editor/templates/src/host.ts.tmpl +19 -2
  281. data/lib/hecks/projections/site/cms_editor/templates/src/media/handler.ts.tmpl +31 -8
  282. data/lib/hecks/projections/site/cms_editor/templates/src/names.ts.tmpl +36 -0
  283. data/lib/hecks/projections/site/cms_editor/templates/src/schema.ts.tmpl +131 -2
  284. data/lib/hecks/projections/site/cms_editor/templates/src/ui/availability.ts.tmpl +15 -0
  285. data/lib/hecks/projections/site/cms_editor/templates/src/ui/block_list.js.tmpl +213 -0
  286. data/lib/hecks/projections/site/cms_editor/templates/src/ui/blocks.ts.tmpl +275 -0
  287. data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_model.js.tmpl +8 -5
  288. data/lib/hecks/projections/site/cms_editor/templates/src/ui/detail.ts.tmpl +88 -0
  289. data/lib/hecks/projections/site/cms_editor/templates/src/ui/dialogs.ts.tmpl +43 -0
  290. data/lib/hecks/projections/site/cms_editor/templates/src/ui/fields.ts.tmpl +229 -34
  291. data/lib/hecks/projections/site/cms_editor/templates/src/ui/html.ts.tmpl +40 -81
  292. data/lib/hecks/projections/site/cms_editor/templates/src/ui/icons.ts.tmpl +15 -0
  293. data/lib/hecks/projections/site/cms_editor/templates/src/ui/input.ts.tmpl +29 -12
  294. data/lib/hecks/projections/site/cms_editor/templates/src/ui/links.ts.tmpl +100 -0
  295. data/lib/hecks/projections/site/cms_editor/templates/src/ui/moments.ts.tmpl +44 -0
  296. data/lib/hecks/projections/site/cms_editor/templates/src/ui/outline.ts.tmpl +53 -17
  297. data/lib/hecks/projections/site/cms_editor/templates/src/ui/pages.ts.tmpl +178 -88
  298. data/lib/hecks/projections/site/cms_editor/templates/src/ui/paging.ts.tmpl +82 -0
  299. data/lib/hecks/projections/site/cms_editor/templates/src/ui/pickers.ts.tmpl +110 -0
  300. data/lib/hecks/projections/site/cms_editor/templates/src/ui/preview.ts.tmpl +87 -0
  301. data/lib/hecks/projections/site/cms_editor/templates/src/ui/related.ts.tmpl +97 -0
  302. data/lib/hecks/projections/site/cms_editor/templates/src/ui/schedule.ts.tmpl +160 -0
  303. data/lib/hecks/projections/site/cms_editor/templates/src/ui/shell.ts.tmpl +211 -0
  304. data/lib/hecks/projections/site/cms_editor/templates/src/ui/table.ts.tmpl +151 -0
  305. data/lib/hecks/projections/site/cms_editor/templates/src/ui/theme.js.tmpl +16 -0
  306. data/lib/hecks/projections/site/cms_editor/templates/src/ui/values.ts.tmpl +69 -0
  307. data/lib/hecks/projections/site/cms_editor/templates/src/ui/words.ts.tmpl +97 -0
  308. data/lib/hecks/projections/site/cms_editor/templates/src/ui/world.ts.tmpl +35 -0
  309. data/lib/hecks/projections/site/cms_editor/templates/tsconfig.json.tmpl +2 -1
  310. data/lib/hecks/projections/site/cms_editor/theme.rb +29 -0
  311. data/lib/hecks/projections/site/cms_editor/theme_checks.rb +57 -0
  312. data/lib/hecks/projections/site/cms_editor.rb +49 -23
  313. data/lib/hecks/projections/site/role_probe.rb +2 -0
  314. data/lib/hecks/projections/site/site_root/workflow.rb +5 -0
  315. data/lib/hecks/projector/cli_audience.rb +22 -1
  316. data/lib/hecks/projector/cli_projector/listing.rb +1 -1
  317. data/lib/hecks/projector/cli_projector/option_specs.rb +1 -1
  318. data/lib/hecks/projector/cli_projector/usage.rb +15 -4
  319. data/lib/hecks/projector/docs_projector/closing.rb +1 -1
  320. data/lib/hecks/projector/docs_projector.rb +1 -1
  321. data/lib/hecks/projector/exporter/bindings.rb +40 -10
  322. data/lib/hecks/projector/exporter/capability_values.rb +55 -0
  323. data/lib/hecks/projector/narrate_projector/reactions.rb +1 -1
  324. data/lib/hecks/projector/target.rb +11 -0
  325. data/lib/hecks/projector.rb +32 -5
  326. data/lib/hecks/quality_control/adapters/github_checks.rb +12 -8
  327. data/lib/hecks/quality_control/adapters/github_issues.rb +8 -4
  328. data/lib/hecks/quality_control/cli/qa_open_pr.rb +2 -2
  329. data/lib/hecks/quality_control/cli/qa_sweep/checks/shrinking.rb +1 -1
  330. data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +1 -1
  331. data/lib/hecks/quality_control/cli/qa_sweep.rb +1 -1
  332. data/lib/hecks/quality_control/quality_control.bluebook +6 -2
  333. data/lib/hecks/release/approval.rb +64 -0
  334. data/lib/hecks/router/{door.rb → aggregate_reader.rb} +5 -4
  335. data/lib/hecks/router/namespace_installer.rb +16 -16
  336. data/lib/hecks/router.rb +1 -1
  337. data/lib/hecks/runtime/best_effort.rb +22 -0
  338. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +12 -2
  339. data/lib/hecks/runtime/command_interpreter.rb +3 -2
  340. data/lib/hecks/runtime/command_rules/emission.rb +1 -1
  341. data/lib/hecks/runtime/dispatcher.rb +4 -4
  342. data/lib/hecks/runtime/entity_element/location.rb +1 -1
  343. data/lib/hecks/runtime/event.rb +5 -0
  344. data/lib/hecks/runtime/identity.rb +7 -6
  345. data/lib/hecks/runtime/interpreting.rb +2 -1
  346. data/lib/hecks/runtime/invocation_builder.rb +1 -1
  347. data/lib/hecks/runtime/loader/boot_steps.rb +8 -5
  348. data/lib/hecks/runtime/loader.rb +10 -10
  349. data/lib/hecks/runtime/policy_interpreter/arguments.rb +7 -4
  350. data/lib/hecks/runtime/policy_interpreter/fan_out.rb +3 -3
  351. data/lib/hecks/runtime/policy_interpreter.rb +6 -6
  352. data/lib/hecks/runtime/port_operation_interpreter.rb +1 -1
  353. data/lib/hecks/runtime/reaction_invocation.rb +1 -1
  354. data/lib/hecks/runtime/rebuild_sweep.rb +11 -1
  355. data/lib/hecks/runtime/registry/ask_verification.rb +35 -0
  356. data/lib/hecks/runtime/registry/hecksagon_rules.rb +10 -0
  357. data/lib/hecks/runtime/registry/merging.rb +1 -1
  358. data/lib/hecks/runtime/registry/providers.rb +16 -0
  359. data/lib/hecks/runtime/registry/read_repository.rb +6 -4
  360. data/lib/hecks/runtime/registry/verification.rb +4 -1
  361. data/lib/hecks/runtime/registry/world_defaults.rb +29 -0
  362. data/lib/hecks/runtime/registry.rb +1 -1
  363. data/lib/hecks/runtime/saga_interpreter/compensation.rb +1 -1
  364. data/lib/hecks/runtime/saga_interpreter/delivery.rb +4 -4
  365. data/lib/hecks/runtime/saga_interpreter/transitions.rb +1 -1
  366. data/lib/hecks/runtime/saga_interpreter.rb +3 -3
  367. data/lib/hecks/runtime/tenant_check.rb +5 -4
  368. data/lib/hecks/runtime/value/coercion.rb +2 -2
  369. data/lib/hecks/runtime/value/validation.rb +16 -4
  370. data/lib/hecks/runtime.rb +9 -9
  371. data/lib/hecks/rust_build/host.rb +11 -4
  372. data/lib/hecks/rust_build/prebuilt_binary.rb +101 -0
  373. data/lib/hecks/rust_build/project_rust/target_ir.rb +1 -1
  374. data/lib/hecks/rust_build/wasm.rb +1 -1
  375. data/lib/hecks/rust_build/wasm_browser.rb +1 -1
  376. data/lib/hecks/rust_build.rb +11 -0
  377. data/lib/hecks/served_options.rb +53 -0
  378. data/lib/hecks/site/bluebook/site.bluebook +1 -0
  379. data/lib/hecks/storehouse/audit.rb +1 -1
  380. data/lib/hecks/storehouse/dispatching.rb +8 -6
  381. data/lib/hecks/storehouse/introspection.rb +2 -2
  382. data/lib/hecks/storehouse/querying.rb +6 -4
  383. data/lib/hecks/storehouse.rb +4 -4
  384. data/lib/hecks/tools/ci_gate_decision.rb +35 -0
  385. data/lib/hecks/tools/ci_gates.rb +20 -1
  386. data/lib/hecks/tools/comment_style/source_file.rb +4 -1
  387. data/lib/hecks/tools/deploy_recipe_lint/fixtures.rb +4 -1
  388. data/lib/hecks/tools/lanes/files.rb +8 -0
  389. data/lib/hecks/tools/lanes/prebuilt_workflow.rb +81 -0
  390. data/lib/hecks/tools/lanes/rows.rb +6 -0
  391. data/lib/hecks/tools/lanes.rb +1 -0
  392. data/lib/hecks/tools/oidc_manifests.rb +4 -5
  393. data/lib/hecks/tools/regeneration_run.rb +11 -4
  394. data/lib/hecks/tools/release_only.rb +111 -0
  395. data/lib/hecks/tools/site_routes/editor_files.rb +12 -5
  396. data/lib/hecks/tools/tenant_provisioning.rb +2 -2
  397. data/lib/hecks/tools/tools_doc.rb +4 -4
  398. data/lib/hecks/version.rb +1 -1
  399. data/lib/hecks/vocabulary.rb +19 -5
  400. data/lib/hecks.rb +15 -15
  401. data/lib/rubocop/cop/hecks/env_dump.rb +69 -0
  402. data/lib/rubocop/cop/hecks/monotonic_duration.rb +53 -0
  403. data/lib/rubocop/cop/hecks/no_wall_clock_in_runtime.rb +34 -0
  404. data/lib/rubocop/cop/hecks/raw_shell_interpolation.rb +79 -0
  405. data/lib/rubocop/cop/hecks/sleep_in_spec.rb +34 -0
  406. data/lib/rubocop/cop/hecks/swallowed_rescue.rb +37 -0
  407. data/lib/rubocop/cop/hecks/symbol_string_key_mix.rb +50 -0
  408. data/lib/rubocop/cop/hecks/untyped_raise.rb +37 -0
  409. data/prebuilt/9ee54e2f0d4d6d3fe046827779b221e02de665fba911a048ae5dd8b8fbfb7d90.marshal +0 -0
  410. data/prebuilt/d74a00e996222c1d8b676d5959092effc46b4dc59e0555f3bf9554ac58e3094a.marshal +0 -0
  411. data/prebuilt/verdicts-df19f35868d3284a70f55e42dcef87ef4e65e6a678e5135f72e1f6cff4c1357b.json +1 -0
  412. data/rust/.gitignore +6 -0
  413. data/rust/Cargo.toml +6 -0
  414. data/rust/build/src/binding_pass.rs +103 -0
  415. data/rust/build/src/cargo_sync.rs +6 -0
  416. data/rust/build/src/lineage_pass.rs +3 -3
  417. data/rust/build/src/main.rs +17 -5
  418. data/rust/build/src/pipeline.rs +48 -5
  419. data/rust/build/src/seams_pass.rs +123 -0
  420. data/rust/build/src/translation_pass.rs +167 -0
  421. data/rust/codegen/src/asks.rs +103 -0
  422. data/rust/codegen/src/commands.rs +13 -8
  423. data/rust/codegen/src/constraints.rs +25 -0
  424. data/rust/codegen/src/domain_generator.rs +2 -0
  425. data/rust/codegen/src/expr_emitter.rs +13 -0
  426. data/rust/codegen/src/json_codec.rs +54 -15
  427. data/rust/codegen/src/main.rs +1 -0
  428. data/rust/codegen/src/mutations.rs +128 -3
  429. data/rust/codegen/src/queries.rs +11 -2
  430. data/rust/codegen/src/reactions.rs +1 -1
  431. data/rust/codegen/src/types.rs +166 -8
  432. data/rust/host/HECKS_RELEASE +1 -1
  433. data/rust/host/src/api.rs +4 -4
  434. data/rust/host/src/bin/validate_harness.rs +43 -0
  435. data/rust/host/src/boundary_fuzz/expr_json.rs +2 -2
  436. data/rust/host/src/boundary_fuzz/ir.rs +1 -0
  437. data/rust/host/src/boundary_fuzz/server.rs +1 -1
  438. data/rust/host/src/checkout.rs +35 -19
  439. data/rust/host/src/commerce_ir.rs +408 -2
  440. data/rust/host/src/expr_json.rs +118 -0
  441. data/rust/host/src/extension.rs +13 -1
  442. data/rust/host/src/lib.rs +1 -0
  443. data/rust/host/src/mint.rs +1 -1
  444. data/rust/host/src/payments/tests.rs +27 -1
  445. data/rust/host/src/payments.rs +12 -4
  446. data/rust/host/src/platform_signin/attack_tests.rs +1100 -0
  447. data/rust/host/src/platform_signin/config.rs +512 -0
  448. data/rust/host/src/platform_signin/cookie.rs +237 -0
  449. data/rust/host/src/platform_signin/extension.rs +190 -0
  450. data/rust/host/src/platform_signin/fetcher.rs +322 -0
  451. data/rust/host/src/platform_signin/handler.rs +251 -0
  452. data/rust/host/src/platform_signin/handler_tests.rs +554 -0
  453. data/rust/host/src/platform_signin/jti.rs +196 -0
  454. data/rust/host/src/platform_signin/jwks.rs +542 -0
  455. data/rust/host/src/platform_signin/jws.rs +249 -0
  456. data/rust/host/src/platform_signin/known_answer_tests.rs +237 -0
  457. data/rust/host/src/platform_signin/mapping_tests.rs +254 -0
  458. data/rust/host/src/platform_signin/mod.rs +75 -0
  459. data/rust/host/src/platform_signin/mode_tests.rs +382 -0
  460. data/rust/host/src/platform_signin/pages.rs +204 -0
  461. data/rust/host/src/platform_signin/query.rs +233 -0
  462. data/rust/host/src/platform_signin/session.rs +151 -0
  463. data/rust/host/src/platform_signin/settings.rs +385 -0
  464. data/rust/host/src/platform_signin/strict_json.rs +351 -0
  465. data/rust/host/src/platform_signin/test_support.rs +235 -0
  466. data/rust/host/src/platform_signin/ttl_tests.rs +94 -0
  467. data/rust/host/src/platform_signin/verify.rs +604 -0
  468. data/rust/host/src/platform_signin/wiring_support.rs +319 -0
  469. data/rust/host/src/query_step.rs +22 -4
  470. data/rust/host/src/reference_validate.rs +653 -108
  471. data/rust/host/src/resend.rs +2 -0
  472. data/rust/host/src/server.rs +19 -6
  473. data/rust/host/src/ui_schema.rs +132 -5
  474. data/rust/host/src/web/newsletter.rs +7 -8
  475. data/rust/host/src/web/newsletter_send.rs +38 -13
  476. data/rust/host/src/web/registrations.rs +229 -26
  477. data/rust/host/src/web.rs +53 -9
  478. data/rust/parser/src/emit.rs +38 -6
  479. data/rust/parser/src/expr/ast_json.rs +7 -1
  480. data/rust/parser/src/expr/resolver.rs +171 -6
  481. data/rust/parser/src/ir.rs +7 -0
  482. data/rust/parser/src/keywords.rs +24 -0
  483. data/rust/parser/src/main.rs +3 -0
  484. data/rust/parser/src/parse/aggregate.rs +2 -1
  485. data/rust/parser/src/parse/chapter.rs +2 -2
  486. data/rust/parser/src/parse/command.rs +2 -0
  487. data/rust/parser/src/parse/hecksagon.rs +5 -2
  488. data/rust/parser/src/parse/lifecycle.rs +81 -0
  489. data/rust/parser/src/parse/mod.rs +15 -1
  490. data/rust/parser/src/parse/policy.rs +13 -2
  491. data/rust/parser/src/parse/value_object.rs +34 -17
  492. data/rust/parser/src/ruby_value.rs +1 -1
  493. data/rust/src/exemplar/commands.rs +4 -4
  494. data/rust/src/exemplar/constraints.rs +1 -1
  495. data/rust/src/exemplar/json.rs +43 -1
  496. data/rust/src/kernel/cli.rs +7 -5
  497. data/rust/src/kernel/dispatch.rs +2 -2
  498. data/rust/src/kernel/dispatch_guard_tests.rs +1 -1
  499. data/rust/src/kernel/expr.rs +66 -3
  500. data/rust/src/kernel/expression_operators/arithmetic.rs +130 -5
  501. data/rust/src/kernel/expression_operators/text.rs +26 -2
  502. data/rust/src/kernel/json.rs +15 -0
  503. data/rust/src/kernel/mod.rs +1 -1
  504. data/rust/src/kernel/orchestrate.rs +54 -5
  505. data/rust/src/kernel/orchestrate_guard_tests.rs +67 -0
  506. data/rust/src/kernel/pattern.rs +60 -0
  507. data/rust/src/kernel/reference_lookup.rs +90 -7
  508. metadata +178 -38
  509. data/lib/hecks/doors/cli_door/nesting.rb +0 -34
  510. data/lib/hecks/doors/cli_door/shorthand.rb +0 -85
  511. data/lib/hecks/doors/cli_door.rb +0 -124
  512. data/lib/hecks/doors/cli_runner/answers.rb +0 -138
  513. data/lib/hecks/doors/cli_runner/resolution.rb +0 -146
  514. data/lib/hecks/doors/cli_runner/settling.rb +0 -115
  515. data/lib/hecks/doors/cli_runner/streaming.rb +0 -90
  516. data/lib/hecks/doors/cli_runner/suggestions.rb +0 -43
  517. data/lib/hecks/doors/cli_runner.rb +0 -66
  518. data/lib/hecks/doors/command_request.rb +0 -119
  519. data/lib/hecks/doors/handle.rb +0 -208
  520. data/lib/hecks/doors/json_door.rb +0 -132
  521. data/lib/hecks/doors/launcher_options.rb +0 -247
  522. data/lib/hecks/doors/mcp_door/properties.rb +0 -47
  523. data/lib/hecks/doors/mcp_door/tool_calls.rb +0 -113
  524. data/lib/hecks/doors/mcp_door/tool_definitions/auditing.rb +0 -51
  525. data/lib/hecks/doors/mcp_door/tool_definitions/calling.rb +0 -69
  526. data/lib/hecks/doors/mcp_door/tool_definitions/reading.rb +0 -97
  527. data/lib/hecks/doors/mcp_door/tool_definitions.rb +0 -15
  528. data/lib/hecks/doors/mcp_door.rb +0 -156
  529. data/lib/hecks/doors/mcp_door_scope/argument_policy.rb +0 -92
  530. data/lib/hecks/doors/mcp_door_scope/env_problems.rb +0 -55
  531. data/lib/hecks/doors/mcp_door_scope/presenter.rb +0 -78
  532. data/lib/hecks/doors/mcp_door_scope.rb +0 -201
  533. data/lib/hecks/doors/mcp_guide_cache.rb +0 -75
  534. data/lib/hecks/doors/ruby_door/aggregate_door.rb +0 -162
  535. data/lib/hecks/doors/ruby_door/chapter.rb +0 -89
  536. data/lib/hecks/doors/ruby_door.rb +0 -80
  537. data/lib/hecks/doors/usage_cache.rb +0 -139
  538. data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_widget.js.tmpl +0 -282
  539. data/lib/hecks/projections/site/cms_editor/templates/src/ui/media_picker.js.tmpl +0 -109
  540. data/prebuilt/c28d6d9db14eea3e17161e9d9c262efee30b256cf97693a7cfd552bce24b2f7f.marshal +0 -0
  541. data/prebuilt/cb375cba61ca0dbe8ddb9739c306510fe05e5f4b7a9b6955caff8224c63ea0e0.marshal +0 -0
  542. data/prebuilt/verdicts-d6d584ad43b5f3cebf88aafbbdbf024c38574acf18e06adffbc555f7786b1c2f.json +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5ab2b4b582c53edd23595354a8db9d84a3a8ec4572d044a77ccfea2ac113c073
4
- data.tar.gz: 80af1db86bd574227fc6a03e7d19b9fa1ede9d4fbf54c4934da9211c67a892a2
3
+ metadata.gz: beba8cd60d4c859286fa1121e3b382cc63e826e010dfd336714befa036dfad94
4
+ data.tar.gz: 16deb6ba2dd98c8d09385e77a9773f92ef12726d097e71601d2e3cfdbb83ae00
5
5
  SHA512:
6
- metadata.gz: d28b07fb0b22a96aa8a2dbe2481010c668555a18f256572ee67dc95afb5c517d833cc749718ca804cac68a1ce34e5268e755347ad2d32668ec264d8519d81b08
7
- data.tar.gz: 6e823533f842634ad3771ccb994feda57439c9f027aa189e0ff36ba049024fe3c845fd96e3543ce720cb96d6bcaf159604a1279db640f4137ac1e72677d436da
6
+ metadata.gz: fe730368d2f8942c67a5510c5b8344bdad86f9aa0990515903e147da4fe71fbc6f17880ea2a1b26de4ffa0d24ad20c416455df58ee8a66617e1f832b2637e2b0
7
+ data.tar.gz: a41080c78fc7b1e9d43347fd4b965d6811c246baee7072214c5f7895564eccc94ce699b86bc739de0f3d7d7830fb8057e6bf2e0699496e201ffd057c7e6a39d5
@@ -35,6 +35,8 @@ Hecks.bluebook "Pizzas" do
35
35
  attribute :value, String, pattern: '[^ \t\n\r]'
36
36
 
37
37
  invariant("a topping is named") { !value.to_s.empty? }
38
+ # The pattern lets a form feed through; `.strip` is what sees past it.
39
+ invariant("a topping name is more than whitespace") { !value.to_s.strip.empty? }
38
40
  end
39
41
 
40
42
  value_object "ToppingAmount" do
@@ -69,6 +71,33 @@ Hecks.bluebook "Pizzas" do
69
71
  order_by :name
70
72
  end
71
73
 
74
+ # No `where`: every order answers, by identity when nothing orders them.
75
+ query "Everything" do
76
+ end
77
+
78
+ # Where-less and ordered. Equal prices keep identity order; the nested path reaches the cents.
79
+ query "ByPrice" do
80
+ order_by :"pizza.price_cents.cents"
81
+ end
82
+
83
+ query "ByPriceDescending" do
84
+ order_by :"pizza.price_cents.cents", :desc
85
+ end
86
+
87
+ # `customer_name` is unset until a purchase, so these show where an unset value sorts.
88
+ query "ByCustomer" do
89
+ order_by :"customer_name.value"
90
+ end
91
+
92
+ query "ByCustomerDescending" do
93
+ order_by :"customer_name.value", :desc
94
+ end
95
+
96
+ query "ByCustomerUnsetLast" do
97
+ order_by :"customer_name.value"
98
+ nulls :last
99
+ end
100
+
72
101
  # Both price queries reach through the nested Pizza value object with a dotted path.
73
102
  query "CostingLessThan" do
74
103
  attribute :ceiling, Price
@@ -4,6 +4,13 @@ Hecks.hecksagon "Pizzas" do
4
4
  attaches "Governance"
5
5
  Pizzas::Order.persisted_by("Memory")
6
6
 
7
+ # Every way in the example is named: the console (Ruby), `hecks run` (Cli), the forms and
8
+ # storehouse reader (Json) and `hecks mcp` (Mcp). An adapter left out would refuse the domain.
9
+ driven_by "Ruby"
10
+ driven_by "Cli"
11
+ driven_by "Json"
12
+ driven_by "Mcp"
13
+
7
14
  # The primary port: an outside adapter (a payment webhook) translates an external fact into
8
15
  # our vocabulary; business rules stay on Purchase, reached via OnPizzaPaymentReceived.
9
16
  Pizzas::Order.port "PaymentGateway" do
data/exe/hecks CHANGED
@@ -41,19 +41,19 @@ rescue StandardError => e
41
41
  abort "cannot open Hecks: #{e.message.lines.first.strip}"
42
42
  end
43
43
 
44
- text, status, reason = Hecks::Doors::CliRunner.usage(
44
+ text, status, reason = Hecks::Adapters::Driving::CliRunner.usage(
45
45
  runtime: described, argv: ARGV, program: program
46
46
  )
47
47
  unless text
48
48
  runtime = begin
49
- Hecks::Doors::LauncherOptions.quietly(hold: MEMORY_COMMANDS.include?(ARGV.first.to_s.chomp("!"))) { Hecks.boot_described(described, install_doors: false) }
49
+ Hecks::Adapters::Driving::LauncherOptions.quietly(hold: MEMORY_COMMANDS.include?(ARGV.first.to_s.chomp("!"))) { Hecks.boot_described(described, install_driving: false) }
50
50
  rescue StandardError => e
51
51
  abort "cannot open Hecks: #{e.message.lines.first.strip}"
52
52
  end
53
53
  # A question the world lists under `streams`, given `--stream`, tails until interrupted.
54
- streamed = Hecks::Doors::CliRunner.stream(runtime: runtime, argv: ARGV, program: program)
54
+ streamed = Hecks::Adapters::Driving::CliRunner.stream(runtime: runtime, argv: ARGV, program: program)
55
55
  exit streamed unless streamed.nil?
56
- text, status, reason = Hecks::Doors::CliRunner.call(
56
+ text, status, reason = Hecks::Adapters::Driving::CliRunner.call(
57
57
  runtime: runtime, argv: ARGV, program: program
58
58
  )
59
59
  end
@@ -42,7 +42,7 @@ module Hecks
42
42
  @aggregate = aggregate
43
43
  @db = connect(settings)
44
44
  # Saga rows are scoped by domain; one D1 database per domain, so aggregates share it.
45
- @domain = setting(settings, :domain, aggregate.name).to_s
45
+ @domain = settings.fetch(:domain) { aggregate.name }.to_s
46
46
 
47
47
  # No PRAGMA synchronous: D1 is managed and its query endpoint disallows PRAGMA writes.
48
48
  create_aggregate_table!
@@ -100,7 +100,7 @@ module Hecks
100
100
 
101
101
  # The settings are read under a Symbol or a String key; each credential is required.
102
102
  def connect(settings)
103
- credentials = %w[account_id database_id api_token].to_h { |name| [name, setting(settings, name.to_sym, nil)] }
103
+ credentials = %w[account_id database_id api_token].to_h { |name| [name, settings[name.to_sym]] }
104
104
  credentials.each do |name, value|
105
105
  raise Runtime::WiringError, "D1 needs a #{name.inspect} in its world settings" if value.to_s.empty?
106
106
  end
@@ -109,14 +109,6 @@ module Hecks
109
109
  api_token: credentials["api_token"])
110
110
  end
111
111
 
112
- # A setting read under its Symbol key, then its String key, then `default`.
113
- def setting(settings, key, default)
114
- return settings[key] if settings.key?(key)
115
- return settings[key.to_s] if settings.key?(key.to_s)
116
-
117
- default
118
- end
119
-
120
112
  # The batch's first statement: what the put will do, decided against the current row.
121
113
  def status_statement(entry, insert_only)
122
114
  outcome = insert_only ? "conflicted" : "replaced"
@@ -1,4 +1,4 @@
1
1
  Hecks.adapter "Heki" do
2
2
  port "persistence"
3
- field :dir
3
+ field :dir, default: "data"
4
4
  end
@@ -39,7 +39,7 @@ module Hecks
39
39
  @journal_path = "#{@path}.journal"
40
40
  @events = []
41
41
  # Saga scope; falls back to the aggregate's name for a directly built adapter.
42
- @domain = setting(settings, :domain, aggregate.name).to_s
42
+ @domain = settings.fetch(:domain) { aggregate.name }.to_s
43
43
  FileUtils.mkdir_p(File.dirname(@path))
44
44
  end
45
45
 
@@ -179,22 +179,12 @@ module Hecks
179
179
  replay_journal(snapshot)
180
180
  end
181
181
 
182
- # `dir: :default` means no setting; a Symbol would make `File.join` raise TypeError.
183
182
  def resolve_path(settings, root)
184
- declared = setting(settings, :dir, "data")
185
- declared = "data" if declared == :default
183
+ declared = settings[:dir]
186
184
  dir = declared.start_with?("/") ? declared : File.join(root || Dir.pwd, declared)
187
185
 
188
186
  File.join(dir, "#{@aggregate.storage_name}.heki")
189
187
  end
190
-
191
- # A setting read under its Symbol key, then its String key, then `default`.
192
- def setting(settings, key, default)
193
- return settings[key] if settings.key?(key)
194
- return settings[key.to_s] if settings.key?(key.to_s)
195
-
196
- default
197
- end
198
188
  end
199
189
  end
200
190
  end
@@ -1,4 +1,4 @@
1
1
  Hecks.adapter "Lambda" do
2
2
  port "persistence"
3
- field :region
3
+ field :region, default: "us-east-1"
4
4
  end
@@ -25,10 +25,10 @@ module Hecks
25
25
  # to resolve which function to call; nil falls further back to `domain`
26
26
  def initialize(aggregate:, settings: {}, root: nil)
27
27
  @aggregate = aggregate
28
- domain = setting(settings, :domain, aggregate.name)
29
- region = setting(settings, :region, "us-east-1")
28
+ domain = settings.fetch(:domain) { aggregate.name }
29
+ region = settings[:region]
30
30
  # Explicit function name, for deployments whose function is not `hecks-<domain>`.
31
- function = setting(settings, :function, nil)
31
+ function = settings[:function]
32
32
  # `domain` only prefixes the instances lookup; the function to call is named by
33
33
  # DOMAIN_NAME (set in a deployed Lambda, where root is always /var/task), else by
34
34
  # root's basename, which matches hecks deploy recipe.project's stack name on a local boot.
@@ -75,21 +75,6 @@ module Hecks
75
75
  Ports::Query::InMemory.execute(instances.values, specification, args)
76
76
  end
77
77
 
78
- # Reads a world setting under either a Symbol or a String key, with a fallback.
79
- #
80
- # Settings arrive symbol-keyed from the DSL and string-keyed from a round-tripped export.
81
- #
82
- # @param settings [Hash] the world settings Hash to read from
83
- # @param key [Symbol] the setting name, tried as itself and as `key.to_s`
84
- # @param fallback [Object] the value to return when neither key is present
85
- # @return [Object] the setting's value, or `fallback` when absent
86
- def setting(settings, key, fallback)
87
- return settings[key] if settings.key?(key)
88
- return settings[key.to_s] if settings.key?(key.to_s)
89
-
90
- fallback
91
- end
92
-
93
78
  private
94
79
 
95
80
  # Never memoized: the adapter outlives requests on a warm container, so a cached read
@@ -18,6 +18,7 @@ require_relative "postgres/outbox"
18
18
  require_relative "postgres/reconnect"
19
19
  require_relative "postgres/shared_connection"
20
20
  require_relative "../../runtime/instance"
21
+ require_relative "../../indifferent_key"
21
22
 
22
23
  module Hecks
23
24
  module Adapters
@@ -57,7 +58,7 @@ module Hecks
57
58
  # @raise [LoadError] if the `pg` gem is not installed
58
59
  def self.connect_for(name, settings)
59
60
  require_pg(name)
60
- declared = settings.key?(:database) ? settings[:database] : settings["database"]
61
+ declared = IndifferentKey.read(settings, :database)
61
62
  refuse_undeclared(name, declared)
62
63
  configure(open_connection(declared), settings)
63
64
  rescue StandardError => e
@@ -95,7 +96,7 @@ module Hecks
95
96
  # PostgresEra's own: a schema/table that already exists is the ordinary case on every
96
97
  # boot after the first, not news.
97
98
  def self.configure(connection, settings)
98
- schema = settings.key?(:schema) ? settings[:schema] : settings["schema"]
99
+ schema = IndifferentKey.read(settings, :schema)
99
100
  connection.exec("SET search_path TO #{connection.quote_ident(schema)}") if schema.to_s != ""
100
101
  connection.exec("SET client_min_messages = warning")
101
102
  connection
@@ -120,7 +121,7 @@ module Hecks
120
121
  @db = PostgresSharedConnection.for(aggregate.name, settings, connector: self.class)
121
122
  # Scopes saga rows; falls back to the aggregate's storage name when
122
123
  # settings gives no domain (e.g. a directly-instantiated adapter).
123
- @domain = setting(settings, :domain, aggregate.storage_name).to_s
124
+ @domain = settings.fetch(:domain) { aggregate.storage_name }.to_s
124
125
  create_tables!
125
126
  end
126
127
 
@@ -139,14 +140,6 @@ module Hecks
139
140
  create_outbox_table!
140
141
  create_checkpoint_table!
141
142
  end
142
-
143
- # A setting read under its Symbol key, then its String key, then `default`.
144
- def setting(settings, key, default)
145
- return settings[key] if settings.key?(key)
146
- return settings[key.to_s] if settings.key?(key.to_s)
147
-
148
- default
149
- end
150
143
  end
151
144
  end
152
145
  end
@@ -1,6 +1,6 @@
1
1
  Hecks.adapter "SqlitePersistence" do
2
2
  port "persistence"
3
- field :database
3
+ field :database, default: "data/{storage_name}.db"
4
4
  end
5
5
 
6
6
  Hecks.adapter "SqliteProjection" do
@@ -60,7 +60,7 @@ module Hecks
60
60
  @aggregate = aggregate
61
61
  @path = resolve_path(settings, root)
62
62
  # Scopes saga rows; falls back to the aggregate's name for a directly built adapter.
63
- @domain = setting(settings, :domain, aggregate.name).to_s
63
+ @domain = settings.fetch(:domain) { aggregate.name }.to_s
64
64
  open_database
65
65
  create_tables!
66
66
  end
@@ -106,16 +106,8 @@ module Hecks
106
106
  create_checkpoint_table!
107
107
  end
108
108
 
109
- # A setting read under its Symbol key, then its String key, then `default`.
110
- def setting(settings, key, default)
111
- return settings[key] if settings.key?(key)
112
- return settings[key.to_s] if settings.key?(key.to_s)
113
-
114
- default
115
- end
116
-
117
109
  def resolve_path(settings, root)
118
- declared = setting(settings, :database, "data/#{table}.db")
110
+ declared = settings[:database]
119
111
  return declared if declared.start_with?("/")
120
112
 
121
113
  File.join(root || Dir.pwd, declared)
@@ -0,0 +1,46 @@
1
+ require_relative "../../bluebook/hecksagon"
2
+
3
+ module Hecks
4
+ module Adapters
5
+ module Driving
6
+ # Which driving adapters a hecksagon may name with `driven_by`, and the refusal an adapter
7
+ # gets from a domain that left it out.
8
+ #
9
+ # A domain that declares no `driven_by` is open to every adapter. One that declares any is
10
+ # reached through those, and each adapter it omits refuses the domain.
11
+ module Admission
12
+ # The driving adapters a hecksagon can admit.
13
+ ADAPTERS = Bluebook::Hecksagon::DRIVING_ADAPTERS
14
+
15
+ # Raised by an adapter that a domain's hecksagon left out of `driven_by`.
16
+ class Refused < StandardError; end
17
+
18
+ module_function
19
+
20
+ # The sentence a domain gives an adapter it did not admit.
21
+ #
22
+ # @param registry [Runtime::Registry] the booted registry, whose hecksagons are asked
23
+ # @param adapter [String] the adapter reaching in, one of `ADAPTERS`
24
+ # @return [String, nil] the refusal, or nil when every domain admits `adapter`
25
+ def refusal(registry, adapter)
26
+ omitting = registry.hecksagons.values.find { |hecksagon| !hecksagon.driven_by?(adapter) }
27
+ return unless omitting
28
+
29
+ "#{omitting.domain} is not driven by #{adapter}: its hecksagon admits only " \
30
+ "#{omitting.driving.join(", ")} (driven_by)"
31
+ end
32
+
33
+ # Raises when a domain in `registry` did not admit `adapter`.
34
+ #
35
+ # @param registry [Runtime::Registry] the booted registry
36
+ # @param adapter [String] the adapter reaching in, one of `ADAPTERS`
37
+ # @return [void]
38
+ # @raise [Refused] with the domain's sentence
39
+ def admit!(registry, adapter)
40
+ sentence = refusal(registry, adapter)
41
+ raise Refused, sentence if sentence
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,36 @@
1
+ module Hecks
2
+ module Adapters
3
+ module Driving
4
+ module Cli
5
+ # Sets values into the nested argument Hash a dotted path names, creating the intermediate
6
+ # Hashes on the way.
7
+ module Nesting
8
+ module_function
9
+
10
+ # Appends one element to a list argument, creating the list on first use.
11
+ #
12
+ # Repeats must grow the list (`bury` would silently keep only the last), and a single
13
+ # element is still a one-item list, since a `list_of` attribute must never see a bare
14
+ # object. Multi-field elements are left to `Json`: a flat command line cannot say
15
+ # which `a.x=` pairs with which `a.y=`.
16
+ def append(hash, path, value)
17
+ *branches, leaf = path.map(&:to_sym)
18
+ holder = branches[0..-2].reduce(hash) { |node, key| node[key] ||= {} }
19
+ list = holder[branches.last] ||= []
20
+
21
+ list << { leaf => value }
22
+ hash
23
+ end
24
+
25
+ # Sets one value at a nested path, creating intermediate Hashes and overwriting the leaf.
26
+ def bury(hash, path, value)
27
+ *branches, leaf = path.map(&:to_sym)
28
+ target = branches.reduce(hash) { |node, key| node[key] ||= {} }
29
+ target[leaf] = value
30
+ hash
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,88 @@
1
+ module Hecks
2
+ module Adapters
3
+ module Driving
4
+ module Cli
5
+ # Rewrites the shorthand spellings of a command line into plain `name=value` words.
6
+ module Shorthand
7
+ module_function
8
+
9
+ # Rewrites the short forms into `name=value`: `--name` for a Boolean (a following `yes`,
10
+ # `no`, `true`, `false`, `on`, `off`, `1` or `0` is that flag's value, not an argument),
11
+ # `--name=value` as `name=value`, and one bare word as the command's first argument
12
+ # (`to` for a command on an existing aggregate, the first attribute for one that creates).
13
+ def normalize(spec, words, options)
14
+ pairs, bare = collect(words, options)
15
+ raise Runtime::NotFound, too_many_bare(bare) if bare.length > 1
16
+
17
+ pairs.map { |pair| bare.include?(pair) && !pair.include?("=") ? positional(pair, spec) : pair }
18
+ end
19
+
20
+ # Every word as a pair, and the words that were neither a flag nor a pair.
21
+ def collect(words, options)
22
+ queue = words.dup
23
+ pairs = []
24
+ bare = []
25
+ until queue.empty?
26
+ word = queue.shift
27
+ pairs << pair_for(word, queue, options)
28
+ bare << word if bare_word?(word)
29
+ end
30
+ [pairs, bare]
31
+ end
32
+
33
+ # A word that is neither a `--flag` nor a `name=value` pair.
34
+ def bare_word?(word)
35
+ !word.start_with?("--") && !word.include?("=")
36
+ end
37
+
38
+ # One word as a `name=value` pair; a `--flag` takes the Boolean word that follows it from
39
+ # `queue`.
40
+ def pair_for(word, queue, options)
41
+ return word unless word.start_with?("--")
42
+ return underscored(word.delete_prefix("--"), options) if word.include?("=")
43
+
44
+ path = flag(word.delete_prefix("--"), options)
45
+ value = BOOLEAN_WORDS.key?(queue.first.to_s.downcase) ? queue.shift : "true"
46
+ "#{path}=#{value}"
47
+ end
48
+
49
+ # A `name=value` pair whose name is spelled with dashes (`seed-start=3`), as the argument
50
+ # spelled with underscores, unless an argument is named with the dashes.
51
+ def underscored(pair, options)
52
+ name, value = pair.split("=", 2)
53
+ options.key?(name) ? pair : "#{name.tr("-", "_")}=#{value}"
54
+ end
55
+
56
+ # A `--name` flag, as the path of the Boolean argument it stands for. A dashed name
57
+ # (`--gem-only`) is the argument spelled with underscores (`gem_only`).
58
+ def flag(name, options)
59
+ name = name.tr("-", "_") unless options.key?(name) || options.keys.any? { |key| key.start_with?("#{name}.") }
60
+ path = options.key?(name) ? name : Cli.expand(name, options)
61
+ return path if options.dig(path, :type) == "Boolean"
62
+
63
+ raise Runtime::NotFound, "--#{name} is a flag, but this command has no Boolean argument #{name.inspect}"
64
+ end
65
+
66
+ # The one bare word, as a pair for the command's first argument the launcher does not
67
+ # mint.
68
+ def positional(word, spec)
69
+ first = spec[:arguments].find { |argument| !argument[:minted] }
70
+ return "#{first[:path]}=#{word}" if first
71
+
72
+ if spec[:arguments].any?
73
+ raise Runtime::NotFound, "#{word.inspect} is not name=value; this command's only argument is its run " \
74
+ "key, which is minted when omitted (run=<key> names one)"
75
+ end
76
+
77
+ raise Runtime::NotFound, "#{word.inspect} is not name=value, and this command takes no arguments"
78
+ end
79
+
80
+ # Words the refusal for more than one unnamed argument.
81
+ def too_many_bare(bare)
82
+ "only one argument may go unnamed, not #{bare.map(&:inspect).join(", ")}; name the rest as name=value"
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,127 @@
1
+ require_relative "../../runtime/errors"
2
+
3
+ module Hecks
4
+ module Adapters
5
+ module Driving
6
+ # Turns `path=value` command-line words into a nested, typed argument Hash.
7
+ # Types come from the projection, never from guessing at the value ("99" may be a String).
8
+ module Cli
9
+ # The words a Boolean is spelled with, and what each means.
10
+ BOOLEAN_WORDS = { "true" => true, "yes" => true, "1" => true, "on" => true,
11
+ "false" => false, "no" => false, "0" => false, "off" => false }.freeze
12
+
13
+ module_function
14
+
15
+ # Builds a command's nested, typed argument Hash from its `name=value` words.
16
+ #
17
+ # arguments(spec, ["reference.value=A-1", "sequence.value=99"])
18
+ # # => { reference: { value: "A-1" }, sequence: { value: 99 } }
19
+ #
20
+ # @param spec [Hash{Symbol => Object}] one command's entry from `Projector::CliProjector`
21
+ # @param pairs [Array<String>] the words after the command: `name=value` pairs (a path may
22
+ # be the short form of a single-field value object), `--name` for a Boolean, and at most
23
+ # one bare word, which fills the first argument
24
+ # @return [Hash{Symbol => Object}] the arguments nested by path, leaves cast to type
25
+ # @raise [Runtime::NotFound] if a path, flag or bare word does not fit the command
26
+ # @raise [Runtime::TypeMismatch] if a value does not parse as its Integer or Float
27
+ def arguments(spec, pairs)
28
+ options = declared_options(spec)
29
+ Shorthand.normalize(spec, pairs, options).each_with_object({}) { |pair, args| store(args, pair, options) }
30
+ end
31
+
32
+ # Every argument the command accepts, by path. Extra accepted arguments stay out of help,
33
+ # which teaches only to=....
34
+ def declared_options(spec)
35
+ (spec[:arguments] + Array(spec[:legacy_arguments])).to_h { |argument| [argument[:path], argument] }
36
+ end
37
+
38
+ # Casts the value of one `name=value` pair and sets it into `args` at its path.
39
+ def store(args, pair, options)
40
+ path, value = split(pair)
41
+ full, argument = declared_argument(path, options)
42
+ keys = full.split(".")
43
+ return words(args, keys, value, argument[:type]) if argument[:words]
44
+ return Nesting.append(args, keys, cast(value, argument[:type])) if argument[:list]
45
+
46
+ Nesting.bury(args, keys, cast(value, argument[:type]))
47
+ end
48
+
49
+ # The full path and declaration of the argument `path` names, expanding a value object's
50
+ # short form. `key?` rather than `||`, so the lookup honors the same spelling `full` chose.
51
+ def declared_argument(path, options)
52
+ full = options.key?(path) ? path : expand(path, options)
53
+ return [full, options[full]] if options.key?(full)
54
+
55
+ raise Runtime::NotFound, unknown(path, options.keys)
56
+ end
57
+
58
+ # Cuts one word at its first `=`, so a value may itself contain `=`.
59
+ def split(pair)
60
+ name, value = pair.split("=", 2)
61
+ raise Runtime::NotFound, "#{pair.inspect} is not name=value" if value.nil?
62
+
63
+ [name, value]
64
+ end
65
+
66
+ # Expands a bare value-object name into the path of its only field.
67
+ # Left unchanged when several fields match, since guessing which was meant is worse.
68
+ def expand(path, options)
69
+ candidates = options.keys.select { |key| key.start_with?("#{path}.") }
70
+ candidates.length == 1 ? candidates.first : path
71
+ end
72
+
73
+ # Converts one value to its declared type; only Integer, Float and Boolean convert.
74
+ def cast(value, type)
75
+ case type
76
+ when "Integer" then Integer(value)
77
+ when "Float" then Float(value)
78
+ when "Boolean" then boolean(value)
79
+ else value
80
+ end
81
+ rescue ArgumentError
82
+ raise Runtime::TypeMismatch, "#{value.inspect} is not #{type} — the chapter declares this field as #{type}"
83
+ end
84
+
85
+ # Reads a Boolean word: `true`, `yes`, `1` or `on` is true; `false`, `no`, `0` or `off` is
86
+ # false; case does not matter. Anything else is refused, since guessing would turn a typo
87
+ # (`dry_run=ture`) into a silent false.
88
+ #
89
+ # @param value [String] the word
90
+ # @return [Boolean]
91
+ # @raise [Runtime::TypeMismatch] if the word is not one of those eight
92
+ def boolean(value)
93
+ BOOLEAN_WORDS.fetch(value.to_s.downcase) do
94
+ raise Runtime::TypeMismatch,
95
+ "#{value.inspect} is not Boolean — use true, false, yes, no, 1, 0, on or off"
96
+ end
97
+ end
98
+
99
+ # Adds the words of a `list_of` scalar argument to its Array: a comma-separated value is
100
+ # several elements and a repeated name adds more, each cast to the element type. The
101
+ # runtime refuses a lone scalar for a list, so the adapter is where every spelling becomes
102
+ # an
103
+ # Array.
104
+ #
105
+ # @param hash [Hash{Symbol => Object}] the arguments built so far
106
+ # @param path [Array<String>] the argument's dotted path
107
+ # @param value [String] the word after `=`
108
+ # @param type [String] the element type
109
+ # @return [Hash{Symbol => Object}] `hash`
110
+ def words(hash, path, value, type)
111
+ *branches, leaf = path.map(&:to_sym)
112
+ holder = branches.reduce(hash) { |node, key| node[key] ||= {} }
113
+ list = holder[leaf] ||= []
114
+ list.concat(value.split(",").map { |word| cast(word.strip, type) })
115
+ hash
116
+ end
117
+
118
+ # Words the refusal for an argument the command does not take, listing what it does.
119
+ def unknown(path, known)
120
+ "no argument #{path.inspect} — this command takes #{known.sort.join(", ")}"
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ require_relative "cli/nesting"
127
+ require_relative "cli/shorthand"