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,20 @@
1
+ module Hecks
2
+ # Reads a hash by key whichever way its keys are spelled, so the guess about Symbol or String
3
+ # keys is made in one place and `Hecks/SymbolStringKeyMix` can refuse it everywhere else.
4
+ #
5
+ # IndifferentKey.read({ "era" => "a1" }, :era) # => "a1"
6
+ module IndifferentKey
7
+ module_function
8
+
9
+ # Reads `key` as given when present, else by its other spelling. Presence decides, not
10
+ # `||`, so a held `false` is not mistaken for an absent key.
11
+ #
12
+ # @param hash [Hash] a hash keyed by Symbols or by Strings
13
+ # @param key [Symbol, String] the key to read; its own spelling is tried first
14
+ # @return [Object, nil] the held value, or nil when neither spelling is present
15
+ def read(hash, key)
16
+ other = key.is_a?(Symbol) ? key.to_s : key.to_sym
17
+ hash.key?(key) ? hash[key] : hash[other]
18
+ end
19
+ end
20
+ end
@@ -22,10 +22,18 @@ Hecks.bluebook "Adapter" do
22
22
  end
23
23
 
24
24
  value_object "AdapterField" do
25
- attribute :value, String
25
+ attribute :value, String
26
+ attribute :default, String, optional: true
26
27
  invariant("a field names itself") { !value.to_s.empty? }
27
28
  end
28
29
 
30
+ # The text of a field's declared default, such as `"data"` or `true`. A `{name}` in a string
31
+ # is filled from the aggregate the adapter is built for, so it names only what the framework
32
+ # can supply.
33
+ value_object "AdapterFieldDefault" do
34
+ attribute :value, String, pattern: '^(?:[^{}]|\{(?:name|storage_name)\})*$'
35
+ end
36
+
29
37
  command "Declare" do
30
38
  role "Deployment"
31
39
  goal "Register a real implementation of one resource port"
@@ -45,9 +53,10 @@ Hecks.bluebook "Adapter" do
45
53
  goal "Name a value this adapter's own world-level wiring must supply"
46
54
 
47
55
  reference_to Adapter
48
- attribute :value, AdapterField
56
+ attribute :value, AdapterField
57
+ attribute :default, AdapterFieldDefault, optional: true
49
58
 
50
- sets :fields, append: { value: :value }
59
+ sets :fields, append: { value: :value, default: :default }
51
60
 
52
61
  emits "AdapterFieldAdded"
53
62
  end
@@ -104,6 +113,7 @@ Hecks.bluebook "Adapter" do
104
113
  member keyword: "adapter", context: "File", at: "1", named: "", kind: "text", required: "true", fills: "name"
105
114
  member keyword: "port", context: "Adapter", at: "1", named: "", kind: "text", required: "true", fills: "port"
106
115
  member keyword: "field", context: "Adapter", at: "1", named: "", kind: "symbol", required: "true", fills: "fields"
116
+ member keyword: "field", context: "Adapter", at: "", named: "default", kind: "literal", required: "false", fills: "default"
107
117
  member keyword: "secret", context: "Adapter", at: "1", named: "", kind: "symbol", required: "true", fills: "secrets"
108
118
 
109
119
  end
@@ -18,6 +18,9 @@ Hecks.bluebook "Bluebook" do
18
18
  attribute :state_field, IdentityField, optional: true
19
19
  attribute :state_start, IdentityField, optional: true
20
20
  attribute :transitions, list_of(Transition)
21
+ # Named meanings the lifecycle's states carry (`mark :holds_seat, "pending", "paid"`), one
22
+ # row per (mark, state), in declared order.
23
+ attribute :marks, list_of(Mark)
21
24
  # Checked after every command, before save (ADR 0025). The same Rule shape as Command's
22
25
  # givens, declared again because a closed set or value object is aggregate-scoped here.
23
26
  attribute :invariants, list_of(Rule)
@@ -74,6 +77,11 @@ Hecks.bluebook "Bluebook" do
74
77
  attribute :to_state, String
75
78
  end
76
79
 
80
+ value_object "Mark" do
81
+ attribute :name, String
82
+ attribute :state, String
83
+ end
84
+
77
85
  # The same shape as Command's "Rule" (behavior.bluebook), declared again rather than shared.
78
86
  value_object "Rule" do
79
87
  attribute :description, String
@@ -255,6 +263,18 @@ Hecks.bluebook "Bluebook" do
255
263
  emits TransitionAttached
256
264
  end
257
265
 
266
+ command "Mark" do
267
+ role "Language"
268
+ goal "Attach one state to a named meaning of the state machine"
269
+
270
+ attribute :name, FieldName
271
+ attribute :state, FieldName
272
+
273
+ sets :marks, append: { name: :name, state: :state }
274
+
275
+ emits MarkAttached
276
+ end
277
+
258
278
  command "Seal" do
259
279
  role "Language"
260
280
  goal "Close an aggregate — the checks that need every declaration present"
