hecks 5.0.0 → 7.0.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 (1687) hide show
  1. checksums.yaml +4 -4
  2. data/examples/pizzas/bluebook/pizzas.bluebook +1 -0
  3. data/exe/hecks +31 -12
  4. data/lib/hecks/adapters/driven/claude_code.rb +3 -3
  5. data/lib/hecks/adapters/driven/d1.rb +2 -1
  6. data/lib/hecks/adapters/driven/folder.rb +44 -28
  7. data/lib/hecks/adapters/driven/folder_overlay.rb +66 -0
  8. data/lib/hecks/adapters/driven/google_authentication.adapter +3 -0
  9. data/lib/hecks/adapters/driven/google_authentication.rb +99 -0
  10. data/lib/hecks/adapters/driven/governance_authorization.rb +32 -36
  11. data/lib/hecks/adapters/driven/heki/journal.rb +45 -0
  12. data/lib/hecks/adapters/driven/heki.rb +1 -1
  13. data/lib/hecks/adapters/driven/identity_registry.rb +1 -1
  14. data/lib/hecks/adapters/driven/in_memory_ordering.rb +3 -2
  15. data/lib/hecks/adapters/driven/in_process_key_vault.rb +1 -1
  16. data/lib/hecks/adapters/driven/key_keeper.rb +42 -0
  17. data/lib/hecks/adapters/driven/lambda/client.rb +8 -2
  18. data/lib/hecks/adapters/driven/lambda/remote_dispatcher.rb +185 -0
  19. data/lib/hecks/adapters/driven/lambda.rb +14 -2
  20. data/lib/hecks/adapters/driven/memory/dispatch_claims.rb +25 -0
  21. data/lib/hecks/adapters/driven/memory/outbox_rows.rb +2 -2
  22. data/lib/hecks/adapters/driven/memory.rb +15 -4
  23. data/lib/hecks/adapters/driven/postgres/codec.rb +1 -1
  24. data/lib/hecks/adapters/driven/postgres/journal.rb +46 -0
  25. data/lib/hecks/adapters/driven/postgres/name_guard.rb +25 -0
  26. data/lib/hecks/adapters/driven/postgres/outbox.rb +33 -7
  27. data/lib/hecks/adapters/driven/postgres/projection.rb +43 -0
  28. data/lib/hecks/adapters/driven/postgres/repository.rb +2 -2
  29. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +2 -1
  30. data/lib/hecks/adapters/driven/postgres/set_dialect.rb +44 -0
  31. data/lib/hecks/adapters/driven/postgres/shared_connection.rb +1 -1
  32. data/lib/hecks/adapters/driven/postgres.adapter +8 -0
  33. data/lib/hecks/adapters/driven/postgres.rb +11 -3
  34. data/lib/hecks/adapters/driven/postgres_dump/connection.rb +57 -0
  35. data/lib/hecks/adapters/driven/postgres_dump.rb +117 -0
  36. data/lib/hecks/adapters/driven/postgres_era/boot.rb +58 -0
  37. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/class_methods.rb +28 -8
  38. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/dialect.rb +11 -6
  39. data/lib/hecks/adapters/driven/postgres_era/field_cache_provisioner.rb +39 -0
  40. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/journal.rb +52 -13
  41. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/chain_sql.rb +37 -53
  42. data/lib/hecks/adapters/driven/postgres_era/lineage/direct_sql.rb +73 -0
  43. data/lib/hecks/adapters/driven/postgres_era/lineage/direct_verified.json +10 -0
  44. data/lib/hecks/adapters/driven/postgres_era/lineage/direct_verified.rb +65 -0
  45. data/lib/hecks/adapters/driven/postgres_era/lineage/domain_rename.rb +143 -0
  46. data/lib/hecks/adapters/driven/postgres_era/lineage/domain_rename_state.rb +108 -0
  47. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/era_integrity.rb +13 -8
  48. data/lib/hecks/adapters/driven/postgres_era/lineage/era_store.rb +125 -0
  49. data/lib/hecks/adapters/driven/postgres_era/lineage/layered_sql.rb +64 -0
  50. data/lib/hecks/adapters/driven/postgres_era/lineage/ledger_reset.rb +150 -0
  51. data/lib/hecks/adapters/driven/postgres_era/lineage/mint_provisioning.rb +85 -0
  52. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/mint_transaction.rb +13 -9
  53. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/provisioning.rb +11 -5
  54. data/lib/hecks/adapters/driven/postgres_era/lineage/split_sql.rb +76 -0
  55. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/sql_templates.rb +15 -2
  56. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/tail_merge.rb +1 -1
  57. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage.rb +11 -3
  58. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/context.rb +68 -0
  59. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage_manager/coverage_check.rb +16 -12
  60. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/disposable_ledger.rb +119 -0
  61. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/era_resolution.rb +219 -0
  62. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/era_resolver.rb +114 -0
  63. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/held_era.rb +68 -0
  64. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/ledger_change.rb +63 -0
  65. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage_manager/merge_coordinator.rb +3 -3
  66. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/mint_decision.rb +163 -0
  67. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/mint_request.rb +24 -0
  68. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/minter.rb +143 -0
  69. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/refusals.rb +76 -0
  70. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage_manager.rb +46 -12
  71. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/query_phases.rb +8 -2
  72. data/lib/hecks/adapters/driven/postgres_era/shared_tables.rb +32 -0
  73. data/lib/hecks/adapters/driven/postgres_era.adapter +19 -1
  74. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era.rb +39 -23
  75. data/lib/hecks/adapters/driven/prism.rb +4 -4
  76. data/lib/hecks/adapters/driven/projection/read_model_walk.rb +126 -0
  77. data/lib/hecks/adapters/driven/projection/row_decoding.rb +57 -0
  78. data/lib/hecks/adapters/driven/set_transform/assignments.rb +101 -0
  79. data/lib/hecks/adapters/driven/set_transform/expressions.rb +138 -0
  80. data/lib/hecks/adapters/driven/set_transform.rb +81 -0
  81. data/lib/hecks/adapters/driven/sql_keyset.rb +61 -0
  82. data/lib/hecks/adapters/driven/sql_query_builder.rb +29 -9
  83. data/lib/hecks/adapters/driven/sqlite/codec.rb +1 -1
  84. data/lib/hecks/adapters/driven/sqlite/ddl.rb +7 -0
  85. data/lib/hecks/adapters/driven/sqlite/dialect.rb +2 -1
  86. data/lib/hecks/adapters/driven/sqlite/dispatch_claims.rb +28 -0
  87. data/lib/hecks/adapters/driven/sqlite/journal.rb +46 -0
  88. data/lib/hecks/adapters/driven/sqlite/name_guard.rb +25 -0
  89. data/lib/hecks/adapters/driven/sqlite/outbox.rb +5 -4
  90. data/lib/hecks/adapters/driven/sqlite/projection.rb +9 -135
  91. data/lib/hecks/adapters/driven/sqlite/repository.rb +2 -2
  92. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +36 -4
  93. data/lib/hecks/adapters/driven/sqlite/set_dialect.rb +39 -0
  94. data/lib/hecks/adapters/driven/sqlite.adapter +2 -0
  95. data/lib/hecks/adapters/driven/sqlite.rb +20 -3
  96. data/lib/hecks/adapters/driven/tenant_provisioner.rb +1 -1
  97. data/lib/hecks/adapters/driven/worktree_overlay.rb +117 -0
  98. data/lib/hecks/adapters/driven.rb +8 -3
  99. data/lib/hecks/adapters/driving/cli/shorthand.rb +5 -1
  100. data/lib/hecks/adapters/driving/cli.rb +2 -2
  101. data/lib/hecks/adapters/driving/cli_runner/answers.rb +96 -34
  102. data/lib/hecks/adapters/driving/cli_runner/bang_rule.rb +35 -0
  103. data/lib/hecks/adapters/driving/cli_runner/completion_words.rb +36 -0
  104. data/lib/hecks/adapters/driving/cli_runner/json_words.rb +55 -0
  105. data/lib/hecks/adapters/driving/cli_runner/name_index.rb +63 -0
  106. data/lib/hecks/adapters/driving/cli_runner/read_context.rb +32 -0
  107. data/lib/hecks/adapters/driving/cli_runner/renamed_names.rb +49 -0
  108. data/lib/hecks/adapters/driving/cli_runner/resolution.rb +66 -39
  109. data/lib/hecks/adapters/driving/cli_runner/resume_words.rb +63 -0
  110. data/lib/hecks/adapters/driving/cli_runner/run_reference.rb +66 -0
  111. data/lib/hecks/adapters/driving/cli_runner/run_replay.rb +118 -0
  112. data/lib/hecks/adapters/driving/cli_runner/search_words.rb +35 -0
  113. data/lib/hecks/adapters/driving/cli_runner/settling.rb +14 -9
  114. data/lib/hecks/adapters/driving/cli_runner/streaming.rb +12 -11
  115. data/lib/hecks/adapters/driving/cli_runner/suggestions.rb +16 -8
  116. data/lib/hecks/adapters/driving/cli_runner/tour_words.rb +77 -0
  117. data/lib/hecks/adapters/driving/cli_runner/verb_names.rb +81 -0
  118. data/lib/hecks/adapters/driving/cli_runner/verbosity.rb +75 -0
  119. data/lib/hecks/adapters/driving/cli_runner.rb +110 -9
  120. data/lib/hecks/adapters/driving/context_line.rb +85 -0
  121. data/lib/hecks/adapters/driving/exit_status.rb +31 -0
  122. data/lib/hecks/adapters/driving/github_webhook.rb +2 -1
  123. data/lib/hecks/adapters/driving/handle/verb_methods.rb +46 -0
  124. data/lib/hecks/adapters/driving/handle.rb +9 -17
  125. data/lib/hecks/adapters/driving/json.rb +11 -8
  126. data/lib/hecks/adapters/driving/launcher_options.rb +41 -34
  127. data/lib/hecks/adapters/driving/mcp/progress_sink.rb +45 -0
  128. data/lib/hecks/adapters/driving/mcp/tool_calls.rb +7 -5
  129. data/lib/hecks/adapters/driving/mcp.rb +25 -5
  130. data/lib/hecks/adapters/driving/mcp_scope/argument_policy.rb +40 -7
  131. data/lib/hecks/adapters/driving/mcp_scope/env_problems.rb +3 -1
  132. data/lib/hecks/adapters/driving/mcp_scope.rb +11 -9
  133. data/lib/hecks/adapters/driving/newcomer.rb +6 -5
  134. data/lib/hecks/adapters/driving/ruby/aggregate_module/hooks.rb +154 -0
  135. data/lib/hecks/adapters/driving/ruby/aggregate_module.rb +73 -101
  136. data/lib/hecks/adapters/driving/ruby/bulk_verbs.rb +127 -0
  137. data/lib/hecks/adapters/driving/ruby/{chapter.rb → declaration.rb} +27 -27
  138. data/lib/hecks/adapters/driving/ruby/query_rows.rb +27 -0
  139. data/lib/hecks/adapters/driving/ruby.rb +25 -24
  140. data/lib/hecks/adapters/driving/usage_cache.rb +14 -3
  141. data/lib/hecks/adapters/driving/usage_log.rb +146 -0
  142. data/lib/hecks/adapters/driving/write_guard.rb +55 -0
  143. data/lib/hecks/adapters/driving.rb +0 -1
  144. data/lib/hecks/assembly.rb +101 -0
  145. data/lib/hecks/behaviors/dsl.rb +18 -4
  146. data/lib/hecks/behaviors/expectations/boot.rb +7 -7
  147. data/lib/hecks/behaviors/expectations/checks.rb +1 -1
  148. data/lib/hecks/behaviors/expectations/dispatching.rb +10 -9
  149. data/lib/hecks/behaviors/expectations/qualify.rb +9 -9
  150. data/lib/hecks/behaviors/expectations.rb +24 -10
  151. data/lib/hecks/behaviors/ir.rb +4 -2
  152. data/lib/hecks/behaviors/rspec.rb +1 -1
  153. data/lib/hecks/behaviors/runner.rb +31 -4
  154. data/lib/hecks/bench/environment.rb +6 -2
  155. data/lib/hecks/bench/postgres_probe.rb +3 -1
  156. data/lib/hecks/bench/report.rb +4 -1
  157. data/lib/hecks/bench/suite.rb +9 -3
  158. data/lib/hecks/bluebook/acknowledgements.rb +81 -0
  159. data/lib/hecks/bluebook/aggregate.rb +29 -17
  160. data/lib/hecks/bluebook/ask_resolution.rb +17 -17
  161. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +8 -5
  162. data/lib/hecks/bluebook/assembly/contracts/sagas.rb +2 -0
  163. data/lib/hecks/bluebook/assembly/contracts/structure.rb +24 -15
  164. data/lib/hecks/bluebook/assembly/marks.rb +10 -1
  165. data/lib/hecks/bluebook/assembly/scalars.rb +8 -2
  166. data/lib/hecks/bluebook/assembly/specializer.rb +2 -2
  167. data/lib/hecks/bluebook/assembly.rb +10 -10
  168. data/lib/hecks/bluebook/behaviour/aggregate.rb +29 -4
  169. data/lib/hecks/bluebook/behaviour/attribute.rb +12 -0
  170. data/lib/hecks/bluebook/behaviour/command.rb +1 -1
  171. data/lib/hecks/bluebook/behaviour/{chapter.rb → declaration.rb} +17 -17
  172. data/lib/hecks/bluebook/behaviour/domain_port.rb +1 -1
  173. data/lib/hecks/bluebook/behaviour/entity.rb +1 -0
  174. data/lib/hecks/bluebook/behaviour/hecksagon.rb +8 -1
  175. data/lib/hecks/bluebook/behaviour/process_manager.rb +4 -1
  176. data/lib/hecks/bluebook/behaviour/query.rb +5 -0
  177. data/lib/hecks/bluebook/behaviour/read_model.rb +4 -1
  178. data/lib/hecks/bluebook/behaviour/traits.rb +13 -0
  179. data/lib/hecks/bluebook/capabilities/attribute_values.rb +17 -16
  180. data/lib/hecks/bluebook/capabilities/package_declared.rb +65 -0
  181. data/lib/hecks/bluebook/capabilities.rb +32 -132
  182. data/lib/hecks/bluebook/command.rb +33 -7
  183. data/lib/hecks/bluebook/context_map.rb +104 -0
  184. data/lib/hecks/bluebook/{chapter.rb → declaration.rb} +45 -17
  185. data/lib/hecks/bluebook/domain_port.rb +7 -3
  186. data/lib/hecks/bluebook/dsl/adapter_builder.rb +28 -4
  187. data/lib/hecks/bluebook/dsl/aggregate_builder/draining.rb +2 -2
  188. data/lib/hecks/bluebook/dsl/aggregate_builder/one_of_reference.rb +54 -0
  189. data/lib/hecks/bluebook/dsl/aggregate_builder/query_field_sealing.rb +11 -2
  190. data/lib/hecks/bluebook/dsl/aggregate_builder/query_sealing.rb +15 -5
  191. data/lib/hecks/bluebook/dsl/aggregate_builder/relationships.rb +21 -5
  192. data/lib/hecks/bluebook/dsl/aggregate_builder/renaming.rb +25 -0
  193. data/lib/hecks/bluebook/dsl/aggregate_builder/rules.rb +22 -22
  194. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +2 -2
  195. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +30 -22
  196. data/lib/hecks/bluebook/dsl/attribute_collector.rb +0 -1
  197. data/lib/hecks/bluebook/dsl/audience_word.rb +40 -0
  198. data/lib/hecks/bluebook/dsl/bluebook_builder/metadata.rb +56 -0
  199. data/lib/hecks/bluebook/dsl/bluebook_builder/modules.rb +80 -0
  200. data/lib/hecks/bluebook/dsl/bluebook_builder/pending_givens.rb +22 -22
  201. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/computed_sources.rb +27 -21
  202. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/correlation_keys.rb +54 -5
  203. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/event_shapes.rb +5 -4
  204. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/fan_out_rows.rb +3 -3
  205. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/hop_tails.rb +13 -5
  206. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/projected_fields.rb +4 -4
  207. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/provisions.rb +38 -84
  208. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/query_hops.rb +5 -5
  209. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/references.rb +6 -6
  210. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/with_projections.rb +6 -6
  211. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +4 -3
  212. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +75 -92
  213. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +10 -0
  214. data/lib/hecks/bluebook/dsl/command_builder/folded_sets.rb +48 -0
  215. data/lib/hecks/bluebook/dsl/command_builder.rb +39 -16
  216. data/lib/hecks/bluebook/dsl/context_map_builder.rb +73 -0
  217. data/lib/hecks/bluebook/dsl/entity_builder/rules.rb +17 -17
  218. data/lib/hecks/bluebook/dsl/entity_builder/scope.rb +3 -3
  219. data/lib/hecks/bluebook/dsl/entity_builder/sealing.rb +2 -2
  220. data/lib/hecks/bluebook/dsl/entity_builder.rb +3 -5
  221. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +6 -4
  222. data/lib/hecks/bluebook/dsl/hecksagon_builder/{chapter_scope.rb → bluebook_scope.rb} +14 -14
  223. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +33 -27
  224. data/lib/hecks/bluebook/dsl/identity_declaration.rb +6 -2
  225. data/lib/hecks/bluebook/dsl/kinds.rb +53 -0
  226. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +4 -1
  227. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +9 -2
  228. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +25 -15
  229. data/lib/hecks/bluebook/dsl/query_builder.rb +14 -8
  230. data/lib/hecks/bluebook/dsl/read_model_builder/sealing.rb +6 -11
  231. data/lib/hecks/bluebook/dsl/read_model_builder.rb +1 -2
  232. data/lib/hecks/bluebook/dsl/translation_builder.rb +16 -1
  233. data/lib/hecks/bluebook/dsl/word_gate.rb +3 -2
  234. data/lib/hecks/bluebook/dsl/world_builder.rb +2 -2
  235. data/lib/hecks/bluebook/dsl.rb +4 -2
  236. data/lib/hecks/bluebook/expression/ast_json/emitters.rb +4 -0
  237. data/lib/hecks/bluebook/expression/ast_json.rb +5 -3
  238. data/lib/hecks/bluebook/expression/ast_reader/blocks.rb +41 -0
  239. data/lib/hecks/bluebook/expression/ast_reader.rb +2 -10
  240. data/lib/hecks/bluebook/expression/canonical_form.rb +9 -4
  241. data/lib/hecks/bluebook/expression/evaluator/parser.rb +4 -1
  242. data/lib/hecks/bluebook/expression/evaluator.rb +13 -9
  243. data/lib/hecks/bluebook/expression/projection.json +6 -0
  244. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +50 -2
  245. data/lib/hecks/bluebook/expression/resolver/lookups.rb +2 -1
  246. data/lib/hecks/bluebook/expression/resolver/operations.rb +1 -0
  247. data/lib/hecks/bluebook/expression/resolver/parsing.rb +21 -15
  248. data/lib/hecks/bluebook/expression/resolver/scanning.rb +4 -1
  249. data/lib/hecks/bluebook/expression.rb +1 -1
  250. data/lib/hecks/bluebook/hecksagon.rb +48 -23
  251. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +4 -1
  252. data/lib/hecks/bluebook/meta_validator/judge/cells.rb +1 -1
  253. data/lib/hecks/bluebook/meta_validator/judge/identity.rb +11 -5
  254. data/lib/hecks/bluebook/meta_validator/modes.rb +27 -27
  255. data/lib/hecks/bluebook/meta_validator/plan/category.rb +10 -2
  256. data/lib/hecks/bluebook/meta_validator/plan/verbs.rb +9 -0
  257. data/lib/hecks/bluebook/meta_validator/plan.rb +7 -6
  258. data/lib/hecks/bluebook/meta_validator/readings/fields.rb +3 -3
  259. data/lib/hecks/bluebook/meta_validator/reconstruction/heads.rb +14 -4
  260. data/lib/hecks/bluebook/meta_validator/reconstruction/reading.rb +8 -1
  261. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +24 -19
  262. data/lib/hecks/bluebook/meta_validator/shapes.rb +9 -1
  263. data/lib/hecks/bluebook/meta_validator/syntax_boot/disk_cache.rb +19 -16
  264. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +21 -15
  265. data/lib/hecks/bluebook/meta_validator/verdict_cache/prebuilt.rb +4 -1
  266. data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +11 -10
  267. data/lib/hecks/bluebook/meta_validator.rb +25 -22
  268. data/lib/hecks/bluebook/model_check/aggregate_checks.rb +95 -0
  269. data/lib/hecks/bluebook/model_check/ask_checks.rb +31 -5
  270. data/lib/hecks/bluebook/model_check/client_profile.rb +8 -42
  271. data/lib/hecks/bluebook/model_check/context_checks.rb +97 -0
  272. data/lib/hecks/bluebook/model_check/context_map_checks.rb +158 -0
  273. data/lib/hecks/bluebook/model_check/lifecycle_checks.rb +27 -5
  274. data/lib/hecks/bluebook/model_check/one_of_checks.rb +106 -0
  275. data/lib/hecks/bluebook/model_check/orphan_checks.rb +129 -0
  276. data/lib/hecks/bluebook/model_check/reaction_chains.rb +114 -0
  277. data/lib/hecks/bluebook/model_check/reaction_cycles.rb +53 -0
  278. data/lib/hecks/bluebook/model_check/reaction_depth_checks.rb +58 -0
  279. data/lib/hecks/bluebook/model_check/reaction_walk.rb +62 -0
  280. data/lib/hecks/bluebook/model_check/redundant_relationships.rb +38 -0
  281. data/lib/hecks/bluebook/model_check/reference_checks.rb +11 -1
  282. data/lib/hecks/bluebook/model_check/saga_checks.rb +8 -3
  283. data/lib/hecks/bluebook/model_check/verbs.rb +4 -4
  284. data/lib/hecks/bluebook/model_check.rb +33 -35
  285. data/lib/hecks/bluebook/named_pattern.rb +12 -1
  286. data/lib/hecks/bluebook/one_of_union.rb +35 -0
  287. data/lib/hecks/bluebook/pattern_subset.rb +12 -3
  288. data/lib/hecks/bluebook/process_manager.rb +20 -3
  289. data/lib/hecks/bluebook/project_loader.rb +3 -2
  290. data/lib/hecks/bluebook/project_register.rb +21 -5
  291. data/lib/hecks/bluebook/query.rb +4 -1
  292. data/lib/hecks/bluebook/read_model.rb +5 -5
  293. data/lib/hecks/bluebook/reference.rb +3 -3
  294. data/lib/hecks/bluebook/smoke_test.rb +15 -13
  295. data/lib/hecks/bluebook/synthesizer.rb +48 -13
  296. data/lib/hecks/bluebook/translation.rb +5 -3
  297. data/lib/hecks/bluebook/translation_expression/parser.rb +14 -5
  298. data/lib/hecks/bluebook/translation_plan.rb +9 -2
  299. data/lib/hecks/bluebook.rb +6 -3
  300. data/lib/hecks/bulk/bluebook/adapters/row_sweeper.adapter +7 -0
  301. data/lib/hecks/bulk/bluebook/bulk.behaviors +18 -0
  302. data/lib/hecks/bulk/bluebook/bulk.bluebook +126 -0
  303. data/lib/hecks/bulk/bluebook/bulk.hecksagon +11 -0
  304. data/lib/hecks/bulk/bluebook/bulk.ports.hecksagon +13 -0
  305. data/lib/hecks/bulk/plan.rb +60 -0
  306. data/lib/hecks/bulk/row_sweeper.rb +34 -0
  307. data/lib/hecks/bulk/set_arguments.rb +68 -0
  308. data/lib/hecks/bulk/set_eligibility.rb +50 -0
  309. data/lib/hecks/bulk/set_planner.rb +100 -0
  310. data/lib/hecks/bulk/set_sweep.rb +78 -0
  311. data/lib/hecks/bulk/sweep.rb +67 -0
  312. data/lib/hecks/ci_pipeline/adapters/ci_cache.adapter +7 -0
  313. data/lib/hecks/ci_pipeline/adapters/ci_cache.rb +39 -0
  314. data/lib/hecks/ci_pipeline/adapters/ci_runner.adapter +7 -0
  315. data/lib/hecks/ci_pipeline/adapters/ci_runner.rb +55 -0
  316. data/lib/hecks/ci_pipeline/ci_pipeline.behaviors +322 -0
  317. data/lib/hecks/ci_pipeline/ci_pipeline.bluebook +938 -0
  318. data/lib/hecks/ci_pipeline/ci_pipeline.hecksagon +12 -0
  319. data/lib/hecks/ci_pipeline/ci_pipeline.ports.hecksagon +16 -0
  320. data/lib/hecks/ci_speed/adapters/ci_reader.adapter +7 -0
  321. data/lib/hecks/ci_speed/adapters/ci_reader.rb +141 -0
  322. data/lib/hecks/ci_speed/adapters/local_runner/answer.rb +41 -0
  323. data/lib/hecks/ci_speed/adapters/local_runner/catalog.rb +89 -0
  324. data/lib/hecks/ci_speed/adapters/local_runner/checkouts.rb +89 -0
  325. data/lib/hecks/ci_speed/adapters/local_runner/launcher.rb +69 -0
  326. data/lib/hecks/ci_speed/adapters/local_runner/load_average.rb +38 -0
  327. data/lib/hecks/ci_speed/adapters/local_runner/refs.rb +39 -0
  328. data/lib/hecks/ci_speed/adapters/local_runner/refused.rb +11 -0
  329. data/lib/hecks/ci_speed/adapters/local_runner/verdict.rb +71 -0
  330. data/lib/hecks/ci_speed/adapters/local_runner.adapter +7 -0
  331. data/lib/hecks/ci_speed/adapters/local_runner.rb +168 -0
  332. data/lib/hecks/ci_speed/adapters/replayed_runner.rb +39 -0
  333. data/lib/hecks/ci_speed/ci_speed.behaviors +341 -0
  334. data/lib/hecks/ci_speed/ci_speed.bluebook +1073 -0
  335. data/lib/hecks/ci_speed/ci_speed.hecksagon +11 -0
  336. data/lib/hecks/ci_speed/ci_speed.ports.hecksagon +21 -0
  337. data/lib/hecks/ci_workflows/ci_workflows.behaviors +13 -0
  338. data/lib/hecks/ci_workflows/ci_workflows.bluebook +173 -0
  339. data/lib/hecks/ci_workflows/ci_workflows.hecksagon +15 -0
  340. data/lib/hecks/codemod.rb +3 -3
  341. data/lib/hecks/coder/coder.bluebook +385 -0
  342. data/lib/hecks/coder/coder.hecksagon +12 -0
  343. data/lib/hecks/command_names/check.rb +39 -0
  344. data/lib/hecks/command_names/extractor.rb +67 -0
  345. data/lib/hecks/command_names/index.rb +40 -0
  346. data/lib/hecks/command_names/resolution.rb +81 -0
  347. data/lib/hecks/command_names/scan.rb +54 -0
  348. data/lib/hecks/command_names/sources.rb +73 -0
  349. data/lib/hecks/command_names.rb +26 -0
  350. data/lib/hecks/composer/adapters/claude_composer.rb +3 -2
  351. data/lib/hecks/composer/agent.rb +1 -1
  352. data/lib/hecks/composer/assistant.rb +2 -2
  353. data/lib/hecks/composer/behavior_run.rb +7 -1
  354. data/lib/hecks/composer/bluebook/composer.bluebook +18 -25
  355. data/lib/hecks/composer/bluebook/composer.hecksagon +1 -1
  356. data/lib/hecks/composer/board.rb +6 -6
  357. data/lib/hecks/composer/board_edit/command_text.rb +4 -1
  358. data/lib/hecks/composer/board_edit/source.rb +18 -9
  359. data/lib/hecks/composer/board_edit.rb +2 -2
  360. data/lib/hecks/composer/check.rb +22 -22
  361. data/lib/hecks/composer/domains.rb +1 -1
  362. data/lib/hecks/composer/jobs.rb +1 -1
  363. data/lib/hecks/composer/live.rb +19 -13
  364. data/lib/hecks/composer/outline.rb +4 -1
  365. data/lib/hecks/composer/page.html +1 -1
  366. data/lib/hecks/composer/panels.rb +6 -3
  367. data/lib/hecks/composer/parse.rb +53 -36
  368. data/lib/hecks/composer/session.rb +22 -16
  369. data/lib/hecks/composer/toolbox.rb +17 -7
  370. data/lib/hecks/composer/web.rb +3 -2
  371. data/lib/hecks/composer/workbench.rb +12 -9
  372. data/lib/hecks/composer.rb +1 -1
  373. data/lib/hecks/construct.rb +7 -7
  374. data/lib/hecks/corpus/adapters.rb +2 -1
  375. data/lib/hecks/corpus/generated_key.rb +205 -0
  376. data/lib/hecks/corpus/listing.rb +34 -0
  377. data/lib/hecks/corpus/routes.rb +25 -5
  378. data/lib/hecks/corpus/rust.rb +86 -35
  379. data/lib/hecks/corpus/sources.rb +14 -12
  380. data/lib/hecks/corpus/worlds.rb +2 -1
  381. data/lib/hecks/corpus.rb +57 -54
  382. data/lib/hecks/deploy/bluebook/deploy.bluebook +304 -518
  383. data/lib/hecks/deprecation.rb +23 -0
  384. data/lib/hecks/doc/reference/harvester.rb +4 -1
  385. data/lib/hecks/doc/reference/readme.rb +11 -6
  386. data/lib/hecks/doc/reference/rendering.rb +12 -6
  387. data/lib/hecks/doc/reference.rb +8 -6
  388. data/lib/hecks/embryonaut_bluebook/lock.rb +5 -1
  389. data/lib/hecks/embryonaut_bluebook/manifest.rb +8 -2
  390. data/lib/hecks/embryonaut_bluebook/registry/changelog.rb +4 -1
  391. data/lib/hecks/embryonaut_bluebook/registry/versions.rb +13 -2
  392. data/lib/hecks/embryonaut_bluebook/registry.rb +5 -2
  393. data/lib/hecks/embryonaut_bluebook/shape.rb +1 -1
  394. data/lib/hecks/embryonaut_bluebook/vendor.rb +5 -2
  395. data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +15 -1
  396. data/lib/hecks/embryonaut_bluebook.rb +26 -12
  397. data/lib/hecks/engine_agreement.rb +4 -1
  398. data/lib/hecks/fixer/fixer.bluebook +240 -0
  399. data/lib/hecks/fixer/fixer.hecksagon +17 -0
  400. data/lib/hecks/forms/app/commands.rb +35 -2
  401. data/lib/hecks/forms/app/records.rb +12 -12
  402. data/lib/hecks/forms/app/responses.rb +1 -1
  403. data/lib/hecks/forms/app.rb +22 -19
  404. data/lib/hecks/forms/command_form_renderer.rb +2 -2
  405. data/lib/hecks/forms/field_controls.rb +6 -1
  406. data/lib/hecks/forms/field_shape.rb +14 -6
  407. data/lib/hecks/forms/html.rb +8 -7
  408. data/lib/hecks/forms/html_pattern.rb +80 -0
  409. data/lib/hecks/forms/index_renderer.rb +11 -11
  410. data/lib/hecks/forms/params.rb +2 -1
  411. data/lib/hecks/forms/port_argument.rb +4 -1
  412. data/lib/hecks/forms/query_form_renderer.rb +1 -1
  413. data/lib/hecks/forms/record_renderer.rb +2 -2
  414. data/lib/hecks/forms/record_table.rb +1 -1
  415. data/lib/hecks/forms/reference_options.rb +2 -1
  416. data/lib/hecks/forms/text_shape.rb +4 -1
  417. data/lib/hecks/forms/value_object_shape.rb +1 -1
  418. data/lib/hecks/forms.rb +7 -7
  419. data/lib/hecks/fqn.rb +8 -2
  420. data/lib/hecks/framework/bluebook/change.bluebook +10 -15
  421. data/lib/hecks/framework/bluebook/change.hecksagon +1 -1
  422. data/lib/hecks/framework/bluebook/compliance.bluebook +27 -126
  423. data/lib/hecks/framework/bluebook/console_settings.bluebook +199 -114
  424. data/lib/hecks/framework/bluebook/framework.hecksagon +7 -0
  425. data/lib/hecks/framework/bluebook/governance.bluebook +36 -14
  426. data/lib/hecks/framework/bluebook/identity.bluebook +1 -1
  427. data/lib/hecks/framework/bluebook/privacy.bluebook +123 -19
  428. data/lib/hecks/framework/bluebook/privacy.key_keeper.adapter +7 -0
  429. data/lib/hecks/framework/bluebook/privacy.ports.hecksagon +19 -0
  430. data/lib/hecks/framework/compliance.oidc.json +6 -36
  431. data/lib/hecks/framework/console_settings.oidc.json +39 -24
  432. data/lib/hecks/framework/privacy.oidc.json +25 -0
  433. data/lib/hecks/framework.rb +24 -8
  434. data/lib/hecks/framework_bluebooks.rb +181 -0
  435. data/lib/hecks/freezer.rb +7 -1
  436. data/lib/hecks/fuzzing/combination_miner.rb +9 -6
  437. data/lib/hecks/fuzzing/concurrent_dispatch/world_copy.rb +4 -1
  438. data/lib/hecks/fuzzing/concurrent_dispatch.rb +2 -2
  439. data/lib/hecks/fuzzing/domain_generator.rb +8 -3
  440. data/lib/hecks/fuzzing/era_boundary.rb +1 -1
  441. data/lib/hecks/fuzzing/failure_triage.rb +13 -6
  442. data/lib/hecks/fuzzing/form_census/{chapter_forms.rb → bluebook_forms.rb} +12 -12
  443. data/lib/hecks/fuzzing/form_census.rb +14 -12
  444. data/lib/hecks/fuzzing/generated_domain_check.rb +2 -1
  445. data/lib/hecks/fuzzing/isolated_boot/postgres_scratch.rb +14 -15
  446. data/lib/hecks/fuzzing/isolated_boot/scratch_schema.rb +19 -9
  447. data/lib/hecks/fuzzing/isolated_boot/worlds.rb +36 -13
  448. data/lib/hecks/fuzzing/isolated_boot.rb +5 -6
  449. data/lib/hecks/fuzzing/mutation/checks.rb +5 -4
  450. data/lib/hecks/fuzzing/mutation/finders.rb +25 -8
  451. data/lib/hecks/fuzzing/mutation/observation.rb +3 -1
  452. data/lib/hecks/fuzzing/mutation/source_map.rb +3 -1
  453. data/lib/hecks/fuzzing/nondeterministic.rb +10 -4
  454. data/lib/hecks/fuzzing/outside_world.rb +1 -1
  455. data/lib/hecks/fuzzing/projection_agreement/binding.rb +63 -0
  456. data/lib/hecks/fuzzing/projection_agreement/comparison.rb +104 -0
  457. data/lib/hecks/fuzzing/projection_agreement.rb +68 -0
  458. data/lib/hecks/fuzzing/properties/aggregation_recompute.rb +1 -1
  459. data/lib/hecks/fuzzing/properties/bulk_parity.rb +118 -0
  460. data/lib/hecks/fuzzing/properties/catalog.rb +7 -0
  461. data/lib/hecks/fuzzing/properties/cursor_walks.rb +56 -0
  462. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +39 -1
  463. data/lib/hecks/fuzzing/properties/group_by_recompute.rb +6 -2
  464. data/lib/hecks/fuzzing/properties/grouped_reduction_recompute.rb +88 -0
  465. data/lib/hecks/fuzzing/properties/guards.rb +4 -1
  466. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +12 -2
  467. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +2 -1
  468. data/lib/hecks/fuzzing/properties/mutation_recompute.rb +3 -2
  469. data/lib/hecks/fuzzing/properties/piece_invariants.rb +3 -2
  470. data/lib/hecks/fuzzing/properties/query_rows.rb +5 -3
  471. data/lib/hecks/fuzzing/properties/querying.rb +6 -3
  472. data/lib/hecks/fuzzing/properties/read_model_names.rb +8 -2
  473. data/lib/hecks/fuzzing/properties/read_models.rb +2 -1
  474. data/lib/hecks/fuzzing/properties/refusals.rb +35 -0
  475. data/lib/hecks/fuzzing/properties/role_checks.rb +3 -2
  476. data/lib/hecks/fuzzing/properties/set_transform_parity.rb +73 -0
  477. data/lib/hecks/fuzzing/properties/tenant_scope.rb +3 -2
  478. data/lib/hecks/fuzzing/properties.rb +6 -0
  479. data/lib/hecks/fuzzing/qa_settings.rb +4 -4
  480. data/lib/hecks/fuzzing/replay/fan_out.rb +4 -3
  481. data/lib/hecks/fuzzing/replay/filters.rb +2 -2
  482. data/lib/hecks/fuzzing/replay/guard_check.rb +2 -2
  483. data/lib/hecks/fuzzing/replay/mutation_trace.rb +23 -10
  484. data/lib/hecks/fuzzing/replay/observation.rb +11 -7
  485. data/lib/hecks/fuzzing/replay/read_only_steps.rb +40 -3
  486. data/lib/hecks/fuzzing/replay/refusal_trace.rb +44 -0
  487. data/lib/hecks/fuzzing/replay/role_check.rb +2 -2
  488. data/lib/hecks/fuzzing/replay/session.rb +38 -18
  489. data/lib/hecks/fuzzing/replay/validation_reports.rb +61 -0
  490. data/lib/hecks/fuzzing/replay.rb +2 -2
  491. data/lib/hecks/fuzzing/rust_gap_manifest.rb +3 -3
  492. data/lib/hecks/fuzzing/self_consistency/saga_redelivery.rb +18 -8
  493. data/lib/hecks/fuzzing/self_consistency/sagas.rb +2 -2
  494. data/lib/hecks/fuzzing/self_consistency/value_objects.rb +9 -2
  495. data/lib/hecks/fuzzing/self_consistency.rb +4 -8
  496. data/lib/hecks/fuzzing/sequence_generator/adversary/argument_mutations.rb +15 -5
  497. data/lib/hecks/fuzzing/sequence_generator/adversary/caller_draw.rb +14 -10
  498. data/lib/hecks/fuzzing/sequence_generator/adversary/precedence_mutations.rb +1 -1
  499. data/lib/hecks/fuzzing/sequence_generator/argument_drawing.rb +2 -1
  500. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +21 -13
  501. data/lib/hecks/fuzzing/sequence_generator/picker.rb +1 -1
  502. data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +3 -3
  503. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +2 -2
  504. data/lib/hecks/fuzzing/sequence_generator.rb +1 -2
  505. data/lib/hecks/fuzzing/shrinker.rb +4 -1
  506. data/lib/hecks/fuzzing/value_generator.rb +14 -8
  507. data/lib/hecks/fuzzing.rb +1 -0
  508. data/lib/hecks/grammar/dialects.rb +56 -0
  509. data/lib/hecks/grammar/evolve/arguments.rb +1 -1
  510. data/lib/hecks/grammar/evolve/keywords.rb +1 -1
  511. data/lib/hecks/grammar/evolve/seeds.rb +34 -8
  512. data/lib/hecks/grammar/expression.bluebook +3 -0
  513. data/lib/hecks/grammar/expression_operators.json +1071 -1
  514. data/lib/hecks/grammar/operators.rb +45 -22
  515. data/lib/hecks/grammar/translation.bluebook +3 -1
  516. data/lib/hecks/grammar/translation_ledger.rb +45 -0
  517. data/lib/hecks/grammar/translation_rules.json +710 -0
  518. data/lib/hecks/grammar.rb +27 -23
  519. data/lib/hecks/hecks/adapter_words.json +7 -0
  520. data/lib/hecks/hecks/adapters/area_overlap.rb +156 -0
  521. data/lib/hecks/hecks/adapters/claude_agents.adapter +7 -0
  522. data/lib/hecks/hecks/adapters/claude_agents.rb +124 -0
  523. data/lib/hecks/hecks/adapters/claude_transcript.rb +71 -0
  524. data/lib/hecks/hecks/adapters/codebase/batch_runs.rb +60 -0
  525. data/lib/hecks/hecks/adapters/codebase/cache_clearing.rb +32 -0
  526. data/lib/hecks/hecks/adapters/codebase/cargo_tests.rb +50 -0
  527. data/lib/hecks/hecks/adapters/codebase/changed_specs.rb +52 -0
  528. data/lib/hecks/hecks/adapters/codebase/codemods.rb +3 -2
  529. data/lib/hecks/hecks/adapters/codebase/composing.rb +3 -2
  530. data/lib/hecks/hecks/adapters/codebase/conformance.rb +30 -1
  531. data/lib/hecks/hecks/adapters/codebase/corpus_queries.rb +53 -0
  532. data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +1 -1
  533. data/lib/hecks/hecks/adapters/codebase/declarations.rb +144 -0
  534. data/lib/hecks/hecks/adapters/codebase/generated_keys.rb +31 -0
  535. data/lib/hecks/hecks/adapters/codebase/impacted_specs.rb +110 -0
  536. data/lib/hecks/hecks/adapters/codebase/inspection.rb +5 -4
  537. data/lib/hecks/hecks/adapters/codebase/language.rb +3 -2
  538. data/lib/hecks/hecks/adapters/codebase/launcher_text.rb +39 -0
  539. data/lib/hecks/hecks/adapters/codebase/minitest_runs.rb +52 -0
  540. data/lib/hecks/hecks/adapters/codebase/npm_registry.rb +7 -1
  541. data/lib/hecks/hecks/adapters/codebase/previewing.rb +62 -0
  542. data/lib/hecks/hecks/adapters/codebase/promotion/gate.rb +1 -1
  543. data/lib/hecks/hecks/adapters/codebase/reading.rb +94 -0
  544. data/lib/hecks/hecks/adapters/codebase/regeneration.rb +57 -10
  545. data/lib/hecks/hecks/adapters/codebase/release_facts.rb +1 -1
  546. data/lib/hecks/hecks/adapters/codebase/ruby_child.rb +5 -2
  547. data/lib/hecks/hecks/adapters/codebase/run_options.rb +35 -0
  548. data/lib/hecks/hecks/adapters/codebase/scripting.rb +39 -0
  549. data/lib/hecks/hecks/adapters/codebase/search.rb +91 -0
  550. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +114 -51
  551. data/lib/hecks/hecks/adapters/codebase/spec_files/report.rb +71 -0
  552. data/lib/hecks/hecks/adapters/codebase/spec_files/shared_run.rb +73 -0
  553. data/lib/hecks/hecks/adapters/codebase/spec_files.rb +95 -0
  554. data/lib/hecks/hecks/adapters/codebase/static_directory.rb +59 -0
  555. data/lib/hecks/hecks/adapters/codebase/style.rb +2 -2
  556. data/lib/hecks/hecks/adapters/codebase/test_runner.rb +82 -14
  557. data/lib/hecks/hecks/adapters/codebase/test_scope.rb +105 -0
  558. data/lib/hecks/hecks/adapters/codebase/test_suite.rb +73 -21
  559. data/lib/hecks/hecks/adapters/console_capture.rb +28 -1
  560. data/lib/hecks/hecks/adapters/cost_explorer.rb +9 -1
  561. data/lib/hecks/hecks/adapters/deploy_plans.rb +12 -3
  562. data/lib/hecks/hecks/adapters/deploy_toolchain/answers.rb +14 -5
  563. data/lib/hecks/hecks/adapters/deploy_toolchain/scripts.rb +6 -3
  564. data/lib/hecks/hecks/adapters/deploy_toolchain.rb +3 -3
  565. data/lib/hecks/hecks/adapters/desktop_alert/alert.rb +50 -0
  566. data/lib/hecks/hecks/adapters/desktop_alert.adapter +7 -0
  567. data/lib/hecks/hecks/adapters/desktop_alert.rb +99 -0
  568. data/lib/hecks/hecks/adapters/dispatching/brief_text.rb +51 -0
  569. data/lib/hecks/hecks/adapters/dispatching/launcher.rb +97 -0
  570. data/lib/hecks/hecks/adapters/dispatching/plan.rb +82 -0
  571. data/lib/hecks/hecks/adapters/dispatching/rows.rb +66 -0
  572. data/lib/hecks/hecks/adapters/dispatching/settings.rb +81 -0
  573. data/lib/hecks/hecks/adapters/dispatching/settler.rb +40 -0
  574. data/lib/hecks/hecks/adapters/dispatching.adapter +7 -0
  575. data/lib/hecks/hecks/adapters/dispatching.rb +93 -0
  576. data/lib/hecks/hecks/adapters/doctor_reading/report.rb +27 -0
  577. data/lib/hecks/hecks/adapters/doctor_reading.adapter +7 -0
  578. data/lib/hecks/hecks/adapters/doctor_reading.rb +161 -0
  579. data/lib/hecks/hecks/adapters/finding_github.rb +4 -1
  580. data/lib/hecks/hecks/adapters/first_admin.rb +16 -9
  581. data/lib/hecks/hecks/adapters/git.rb +5 -5
  582. data/lib/hecks/hecks/adapters/git_checkout.adapter +7 -0
  583. data/lib/hecks/hecks/adapters/git_checkout.rb +99 -0
  584. data/lib/hecks/hecks/adapters/git_commit.adapter +7 -0
  585. data/lib/hecks/hecks/adapters/git_commit.rb +61 -0
  586. data/lib/hecks/hecks/adapters/git_landing/finisher.rb +82 -0
  587. data/lib/hecks/hecks/adapters/git_landing/foreign_hold.rb +111 -0
  588. data/lib/hecks/hecks/adapters/git_landing/line.rb +171 -0
  589. data/lib/hecks/hecks/adapters/git_landing/lineup.rb +98 -0
  590. data/lib/hecks/hecks/adapters/git_landing/merger.rb +85 -0
  591. data/lib/hecks/hecks/adapters/git_landing/numbering.rb +54 -0
  592. data/lib/hecks/hecks/adapters/git_landing/rivals.rb +59 -0
  593. data/lib/hecks/hecks/adapters/git_landing/ticket.rb +156 -0
  594. data/lib/hecks/hecks/adapters/git_landing/tidy.rb +147 -0
  595. data/lib/hecks/hecks/adapters/git_landing/train.rb +104 -0
  596. data/lib/hecks/hecks/adapters/git_landing/verdict.rb +64 -0
  597. data/lib/hecks/hecks/adapters/git_landing/waiting.rb +120 -0
  598. data/lib/hecks/hecks/adapters/git_landing.adapter +7 -0
  599. data/lib/hecks/hecks/adapters/git_landing.rb +189 -0
  600. data/lib/hecks/hecks/adapters/git_launcher.adapter +7 -0
  601. data/lib/hecks/hecks/adapters/git_launcher.rb +82 -0
  602. data/lib/hecks/hecks/adapters/git_main_pull.rb +99 -0
  603. data/lib/hecks/hecks/adapters/git_refs.rb +8 -4
  604. data/lib/hecks/hecks/adapters/git_rehearsal/bench.rb +82 -0
  605. data/lib/hecks/hecks/adapters/git_rehearsal/due.rb +84 -0
  606. data/lib/hecks/hecks/adapters/git_rehearsal/impacted/skips.rb +47 -0
  607. data/lib/hecks/hecks/adapters/git_rehearsal/impacted/spec_run.rb +75 -0
  608. data/lib/hecks/hecks/adapters/git_rehearsal/impacted.rb +181 -0
  609. data/lib/hecks/hecks/adapters/git_rehearsal/line.rb +76 -0
  610. data/lib/hecks/hecks/adapters/git_rehearsal/reading.rb +24 -0
  611. data/lib/hecks/hecks/adapters/git_rehearsal/record.rb +146 -0
  612. data/lib/hecks/hecks/adapters/git_rehearsal/spec_map/corpus.rb +25 -0
  613. data/lib/hecks/hecks/adapters/git_rehearsal/spec_map/needles.rb +61 -0
  614. data/lib/hecks/hecks/adapters/git_rehearsal/spec_map.rb +105 -0
  615. data/lib/hecks/hecks/adapters/git_rehearsal/stages.rb +118 -0
  616. data/lib/hecks/hecks/adapters/git_rehearsal.adapter +7 -0
  617. data/lib/hecks/hecks/adapters/git_rehearsal.rb +141 -0
  618. data/lib/hecks/hecks/adapters/git_train/bench.rb +76 -0
  619. data/lib/hecks/hecks/adapters/git_train/branch_guard.rb +62 -0
  620. data/lib/hecks/hecks/adapters/git_train/carriage.rb +115 -0
  621. data/lib/hecks/hecks/adapters/git_train/launcher_shape.rb +75 -0
  622. data/lib/hecks/hecks/adapters/git_train/local_branch.rb +69 -0
  623. data/lib/hecks/hecks/adapters/git_train/mint_step.rb +47 -0
  624. data/lib/hecks/hecks/adapters/git_train/reading.rb +26 -0
  625. data/lib/hecks/hecks/adapters/git_train/settlement.rb +167 -0
  626. data/lib/hecks/hecks/adapters/git_train/survey.rb +60 -0
  627. data/lib/hecks/hecks/adapters/git_train.adapter +7 -0
  628. data/lib/hecks/hecks/adapters/git_train.rb +146 -0
  629. data/lib/hecks/hecks/adapters/git_worktree_add.adapter +7 -0
  630. data/lib/hecks/hecks/adapters/git_worktree_add.rb +68 -0
  631. data/lib/hecks/hecks/adapters/git_worktrees.rb +81 -0
  632. data/lib/hecks/hecks/adapters/github_pull_requests.adapter +7 -0
  633. data/lib/hecks/hecks/adapters/github_pull_requests.rb +109 -0
  634. data/lib/hecks/hecks/adapters/host_http.rb +9 -8
  635. data/lib/hecks/hecks/adapters/in_process_access.rb +3 -2
  636. data/lib/hecks/hecks/adapters/in_process_boot.rb +68 -39
  637. data/lib/hecks/hecks/adapters/in_process_operations.rb +40 -20
  638. data/lib/hecks/hecks/adapters/in_process_undo.rb +17 -0
  639. data/lib/hecks/hecks/adapters/journal_store/changes.rb +18 -9
  640. data/lib/hecks/hecks/adapters/journal_store/compaction.rb +14 -7
  641. data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +35 -20
  642. data/lib/hecks/hecks/adapters/journal_store/examination.rb +20 -4
  643. data/lib/hecks/hecks/adapters/journal_store/held_domain/renaming.rb +45 -0
  644. data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +47 -9
  645. data/lib/hecks/hecks/adapters/journal_store/held_mint.rb +128 -0
  646. data/lib/hecks/hecks/adapters/journal_store/readings/attestation.rb +3 -3
  647. data/lib/hecks/hecks/adapters/journal_store/readings/boot_status.rb +58 -0
  648. data/lib/hecks/hecks/adapters/journal_store/readings/composition.rb +39 -0
  649. data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/outcomes.rb +76 -0
  650. data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/postgres_reader.rb +44 -0
  651. data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/reader.rb +64 -0
  652. data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/report.rb +102 -0
  653. data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/sqlite_reader.rb +43 -0
  654. data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/stores.rb +80 -0
  655. data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/subscribers.rb +72 -0
  656. data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/summary.rb +39 -0
  657. data/lib/hecks/hecks/adapters/journal_store/readings/event_trace.rb +39 -0
  658. data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/command_line.rb +57 -0
  659. data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/edge_guards.rb +107 -0
  660. data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/inspection.rb +94 -0
  661. data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/plan.rb +66 -0
  662. data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/report.rb +72 -0
  663. data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/store.rb +34 -0
  664. data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/watermark_guards.rb +86 -0
  665. data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/world.rb +66 -0
  666. data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan.rb +36 -0
  667. data/lib/hecks/hecks/adapters/journal_store/readings/projection_audit.rb +64 -0
  668. data/lib/hecks/hecks/adapters/journal_store/readings/scaffolding.rb +10 -7
  669. data/lib/hecks/hecks/adapters/journal_store/readings/sqlite_status/database_reading.rb +113 -0
  670. data/lib/hecks/hecks/adapters/journal_store/readings/sqlite_status/read_only.rb +33 -0
  671. data/lib/hecks/hecks/adapters/journal_store/readings/sqlite_status.rb +32 -0
  672. data/lib/hecks/hecks/adapters/journal_store/readings.rb +15 -3
  673. data/lib/hecks/hecks/adapters/journal_store/reconcile_report.rb +60 -0
  674. data/lib/hecks/hecks/adapters/journal_store/reconciliation.rb +121 -0
  675. data/lib/hecks/hecks/adapters/journal_store/translation_write/shape_edge.rb +59 -0
  676. data/lib/hecks/hecks/adapters/journal_store/translation_write.rb +123 -0
  677. data/lib/hecks/hecks/adapters/journal_store.rb +18 -2
  678. data/lib/hecks/hecks/adapters/landed_work.adapter +7 -0
  679. data/lib/hecks/hecks/adapters/landed_work.rb +52 -0
  680. data/lib/hecks/hecks/adapters/ledger/chain.rb +63 -0
  681. data/lib/hecks/hecks/adapters/ledger/journal.rb +105 -0
  682. data/lib/hecks/hecks/adapters/ledger.adapter +7 -0
  683. data/lib/hecks/hecks/adapters/ledger.rb +89 -0
  684. data/lib/hecks/hecks/adapters/local_files.rb +21 -7
  685. data/lib/hecks/hecks/adapters/pg_admin.rb +7 -1
  686. data/lib/hecks/hecks/adapters/process_pool.rb +3 -2
  687. data/lib/hecks/hecks/adapters/push_size.rb +57 -0
  688. data/lib/hecks/hecks/adapters/qa_quiescing.adapter +7 -0
  689. data/lib/hecks/hecks/adapters/qa_quiescing.rb +57 -0
  690. data/lib/hecks/hecks/adapters/rails_app_files.rb +64 -0
  691. data/lib/hecks/hecks/adapters/rehearsal/audit_report.rb +4 -1
  692. data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +39 -12
  693. data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +65 -5
  694. data/lib/hecks/hecks/adapters/rehearsal/postgres_census.rb +4 -4
  695. data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +30 -7
  696. data/lib/hecks/hecks/adapters/rehearsal/postgres_store.rb +4 -1
  697. data/lib/hecks/hecks/adapters/rehearsal/rust_audit.rb +1 -1
  698. data/lib/hecks/hecks/adapters/rehearsal/subject.rb +44 -9
  699. data/lib/hecks/hecks/adapters/rust_toolchain.rb +15 -4
  700. data/lib/hecks/hecks/adapters/rust_workspace.rb +1 -1
  701. data/lib/hecks/hecks/adapters/settings_reading/disposable_ledger.rb +32 -0
  702. data/lib/hecks/hecks/adapters/settings_reading/push_nudge.rb +26 -0
  703. data/lib/hecks/hecks/adapters/settings_reading/stores.rb +76 -0
  704. data/lib/hecks/hecks/adapters/settings_reading/test_scope.rb +28 -0
  705. data/lib/hecks/hecks/adapters/settings_reading.adapter +7 -0
  706. data/lib/hecks/hecks/adapters/settings_reading.rb +168 -0
  707. data/lib/hecks/hecks/adapters/shell.rb +3 -0
  708. data/lib/hecks/hecks/adapters/site_toolchain.rb +8 -8
  709. data/lib/hecks/hecks/adapters/started_work.rb +42 -0
  710. data/lib/hecks/hecks/adapters/terminal.rb +12 -10
  711. data/lib/hecks/hecks/adapters/where_reading.adapter +7 -0
  712. data/lib/hecks/hecks/adapters/where_reading.rb +64 -0
  713. data/lib/hecks/hecks/adapters/work_areas.rb +57 -0
  714. data/lib/hecks/hecks/adapters/work_limits.rb +57 -0
  715. data/lib/hecks/hecks/adapters/world_clearing.rb +2 -1
  716. data/lib/hecks/hecks/build/build.bluebook +576 -0
  717. data/lib/hecks/hecks/check/check.bluebook +966 -0
  718. data/lib/hecks/hecks/codebase.behaviors +72 -4
  719. data/lib/hecks/hecks/composer.behaviors +10 -10
  720. data/lib/hecks/hecks/context_map.hecksagon +7 -6
  721. data/lib/hecks/hecks/custodian.behaviors +124 -3
  722. data/lib/hecks/hecks/delegate/delegate.bluebook +355 -0
  723. data/lib/hecks/hecks/deliver/deliver.bluebook +945 -0
  724. data/lib/hecks/hecks/deploy.behaviors +1 -1
  725. data/lib/hecks/hecks/deploy.world +3 -4
  726. data/lib/hecks/hecks/evolve/evolve.bluebook +788 -0
  727. data/lib/hecks/hecks/find/find.bluebook +81 -0
  728. data/lib/hecks/hecks/hecks.behaviors +2 -2
  729. data/lib/hecks/hecks/hecks.bluebook +0 -138
  730. data/lib/hecks/hecks/hecks.contextmap +27 -0
  731. data/lib/hecks/hecks/hecks.hecksagon +312 -23
  732. data/lib/hecks/hecks/hecks.world +54 -27
  733. data/lib/hecks/hecks/implicit_moves.json +24 -0
  734. data/lib/hecks/hecks/migrate/migrate.bluebook +1066 -0
  735. data/lib/hecks/hecks/operate/operate.bluebook +695 -0
  736. data/lib/hecks/hecks/quiesce/quiesce.bluebook +117 -0
  737. data/lib/hecks/hecks/rehearse/rehearse.bluebook +133 -0
  738. data/lib/hecks/hecks/release/release.bluebook +916 -0
  739. data/lib/hecks/hecks/serve/serve.bluebook +134 -0
  740. data/lib/hecks/hecks/shapes.json +3 -0
  741. data/lib/hecks/hecks/site.behaviors +1 -1
  742. data/lib/hecks/hecks/site.world +3 -3
  743. data/lib/hecks/hecks/start/start.bluebook +194 -0
  744. data/lib/hecks/hecks/test/test.bluebook +638 -0
  745. data/lib/hecks/hecks/tickets.world +14 -0
  746. data/lib/hecks/ir.rb +8 -1
  747. data/lib/hecks/kernel_capabilities/templates.rb +4 -3
  748. data/lib/hecks/kernel_capabilities.rb +6 -6
  749. data/lib/hecks/language/adapter.bluebook +61 -0
  750. data/lib/hecks/language/bluebook/aggregate.bluebook +58 -6
  751. data/lib/hecks/language/bluebook/attaches/paging.bluebook +1 -1
  752. data/lib/hecks/language/bluebook/bluebook.behaviors +6 -6
  753. data/lib/hecks/language/bluebook/bluebook.bluebook +62 -51
  754. data/lib/hecks/language/bluebook/bluebook.hecksagon +1 -1
  755. data/lib/hecks/language/bluebook/command.bluebook +69 -2
  756. data/lib/hecks/language/bluebook/entity.bluebook +2 -2
  757. data/lib/hecks/language/bluebook/policy.behaviors +2 -2
  758. data/lib/hecks/language/bluebook/policy.bluebook +3 -3
  759. data/lib/hecks/language/bluebook/process_manager.bluebook +17 -4
  760. data/lib/hecks/language/bluebook/projection.bluebook +11 -10
  761. data/lib/hecks/language/bluebook/query.bluebook +1 -0
  762. data/lib/hecks/language/bluebook/shape.bluebook +1 -1
  763. data/lib/hecks/language/bluebook/syntax.bluebook +3 -1
  764. data/lib/hecks/language/bluebook/vocabulary.bluebook +417 -16
  765. data/lib/hecks/language/context_map/context_map.bluebook +90 -0
  766. data/lib/hecks/language/hecksagon/domain_port.bluebook +3 -2
  767. data/lib/hecks/language/hecksagon/hecksagon.bluebook +8 -8
  768. data/lib/hecks/language/hecksagon/port_operation.bluebook +14 -0
  769. data/lib/hecks/language/kinds/run.kind +120 -0
  770. data/lib/hecks/language/oidc.json +20 -0
  771. data/lib/hecks/language/port.bluebook +1 -1
  772. data/lib/hecks/language/sentence/sentence.bluebook +210 -0
  773. data/lib/hecks/language/translation/translation_aggregate.bluebook +24 -0
  774. data/lib/hecks/language/world/world.bluebook +6 -6
  775. data/lib/hecks/literal.rb +8 -2
  776. data/lib/hecks/module_folders.rb +31 -0
  777. data/lib/hecks/naming.rb +44 -14
  778. data/lib/hecks/persistence_legacy_fixture/postgres_stores.rb +7 -6
  779. data/lib/hecks/persistence_legacy_fixture/regenerate.rb +6 -2
  780. data/lib/hecks/ports/access_control.rb +2 -2
  781. data/lib/hecks/ports/agent/answers.rb +2 -2
  782. data/lib/hecks/ports/agent.rb +2 -2
  783. data/lib/hecks/ports/authentication.port +6 -0
  784. data/lib/hecks/ports/authentication.rb +67 -0
  785. data/lib/hecks/ports/authorization.rb +3 -3
  786. data/lib/hecks/ports/dispatch.port +5 -0
  787. data/lib/hecks/ports/dispatch.rb +28 -0
  788. data/lib/hecks/ports/key_vault.rb +10 -7
  789. data/lib/hecks/ports/loading.rb +23 -7
  790. data/lib/hecks/ports/persistence/append_only.rb +44 -9
  791. data/lib/hecks/ports/persistence/binding_policy.rb +73 -12
  792. data/lib/hecks/ports/persistence/codec_boundary.rb +4 -3
  793. data/lib/hecks/ports/persistence/mint_door.rb +74 -0
  794. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +16 -15
  795. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +9 -6
  796. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +11 -11
  797. data/lib/hecks/ports/persistence/plugins/era/era_status.rb +48 -0
  798. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +1 -1
  799. data/lib/hecks/ports/persistence/plugins/era/projected_bluebook.rb +127 -0
  800. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +6 -6
  801. data/lib/hecks/ports/persistence/plugins/era/translation/additive_shape.rb +107 -0
  802. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +9 -3
  803. data/lib/hecks/ports/persistence/plugins/era/translation/compose.rb +155 -0
  804. data/lib/hecks/ports/persistence/plugins/era/translation/compose_render.rb +79 -0
  805. data/lib/hecks/ports/persistence/plugins/era/translation/edge_files.rb +49 -0
  806. data/lib/hecks/ports/persistence/plugins/era/translation/edge_route.rb +61 -0
  807. data/lib/hecks/ports/persistence/plugins/era/translation/implicit_edge.rb +67 -0
  808. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +1 -1
  809. data/lib/hecks/ports/persistence/plugins/era/translation/reconcile.rb +130 -0
  810. data/lib/hecks/ports/persistence/plugins/era/translation/reconcile_files.rb +35 -0
  811. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +32 -19
  812. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +13 -5
  813. data/lib/hecks/ports/persistence/plugins/era/translation.rb +5 -0
  814. data/lib/hecks/ports/persistence/plugins/era.rb +1 -1
  815. data/lib/hecks/ports/persistence/remote_runtime.rb +3 -2
  816. data/lib/hecks/ports/persistence/repository_factory.rb +11 -3
  817. data/lib/hecks/ports/persistence/set_statement.rb +29 -0
  818. data/lib/hecks/ports/persistence/set_transforming.rb +30 -0
  819. data/lib/hecks/ports/persistence/state_codec.rb +3 -3
  820. data/lib/hecks/ports/persistence/stored_name_guard.rb +36 -0
  821. data/lib/hecks/ports/persistence.rb +2 -4
  822. data/lib/hecks/ports/projection.rb +4 -1
  823. data/lib/hecks/ports/query/cursor.rb +131 -0
  824. data/lib/hecks/ports/query/in_memory.rb +17 -10
  825. data/lib/hecks/ports/query.rb +4 -2
  826. data/lib/hecks/ports.rb +1 -0
  827. data/lib/hecks/projection_files/expression_tables.rb +3 -3
  828. data/lib/hecks/projection_files/sentence_table.rb +17 -0
  829. data/lib/hecks/projection_files.rb +16 -13
  830. data/lib/hecks/projections/bootstrap_table.rb +3 -3
  831. data/lib/hecks/projections/ci_pipeline/table/checks.rb +137 -0
  832. data/lib/hecks/projections/ci_pipeline/table/lookup.rb +59 -0
  833. data/lib/hecks/projections/ci_pipeline/table/refusals.rb +58 -0
  834. data/lib/hecks/projections/ci_pipeline/table/rows.rb +119 -0
  835. data/lib/hecks/projections/ci_pipeline/table/tree.rb +30 -0
  836. data/lib/hecks/projections/ci_pipeline/table/typing.rb +92 -0
  837. data/lib/hecks/projections/ci_pipeline/table.rb +112 -0
  838. data/lib/hecks/projections/ci_pipeline/workflows.rb +162 -0
  839. data/lib/hecks/projections/ci_pipeline/yaml_text.rb +104 -0
  840. data/lib/hecks/projections/command_schema/shape.rb +95 -0
  841. data/lib/hecks/projections/command_schema.rb +74 -0
  842. data/lib/hecks/projections/context_map_diagram.rb +135 -0
  843. data/lib/hecks/projections/context_relationships.rb +125 -0
  844. data/lib/hecks/projections/deploy/box/access.rb +7 -4
  845. data/lib/hecks/projections/deploy/box/caddy.rb +2 -1
  846. data/lib/hecks/projections/deploy/box/compose.rb +4 -8
  847. data/lib/hecks/projections/deploy/box/edge.rb +2 -1
  848. data/lib/hecks/projections/deploy/box/hosting.rb +5 -3
  849. data/lib/hecks/projections/deploy/box/hosting_parts.rb +27 -12
  850. data/lib/hecks/projections/deploy/box/logging.rb +2 -3
  851. data/lib/hecks/projections/deploy/box/roll_recipe.rb +2 -4
  852. data/lib/hecks/projections/deploy/box/roll_scripts.rb +2 -17
  853. data/lib/hecks/projections/deploy/box/settings/containers.rb +29 -15
  854. data/lib/hecks/projections/deploy/box/settings/patterns.rb +5 -6
  855. data/lib/hecks/projections/deploy/box/settings/types.rb +5 -10
  856. data/lib/hecks/projections/deploy/box/settings.rb +9 -8
  857. data/lib/hecks/projections/deploy/box/stack_yaml.rb +10 -14
  858. data/lib/hecks/projections/deploy/box/templates/box.yaml.tmpl +0 -1
  859. data/lib/hecks/projections/deploy/box/templates/deploy-service.sh.tmpl +16 -3
  860. data/lib/hecks/projections/deploy/box/templates/hosting.mk.tmpl +7 -6
  861. data/lib/hecks/projections/deploy/box/templates/render-compose-taskdef.sh.tmpl +2 -6
  862. data/lib/hecks/projections/deploy/box/templates/render-compose.sh.tmpl +2 -6
  863. data/lib/hecks/projections/deploy/box.rb +17 -10
  864. data/lib/hecks/projections/deploy/fargate/assembly.rb +7 -2
  865. data/lib/hecks/projections/deploy/fargate/cdn/options.rb +4 -1
  866. data/lib/hecks/projections/deploy/fargate/check.rb +3 -1
  867. data/lib/hecks/projections/deploy/fargate/containers/rendering.rb +8 -5
  868. data/lib/hecks/projections/deploy/fargate/containers/routing.rb +2 -1
  869. data/lib/hecks/projections/deploy/fargate/monitoring/reading.rb +4 -1
  870. data/lib/hecks/projections/deploy/fargate/sections.rb +7 -4
  871. data/lib/hecks/projections/deploy/fargate/settings/ids.rb +4 -1
  872. data/lib/hecks/projections/deploy/fargate/settings/tuning.rb +13 -4
  873. data/lib/hecks/projections/deploy/fargate/stack/blocks.rb +6 -1
  874. data/lib/hecks/projections/deploy/fargate/stack/readers.rb +2 -1
  875. data/lib/hecks/projections/deploy/fargate/stack.rb +11 -6
  876. data/lib/hecks/projections/deploy/fargate/yaml.rb +10 -3
  877. data/lib/hecks/projections/deploy/fargate.rb +8 -3
  878. data/lib/hecks/projections/deploy/lambda/stack/blocks.rb +4 -2
  879. data/lib/hecks/projections/deploy/lambda/stack/deploy_chain.rb +2 -1
  880. data/lib/hecks/projections/deploy/lambda/stack/reading.rb +12 -9
  881. data/lib/hecks/projections/deploy/lambda/stack/reading_web.rb +7 -2
  882. data/lib/hecks/projections/deploy/lambda/stack/web.rb +2 -1
  883. data/lib/hecks/projections/deploy/lambda.rb +8 -3
  884. data/lib/hecks/projections/deploy/preview/containers.rb +14 -1
  885. data/lib/hecks/projections/deploy/preview/preview.sh.erb +2 -2
  886. data/lib/hecks/projections/deploy/preview/routing_yaml.rb +8 -6
  887. data/lib/hecks/projections/deploy/preview/settings.rb +23 -5
  888. data/lib/hecks/projections/deploy/preview/task_yaml.rb +1 -1
  889. data/lib/hecks/projections/deploy/preview/yaml_text.rb +2 -1
  890. data/lib/hecks/projections/deploy/scripts/settings.rb +5 -2
  891. data/lib/hecks/projections/deploy/shared.rb +12 -1
  892. data/lib/hecks/projections/deploy/shared_database_instance.rb +1 -1
  893. data/lib/hecks/projections/deploy/smoke.rb +3 -1
  894. data/lib/hecks/projections/deploy/template_diff/normalizer.rb +4 -1
  895. data/lib/hecks/projections/deploy/templates/fargate/Makefile.tmpl +1 -1
  896. data/lib/hecks/projections/deploy/templates/lambda/Makefile.tmpl +3 -3
  897. data/lib/hecks/projections/deploy/templates/lambda/mint_era_shared.tmpl +2 -2
  898. data/lib/hecks/projections/deploy/templates/lambda/nat_comment_oauth.tmpl +1 -1
  899. data/lib/hecks/projections/deploy/vercel.rb +1 -1
  900. data/lib/hecks/projections/diagrams/dispatch.rb +5 -3
  901. data/lib/hecks/projections/diagrams/statecharts.rb +8 -3
  902. data/lib/hecks/projections/diagrams/structure.rb +9 -6
  903. data/lib/hecks/projections/diagrams.rb +9 -7
  904. data/lib/hecks/projections/glossary/entries.rb +4 -4
  905. data/lib/hecks/projections/glossary/html/page.rb +4 -1
  906. data/lib/hecks/projections/glossary/html/parser.rb +13 -4
  907. data/lib/hecks/projections/glossary/html/sections.rb +4 -1
  908. data/lib/hecks/projections/glossary/html.rb +10 -3
  909. data/lib/hecks/projections/glossary/markdown.rb +4 -3
  910. data/lib/hecks/projections/glossary/mermaid.rb +8 -5
  911. data/lib/hecks/projections/glossary/sections.rb +1 -1
  912. data/lib/hecks/projections/glossary/sensitivity.rb +11 -4
  913. data/lib/hecks/projections/glossary/sentences/actions.rb +5 -2
  914. data/lib/hecks/projections/glossary/sentences/queries.rb +2 -1
  915. data/lib/hecks/projections/glossary/sentences/value_objects.rb +15 -6
  916. data/lib/hecks/projections/glossary/sentences.rb +6 -3
  917. data/lib/hecks/projections/glossary/slugs.rb +8 -1
  918. data/lib/hecks/projections/glossary.rb +3 -3
  919. data/lib/hecks/projections/model.rb +10 -6
  920. data/lib/hecks/projections/oidc.rb +19 -7
  921. data/lib/hecks/projections/parser_table.rb +10 -10
  922. data/lib/hecks/projections/phrase_table.rb +105 -0
  923. data/lib/hecks/projections/plain_english.rb +14 -4
  924. data/lib/hecks/projections/rails_app.rb +77 -0
  925. data/lib/hecks/projections/reference.rb +2 -2
  926. data/lib/hecks/projections/rust_vocabulary/arguments.rb +1 -1
  927. data/lib/hecks/projections/rust_vocabulary/tables.rb +20 -0
  928. data/lib/hecks/projections/rust_vocabulary.rb +14 -17
  929. data/lib/hecks/projections/site/admin.rb +7 -7
  930. data/lib/hecks/projections/site/admin_cms.rb +1 -1
  931. data/lib/hecks/projections/site/cms_editor/blocks.rb +5 -2
  932. data/lib/hecks/projections/site/cms_editor/checks.rb +9 -6
  933. data/lib/hecks/projections/site/cms_editor/color.rb +4 -1
  934. data/lib/hecks/projections/site/cms_editor/composition.rb +20 -18
  935. data/lib/hecks/projections/site/cms_editor/destructive.rb +4 -1
  936. data/lib/hecks/projections/site/cms_editor/drafts.rb +6 -2
  937. data/lib/hecks/projections/site/cms_editor/key_kinds.rb +4 -1
  938. data/lib/hecks/projections/site/cms_editor/limits.rb +1 -1
  939. data/lib/hecks/projections/site/cms_editor/media.rb +1 -1
  940. data/lib/hecks/projections/site/cms_editor/moment.rb +3 -1
  941. data/lib/hecks/projections/site/cms_editor/pickers.rb +1 -1
  942. data/lib/hecks/projections/site/cms_editor/preview.rb +10 -4
  943. data/lib/hecks/projections/site/cms_editor/relations.rb +3 -2
  944. data/lib/hecks/projections/site/cms_editor/scale_checks.rb +32 -25
  945. data/lib/hecks/projections/site/cms_editor/scheduling.rb +1 -1
  946. data/lib/hecks/projections/site/cms_editor/schema.rb +31 -29
  947. data/lib/hecks/projections/site/cms_editor/setting.rb +34 -27
  948. data/lib/hecks/projections/site/cms_editor/targets.rb +9 -6
  949. data/lib/hecks/projections/site/cms_editor/templates/src/app.ts.tmpl +3 -3
  950. data/lib/hecks/projections/site/cms_editor/templates/src/commands.ts.tmpl +2 -2
  951. data/lib/hecks/projections/site/cms_editor/templates/src/host.ts.tmpl +1 -1
  952. data/lib/hecks/projections/site/cms_editor/templates/src/media/handler.ts.tmpl +4 -4
  953. data/lib/hecks/projections/site/cms_editor/templates/src/names.ts.tmpl +14 -14
  954. data/lib/hecks/projections/site/cms_editor/templates/src/schema.ts.tmpl +12 -12
  955. data/lib/hecks/projections/site/cms_editor/templates/src/ui/pages.ts.tmpl +12 -12
  956. data/lib/hecks/projections/site/cms_editor/templates/src/ui/pickers.ts.tmpl +1 -1
  957. data/lib/hecks/projections/site/cms_editor/templates/src/ui/schedule.ts.tmpl +2 -2
  958. data/lib/hecks/projections/site/cms_editor/templates/src/ui/shell.ts.tmpl +11 -11
  959. data/lib/hecks/projections/site/cms_editor/templates/src/ui/world.ts.tmpl +3 -3
  960. data/lib/hecks/projections/site/cms_editor/theme_checks.rb +8 -2
  961. data/lib/hecks/projections/site/cms_editor.rb +19 -16
  962. data/lib/hecks/projections/site/edge/checks.rb +1 -1
  963. data/lib/hecks/projections/site/edge/function_checks.rb +1 -1
  964. data/lib/hecks/projections/site/edge/function_code.rb +4 -1
  965. data/lib/hecks/projections/site/edge/pattern.rb +4 -1
  966. data/lib/hecks/projections/site/edge/route_checks.rb +2 -2
  967. data/lib/hecks/projections/site/edge/rules.rb +1 -1
  968. data/lib/hecks/projections/site/edge.rb +14 -8
  969. data/lib/hecks/projections/site/live_edge.rb +2 -2
  970. data/lib/hecks/projections/site/payload/fields/definitions.rb +1 -1
  971. data/lib/hecks/projections/site/payload/lifecycle/text.rb +1 -1
  972. data/lib/hecks/projections/site/payload/model.rb +16 -12
  973. data/lib/hecks/projections/site/payload/specs.rb +4 -2
  974. data/lib/hecks/projections/site/payload_driver.rb +9 -8
  975. data/lib/hecks/projections/site/probe.rb +12 -3
  976. data/lib/hecks/projections/site/regions.rb +9 -1
  977. data/lib/hecks/projections/site/role_probe.rb +7 -7
  978. data/lib/hecks/projections/site/root_rows.rb +4 -4
  979. data/lib/hecks/projections/site/routes_ts/listings.rb +13 -6
  980. data/lib/hecks/projections/site/routes_ts.rb +6 -3
  981. data/lib/hecks/projections/site/site_admin.rb +1 -1
  982. data/lib/hecks/projections/site/site_cdn.rb +3 -2
  983. data/lib/hecks/projections/site/site_host.rb +1 -1
  984. data/lib/hecks/projections/site/site_root.rb +4 -4
  985. data/lib/hecks/projections/site/table/checks.rb +4 -1
  986. data/lib/hecks/projections/site/table/navigation_checks.rb +2 -2
  987. data/lib/hecks/projections/site/table/row_builder.rb +7 -7
  988. data/lib/hecks/projections/site/table/row_defaults.rb +1 -1
  989. data/lib/hecks/projections/site/table.rb +32 -26
  990. data/lib/hecks/projections/statements/actions.rb +4 -1
  991. data/lib/hecks/projections/statements/relationships.rb +15 -2
  992. data/lib/hecks/projections/statements.rb +2 -2
  993. data/lib/hecks/projections/undo.rb +46 -0
  994. data/lib/hecks/projections/vocabulary.rb +6 -6
  995. data/lib/hecks/projections/world_skeleton.rb +68 -0
  996. data/lib/hecks/projections.rb +7 -0
  997. data/lib/hecks/projector/cli_audience.rb +12 -9
  998. data/lib/hecks/projector/cli_projector/claims.rb +19 -8
  999. data/lib/hecks/projector/cli_projector/command_help.rb +6 -1
  1000. data/lib/hecks/projector/cli_projector/command_specs.rb +20 -6
  1001. data/lib/hecks/projector/cli_projector/completion.rb +87 -0
  1002. data/lib/hecks/projector/cli_projector/everyday.rb +40 -0
  1003. data/lib/hecks/projector/cli_projector/json_listing.rb +77 -0
  1004. data/lib/hecks/projector/cli_projector/listing.rb +20 -8
  1005. data/lib/hecks/projector/cli_projector/modules.rb +39 -0
  1006. data/lib/hecks/projector/cli_projector/option_specs.rb +1 -1
  1007. data/lib/hecks/projector/cli_projector/query_specs.rb +2 -1
  1008. data/lib/hecks/projector/cli_projector/renames.rb +58 -0
  1009. data/lib/hecks/projector/cli_projector/search.rb +74 -0
  1010. data/lib/hecks/projector/cli_projector/usage.rb +33 -12
  1011. data/lib/hecks/projector/cli_projector.rb +26 -3
  1012. data/lib/hecks/projector/docs_projector/behaviour.rb +5 -4
  1013. data/lib/hecks/projector/docs_projector/closing.rb +3 -3
  1014. data/lib/hecks/projector/docs_projector.rb +12 -7
  1015. data/lib/hecks/projector/exporter/adapters.rb +15 -2
  1016. data/lib/hecks/projector/exporter/bindings.rb +39 -134
  1017. data/lib/hecks/projector/exporter/plugins.rb +55 -0
  1018. data/lib/hecks/projector/exporter.rb +2 -0
  1019. data/lib/hecks/projector/narrate_projector/commands.rb +4 -3
  1020. data/lib/hecks/projector/narrate_projector/reactions.rb +2 -2
  1021. data/lib/hecks/projector/narrate_projector.rb +9 -8
  1022. data/lib/hecks/projector/target.rb +8 -8
  1023. data/lib/hecks/projector.rb +9 -9
  1024. data/lib/hecks/quality_control/adapters/agent.rb +2 -1
  1025. data/lib/hecks/quality_control/adapters/ci_head.adapter +8 -0
  1026. data/lib/hecks/quality_control/adapters/github_checks.adapter +1 -1
  1027. data/lib/hecks/quality_control/adapters/github_issues.adapter +1 -1
  1028. data/lib/hecks/quality_control/adapters/github_issues.rb +6 -2
  1029. data/lib/hecks/quality_control/adapters/image_roll.adapter +9 -0
  1030. data/lib/hecks/quality_control/adapters/image_roll.rb +158 -0
  1031. data/lib/hecks/quality_control/adapters/main_maintenance.adapter +8 -0
  1032. data/lib/hecks/quality_control/adapters/main_maintenance.rb +275 -0
  1033. data/lib/hecks/quality_control/adapters/pump_tools.adapter +7 -0
  1034. data/lib/hecks/quality_control/adapters/pump_tools.rb +18 -0
  1035. data/lib/hecks/quality_control/adapters/push_due_reader.adapter +8 -0
  1036. data/lib/hecks/quality_control/adapters/push_due_reader.rb +99 -0
  1037. data/lib/hecks/quality_control/adapters/qa_auto_mint.rb +183 -0
  1038. data/lib/hecks/quality_control/adapters/qa_era_mint.rb +94 -0
  1039. data/lib/hecks/quality_control/adapters/qa_quiesce_marker.rb +126 -0
  1040. data/lib/hecks/quality_control/adapters/run_keys.rb +36 -0
  1041. data/lib/hecks/quality_control/adapters/run_reader.adapter +7 -0
  1042. data/lib/hecks/quality_control/adapters/run_reader.rb +188 -0
  1043. data/lib/hecks/quality_control/adapters/target_tools.rb +9 -3
  1044. data/lib/hecks/quality_control/cli/child.rb +2 -0
  1045. data/lib/hecks/quality_control/cli/qa_discover_external_domains/scanning.rb +1 -1
  1046. data/lib/hecks/quality_control/cli/qa_domain_novelty/verdict.rb +1 -1
  1047. data/lib/hecks/quality_control/cli/qa_domain_novelty.rb +3 -3
  1048. data/lib/hecks/quality_control/cli/qa_event_pump/attached.rb +67 -0
  1049. data/lib/hecks/quality_control/cli/qa_event_pump/consumer.rb +103 -0
  1050. data/lib/hecks/quality_control/cli/qa_event_pump/cursors.rb +60 -0
  1051. data/lib/hecks/quality_control/cli/qa_event_pump/reactions.rb +46 -0
  1052. data/lib/hecks/quality_control/cli/qa_event_pump/reader.rb +30 -0
  1053. data/lib/hecks/quality_control/cli/qa_event_pump.rb +76 -0
  1054. data/lib/hecks/quality_control/cli/qa_fix_main_bug/publishing.rb +79 -0
  1055. data/lib/hecks/quality_control/cli/qa_fix_main_bug.rb +167 -0
  1056. data/lib/hecks/quality_control/cli/qa_generated_domains/evaluation.rb +13 -3
  1057. data/lib/hecks/quality_control/cli/qa_generated_domains/promotion.rb +7 -4
  1058. data/lib/hecks/quality_control/cli/qa_generated_domains.rb +4 -1
  1059. data/lib/hecks/quality_control/cli/qa_log_bug/proof.rb +16 -3
  1060. data/lib/hecks/quality_control/cli/qa_main_gate/gate_run.rb +52 -0
  1061. data/lib/hecks/quality_control/cli/qa_main_gate/logging.rb +70 -0
  1062. data/lib/hecks/quality_control/cli/qa_main_gate/shell.rb +47 -0
  1063. data/lib/hecks/quality_control/cli/qa_main_gate/sweeping.rb +59 -0
  1064. data/lib/hecks/quality_control/cli/qa_main_gate/worktree.rb +62 -0
  1065. data/lib/hecks/quality_control/cli/qa_main_gate.rb +135 -0
  1066. data/lib/hecks/quality_control/cli/qa_mine_combinations/booting.rb +4 -2
  1067. data/lib/hecks/quality_control/cli/qa_mutate.rb +161 -0
  1068. data/lib/hecks/quality_control/cli/qa_postgres_role.rb +1 -1
  1069. data/lib/hecks/quality_control/cli/qa_pr_check/checking.rb +2 -3
  1070. data/lib/hecks/quality_control/cli/qa_push_nudge.rb +48 -0
  1071. data/lib/hecks/quality_control/cli/qa_seed_angles/seed.rb +1 -1
  1072. data/lib/hecks/quality_control/cli/qa_sweep/all_mode/report.rb +4 -1
  1073. data/lib/hecks/quality_control/cli/qa_sweep/parsing.rb +2 -1
  1074. data/lib/hecks/quality_control/cli/qa_sweep/seat.rb +1 -1
  1075. data/lib/hecks/quality_control/cli/qa_sweep.rb +4 -1
  1076. data/lib/hecks/quality_control/cli/qa_tick/event_pump_step.rb +34 -0
  1077. data/lib/hecks/quality_control/cli/qa_tick/output.rb +43 -26
  1078. data/lib/hecks/quality_control/cli/qa_tick/preflight.rb +12 -3
  1079. data/lib/hecks/quality_control/cli/qa_tick/push_nudge_step.rb +28 -0
  1080. data/lib/hecks/quality_control/cli/qa_tick/quiesce_guard.rb +51 -0
  1081. data/lib/hecks/quality_control/cli/qa_tick.rb +52 -8
  1082. data/lib/hecks/quality_control/quality_control.behaviors +65 -0
  1083. data/lib/hecks/quality_control/quality_control.bluebook +1224 -103
  1084. data/lib/hecks/quality_control/quality_control.hecksagon +14 -3
  1085. data/lib/hecks/quality_control/quality_control.ports.hecksagon +141 -2
  1086. data/lib/hecks/query_ir/rules.rb +9 -9
  1087. data/lib/hecks/query_ir/touchpoints.rb +10 -9
  1088. data/lib/hecks/query_ir.rb +3 -3
  1089. data/lib/hecks/query_specification/common/comparison.rb +1 -1
  1090. data/lib/hecks/query_specification/common/dsl.rb +3 -4
  1091. data/lib/hecks/query_specification/hop_path.rb +7 -4
  1092. data/lib/hecks/rails_app/bluebook/rails_app.bluebook +60 -0
  1093. data/lib/hecks/rails_app/bluebook/rails_app.hecksagon +4 -0
  1094. data/lib/hecks/refused.rb +125 -0
  1095. data/lib/hecks/release/approval.rb +15 -5
  1096. data/lib/hecks/release/conditions.rb +46 -0
  1097. data/lib/hecks/release/gem_pin.rb +7 -3
  1098. data/lib/hecks/release/lane.rb +1 -1
  1099. data/lib/hecks/release/runner/clean_tree.rb +4 -1
  1100. data/lib/hecks/release/runner/console.rb +2 -1
  1101. data/lib/hecks/release/runner/gem_publisher.rb +4 -4
  1102. data/lib/hecks/release/version_commit.rb +48 -0
  1103. data/lib/hecks/router/aggregate_reader.rb +3 -4
  1104. data/lib/hecks/router/namespace_installer.rb +31 -17
  1105. data/lib/hecks/router.rb +2 -10
  1106. data/lib/hecks/runtime/adapter_lookup.rb +30 -7
  1107. data/lib/hecks/runtime/adapter_namespace.rb +21 -0
  1108. data/lib/hecks/runtime/asking.rb +30 -0
  1109. data/lib/hecks/runtime/caller.rb +22 -0
  1110. data/lib/hecks/runtime/capability_graph.rb +2 -1
  1111. data/lib/hecks/runtime/collecting.rb +86 -0
  1112. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +13 -8
  1113. data/lib/hecks/runtime/command_interpreter/delegation.rb +46 -17
  1114. data/lib/hecks/runtime/command_interpreter/hydration.rb +22 -25
  1115. data/lib/hecks/runtime/command_interpreter/list_mutations.rb +2 -1
  1116. data/lib/hecks/runtime/command_interpreter/persisting.rb +23 -14
  1117. data/lib/hecks/runtime/command_interpreter.rb +19 -12
  1118. data/lib/hecks/runtime/command_rules/admissibility.rb +17 -12
  1119. data/lib/hecks/runtime/command_rules/authorization.rb +8 -6
  1120. data/lib/hecks/runtime/command_rules/emission.rb +0 -1
  1121. data/lib/hecks/runtime/command_rules/guard_state.rb +10 -10
  1122. data/lib/hecks/runtime/command_rules/invariants.rb +3 -1
  1123. data/lib/hecks/runtime/command_rules/missing_reference.rb +25 -0
  1124. data/lib/hecks/runtime/command_rules/one_of_references.rb +44 -0
  1125. data/lib/hecks/runtime/command_rules/references.rb +14 -14
  1126. data/lib/hecks/runtime/command_rules.rb +2 -0
  1127. data/lib/hecks/runtime/declarations.rb +66 -0
  1128. data/lib/hecks/runtime/dependency_planning/analyzer.rb +2 -1
  1129. data/lib/hecks/runtime/dependency_planning/expression_reads.rb +3 -3
  1130. data/lib/hecks/runtime/dependency_planning/mutation_analysis.rb +2 -1
  1131. data/lib/hecks/runtime/dispatch_claim.rb +72 -0
  1132. data/lib/hecks/runtime/dispatch_context.rb +41 -0
  1133. data/lib/hecks/runtime/dispatcher/planning.rb +24 -0
  1134. data/lib/hecks/runtime/dispatcher/queries.rb +17 -0
  1135. data/lib/hecks/runtime/dispatcher/routing.rb +40 -12
  1136. data/lib/hecks/runtime/dispatcher/validating.rb +73 -0
  1137. data/lib/hecks/runtime/dispatcher/value_routing.rb +54 -0
  1138. data/lib/hecks/runtime/dispatcher.rb +46 -24
  1139. data/lib/hecks/runtime/dry_run_plan.rb +60 -0
  1140. data/lib/hecks/runtime/entity_element/location.rb +15 -8
  1141. data/lib/hecks/runtime/entity_element/mutations.rb +4 -2
  1142. data/lib/hecks/runtime/entity_interpreter/enforcement.rb +1 -1
  1143. data/lib/hecks/runtime/entity_interpreter/locating.rb +6 -1
  1144. data/lib/hecks/runtime/entity_interpreter/saving.rb +5 -5
  1145. data/lib/hecks/runtime/entity_interpreter.rb +11 -5
  1146. data/lib/hecks/runtime/errors.rb +56 -17
  1147. data/lib/hecks/runtime/identity.rb +9 -0
  1148. data/lib/hecks/runtime/instance.rb +3 -3
  1149. data/lib/hecks/runtime/interpreting.rb +63 -6
  1150. data/lib/hecks/runtime/invocation_builder.rb +7 -5
  1151. data/lib/hecks/runtime/invocation_facts.rb +13 -8
  1152. data/lib/hecks/runtime/loader/boot_steps.rb +3 -2
  1153. data/lib/hecks/runtime/loader/described.rb +4 -0
  1154. data/lib/hecks/runtime/loader.rb +52 -62
  1155. data/lib/hecks/runtime/outbox/consumer_runner.rb +9 -1
  1156. data/lib/hecks/runtime/outbox/redrive.rb +105 -0
  1157. data/lib/hecks/runtime/outbox/relay.rb +36 -43
  1158. data/lib/hecks/runtime/outbox/runnable.rb +62 -0
  1159. data/lib/hecks/runtime/outbox.rb +21 -3
  1160. data/lib/hecks/runtime/policy_interpreter/arguments.rb +7 -1
  1161. data/lib/hecks/runtime/policy_interpreter/fan_out.rb +26 -37
  1162. data/lib/hecks/runtime/policy_interpreter.rb +25 -9
  1163. data/lib/hecks/runtime/port_operation_interpreter.rb +47 -19
  1164. data/lib/hecks/runtime/query_interpreter/arguments.rb +6 -5
  1165. data/lib/hecks/runtime/query_interpreter/outside_answer.rb +1 -1
  1166. data/lib/hecks/runtime/query_interpreter/paging.rb +42 -0
  1167. data/lib/hecks/runtime/query_interpreter/plugin_answer.rb +54 -0
  1168. data/lib/hecks/runtime/query_interpreter/store_specification.rb +32 -0
  1169. data/lib/hecks/runtime/query_interpreter.rb +16 -9
  1170. data/lib/hecks/runtime/reaction_invocation/target_resolution.rb +10 -4
  1171. data/lib/hecks/runtime/reaction_invocation.rb +5 -3
  1172. data/lib/hecks/runtime/read_model_interpreter/head_ordering.rb +1 -1
  1173. data/lib/hecks/runtime/read_model_interpreter/reductions.rb +23 -15
  1174. data/lib/hecks/runtime/read_model_interpreter/shaping.rb +18 -7
  1175. data/lib/hecks/runtime/read_model_interpreter.rb +5 -2
  1176. data/lib/hecks/runtime/reference_hop.rb +3 -2
  1177. data/lib/hecks/runtime/refusal_wording.rb +27 -7
  1178. data/lib/hecks/runtime/registry/adapter_capabilities.rb +43 -0
  1179. data/lib/hecks/runtime/registry/ask_verification.rb +15 -4
  1180. data/lib/hecks/runtime/registry/{chapter_verification.rb → bluebook_verification.rb} +23 -23
  1181. data/lib/hecks/runtime/registry/context_maps.rb +26 -0
  1182. data/lib/hecks/runtime/registry/declarations.rb +38 -6
  1183. data/lib/hecks/runtime/registry/durability_warnings.rb +18 -2
  1184. data/lib/hecks/runtime/registry/hecksagon_rules.rb +42 -25
  1185. data/lib/hecks/runtime/registry/plugin_query_verification.rb +45 -0
  1186. data/lib/hecks/runtime/registry/providers.rb +34 -99
  1187. data/lib/hecks/runtime/registry/query_verification.rb +30 -21
  1188. data/lib/hecks/runtime/registry/runtime_state.rb +14 -3
  1189. data/lib/hecks/runtime/registry/saga_persistence.rb +7 -16
  1190. data/lib/hecks/runtime/registry/verification.rb +9 -5
  1191. data/lib/hecks/runtime/registry/world_defaults.rb +31 -6
  1192. data/lib/hecks/runtime/registry/world_names.rb +99 -0
  1193. data/lib/hecks/runtime/registry.rb +84 -53
  1194. data/lib/hecks/runtime/row_sweep.rb +104 -0
  1195. data/lib/hecks/runtime/saga_interpreter/compensation.rb +15 -4
  1196. data/lib/hecks/runtime/saga_interpreter/correlation.rb +29 -3
  1197. data/lib/hecks/runtime/saga_interpreter/delivery.rb +13 -11
  1198. data/lib/hecks/runtime/saga_interpreter/lifecycle.rb +1 -1
  1199. data/lib/hecks/runtime/saga_interpreter/redrive.rb +141 -0
  1200. data/lib/hecks/runtime/saga_interpreter/transitions.rb +35 -6
  1201. data/lib/hecks/runtime/saga_interpreter.rb +82 -6
  1202. data/lib/hecks/runtime/saga_pending_dispatch.rb +4 -2
  1203. data/lib/hecks/runtime/scalar_field.rb +73 -0
  1204. data/lib/hecks/runtime/tenant_scope.rb +2 -1
  1205. data/lib/hecks/runtime/validation.rb +24 -0
  1206. data/lib/hecks/runtime/value/admission.rb +11 -11
  1207. data/lib/hecks/runtime/value/coercion.rb +43 -15
  1208. data/lib/hecks/runtime/value/field_checks.rb +13 -13
  1209. data/lib/hecks/runtime/value/invariant_violation.rb +4 -1
  1210. data/lib/hecks/runtime/value/references.rb +7 -4
  1211. data/lib/hecks/runtime/value/validation.rb +7 -4
  1212. data/lib/hecks/runtime/wasm_plugin.rb +98 -0
  1213. data/lib/hecks/runtime.rb +14 -14
  1214. data/lib/hecks/rust_build/command_schemas.rb +27 -0
  1215. data/lib/hecks/rust_build/conformance/surface.rb +50 -7
  1216. data/lib/hecks/rust_build/conformance.rb +41 -8
  1217. data/lib/hecks/rust_build/conformance_fuzz.rb +2 -2
  1218. data/lib/hecks/rust_build/domain_name.rb +4 -1
  1219. data/lib/hecks/rust_build/host.rb +15 -9
  1220. data/lib/hecks/rust_build/native_build.rb +2 -1
  1221. data/lib/hecks/rust_build/parser.rb +25 -0
  1222. data/lib/hecks/rust_build/prebuilt_binary.rb +25 -5
  1223. data/lib/hecks/rust_build/project_rust/codegen_run.rb +51 -17
  1224. data/lib/hecks/rust_build/project_rust/declared.rb +80 -0
  1225. data/lib/hecks/rust_build/project_rust/domain_loader.rb +15 -2
  1226. data/lib/hecks/rust_build/project_rust/features_header.rb +22 -0
  1227. data/lib/hecks/rust_build/project_rust/manifest.rb +96 -0
  1228. data/lib/hecks/rust_build/project_rust/meta.rb +36 -0
  1229. data/lib/hecks/rust_build/project_rust/root_mod/notes.rb +1 -1
  1230. data/lib/hecks/rust_build/project_rust/target_ir.rb +12 -3
  1231. data/lib/hecks/rust_build/project_rust.rb +31 -73
  1232. data/lib/hecks/rust_build/target_dir.rb +40 -0
  1233. data/lib/hecks/rust_build/wasm.rb +46 -17
  1234. data/lib/hecks/rust_build/wasm_browser.rb +8 -2
  1235. data/lib/hecks/rust_build/write_if_changed.rb +19 -4
  1236. data/lib/hecks/rust_build.rb +31 -3
  1237. data/lib/hecks/rust_format.rb +32 -2
  1238. data/lib/hecks/secrets.rb +111 -0
  1239. data/lib/hecks/sentence_form.rb +212 -0
  1240. data/lib/hecks/served_options.rb +2 -1
  1241. data/lib/hecks/shape_ledger/edge_hygiene.rb +170 -0
  1242. data/lib/hecks/shape_ledger/scope.rb +51 -0
  1243. data/lib/hecks/shape_ledger/status.rb +89 -0
  1244. data/lib/hecks/shape_ledger.rb +194 -0
  1245. data/lib/hecks/site/bluebook/site.bluebook +40 -62
  1246. data/lib/hecks/site/bluebook/site.ports.hecksagon +1 -1
  1247. data/lib/hecks/sme/bluebook/sme.bluebook +7 -1
  1248. data/lib/hecks/storehouse/audit.rb +38 -5
  1249. data/lib/hecks/storehouse/bluebook_words.rb +35 -0
  1250. data/lib/hecks/storehouse/callers.rb +8 -0
  1251. data/lib/hecks/storehouse/dispatching.rb +20 -6
  1252. data/lib/hecks/storehouse/identity.rb +41 -0
  1253. data/lib/hecks/storehouse/introspection.rb +4 -4
  1254. data/lib/hecks/storehouse/querying.rb +9 -9
  1255. data/lib/hecks/storehouse.rb +38 -10
  1256. data/lib/hecks/switches.rb +21 -0
  1257. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +5 -9
  1258. data/lib/hecks/text.rb +57 -0
  1259. data/lib/hecks/tickets/bluebook/adapters/work_areas.adapter +7 -0
  1260. data/lib/hecks/tickets/bluebook/adapters/work_limits.adapter +7 -0
  1261. data/lib/hecks/tickets/bluebook/tickets.behaviors +70 -0
  1262. data/lib/hecks/tickets/bluebook/tickets.bluebook +350 -76
  1263. data/lib/hecks/tickets/bluebook/tickets.hecksagon +8 -1
  1264. data/lib/hecks/tickets/bluebook/tickets.ports.hecksagon +18 -1
  1265. data/lib/hecks/tools/argument_gate_matrix/dispatching.rb +1 -1
  1266. data/lib/hecks/tools/argument_gate_matrix/rows.rb +7 -7
  1267. data/lib/hecks/tools/argument_gate_matrix/violations.rb +1 -1
  1268. data/lib/hecks/tools/argument_gate_matrix.rb +3 -3
  1269. data/lib/hecks/{cli → tools}/behaviors.rb +1 -1
  1270. data/lib/hecks/{cli → tools}/canonicalise.rb +1 -1
  1271. data/lib/hecks/{cli → tools}/check_engine_agreement.rb +1 -1
  1272. data/lib/hecks/{cli → tools}/check_era.rb +7 -7
  1273. data/lib/hecks/tools/ci_gate_decision.rb +5 -2
  1274. data/lib/hecks/tools/ci_gates.rb +9 -3
  1275. data/lib/hecks/tools/ci_workflows.rb +98 -0
  1276. data/lib/hecks/tools/comment_style/caps_words.rb +11 -8
  1277. data/lib/hecks/tools/comment_style/dictionary.rb +7 -2
  1278. data/lib/hecks/tools/comment_style/fixing.rb +10 -5
  1279. data/lib/hecks/tools/comment_style/long_blocks.rb +1 -1
  1280. data/lib/hecks/tools/comment_style/masking.rb +5 -5
  1281. data/lib/hecks/tools/comment_style/run.rb +4 -1
  1282. data/lib/hecks/tools/comment_style/source_file.rb +10 -3
  1283. data/lib/hecks/tools/comment_style/structure_checks.rb +13 -4
  1284. data/lib/hecks/tools/comment_style/text_checks.rb +1 -1
  1285. data/lib/hecks/tools/comment_style.rb +25 -2
  1286. data/lib/hecks/{cli → tools}/console.rb +12 -7
  1287. data/lib/hecks/tools/deploy_recipe/generation.rb +1 -1
  1288. data/lib/hecks/tools/deploy_recipe/targets.rb +1 -1
  1289. data/lib/hecks/tools/deploy_recipe/worlds.rb +15 -9
  1290. data/lib/hecks/tools/deploy_recipe.rb +17 -10
  1291. data/lib/hecks/tools/deploy_recipe_lint/fixtures.rb +1 -1
  1292. data/lib/hecks/tools/deploy_recipe_lint/makefile.rb +7 -1
  1293. data/lib/hecks/{cli → tools}/deploy_template_diff.rb +1 -1
  1294. data/lib/hecks/{cli → tools}/doc_coverage.rb +2 -2
  1295. data/lib/hecks/{cli → tools}/document.rb +15 -10
  1296. data/lib/hecks/{cli → tools}/domain_stub.rb +1 -1
  1297. data/lib/hecks/{cli → tools}/domain_writer.rb +5 -2
  1298. data/lib/hecks/tools/downstream_model_check.rb +122 -0
  1299. data/lib/hecks/tools/drop_implicit_append_fields.rb +4 -1
  1300. data/lib/hecks/tools/era_projection_backfill.rb +11 -6
  1301. data/lib/hecks/tools/era_reattest.rb +11 -6
  1302. data/lib/hecks/tools/evolve_run.rb +2 -2
  1303. data/lib/hecks/tools/expected_era.rb +139 -0
  1304. data/lib/hecks/{cli → tools}/expression_projection.rb +3 -3
  1305. data/lib/hecks/{cli → tools}/follow.rb +4 -4
  1306. data/lib/hecks/tools/fuzz_sweep/findings.rb +18 -2
  1307. data/lib/hecks/tools/fuzz_sweep/options.rb +45 -6
  1308. data/lib/hecks/tools/fuzz_sweep/pool.rb +12 -1
  1309. data/lib/hecks/tools/fuzz_sweep/shards.rb +155 -0
  1310. data/lib/hecks/tools/fuzz_sweep.rb +28 -10
  1311. data/lib/hecks/tools/gate.rb +65 -34
  1312. data/lib/hecks/tools/gate_memo/local_files.rb +33 -0
  1313. data/lib/hecks/tools/gate_memo/rehearsal.rb +54 -0
  1314. data/lib/hecks/tools/gate_memo/ruby_files.rb +152 -0
  1315. data/lib/hecks/tools/gate_memo/session.rb +114 -0
  1316. data/lib/hecks/tools/gate_memo/standalone.rb +67 -0
  1317. data/lib/hecks/tools/gate_memo.rb +196 -0
  1318. data/lib/hecks/tools/gate_report.rb +56 -0
  1319. data/lib/hecks/{cli → tools}/history.rb +5 -2
  1320. data/lib/hecks/tools/hoist_local_givens/rewriting.rb +3 -2
  1321. data/lib/hecks/tools/hoist_local_givens.rb +2 -4
  1322. data/lib/hecks/{cli → tools}/interview_agent.rb +1 -1
  1323. data/lib/hecks/{cli → tools}/interview_aggregate.rb +1 -1
  1324. data/lib/hecks/{cli → tools}/interview_draft.rb +9 -2
  1325. data/lib/hecks/{cli → tools}/interview_record.rb +8 -4
  1326. data/lib/hecks/{cli → tools}/interview_run.rb +9 -7
  1327. data/lib/hecks/{cli → tools}/interview_session/agent_state.rb +1 -1
  1328. data/lib/hecks/{cli → tools}/interview_session/catalog.rb +1 -1
  1329. data/lib/hecks/{cli → tools}/interview_session/manual_findings.rb +1 -1
  1330. data/lib/hecks/{cli → tools}/interview_session/proposals.rb +6 -3
  1331. data/lib/hecks/{cli → tools}/interview_session.rb +5 -4
  1332. data/lib/hecks/tools/interview_shape/lifecycle.rb +92 -0
  1333. data/lib/hecks/{cli → tools}/interview_shape.rb +20 -74
  1334. data/lib/hecks/{cli → tools}/ir.rb +1 -1
  1335. data/lib/hecks/tools/lanes/files.rb +1 -1
  1336. data/lib/hecks/tools/lanes/prebuilt_workflow.rb +1 -1
  1337. data/lib/hecks/tools/lanes/rows.rb +9 -3
  1338. data/lib/hecks/tools/lanes/ruleset.rb +1 -1
  1339. data/lib/hecks/tools/lanes/watch_workflow.rb +7 -9
  1340. data/lib/hecks/tools/lanes/workflow.rb +2 -2
  1341. data/lib/hecks/tools/lanes.rb +2 -2
  1342. data/lib/hecks/{cli → tools}/mcp.rb +1 -1
  1343. data/lib/hecks/tools/model_check/examination.rb +142 -0
  1344. data/lib/hecks/{cli → tools}/model_check/sources.rb +5 -3
  1345. data/lib/hecks/{cli → tools}/model_check.rb +2 -2
  1346. data/lib/hecks/tools/move_disables/reasons.rb +35 -0
  1347. data/lib/hecks/tools/move_disables.rb +211 -0
  1348. data/lib/hecks/tools/move_former_name.rb +105 -0
  1349. data/lib/hecks/tools/oidc_manifests.rb +16 -19
  1350. data/lib/hecks/{cli → tools}/pattern_cases.rb +1 -1
  1351. data/lib/hecks/tools/persistence_notice.rb +45 -0
  1352. data/lib/hecks/{cli → tools}/present.rb +2 -2
  1353. data/lib/hecks/{cli → tools}/project_cli/launcher_source.rb +39 -27
  1354. data/lib/hecks/{cli → tools}/project_cli/templates.rb +42 -14
  1355. data/lib/hecks/{cli → tools}/project_cli.rb +22 -20
  1356. data/lib/hecks/tools/project_diagrams.rb +59 -0
  1357. data/lib/hecks/{cli → tools}/project_glossary.rb +16 -16
  1358. data/lib/hecks/{cli → tools}/reference.rb +3 -3
  1359. data/lib/hecks/{cli → tools}/refresh_projections.rb +17 -12
  1360. data/lib/hecks/{cli → tools}/refresh_rspec_runtime_baseline.rb +1 -1
  1361. data/lib/hecks/tools/regeneration_run/pool.rb +148 -0
  1362. data/lib/hecks/tools/regeneration_run/scratch.rb +76 -0
  1363. data/lib/hecks/tools/regeneration_run.rb +134 -107
  1364. data/lib/hecks/{cli → tools}/release.rb +1 -1
  1365. data/lib/hecks/{cli → tools}/release_gem.rb +1 -1
  1366. data/lib/hecks/tools/release_only.rb +5 -2
  1367. data/lib/hecks/{cli → tools}/rspec_io_parallel_files.rb +1 -1
  1368. data/lib/hecks/{cli → tools}/rspec_shard_files.rb +1 -1
  1369. data/lib/hecks/{cli → tools}/run.rb +36 -7
  1370. data/lib/hecks/{cli → tools}/run_expectations.rb +1 -1
  1371. data/lib/hecks/tools/rust_comment_style/caps_words.rb +5 -5
  1372. data/lib/hecks/tools/rust_comment_style/fixing.rb +1 -1
  1373. data/lib/hecks/tools/rust_comment_style/masking.rb +7 -4
  1374. data/lib/hecks/tools/rust_comment_style/source_file.rb +7 -2
  1375. data/lib/hecks/tools/rust_comment_style/text_checks.rb +1 -1
  1376. data/lib/hecks/tools/rust_comment_style/tokenizer.rb +4 -1
  1377. data/lib/hecks/{cli → tools}/seed_semantics_corpus.rb +18 -8
  1378. data/lib/hecks/{cli → tools}/serve_query_ir_mcp.rb +1 -1
  1379. data/lib/hecks/{cli → tools}/shape.rb +6 -6
  1380. data/lib/hecks/tools/site_routes/editor_files.rb +16 -13
  1381. data/lib/hecks/tools/site_routes/root_files.rb +14 -13
  1382. data/lib/hecks/tools/site_routes/templates.rb +5 -4
  1383. data/lib/hecks/tools/site_routes.rb +18 -15
  1384. data/lib/hecks/{cli → tools}/smoke_http/checks.rb +1 -1
  1385. data/lib/hecks/{cli → tools}/smoke_http/settings.rb +1 -1
  1386. data/lib/hecks/{cli → tools}/smoke_http/transport.rb +4 -3
  1387. data/lib/hecks/{cli → tools}/smoke_http.rb +1 -1
  1388. data/lib/hecks/{cli → tools}/smoke_test.rb +1 -1
  1389. data/lib/hecks/{cli → tools}/spec_example.rb +1 -1
  1390. data/lib/hecks/tools/stage_progress.rb +54 -0
  1391. data/lib/hecks/{cli → tools}/statements.rb +8 -8
  1392. data/lib/hecks/tools/stores.rb +123 -0
  1393. data/lib/hecks/{cli → tools}/stress_concurrency_specs/reporting.rb +5 -2
  1394. data/lib/hecks/{cli → tools}/stress_concurrency_specs/usage.rb +4 -4
  1395. data/lib/hecks/{cli → tools}/stress_concurrency_specs.rb +3 -3
  1396. data/lib/hecks/tools/tail_merge.rb +3 -3
  1397. data/lib/hecks/tools/tenant_provisioning.rb +29 -6
  1398. data/lib/hecks/tools/tools_doc.rb +22 -20
  1399. data/lib/hecks/tools/translation_audit/domain_loading.rb +3 -3
  1400. data/lib/hecks/tools/translation_audit/edges.rb +21 -16
  1401. data/lib/hecks/tools/translation_audit/verdicts.rb +1 -1
  1402. data/lib/hecks/tools/translation_audit.rb +16 -5
  1403. data/lib/hecks/tools/translation_scaffold/reporting.rb +4 -1
  1404. data/lib/hecks/tools/translation_scaffold.rb +5 -15
  1405. data/lib/hecks/tools.rb +4 -1
  1406. data/lib/hecks/vendoring/git_source.rb +7 -4
  1407. data/lib/hecks/version.rb +1 -1
  1408. data/lib/hecks/vocabulary.rb +147 -10
  1409. data/lib/hecks/workflow_pagination/judge.rb +48 -0
  1410. data/lib/hecks/workflow_pagination/statement.rb +50 -0
  1411. data/lib/hecks/workflow_pagination.rb +58 -0
  1412. data/lib/hecks.rb +39 -18
  1413. data/lib/rubocop/cop/hecks/bare_event_and_query_names.rb +58 -0
  1414. data/lib/rubocop/cop/hecks/correlates_by_aggregate.rb +39 -0
  1415. data/lib/rubocop/cop/hecks/creating_command_sets.rb +75 -0
  1416. data/lib/rubocop/cop/hecks/inline_regexp.rb +29 -0
  1417. data/lib/rubocop/cop/hecks/invariant_expression_language.rb +47 -0
  1418. data/lib/rubocop/cop/hecks/lifecycle_needs_attribute.rb +58 -0
  1419. data/lib/rubocop/cop/hecks/no_id_string_attribute.rb +49 -0
  1420. data/lib/rubocop/cop/hecks/no_transport_in_runtime.rb +78 -0
  1421. data/lib/rubocop/cop/hecks/one_terminal_mark_per_lifecycle.rb +47 -0
  1422. data/lib/rubocop/cop/hecks/rails_model_rules.rb +46 -0
  1423. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +1 -1
  1424. data/lib/rubocop/cop/hecks/train_wreck.rb +69 -0
  1425. data/lib/rubocop/cop/hecks/unnamed_pipeline.rb +51 -0
  1426. data/lib/rubocop/cop/hecks/value_object_shorthand.rb +56 -0
  1427. data/prebuilt/13c04bdc5970295b310f0a980d397ba722232c8da99cc1a0cdb383249921e871.marshal +0 -0
  1428. data/prebuilt/e3857b77f47d9ecc1787b2e1f5136364f3cb3444ef4aea5479f3b98167affea2.marshal +0 -0
  1429. data/qa/settings.yml +32 -0
  1430. data/rust/.gitignore +0 -6
  1431. data/rust/Cargo.lock +8 -1
  1432. data/rust/Cargo.toml +8 -8
  1433. data/rust/build/Cargo.toml +1 -1
  1434. data/rust/build/src/binding_pass.rs +68 -19
  1435. data/rust/build/src/cargo_sync.rs +1 -1
  1436. data/rust/build/src/lineage_pass.rs +4 -71
  1437. data/rust/build/src/pipeline.rs +104 -73
  1438. data/rust/build/src/resolve.rs +86 -29
  1439. data/rust/build/src/seams_pass.rs +117 -99
  1440. data/rust/build/src/world_pass.rs +21 -12
  1441. data/rust/codegen/Cargo.toml +1 -1
  1442. data/rust/codegen/src/domain_generator.rs +27 -2
  1443. data/rust/codegen/src/expr_emitter.rs +6 -0
  1444. data/rust/codegen/src/json_codec.rs +230 -33
  1445. data/rust/codegen/src/main.rs +35 -34
  1446. data/rust/codegen/src/manifest.rs +1 -1
  1447. data/rust/codegen/src/naming.rs +5 -5
  1448. data/rust/codegen/src/one_of_references.rs +125 -0
  1449. data/rust/codegen/src/presenters.rs +3 -3
  1450. data/rust/codegen/src/queries.rs +76 -9
  1451. data/rust/codegen/src/reactions.rs +19 -14
  1452. data/rust/codegen/src/read_models.rs +19 -14
  1453. data/rust/codegen/src/registry.rs +110 -20
  1454. data/rust/codegen/src/sidecars.rs +1 -1
  1455. data/rust/expr/Cargo.lock +54 -0
  1456. data/rust/expr/Cargo.toml +52 -0
  1457. data/rust/{src/kernel → expr/src}/attribute_shapes/composite.rs +45 -14
  1458. data/rust/{src/kernel → expr/src}/attribute_shapes/list.rs +1 -1
  1459. data/rust/{src/kernel → expr/src}/attribute_shapes/mod.rs +2 -2
  1460. data/rust/{src/kernel → expr/src}/attribute_shapes/optional.rs +1 -1
  1461. data/rust/{src/kernel → expr/src}/attribute_shapes/scalar.rs +3 -3
  1462. data/rust/{src/kernel → expr/src}/expr.rs +30 -8
  1463. data/rust/{src/kernel → expr/src}/expression_operators/arithmetic.rs +41 -18
  1464. data/rust/{src/kernel → expr/src}/expression_operators/comparison.rs +54 -8
  1465. data/rust/{src/kernel → expr/src}/expression_operators/enumeration.rs +73 -6
  1466. data/rust/{src/kernel → expr/src}/expression_operators/logical.rs +2 -2
  1467. data/rust/expr/src/expression_operators/membership.rs +33 -0
  1468. data/rust/{src/kernel → expr/src}/expression_operators/mod.rs +2 -2
  1469. data/rust/expr/src/expression_operators/pattern_match.rs +179 -0
  1470. data/rust/{src/kernel → expr/src}/expression_operators/positional.rs +8 -6
  1471. data/rust/{src/kernel → expr/src}/expression_operators/presence.rs +7 -6
  1472. data/rust/{src/kernel → expr/src}/expression_operators/sign_test.rs +14 -4
  1473. data/rust/{src/kernel → expr/src}/expression_operators/sized.rs +16 -9
  1474. data/rust/{src/kernel → expr/src}/expression_operators/text.rs +30 -17
  1475. data/rust/{src/kernel → expr/src}/expression_operators/to_string.rs +8 -6
  1476. data/rust/expr/src/lib.rs +17 -0
  1477. data/rust/expr/src/refusal.rs +62 -0
  1478. data/rust/expr/src/rendering.rs +135 -0
  1479. data/rust/{src/kernel → expr/src}/ruby_float.rs +7 -6
  1480. data/rust/host/Cargo.lock +137 -123
  1481. data/rust/host/Cargo.toml +8 -4
  1482. data/rust/host/HECKS_RELEASE +1 -1
  1483. data/rust/host/core/Cargo.toml +64 -0
  1484. data/rust/host/core/clippy.toml +4 -0
  1485. data/rust/host/core/src/arg_limits.rs +48 -0
  1486. data/rust/host/core/src/clock.rs +146 -0
  1487. data/rust/host/core/src/dispatch.rs +920 -0
  1488. data/rust/host/core/src/expr_json.rs +1137 -0
  1489. data/rust/host/{src → core/src}/host.rs +13 -14
  1490. data/rust/host/{src → core/src}/ir.rs +227 -18
  1491. data/rust/host/core/src/journal.rs +126 -0
  1492. data/rust/host/{src → core/src}/lambda_client.rs +27 -96
  1493. data/rust/host/core/src/lib.rs +37 -0
  1494. data/rust/host/{src → core/src}/needs.rs +66 -20
  1495. data/rust/host/core/src/plugins.rs +269 -0
  1496. data/rust/host/core/src/presentation.rs +471 -0
  1497. data/rust/host/{src → core/src}/presentation_write.rs +75 -43
  1498. data/rust/host/{src → core/src}/query_step.rs +54 -16
  1499. data/rust/host/{src → core/src}/reference_validate.rs +6 -6
  1500. data/rust/host/{src → core/src}/storage_shape.rs +2 -2
  1501. data/rust/host/core/src/store/memory.rs +635 -0
  1502. data/rust/host/core/src/store.rs +418 -0
  1503. data/rust/host/{src → core/src}/ui_schema.rs +8 -8
  1504. data/rust/host/core/src/validation.rs +129 -0
  1505. data/rust/host/core/src/wasm_runner.rs +410 -0
  1506. data/rust/host/fuzz/.gitignore +4 -0
  1507. data/rust/host/fuzz/Cargo.lock +4479 -0
  1508. data/rust/host/fuzz/Cargo.toml +42 -0
  1509. data/rust/host/fuzz/README.md +55 -0
  1510. data/rust/host/fuzz/fuzz_targets/postgres_equivalence.rs +24 -0
  1511. data/rust/host/fuzz/src/backend.rs +273 -0
  1512. data/rust/host/fuzz/src/equivalence.rs +195 -0
  1513. data/rust/host/fuzz/src/footprint.rs +182 -0
  1514. data/rust/host/fuzz/src/lib.rs +17 -0
  1515. data/rust/host/fuzz/src/ops.rs +345 -0
  1516. data/rust/host/fuzz/tests/smoke.rs +50 -0
  1517. data/rust/host/src/api.rs +316 -105
  1518. data/rust/host/src/auth/google_signin.rs +183 -0
  1519. data/rust/host/src/auth.rs +527 -370
  1520. data/rust/host/src/aws_lambda_invoker.rs +61 -0
  1521. data/rust/host/src/bin/expr_harness.rs +9 -4
  1522. data/rust/host/src/bin/ledger.rs +84 -0
  1523. data/rust/host/src/bin/lineage_harness.rs +17 -21
  1524. data/rust/host/src/bin/mint_harness.rs +12 -36
  1525. data/rust/host/src/bin/validate_harness.rs +1 -6
  1526. data/rust/host/src/boot.rs +466 -174
  1527. data/rust/host/src/boundary_fuzz/ir.rs +10 -5
  1528. data/rust/host/src/boundary_fuzz/server.rs +6 -5
  1529. data/rust/host/src/checkout.rs +4 -1
  1530. data/rust/host/src/commerce_ir.rs +126 -105
  1531. data/rust/host/src/{dispatch.rs → dispatch_tests.rs} +367 -847
  1532. data/rust/host/src/extension.rs +3 -4
  1533. data/rust/host/src/hecksagon_boundary_tests.rs +149 -0
  1534. data/rust/host/src/ledger.rs +683 -0
  1535. data/rust/host/src/lib.rs +21 -28
  1536. data/rust/host/src/payments/tests.rs +136 -38
  1537. data/rust/host/src/payments.rs +36 -32
  1538. data/rust/host/src/platform_signin/extension.rs +1 -1
  1539. data/rust/host/src/platform_signin/mapping_tests.rs +12 -9
  1540. data/rust/host/src/platform_signin/session.rs +7 -14
  1541. data/rust/host/src/platform_signin/verify.rs +0 -2
  1542. data/rust/host/src/presentation_tests.rs +382 -0
  1543. data/rust/host/src/rate_limit.rs +18 -2
  1544. data/rust/host/src/server.rs +222 -43
  1545. data/rust/host/src/{journal.rs → store/journal_sql.rs} +21 -127
  1546. data/rust/host/src/{mint.rs → store/mint.rs} +52 -18
  1547. data/rust/host/src/store/postgres.rs +493 -0
  1548. data/rust/host/src/store.rs +97 -0
  1549. data/rust/host/src/store_conformance.rs +1265 -0
  1550. data/rust/host/src/web/commerce.rs +10 -18
  1551. data/rust/host/src/web/newsletter/cooldown.rs +107 -22
  1552. data/rust/host/src/web/newsletter/naming.rs +104 -0
  1553. data/rust/host/src/web/newsletter/tests.rs +53 -0
  1554. data/rust/host/src/web/newsletter.rs +73 -50
  1555. data/rust/host/src/web/newsletter_send.rs +12 -13
  1556. data/rust/host/src/web/registration_receipt.rs +2 -2
  1557. data/rust/host/src/web/registrations.rs +754 -375
  1558. data/rust/host/src/web/resend_webhook.rs +3 -3
  1559. data/rust/host/src/web/signin_tests.rs +131 -0
  1560. data/rust/host/src/web.rs +1479 -149
  1561. data/rust/lsp/Cargo.toml +1 -1
  1562. data/rust/lsp/README.md +27 -27
  1563. data/rust/lsp/src/diagnostics.rs +75 -15
  1564. data/rust/lsp/src/main.rs +101 -5
  1565. data/rust/parser/Cargo.toml +1 -1
  1566. data/rust/parser/fuzz/Cargo.toml +2 -2
  1567. data/rust/parser/fuzz/README.md +3 -3
  1568. data/rust/parser/fuzz/fuzz_targets/{chapter.rs → bluebook.rs} +4 -4
  1569. data/rust/parser/fuzz/fuzz_targets/resolve.rs +2 -2
  1570. data/rust/parser/src/build/correlation_keys.rs +125 -0
  1571. data/rust/parser/src/build/mod.rs +1 -0
  1572. data/rust/parser/src/build/named_pattern.rs +22 -0
  1573. data/rust/parser/src/build/references.rs +52 -0
  1574. data/rust/parser/src/diag.rs +22 -1
  1575. data/rust/parser/src/emit.rs +122 -6
  1576. data/rust/parser/src/expr/ast_json.rs +10 -0
  1577. data/rust/parser/src/expr/resolver.rs +49 -9
  1578. data/rust/parser/src/ir.rs +66 -7
  1579. data/rust/parser/src/keywords.rs +704 -419
  1580. data/rust/parser/src/lex.rs +115 -87
  1581. data/rust/parser/src/main.rs +182 -32
  1582. data/rust/parser/src/parse/adapter.rs +28 -0
  1583. data/rust/parser/src/parse/aggregate.rs +64 -35
  1584. data/rust/parser/src/parse/audience.rs +65 -0
  1585. data/rust/parser/src/parse/command.rs +114 -15
  1586. data/rust/parser/src/parse/{chapter.rs → declaration.rs} +182 -54
  1587. data/rust/parser/src/parse/domain_port.rs +66 -9
  1588. data/rust/parser/src/parse/entity.rs +35 -32
  1589. data/rust/parser/src/parse/file.rs +1 -1
  1590. data/rust/parser/src/parse/hecksagon.rs +15 -5
  1591. data/rust/parser/src/parse/mod.rs +85 -2
  1592. data/rust/parser/src/parse/process_manager.rs +18 -11
  1593. data/rust/parser/src/parse/query.rs +13 -3
  1594. data/rust/parser/src/parse/read_model.rs +2 -1
  1595. data/rust/parser/src/parse/world.rs +12 -8
  1596. data/rust/parser/src/phrases.rs +1258 -0
  1597. data/rust/parser/src/render_ruby.rs +549 -0
  1598. data/rust/parser/src/sentence/changes.rs +437 -0
  1599. data/rust/parser/src/sentence/command_step.rs +206 -0
  1600. data/rust/parser/src/sentence/complete.rs +276 -0
  1601. data/rust/parser/src/sentence/entity.rs +41 -0
  1602. data/rust/parser/src/sentence/examples.rs +1012 -0
  1603. data/rust/parser/src/sentence/format.rs +885 -0
  1604. data/rust/parser/src/sentence/lower.rs +1366 -0
  1605. data/rust/parser/src/sentence/mod.rs +444 -0
  1606. data/rust/parser/src/sentence/names.rs +69 -0
  1607. data/rust/parser/src/sentence/options.rs +154 -0
  1608. data/rust/parser/src/sentence/pattern.rs +433 -0
  1609. data/rust/parser/src/sentence/process.rs +200 -0
  1610. data/rust/parser/src/sentence/queries.rs +431 -0
  1611. data/rust/parser/src/sentence/report.rs +283 -0
  1612. data/rust/parser/src/sentence/token.rs +106 -0
  1613. data/rust/parser/src/sentence/values.rs +498 -0
  1614. data/rust/src/exemplar/json.rs +17 -0
  1615. data/rust/src/exemplar/queries.rs +1 -0
  1616. data/rust/src/exemplar/reactions.rs +1 -1
  1617. data/rust/src/exemplar/registry.rs +30 -0
  1618. data/rust/src/kernel/cli.rs +239 -15
  1619. data/rust/src/kernel/collecting.rs +174 -0
  1620. data/rust/src/kernel/dispatch.rs +59 -29
  1621. data/rust/src/kernel/json.rs +62 -0
  1622. data/rust/src/kernel/mod.rs +7 -67
  1623. data/rust/src/kernel/named_query.rs +225 -11
  1624. data/rust/src/kernel/orchestrate.rs +241 -28
  1625. data/rust/src/kernel/orchestrate_guard_tests.rs +15 -12
  1626. data/rust/src/kernel/query_cursor.rs +251 -0
  1627. data/rust/src/kernel/query_ordering.rs +6 -6
  1628. data/rust/src/kernel/read_model.rs +162 -17
  1629. data/rust/src/kernel/repository.rs +107 -17
  1630. data/rust/src/kernel/vocab/mod.rs +4 -0
  1631. data/rust/src/kernel/vocab/reducer.rs +109 -0
  1632. data/rust/src/kernel/vocab/refusal_class.rs +124 -0
  1633. data/rust/src/kernel/vocab/refusal_template.rs +2 -2
  1634. data/rust/src/lib.rs +1 -1
  1635. data/rust/web/Cargo.lock +47 -0
  1636. data/rust/web/Cargo.toml +1 -1
  1637. metadata +689 -161
  1638. data/lib/hecks/chapters.rb +0 -147
  1639. data/lib/hecks/cli/command_table.rb +0 -72
  1640. data/lib/hecks/cli/launcher_words.rb +0 -168
  1641. data/lib/hecks/cli/model_check/examination.rb +0 -93
  1642. data/lib/hecks/cli/project_diagrams.rb +0 -59
  1643. data/lib/hecks/cli/stores.rb +0 -55
  1644. data/lib/hecks/cli.rb +0 -133
  1645. data/lib/hecks/gate/stages.yml +0 -234
  1646. data/lib/hecks/hecks/codebase.bluebook +0 -3241
  1647. data/lib/hecks/hecks/custodian.bluebook +0 -904
  1648. data/lib/hecks/hecks/governance.world +0 -7
  1649. data/lib/hecks/hecks/rehearsal.bluebook +0 -471
  1650. data/lib/hecks/hecks/tooling.bluebook +0 -1238
  1651. data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +0 -137
  1652. data/lib/hecks/ports/persistence/plugins/era/postgres_era/boot.rb +0 -43
  1653. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/domain_rename.rb +0 -91
  1654. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +0 -67
  1655. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/context.rb +0 -38
  1656. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +0 -100
  1657. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +0 -102
  1658. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/refusals.rb +0 -51
  1659. data/lib/hecks/ports/persistence/postgres_dump/connection.rb +0 -59
  1660. data/lib/hecks/ports/persistence/postgres_dump.rb +0 -100
  1661. data/lib/hecks/projections/deploy/box/templates/tunnel-egress.yaml.tmpl +0 -11
  1662. data/lib/hecks/projector/exporter/capability_values.rb +0 -55
  1663. data/lib/hecks/quality_control/adapters/clearance_recorder.rb +0 -36
  1664. data/lib/hecks/quality_control/adapters/github_ci_webhook.rb +0 -56
  1665. data/lib/hecks/runtime/remote_dispatcher.rb +0 -176
  1666. data/prebuilt/ad68f9e06c8068c2a4176b57abf3083d591d5d8980c2b3e897b97331f965b643.marshal +0 -0
  1667. data/prebuilt/ee22ec8642c4006717ba2b80f46e5efb81de459eef9dfec043e838c0ad4fd720.marshal +0 -0
  1668. data/prebuilt/verdicts-b69f5dcbba79c20daa0493476fa7c317004a30aa2bc3d3dc6a722ea70ae2b43b.json +0 -1
  1669. data/rust/host/src/clock.rs +0 -8
  1670. data/rust/host/src/expr_json.rs +0 -1983
  1671. data/rust/host/src/presentation.rs +0 -842
  1672. data/rust/host/src/wasm_runner.rs +0 -206
  1673. data/rust/src/kernel/expression_operators/membership.rs +0 -20
  1674. data/rust/src/kernel/expression_operators/pattern_match.rs +0 -108
  1675. /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/events.rb +0 -0
  1676. /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/head_writes.rb +0 -0
  1677. /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/base_sql.rb +0 -0
  1678. /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/field_cache.rb +0 -0
  1679. /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/head_compiler.rb +0 -0
  1680. /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/merge_writes.rb +0 -0
  1681. /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/resumable_backfill.rb +0 -0
  1682. /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/transform_installer.rb +0 -0
  1683. /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/transform_sql.rb +0 -0
  1684. /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/sagas.rb +0 -0
  1685. /data/rust/host/{src → core/src}/field_hints.rs +0 -0
  1686. /data/rust/host/{src → core/src}/log.rs +0 -0
  1687. /data/rust/host/{src → core/src}/reference_transform.rs +0 -0
