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
@@ -1,1983 +0,0 @@
1
- //! Host-local JSON port of `rust::kernel::expr::Expr`: `parse` reads every node and `interpret`
2
- //! evaluates every one with the Ruby runtime's semantics (`Evaluator`/`Resolver`): the same
3
- //! answers, null handling and error wording, so a value object's invariants mint the same way.
4
-
5
- use serde_json::Value as Json;
6
-
7
- /// The `cmp` flags of a comparison node: `<`, `==`, and whether the result is negated.
8
- #[derive(Debug, Clone, Copy)]
9
- pub struct Comparison {
10
- pub less_than: bool,
11
- pub equal: bool,
12
- pub negated: bool,
13
- }
14
-
15
- /// Which ends of a string `Expr::Strip` trims.
16
- #[derive(Debug, Clone, Copy)]
17
- pub enum StripSide {
18
- Both,
19
- Left,
20
- Right,
21
- }
22
-
23
- #[derive(Debug, Clone, Copy)]
24
- pub enum BlockMode {
25
- All,
26
- Any,
27
- None,
28
- }
29
-
30
- /// One JSON object per node, tagged by `"op"`; field names match the Hash keys
31
- /// `ast_json.rb` emits.
32
- #[derive(Debug, Clone)]
33
- #[allow(dead_code)]
34
- pub enum Expr {
35
- Or {
36
- left: Box<Self>,
37
- right: Box<Self>,
38
- },
39
- And {
40
- left: Box<Self>,
41
- right: Box<Self>,
42
- },
43
- Not {
44
- expr: Box<Self>,
45
- },
46
- Compare {
47
- cmp: Comparison,
48
- left: Box<Self>,
49
- right: Box<Self>,
50
- },
51
- Include {
52
- haystack: Box<Self>,
53
- needle: Box<Self>,
54
- },
55
- Int {
56
- value: i64,
57
- },
58
- Float {
59
- value: f64,
60
- },
61
- Str {
62
- value: String,
63
- },
64
- Bool {
65
- value: bool,
66
- },
67
- Nil,
68
- Add {
69
- left: Box<Self>,
70
- right: Box<Self>,
71
- },
72
- Sub {
73
- left: Box<Self>,
74
- right: Box<Self>,
75
- },
76
- Mul {
77
- left: Box<Self>,
78
- right: Box<Self>,
79
- },
80
- Div {
81
- left: Box<Self>,
82
- right: Box<Self>,
83
- },
84
- SignTest {
85
- cmp: Comparison,
86
- receiver: Box<Self>,
87
- },
88
- Empty {
89
- receiver: Box<Self>,
90
- },
91
- ToS {
92
- receiver: Box<Self>,
93
- },
94
- Modulo {
95
- receiver: Box<Self>,
96
- divisor: Box<Self>,
97
- },
98
- Size {
99
- receiver: Box<Self>,
100
- },
101
- Lookup {
102
- path: Vec<String>,
103
- },
104
- BlockPredicate {
105
- mode: BlockMode,
106
- receiver: Box<Self>,
107
- param: String,
108
- predicate: Box<Self>,
109
- },
110
- Find {
111
- receiver: Box<Self>,
112
- param: String,
113
- predicate: Box<Self>,
114
- path: Vec<String>,
115
- },
116
- Array {
117
- elements: Vec<Self>,
118
- },
119
- MatchesRegex {
120
- receiver: Box<Self>,
121
- pattern: String,
122
- flags: String,
123
- },
124
- Presence {
125
- receiver: Box<Self>,
126
- negated: bool,
127
- },
128
- Assignment {
129
- receiver: Box<Self>,
130
- negated: bool,
131
- },
132
- Split {
133
- receiver: Box<Self>,
134
- separator: String,
135
- },
136
- Strip {
137
- receiver: Box<Self>,
138
- side: StripSide,
139
- },
140
- StartsWith {
141
- receiver: Box<Self>,
142
- substring: String,
143
- },
144
- EndsWith {
145
- receiver: Box<Self>,
146
- substring: String,
147
- },
148
- First {
149
- receiver: Box<Self>,
150
- },
151
- Last {
152
- receiver: Box<Self>,
153
- },
154
- }
155
-
156
- /// Parses an invariant's `ast` node into an `Expr`.
157
- ///
158
- /// An unrecognized `"op"`, or a malformed shape under a known one, is an `Err`.
159
- pub fn parse(json: &Json) -> Result<Expr, String> {
160
- let op = json
161
- .get("op")
162
- .and_then(Json::as_str)
163
- .ok_or_else(|| format!("{json} has no string \"op\" field"))?;
164
-
165
- let field = |name: &str| {
166
- json.get(name)
167
- .ok_or_else(|| format!("{op} node has no {name:?} field: {json}"))
168
- };
169
- let expr = |name: &str| parse(field(name)?);
170
- let str_field = |name: &str| -> Result<String, String> {
171
- field(name)?
172
- .as_str()
173
- .map(str::to_string)
174
- .ok_or_else(|| format!("{op}'s {name:?} isn't a string: {json}"))
175
- };
176
- // `lookup.path` and `find.path` are segment arrays, never dotted strings.
177
- let path_field = |name: &str| -> Result<Vec<String>, String> {
178
- field(name)?
179
- .as_array()
180
- .ok_or_else(|| format!("{op}'s {name:?} isn't an array: {json}"))?
181
- .iter()
182
- .map(|v| {
183
- v.as_str()
184
- .map(str::to_string)
185
- .ok_or_else(|| format!("{op}'s {name:?} has a non-string element: {json}"))
186
- })
187
- .collect::<Result<Vec<_>, _>>()
188
- };
189
- let comparison = || -> Result<Comparison, String> {
190
- let cmp = field("cmp")?;
191
- Ok(Comparison {
192
- less_than: cmp
193
- .get("less_than")
194
- .and_then(Json::as_bool)
195
- .ok_or_else(|| format!("{op}'s cmp has no less_than: {json}"))?,
196
- equal: cmp
197
- .get("equal")
198
- .and_then(Json::as_bool)
199
- .ok_or_else(|| format!("{op}'s cmp has no equal: {json}"))?,
200
- negated: cmp
201
- .get("negated")
202
- .and_then(Json::as_bool)
203
- .ok_or_else(|| format!("{op}'s cmp has no negated: {json}"))?,
204
- })
205
- };
206
-
207
- Ok(match op {
208
- "or" => Expr::Or {
209
- left: Box::new(expr("left")?),
210
- right: Box::new(expr("right")?),
211
- },
212
- "and" => Expr::And {
213
- left: Box::new(expr("left")?),
214
- right: Box::new(expr("right")?),
215
- },
216
- "not" => Expr::Not {
217
- expr: Box::new(expr("expr")?),
218
- },
219
- "compare" => Expr::Compare {
220
- cmp: comparison()?,
221
- left: Box::new(expr("left")?),
222
- right: Box::new(expr("right")?),
223
- },
224
- "include" => Expr::Include {
225
- haystack: Box::new(expr("haystack")?),
226
- needle: Box::new(expr("needle")?),
227
- },
228
- "int" => Expr::Int {
229
- value: field("value")?
230
- .as_i64()
231
- .ok_or_else(|| format!("int's value isn't an integer: {json}"))?,
232
- },
233
- "float" => Expr::Float {
234
- value: field("value")?
235
- .as_f64()
236
- .ok_or_else(|| format!("float's value isn't a number: {json}"))?,
237
- },
238
- "str" => Expr::Str {
239
- value: str_field("value")?,
240
- },
241
- "bool" => Expr::Bool {
242
- value: field("value")?
243
- .as_bool()
244
- .ok_or_else(|| format!("bool's value isn't a boolean: {json}"))?,
245
- },
246
- "nil" => Expr::Nil,
247
- "add" => Expr::Add {
248
- left: Box::new(expr("left")?),
249
- right: Box::new(expr("right")?),
250
- },
251
- "sub" => Expr::Sub {
252
- left: Box::new(expr("left")?),
253
- right: Box::new(expr("right")?),
254
- },
255
- "mul" => Expr::Mul {
256
- left: Box::new(expr("left")?),
257
- right: Box::new(expr("right")?),
258
- },
259
- "div" => Expr::Div {
260
- left: Box::new(expr("left")?),
261
- right: Box::new(expr("right")?),
262
- },
263
- "sign_test" => Expr::SignTest {
264
- cmp: comparison()?,
265
- receiver: Box::new(expr("receiver")?),
266
- },
267
- "empty" => Expr::Empty {
268
- receiver: Box::new(expr("receiver")?),
269
- },
270
- "to_s" => Expr::ToS {
271
- receiver: Box::new(expr("receiver")?),
272
- },
273
- "modulo" => Expr::Modulo {
274
- receiver: Box::new(expr("receiver")?),
275
- divisor: Box::new(expr("divisor")?),
276
- },
277
- "size" => Expr::Size {
278
- receiver: Box::new(expr("receiver")?),
279
- },
280
- "lookup" => Expr::Lookup {
281
- path: path_field("path")?,
282
- },
283
- "block_predicate" => Expr::BlockPredicate {
284
- mode: match str_field("mode")?.as_str() {
285
- "all" => BlockMode::All,
286
- "any" => BlockMode::Any,
287
- "none" => BlockMode::None,
288
- other => {
289
- return Err(format!(
290
- "block_predicate's mode {other:?} is none of all/any/none: {json}"
291
- ))
292
- }
293
- },
294
- receiver: Box::new(expr("receiver")?),
295
- param: str_field("param")?,
296
- predicate: Box::new(expr("predicate")?),
297
- },
298
- "find" => Expr::Find {
299
- receiver: Box::new(expr("receiver")?),
300
- param: str_field("param")?,
301
- predicate: Box::new(expr("predicate")?),
302
- path: path_field("path")?,
303
- },
304
- "array" => Expr::Array {
305
- elements: field("elements")?
306
- .as_array()
307
- .ok_or_else(|| format!("array's elements isn't an array: {json}"))?
308
- .iter()
309
- .map(parse)
310
- .collect::<Result<Vec<_>, _>>()?,
311
- },
312
- "matches_regex" => Expr::MatchesRegex {
313
- receiver: Box::new(expr("receiver")?),
314
- pattern: str_field("pattern")?,
315
- flags: str_field("flags")?,
316
- },
317
- "presence" => Expr::Presence {
318
- receiver: Box::new(expr("receiver")?),
319
- negated: field("negated")?
320
- .as_bool()
321
- .ok_or_else(|| format!("presence's negated isn't a boolean: {json}"))?,
322
- },
323
- "assignment" => Expr::Assignment {
324
- receiver: Box::new(expr("receiver")?),
325
- negated: field("negated")?
326
- .as_bool()
327
- .ok_or_else(|| format!("assignment's negated isn't a boolean: {json}"))?,
328
- },
329
- "split" => Expr::Split {
330
- receiver: Box::new(expr("receiver")?),
331
- separator: str_field("separator")?,
332
- },
333
- "strip" => Expr::Strip {
334
- receiver: Box::new(expr("receiver")?),
335
- side: match str_field("side")?.as_str() {
336
- "both" => StripSide::Both,
337
- "left" => StripSide::Left,
338
- "right" => StripSide::Right,
339
- other => {
340
- return Err(format!(
341
- "strip's side {other:?} is none of both/left/right: {json}"
342
- ))
343
- }
344
- },
345
- },
346
- "starts_with" => Expr::StartsWith {
347
- receiver: Box::new(expr("receiver")?),
348
- substring: str_field("substring")?,
349
- },
350
- "ends_with" => Expr::EndsWith {
351
- receiver: Box::new(expr("receiver")?),
352
- substring: str_field("substring")?,
353
- },
354
- "first" => Expr::First {
355
- receiver: Box::new(expr("receiver")?),
356
- },
357
- "last" => Expr::Last {
358
- receiver: Box::new(expr("receiver")?),
359
- },
360
- other => return Err(format!("unrecognized expression op {other:?}: {json}")),
361
- })
362
- }
363
-
364
- /// The runtime value an `Expr` evaluates to.
365
- ///
366
- /// Lists are a single `Array` variant: every field here is already a materialised JSON value.
367
- /// An `Object` is a multi-field value object, kept as the JSON it arrived as; a one-field
368
- /// object never survives a lookup (see `unwrap_scalar`).
369
- #[derive(Debug, Clone, PartialEq)]
370
- pub enum Value {
371
- Int(i64),
372
- Float(f64),
373
- Str(String),
374
- Bool(bool),
375
- Nil,
376
- Array(Vec<Self>),
377
- Object(Json),
378
- }
379
-
380
- impl Value {
381
- /// Ruby truthiness: everything but `Nil` and `false`.
382
- ///
383
- /// Invariant results are checked for truthiness, not `== true`, as `given`/`ensures` are.
384
- pub(crate) const fn truthy(&self) -> bool {
385
- !matches!(self, Self::Nil | Self::Bool(false))
386
- }
387
-
388
- fn from_json(json: &Json) -> Result<Self, String> {
389
- match json {
390
- Json::Null => Ok(Self::Nil),
391
- Json::Bool(b) => Ok(Self::Bool(*b)),
392
- Json::Number(n) => n
393
- .as_i64()
394
- .map(Self::Int)
395
- .or_else(|| n.as_f64().map(Self::Float))
396
- .ok_or_else(|| format!("{n} is a number this interpreter cannot represent")),
397
- Json::String(s) => Ok(Self::Str(s.clone())),
398
- Json::Array(items) => Ok(Self::Array(
399
- items
400
- .iter()
401
- .map(Self::from_json)
402
- .collect::<Result<Vec<_>, _>>()?,
403
- )),
404
- Json::Object(_) => Ok(Self::Object(json.clone())),
405
- }
406
- }
407
- }
408
-
409
- /// The value as an `f64` when it is an `Int` or `Float`.
410
- const fn numeric(v: &Value) -> Option<f64> {
411
- match v {
412
- Value::Int(i) => Some(*i as f64),
413
- Value::Float(f) => Some(*f),
414
- _ => None,
415
- }
416
- }
417
-
418
- /// An `Int` and a `Float` are equal only when the float is a whole number that is that integer.
419
- fn int_equals_float(i: i64, f: f64) -> bool {
420
- f.is_finite()
421
- && f.fract() == 0.0
422
- && (-9.223_372_036_854_776e18..9.223_372_036_854_776e18).contains(&f)
423
- && f as i64 == i
424
- }
425
-
426
- /// Ruby's `==` after `Evaluator.equal?`: numbers compare across kinds, and lists and objects
427
- /// compare element by element with the same rule, so `[1] == [1.0]`.
428
- #[allow(clippy::float_cmp)] // reason: Ruby's `Float#==` is exact, and this mirrors it
429
- fn values_equal(l: &Value, r: &Value) -> bool {
430
- match (l, r) {
431
- (Value::Int(a), Value::Int(b)) => a == b,
432
- (Value::Int(i), Value::Float(f)) | (Value::Float(f), Value::Int(i)) => {
433
- int_equals_float(*i, *f)
434
- }
435
- (Value::Float(a), Value::Float(b)) => a == b,
436
- (Value::Array(a), Value::Array(b)) => {
437
- a.len() == b.len() && a.iter().zip(b).all(|(x, y)| values_equal(x, y))
438
- }
439
- (Value::Object(Json::Object(a)), Value::Object(Json::Object(b))) => {
440
- a.len() == b.len()
441
- && a.iter()
442
- .all(|(key, x)| match (b.get(key), Value::from_json(x)) {
443
- (Some(y), Ok(x)) => Value::from_json(y).is_ok_and(|y| values_equal(&x, &y)),
444
- _ => false,
445
- })
446
- }
447
- _ => l == r,
448
- }
449
- }
450
-
451
- /// Orders numbers, then strings; any other pairing is an error worded as Ruby's.
452
- fn less_than(l: &Value, r: &Value) -> Result<bool, String> {
453
- match (l, r) {
454
- (Value::Int(a), Value::Int(b)) => Ok(a < b),
455
- _ => match (numeric(l), numeric(r)) {
456
- (Some(a), Some(b)) => Ok(a < b),
457
- _ => match (l, r) {
458
- (Value::Str(a), Value::Str(b)) => Ok(a < b),
459
- _ => Err(format!(
460
- "comparison of {} with {} failed",
461
- class_of(l),
462
- describe(r)
463
- )),
464
- },
465
- },
466
- }
467
- }
468
-
469
- /// ORs `<` and `==`, then negates if the operator says so; `SignTest` reuses it against 0.
470
- fn apply_comparison(op: Comparison, l: &Value, r: &Value) -> Result<bool, String> {
471
- let lt = op.less_than && less_than(l, r)?;
472
- let eq = op.equal && values_equal(l, r);
473
- Ok(if op.negated { !(lt || eq) } else { lt || eq })
474
- }
475
-
476
- /// Stringifies a scalar as Ruby's `to_s` does (`Nil` becomes `""`); a list or object is an error.
477
- fn to_s(v: &Value) -> Result<String, String> {
478
- match v {
479
- Value::Str(s) => Ok(s.clone()),
480
- Value::Int(i) => Ok(i.to_string()),
481
- Value::Float(f) => Ok(ruby_float(*f)),
482
- Value::Bool(b) => Ok(b.to_string()),
483
- Value::Nil => Ok(String::new()),
484
- Value::Array(_) | Value::Object(_) => {
485
- Err(format!("to_s expects a scalar, got {}", describe(v)))
486
- }
487
- }
488
- }
489
-
490
- /// Ruby's `Float#to_s`: shortest round-trip digits, fixed notation for exponents -4 through 14,
491
- /// `d.ddde+XX` outside that.
492
- #[allow(clippy::expect_used)] // reason: Rust's exponent format always has `e` and an integer exponent
493
- fn ruby_float(f: f64) -> String {
494
- if f.is_nan() {
495
- return "NaN".to_string();
496
- }
497
- if f.is_infinite() {
498
- return if f < 0.0 { "-Infinity" } else { "Infinity" }.to_string();
499
- }
500
- let sign = if f.is_sign_negative() { "-" } else { "" };
501
- if f == 0.0 {
502
- return format!("{sign}0.0");
503
- }
504
- let scientific = format!("{:e}", f.abs());
505
- let (mantissa, exponent) = scientific
506
- .split_once('e')
507
- .expect("exponent form always has an exponent");
508
- let exponent: i32 = exponent.parse().expect("exponent is an integer");
509
- let digits: String = mantissa.chars().filter(|c| *c != '.').collect();
510
- if (-4..15).contains(&exponent) {
511
- if exponent >= 0 {
512
- let whole = exponent as usize + 1;
513
- let (int_part, frac_part) = if digits.len() > whole {
514
- (digits[..whole].to_string(), digits[whole..].to_string())
515
- } else {
516
- (
517
- format!("{digits}{}", "0".repeat(whole - digits.len())),
518
- "0".to_string(),
519
- )
520
- };
521
- format!("{sign}{int_part}.{frac_part}")
522
- } else {
523
- format!("{sign}0.{}{digits}", "0".repeat((-exponent - 1) as usize))
524
- }
525
- } else {
526
- let frac = if digits.len() > 1 { &digits[1..] } else { "0" };
527
- format!(
528
- "{sign}{}.{frac}e{}{:02}",
529
- &digits[..1],
530
- if exponent < 0 { '-' } else { '+' },
531
- exponent.abs()
532
- )
533
- }
534
- }
535
-
536
- /// Ruby's `String#inspect` for a UTF-8 string.
537
- fn ruby_inspect(s: &str) -> String {
538
- use std::fmt::Write;
539
- let mut out = String::from("\"");
540
- let mut chars = s.chars().peekable();
541
- while let Some(c) = chars.next() {
542
- match c {
543
- '"' => out.push_str("\\\""),
544
- '\\' => out.push_str("\\\\"),
545
- '\n' => out.push_str("\\n"),
546
- '\t' => out.push_str("\\t"),
547
- '\r' => out.push_str("\\r"),
548
- '\u{07}' => out.push_str("\\a"),
549
- '\u{08}' => out.push_str("\\b"),
550
- '\u{0b}' => out.push_str("\\v"),
551
- '\u{0c}' => out.push_str("\\f"),
552
- '\u{1b}' => out.push_str("\\e"),
553
- '#' if matches!(chars.peek(), Some('{' | '$' | '@')) => out.push_str("\\#"),
554
- c if (c as u32) < 0x20 || c as u32 == 0x7f => {
555
- let _ = write!(out, "\\u{:04X}", c as u32); // writing to a String cannot fail
556
- }
557
- c => out.push(c),
558
- }
559
- }
560
- out.push('"');
561
- out
562
- }
563
-
564
- /// Ruby's `JSON.generate` for a value: what `Rendering.describe` prints for a list or hash.
565
- fn generate(v: &Value) -> String {
566
- match v {
567
- Value::Nil => "null".to_string(),
568
- Value::Bool(b) => b.to_string(),
569
- Value::Int(i) => i.to_string(),
570
- Value::Float(f) => ruby_float(*f),
571
- Value::Str(s) => Json::String(s.clone()).to_string(),
572
- Value::Array(items) => format!(
573
- "[{}]",
574
- items.iter().map(generate).collect::<Vec<_>>().join(",")
575
- ),
576
- Value::Object(json) => json.to_string(),
577
- }
578
- }
579
-
580
- /// Ruby's `Rendering.describe`: a value as an error message names it.
581
- fn describe(v: &Value) -> String {
582
- match v {
583
- Value::Nil => "nil".to_string(),
584
- Value::Str(s) => ruby_inspect(s),
585
- other => generate(other),
586
- }
587
- }
588
-
589
- /// The Ruby class name an error message gives for a value; `nil` for nil, as `class_of` does.
590
- const fn class_of(v: &Value) -> &'static str {
591
- match v {
592
- Value::Nil => "nil",
593
- Value::Int(_) => "Integer",
594
- Value::Float(_) => "Float",
595
- Value::Str(_) => "String",
596
- Value::Bool(true) => "TrueClass",
597
- Value::Bool(false) => "FalseClass",
598
- Value::Array(_) => "Array",
599
- // A value object's own class name is not in the JSON; Hash is the nearest honest answer.
600
- Value::Object(_) => "Hash",
601
- }
602
- }
603
-
604
- /// Requires an `Int` or `Float`, worded as `Resolver.require_number`.
605
- fn require_number(v: &Value, operation: &str) -> Result<Value, String> {
606
- match v {
607
- Value::Int(_) | Value::Float(_) => Ok(v.clone()),
608
- other => Err(format!(
609
- "{operation} expects a number, got {}",
610
- describe(other)
611
- )),
612
- }
613
- }
614
-
615
- /// `Resolver.add`: 64-bit integer sums that overflow, and float sums that are not finite, fail.
616
- fn add(left: &Value, right: &Value) -> Result<Value, String> {
617
- let lhs = require_number(left, "addition")?;
618
- let rhs = require_number(right, "addition")?;
619
- if let (Value::Int(a), Value::Int(b)) = (&lhs, &rhs) {
620
- return a.checked_add(*b).map(Value::Int).ok_or_else(|| {
621
- format!("addition overflowed: {a} + {b} does not fit in a 64-bit integer")
622
- });
623
- }
624
- let sum = numeric(&lhs).unwrap_or_default() + numeric(&rhs).unwrap_or_default();
625
- if sum.is_finite() {
626
- Ok(Value::Float(sum))
627
- } else {
628
- Err(format!(
629
- "addition overflowed: {} + {} is not a finite number",
630
- to_s(&lhs)?,
631
- to_s(&rhs)?
632
- ))
633
- }
634
- }
635
-
636
- /// `Resolver.checked_arithmetic`: the same 64-bit and finiteness limits `add` holds to, named for
637
- /// the operation that overflowed.
638
- fn arithmetic(
639
- name: &str,
640
- symbol: &str,
641
- left: &Value,
642
- right: &Value,
643
- int_op: fn(i64, i64) -> Option<i64>,
644
- float_op: fn(f64, f64) -> f64,
645
- ) -> Result<Value, String> {
646
- let lhs = require_number(left, name)?;
647
- let rhs = require_number(right, name)?;
648
- if let (Value::Int(a), Value::Int(b)) = (&lhs, &rhs) {
649
- return int_op(*a, *b).map(Value::Int).ok_or_else(|| {
650
- format!("{name} overflowed: {a} {symbol} {b} does not fit in a 64-bit integer")
651
- });
652
- }
653
- let result = float_op(
654
- numeric(&lhs).unwrap_or_default(),
655
- numeric(&rhs).unwrap_or_default(),
656
- );
657
- if result.is_finite() {
658
- Ok(Value::Float(result))
659
- } else {
660
- Err(format!(
661
- "{name} overflowed: {} {symbol} {} is not a finite number",
662
- to_s(&lhs)?,
663
- to_s(&rhs)?
664
- ))
665
- }
666
- }
667
-
668
- /// `Resolver.divide`: a zero divisor faults first; integers floor toward negative infinity.
669
- fn divide(left: &Value, right: &Value) -> Result<Value, String> {
670
- if numeric(&require_number(right, "division")?) == Some(0.0) {
671
- return Err("divided by 0".to_string());
672
- }
673
- arithmetic("division", "/", left, right, floor_div, |a, b| a / b)
674
- }
675
-
676
- /// Floored integer division; `None` only for `i64::MIN / -1`, which does not fit in 64 bits.
677
- fn floor_div(a: i64, b: i64) -> Option<i64> {
678
- let quotient = a.checked_div(b)?;
679
- Some(if a % b != 0 && ((a < 0) != (b < 0)) {
680
- quotient - 1
681
- } else {
682
- quotient
683
- })
684
- }
685
-
686
- /// `Resolver.apply_modulo`: floored like Ruby's `%`, so the result takes the divisor's sign.
687
- fn modulo(receiver: &Value, divisor: &Value) -> Result<Value, String> {
688
- let receiver = require_number(receiver, "modulo")?;
689
- let divisor = require_number(divisor, "modulo")?;
690
- if numeric(&divisor) == Some(0.0) {
691
- return Err("divided by 0".to_string());
692
- }
693
- if let (Value::Int(r), Value::Int(d)) = (&receiver, &divisor) {
694
- // `i64::MIN % -1` overflows in Rust; Ruby answers 0.
695
- if *d == -1 {
696
- return Ok(Value::Int(0));
697
- }
698
- let raw = r % d;
699
- return Ok(Value::Int(if raw != 0 && (raw < 0) != (*d < 0) {
700
- raw + d
701
- } else {
702
- raw
703
- }));
704
- }
705
- let x = numeric(&receiver).unwrap_or_default();
706
- let y = numeric(&divisor).unwrap_or_default();
707
- let remainder = if y.is_infinite() && !x.is_infinite() {
708
- x
709
- } else {
710
- x % y
711
- };
712
- Ok(Value::Float(if y * remainder < 0.0 {
713
- remainder + y
714
- } else {
715
- remainder
716
- }))
717
- }
718
-
719
- /// `Resolver.blank?`: nil, false, and an empty string, list or object.
720
- ///
721
- /// Ruby's version judges a list by its own emptiness, so a non-empty list is present in both
722
- /// hosts, whether or not it is made of pairs.
723
- fn blank(v: &Value) -> bool {
724
- match v {
725
- Value::Nil | Value::Bool(false) => true,
726
- Value::Str(s) => s.is_empty(),
727
- Value::Array(items) => items.is_empty(),
728
- Value::Object(json) => json.as_object().is_some_and(serde_json::Map::is_empty),
729
- _ => false,
730
- }
731
- }
732
-
733
- /// Ruby's `String#split` with a string separator: `" "` splits on runs of whitespace, `""` on
734
- /// characters, anything else on the literal text; trailing empty fields are dropped.
735
- fn split(text: &str, separator: &str) -> Vec<Value> {
736
- let mut parts: Vec<String> = match separator {
737
- " " => text
738
- .split([' ', '\t', '\n', '\u{0b}', '\u{0c}', '\r'])
739
- .filter(|p| !p.is_empty())
740
- .map(str::to_string)
741
- .collect(),
742
- "" => text.chars().map(String::from).collect(),
743
- sep => text.split(sep).map(str::to_string).collect(),
744
- };
745
- while parts.last().is_some_and(String::is_empty) {
746
- parts.pop();
747
- }
748
- parts.into_iter().map(Value::Str).collect()
749
- }
750
-
751
- /// Ruby's `String#strip` family: trims null, tab, line feed, vertical tab, form feed, carriage
752
- /// return and space, never a Unicode space (Rust's own `trim` would drop U+00A0 too).
753
- fn strip(text: &str, side: StripSide) -> String {
754
- let stripped = |c: char| matches!(c, '\0' | '\t' | '\n' | '\u{b}' | '\u{c}' | '\r' | ' ');
755
- match side {
756
- StripSide::Both => text.trim_matches(stripped),
757
- StripSide::Left => text.trim_start_matches(stripped),
758
- StripSide::Right => text.trim_end_matches(stripped),
759
- }
760
- .to_string()
761
- }
762
-
763
- /// Compiles a Ruby `Regexp` source for the `regex` crate.
764
- ///
765
- /// Ruby's `\d \w \s \h` are ASCII and its `^ $` are always line anchors, where `regex` reads
766
- /// the classes as Unicode and the anchors as text anchors, so both are rewritten. `m` is
767
- /// Ruby's dot-matches-newline; `\Z` (end, or before a final newline) becomes `\n?\z`.
768
- fn ruby_regex(pattern: &str, flags: &str) -> Result<regex::Regex, String> {
769
- use std::fmt::Write;
770
- let mut source = String::new();
771
- let mut in_class = false;
772
- let mut class_start = 0;
773
- let chars: Vec<char> = pattern.chars().collect();
774
- let mut index = 0;
775
- while index < chars.len() {
776
- let c = chars[index];
777
- if c == '\\' {
778
- let Some(&next) = chars.get(index + 1) else {
779
- source.push(c);
780
- break;
781
- };
782
- index += 2;
783
- let (positive, negative) = match next.to_ascii_lowercase() {
784
- 'd' => ("0-9", "[^0-9]"),
785
- 'w' => ("A-Za-z0-9_", "[^A-Za-z0-9_]"),
786
- 's' => ("\\x20\\t\\r\\n\\f\\x0B", "[^\\x20\\t\\r\\n\\f\\x0B]"),
787
- 'h' => ("0-9a-fA-F", "[^0-9a-fA-F]"),
788
- _ => {
789
- match next {
790
- 'Z' if !in_class => source.push_str("(?:\\n?\\z)"),
791
- _ => {
792
- source.push(c);
793
- source.push(next);
794
- }
795
- }
796
- continue;
797
- }
798
- };
799
- match (next.is_ascii_uppercase(), in_class) {
800
- (false, true) => source.push_str(positive),
801
- (false, false) => {
802
- let _ = write!(source, "[{positive}]"); // writing to a String cannot fail
803
- }
804
- (true, _) => source.push_str(negative),
805
- }
806
- continue;
807
- }
808
- if in_class {
809
- if c == ']' && index != class_start {
810
- in_class = false;
811
- }
812
- } else if c == '[' {
813
- in_class = true;
814
- class_start = index + 1;
815
- if chars.get(class_start) == Some(&'^') {
816
- class_start += 1;
817
- }
818
- } else if c == '^' || c == '$' {
819
- source.push_str(if c == '^' { "(?m:^)" } else { "(?m:$)" });
820
- index += 1;
821
- continue;
822
- }
823
- source.push(c);
824
- index += 1;
825
- }
826
-
827
- regex::RegexBuilder::new(&source)
828
- .case_insensitive(flags.contains('i'))
829
- .dot_matches_new_line(flags.contains('m'))
830
- .ignore_whitespace(flags.contains('x'))
831
- .build()
832
- .map_err(|e| {
833
- format!(
834
- "match? given an invalid pattern {} — {e}",
835
- ruby_inspect(pattern)
836
- )
837
- })
838
- }
839
-
840
- /// One block parameter bound to its element, innermost first; `attrs` in `Resolver#fetch`.
841
- struct Scope<'a> {
842
- name: &'a str,
843
- value: Value,
844
- outer: Option<&'a Self>,
845
- }
846
-
847
- impl Scope<'_> {
848
- fn get(&self, name: &str) -> Option<&Value> {
849
- if self.name == name {
850
- Some(&self.value)
851
- } else {
852
- self.outer.and_then(|outer| outer.get(name))
853
- }
854
- }
855
- }
856
-
857
- /// Evaluates `expr` against a value object's JSON fields.
858
- ///
859
- /// Every node the Ruby runtime (`Evaluator`/`Resolver`) has is interpreted, with its semantics,
860
- /// null handling and error wording. No command arguments are in scope, so `Lookup` reads
861
- /// `instance` alone, after any enclosing block parameters.
862
- pub fn interpret(expr: &Expr, instance: &Json) -> Result<Value, String> {
863
- eval(expr, instance, None)
864
- }
865
-
866
- fn eval(expr: &Expr, instance: &Json, scope: Option<&Scope>) -> Result<Value, String> {
867
- let recur = |e: &Expr| eval(e, instance, scope);
868
- match expr {
869
- Expr::Int { value } => Ok(Value::Int(*value)),
870
- Expr::Float { value } => Ok(Value::Float(*value)),
871
- Expr::Str { value } => Ok(Value::Str(value.clone())),
872
- Expr::Bool { value } => Ok(Value::Bool(*value)),
873
- Expr::Nil => Ok(Value::Nil),
874
- Expr::Array { elements } => Ok(Value::Array(
875
- elements.iter().map(recur).collect::<Result<Vec<_>, _>>()?,
876
- )),
877
- Expr::Lookup { path } => lookup(path, instance, scope),
878
- Expr::Or { left, right } => {
879
- Ok(Value::Bool(recur(left)?.truthy() || recur(right)?.truthy()))
880
- }
881
- Expr::And { left, right } => {
882
- Ok(Value::Bool(recur(left)?.truthy() && recur(right)?.truthy()))
883
- }
884
- Expr::Not { expr } => Ok(Value::Bool(!recur(expr)?.truthy())),
885
- Expr::Compare { cmp, left, right } => Ok(Value::Bool(apply_comparison(
886
- *cmp,
887
- &recur(left)?,
888
- &recur(right)?,
889
- )?)),
890
- // The needle resolves before the haystack, as `Evaluator.includes?` does.
891
- Expr::Include { haystack, needle } => {
892
- let wanted = recur(needle)?;
893
- match recur(haystack)? {
894
- Value::Array(items) => Ok(Value::Bool(
895
- items.iter().any(|item| values_equal(item, &wanted)),
896
- )),
897
- Value::Str(text) => match &wanted {
898
- Value::Str(part) => Ok(Value::Bool(text.contains(part.as_str()))),
899
- other => Err(format!(
900
- "no implicit conversion of {} into String",
901
- class_of(other)
902
- )),
903
- },
904
- _ => Ok(Value::Bool(false)),
905
- }
906
- }
907
- Expr::Add { left, right } => add(&recur(left)?, &recur(right)?),
908
- Expr::Sub { left, right } => arithmetic(
909
- "subtraction",
910
- "-",
911
- &recur(left)?,
912
- &recur(right)?,
913
- i64::checked_sub,
914
- |a, b| a - b,
915
- ),
916
- Expr::Mul { left, right } => arithmetic(
917
- "multiplication",
918
- "*",
919
- &recur(left)?,
920
- &recur(right)?,
921
- i64::checked_mul,
922
- |a, b| a * b,
923
- ),
924
- Expr::Div { left, right } => divide(&recur(left)?, &recur(right)?),
925
- Expr::Modulo { receiver, divisor } => modulo(&recur(receiver)?, &recur(divisor)?),
926
- Expr::SignTest { cmp, receiver } => {
927
- let v = recur(receiver)?;
928
- if numeric(&v).is_none() {
929
- return Err(format!(
930
- "{} expects a number, got {}",
931
- sign_test_name(*cmp),
932
- describe(&v)
933
- ));
934
- }
935
- Ok(Value::Bool(apply_comparison(*cmp, &v, &Value::Int(0))?))
936
- }
937
- Expr::Empty { receiver } => match recur(receiver)? {
938
- Value::Str(s) => Ok(Value::Bool(s.is_empty())),
939
- Value::Array(items) => Ok(Value::Bool(items.is_empty())),
940
- other => Err(format!(
941
- "empty? expects a list or string, got {}",
942
- describe(&other)
943
- )),
944
- },
945
- Expr::Size { receiver } => match recur(receiver)? {
946
- Value::Str(s) => Ok(Value::Int(s.chars().count() as i64)),
947
- Value::Array(items) => Ok(Value::Int(items.len() as i64)),
948
- other => Err(format!(
949
- "size expects a list or string, got {}",
950
- describe(&other)
951
- )),
952
- },
953
- Expr::ToS { receiver } => Ok(Value::Str(to_s(&recur(receiver)?)?)),
954
- Expr::MatchesRegex {
955
- receiver,
956
- pattern,
957
- flags,
958
- } => {
959
- let text = match recur(receiver)? {
960
- Value::Str(s) => s,
961
- v @ (Value::Int(_) | Value::Float(_)) => to_s(&v)?,
962
- Value::Nil => String::new(),
963
- other => return Err(format!("match? expects a scalar, got {}", class_of(&other))),
964
- };
965
- Ok(Value::Bool(ruby_regex(pattern, flags)?.is_match(&text)))
966
- }
967
- Expr::Presence { receiver, negated } => {
968
- Ok(Value::Bool(blank(&recur(receiver)?) == *negated))
969
- }
970
- Expr::Assignment { receiver, negated } => {
971
- Ok(Value::Bool((recur(receiver)? == Value::Nil) == *negated))
972
- }
973
- Expr::Split {
974
- receiver,
975
- separator,
976
- } => match recur(receiver)? {
977
- Value::Str(text) => Ok(Value::Array(split(&text, separator))),
978
- other => Err(format!("split expects a string, got {}", describe(&other))),
979
- },
980
- Expr::First { receiver } => match recur(receiver)? {
981
- Value::Array(items) => Ok(items.into_iter().next().unwrap_or(Value::Nil)),
982
- other => Err(format!("first expects a list, got {}", describe(&other))),
983
- },
984
- Expr::Last { receiver } => match recur(receiver)? {
985
- Value::Array(items) => Ok(items.into_iter().next_back().unwrap_or(Value::Nil)),
986
- other => Err(format!("last expects a list, got {}", describe(&other))),
987
- },
988
- Expr::Strip { receiver, side } => match recur(receiver)? {
989
- Value::Str(text) => Ok(Value::Str(strip(&text, *side))),
990
- other => Err(format!("strip expects a string, got {}", describe(&other))),
991
- },
992
- Expr::StartsWith {
993
- receiver,
994
- substring,
995
- } => match recur(receiver)? {
996
- Value::Str(text) => Ok(Value::Bool(text.starts_with(substring.as_str()))),
997
- other => Err(format!(
998
- "start_with? expects a string, got {}",
999
- describe(&other)
1000
- )),
1001
- },
1002
- Expr::EndsWith {
1003
- receiver,
1004
- substring,
1005
- } => match recur(receiver)? {
1006
- Value::Str(text) => Ok(Value::Bool(text.ends_with(substring.as_str()))),
1007
- other => Err(format!(
1008
- "end_with? expects a string, got {}",
1009
- describe(&other)
1010
- )),
1011
- },
1012
- // Every element is evaluated before aggregating, so an error on any element surfaces as
1013
- // it does in Ruby.
1014
- Expr::BlockPredicate {
1015
- mode,
1016
- receiver,
1017
- param,
1018
- predicate,
1019
- } => {
1020
- let mode_name = match mode {
1021
- BlockMode::All => "all",
1022
- BlockMode::Any => "any",
1023
- BlockMode::None => "none",
1024
- };
1025
- let items = match recur(receiver)? {
1026
- Value::Array(items) => items,
1027
- other => {
1028
- return Err(format!(
1029
- "{mode_name}? expects a list, got {}",
1030
- describe(&other)
1031
- ))
1032
- }
1033
- };
1034
- let mut outcomes = Vec::with_capacity(items.len());
1035
- for item in items {
1036
- outcomes.push(bound(param, item, instance, scope, predicate)?.truthy());
1037
- }
1038
- Ok(Value::Bool(match mode {
1039
- BlockMode::All => outcomes.iter().all(|o| *o),
1040
- BlockMode::Any => outcomes.iter().any(|o| *o),
1041
- BlockMode::None => !outcomes.iter().any(|o| *o),
1042
- }))
1043
- }
1044
- // The first accepted element ends the scan; a miss is nil, not an error.
1045
- Expr::Find {
1046
- receiver,
1047
- param,
1048
- predicate,
1049
- path,
1050
- } => {
1051
- let items = match recur(receiver)? {
1052
- Value::Array(items) => items,
1053
- other => return Err(format!("find expects a list, got {}", describe(&other))),
1054
- };
1055
- for item in items {
1056
- if bound(param, item.clone(), instance, scope, predicate)?.truthy() {
1057
- return if path.is_empty() {
1058
- Ok(unwrap_scalar(item))
1059
- } else {
1060
- Ok(unwrap_scalar(walk_path(item, path)?))
1061
- };
1062
- }
1063
- }
1064
- Ok(Value::Nil)
1065
- }
1066
- }
1067
- }
1068
-
1069
- /// Evaluates a block's `predicate` with `param` bound to `item`, shadowing any same-named field.
1070
- fn bound(
1071
- param: &str,
1072
- item: Value,
1073
- instance: &Json,
1074
- scope: Option<&Scope>,
1075
- predicate: &Expr,
1076
- ) -> Result<Value, String> {
1077
- let inner = Scope {
1078
- name: param,
1079
- value: item,
1080
- outer: scope,
1081
- };
1082
- eval(predicate, instance, Some(&inner))
1083
- }
1084
-
1085
- /// The `sign_test` a comparison's flags spell, for the error message: `positive?` is
1086
- /// `!(< 0 || == 0)`, `negative?` is `< 0`, `zero?` is `== 0`.
1087
- const fn sign_test_name(cmp: Comparison) -> &'static str {
1088
- match (cmp.less_than, cmp.equal) {
1089
- (true, true) => "positive?",
1090
- (true, false) => "negative?",
1091
- _ => "zero?",
1092
- }
1093
- }
1094
-
1095
- /// `Resolver.unwrap_scalar`: a one-field value object reads as its one value.
1096
- fn unwrap_scalar(v: Value) -> Value {
1097
- let sole = match &v {
1098
- Value::Object(Json::Object(fields)) if fields.len() == 1 => fields
1099
- .values()
1100
- .next()
1101
- .and_then(|only| Value::from_json(only).ok()),
1102
- _ => None,
1103
- };
1104
- sole.unwrap_or(v)
1105
- }
1106
-
1107
- /// `Resolver.walk_path`: each segment indexes the current value. An object yields the field or
1108
- /// nil; a string yields the segment when it is a substring (Ruby's `String#[]`), else nil; a
1109
- /// list or integer cannot be indexed by name; nil, floats and booleans end the walk in nil.
1110
- /// That last fallthrough is what makes `!flag.nil?` (`["flag", "nil?"]`) mean "does `flag` exist".
1111
- fn walk_path(start: Value, segments: &[String]) -> Result<Value, String> {
1112
- let mut current = start;
1113
- for segment in segments {
1114
- current = match &current {
1115
- Value::Object(Json::Object(fields)) => match fields.get(segment) {
1116
- Some(next) => Value::from_json(next)?,
1117
- None => Value::Nil,
1118
- },
1119
- Value::Str(text) if text.contains(segment.as_str()) => Value::Str(segment.clone()),
1120
- Value::Str(_) => Value::Nil,
1121
- Value::Array(_) | Value::Int(_) => {
1122
- return Err(format!(
1123
- "cannot read {} from {}",
1124
- ruby_inspect(segment),
1125
- describe(&current)
1126
- ))
1127
- }
1128
- _ => return Ok(Value::Nil),
1129
- };
1130
- }
1131
- Ok(current)
1132
- }
1133
-
1134
- /// Resolves a path as `Resolver#lookup`: the head is a block parameter (innermost first) or a
1135
- /// field of `instance`, and must exist; later segments go through `walk_path`.
1136
- fn lookup(path: &[String], instance: &Json, scope: Option<&Scope>) -> Result<Value, String> {
1137
- let Some((head, rest)) = path.split_first() else {
1138
- return Err(format!("lookup given an empty path (path {path:?})"));
1139
- };
1140
- let start = match scope.and_then(|s| s.get(head)) {
1141
- Some(value) => value.clone(),
1142
- None => Value::from_json(instance.get(head).ok_or_else(|| {
1143
- format!(
1144
- "cannot resolve {} — no such attribute or argument",
1145
- ruby_inspect(head)
1146
- )
1147
- })?)?,
1148
- };
1149
- Ok(unwrap_scalar(walk_path(start, rest)?))
1150
- }
1151
-
1152
- #[cfg(test)]
1153
- mod tests {
1154
- use super::*;
1155
-
1156
- #[allow(clippy::needless_pass_by_value)] // reason: test helper called with inline literals
1157
- fn field(name: &str, value: Json) -> Json {
1158
- serde_json::json!({ name: value })
1159
- }
1160
-
1161
- #[test]
1162
- fn cents_gte_zero_holds_for_a_non_negative_amount() {
1163
- // `cents >= 0`, the shape `AstJson.emit_predicate` builds.
1164
- let ast = serde_json::json!({"op":"compare","cmp":{"less_than":true,"equal":false,"negated":true},
1165
- "left":{"op":"lookup","path":["cents"]},"right":{"op":"int","value":0}});
1166
- let expr = parse(&ast).expect("valid Expr JSON");
1167
-
1168
- assert_eq!(
1169
- interpret(&expr, &field("cents", serde_json::json!(1200))),
1170
- Ok(Value::Bool(true))
1171
- );
1172
- assert_eq!(
1173
- interpret(&expr, &field("cents", serde_json::json!(0))),
1174
- Ok(Value::Bool(true))
1175
- );
1176
- assert_eq!(
1177
- interpret(&expr, &field("cents", serde_json::json!(-1))),
1178
- Ok(Value::Bool(false))
1179
- );
1180
- }
1181
-
1182
- #[test]
1183
- fn a_compound_and_of_four_comparisons_matches_chess_square_bounds() {
1184
- // `file >= 0 && file <= 7 && rank >= 0 && rank <= 7`, chess.bluebook's `Square`.
1185
- fn cmp(
1186
- less_than: bool,
1187
- equal: bool,
1188
- negated: bool,
1189
- field: &str,
1190
- n: i64,
1191
- ) -> serde_json::Value {
1192
- serde_json::json!({"op":"compare","cmp":{"less_than":less_than,"equal":equal,"negated":negated},
1193
- "left":{"op":"lookup","path":[field]},"right":{"op":"int","value":n}})
1194
- }
1195
- let ast = serde_json::json!({"op":"and","left":{"op":"and","left":{"op":"and",
1196
- "left": cmp(true, false, true, "file", 0),
1197
- "right": cmp(true, true, false, "file", 7)},
1198
- "right": cmp(true, false, true, "rank", 0)},
1199
- "right": cmp(true, true, false, "rank", 7)});
1200
- let expr = parse(&ast).expect("valid Expr JSON");
1201
-
1202
- let on_board = serde_json::json!({"file": 3, "rank": 7});
1203
- let off_board = serde_json::json!({"file": 8, "rank": 0});
1204
- assert_eq!(interpret(&expr, &on_board), Ok(Value::Bool(true)));
1205
- assert_eq!(interpret(&expr, &off_board), Ok(Value::Bool(false)));
1206
- }
1207
-
1208
- #[test]
1209
- fn a_blank_string_fails_the_not_empty_to_s_pattern() {
1210
- // `!value.to_s.empty?`, the most common real invariant shape.
1211
- let ast = serde_json::json!({"op":"not","expr":{"op":"empty","receiver":{"op":"to_s","receiver":{"op":"lookup","path":["value"]}}}});
1212
- let expr = parse(&ast).expect("valid Expr JSON");
1213
-
1214
- assert_eq!(
1215
- interpret(&expr, &field("value", serde_json::json!("Margherita"))),
1216
- Ok(Value::Bool(true))
1217
- );
1218
- assert_eq!(
1219
- interpret(&expr, &field("value", serde_json::json!(""))),
1220
- Ok(Value::Bool(false))
1221
- );
1222
- }
1223
-
1224
- #[test]
1225
- fn sign_test_positive_matches_a_real_amount_invariant() {
1226
- // `value.positive?` compiles to NOT(value < 0 or value == 0).
1227
- let ast = serde_json::json!({"op":"sign_test","cmp":{"less_than":true,"equal":true,"negated":true},"receiver":{"op":"lookup","path":["value"]}});
1228
- let expr = parse(&ast).expect("valid Expr JSON");
1229
-
1230
- assert_eq!(
1231
- interpret(&expr, &field("value", serde_json::json!(5))),
1232
- Ok(Value::Bool(true))
1233
- );
1234
- assert_eq!(
1235
- interpret(&expr, &field("value", serde_json::json!(0))),
1236
- Ok(Value::Bool(false))
1237
- );
1238
- assert_eq!(
1239
- interpret(&expr, &field("value", serde_json::json!(-5))),
1240
- Ok(Value::Bool(false))
1241
- );
1242
- }
1243
-
1244
- #[test]
1245
- fn int_and_float_compare_equal_across_kinds_the_same_way_the_kernel_does() {
1246
- let ast = serde_json::json!({"op":"compare","cmp":{"less_than":false,"equal":true,"negated":false},
1247
- "left":{"op":"lookup","path":["cents"]},"right":{"op":"float","value":3.0}});
1248
- let expr = parse(&ast).expect("valid Expr JSON");
1249
-
1250
- assert_eq!(
1251
- interpret(&expr, &field("cents", serde_json::json!(3))),
1252
- Ok(Value::Bool(true))
1253
- );
1254
- }
1255
-
1256
- #[test]
1257
- fn an_unrecognized_op_refuses_to_parse_rather_than_defaulting() {
1258
- let ast = serde_json::json!({"op":"frobnicate"});
1259
- let err = parse(&ast).unwrap_err();
1260
- assert!(err.contains("unrecognized"), "{err}");
1261
- }
1262
-
1263
- // Expected values below are real Ruby results (`Resolver.resolve` / `Evaluator.call`); the
1264
- // differential spec `spec/rust_host_expr_json_conformance_spec.rb` re-checks them live.
1265
- use serde_json::json;
1266
-
1267
- #[allow(clippy::needless_pass_by_value)] // reason: test helper called with inline literals
1268
- fn run(ast: Json, instance: Json) -> Result<Value, String> {
1269
- interpret(&parse(&ast).expect("valid Expr JSON"), &instance)
1270
- }
1271
-
1272
- fn lookup_of(name: &str) -> Json {
1273
- json!({"op":"lookup","path":[name]})
1274
- }
1275
-
1276
- fn int(n: i64) -> Json {
1277
- json!({"op":"int","value":n})
1278
- }
1279
-
1280
- fn string(s: &str) -> Json {
1281
- json!({"op":"str","value":s})
1282
- }
1283
-
1284
- #[allow(clippy::needless_pass_by_value)] // reason: test helper called with inline literals
1285
- fn call(op: &str, receiver: Json) -> Json {
1286
- json!({"op":op,"receiver":receiver})
1287
- }
1288
-
1289
- #[allow(clippy::needless_pass_by_value)] // reason: test helper called with inline literals
1290
- fn eq(left: Json, right: Json) -> Json {
1291
- json!({"op":"compare","cmp":{"less_than":false,"equal":true,"negated":false},"left":left,"right":right})
1292
- }
1293
-
1294
- fn strs(items: &[&str]) -> Value {
1295
- Value::Array(items.iter().map(|s| Value::Str(s.to_string())).collect())
1296
- }
1297
-
1298
- #[test]
1299
- fn add_sums_numbers_and_promotes_to_float() {
1300
- let add = |l: Json, r: Json| json!({"op":"add","left":l,"right":r});
1301
- assert_eq!(run(add(int(2), int(3)), json!({})), Ok(Value::Int(5)));
1302
- assert_eq!(
1303
- run(add(int(2), json!({"op":"float","value":1.5})), json!({})),
1304
- Ok(Value::Float(3.5))
1305
- );
1306
- assert_eq!(
1307
- run(add(lookup_of("a"), int(1)), json!({"a": 9})),
1308
- Ok(Value::Int(10))
1309
- );
1310
- }
1311
-
1312
- #[test]
1313
- fn add_refuses_a_non_number_and_an_overflow_with_rubys_words() {
1314
- let add = |l: Json, r: Json| json!({"op":"add","left":l,"right":r});
1315
- assert_eq!(
1316
- run(add(string("a"), int(1)), json!({})),
1317
- Err("addition expects a number, got \"a\"".to_string())
1318
- );
1319
- assert_eq!(
1320
- run(add(lookup_of("a"), int(1)), json!({"a": null})),
1321
- Err("addition expects a number, got nil".to_string())
1322
- );
1323
- assert_eq!(
1324
- run(add(int(1), lookup_of("a")), json!({"a": [1]})),
1325
- Err("addition expects a number, got [1]".to_string())
1326
- );
1327
- assert_eq!(
1328
- run(add(int(i64::MAX), int(1)), json!({})),
1329
- Err(
1330
- "addition overflowed: 9223372036854775807 + 1 does not fit in a 64-bit integer"
1331
- .to_string()
1332
- )
1333
- );
1334
- let huge = json!({"op":"float","value":1.7e308});
1335
- assert_eq!(
1336
- run(add(huge.clone(), huge), json!({})),
1337
- Err("addition overflowed: 1.7e+308 + 1.7e+308 is not a finite number".to_string())
1338
- );
1339
- }
1340
-
1341
- #[test]
1342
- fn sub_mul_div_follow_rubys_limits_and_floor_integer_division() {
1343
- let op = |name: &str, l: Json, r: Json| json!({"op":name,"left":l,"right":r});
1344
- assert_eq!(run(op("sub", int(5), int(2)), json!({})), Ok(Value::Int(3)));
1345
- assert_eq!(
1346
- run(
1347
- op("mul", int(6), json!({"op":"float","value":0.5})),
1348
- json!({})
1349
- ),
1350
- Ok(Value::Float(3.0))
1351
- );
1352
- assert_eq!(
1353
- run(op("div", int(-7), int(2)), json!({})),
1354
- Ok(Value::Int(-4))
1355
- );
1356
- assert_eq!(
1357
- run(op("div", int(7), int(-2)), json!({})),
1358
- Ok(Value::Int(-4))
1359
- );
1360
- assert_eq!(
1361
- run(op("div", int(7), int(0)), json!({})),
1362
- Err("divided by 0".to_string())
1363
- );
1364
- assert_eq!(
1365
- run(op("mul", int(i64::MAX), int(2)), json!({})),
1366
- Err("multiplication overflowed: 9223372036854775807 * 2 does not fit in a 64-bit integer".to_string())
1367
- );
1368
- assert_eq!(
1369
- run(op("div", int(i64::MIN), int(-1)), json!({})),
1370
- Err(
1371
- "division overflowed: -9223372036854775808 / -1 does not fit in a 64-bit integer"
1372
- .to_string()
1373
- )
1374
- );
1375
- assert_eq!(
1376
- run(op("sub", lookup_of("a"), int(1)), json!({"a": null})),
1377
- Err("subtraction expects a number, got nil".to_string())
1378
- );
1379
- }
1380
-
1381
- #[test]
1382
- fn modulo_floors_like_ruby_for_integers_and_floats() {
1383
- let modulo = |r: Json, d: Json| json!({"op":"modulo","receiver":r,"divisor":d});
1384
- assert_eq!(run(modulo(int(-7), int(3)), json!({})), Ok(Value::Int(2)));
1385
- assert_eq!(run(modulo(int(7), int(-3)), json!({})), Ok(Value::Int(-2)));
1386
- assert_eq!(
1387
- run(modulo(int(i64::MIN), int(-1)), json!({})),
1388
- Ok(Value::Int(0))
1389
- );
1390
- assert_eq!(
1391
- run(modulo(json!({"op":"float","value":5.5}), int(2)), json!({})),
1392
- Ok(Value::Float(1.5))
1393
- );
1394
- assert_eq!(
1395
- run(
1396
- modulo(int(7), json!({"op":"float","value":-2.5})),
1397
- json!({})
1398
- ),
1399
- Ok(Value::Float(-0.5))
1400
- );
1401
- assert_eq!(
1402
- run(
1403
- modulo(json!({"op":"float","value":-1.0}), int(2)),
1404
- json!({})
1405
- ),
1406
- Ok(Value::Float(1.0))
1407
- );
1408
- }
1409
-
1410
- #[test]
1411
- fn modulo_refuses_a_zero_divisor_and_a_non_number() {
1412
- let modulo = |r: Json, d: Json| json!({"op":"modulo","receiver":r,"divisor":d});
1413
- assert_eq!(
1414
- run(modulo(int(5), int(0)), json!({})),
1415
- Err("divided by 0".to_string())
1416
- );
1417
- assert_eq!(
1418
- run(modulo(int(5), json!({"op":"float","value":0.0})), json!({})),
1419
- Err("divided by 0".to_string())
1420
- );
1421
- assert_eq!(
1422
- run(modulo(string("a"), int(2)), json!({})),
1423
- Err("modulo expects a number, got \"a\"".to_string())
1424
- );
1425
- assert_eq!(
1426
- run(modulo(int(2), lookup_of("d")), json!({"d": null})),
1427
- Err("modulo expects a number, got nil".to_string())
1428
- );
1429
- }
1430
-
1431
- #[test]
1432
- fn include_tests_list_membership_with_numeric_coercion_and_substrings() {
1433
- let include = |h: Json, n: Json| json!({"op":"include","haystack":h,"needle":n});
1434
- assert_eq!(
1435
- run(
1436
- include(lookup_of("tags"), string("b")),
1437
- json!({"tags": ["a", "b"]})
1438
- ),
1439
- Ok(Value::Bool(true))
1440
- );
1441
- assert_eq!(
1442
- run(
1443
- include(lookup_of("tags"), string("z")),
1444
- json!({"tags": ["a", "b"]})
1445
- ),
1446
- Ok(Value::Bool(false))
1447
- );
1448
- assert_eq!(
1449
- run(
1450
- include(lookup_of("n"), json!({"op":"float","value":2.0})),
1451
- json!({"n": [1, 2]})
1452
- ),
1453
- Ok(Value::Bool(true))
1454
- );
1455
- assert_eq!(
1456
- run(
1457
- include(lookup_of("n"), lookup_of("m")),
1458
- json!({"n": [1, null], "m": null})
1459
- ),
1460
- Ok(Value::Bool(true))
1461
- );
1462
- assert_eq!(
1463
- run(
1464
- include(lookup_of("s"), string("ell")),
1465
- json!({"s": "hello"})
1466
- ),
1467
- Ok(Value::Bool(true))
1468
- );
1469
- assert_eq!(
1470
- run(include(lookup_of("s"), string("")), json!({"s": "hello"})),
1471
- Ok(Value::Bool(true))
1472
- );
1473
- }
1474
-
1475
- #[test]
1476
- fn include_on_anything_but_a_list_or_string_is_false_and_a_string_needs_a_string() {
1477
- let include = |h: Json, n: Json| json!({"op":"include","haystack":h,"needle":n});
1478
- assert_eq!(
1479
- run(include(lookup_of("s"), string("a")), json!({"s": null})),
1480
- Ok(Value::Bool(false))
1481
- );
1482
- assert_eq!(
1483
- run(include(lookup_of("s"), string("a")), json!({"s": 5})),
1484
- Ok(Value::Bool(false))
1485
- );
1486
- assert_eq!(
1487
- run(include(lookup_of("s"), int(1)), json!({"s": "abc"})),
1488
- Err("no implicit conversion of Integer into String".to_string())
1489
- );
1490
- assert_eq!(
1491
- run(
1492
- include(lookup_of("s"), lookup_of("n")),
1493
- json!({"s": "abc", "n": null})
1494
- ),
1495
- Err("no implicit conversion of nil into String".to_string())
1496
- );
1497
- }
1498
-
1499
- #[test]
1500
- fn array_literals_evaluate_each_element_and_compose_with_first_last_and_include() {
1501
- let array = json!({"op":"array","elements":[int(1), json!({"op":"float","value":2.5}), string("a"), json!({"op":"nil"})]});
1502
- assert_eq!(
1503
- run(array.clone(), json!({})),
1504
- Ok(Value::Array(vec![
1505
- Value::Int(1),
1506
- Value::Float(2.5),
1507
- Value::Str("a".to_string()),
1508
- Value::Nil
1509
- ]))
1510
- );
1511
- assert_eq!(
1512
- run(call("first", array.clone()), json!({})),
1513
- Ok(Value::Int(1))
1514
- );
1515
- assert_eq!(run(call("last", array), json!({})), Ok(Value::Nil));
1516
- assert_eq!(
1517
- run(json!({"op":"array","elements":[]}), json!({})),
1518
- Ok(Value::Array(vec![]))
1519
- );
1520
- }
1521
-
1522
- #[test]
1523
- fn matches_regex_reads_ruby_anchors_flags_and_ascii_classes() {
1524
- let matches = |pattern: &str, flags: &str| json!({"op":"matches_regex","receiver":lookup_of("x"),"pattern":pattern,"flags":flags});
1525
- assert_eq!(
1526
- run(matches("^b$", ""), json!({"x": "a\nb"})),
1527
- Ok(Value::Bool(true))
1528
- );
1529
- assert_eq!(
1530
- run(matches("\\Ab\\z", ""), json!({"x": "a\nb"})),
1531
- Ok(Value::Bool(false))
1532
- );
1533
- assert_eq!(
1534
- run(matches("a.b", ""), json!({"x": "a\nb"})),
1535
- Ok(Value::Bool(false))
1536
- );
1537
- assert_eq!(
1538
- run(matches("a.b", "m"), json!({"x": "a\nb"})),
1539
- Ok(Value::Bool(true))
1540
- );
1541
- assert_eq!(
1542
- run(matches("A", "i"), json!({"x": "a"})),
1543
- Ok(Value::Bool(true))
1544
- );
1545
- assert_eq!(
1546
- run(matches("\\d", ""), json!({"x": "\u{663}"})),
1547
- Ok(Value::Bool(false))
1548
- );
1549
- assert_eq!(
1550
- run(matches("\\A[\\w.]+\\z", ""), json!({"x": "a.b_c"})),
1551
- Ok(Value::Bool(true))
1552
- );
1553
- assert_eq!(
1554
- run(matches("a b", "x"), json!({"x": "ab"})),
1555
- Ok(Value::Bool(true))
1556
- );
1557
- assert_eq!(
1558
- run(matches("\\Aa\\Z", ""), json!({"x": "a\n"})),
1559
- Ok(Value::Bool(true))
1560
- );
1561
- }
1562
-
1563
- #[test]
1564
- fn matches_regex_coerces_scalars_and_refuses_the_rest_by_class() {
1565
- let matches = json!({"op":"matches_regex","receiver":lookup_of("x"),"pattern":"\\A[0-9.]+\\z","flags":""});
1566
- assert_eq!(
1567
- run(matches.clone(), json!({"x": 12})),
1568
- Ok(Value::Bool(true))
1569
- );
1570
- assert_eq!(
1571
- run(matches.clone(), json!({"x": 1.5})),
1572
- Ok(Value::Bool(true))
1573
- );
1574
- assert_eq!(
1575
- run(
1576
- json!({"op":"matches_regex","receiver":lookup_of("x"),"pattern":"\\A\\z","flags":""}),
1577
- json!({"x": null})
1578
- ),
1579
- Ok(Value::Bool(true))
1580
- );
1581
- assert_eq!(
1582
- run(matches.clone(), json!({"x": true})),
1583
- Err("match? expects a scalar, got TrueClass".to_string())
1584
- );
1585
- assert_eq!(
1586
- run(matches.clone(), json!({"x": false})),
1587
- Err("match? expects a scalar, got FalseClass".to_string())
1588
- );
1589
- assert_eq!(
1590
- run(matches, json!({"x": [1]})),
1591
- Err("match? expects a scalar, got Array".to_string())
1592
- );
1593
- }
1594
-
1595
- #[test]
1596
- fn matches_regex_refuses_an_invalid_pattern_by_name() {
1597
- let err = run(
1598
- json!({"op":"matches_regex","receiver":string("a"),"pattern":"(","flags":""}),
1599
- json!({}),
1600
- )
1601
- .unwrap_err();
1602
- assert!(
1603
- err.starts_with("match? given an invalid pattern \"(\" — "),
1604
- "{err}"
1605
- );
1606
- }
1607
-
1608
- #[test]
1609
- fn block_predicates_aggregate_with_ruby_answers_for_an_empty_list() {
1610
- let block = |mode: &str| {
1611
- json!({"op":"block_predicate","mode":mode,"receiver":lookup_of("seats"),"param":"s",
1612
- "predicate":eq(json!({"op":"lookup","path":["s","taken"]}), json!({"op":"bool","value":false}))})
1613
- };
1614
- let seats = json!({"seats": [{"n": 1, "taken": true}, {"n": 2, "taken": false}]});
1615
- assert_eq!(run(block("any"), seats.clone()), Ok(Value::Bool(true)));
1616
- assert_eq!(run(block("all"), seats.clone()), Ok(Value::Bool(false)));
1617
- assert_eq!(run(block("none"), seats), Ok(Value::Bool(false)));
1618
- let empty = json!({"seats": []});
1619
- assert_eq!(run(block("all"), empty.clone()), Ok(Value::Bool(true)));
1620
- assert_eq!(run(block("any"), empty.clone()), Ok(Value::Bool(false)));
1621
- assert_eq!(run(block("none"), empty), Ok(Value::Bool(true)));
1622
- }
1623
-
1624
- #[test]
1625
- fn a_block_predicate_evaluates_every_element_so_a_later_error_still_surfaces() {
1626
- // `all?` would stop at the first false in a short-circuit loop; Ruby maps first.
1627
- let ast = json!({"op":"block_predicate","mode":"all","receiver":lookup_of("xs"),"param":"x",
1628
- "predicate":json!({"op":"sign_test","cmp":{"less_than":true,"equal":true,"negated":true},"receiver":lookup_of("x")})});
1629
- assert_eq!(
1630
- run(ast, json!({"xs": [-1, "a"]})),
1631
- Err("positive? expects a number, got \"a\"".to_string())
1632
- );
1633
- }
1634
-
1635
- #[test]
1636
- fn a_block_predicate_over_a_non_list_refuses_with_the_mode_named() {
1637
- for (mode, word) in [("all", "all?"), ("any", "any?"), ("none", "none?")] {
1638
- let ast = json!({"op":"block_predicate","mode":mode,"receiver":lookup_of("x"),"param":"y","predicate":{"op":"bool","value":true}});
1639
- assert_eq!(
1640
- run(ast.clone(), json!({"x": 5})),
1641
- Err(format!("{word} expects a list, got 5"))
1642
- );
1643
- assert_eq!(
1644
- run(ast, json!({"x": null})),
1645
- Err(format!("{word} expects a list, got nil"))
1646
- );
1647
- }
1648
- }
1649
-
1650
- #[test]
1651
- fn the_block_parameter_shadows_a_same_named_field_only_inside_the_block() {
1652
- let ast = json!({"op":"block_predicate","mode":"any","receiver":lookup_of("xs"),"param":"n",
1653
- "predicate":{"op":"and","left":eq(lookup_of("n"), int(2)),"right":eq(lookup_of("m"), int(9))}});
1654
- assert_eq!(
1655
- run(ast.clone(), json!({"xs": [1, 2], "m": 9, "n": 100})),
1656
- Ok(Value::Bool(true))
1657
- );
1658
- assert_eq!(
1659
- run(ast, json!({"xs": [1, 2], "m": 8})),
1660
- Ok(Value::Bool(false))
1661
- );
1662
- }
1663
-
1664
- #[test]
1665
- fn nested_blocks_see_the_outer_parameter() {
1666
- let inner = json!({"op":"block_predicate","mode":"none","receiver":lookup_of("xs"),"param":"o",
1667
- "predicate":eq(lookup_of("o"), json!({"op":"add","left":lookup_of("s"),"right":int(1)}))});
1668
- let outer = json!({"op":"block_predicate","mode":"any","receiver":lookup_of("xs"),"param":"s","predicate":inner});
1669
- // Every element has a successor except the largest, so `any?` holds.
1670
- assert_eq!(run(outer, json!({"xs": [1, 2, 4]})), Ok(Value::Bool(true)));
1671
- }
1672
-
1673
- #[test]
1674
- fn an_unbound_name_in_a_block_refuses_as_ruby_does() {
1675
- let ast = json!({"op":"block_predicate","mode":"any","receiver":lookup_of("xs"),"param":"x","predicate":lookup_of("ghost")});
1676
- assert_eq!(
1677
- run(ast, json!({"xs": [1]})),
1678
- Err("cannot resolve \"ghost\" — no such attribute or argument".to_string())
1679
- );
1680
- }
1681
-
1682
- #[test]
1683
- fn find_answers_the_first_match_projected_through_its_path_and_nil_on_a_miss() {
1684
- let find = |wanted: bool, path: &[&str]| {
1685
- json!({"op":"find","receiver":lookup_of("legs"),"param":"l","path":path,
1686
- "predicate":eq(json!({"op":"lookup","path":["l","open"]}), json!({"op":"bool","value":wanted}))})
1687
- };
1688
- let legs = json!({"legs": [{"to": "A", "open": false}, {"to": "B", "open": true}, {"to": "C", "open": true}]});
1689
- assert_eq!(
1690
- run(find(true, &["to"]), legs.clone()),
1691
- Ok(Value::Str("B".to_string()))
1692
- );
1693
- assert_eq!(run(find(true, &["nope"]), legs.clone()), Ok(Value::Nil));
1694
- assert!(matches!(run(find(true, &[]), legs), Ok(Value::Object(_))));
1695
- assert_eq!(
1696
- run(
1697
- find(true, &["to"]),
1698
- json!({"legs": [{"to": "A", "open": false}]})
1699
- ),
1700
- Ok(Value::Nil)
1701
- );
1702
- assert_eq!(
1703
- run(find(true, &["to"]), json!({"legs": []})),
1704
- Ok(Value::Nil)
1705
- );
1706
- }
1707
-
1708
- #[test]
1709
- fn find_stops_at_the_first_match_but_refuses_a_non_list() {
1710
- // The second element would raise; `find` never reaches it, as Ruby's `Array#find`.
1711
- let ast = json!({"op":"find","receiver":lookup_of("xs"),"param":"x","path":[],
1712
- "predicate":json!({"op":"sign_test","cmp":{"less_than":true,"equal":true,"negated":true},"receiver":lookup_of("x")})});
1713
- assert_eq!(run(ast.clone(), json!({"xs": [3, "a"]})), Ok(Value::Int(3)));
1714
- assert_eq!(
1715
- run(ast, json!({"xs": "abc"})),
1716
- Err("find expects a list, got \"abc\"".to_string())
1717
- );
1718
- }
1719
-
1720
- #[test]
1721
- fn presence_and_assignment_follow_blank_and_nil() {
1722
- let presence =
1723
- |negated: bool| json!({"op":"presence","receiver":lookup_of("x"),"negated":negated});
1724
- let assignment =
1725
- |negated: bool| json!({"op":"assignment","receiver":lookup_of("x"),"negated":negated});
1726
- for (value, present) in [
1727
- (json!(null), false),
1728
- (json!(false), false),
1729
- (json!(""), false),
1730
- (json!([]), false),
1731
- (json!(" "), true),
1732
- (json!(0), true),
1733
- (json!(true), true),
1734
- (json!([0]), true),
1735
- ] {
1736
- assert_eq!(
1737
- run(presence(false), json!({"x": value.clone()})),
1738
- Ok(Value::Bool(present)),
1739
- "present? of {value}"
1740
- );
1741
- assert_eq!(
1742
- run(presence(true), json!({"x": value.clone()})),
1743
- Ok(Value::Bool(!present)),
1744
- "blank? of {value}"
1745
- );
1746
- }
1747
- // `set?` asks only `!nil?`: an assigned empty or false value is set.
1748
- assert_eq!(
1749
- run(assignment(false), json!({"x": ""})),
1750
- Ok(Value::Bool(true))
1751
- );
1752
- assert_eq!(
1753
- run(assignment(false), json!({"x": false})),
1754
- Ok(Value::Bool(true))
1755
- );
1756
- assert_eq!(
1757
- run(assignment(false), json!({"x": null})),
1758
- Ok(Value::Bool(false))
1759
- );
1760
- assert_eq!(
1761
- run(assignment(true), json!({"x": null})),
1762
- Ok(Value::Bool(true))
1763
- );
1764
- }
1765
-
1766
- #[test]
1767
- fn split_follows_ruby_for_awk_empty_and_literal_separators() {
1768
- let split = |sep: &str| json!({"op":"split","receiver":lookup_of("x"),"separator":sep});
1769
- assert_eq!(
1770
- run(split(" "), json!({"x": " a b c "})),
1771
- Ok(strs(&["a", "b", "c"]))
1772
- );
1773
- assert_eq!(run(split(""), json!({"x": "aé"})), Ok(strs(&["a", "é"])));
1774
- assert_eq!(
1775
- run(split(","), json!({"x": "a,b,,"})),
1776
- Ok(strs(&["a", "b"]))
1777
- );
1778
- assert_eq!(run(split(","), json!({"x": ",a"})), Ok(strs(&["", "a"])));
1779
- assert_eq!(run(split(","), json!({"x": ""})), Ok(strs(&[])));
1780
- assert_eq!(
1781
- run(split(","), json!({"x": 5})),
1782
- Err("split expects a string, got 5".to_string())
1783
- );
1784
- assert_eq!(
1785
- run(split(","), json!({"x": null})),
1786
- Err("split expects a string, got nil".to_string())
1787
- );
1788
- }
1789
-
1790
- #[test]
1791
- fn first_and_last_answer_nil_for_an_empty_list_and_refuse_a_non_list() {
1792
- assert_eq!(
1793
- run(call("first", lookup_of("x")), json!({"x": [7, 8]})),
1794
- Ok(Value::Int(7))
1795
- );
1796
- assert_eq!(
1797
- run(call("last", lookup_of("x")), json!({"x": [7, 8]})),
1798
- Ok(Value::Int(8))
1799
- );
1800
- assert_eq!(
1801
- run(call("first", lookup_of("x")), json!({"x": []})),
1802
- Ok(Value::Nil)
1803
- );
1804
- assert_eq!(
1805
- run(call("last", lookup_of("x")), json!({"x": "abc"})),
1806
- Err("last expects a list, got \"abc\"".to_string())
1807
- );
1808
- assert_eq!(
1809
- run(call("first", lookup_of("x")), json!({"x": null})),
1810
- Err("first expects a list, got nil".to_string())
1811
- );
1812
- }
1813
-
1814
- #[test]
1815
- fn strip_trims_rubys_whitespace_and_needs_a_string() {
1816
- let strip_of = |side: &str, text: &str| {
1817
- run(
1818
- json!({"op":"strip","receiver":lookup_of("x"),"side":side}),
1819
- json!({"x": text}),
1820
- )
1821
- };
1822
- assert_eq!(strip_of("both", " \t a b \0"), Ok(Value::Str("a b".into())));
1823
- assert_eq!(strip_of("left", " a "), Ok(Value::Str("a ".into())));
1824
- assert_eq!(strip_of("right", " a "), Ok(Value::Str(" a".into())));
1825
- assert_eq!(
1826
- strip_of("both", "\u{a0}a"),
1827
- Ok(Value::Str("\u{a0}a".into()))
1828
- );
1829
- assert_eq!(
1830
- run(
1831
- json!({"op":"strip","receiver":lookup_of("x"),"side":"both"}),
1832
- json!({"x": null})
1833
- ),
1834
- Err("strip expects a string, got nil".to_string())
1835
- );
1836
- }
1837
-
1838
- #[test]
1839
- fn starts_with_and_ends_with_need_a_string() {
1840
- let starts = json!({"op":"starts_with","receiver":lookup_of("x"),"substring":"ab"});
1841
- let ends = json!({"op":"ends_with","receiver":lookup_of("x"),"substring":"bc"});
1842
- assert_eq!(
1843
- run(starts.clone(), json!({"x": "abc"})),
1844
- Ok(Value::Bool(true))
1845
- );
1846
- assert_eq!(
1847
- run(ends.clone(), json!({"x": "abc"})),
1848
- Ok(Value::Bool(true))
1849
- );
1850
- assert_eq!(run(ends, json!({"x": "abd"})), Ok(Value::Bool(false)));
1851
- assert_eq!(
1852
- run(starts.clone(), json!({"x": null})),
1853
- Err("start_with? expects a string, got nil".to_string())
1854
- );
1855
- assert_eq!(
1856
- run(starts, json!({"x": [1]})),
1857
- Err("start_with? expects a string, got [1]".to_string())
1858
- );
1859
- }
1860
-
1861
- #[test]
1862
- fn a_path_through_a_string_list_or_integer_walks_like_ruby() {
1863
- let path = |segments: &[&str]| json!({"op":"lookup","path":segments});
1864
- // `String#[]` answers the segment when it is a substring.
1865
- assert_eq!(
1866
- run(path(&["x", "foo"]), json!({"x": "abcfoo"})),
1867
- Ok(Value::Str("foo".to_string()))
1868
- );
1869
- assert_eq!(
1870
- run(path(&["x", "nil?"]), json!({"x": "abc"})),
1871
- Ok(Value::Nil)
1872
- );
1873
- assert_eq!(run(path(&["x", "a"]), json!({"x": 1.5})), Ok(Value::Nil));
1874
- assert_eq!(run(path(&["x", "a"]), json!({"x": true})), Ok(Value::Nil));
1875
- assert_eq!(
1876
- run(path(&["x", "a"]), json!({"x": [1]})),
1877
- Err("cannot read \"a\" from [1]".to_string())
1878
- );
1879
- assert_eq!(
1880
- run(path(&["x", "a"]), json!({"x": 5})),
1881
- Err("cannot read \"a\" from 5".to_string())
1882
- );
1883
- }
1884
-
1885
- #[test]
1886
- fn a_one_field_object_reads_as_its_value_and_a_wider_one_stays_an_object() {
1887
- assert_eq!(
1888
- run(lookup_of("x"), json!({"x": {"value": 7}})),
1889
- Ok(Value::Int(7))
1890
- );
1891
- assert!(matches!(
1892
- run(lookup_of("x"), json!({"x": {"a": 1, "b": 2}})),
1893
- Ok(Value::Object(_))
1894
- ));
1895
- assert_eq!(
1896
- run(
1897
- json!({"op":"lookup","path":["x","b"]}),
1898
- json!({"x": {"a": 1, "b": 2}})
1899
- ),
1900
- Ok(Value::Int(2))
1901
- );
1902
- }
1903
-
1904
- #[test]
1905
- fn errors_are_worded_as_rubys_describe() {
1906
- assert_eq!(
1907
- run(call("size", lookup_of("x")), json!({"x": 5})),
1908
- Err("size expects a list or string, got 5".to_string())
1909
- );
1910
- assert_eq!(
1911
- run(call("empty", lookup_of("x")), json!({"x": null})),
1912
- Err("empty? expects a list or string, got nil".to_string())
1913
- );
1914
- assert_eq!(
1915
- run(call("to_s", lookup_of("x")), json!({"x": [1, "a"]})),
1916
- Err("to_s expects a scalar, got [1,\"a\"]".to_string())
1917
- );
1918
- assert_eq!(
1919
- run(
1920
- json!({"op":"compare","cmp":{"less_than":true,"equal":false,"negated":false},"left":lookup_of("x"),"right":int(1)}),
1921
- json!({"x": "a"})
1922
- ),
1923
- Err("comparison of String with 1 failed".to_string())
1924
- );
1925
- assert_eq!(
1926
- run(
1927
- json!({"op":"sign_test","cmp":{"less_than":false,"equal":true,"negated":false},"receiver":lookup_of("x")}),
1928
- json!({"x": "a"})
1929
- ),
1930
- Err("zero? expects a number, got \"a\"".to_string())
1931
- );
1932
- assert_eq!(
1933
- run(lookup_of("ghost"), json!({})),
1934
- Err("cannot resolve \"ghost\" — no such attribute or argument".to_string())
1935
- );
1936
- }
1937
-
1938
- #[test]
1939
- fn floats_print_as_ruby_prints_them() {
1940
- for (f, text) in [
1941
- (3.0, "3.0"),
1942
- (0.5, "0.5"),
1943
- (-0.0, "-0.0"),
1944
- (100_000_000_000_000.0, "100000000000000.0"),
1945
- (999_999_999_999_999.9, "999999999999999.9"),
1946
- (1e15, "1.0e+15"),
1947
- (1.5e15, "1.5e+15"),
1948
- (1e20, "1.0e+20"),
1949
- (0.0001, "0.0001"),
1950
- (0.00001, "1.0e-05"),
1951
- (1.5e-7, "1.5e-07"),
1952
- (123_456_789.123_456_79, "123456789.12345679"),
1953
- ] {
1954
- assert_eq!(ruby_float(f), text);
1955
- assert_eq!(
1956
- run(call("to_s", lookup_of("x")), json!({"x": f})),
1957
- Ok(Value::Str(text.to_string()))
1958
- );
1959
- }
1960
- }
1961
-
1962
- #[test]
1963
- fn strings_inspect_as_ruby_inspects_them() {
1964
- assert_eq!(
1965
- ruby_inspect("a\"b\\c\n\u{1}#{x}\u{7f}é"),
1966
- "\"a\\\"b\\\\c\\n\\u0001\\#{x}\\u007Fé\""
1967
- );
1968
- }
1969
-
1970
- #[test]
1971
- fn equality_compares_nested_numbers_across_kinds_like_ruby() {
1972
- assert!(values_equal(
1973
- &Value::Array(vec![Value::Int(1)]),
1974
- &Value::Array(vec![Value::Float(1.0)])
1975
- ));
1976
- assert!(!values_equal(
1977
- &Value::Int(9_007_199_254_740_993),
1978
- &Value::Float(9_007_199_254_740_992.0)
1979
- ));
1980
- assert!(values_equal(&Value::Int(3), &Value::Float(3.0)));
1981
- assert!(!values_equal(&Value::Int(3), &Value::Float(3.5)));
1982
- }
1983
- }