@@ -366,6 +386,7 @@ Hecks.bluebook "Bluebook" do
366
386
  member word: "given", context: "Aggregate", body: "source", inner: "", opens: "", fills: "preconditions", resolves_via: "owner_keyed", disambiguator: "declared_by", calls: "given_impl"
367
387
  member word: "projects", context: "Aggregate", body: "none", inner: "", opens: "", fills: "projected_fields", calls: "projects_impl"
368
388
  member word: "transition", context: "Lifecycle", body: "none", inner: "", opens: "", fills: "transitions", calls: "transition_impl"
389
+ member word: "mark", context: "Lifecycle", body: "none", inner: "", opens: "", fills: "marks", calls: "mark_impl"
369
390
 
370
391
  end
371
392
 
@@ -429,6 +450,8 @@ Hecks.bluebook "Bluebook" do
429
450
  member keyword: "transition", context: "Lifecycle", at: "1", named: "", kind: "pairs", required: "true", fills: "", pair_key_fills: "command", pair_value_fills: "to_state", pairs_shape: "fields"
430
451
  member keyword: "transition", context: "Lifecycle", at: "", named: "from", kind: "text", required: "false", fills: "from_state"
431
452
  member keyword: "transition", context: "Lifecycle", at: "", named: "from", kind: "list", required: "false", fills: "from_state"
453
+ member keyword: "mark", context: "Lifecycle", at: "1", named: "", kind: "symbol", required: "true", fills: "name"
454
+ member keyword: "mark", context: "Lifecycle", at: "2", named: "", kind: "text", required: "true", fills: "state", variadic: "true"
432
455
 
433
456
  end
434
457
  end
@@ -243,13 +243,24 @@ Hecks.bluebook "Bluebook", version: "1" do
243
243
  member keyword: "provides", context: "Bluebook", at: "", named: "add_name", kind: "text", required: "false", fills: "provides"
244
244
  member keyword: "provides", context: "Bluebook", at: "", named: "confirm", kind: "text", required: "false", fills: "provides"
245
245
  member keyword: "provides", context: "Bluebook", at: "", named: "unsubscribe", kind: "text", required: "false", fills: "provides"
246
+ member keyword: "provides", context: "Bluebook", at: "", named: "awaiting_confirmation", kind: "text", required: "false", fills: "provides"
247
+ member keyword: "provides", context: "Bluebook", at: "", named: "receives_issues", kind: "text", required: "false", fills: "provides"
248
+ member keyword: "provides", context: "Bluebook", at: "", named: "left", kind: "text", required: "false", fills: "provides"
249
+ member keyword: "provides", context: "Bluebook", at: "", named: "confirm_window", kind: "text", required: "false", fills: "provides"
250
+ member keyword: "provides", context: "Bluebook", at: "", named: "unsubscribe_window", kind: "text", required: "false", fills: "provides"
251
+ member keyword: "provides", context: "Bluebook", at: "", named: "webhook_tolerance", kind: "text", required: "false", fills: "provides"
252
+ member keyword: "provides", context: "Bluebook", at: "", named: "session_hold", kind: "text", required: "false", fills: "provides"
246
253
  member keyword: "provides", context: "Bluebook", at: "", named: "initiate", kind: "text", required: "false", fills: "provides"
247
254
  member keyword: "provides", context: "Bluebook", at: "", named: "succeeded", kind: "text", required: "false", fills: "provides"
248
255
  member keyword: "provides", context: "Bluebook", at: "", named: "failed", kind: "text", required: "false", fills: "provides"
256
+ member keyword: "provides", context: "Bluebook", at: "", named: "lapse_reason", kind: "text", required: "false", fills: "provides"
257
+ member keyword: "provides", context: "Bluebook", at: "", named: "holds_seat", kind: "text", required: "false", fills: "provides"
249
258
  member keyword: "provides", context: "Bluebook", at: "", named: "send_issue", kind: "text", required: "false", fills: "provides"
250
259
  member keyword: "provides", context: "Bluebook", at: "", named: "record_delivery", kind: "text", required: "false", fills: "provides"
251
260
  member keyword: "provides", context: "Bluebook", at: "", named: "schedule", kind: "text", required: "false", fills: "provides"
252
261
  member keyword: "provides", context: "Bluebook", at: "", named: "request", kind: "text", required: "false", fills: "provides"
262
+ member keyword: "provides", context: "Bluebook", at: "", named: "registered_at", kind: "text", required: "false", fills: "provides"
263
+ member keyword: "provides", context: "Bluebook", at: "", named: "frees_seat" , kind: "text", required: "false", fills: "provides"
253
264
  member keyword: "provides", context: "Bluebook", at: "", named: "connect", kind: "text", required: "false", fills: "provides"
254
265
  member keyword: "provides", context: "Bluebook", at: "", named: "reconnect", kind: "text", required: "false", fills: "provides"
255
266
  member keyword: "provides", context: "Bluebook", at: "", named: "disconnect", kind: "text", required: "false", fills: "provides"