@@ -4,12 +4,12 @@ use crate::clock::unix_now;
4
4
  use crate::commerce_ir::{payments_provider, PaymentsProvider};
5
5
  use crate::dispatch;
6
6
  use crate::host::Host;
7
- use crate::journal::LineageConfig;
8
7
  use crate::payments;
8
+ use crate::store::journal_sql::LineageConfig;
9
+ use crate::store::Store;
9
10
  use serde_json::{json, Value};
11
+ use std::collections::HashMap;
10
12
  use std::path::Path;
11
- use tokio::sync::Mutex;
12
- use tokio_postgres::Client;
13
13
 
14
14
  // Routes for /events, /registrations and /webhooks/stripe. Event and
15
15
  // Registration names are fixed here rather than read from the IR.
@@ -44,8 +44,40 @@ fn payment_processor(read: &Value, payments: &PaymentsProvider, reference: &str)
44
44
  // Payment statuses whose registration still holds a seat come from the
45
45
  // payment lifecycle's `holds_seat` mark (`PaymentsProvider::holds_seat`). A
46
46
  // registration with no Payment, or one in a state that frees its seat, holds none.
47
+ // A mock walkthrough payment still waiting to be settled holds none either: anyone may open
48
+ // one, nothing ever lapses it, and its seat is taken when it is settled instead.
47
49
 