@@ -257,6 +268,10 @@ Hecks.bluebook "Bluebook", version: "1" do
257
268
  member keyword: "provides", context: "Bluebook", at: "", named: "resume", kind: "text", required: "false", fills: "provides"
258
269
  member keyword: "provides", context: "Bluebook", at: "", named: "enable", kind: "text", required: "false", fills: "provides"
259
270
  member keyword: "provides", context: "Bluebook", at: "", named: "disable", kind: "text", required: "false", fills: "provides"
271
+ member keyword: "provides", context: "Bluebook", at: "", named: "mark_sensitive", kind: "text", required: "false", fills: "provides"
272
+ member keyword: "provides", context: "Bluebook", at: "", named: "markings_for", kind: "text", required: "false", fills: "provides"
273
+ member keyword: "provides", context: "Bluebook", at: "", named: "key_for", kind: "text", required: "false", fills: "provides"
274
+ member keyword: "provides", context: "Bluebook", at: "", named: "shred", kind: "text", required: "false", fills: "provides"
260
275
  member keyword: "aggregate", context: "Bluebook", at: "1", named: "", kind: "text", required: "true", fills: "name"
261
276
  member keyword: "read_model", context: "Bluebook", at: "1", named: "", kind: "text", required: "true", fills: "name"
262
277
  member keyword: "policy", context: "Bluebook", at: "1", named: "", kind: "text", required: "true", fills: "name"
@@ -20,6 +20,7 @@ Hecks.bluebook "Bluebook" do
20
20
  attribute :state_field, EntityText, optional: true
21
21
  attribute :state_start, EntityText, optional: true
22
22
  attribute :transitions, list_of(PieceTransition)
23
+ attribute :marks, list_of(PieceMark)
23
24
 
24
25
  value_object "EntityName" do
25
26
  attribute :value, String
@@ -60,6 +61,11 @@ Hecks.bluebook "Bluebook" do
60
61
  attribute :to_state, String
61
62
  end
62
63
 
64
+ value_object "PieceMark" do
65
+ attribute :name, String
66
+ attribute :state, String
67
+ end
68
+
63
69
  # Declaration order is a fact about the source, so the language holds it, not the store.
64
70
  value_object "Position" do
65
71
  attribute :value, Integer
@@ -233,6 +239,18 @@ Hecks.bluebook "Bluebook" do
233
239
  emits PieceTransitionAttached
234
240
  end
235
241
 
242
+ command "Mark" do
243
+ role "Language"
244
+ goal "Attach one state to a named meaning of the entity's state machine"
245
+
246
+ attribute :name, EntityText
247
+ attribute :state, EntityText
248
+
249
+ sets :marks, append: { name: :name, state: :state }
250
+
251
+ emits PieceMarkAttached
252
+ end
253
+
236
254
  # Keyword rows for this chapter; SyntaxBoot discovers aggregate-local seed tables.
237
255
  value_object "KeywordSeed" do
238
256
  attribute :word, String
@@ -13,6 +13,9 @@ Hecks.bluebook "Bluebook" do
13
13
  attribute :aggregate, PolicyText
14
14
  attribute :on_event, PolicyText
15
15
  attribute :trigger_command, PolicyText
16
+ # The need an `ask` names in the domain's own words, resolved at boot against the hecksagon's
17
+ # declared asks. Present in place of a port-naming `trigger_command`; empty otherwise.
18
+ attribute :ask, PolicyText, optional: true
16
19
  attribute :target_domain, PolicyText
17
20
  # An `across` target this domain expects never to reach. ModelCheck treats the related findings
18
21
  # as expected, and fails a declaration whose target turns out to be known after all.
@@ -63,7 +66,8 @@ Hecks.bluebook "Bluebook" do
63
66
  attribute :name, PolicyName
64
67
  attribute :aggregate, PolicyText, optional: true
65
68
  attribute :on_event, PolicyText
66
- attribute :trigger_command, PolicyText
69
+ attribute :trigger_command, PolicyText, optional: true
70
+ attribute :ask, PolicyText, optional: true
67
71
  attribute :target_domain, PolicyText, optional: true
68
72
  attribute :expect_undelivered, PolicyText, optional: true
69
73
  attribute :where, PolicyText, optional: true
@@ -75,6 +79,7 @@ Hecks.bluebook "Bluebook" do
75
79
  sets :aggregate
76
80
  sets :on_event
77
81
  sets :trigger_command
82
+ sets :ask
78
83
  sets :target_domain
79
84
  sets :expect_undelivered
80
85
  sets :where
@@ -111,6 +116,7 @@ Hecks.bluebook "Bluebook" do
111
116
 
112
117
  member word: "on", context: "Policy", body: "none", inner: "", opens: "", fills: "on_event", calls: "on_impl"
113
118
  member word: "trigger", context: "Policy", body: "none", inner: "", opens: "", fills: "trigger_command", calls: "trigger_impl"
119
+ member word: "ask", context: "Policy", body: "none", inner: "", opens: "", fills: "ask", calls: "ask_impl"
114
120
  member word: "across", context: "Policy", body: "none", inner: "", opens: "", fills: "target_domain", calls: "across_impl"