48
- /// Whether a registration's own lifecycle state is one the chapter declares gives its seat
50
+ /// The processor a Payment records when checkout is the mock walkthrough.
51
+ const MOCK_PROCESSOR: &str = "mock_stripe";
52
+
53
+ /// The status a Payment waits in until its processor settles it.
54
+ const PENDING: &str = "pending";
55
+
56
+ /// A Payment's `status`, when it has one.
57
+ fn payment_status(read: &Value, payments: &PaymentsProvider, reference: &str) -> Option<String> {
58
+ instances_for(read, &payments.instance_prefix())
59
+ .into_iter()
60
+ .find(|(id, _)| id == reference)
61
+ .and_then(|(_, payment)| {
62
+ payment
63
+ .get("status")
64
+ .and_then(|s| s.as_str())
65
+ .map(String::from)
66
+ })
67
+ }
68
+
69
+ /// Whether a Payment holds its registration's seat: its status is one `holds_seat` lists, and
70
+ /// it is not a mock walkthrough payment still waiting to be settled.
71
+ fn payment_holds_seat(payment: &Value, payments: &PaymentsProvider) -> bool {
72
+ let Some(status) = payment.get("status").and_then(|s| s.as_str()) else {
73
+ return false;
74
+ };
75
+ let processor = plain_id(payment.get("processor"));
76
+ let waiting_walkthrough = status == PENDING && processor.as_deref() == Some(MOCK_PROCESSOR);
77
+ payments.holds_seat.iter().any(|held| held == status) && !waiting_walkthrough
78
+ }
79
+
80
+ /// Whether a registration's own lifecycle state is one the bluebook declares gives its seat
49
81
  /// back (`RegistrationsProvider::frees_seat`). A missing or non-text `status` is active.