115
121
  member word: "where", context: "Policy", body: "source", inner: "", opens: "", fills: "where"
116
122
  member word: "for_each", context: "Policy", body: "none", inner: "", opens: "", fills: "for_each"
@@ -138,10 +144,12 @@ Hecks.bluebook "Bluebook" do
138
144
  member keyword: "on", context: "Policy", at: "1", named: "", kind: "text", required: "true", fills: "on_event"
139
145
  member keyword: "trigger", context: "Policy", at: "1", named: "", kind: "constant", required: "true", fills: "trigger_command"
140
146
  member keyword: "trigger", context: "Policy", at: "1", named: "", kind: "text", required: "true", fills: "trigger_command"
147
+ member keyword: "ask", context: "Policy", at: "1", named: "", kind: "symbol", required: "true", fills: "ask"
141
148
  member keyword: "across", context: "Policy", at: "1", named: "", kind: "text", required: "true", fills: "target_domain"
142
149
  member keyword: "across", context: "Policy", at: "", named: "expect_undelivered", kind: "flag", required: "false", fills: "expect_undelivered"
143
150
  member keyword: "for_each", context: "Policy", at: "1", named: "", kind: "text", required: "true", fills: "for_each"
144
151
  member keyword: "trigger", context: "Policy", at: "", named: "with", kind: "pairs", required: "false", fills: "with_spec", pairs_shape: "verbatim"
152
+ member keyword: "ask", context: "Policy", at: "", named: "with", kind: "pairs", required: "false", fills: "with_spec", pairs_shape: "verbatim"
145
153
 
146
154
  end
147
155
  end
@@ -121,7 +121,7 @@ Hecks.bluebook "Bluebook" do
121
121
  goal "Attach one where-clause to the query"
122
122
 
123
123
  attribute :field, QueryText
124
- # The same word at the same door — see Command.Change.op.
124
+ # The same word at the same entry point — see Command.Change.op.
125
125
  attribute :op, QueryText, admits: "Vocabulary::QueryComparator"
126
126
  attribute :value, QueryText
127
127
 
@@ -660,6 +660,10 @@ Hecks.bluebook "Bluebook" do
660
660
  # empty when none does; `follows` is the lane a promotion draws from, or empty. `alert_after`
661
661
  # is the whole hours the lane may stand behind the lane it follows before `hecks
662
662
  # promotion_run.watch` files a finding, or empty for a lane that is never late.
663
+ # `release_trailer` is the commit-message trailer a commit that sets the version must carry
664
+ # (`Release-Approved-By: <name>`) before a release is cut from this lane, or empty for a lane
665
+ # nothing is released from: the owner decides when a release happens by writing that line in
666
+ # the bump commit, and `release.yml` and `publishing_run.publish` both refuse a bump without it.
663
667
  value_object "Lane" do
664
668
  attribute :name, String
665
669
  attribute :guarded, String
@@ -667,9 +671,12 @@ Hecks.bluebook "Bluebook" do
667
671
  attribute :feeds, String
668
672
  attribute :follows, String
669
673
  attribute :alert_after, String
674
+ attribute :release_trailer, String
670
675
 
671
- member name: "main", guarded: "no", pushers: "anyone", feeds: "", follows: "", alert_after: ""
672
- member name: "stable", guarded: "yes", pushers: "green", feeds: "edge", follows: "main", alert_after: "4"
676
+ member name: "main", guarded: "no", pushers: "anyone", feeds: "", follows: "", alert_after: "",
677
+ release_trailer: ""
678
+ member name: "stable", guarded: "yes", pushers: "green", feeds: "edge", follows: "main", alert_after: "4",
679
+ release_trailer: "Release-Approved-By"
673
680
  end
674
681
 
675
682
  # The dials of the hecks_qa loop: what each is called, the type of value it takes and what it
@@ -791,8 +798,8 @@ Hecks.bluebook "Bluebook" do
791
798
  member script: "compact", section: "Custodian", aggregate: "Era", verb: "compact"
792
799
  member script: "heki_compact", section: "Custodian", aggregate: "Era", verb: "compact_heki"
793
800
  member script: "vendor_bluebook", section: "Custodian", aggregate: "Package", verb: "vendor", note: "waits: a refusal exits 1 with its reason on stderr"
794
- member script: "project_cli", section: "Custodian", aggregate: "Door", verb: "project_cli"
795
- member script: "hecks_mcp_door", section: "Custodian", aggregate: "Door", verb: "serve_mcp"
801
+ member script: "project_cli", section: "Custodian", aggregate: "Launch", verb: "project_cli"
802
+ member script: "hecks_mcp_door", section: "Custodian", aggregate: "Launch", verb: "serve_mcp"
796
803
  member script: "project_rust", section: "Custodian", aggregate: "Build", verb: "project_rust"
797
804
  member script: "project_wasm", section: "Custodian", aggregate: "Build", verb: "build_wasm"
798
805
  member script: "project_host", section: "Custodian", aggregate: "Build", verb: "build_host"