50
82
  fn registration_frees_seat(registration: &Value, frees_seat: &[String]) -> bool {
51
83
  let status = registration.get("status").and_then(|s| s.as_str());
@@ -77,21 +109,15 @@ fn count_seats(
77
109
  binding: &crate::commerce_ir::RegistrationsProvider,
78
110
  payments: &PaymentsProvider,
79
111
  ) -> std::collections::HashMap<String, usize> {
80
- let statuses: std::collections::HashMap<String, String> =
112
+ let holding: std::collections::HashSet<String> =
81
113
  instances_for(read, &payments.instance_prefix())
82
114
  .into_iter()
83
- .filter_map(|(id, payment)| {
84
- payment
85
- .get("status")
86
- .and_then(|s| s.as_str())
87
- .map(|s| (id, s.to_string()))
88
- })
115
+ .filter(|(_, payment)| payment_holds_seat(payment, payments))
116
+ .map(|(id, _)| id)
89
117
  .collect();
90
118
  let mut counts = std::collections::HashMap::new();
91
119
  for (id, registration) in instances_for(read, &binding.registration_prefix()) {
92
- let holds = statuses
93
- .get(&id)
94
- .is_some_and(|status| payments.holds_seat.iter().any(|held| held == status));
120
+ let holds = holding.contains(&id);
95
121
  let freed = registration_frees_seat(&registration, &binding.frees_seat);
96
122
  if let (true, false, Some(event_slug)) =
97
123
  (holds, freed, plain_id(registration.get("event_slug")))
@@ -226,12 +252,12 @@ fn seats_path_slug(path: &str) -> Option<&str> {
226
252
  /// so a site never has to count registrations itself.
227
253
  async fn seats_route(
228
254
  path: &str,
229
- client: &Mutex<Client>,
255
+ store: &Store,
230
256
  wasm_path: &Path,
231
257
  config: &LineageConfig,
232
258
  payments: &PaymentsProvider,
233
259
  ) -> Value {
234
- let read = match dispatch::read(client, wasm_path).await {
260
+ let read = match dispatch::read(store, wasm_path).await {
235
261
  Ok(r) => r,
236
262
  Err(e) => return respond(500, "text/plain", &format!("{e:#}")),
237
263
  };
@@ -259,22 +285,33 @@ pub(super) async fn payments_routes(
259
285
  method: &str,
260
286
  path: &str,
261
287
  raw_body: &str,
262
- stripe_signature: &str,
288
+ request: &CheckoutRequest<'_>,
263
289
  host: Host<'_>,
264
290
  ) -> Option<Value> {
265
- let payments = domain_ir.and_then(payments_provider)?;
266
- checkout_route(method, path, raw_body, stripe_signature, host, &payments).await
291
+ let domain_ir = domain_ir?;
292
+ let payments = payments_provider(domain_ir)?;
293
+ checkout_route(method, path, raw_body, request, host, domain_ir, &payments).await
294
+ }
295
+
296
+ /// What a checkout route reads off the request besides its method, path and body.
297
+ pub(super) struct CheckoutRequest<'a> {
298
+ /// The `Stripe-Signature` header, empty when absent.
299
+ pub stripe_signature: &'a str,
300
+ /// The request's cookies, which carry the account cookie POST /events checks.
301
+ pub cookies: &'a HashMap<String, String>,
267
302
  }
303
+
268
304
  pub(super) async fn checkout_route(
269
305
  method: &str,
270
306
  path: &str,
271
307
  raw_body: &str,
272
- stripe_signature: &str,
308
+ request: &CheckoutRequest<'_>,
273
309
  host: Host<'_>,
310
+ domain_ir: &Value,
274
311
  payments: &PaymentsProvider,
275
312
  ) -> Option<Value> {
276
313
  let Host {
277
- client,
314
+ store,
278
315
  wasm_path,
279
316
  config,
280
317
  ..
@@ -294,53 +331,155 @@ pub(super) async fn checkout_route(
294
331
  // query string.
295
332
  ("GET", path) if path.starts_with("/registrations/") && !path.ends_with("/complete") => {
296
333
  let registration_id = path.trim_start_matches("/registrations/");
297
- Some(
298
- registration_show_route(registration_id, client, wasm_path, config, payments).await,
299
- )
334
+ Some(registration_show_route(registration_id, store, wasm_path, config, payments).await)
300
335
  }
301
336
  // POST /registrations/:id/complete: mock checkout's Pay/Cancel button,
302
- // refused unless the Payment's own processor is "mock_stripe". Settles
337
+ // refused unless checkout is the walkthrough and the Payment's own processor is
338
+ // "mock_stripe". Settles
303
339
  // through the same PaymentGateway port as POST /webhooks/stripe.
304
340
  ("POST", path) if path.starts_with("/registrations/") && path.ends_with("/complete") => {
305
341
  let registration_id = path
306
342
  .trim_start_matches("/registrations/")
307
343
  .trim_end_matches("/complete")
308
344
  .trim_end_matches('/');
309
- Some(registration_complete_route(registration_id, raw_body, host, payments).await)
345
+ Some(
346
+ registration_complete_route(
347
+ registration_id,
348
+ raw_body,
349
+ &payments::PlatformConfig::load().await,
350
+ host,
351
+ payments,
352
+ )
353
+ .await,
354
+ )
310
355
  }
311
356
  ("POST", "/webhooks/stripe") => Some(
312
357
  webhook_route(
313
358
  raw_body,
314
- stripe_signature,
359
+ request.stripe_signature,
315
360
  &payments::PlatformConfig::load().await,
316
361
  host,
317
362
  payments,
318
363
  )
319
364
  .await,
320
365
  ),
321
- // POST /events: a mock-payments service schedules a new session
322
- // when an editor picks a date. Idempotent — Event.find first means
323
- // a repeat call for the same slug is a no-op, not an error.
324
366
  ("GET", "/events/seats") => {
325
- Some(seats_route(path, client, wasm_path, config, payments).await)
367
+ Some(seats_route(path, store, wasm_path, config, payments).await)
326
368
  }
327
369
  ("GET", path) if seats_path_slug(path).is_some() => {
328
- Some(seats_route(path, client, wasm_path, config, payments).await)
370
+ Some(seats_route(path, store, wasm_path, config, payments).await)
329
371
  }
330
- ("POST", "/events") => Some(events_route(raw_body, host).await),
372
+ // POST /events: an Admin or Owner schedules a new session by hand. The CMS schedules
373
+ // through the internal dispatch protocol instead, so this route answers no guest.
374
+ ("POST", "/events") => Some(
375
+ events_route(
376
+ raw_body,
377
+ domain_ir,
378
+ request.cookies,
379
+ // Unset, it verifies no cookie, so every caller is refused.
380
+ &crate::secrets::var("SESSION_SECRET").unwrap_or_default(),
381
+ host,
382
+ )
383
+ .await,
384
+ ),
331
385
  _ => None,
332
386
  }
333
387
  }
334
388
 
335
- /// POST /events. Dispatches `<domain>::Event.Schedule` — Event is this
336
- /// deploy's own top-level aggregate, not a vendored chapter like Payments.
337
- async fn events_route(raw_body: &str, host: Host<'_>) -> Value {
389
+ /// The account cookie's person, when they are an active Admin or Owner; otherwise the 401 or 403
390
+ /// to answer. Read before anything else, so a refused caller learns nothing about the domain.
391
+ async fn require_admin_caller(
392
+ domain_ir: &Value,
393
+ cookies: &HashMap<String, String>,
394
+ secret: &str,
395
+ store: &Store,
396
+ ) -> Result<String, Value> {
397
+ let Some(caller) = cookies
398
+ .get(&crate::auth::account_cookie_name())
399
+ .filter(|_| !secret.is_empty())
400
+ .and_then(|token| crate::auth::verify_account_token(secret, token))
401
+ else {
402
+ return Err(super::json_error(401, "not logged in"));
403
+ };
404
+ match crate::auth::caller_is_admin(store, domain_ir, &caller).await {
405
+ Ok(true) => Ok(caller),
406
+ Ok(false) => Err(super::json_error(403, "admins only")),
407
+ Err(e) => Err(super::json_error(
408
+ 500,
409
+ &format!("members lookup failed: {e}"),
410
+ )),
411
+ }
412
+ }
413
+
414
+ /// The fields POST /events schedules an event with, as `Event.Schedule` takes them.
415
+ fn schedule_args(body: &Value) -> Result<Value, Value> {
416
+ let missing = |field: &str| {
417
+ respond(
418
+ 400,
419
+ "application/json",
420
+ &json!({"error": format!("missing {field}")}).to_string(),
421
+ )
422
+ };
423
+ let slug = body
424
+ .get("slug")
425
+ .and_then(|v| v.as_str())
426
+ .ok_or_else(|| missing("slug"))?;
427
+ let name = body
428
+ .get("name")
429
+ .and_then(|v| v.as_str())
430
+ .ok_or_else(|| missing("name"))?;
431
+ let price_cents = body
432
+ .get("price_cents")
433
+ .and_then(Value::as_i64)
434
+ .ok_or_else(|| missing("price_cents"))?;
435
+ let capacity = body
436
+ .get("capacity")
437
+ .and_then(Value::as_i64)
438
+ .ok_or_else(|| missing("capacity"))?;
439
+ Ok(json!({
440
+ "slug": {"value": slug},
441
+ "name": {"value": name},
442
+ "price": {"cents": price_cents},
443
+ "capacity": {"value": capacity},
444
+ }))
445
+ }
446
+
447
+ /// Whether a scheduled event already carries the name, price and capacity of `args`. A
448
+ /// single-field value reads the same bare or wrapped as `{"value": ...}`.
449
+ fn schedules_the_same(event: &Value, args: &Value) -> bool {
450
+ let plain = |v: &Value, field: &str| {
451
+ v.get(field)
452
+ .map(|value| value.get("value").unwrap_or(value).clone())
453
+ };
454
+ let cents = |v: &Value| v.get("price").and_then(|p| p.get("cents")).cloned();
455
+ ["name", "capacity"]
456
+ .iter()
457
+ .all(|field| plain(event, field) == plain(args, field))
458
+ && cents(event) == cents(args)
459
+ }
460
+
461
+ /// POST /events. Dispatches `<domain>::Event.Schedule` — Event is this deploy's own top-level
462
+ /// aggregate, not a vendored bluebook like Payments — for an active Admin or Owner holding the
463
+ /// account cookie. The host vouches for the caller as GET /registrations does and dispatches
464
+ /// host-internally: a member's role (Admin, Owner) is not one the domain's commands declare.
465
+ /// A repeat call for a scheduled slug answers the event as it stands when every field matches,
466
+ /// and 409 when any differs, so nobody can hold a slug at a price the editor never set.
467
+ async fn events_route(
468
+ raw_body: &str,
469
+ domain_ir: &Value,
470
+ cookies: &HashMap<String, String>,
471
+ secret: &str,
472
+ host: Host<'_>,
473
+ ) -> Value {
338
474
  let Host {
339
- client,
475
+ store,
340
476
  wasm_path,
341
477
  config,
342
478
  ..
343
479
  } = host;
480
+ if let Err(response) = require_admin_caller(domain_ir, cookies, secret, store).await {
481
+ return response;
482
+ }
344
483
  let body: Value = match serde_json::from_str(raw_body) {
345
484
  Ok(v) => v,
346
485
  Err(e) => {
@@ -351,15 +490,16 @@ async fn events_route(raw_body: &str, host: Host<'_>) -> Value {
351
490
  )
352
491
  }
353
492
  };
354
- let Some(slug) = body.get("slug").and_then(|v| v.as_str()) else {
355
- return respond(
356
- 400,
357
- "application/json",
358
- &json!({"error": "missing slug"}).to_string(),
359
- );
493
+ let args = match schedule_args(&body) {
494
+ Ok(args) => args,
495
+ Err(response) => return response,
360
496
  };
497
+ let slug = args["slug"]["value"]
498
+ .as_str()
499
+ .unwrap_or_default()
500
+ .to_string();
361
501
 
362
- let read = match dispatch::read(client, wasm_path).await {
502
+ let read = match dispatch::read(store, wasm_path).await {
363
503
  Ok(r) => r,
364
504
  Err(e) => return respond(500, "text/plain", &format!("{e:#}")),
365
505
  };
@@ -367,42 +507,22 @@ async fn events_route(raw_body: &str, host: Host<'_>) -> Value {
367
507
  &read,
368
508
  &crate::commerce_ir::registrations_binding(&config.domain).event_prefix(),
369
509
  );
370
- if let Some((_, existing)) = events.iter().find(|(id, _)| id == slug) {
510
+ if let Some((_, existing)) = events.iter().find(|(id, _)| *id == slug) {
511
+ if !schedules_the_same(existing, &args) {
512
+ return respond(
513
+ 409,
514
+ "application/json",
515
+ &json!({"error": "an event with this slug is already scheduled with other details"})
516
+ .to_string(),
517
+ );
518
+ }
371
519
  return respond(
372
520
  200,
373
521
  "application/json",
374
- &serde_json::to_string_pretty(&with_id(slug, existing)).unwrap_or_default(),
522
+ &serde_json::to_string_pretty(&with_id(&slug, existing)).unwrap_or_default(),
375
523
  );
376
524
  }
377
525
 
378
- let Some(name) = body.get("name").and_then(|v| v.as_str()) else {
379
- return respond(
380
- 400,
381
- "application/json",
382
- &json!({"error": "missing name"}).to_string(),
383
- );
384
- };
385
- let Some(price_cents) = body.get("price_cents").and_then(serde_json::Value::as_i64) else {
386
- return respond(
387
- 400,
388
- "application/json",
389
- &json!({"error": "missing price_cents"}).to_string(),
390
- );
391
- };
392
- let Some(capacity) = body.get("capacity").and_then(serde_json::Value::as_i64) else {
393
- return respond(
394
- 400,
395
- "application/json",
396
- &json!({"error": "missing capacity"}).to_string(),
397
- );
398
- };
399
-
400
- let args = json!({
401
- "slug": {"value": slug},
402
- "name": {"value": name},
403
- "price": {"cents": price_cents},
404
- "capacity": {"value": capacity},
405
- });
406
526
  let verb = crate::commerce_ir::registrations_binding(&config.domain).schedule;
407
527
  let outcome = match dispatch::handle(host, &verb, args, None).await {
408
528
  Ok(o) => o,
@@ -416,7 +536,7 @@ async fn events_route(raw_body: &str, host: Host<'_>) -> Value {
416
536
  );
417
537
  }
418
538
 
419
- let read = match dispatch::read(client, wasm_path).await {
539
+ let read = match dispatch::read(store, wasm_path).await {
420
540
  Ok(r) => r,
421
541
  Err(e) => return respond(500, "text/plain", &format!("{e:#}")),
422
542
  };
@@ -424,7 +544,7 @@ async fn events_route(raw_body: &str, host: Host<'_>) -> Value {
424
544
  &read,
425
545
  &crate::commerce_ir::registrations_binding(&config.domain).event_prefix(),
426
546
  );
427
- let Some((_, event)) = events.iter().find(|(id, _)| id == slug) else {
547
+ let Some((_, event)) = events.iter().find(|(id, _)| *id == slug) else {
428
548
  return respond(
429
549
  500,
430
550
  "text/plain",
@@ -434,20 +554,21 @@ async fn events_route(raw_body: &str, host: Host<'_>) -> Value {
434
554
  respond(
435
555
  201,
436
556
  "application/json",
437
- &serde_json::to_string_pretty(&with_id(slug, event)).unwrap_or_default(),
557
+ &serde_json::to_string_pretty(&with_id(&slug, event)).unwrap_or_default(),
438
558
  )
439
559
  }
440
560
 
441
- /// GET /registrations/:id: event and attendee fields, plus amount_cents and
442
- /// payment_status from the same-reference Payment.
561
+ /// GET /registrations/:id: the event, the attendee's name, and amount_cents and payment_status
562
+ /// from the same-reference Payment. Anyone holding the id may ask (it rides in the pay and
563
+ /// confirmation URLs), so the answer carries no contact detail: no email, no phone.
443
564
  async fn registration_show_route(
444
565
  registration_id: &str,
445
- client: &Mutex<Client>,
566
+ store: &Store,
446
567
  wasm_path: &Path,
447
568
  config: &LineageConfig,
448
569
  payments: &PaymentsProvider,
449
570
  ) -> Value {
450
- let read = match dispatch::read(client, wasm_path).await {
571
+ let read = match dispatch::read(store, wasm_path).await {
451
572
  Ok(r) => r,
452
573
  Err(e) => return respond(500, "text/plain", &format!("{e:#}")),
453
574
  };
@@ -490,24 +611,26 @@ async fn registration_show_route(
490
611
  "attendee": {
491
612
  "first_name": attendee.get("first_name"),
492
613
  "last_name": attendee.get("last_name"),
493
- "email": attendee.get("email"),
494
- "phone": attendee.get("phone"),
495
614
  },
496
615
  "amount_cents": payment.and_then(|p| p.get("amount")).and_then(|a| a.get("cents")),
497
616
  "payment_status": payment.and_then(|p| p.get("status")),
498
617
  }).to_string())
499
618
  }
500
619
 
501
- /// POST /registrations/:id/complete. Refused unless the Payment itself was
502
- /// initiated on "mock_stripe"; settles through the same PaymentGateway port webhook_route uses.
620
+ /// POST /registrations/:id/complete. Refused unless checkout is the mock walkthrough right now
621
+ /// and the Payment itself was initiated on "mock_stripe", so a walkthrough payment left over from
622
+ /// before real payments were switched on can never be settled for free; settles through the same
623
+ /// PaymentGateway port webhook_route uses. A walkthrough payment holds no seat while it waits, so
624
+ /// a success takes the seat here and is refused when the event has none left.
503
625
  pub(crate) async fn registration_complete_route(
504
626
  registration_id: &str,
505
627
  raw_body: &str,
628
+ platform: &payments::PlatformConfig,
506
629
  host: Host<'_>,
507
630
  payments: &PaymentsProvider,
508
631
  ) -> Value {
509
632
  let Host {
510
- client,
633
+ store,
511
634
  wasm_path,
512
635
  config,
513
636
  ..
@@ -537,7 +660,7 @@ pub(crate) async fn registration_complete_route(
537
660
  );
538
661
  }
539
662
 
540
- let read = match dispatch::read(client, wasm_path).await {
663
+ let read = match dispatch::read(store, wasm_path).await {
541
664
  Ok(r) => r,
542
665
  Err(e) => return respond(500, "text/plain", &format!("{e:#}")),
543
666
  };
@@ -545,16 +668,23 @@ pub(crate) async fn registration_complete_route(
545
668
  &read,
546
669
  &crate::commerce_ir::registrations_binding(&config.domain).registration_prefix(),
547
670
  );
548
- if !registrations.iter().any(|(id, _)| id == registration_id) {
671
+ let Some((_, registration)) = registrations.iter().find(|(id, _)| id == registration_id) else {
549
672
  return respond(
550
673
  404,
551
674
  "application/json",
552
675
  &json!({"error": "no such registration"}).to_string(),
553
676
  );
554
- }
677
+ };
555
678
 
679
+ let walkthrough = matches!(
680
+ payments::checkout_plan(
681
+ payments::connection(&read, &config.domain).as_ref(),
682
+ platform
683
+ ),
684
+ payments::CheckoutPlan::Mock
685
+ );
556
686
  let processor = match payment_processor(&read, payments, registration_id) {
557
- Some(processor) if processor == "mock_stripe" => processor,
687
+ Some(processor) if walkthrough && processor == MOCK_PROCESSOR => processor,
558
688
  _ => {
559
689
  return respond(
560
690
  403,
@@ -563,6 +693,18 @@ pub(crate) async fn registration_complete_route(
563
693
  )
564
694
  }
565
695
  };
696
+ let event_slug = plain_id(registration.get("event_slug")).unwrap_or_default();
697
+ let waiting = payment_status(&read, payments, registration_id).as_deref() == Some(PENDING);
698
+ if outcome == "succeeded"
699
+ && waiting
700
+ && seats_left(&read, &config.domain, payments, &event_slug) == Some(0)
701
+ {
702
+ return respond(
703
+ 409,
704
+ "application/json",
705
+ &json!({"error": "this event is full"}).to_string(),
706
+ );
707
+ }
566
708
  let reported_processor = json!({"value": processor});
567
709
  // `reference_to Payment, as: :reference` keeps `reference` inside `with:`
568
710
  // too — `to:` alone isn't enough, the argument parser still expects it
@@ -604,7 +746,7 @@ pub(crate) async fn registration_complete_route(
604
746
  super::registration_receipt::deliver_receipt(registration_id, host, payments).await;
605
747
  }
606
748
 
607
- let read = match dispatch::read(client, wasm_path).await {
749
+ let read = match dispatch::read(store, wasm_path).await {
608
750
  Ok(r) => r,
609
751
  Err(e) => return respond(500, "text/plain", &format!("{e:#}")),
610
752
  };
@@ -669,7 +811,7 @@ pub(crate) async fn registrations_route_at(
669
811
  now_secs: i64,
670
812
  ) -> Value {
671
813
  let Host {
672
- client,
814
+ store,
673
815
  wasm_path,
674
816
  config,
675
817
  ..
@@ -709,7 +851,7 @@ pub(crate) async fn registrations_route_at(
709
851
  );
710
852
  };
711
853
 
712
- let read = match dispatch::read(client, wasm_path).await {
854
+ let read = match dispatch::read(store, wasm_path).await {
713
855
  Ok(r) => r,
714
856
  Err(e) => return respond(500, "text/plain", &format!("{e:#}")),
715
857
  };
@@ -762,14 +904,14 @@ pub(crate) async fn registrations_route_at(
762
904
  .unwrap_or("");
763
905
 
764
906
  // What checkout does is decided per request from this tenant's own
765
- // PaymentConnection, before anything is written — a paused connection
766
- // must never fall back to the mock walkthrough.
907
+ // PaymentConnection, before anything is written — a connection that is
908
+ // not enabled must never fall back to the mock walkthrough.
767
909
  let plan = payments::checkout_plan(
768
910
  payments::connection(&read, &config.domain).as_ref(),
769
911
  platform,
770
912
  );
771
913
  let processor = match plan {
772
- payments::CheckoutPlan::Mock => "mock_stripe",
914
+ payments::CheckoutPlan::Mock => MOCK_PROCESSOR,
773
915
  payments::CheckoutPlan::Stripe { .. } => "stripe",
774
916
  payments::CheckoutPlan::Paused => {
775
917
  return respond(
@@ -889,12 +1031,17 @@ pub(crate) async fn registrations_route_at(
889
1031
  &last_refusal(&outcome.result).to_string(),
890
1032
  );
891
1033
  }
1034
+ if let Some(full) =
1035
+ give_back_an_oversold_seat(&reference, event_slug, processor, host, payments).await
1036
+ {
1037
+ return full;
1038
+ }
892
1039
 
893
1040
  // The domain's reaction has already subscribed the address by now; a
894
1041
  // registrant who ticked the box gets the same signed confirm link as the
895
1042
  // footer form. Best effort: the registration stands whatever mail does.
896
1043
  if forwards_newsletter && news_signup {
897
- super::newsletter::send_confirmation_if_pending(email, client, wasm_path).await;
1044
+ super::newsletter::send_confirmation_if_pending(email, store, wasm_path).await;
898
1045
  }
899
1046
 
900
1047
  if let Some(embedded) = embedded_checkout {
@@ -932,8 +1079,8 @@ pub(crate) async fn registrations_route_at(
932
1079
  confirm_query("cancelled")
933
1080
  );
934
1081
 
935
- // A tenant with no connection, or a disabled one, is on the mock
936
- // walkthrough, never a misconfiguration to refuse. success_url/cancel_url
1082
+ // A tenant with no connection is on the mock walkthrough, never a
1083
+ // misconfiguration to refuse. success_url/cancel_url
937
1084
  // belong to this walkthrough only — an embedded session redirects nowhere.
938
1085
  let checkout_url =
939
1086
  checkout::mock_checkout_session(&reference, &success_url, &cancel_url, site_url);
@@ -944,6 +1091,61 @@ pub(crate) async fn registrations_route_at(
944
1091
  )
945
1092
  }
946
1093
 
1094
+ /// The failure reason a Payment records when its registration lost the event's last seat.
1095
+ const EVENT_FULL_REASON: &str = "event_full";
1096
+
1097
+ /// The seat check of POST /registrations, made again once its Payment and Registration are
1098
+ /// written: the first check and these writes are seconds apart (a Stripe session opens in
1099
+ /// between), so two guests can both pass it for the last seat. When the event now holds more
1100
+ /// registrations than seats, this one's Payment fails, which gives its seat back, and the answer
1101
+ /// is the 409 of a full event. When both racers see the overflow both give their seat back,
1102
+ /// which leaves a seat open rather than one too many taken.
1103
+ async fn give_back_an_oversold_seat(
1104
+ reference: &str,
1105
+ event_slug: &str,
1106
+ processor: &str,
1107
+ host: Host<'_>,
1108
+ payments: &PaymentsProvider,
1109
+ ) -> Option<Value> {
1110
+ let Host {
1111
+ store,
1112
+ wasm_path,
1113
+ config,
1114
+ ..
1115
+ } = host;
1116
+ let read = match dispatch::read(store, wasm_path).await {
1117
+ Ok(r) => r,
1118
+ Err(e) => return Some(respond(500, "text/plain", &format!("{e:#}"))),
1119
+ };
1120
+ let events = instances_for(
1121
+ &read,
1122
+ &crate::commerce_ir::registrations_binding(&config.domain).event_prefix(),
1123
+ );
1124
+ let capacity = events
1125
+ .iter()
1126
+ .find(|(id, _)| id == event_slug)
1127
+ .and_then(|(_, event)| event_capacity(event))?;
1128
+ let taken = seats_taken(&read, &config.domain, payments, event_slug);
1129
+ if taken as i64 <= capacity {
1130
+ return None;
1131
+ }
1132
+ let facts = json!({
1133
+ "reference": {"value": reference},
1134
+ "reason": {"value": EVENT_FULL_REASON},
1135
+ "reported_processor": {"value": processor},
1136
+ });
1137
+ if let Err(e) =
1138
+ dispatch::handle_routed(host, &payments.failed, json!(reference), facts, None).await
1139
+ {
1140
+ return Some(respond(500, "text/plain", &format!("{e:#}")));
1141
+ }
1142
+ Some(respond(
1143
+ 409,
1144
+ "application/json",
1145
+ &json!({"error": "this event is full"}).to_string(),
1146
+ ))
1147
+ }
1148
+
947
1149
  // The answer for an event that only the public mock secret could vouch for.
948
1150
  fn mock_secret_refused() -> Value {
949
1151
  respond(
@@ -955,6 +1157,81 @@ fn mock_secret_refused() -> Value {
955
1157
  )
956
1158
  }
957
1159
 
1160
+ /// Whether a Checkout Session collected what its Payment asked for: Stripe reports it `paid`, for
1161
+ /// the Payment's amount, in its currency. A session paid by a delayed method (a bank debit)
1162
+ /// completes `unpaid` and is settled later by `checkout.session.async_payment_succeeded`.
1163
+ fn session_collected(session: &Value, payment: Option<&Value>) -> bool {
1164
+ let Some(amount) = payment.and_then(|p| p.get("amount")) else {
1165
+ return false;
1166
+ };
1167
+ let lower = |v: Option<&Value>| v.and_then(Value::as_str).map(str::to_lowercase);
1168
+ let asked_currency =
1169
+ lower(amount.get("currency")).unwrap_or_else(|| checkout::CURRENCY.to_string());
1170
+ let paid = session.get("payment_status").and_then(Value::as_str) == Some("paid");
1171
+ let asked_cents = amount.get("cents").and_then(Value::as_i64);
1172
+ paid && asked_cents.is_some()
1173
+ && session.get("amount_total").and_then(Value::as_i64) == asked_cents
1174
+ && lower(session.get("currency")) == Some(asked_currency)
1175
+ }
1176
+
1177
+ /// What one Checkout Session event settles, as the port operation and its facts. A session that
1178
+ /// collected the Payment's money succeeds it; one that expired, or whose delayed payment failed,
1179
+ /// fails it. A session not yet paid, or paid a sum other than the Payment's, settles nothing: the
1180
+ /// mismatch is logged for the business to reconcile. A mock walkthrough payment settles on the
1181
+ /// event alone, since the mock webhook carries no amounts and the walkthrough charges nothing.
1182
+ fn settlement<'a>(
1183
+ event_type: &str,
1184
+ session: &Value,
1185
+ payment: Option<&Value>,
1186
+ reference_fact: &Value,
1187
+ reported_processor: &Value,
1188
+ payments: &'a PaymentsProvider,
1189
+ ) -> Option<(&'a str, Value)> {
1190
+ let fail = |reason: &str| {
1191
+ Some((
1192
+ payments.failed.as_str(),
1193
+ json!({"reference": reference_fact, "reason": {"value": reason}, "reported_processor": reported_processor}),
1194
+ ))
1195
+ };
1196
+ match event_type {
1197
+ "checkout.session.completed" | "checkout.session.async_payment_succeeded" => {
1198
+ let walkthrough = reported_processor["value"] == MOCK_PROCESSOR;
1199
+ if !walkthrough && !session_collected(session, payment) {
1200
+ if session.get("payment_status").and_then(Value::as_str) == Some("paid") {
1201
+ eprintln!(
1202
+ "webhook: a paid Checkout Session does not match its Payment's amount, so it was not settled: {}",
1203
+ reference_fact["value"]
1204
+ );
1205
+ }
1206
+ return None;
1207
+ }
1208
+ // The PaymentIntent id when one exists, else the Checkout
1209
+ // Session's own id — `.get`, not a panic, since a synthetic
1210
+ // test payload carries no payment_intent at all.
1211
+ let transaction_id = session
1212
+ .get("payment_intent")
1213
+ .and_then(|v| v.as_str())
1214
+ .or_else(|| session.get("id").and_then(|v| v.as_str()))
1215
+ .unwrap_or("")
1216
+ .to_string();
1217
+ Some((
1218
+ payments.succeeded.as_str(),
1219
+ json!({
1220
+ "reference": reference_fact,
1221
+ "transaction_id": {"value": transaction_id},
1222
+ "reported_processor": reported_processor,
1223
+ }),
1224
+ ))
1225
+ }
1226
+ "checkout.session.expired" => fail(&payments.lapse_reason),
1227
+ "checkout.session.async_payment_failed" => fail(ASYNC_PAYMENT_FAILED_REASON),
1228
+ _ => None,
1229
+ }
1230
+ }
1231
+
1232
+ /// The failure reason a Payment records when the processor reports its delayed payment failed.
1233
+ const ASYNC_PAYMENT_FAILED_REASON: &str = "async_payment_failed";
1234
+
958
1235
  /// POST /webhooks/stripe. Verifies the signature against
959
1236
  /// `STRIPE_WEBHOOK_SECRET`, a saved signing secret, or — only while neither
960
1237
  /// is set — the public mock secret, refusing anything only a real processor
@@ -967,7 +1244,7 @@ pub(crate) async fn webhook_route(
967
1244
  payments: &PaymentsProvider,
968
1245
  ) -> Value {
969
1246
  let Host {
970
- client, wasm_path, ..
1247
+ store, wasm_path, ..
971
1248
  } = host;
972
1249
  let now = unix_now();
973
1250
  // The environment's signing secret and any saved with the business's own
@@ -1000,7 +1277,7 @@ pub(crate) async fn webhook_route(
1000
1277
  Err(_) => return respond(400, "text/plain", "invalid JSON"),
1001
1278
  };
1002
1279
 
1003
- let read = match dispatch::read(client, wasm_path).await {
1280
+ let read = match dispatch::read(store, wasm_path).await {
1004
1281
  Ok(r) => r,
1005
1282
  Err(e) => return respond(500, "text/plain", &format!("{e:#}")),
1006
1283
  };
@@ -1028,7 +1305,7 @@ pub(crate) async fn webhook_route(
1028
1305
  // Defaults to "stripe" when there's no matching Payment.
1029
1306
  let processor =
1030
1307
  payment_processor(&read, payments, &reference).unwrap_or_else(|| "stripe".to_string());
1031
- if fallback_secret && processor != "mock_stripe" {
1308
+ if fallback_secret && processor != MOCK_PROCESSOR {
1032
1309
  return refuse_fallback();
1033
1310
  }
1034
1311
  let reported_processor = json!({"value": processor});
@@ -1037,36 +1314,18 @@ pub(crate) async fn webhook_route(
1037
1314
  // string, and `reference` stays inside `with:` too, since the
1038
1315
  // argument parser still expects to find it in facts.
1039
1316
  let reference_fact = json!({"value": reference.clone()});
1040
- let verb_and_facts = match event_type {
1041
- "checkout.session.completed" => {
1042
- // The PaymentIntent id when one exists, else the Checkout
1043
- // Session's own id — `.get`, not a panic, since a synthetic
1044
- // test payload carries no payment_intent at all.
1045
- let transaction_id = object
1046
- .get("payment_intent")
1047
- .and_then(|v| v.as_str())
1048
- .or_else(|| object.get("id").and_then(|v| v.as_str()))
1049
- .unwrap_or("")
1050
- .to_string();
1051
- Some((
1052
- payments.succeeded.as_str(),
1053
- json!({
1054
- "reference": reference_fact,
1055
- "transaction_id": {"value": transaction_id},
1056
- "reported_processor": reported_processor,
1057
- }),
1058
- ))
1059
- }
1060
- "checkout.session.expired" => Some((
1061
- payments.failed.as_str(),
1062
- json!({
1063
- "reference": reference_fact,
1064
- "reason": {"value": payments.lapse_reason},
1065
- "reported_processor": reported_processor,
1066
- }),
1067
- )),
1068
- _ => None,
1069
- };
1317
+ let payment = instances_for(&read, &payments.instance_prefix())
1318
+ .into_iter()
1319
+ .find(|(id, _)| *id == reference)
1320
+ .map(|(_, payment)| payment);
1321
+ let verb_and_facts = settlement(
1322
+ event_type,
1323
+ &object,
1324
+ payment.as_ref(),
1325
+ &reference_fact,
1326
+ &reported_processor,
1327
+ payments,
1328
+ );
1070
1329
 
1071
1330
  if let Some((verb, facts)) = verb_and_facts {
1072
1331
  // A refusal here (e.g. a redelivered webhook — Stripe's delivery
@@ -1076,7 +1335,7 @@ pub(crate) async fn webhook_route(
1076
1335
  Err(e) => return respond(500, "text/plain", &format!("{e:#}")),
1077
1336
  // Only the delivery that settles the payment sends the receipt;
1078
1337
  // a redelivery is refused and stays quiet.
1079
- Ok(settled) if settled.accepted && event_type == "checkout.session.completed" => {
1338
+ Ok(settled) if settled.accepted && verb == payments.succeeded => {
1080
1339
  super::registration_receipt::deliver_receipt(&reference, host, payments).await;
1081
1340
  }
1082
1341
  Ok(_) => {}
@@ -1091,6 +1350,8 @@ pub(crate) async fn webhook_route(
1091
1350
  mod tests {
1092
1351
  use super::*;
1093
1352
  use crate::lambda_client;
1353
+ #[allow(unused_imports)]
1354
+ use crate::store::StoreFixtures;
1094
1355
  use crate::web::tests::{provision_lineage, scratch_db};
1095
1356
 
1096
1357
  fn checkout_config(era: i32) -> LineageConfig {
@@ -1098,6 +1359,21 @@ mod tests {
1098
1359
  domain: "CheckoutFixture".to_string(),
1099
1360
  era: Some(era),
1100
1361
  mirrored: None,
1362
+ seal_every: None,
1363
+ }
1364
+ }
1365
+
1366
+ fn checkout_memory_config() -> LineageConfig {
1367
+ crate::dispatch_tests::tests::memory_config("CheckoutFixture")
1368
+ }
1369
+
1370
+ // A request with no Stripe signature and no cookies.
1371
+ fn no_request() -> CheckoutRequest<'static> {
1372
+ static NO_COOKIES: std::sync::LazyLock<HashMap<String, String>> =
1373
+ std::sync::LazyLock::new(HashMap::new);
1374
+ CheckoutRequest {
1375
+ stripe_signature: "",
1376
+ cookies: &NO_COOKIES,
1101
1377
  }
1102
1378
  }
1103
1379
 
@@ -1106,7 +1382,7 @@ mod tests {
1106
1382
  }
1107
1383
 
1108
1384
  async fn schedule_event(
1109
- client: &Mutex<Client>,
1385
+ client: &Store,
1110
1386
  wasm_path: &Path,
1111
1387
  config: &LineageConfig,
1112
1388
  slug: &str,
@@ -1304,7 +1580,7 @@ mod tests {
1304
1580
  }
1305
1581
 
1306
1582
  #[test]
1307
- fn a_state_the_chapter_does_not_declare_keeps_its_seat_even_when_it_is_archived() {
1583
+ fn a_state_the_bluebook_does_not_declare_keeps_its_seat_even_when_it_is_archived() {
1308
1584
  let payments = crate::commerce_ir::fixture_payments();
1309
1585
  let mut read = read_with(3, &[("a", "yoga", "succeeded")]);
1310
1586
  set_registration_status(&mut read, "a", "archived");
@@ -1610,15 +1886,8 @@ mod tests {
1610
1886
 
1611
1887
  #[tokio::test]
1612
1888
  async fn the_seat_routes_answer_from_a_real_read_and_404_an_unknown_event() {
1613
- let client = scratch_db("hecks_host_web_test_seat_routes").await;
1614
- provision_lineage(
1615
- &*client.lock().await,
1616
- "CheckoutFixture",
1617
- 1,
1618
- &["Event", "Registration", "Payment"],
1619
- )
1620
- .await;
1621
- let config = checkout_config(1);
1889
+ let client = crate::dispatch_tests::tests::memory_store();
1890
+ let config = checkout_memory_config();
1622
1891
  let wasm_path = checkout_wasm_path();
1623
1892
  schedule_event(&client, &wasm_path, &config, "yoga", 4200).await;
1624
1893
  let ir = crate::commerce_ir::fixture_ir();
@@ -1630,7 +1899,7 @@ mod tests {
1630
1899
  "GET",
1631
1900
  path,
1632
1901
  "",
1633
- "",
1902
+ &no_request(),
1634
1903
  Host::new(client, wasm_path, config, &lambda_client::NeverInvoker),
1635
1904
  )
1636
1905
  .await
@@ -1749,14 +2018,7 @@ mod tests {
1749
2018
 
1750
2019
  #[tokio::test]
1751
2020
  async fn registrations_route_refuses_a_body_missing_any_required_field() {
1752
- let client = scratch_db("hecks_host_web_test_registrations_missing_fields").await;
1753
- provision_lineage(
1754
- &*client.lock().await,
1755
- "CheckoutFixture",
1756
- 1,
1757
- &["Event", "Registration", "Payment"],
1758
- )
1759
- .await;
2021
+ let client = crate::dispatch_tests::tests::memory_store();
1760
2022
 
1761
2023
  let response = registrations_route(
1762
2024
  r#"{"event_slug":"yoga-aug"}"#,
@@ -1764,7 +2026,7 @@ mod tests {
1764
2026
  Host::new(
1765
2027
  &client,
1766
2028
  &checkout_wasm_path(),
1767
- &checkout_config(1),
2029
+ &checkout_memory_config(),
1768
2030
  &lambda_client::NeverInvoker,
1769
2031
  ),
1770
2032
  &crate::commerce_ir::fixture_payments(),
@@ -1780,15 +2042,8 @@ mod tests {
1780
2042
  #[tokio::test]
1781
2043
  async fn registrations_route_forwards_a_new_shaped_attendee_verbatim_even_against_an_old_shaped_fixture(
1782
2044
  ) {
1783
- let client = scratch_db("hecks_host_web_test_registrations_new_shape").await;
1784
- provision_lineage(
1785
- &*client.lock().await,
1786
- "CheckoutFixture",
1787
- 1,
1788
- &["Event", "Registration", "Payment"],
1789
- )
1790
- .await;
1791
- let config = checkout_config(1);
2045
+ let client = crate::dispatch_tests::tests::memory_store();
2046
+ let config = checkout_memory_config();
1792
2047
  let wasm_path = checkout_wasm_path();
1793
2048
 
1794
2049
  schedule_event(&client, &wasm_path, &config, "happy-event", 4200).await;
@@ -1836,9 +2091,9 @@ mod tests {
1836
2091
  // (the fixture's own IR) shows the same call is answered when it does.
1837
2092
  #[tokio::test]
1838
2093
  async fn payments_routes_serve_nothing_without_a_payments_capability() {
1839
- let client = scratch_db("hecks_host_web_test_payments_gate").await;
2094
+ let client = crate::dispatch_tests::tests::memory_store();
1840
2095
  let wasm_path = checkout_wasm_path();
1841
- let config = checkout_config(1);
2096
+ let config = checkout_memory_config();
1842
2097
  let no_payments = json!({"name": "Studio"});
1843
2098
 
1844
2099
  for (method, path) in [
@@ -1856,7 +2111,7 @@ mod tests {
1856
2111
  method,
1857
2112
  path,
1858
2113
  "not json",
1859
- "",
2114
+ &no_request(),
1860
2115
  Host::new(&client, &wasm_path, &config, &lambda_client::NeverInvoker),
1861
2116
  )
1862
2117
  .await;
@@ -1873,7 +2128,7 @@ mod tests {
1873
2128
  "POST",
1874
2129
  "/registrations",
1875
2130
  "not json",
1876
- "",
2131
+ &no_request(),
1877
2132
  Host::new(&client, &wasm_path, &config, &lambda_client::NeverInvoker),
1878
2133
  )
1879
2134
  .await;
@@ -1885,14 +2140,7 @@ mod tests {
1885
2140
 
1886
2141
  #[tokio::test]
1887
2142
  async fn registrations_route_refuses_invalid_json_outright() {
1888
- let client = scratch_db("hecks_host_web_test_registrations_bad_json").await;
1889
- provision_lineage(
1890
- &*client.lock().await,
1891
- "CheckoutFixture",
1892
- 1,
1893
- &["Event", "Registration", "Payment"],
1894
- )
1895
- .await;
2143
+ let client = crate::dispatch_tests::tests::memory_store();
1896
2144
 
1897
2145
  let response = registrations_route(
1898
2146
  "not json",
@@ -1900,7 +2148,7 @@ mod tests {
1900
2148
  Host::new(
1901
2149
  &client,
1902
2150
  &checkout_wasm_path(),
1903
- &checkout_config(1),
2151
+ &checkout_memory_config(),
1904
2152
  &lambda_client::NeverInvoker,
1905
2153
  ),
1906
2154
  &crate::commerce_ir::fixture_payments(),
@@ -1909,43 +2157,116 @@ mod tests {
1909
2157
  assert_eq!(response["statusCode"], 400);
1910
2158
  }
1911
2159
 
1912
- #[tokio::test]
1913
- async fn events_route_refuses_invalid_json_outright() {
1914
- let client = scratch_db("hecks_host_web_test_events_bad_json").await;
1915
- provision_lineage(
1916
- &*client.lock().await,
1917
- "CheckoutFixture",
1918
- 1,
1919
- &["Event", "Registration", "Payment"],
1920
- )
1921
- .await;
2160
+ const EVENTS_SECRET: &str = "s3cret";
2161
+ const EVENTS_ADMIN: &str = "admin@example.com";
2162
+ const EVENTS_MEMBER: &str = "member@example.com";
1922
2163
 
1923
- let response = events_route(
1924
- "not json",
2164
+ // A scratch database with the fixture's lineage and a membership table holding one Admin
2165
+ // and one Member, and the domain IR that names that membership.
2166
+ async fn events_db(name: &str) -> (Store, Value) {
2167
+ let client = scratch_db(name).await;
2168
+ {
2169
+ let guard = client.lock().await;
2170
+ provision_lineage(
2171
+ &guard,
2172
+ "CheckoutFixture",
2173
+ 1,
2174
+ &["Event", "Registration", "Payment"],
2175
+ )
2176
+ .await;
2177
+ guard
2178
+ .batch_execute(
2179
+ "CREATE TABLE acme_member_head_snapshot_1 (id text PRIMARY KEY, ordinal bigint NOT NULL, state jsonb NOT NULL);
2180
+ CREATE VIEW acme_member_head AS SELECT id, state FROM acme_member_head_snapshot_1;",
2181
+ )
2182
+ .await
2183
+ .unwrap();
2184
+ for (email, role) in [(EVENTS_ADMIN, "Admin"), (EVENTS_MEMBER, "Member")] {
2185
+ let state = json!({"email": {"value": email}, "role": {"value": role}});
2186
+ guard
2187
+ .execute(
2188
+ "INSERT INTO acme_member_head_snapshot_1 (id, ordinal, state) VALUES ($1, 0, $2::jsonb)",
2189
+ &[&email, &state],
2190
+ )
2191
+ .await
2192
+ .unwrap();
2193
+ }
2194
+ }
2195
+ let domain_ir = json!({
2196
+ "name": "Acme",
2197
+ "lineage": {"capable_aggregates": [{"name": "Member", "storage_name": "member"}]},
2198
+ "membership": {"provider": "Acme", "aggregate": "Acme::Member"},
2199
+ });
2200
+ (client, domain_ir)
2201
+ }
2202
+
2203
+ // The cookies of a signed-in `email`, or none.
2204
+ fn events_cookies(email: Option<&str>) -> HashMap<String, String> {
2205
+ email
2206
+ .map(|email| {
2207
+ (
2208
+ crate::auth::account_cookie_name(),
2209
+ crate::auth::account_token(EVENTS_SECRET, email, 60),
2210
+ )
2211
+ })
2212
+ .into_iter()
2213
+ .collect()
2214
+ }
2215
+
2216
+ // POST /events as `email` (none: no account cookie).
2217
+ async fn post_event(
2218
+ client: &Store,
2219
+ domain_ir: &Value,
2220
+ email: Option<&str>,
2221
+ body: &str,
2222
+ ) -> Value {
2223
+ events_route(
2224
+ body,
2225
+ domain_ir,
2226
+ &events_cookies(email),
2227
+ EVENTS_SECRET,
1925
2228
  Host::new(
1926
- &client,
2229
+ client,
1927
2230
  &checkout_wasm_path(),
1928
2231
  &checkout_config(1),
1929
2232
  &lambda_client::NeverInvoker,
1930
2233
  ),
1931
2234
  )
1932
- .await;
1933
- assert_eq!(response["statusCode"], 400);
2235
+ .await
2236
+ }
2237
+
2238
+ fn event_keys(read: &Value) -> Vec<String> {
2239
+ read["instances"]
2240
+ .as_object()
2241
+ .unwrap()
2242
+ .keys()
2243
+ .filter(|k| k.starts_with("CheckoutFixture::Event#"))
2244
+ .cloned()
2245
+ .collect()
1934
2246
  }
1935
2247
 
2248
+ // A guest, a forged cookie and a signed-in Member are all refused before anything is read
2249
+ // or written: scheduling (and so pricing) a bookable event is an Admin's or Owner's.
1936
2250
  #[tokio::test]
1937
- async fn events_route_refuses_a_body_missing_any_required_field() {
1938
- let client = scratch_db("hecks_host_web_test_events_missing_fields").await;
1939
- provision_lineage(
1940
- &*client.lock().await,
1941
- "CheckoutFixture",
1942
- 1,
1943
- &["Event", "Registration", "Payment"],
1944
- )
1945
- .await;
2251
+ async fn events_route_schedules_nothing_for_anyone_but_an_active_admin() {
2252
+ let (client, domain_ir) = events_db("hecks_host_web_test_events_auth").await;
2253
+ let body = json!({"slug": "squat", "name": "Squatted", "price_cents": 1, "capacity": 999})
2254
+ .to_string();
1946
2255
 
1947
- let response = events_route(
1948
- r#"{"slug":"new-event"}"#,
2256
+ let guest = post_event(&client, &domain_ir, None, &body).await;
2257
+ assert_eq!(guest["statusCode"], 401, "{guest:?}");
2258
+ let member = post_event(&client, &domain_ir, Some(EVENTS_MEMBER), &body).await;
2259
+ assert_eq!(member["statusCode"], 403, "{member:?}");
2260
+ let mut forged = HashMap::new();
2261
+ forged.insert(
2262
+ crate::auth::account_cookie_name(),
2263
+ crate::auth::account_token("another-secret", EVENTS_ADMIN, 60),
2264
+ );
2265
+ let forged = events_route(
2266
+ &body,
2267
+ &domain_ir,
2268
+ &forged,
2269
+ EVENTS_SECRET,
1949
2270
  Host::new(
1950
2271
  &client,
1951
2272
  &checkout_wasm_path(),
@@ -1954,29 +2275,43 @@ mod tests {
1954
2275
  ),
1955
2276
  )
1956
2277
  .await;
2278
+ assert_eq!(forged["statusCode"], 401, "{forged:?}");
2279
+
2280
+ let read = dispatch::read(&client, &checkout_wasm_path())
2281
+ .await
2282
+ .unwrap();
2283
+ assert!(
2284
+ event_keys(&read).is_empty(),
2285
+ "a refused caller scheduled an event"
2286
+ );
2287
+ }
2288
+
2289
+ #[tokio::test]
2290
+ async fn events_route_refuses_invalid_json_outright() {
2291
+ let (client, domain_ir) = events_db("hecks_host_web_test_events_bad_json").await;
2292
+ let response = post_event(&client, &domain_ir, Some(EVENTS_ADMIN), "not json").await;
1957
2293
  assert_eq!(response["statusCode"], 400);
1958
- assert!(response["body"].as_str().unwrap().contains("missing name"));
1959
2294
  }
1960
2295
 
1961
2296
  #[tokio::test]
1962
- async fn events_route_schedules_a_new_event_and_returns_201() {
1963
- let client = scratch_db("hecks_host_web_test_events_new").await;
1964
- provision_lineage(
1965
- &*client.lock().await,
1966
- "CheckoutFixture",
1967
- 1,
1968
- &["Event", "Registration", "Payment"],
2297
+ async fn events_route_refuses_a_body_missing_any_required_field() {
2298
+ let (client, domain_ir) = events_db("hecks_host_web_test_events_missing_fields").await;
2299
+ let response = post_event(
2300
+ &client,
2301
+ &domain_ir,
2302
+ Some(EVENTS_ADMIN),
2303
+ r#"{"slug":"new-event"}"#,
1969
2304
  )
1970
2305
  .await;
1971
- let config = checkout_config(1);
1972
- let wasm_path = checkout_wasm_path();
2306
+ assert_eq!(response["statusCode"], 400);
2307
+ assert!(response["body"].as_str().unwrap().contains("missing name"));
2308
+ }
1973
2309
 
2310
+ #[tokio::test]
2311
+ async fn events_route_schedules_a_new_event_and_returns_201() {
2312
+ let (client, domain_ir) = events_db("hecks_host_web_test_events_new").await;
1974
2313
  let body = json!({"slug": "mock-payments-event", "name": "Mock Payments Event", "price_cents": 4200, "capacity": 20}).to_string();
1975
- let response = events_route(
1976
- &body,
1977
- Host::new(&client, &wasm_path, &config, &lambda_client::NeverInvoker),
1978
- )
1979
- .await;
2314
+ let response = post_event(&client, &domain_ir, Some(EVENTS_ADMIN), &body).await;
1980
2315
  assert_eq!(response["statusCode"], 201, "{response:?}");
1981
2316
  let response_body: Value =
1982
2317
  serde_json::from_str(response["body"].as_str().unwrap()).unwrap();
@@ -1986,77 +2321,98 @@ mod tests {
1986
2321
  assert_eq!(response_body["capacity"]["value"], 20);
1987
2322
  assert_eq!(response_body["status"], "open");
1988
2323
 
1989
- let read = dispatch::read(&client, &wasm_path).await.unwrap();
1990
- let instances = read["instances"].as_object().unwrap();
1991
- assert!(
1992
- instances
1993
- .keys()
1994
- .any(|k| k == "CheckoutFixture::Event#mock-payments-event"),
1995
- "Event.Schedule should have committed for real: {instances:?}"
2324
+ let read = dispatch::read(&client, &checkout_wasm_path())
2325
+ .await
2326
+ .unwrap();
2327
+ assert_eq!(
2328
+ event_keys(&read),
2329
+ vec!["CheckoutFixture::Event#mock-payments-event".to_string()],
2330
+ "Event.Schedule should have committed for real"
1996
2331
  );
1997
2332
  }
1998
2333
 
1999
- // A repeat call for the same slug is a 200 no-op returning the existing
2000
- // event's state, never a second Event.Schedule dispatch.
2334
+ // A repeat call with the same fields is a 200 answering the existing event; one with other
2335
+ // fields is a 409 and changes nothing, so an early call cannot fix a slug's price.
2001
2336
  #[tokio::test]
2002
- async fn events_route_is_idempotent_on_a_repeat_slug() {
2003
- let client = scratch_db("hecks_host_web_test_events_idempotent").await;
2004
- provision_lineage(
2005
- &*client.lock().await,
2006
- "CheckoutFixture",
2337
+ async fn events_route_answers_a_matching_repeat_and_refuses_a_different_one() {
2338
+ let (client, domain_ir) = events_db("hecks_host_web_test_events_idempotent").await;
2339
+ let body = json!({"slug": "repeat-event", "name": "Repeat Event", "price_cents": 1000, "capacity": 5}).to_string();
2340
+ let first = post_event(&client, &domain_ir, Some(EVENTS_ADMIN), &body).await;
2341
+ assert_eq!(first["statusCode"], 201, "{first:?}");
2342
+
2343
+ let again = post_event(&client, &domain_ir, Some(EVENTS_ADMIN), &body).await;
2344
+ assert_eq!(again["statusCode"], 200, "{again:?}");
2345
+ let different = json!({"slug": "repeat-event", "name": "Different Name", "price_cents": 9999, "capacity": 1}).to_string();
2346
+ let refused = post_event(&client, &domain_ir, Some(EVENTS_ADMIN), &different).await;
2347
+ assert_eq!(refused["statusCode"], 409, "{refused:?}");
2348
+
2349
+ let read = dispatch::read(&client, &checkout_wasm_path())
2350
+ .await
2351
+ .unwrap();
2352
+ assert_eq!(
2353
+ event_keys(&read).len(),
2007
2354
  1,
2008
- &["Event", "Registration", "Payment"],
2009
- )
2010
- .await;
2011
- let config = checkout_config(1);
2012
- let wasm_path = checkout_wasm_path();
2355
+ "exactly one Event, never a second dispatch"
2356
+ );
2357
+ let event = &read["instances"]["CheckoutFixture::Event#repeat-event"];
2358
+ assert_eq!(
2359
+ event["price"]["cents"], 1000,
2360
+ "the refused repeat changed the price"
2361
+ );
2362
+ }
2013
2363
 
2014
- let body = json!({"slug": "repeat-event", "name": "Repeat Event", "price_cents": 1000, "capacity": 5}).to_string();
2015
- let first = events_route(
2016
- &body,
2364
+ // The registration id rides in the pay and confirmation URLs (and so in Referer headers), so
2365
+ // whoever holds it may read the registration back, but never the attendee's contact details.
2366
+ #[tokio::test]
2367
+ async fn the_registration_read_back_by_id_carries_no_email_or_phone() {
2368
+ let client = crate::dispatch_tests::tests::memory_store();
2369
+ let (config, wasm_path) = (checkout_memory_config(), checkout_wasm_path());
2370
+ schedule_event(&client, &wasm_path, &config, "show-event", 4200).await;
2371
+ let body =
2372
+ json!({"event_slug": "show-event", "name": "Ada Lovelace", "email": "ada@example.com"});
2373
+ let registered = registrations_route(
2374
+ &body.to_string(),
2375
+ &payments::test_platform(),
2017
2376
  Host::new(&client, &wasm_path, &config, &lambda_client::NeverInvoker),
2377
+ &crate::commerce_ir::fixture_payments(),
2018
2378
  )
2019
2379
  .await;
2020
- assert_eq!(first["statusCode"], 201, "{first:?}");
2380
+ let registered: Value = serde_json::from_str(registered["body"].as_str().unwrap()).unwrap();
2381
+ let id = registered["registration_id"]
2382
+ .as_str()
2383
+ .expect("a registration id");
2021
2384
 
2022
- // A second call with different name/price/capacity still returns the
2023
- // original event's state, 200 not 201 — idempotency keys only on slug.
2024
- let second_body = json!({"slug": "repeat-event", "name": "Different Name", "price_cents": 9999, "capacity": 1}).to_string();
2025
- let second = events_route(
2026
- &second_body,
2027
- Host::new(&client, &wasm_path, &config, &lambda_client::NeverInvoker),
2385
+ let shown = registration_show_route(
2386
+ id,
2387
+ &client,
2388
+ &wasm_path,
2389
+ &config,
2390
+ &crate::commerce_ir::fixture_payments(),
2028
2391
  )
2029
2392
  .await;
2030
- assert_eq!(second["statusCode"], 200, "{second:?}");
2031
- let second_body: Value = serde_json::from_str(second["body"].as_str().unwrap()).unwrap();
2393
+ let shown_body = shown["body"].as_str().unwrap();
2394
+ assert_eq!(shown["statusCode"], 200, "{shown:?}");
2395
+ assert!(!shown_body.contains("ada@example.com"), "{shown_body}");
2396
+ let shown: Value = serde_json::from_str(shown_body).unwrap();
2032
2397
  assert_eq!(
2033
- second_body["name"]["value"], "Repeat Event",
2034
- "must return the ORIGINAL event, not re-schedule with the new fields"
2398
+ (
2399
+ shown["attendee"].get("email"),
2400
+ shown["attendee"].get("phone")
2401
+ ),
2402
+ (None, None)
2035
2403
  );
2036
- assert_eq!(second_body["price"]["cents"], 1000);
2037
-
2038
- let read = dispatch::read(&client, &wasm_path).await.unwrap();
2039
- let instances = read["instances"].as_object().unwrap();
2040
2404
  assert_eq!(
2041
- instances
2042
- .keys()
2043
- .filter(|k| k.starts_with("CheckoutFixture::Event#repeat-event"))
2044
- .count(),
2045
- 1,
2046
- "exactly one Event, never a second dispatch: {instances:?}"
2405
+ (
2406
+ shown["amount_cents"].as_i64(),
2407
+ shown["payment_status"].as_str()
2408
+ ),
2409
+ (Some(4200), Some("pending"))
2047
2410
  );
2048
2411
  }
2049
2412
 
2050
2413
  #[tokio::test]
2051
2414
  async fn registrations_route_404s_an_unknown_event_slug() {
2052
- let client = scratch_db("hecks_host_web_test_registrations_no_event").await;
2053
- provision_lineage(
2054
- &*client.lock().await,
2055
- "CheckoutFixture",
2056
- 1,
2057
- &["Event", "Registration", "Payment"],
2058
- )
2059
- .await;
2415
+ let client = crate::dispatch_tests::tests::memory_store();
2060
2416
 
2061
2417
  let body =
2062
2418
  json!({"event_slug": "nope", "name": "Ada", "email": "ada@example.com"}).to_string();
@@ -2066,7 +2422,7 @@ mod tests {
2066
2422
  Host::new(
2067
2423
  &client,
2068
2424
  &checkout_wasm_path(),
2069
- &checkout_config(1),
2425
+ &checkout_memory_config(),
2070
2426
  &lambda_client::NeverInvoker,
2071
2427
  ),
2072
2428
  &crate::commerce_ir::fixture_payments(),
@@ -2077,15 +2433,8 @@ mod tests {
2077
2433
 
2078
2434
  #[tokio::test]
2079
2435
  async fn registrations_route_refuses_a_closed_event() {
2080
- let client = scratch_db("hecks_host_web_test_registrations_closed_event").await;
2081
- provision_lineage(
2082
- &*client.lock().await,
2083
- "CheckoutFixture",
2084
- 1,
2085
- &["Event", "Registration", "Payment"],
2086
- )
2087
- .await;
2088
- let config = checkout_config(1);
2436
+ let client = crate::dispatch_tests::tests::memory_store();
2437
+ let config = checkout_memory_config();
2089
2438
  let wasm_path = checkout_wasm_path();
2090
2439
 
2091
2440
  schedule_event(&client, &wasm_path, &config, "closed-event", 4200).await;
@@ -2119,7 +2468,7 @@ mod tests {
2119
2468
  // Schedules `slug` starting at `starts_at` with the optional cutoff set
2120
2469
  // (or absent), through the real Event.Schedule command.
2121
2470
  async fn schedule_timed_event(
2122
- client: &Mutex<Client>,
2471
+ client: &Store,
2123
2472
  wasm_path: &Path,
2124
2473
  config: &LineageConfig,
2125
2474
  slug: &str,
@@ -2152,15 +2501,8 @@ mod tests {
2152
2501
  #[tokio::test]
2153
2502
  async fn registrations_route_refuses_a_session_inside_its_registration_cutoff_and_writes_nothing(
2154
2503
  ) {
2155
- let client = scratch_db("hecks_host_web_test_registrations_cutoff_passed").await;
2156
- provision_lineage(
2157
- &*client.lock().await,
2158
- "CheckoutFixture",
2159
- 1,
2160
- &["Event", "Registration", "Payment"],
2161
- )
2162
- .await;
2163
- let config = checkout_config(1);
2504
+ let client = crate::dispatch_tests::tests::memory_store();
2505
+ let config = checkout_memory_config();
2164
2506
  let wasm_path = checkout_wasm_path();
2165
2507
 
2166
2508
  // Starts in 5 hours with a 6 hour cutoff: registration closed an hour ago.
@@ -2204,15 +2546,8 @@ mod tests {
2204
2546
 
2205
2547
  #[tokio::test]
2206
2548
  async fn registrations_route_accepts_a_session_with_no_cutoff_even_when_it_has_a_start() {
2207
- let client = scratch_db("hecks_host_web_test_registrations_cutoff_absent").await;
2208
- provision_lineage(
2209
- &*client.lock().await,
2210
- "CheckoutFixture",
2211
- 1,
2212
- &["Event", "Registration", "Payment"],
2213
- )
2214
- .await;
2215
- let config = checkout_config(1);
2549
+ let client = crate::dispatch_tests::tests::memory_store();
2550
+ let config = checkout_memory_config();
2216
2551
  let wasm_path = checkout_wasm_path();
2217
2552
 
2218
2553
  // A start in the past and no cutoff field: nothing to enforce.
@@ -2320,15 +2655,8 @@ mod tests {
2320
2655
  // A zero-price event: PositiveMoney's invariant refuses
2321
2656
  // Payment.Initiate before Registration.Request is ever reached,
2322
2657
  // proving the refusal is the real domain rule.
2323
- let client = scratch_db("hecks_host_web_test_registrations_zero_price").await;
2324
- provision_lineage(
2325
- &*client.lock().await,
2326
- "CheckoutFixture",
2327
- 1,
2328
- &["Event", "Registration", "Payment"],
2329
- )
2330
- .await;
2331
- let config = checkout_config(1);
2658
+ let client = crate::dispatch_tests::tests::memory_store();
2659
+ let config = checkout_memory_config();
2332
2660
  let wasm_path = checkout_wasm_path();
2333
2661
 
2334
2662
  schedule_event(&client, &wasm_path, &config, "free-event", 0).await;
@@ -2361,15 +2689,8 @@ mod tests {
2361
2689
  #[tokio::test]
2362
2690
  async fn registrations_route_runs_the_whole_dispatch_chain_and_returns_a_real_mock_checkout_url(
2363
2691
  ) {
2364
- let client = scratch_db("hecks_host_web_test_registrations_happy_path").await;
2365
- provision_lineage(
2366
- &*client.lock().await,
2367
- "CheckoutFixture",
2368
- 1,
2369
- &["Event", "Registration", "Payment"],
2370
- )
2371
- .await;
2372
- let config = checkout_config(1);
2692
+ let client = crate::dispatch_tests::tests::memory_store();
2693
+ let config = checkout_memory_config();
2373
2694
  let wasm_path = checkout_wasm_path();
2374
2695
 
2375
2696
  schedule_event(&client, &wasm_path, &config, "happy-event", 4200).await;
@@ -2437,15 +2758,8 @@ mod tests {
2437
2758
  // 404ing every guest right after a successful, already-charged registration.
2438
2759
  #[tokio::test]
2439
2760
  async fn registrations_route_redirects_to_the_caller_s_own_return_to_not_the_event_slug() {
2440
- let client = scratch_db("hecks_host_web_test_registrations_return_to").await;
2441
- provision_lineage(
2442
- &*client.lock().await,
2443
- "CheckoutFixture",
2444
- 1,
2445
- &["Event", "Registration", "Payment"],
2446
- )
2447
- .await;
2448
- let config = checkout_config(1);
2761
+ let client = crate::dispatch_tests::tests::memory_store();
2762
+ let config = checkout_memory_config();
2449
2763
  let wasm_path = checkout_wasm_path();
2450
2764
 
2451
2765
  schedule_event(&client, &wasm_path, &config, "happy-event", 4200).await;
@@ -2479,15 +2793,8 @@ mod tests {
2479
2793
  // redirect; falls back to "/" exactly as if none had been sent.
2480
2794
  #[tokio::test]
2481
2795
  async fn registrations_route_refuses_an_absolute_or_protocol_relative_return_to() {
2482
- let client = scratch_db("hecks_host_web_test_registrations_return_to_unsafe").await;
2483
- provision_lineage(
2484
- &*client.lock().await,
2485
- "CheckoutFixture",
2486
- 1,
2487
- &["Event", "Registration", "Payment"],
2488
- )
2489
- .await;
2490
- let config = checkout_config(1);
2796
+ let client = crate::dispatch_tests::tests::memory_store();
2797
+ let config = checkout_memory_config();
2491
2798
  let wasm_path = checkout_wasm_path();
2492
2799
 
2493
2800
  schedule_event(&client, &wasm_path, &config, "happy-event", 4200).await;
@@ -2531,15 +2838,8 @@ mod tests {
2531
2838
  // The full loop, mock both ends: registrations_route's own
2532
2839
  // checkout_url, then a webhook signed against the same mock secret
2533
2840
  // webhook_route falls back to while STRIPE_WEBHOOK_SECRET is unset.
2534
- let client = scratch_db("hecks_host_web_test_mock_full_loop").await;
2535
- provision_lineage(
2536
- &*client.lock().await,
2537
- "CheckoutFixture",
2538
- 1,
2539
- &["Event", "Registration", "Payment"],
2540
- )
2541
- .await;
2542
- let config = checkout_config(1);
2841
+ let client = crate::dispatch_tests::tests::memory_store();
2842
+ let config = checkout_memory_config();
2543
2843
  let wasm_path = checkout_wasm_path();
2544
2844
 
2545
2845
  schedule_event(&client, &wasm_path, &config, "mock-loop-event", 4200).await;
@@ -2589,14 +2889,7 @@ mod tests {
2589
2889
 
2590
2890
  #[tokio::test]
2591
2891
  async fn webhook_route_rejects_a_bad_signature_before_touching_the_domain_at_all() {
2592
- let client = scratch_db("hecks_host_web_test_webhook_bad_sig").await;
2593
- provision_lineage(
2594
- &*client.lock().await,
2595
- "CheckoutFixture",
2596
- 1,
2597
- &["Event", "Registration", "Payment"],
2598
- )
2599
- .await;
2892
+ let client = crate::dispatch_tests::tests::memory_store();
2600
2893
 
2601
2894
  let payload =
2602
2895
  json!({"type": "checkout.session.completed", "data": {"object": {}}}).to_string();
@@ -2609,7 +2902,7 @@ mod tests {
2609
2902
  Host::new(
2610
2903
  &client,
2611
2904
  &checkout_wasm_path(),
2612
- &checkout_config(1),
2905
+ &checkout_memory_config(),
2613
2906
  &lambda_client::NeverInvoker,
2614
2907
  ),
2615
2908
  &crate::commerce_ir::fixture_payments(),
@@ -2620,15 +2913,8 @@ mod tests {
2620
2913
 
2621
2914
  #[tokio::test]
2622
2915
  async fn webhook_route_settles_a_payment_on_checkout_session_completed() {
2623
- let client = scratch_db("hecks_host_web_test_webhook_completed").await;
2624
- provision_lineage(
2625
- &*client.lock().await,
2626
- "CheckoutFixture",
2627
- 1,
2628
- &["Event", "Registration", "Payment"],
2629
- )
2630
- .await;
2631
- let config = checkout_config(1);
2916
+ let client = crate::dispatch_tests::tests::memory_store();
2917
+ let config = checkout_memory_config();
2632
2918
  let wasm_path = checkout_wasm_path();
2633
2919
 
2634
2920
  schedule_event(&client, &wasm_path, &config, "webhook-event", 4200).await;
@@ -2649,8 +2935,12 @@ mod tests {
2649
2935
  let secret = "whsec_test_completed";
2650
2936
  let payload = json!({
2651
2937
  "type": "checkout.session.completed",
2652
- "data": {"object": {"metadata": {"registration_id": "REG-WEBHOOK-1"}, "payment_intent": "pi_test_abc"}},
2653
- }).to_string();
2938
+ "data": {"object": {
2939
+ "metadata": {"registration_id": "REG-WEBHOOK-1"}, "payment_intent": "pi_test_abc",
2940
+ "payment_status": "paid", "amount_total": 4200, "currency": "usd",
2941
+ }},
2942
+ })
2943
+ .to_string();
2654
2944
  let now = now_secs();
2655
2945
  let header = sign_stripe_header(secret, now, &payload);
2656
2946
 
@@ -2683,9 +2973,9 @@ mod tests {
2683
2973
  assert_eq!(redelivered["statusCode"], 200, "a redelivered webhook must not surface the resulting refusal as an error: {redelivered:?}");
2684
2974
  }
2685
2975
 
2686
- #[tokio::test]
2687
- async fn webhook_route_declines_a_payment_on_checkout_session_expired() {
2688
- let client = scratch_db("hecks_host_web_test_webhook_expired").await;
2976
+ // A scratch fixture with a scheduled event and a pending 4200-cent Stripe Payment `reference`.
2977
+ async fn stripe_payment_db(name: &str, reference: &str) -> Store {
2978
+ let client = scratch_db(name).await;
2689
2979
  provision_lineage(
2690
2980
  &*client.lock().await,
2691
2981
  "CheckoutFixture",
@@ -2693,7 +2983,103 @@ mod tests {
2693
2983
  &["Event", "Registration", "Payment"],
2694
2984
  )
2695
2985
  .await;
2696
- let config = checkout_config(1);
2986
+ let (config, wasm_path) = (checkout_config(1), checkout_wasm_path());
2987
+ schedule_event(&client, &wasm_path, &config, "webhook-event", 4200).await;
2988
+ let initiate = dispatch::handle(
2989
+ Host::new(&client, &wasm_path, &config, &lambda_client::NeverInvoker),
2990
+ "Payments::Payment.Initiate",
2991
+ json!({
2992
+ "reference": {"value": reference}, "processor": {"value": "stripe"},
2993
+ "payment_type": {"value": "card"}, "amount": {"cents": 4200},
2994
+ "client": {"name": "Ada Lovelace", "email": "ada@example.com"},
2995
+ }),
2996
+ None,
2997
+ )
2998
+ .await
2999
+ .unwrap();
3000
+ assert!(initiate.accepted, "{:?}", initiate.result);
3001
+ client
3002
+ }
3003
+
3004
+ // Delivers one signed Checkout Session event for `reference` and answers the Payment's status.
3005
+ async fn deliver_session_event(
3006
+ client: &Store,
3007
+ reference: &str,
3008
+ event_type: &str,
3009
+ session: Value,
3010
+ ) -> Value {
3011
+ let secret = "whsec_test_session";
3012
+ let mut object = session;
3013
+ object["metadata"] = json!({"registration_id": reference});
3014
+ let payload = json!({"type": event_type, "data": {"object": object}}).to_string();
3015
+ let header = sign_stripe_header(secret, now_secs(), &payload);
3016
+ let (config, wasm_path) = (checkout_config(1), checkout_wasm_path());
3017
+ let response = webhook_route(
3018
+ &payload,
3019
+ &header,
3020
+ &payments::test_platform_with_secret(secret),
3021
+ Host::new(client, &wasm_path, &config, &lambda_client::NeverInvoker),
3022
+ &crate::commerce_ir::fixture_payments(),
3023
+ )
3024
+ .await;
3025
+ assert_eq!(response["statusCode"], 200, "{response:?}");
3026
+ let read = dispatch::read(client, &wasm_path).await.unwrap();
3027
+ read["instances"][format!("Payments::Payment#{reference}")]["status"].clone()
3028
+ }
3029
+
3030
+ // A completed session settles the Payment only when Stripe says it is paid, for the
3031
+ // Payment's amount, in its currency; a delayed method settles on its own later event.
3032
+ #[tokio::test]
3033
+ async fn webhook_route_settles_only_a_session_paid_in_full() {
3034
+ let reference = "REG-WEBHOOK-PAID";
3035
+ let client = stripe_payment_db("hecks_host_web_test_webhook_paid_in_full", reference).await;
3036
+ let completed = "checkout.session.completed";
3037
+ for (session, why) in [
3038
+ (
3039
+ json!({"payment_status": "unpaid", "amount_total": 4200, "currency": "usd"}),
3040
+ "a delayed method, not yet paid",
3041
+ ),
3042
+ (
3043
+ json!({"payment_status": "paid", "amount_total": 100, "currency": "usd"}),
3044
+ "another amount",
3045
+ ),
3046
+ (
3047
+ json!({"payment_status": "paid", "amount_total": 4200, "currency": "eur"}),
3048
+ "another currency",
3049
+ ),
3050
+ (json!({"id": "cs_bare"}), "no payment facts at all"),
3051
+ ] {
3052
+ let status = deliver_session_event(&client, reference, completed, session).await;
3053
+ assert_eq!(status, "pending", "{why} must not settle the payment");
3054
+ }
3055
+
3056
+ let paid = json!({"payment_status": "paid", "amount_total": 4200, "currency": "usd"});
3057
+ let status = deliver_session_event(
3058
+ &client,
3059
+ reference,
3060
+ "checkout.session.async_payment_succeeded",
3061
+ paid,
3062
+ )
3063
+ .await;
3064
+ assert_eq!(status, "succeeded", "the delayed payment settled");
3065
+ }
3066
+
3067
+ #[tokio::test]
3068
+ async fn webhook_route_fails_a_payment_whose_delayed_payment_failed() {
3069
+ let reference = "REG-WEBHOOK-ASYNC-FAIL";
3070
+ let client = stripe_payment_db("hecks_host_web_test_webhook_async_failed", reference).await;
3071
+ let unpaid = json!({"payment_status": "unpaid", "amount_total": 4200, "currency": "usd"});
3072
+ let failed = "checkout.session.async_payment_failed";
3073
+ assert_eq!(
3074
+ deliver_session_event(&client, reference, failed, unpaid).await,
3075
+ "failed"
3076
+ );
3077
+ }
3078
+
3079
+ #[tokio::test]
3080
+ async fn webhook_route_declines_a_payment_on_checkout_session_expired() {
3081
+ let client = crate::dispatch_tests::tests::memory_store();
3082
+ let config = checkout_memory_config();
2697
3083
  let wasm_path = checkout_wasm_path();
2698
3084
 
2699
3085
  schedule_event(&client, &wasm_path, &config, "webhook-event-2", 4200).await;
@@ -2740,14 +3126,7 @@ mod tests {
2740
3126
 
2741
3127
  #[tokio::test]
2742
3128
  async fn webhook_route_ignores_an_event_type_it_does_not_handle_and_still_answers_200() {
2743
- let client = scratch_db("hecks_host_web_test_webhook_unhandled_type").await;
2744
- provision_lineage(
2745
- &*client.lock().await,
2746
- "CheckoutFixture",
2747
- 1,
2748
- &["Event", "Registration", "Payment"],
2749
- )
2750
- .await;
3129
+ let client = crate::dispatch_tests::tests::memory_store();
2751
3130
 
2752
3131
  let secret = "whsec_test_unhandled";
2753
3132
  let payload = json!({"type": "charge.refunded", "data": {"object": {"metadata": {"registration_id": "whatever"}}}}).to_string();
@@ -2761,7 +3140,7 @@ mod tests {
2761
3140
  Host::new(
2762
3141
  &client,
2763
3142
  &checkout_wasm_path(),
2764
- &checkout_config(1),
3143
+ &checkout_memory_config(),
2765
3144
  &lambda_client::NeverInvoker,
2766
3145
  ),
2767
3146
  &crate::commerce_ir::fixture_payments(),