@@ -1,6 +1,6 @@
1
1
  Hecks.bluebook "Hecksagon" do
2
2
  aggregate "DomainPort" do
3
- description "A primary port — the boundary between a domain and its adapters, in hexagonal terms, is exactly what a hecksagon already IS for every other port. Root-level (belongs to the chapter) or aggregate-scoped (reached the same way persisted_by is, through the open Const.verb(...) door). One of two shapes, never both: a list of operations translating an inbound fact into this domain's own events, or a bare verb naming a swappable resource port (the same Hecks.port a project's own .port file declares, reached from inside the hecksagon instead)."
3
+ description "A primary port — the boundary between a domain and its adapters, in hexagonal terms, is exactly what a hecksagon already IS for every other port. Root-level (belongs to the chapter) or aggregate-scoped (reached the same way persisted_by is, through the open Const.verb(...) entry point). One of two shapes, never both: a list of operations translating an inbound fact into this domain's own events, or a bare verb naming a swappable resource port (the same Hecks.port a project's own .port file declares, reached from inside the hecksagon instead)."
4
4
 
5
5
  identified_by PortName, as: :name
6
6
 
@@ -6,6 +6,7 @@ Hecks.bluebook "Hecksagon" do
6
6
 
7
7
  attribute :subscriptions, list_of(Subscription)
8
8
  attribute :attachments, list_of(Attachment)
9
+ attribute :driving, list_of(DrivingAdapter)
9
10
 
10
11
  value_object "HecksagonName" do
11
12
  attribute :value, String
@@ -27,6 +28,14 @@ Hecks.bluebook "Hecksagon" do
27
28
  invariant("an attachment comes from the gem or from vendor") { source == "gem" || source == "vendor" }
28
29
  end
29
30
 
31
+ # A driving adapter this hecksagon admits: `value` names one of `Ruby`, `Cli`, `Json` or `Mcp`.
32
+ # A domain that declares none is open to all of them; one that declares any is reached through
33
+ # those and refused by the rest.
34
+ value_object "DrivingAdapter" do
35
+ attribute :value, String
36
+ invariant("a driving adapter is named") { !value.to_s.empty? }
37
+ end
38
+
30
39
  # The name an attachment is asked for by, before its source is known.
31
40
  value_object "ChapterName" do
32
41
  attribute :value, String
@@ -67,6 +76,17 @@ Hecks.bluebook "Hecksagon" do
67
76
  emits "ChapterAttached"
68
77
  end
69
78
 
79
+ command "DrivenBy" do
80
+ role "Deployment"
81
+ goal "Admit a driving adapter to reach this hecksagon's domain; adapters left out refuse it"
82
+
83
+ attribute :adapter, DrivingAdapter
84
+
85
+ sets :driving, append: { value: :adapter }
86
+
87
+ emits "DrivingAdapterAdmitted"
88
+ end
89
+
70
90
  # Keyword rows for File and Hecksagon; SyntaxBoot discovers every aggregate-local seed table.
71
91
  value_object "KeywordSeed" do
72
92
  attribute :word, String
@@ -83,6 +103,7 @@ Hecks.bluebook "Hecksagon" do
83
103
  member word: "hecksagon", context: "File", body: "keywords", inner: "Hecksagon", opens: "Hecksagon", fills: ""
84
104
  member word: "subscribe", context: "Hecksagon", body: "none", inner: "", opens: "", fills: "subscriptions"
85
105
  member word: "attaches", context: "Hecksagon", body: "none", inner: "", opens: "", fills: "attachments"
106
+ member word: "driven_by", context: "Hecksagon", body: "none", inner: "", opens: "", fills: "driving"
86
107
  member word: "port", context: "Hecksagon", body: "keywords", inner: "DomainPort", opens: "DomainPort", fills: "", calls: "port_impl"
87
108
  # A cross-domain reaction wired here rather than in the bluebook; reuses the Policy context.
88
109
  # No `calls:` — HecksagonBuilder#translates is an ordinary method, found before `word_gate`.
@@ -113,6 +134,7 @@ Hecks.bluebook "Hecksagon" do
113
134
  member keyword: "subscribe", context: "Hecksagon", at: "1", named: "", kind: "text", required: "true", fills: "subscriptions"
114
135
  member keyword: "attaches", context: "Hecksagon", at: "1", named: "", kind: "text", required: "true", fills: "attachments"
115
136
  member keyword: "attaches", context: "Hecksagon", at: "", named: "from", kind: "symbol", required: "false", fills: "source"
137
+ member keyword: "driven_by", context: "Hecksagon", at: "1", named: "", kind: "text", required: "true", fills: "driving"
116
138
  member keyword: "port", context: "Hecksagon", at: "1", named: "", kind: "text", required: "true", fills: "name"
117
139
  member keyword: "translates", context: "Hecksagon", at: "1", named: "", kind: "text", required: "true", fills: "name"
118
140
 
@@ -31,6 +31,11 @@
31
31
  "verb": "Bluebook::Aggregate.Lifecycle",
32
32
  "role": "Language"
33
33
  },
34
+ {
35
+ "scope": "bluebook:aggregate.mark",
36
+ "verb": "Bluebook::Aggregate.Mark",
37
+ "role": "Language"
38
+ },
34
39
  {
35
40
  "scope": "bluebook:aggregate.precondition",
36
41
  "verb": "Bluebook::Aggregate.Precondition",
@@ -156,6 +161,11 @@
156
161
  "verb": "Bluebook::Entity.Lifecycle",
157
162
  "role": "Language"
158
163
  },
164
+ {
165
+ "scope": "bluebook:entity.mark",
166
+ "verb": "Bluebook::Entity.Mark",
167
+ "role": "Language"
168
+ },
159
169
  {
160
170
  "scope": "bluebook:entity.precondition",
161
171
  "verb": "Bluebook::Entity.Precondition",
@@ -1,9 +1,9 @@
1
1
  Hecks.bluebook "Port" do
2
- vision "The .port language, declared in itself. A port names one resource door a domain's own aggregates call by verb — persisted_by, posted_by, whatever a chapter spells — bound to a real driven adapter at the world level. A sibling artifact to the bluebook, not part of one: the same port name is reused across every domain that needs the same kind of door."
2
+ vision "The .port language, declared in itself. A port names one resource a domain's own aggregates call by verb — persisted_by, posted_by, whatever a chapter spells — bound to a real driven adapter at the world level. A sibling artifact to the bluebook, not part of one: the same port name is reused across every domain that needs the same kind of entry point."
3
3
  core
4
4
 
5
5
  aggregate "Port" do
6
- description "One .port — a resource door a domain's own aggregates call by verb, and whether calling it answers with a value (reply) or fires an effect with nothing to hand back (effect)."
6
+ description "One .port — a resource a domain's own aggregates call by verb, and whether calling it answers with a value (reply) or fires an effect with nothing to hand back (effect)."
7
7
 
8
8
  identified_by PortName, as: :name
9
9
 
@@ -25,7 +25,7 @@ Hecks.bluebook "Port" do
25
25
  attribute :value, String, one_of: ["reply", "effect"]
26
26
  end
27
27
 
28
- # The method contract: what an adapter must `respond_to?` for a dispatch through this door.
28
+ # The method contract: what an adapter must `respond_to?` for a dispatch through this port.
29
29
  # A port that declares none is unchecked beyond `verify!`.
30
30
  value_object "PortAnswer" do
31
31
  attribute :value, String
@@ -34,7 +34,7 @@ Hecks.bluebook "Port" do
34
34
 
35
35
  command "Declare" do
36
36
  role "Deployment"
37
- goal "Open a resource door a domain's own aggregates may call by verb"
37
+ goal "Open a resource a domain's own aggregates may call by verb"
38
38
 
39
39
  attribute :name, PortName
40
40
  # Optional: a port may declare only `signal`; `verb` has no default, unlike `signal`.
data/lib/hecks/literal.rb CHANGED
@@ -7,6 +7,14 @@ module Hecks
7
7
  def to_s = "state(:#{name})"
8
8
  end
9
9
 
10
+ # A mutation source computed from arithmetic over command arguments, state and numbers:
11
+ # `sets :refund, to: paid * rate / 100`. `text` is the canonical expression, read by
12
+ # `Bluebook::Expression::Resolver`; its operands name command arguments first, then the
13
+ # record's own fields.
14
+ Computed = Struct.new(:text) do
15
+ def to_s = "expr(#{text})"
16
+ end
17
+
10
18
  # The one wire spelling for a Ruby literal captured from a bluebook: a symbol wears its
11
19
  # colon, a string its quotes, a hash `{key: value}`, a list brackets; the rest are bare.
12
20
  # Stated here, not left to `inspect`, whose Hash rendering differs between Ruby 3.3 and 3.4.
@@ -15,7 +23,7 @@ module Hecks
15
23
 
16
24
  # Turns a Ruby value into its self-describing wire spelling.
17
25
  #
18
- # @param value [Object] value to render: nil, Symbol, String, StateRef, true,
26
+ # @param value [Object] value to render: nil, Symbol, String, StateRef, Computed, true,
19
27
  # false, Integer, Float, Hash, or Array (recursively)
20
28
  # @return [String] the self-describing spelling `read` can parse back
21
29
  # @raise [ArgumentError] if `value` is a type with no pinned literal spelling
@@ -24,7 +32,7 @@ module Hecks
24
32
  when nil then "nil"
25
33
  when Symbol then ":#{value}"
26
34
  when String then quote(value)
27
- when StateRef, true, false, Integer, Float then value.to_s
35
+ when StateRef, Computed, true, false, Integer, Float then value.to_s
28
36
  when Hash, Array then render_collection(value)
29
37
  else
30
38
  raise ArgumentError, "#{value.class} has no pinned literal spelling — teach Literal.render one " \
@@ -44,7 +52,7 @@ module Hecks
44
52
  # A bare word stays a String, since some fields are stored as plain text, never rendered.
45
53
  #
46
54
  # @param text [String, #to_s] wire spelling produced by `render`, or a bare word
47
- # @return [Object] nil, true, false, Integer, Float, Symbol, StateRef, String,
55
+ # @return [Object] nil, true, false, Integer, Float, Symbol, StateRef, Computed, String,
48
56
  # Hash, or Array — or `text` itself, stripped, when it matches no known spelling
49
57
  def read(text)
50
58
  raw = text.to_s.strip
@@ -65,6 +73,7 @@ module Hecks
65
73
  [->(raw) { raw.match?(/\A-?\d+\.\d+\z/) }, :to_f.to_proc],
66
74
  [->(raw) { raw.start_with?(":") }, ->(raw) { raw[1..].to_sym }],
67
75
  [->(raw) { raw.match?(/\Astate\(:[A-Za-z_][A-Za-z0-9_]*\)\z/) }, ->(raw) { StateRef.new(raw[7..-2].to_sym) }],
76
+ [->(raw) { raw.match?(/\Aexpr\(.+\)\z/m) }, ->(raw) { Computed.new(raw[5..-2]) }],
68
77
  [->(raw) { quoted?(raw) }, ->(raw) { unquote(raw) }],
69
78
  [->(raw) { raw.start_with?("{") && raw.end_with?("}") }, ->(raw) { read_hash(raw) }],
70
79
  [->(raw) { raw.start_with?("[") && raw.end_with?("]") }, ->(raw) { read_array(raw) }]
@@ -20,12 +20,17 @@ module Hecks
20
20
  # reverse, so Shred cannot be dispatched before the key is actually gone. Idempotent:
21
21
  # a subject with no live key, or one already shredded, is left alone and returns false.
22
22
  def shred!(dispatcher, domain:, subject_id:) # rubocop:disable Naming/PredicateMethod
23
- record = dispatcher.query("Privacy::SubjectKey.ForSubject", domain: domain, subject_id: subject_id).first
23
+ provider = dispatcher.registry.provider_of(Bluebook::Capabilities::SUBJECT_KEYS)
24
+ return false unless provider
25
+
26
+ record = dispatcher.query(provider.provided_verb(Bluebook::Capabilities::SUBJECT_KEYS, :key_for),
27
+ domain: domain, subject_id: subject_id).first
24
28
  return false unless record
25
29
  return false if record[:status].to_s == "shredded"
26
30
 
27
31
  destroy(dispatcher.registry, key_reference: record[:key_reference].value)
28
- dispatcher.dispatch_flat("Privacy::SubjectKey.Shred", domain: { value: domain }, subject_id: { value: subject_id })
32
+ shred = provider.provided_verb(Bluebook::Capabilities::SUBJECT_KEYS, :shred)
33
+ dispatcher.dispatch_flat(shred, domain: { value: domain }, subject_id: { value: subject_id })
29
34
  true
30
35
  end
31
36
 
@@ -126,7 +126,7 @@ module Hecks
126
126
  # skips the vendored lookup
127
127
  # @return [Array<String>] file paths to read, in load order; `[]` when none applies
128
128
  def fallback_source_files(bluebook, directory, domain_files, registry)
129
- vendored_name = registry && vendored_bluebook_name_for(registry, bluebook.name)
129
+ vendored_name = registry && vendored_bluebook_name_for(registry, bluebook.name, directory)
130
130
  framework_path = Framework.members[bluebook.name]
131
131
  return [framework_path] if framework_path
132
132
  return vendored_source_for(directory, vendored_name) if vendored_name
@@ -139,19 +139,30 @@ module Hecks
139
139
 
140
140
  # Recovers the vendored package name behind a bluebook, if some hecksagon vendored it.
141
141
  #
142
- # Matched by Pascal-casing: the vendored name Pascal-cases to this
143
- # bluebook's own declared name.
142
+ # Matched first by Pascal-casing (the package name Pascal-cases to the bluebook's declared
143
+ # name), then by the package's own files: a package whose chapter is named otherwise (the
144
+ # package `gallery` declaring `Galleries`) is the one whose files declare that name.
144
145
  #
145
146
  # @param registry [Runtime::Registry] the registry whose hecksagons are searched
146
147
  # @param bluebook_name [String] the bluebook's declared Pascal-case name
148
+ # @param directory [String] path of the domain's own bluebook directory, whose parent
149
+ # holds `vendor/embryonaut_bluebooks`
147
150
  # @return [String, nil] the package name as written in `attaches ... from: :vendor`; nil
148
- # when no hecksagon vendored a package whose name Pascal-cases to `bluebook_name`
149
- def vendored_bluebook_name_for(registry, bluebook_name)
150
- registry.hecksagons.each_value do |hecksagon|
151
- match = hecksagon.vendored_packages.find { |name| Naming.pascal(name) == bluebook_name }
152
- return match if match
153
- end
154
- nil
151
+ # when no vendored package matches `bluebook_name` by name or by declaration
152
+ def vendored_bluebook_name_for(registry, bluebook_name, directory)
153
+ names = registry.hecksagons.each_value.flat_map(&:vendored_packages).uniq
154
+ names.find { |name| Naming.pascal(name) == bluebook_name } ||
155
+ names.find { |name| declared_by_package?(directory, name, bluebook_name) }
156
+ end
157
+
158
+ # Asks whether any file of a vendored package opens the named bluebook.
159
+ #
160
+ # @param directory [String] path of the domain's own bluebook directory
161
+ # @param package [String] the vendored package name
162
+ # @param bluebook_name [String] the declared bluebook name looked for
163
+ # @return [Boolean] true when some `.bluebook` file of the package declares `bluebook_name`
164
+ def declared_by_package?(directory, package, bluebook_name)
165
+ vendored_source_for(directory, package).any? { |path| declares_bluebook?(path, bluebook_name) }
155
166
  end
156
167
 
157
168
  # Lists the `.bluebook` files a vendored embryonaut package ships.
@@ -1,3 +1,5 @@
1
+ require_relative "../../../../../indifferent_key"
2
+
1
3
  module Hecks
2
4
  module Adapters
3
5
  class PostgresEra
@@ -17,11 +19,11 @@ module Hecks
17
19
  # Coalesce instead of `setting` for the era: the factory always passes `era:`, nil until
18
20
  # the boot gate resolves it, and nil can never be a real override.
19
21
  def resolve_era!(settings)
20
- @era = settings.key?(:era) ? settings[:era] : settings["era"]
22
+ @era = IndifferentKey.read(settings, :era)
21
23
  @era ||= @lineage.current_era
22
24
  # Non-nil only for a held-but-superseded boot. `append` and `atomic_put` refuse on it
23
25
  # because a superuser walks through the RLS fence.
24
- @superseded_by = settings.key?(:superseded_by) ? settings[:superseded_by] : settings["superseded_by"]
26
+ @superseded_by = IndifferentKey.read(settings, :superseded_by)
25
27
  end
26
28
 
27
29
  def provision!
@@ -1,6 +1,7 @@
1
1
  require_relative "../lineage"
2
2
  require_relative "../../storage_shape"
3
3
  require_relative "context"
4
+ require_relative "../../../../../../indifferent_key"
4
5
 
5
6
  module Hecks
6
7
  module Adapters
@@ -22,7 +23,7 @@ module Hecks
22
23
  lineage.check_fence_applies!(allow_superuser: PostgresEra.setting(settings, :allow_superuser, default: false))
23
24
  lineage.ensure_base!
24
25
  # key? first, never `||`: a stored `false` must not read as an absent key.
25
- role = settings.key?(:role) ? settings[:role] : settings["role"]
26
+ role = IndifferentKey.read(settings, :role)
26
27
  resolve_era(Context.new(registry: registry, bluebook: bluebook, current_text: current_text,
27
28
  lineage: lineage, role: role, directory: directory))
28
29
  ensure
@@ -26,7 +26,7 @@ module Hecks
26
26
  # @raise [ArgumentError] on an unknown option
27
27
  def build(registry, domain, aggregate, bind, **)
28
28
  opts = BuildOptions.new(**)
29
- settings = adapter_settings(registry, domain, bind, opts.settings_verb)
29
+ settings = adapter_settings(registry, domain, aggregate, bind, opts.settings_verb)
30
30
  adapter = registry.adapter_class(bind.adapter).new(aggregate: aggregate, settings: settings, root: registry.root)
31
31
  repository = AppendOnly.new(CodecBoundary.guard!(adapter))
32
32
  # `:atomic_append` (PostgresEra) means `append` already committed the
@@ -39,14 +39,17 @@ module Hecks
39
39
  #
40
40
  # @param registry [Runtime::Registry] the booted registry
41
41
  # @param domain [String, Symbol] the domain the aggregate belongs to
42
+ # @param aggregate [Bluebook::Aggregate] the aggregate the adapter is built for
42
43
  # @param bind [Bluebook::Bind] the bind naming the adapter
43
44
  # @param settings_verb [String] the verb whose settings configure the adapter
44
- # @return [Hash] the declared settings plus the domain, resolved era and superseding era
45
- def adapter_settings(registry, domain, bind, settings_verb)
45
+ # @return [Hash] the declared settings, under the adapter's own declared defaults, plus
46
+ # the domain, resolved era and superseding era
47
+ def adapter_settings(registry, domain, aggregate, bind, settings_verb)
46
48
  registry.check_verb(bind)
47
49
  settings = registry.binding_settings(domain, settings_verb, bind.adapter)
48
50
  .reject { |key, _| key.to_sym == :role }
49
51
  registry.check_settings(bind, settings)
52
+ settings = registry.with_adapter_defaults(bind.adapter, aggregate, settings)
50
53
  # domain, resolved era and superseding era ride along outside the declared
51
54
  # settings: a lineage adapter needs them to journal per domain, per-era.
52
55
  settings.merge(domain: domain.to_s, era: registry.resolved_eras[domain.to_s],