hecks 2.8.0 → 3.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 (886) hide show
  1. checksums.yaml +4 -4
  2. data/exe/hecks +50 -7
  3. data/lib/hecks/adapters/driven/claude_code.rb +4 -104
  4. data/lib/hecks/adapters/driven/d1.rb +44 -237
  5. data/lib/hecks/adapters/driven/folder.rb +17 -76
  6. data/lib/hecks/adapters/driven/google_authentication.rb +15 -49
  7. data/lib/hecks/adapters/driven/governance_authorization.rb +23 -70
  8. data/lib/hecks/adapters/driven/heki/journal.rb +8 -41
  9. data/lib/hecks/adapters/driven/heki/saga_store.rb +12 -54
  10. data/lib/hecks/adapters/driven/heki/snapshot.rb +4 -12
  11. data/lib/hecks/adapters/driven/heki.rb +13 -42
  12. data/lib/hecks/adapters/driven/identity_registry.rb +3 -14
  13. data/lib/hecks/adapters/driven/in_memory_ordering.rb +9 -26
  14. data/lib/hecks/adapters/driven/in_process_key_vault.rb +2 -14
  15. data/lib/hecks/adapters/driven/lambda/client.rb +10 -51
  16. data/lib/hecks/adapters/driven/lambda.rb +10 -84
  17. data/lib/hecks/adapters/driven/local_storage.rb +5 -40
  18. data/lib/hecks/adapters/driven/memory.rb +32 -100
  19. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +5 -21
  20. data/lib/hecks/adapters/driven/postgres/codec.rb +6 -28
  21. data/lib/hecks/adapters/driven/postgres/outbox.rb +3 -12
  22. data/lib/hecks/adapters/driven/postgres/reconnect.rb +10 -32
  23. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +34 -108
  24. data/lib/hecks/adapters/driven/postgres/shared_connection.rb +180 -0
  25. data/lib/hecks/adapters/driven/postgres.rb +149 -136
  26. data/lib/hecks/adapters/driven/postgres_era.adapter +4 -15
  27. data/lib/hecks/adapters/driven/prism.rb +48 -38
  28. data/lib/hecks/adapters/driven/sql_query_builder.rb +21 -121
  29. data/lib/hecks/adapters/driven/sqlite/codec.rb +8 -40
  30. data/lib/hecks/adapters/driven/sqlite/projection.rb +26 -82
  31. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +40 -84
  32. data/lib/hecks/adapters/driven/sqlite.rb +128 -65
  33. data/lib/hecks/adapters/driven/tenant_provisioner.rb +76 -62
  34. data/lib/hecks/adapters/driven.rb +5 -36
  35. data/lib/hecks/adapters/driving/github_webhook.rb +8 -76
  36. data/lib/hecks/behaviors/dsl.rb +7 -25
  37. data/lib/hecks/behaviors/expectations.rb +32 -142
  38. data/lib/hecks/behaviors/ir.rb +7 -20
  39. data/lib/hecks/behaviors/rspec.rb +6 -17
  40. data/lib/hecks/behaviors/runner.rb +10 -34
  41. data/lib/hecks/behaviors.rb +4 -8
  42. data/lib/hecks/bench/cli.rb +62 -0
  43. data/lib/hecks/bench/environment.rb +71 -0
  44. data/lib/hecks/bench/postgres_probe.rb +30 -0
  45. data/lib/hecks/bench/report.rb +76 -0
  46. data/lib/hecks/bench/ruby_runner.rb +93 -0
  47. data/lib/hecks/bench/run.rb +55 -0
  48. data/lib/hecks/bench/rust_runner.rb +101 -0
  49. data/lib/hecks/bench/stats.rb +46 -0
  50. data/lib/hecks/bench/suite.rb +98 -0
  51. data/lib/hecks/bench/workload.rb +89 -0
  52. data/lib/hecks/bench.rb +17 -0
  53. data/lib/hecks/bluebook/aggregate.rb +9 -75
  54. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +9 -38
  55. data/lib/hecks/bluebook/assembly/build.rb +4 -15
  56. data/lib/hecks/bluebook/assembly/contract.rb +15 -136
  57. data/lib/hecks/bluebook/assembly/contracts.rb +56 -174
  58. data/lib/hecks/bluebook/assembly/marks.rb +31 -223
  59. data/lib/hecks/bluebook/assembly/specializer.rb +6 -52
  60. data/lib/hecks/bluebook/assembly.rb +6 -45
  61. data/lib/hecks/bluebook/attribute.rb +12 -61
  62. data/lib/hecks/bluebook/behaviour/aggregate.rb +39 -39
  63. data/lib/hecks/bluebook/behaviour/attribute.rb +3 -14
  64. data/lib/hecks/bluebook/behaviour/chapter.rb +14 -42
  65. data/lib/hecks/bluebook/behaviour/command.rb +14 -66
  66. data/lib/hecks/bluebook/behaviour/domain_port.rb +11 -20
  67. data/lib/hecks/bluebook/behaviour/entity.rb +7 -27
  68. data/lib/hecks/bluebook/behaviour/hexagon.rb +23 -41
  69. data/lib/hecks/bluebook/behaviour/lifecycle.rb +15 -40
  70. data/lib/hecks/bluebook/behaviour/policy.rb +23 -52
  71. data/lib/hecks/bluebook/behaviour/process_manager.rb +30 -57
  72. data/lib/hecks/bluebook/behaviour/read_model.rb +25 -31
  73. data/lib/hecks/bluebook/behaviour/traits.rb +27 -64
  74. data/lib/hecks/bluebook/behaviour/value_object.rb +5 -24
  75. data/lib/hecks/bluebook/capabilities.rb +17 -49
  76. data/lib/hecks/bluebook/chapter.rb +16 -50
  77. data/lib/hecks/bluebook/command.rb +23 -81
  78. data/lib/hecks/bluebook/domain_port.rb +49 -67
  79. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +50 -229
  80. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +50 -377
  81. data/lib/hecks/bluebook/dsl/attribute_collector.rb +33 -204
  82. data/lib/hecks/bluebook/dsl/binding_proxy.rb +16 -69
  83. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +96 -708
  84. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +63 -169
  85. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +3 -1
  86. data/lib/hecks/bluebook/dsl/command_builder.rb +74 -546
  87. data/lib/hecks/bluebook/dsl/const_shim.rb +8 -41
  88. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +42 -89
  89. data/lib/hecks/bluebook/dsl/entity_builder.rb +44 -330
  90. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +20 -269
  91. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +148 -197
  92. data/lib/hecks/bluebook/dsl/identity_declaration.rb +13 -88
  93. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +6 -20
  94. data/lib/hecks/bluebook/dsl/policy_builder.rb +16 -88
  95. data/lib/hecks/bluebook/dsl/port_builder.rb +2 -6
  96. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +22 -96
  97. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +31 -184
  98. data/lib/hecks/bluebook/dsl/query_builder.rb +38 -43
  99. data/lib/hecks/bluebook/dsl/read_model_builder.rb +75 -201
  100. data/lib/hecks/bluebook/dsl/rule_reference.rb +22 -163
  101. data/lib/hecks/bluebook/dsl/translation_builder.rb +28 -114
  102. data/lib/hecks/bluebook/dsl/value_object_builder.rb +10 -124
  103. data/lib/hecks/bluebook/dsl/word_gate.rb +16 -162
  104. data/lib/hecks/bluebook/dsl/world_builder.rb +46 -111
  105. data/lib/hecks/bluebook/entity.rb +13 -71
  106. data/lib/hecks/bluebook/expression/ast_json.rb +24 -190
  107. data/lib/hecks/bluebook/expression/ast_reader.rb +12 -54
  108. data/lib/hecks/bluebook/expression/canonical_form.rb +11 -88
  109. data/lib/hecks/bluebook/expression/evaluator.rb +47 -295
  110. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +30 -147
  111. data/lib/hecks/bluebook/expression/resolver.rb +39 -661
  112. data/lib/hecks/bluebook/hexagon.rb +18 -33
  113. data/lib/hecks/bluebook/lifecycle.rb +5 -16
  114. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -7
  115. data/lib/hecks/bluebook/meta_validator/judge.rb +111 -361
  116. data/lib/hecks/bluebook/meta_validator/plan.rb +33 -162
  117. data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -7
  118. data/lib/hecks/bluebook/meta_validator/readings.rb +56 -342
  119. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +25 -176
  120. data/lib/hecks/bluebook/meta_validator/shapes.rb +53 -267
  121. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +47 -306
  122. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +7 -43
  123. data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +246 -0
  124. data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -18
  125. data/lib/hecks/bluebook/meta_validator.rb +87 -352
  126. data/lib/hecks/bluebook/model_check/client_profile.rb +6 -21
  127. data/lib/hecks/bluebook/model_check.rb +122 -596
  128. data/lib/hecks/bluebook/pattern_subset.rb +49 -69
  129. data/lib/hecks/bluebook/process_manager.rb +13 -83
  130. data/lib/hecks/bluebook/project_loader.rb +3 -39
  131. data/lib/hecks/bluebook/project_register.rb +12 -60
  132. data/lib/hecks/bluebook/query.rb +27 -51
  133. data/lib/hecks/bluebook/read_model.rb +57 -81
  134. data/lib/hecks/bluebook/reference.rb +7 -41
  135. data/lib/hecks/bluebook/smoke_test.rb +9 -99
  136. data/lib/hecks/bluebook/synthesizer.rb +13 -62
  137. data/lib/hecks/bluebook/translation.rb +26 -62
  138. data/lib/hecks/bluebook/value_object.rb +11 -36
  139. data/lib/hecks/bluebook.rb +5 -22
  140. data/lib/hecks/cache_dir.rb +4 -23
  141. data/lib/hecks/canonical_json.rb +30 -0
  142. data/lib/hecks/chapters.rb +102 -0
  143. data/lib/hecks/cli/behaviors.rb +61 -0
  144. data/lib/hecks/cli/canonicalise.rb +25 -0
  145. data/lib/hecks/cli/check_engine_agreement.rb +32 -0
  146. data/lib/hecks/cli/check_era.rb +88 -0
  147. data/lib/hecks/cli/console.rb +66 -0
  148. data/lib/hecks/cli/deploy_template_diff.rb +40 -0
  149. data/lib/hecks/cli/doc_coverage.rb +32 -0
  150. data/lib/hecks/cli/document.rb +5 -23
  151. data/lib/hecks/cli/expression_projection.rb +41 -0
  152. data/lib/hecks/cli/follow.rb +88 -0
  153. data/lib/hecks/cli/history.rb +44 -0
  154. data/lib/hecks/cli/ir.rb +4 -6
  155. data/lib/hecks/cli/mcp.rb +5 -6
  156. data/lib/hecks/cli/model_check.rb +24 -88
  157. data/lib/hecks/cli/pattern_cases.rb +47 -0
  158. data/lib/hecks/cli/present.rb +39 -0
  159. data/lib/hecks/cli/project_cli.rb +223 -64
  160. data/lib/hecks/cli/project_diagrams.rb +4 -10
  161. data/lib/hecks/cli/project_glossary.rb +44 -0
  162. data/lib/hecks/cli/reference.rb +46 -0
  163. data/lib/hecks/cli/refresh_projections.rb +44 -0
  164. data/lib/hecks/cli/refresh_rspec_runtime_baseline.rb +150 -0
  165. data/lib/hecks/cli/release.rb +70 -0
  166. data/lib/hecks/cli/release_gem.rb +59 -0
  167. data/lib/hecks/cli/rspec_io_parallel_files.rb +137 -0
  168. data/lib/hecks/cli/rspec_shard_files.rb +66 -0
  169. data/lib/hecks/cli/run.rb +15 -61
  170. data/lib/hecks/cli/seed_semantics_corpus.rb +71 -0
  171. data/lib/hecks/cli/serve_query_ir_mcp.rb +30 -0
  172. data/lib/hecks/cli/shape.rb +70 -0
  173. data/lib/hecks/cli/smoke_http.rb +230 -0
  174. data/lib/hecks/cli/smoke_test.rb +7 -19
  175. data/lib/hecks/cli/spec_example.rb +27 -0
  176. data/lib/hecks/cli/statements.rb +32 -0
  177. data/lib/hecks/cli/stores.rb +4 -6
  178. data/lib/hecks/cli/stress_concurrency_specs.rb +177 -0
  179. data/lib/hecks/cli.rb +216 -73
  180. data/lib/hecks/codemod.rb +324 -0
  181. data/lib/hecks/construct.rb +8 -34
  182. data/lib/hecks/corpus.rb +423 -0
  183. data/lib/hecks/deploy/bluebook/deploy.bluebook +617 -158
  184. data/lib/hecks/deploy/bluebook/deploy.hecksagon +41 -11
  185. data/lib/hecks/deploy/oidc.json +82 -2
  186. data/lib/hecks/doc/reference.rb +351 -0
  187. data/lib/hecks/doors/cli_door.rb +196 -0
  188. data/lib/hecks/doors/cli_runner.rb +297 -0
  189. data/lib/hecks/{facade → doors}/command_request.rb +14 -34
  190. data/lib/hecks/doors/handle.rb +206 -0
  191. data/lib/hecks/doors/json_door.rb +132 -0
  192. data/lib/hecks/doors/launcher_options.rb +129 -0
  193. data/lib/hecks/{cli → doors}/mcp_door.rb +25 -120
  194. data/lib/hecks/doors/mcp_door_scope.rb +97 -0
  195. data/lib/hecks/doors/ruby_door/aggregate_door.rb +162 -0
  196. data/lib/hecks/doors/ruby_door/chapter.rb +72 -0
  197. data/lib/hecks/doors/ruby_door.rb +77 -0
  198. data/lib/hecks/doors.rb +61 -0
  199. data/lib/hecks/embryonaut_bluebook/lock.rb +3 -35
  200. data/lib/hecks/embryonaut_bluebook/shape.rb +1 -17
  201. data/lib/hecks/embryonaut_bluebook/vendor.rb +8 -53
  202. data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +4 -19
  203. data/lib/hecks/embryonaut_bluebook.rb +5 -72
  204. data/lib/hecks/engine_agreement.rb +156 -0
  205. data/lib/hecks/facade.rb +23 -51
  206. data/lib/hecks/forms/app.rb +21 -76
  207. data/lib/hecks/forms/banking_presentation.rb +50 -0
  208. data/lib/hecks/forms/command_form_renderer.rb +10 -29
  209. data/lib/hecks/forms/field_renderer.rb +17 -156
  210. data/lib/hecks/forms/field_shape.rb +31 -222
  211. data/lib/hecks/forms/html.rb +12 -47
  212. data/lib/hecks/forms/index_renderer.rb +1 -4
  213. data/lib/hecks/forms/page.rb +3 -9
  214. data/lib/hecks/forms/params.rb +14 -60
  215. data/lib/hecks/forms/port_argument.rb +2 -19
  216. data/lib/hecks/forms/query_form_renderer.rb +4 -79
  217. data/lib/hecks/forms/record_renderer.rb +8 -75
  218. data/lib/hecks/forms/record_table.rb +7 -50
  219. data/lib/hecks/forms/reference_options.rb +3 -30
  220. data/lib/hecks/forms/value_object_shape.rb +8 -35
  221. data/lib/hecks/forms.rb +5 -33
  222. data/lib/hecks/fqn.rb +5 -12
  223. data/lib/hecks/framework/bluebook/compliance.bluebook +16 -65
  224. data/lib/hecks/framework/bluebook/console_settings.bluebook +47 -134
  225. data/lib/hecks/framework/bluebook/framework.hecksagon +5 -14
  226. data/lib/hecks/framework/bluebook/governance.bluebook +13 -34
  227. data/lib/hecks/framework/bluebook/identity.bluebook +11 -26
  228. data/lib/hecks/framework/bluebook/privacy.bluebook +8 -33
  229. data/lib/hecks/framework.rb +11 -80
  230. data/lib/hecks/freezer.rb +5 -39
  231. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +393 -0
  232. data/lib/hecks/fuzzing/combination_miner.rb +86 -0
  233. data/lib/hecks/fuzzing/concurrent_dispatch.rb +214 -0
  234. data/lib/hecks/fuzzing/coverage_campaign.rb +143 -0
  235. data/lib/hecks/fuzzing/differential.rb +156 -0
  236. data/lib/hecks/fuzzing/domain_generator.rb +615 -0
  237. data/lib/hecks/fuzzing/era_boundary.rb +71 -0
  238. data/lib/hecks/fuzzing/form_census.rb +136 -0
  239. data/lib/hecks/fuzzing/generated_domain_check.rb +93 -0
  240. data/lib/hecks/fuzzing/invalid_value_generator.rb +64 -0
  241. data/lib/hecks/fuzzing/isolated_boot.rb +301 -0
  242. data/lib/hecks/fuzzing/nondeterministic.rb +51 -0
  243. data/lib/hecks/fuzzing/outside_world.rb +45 -0
  244. data/lib/hecks/fuzzing/persistence_parity.rb +102 -0
  245. data/lib/hecks/fuzzing/properties/corrections.rb +88 -0
  246. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +314 -0
  247. data/lib/hecks/fuzzing/properties/guards.rb +190 -0
  248. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +300 -0
  249. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +93 -0
  250. data/lib/hecks/fuzzing/properties/outbox.rb +89 -0
  251. data/lib/hecks/fuzzing/properties/querying.rb +175 -0
  252. data/lib/hecks/fuzzing/properties.rb +162 -0
  253. data/lib/hecks/fuzzing/qa_settings.rb +123 -0
  254. data/lib/hecks/fuzzing/replay.rb +505 -0
  255. data/lib/hecks/fuzzing/rotation_priority.rb +48 -0
  256. data/lib/hecks/fuzzing/rust_gap_manifest.rb +108 -0
  257. data/lib/hecks/fuzzing/self_consistency.rb +339 -0
  258. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +385 -0
  259. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +132 -0
  260. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +95 -0
  261. data/lib/hecks/fuzzing/sequence_generator/picker.rb +77 -0
  262. data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +109 -0
  263. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +237 -0
  264. data/lib/hecks/fuzzing/sequence_generator.rb +189 -0
  265. data/lib/hecks/fuzzing/shrinker.rb +166 -0
  266. data/lib/hecks/fuzzing/structural_skips.rb +33 -0
  267. data/lib/hecks/fuzzing/sweep_depth.rb +43 -0
  268. data/lib/hecks/fuzzing/target_capabilities.rb +163 -0
  269. data/lib/hecks/fuzzing/value_generator.rb +216 -0
  270. data/lib/hecks/fuzzing.rb +23 -0
  271. data/lib/hecks/grammar/evolve.rb +336 -0
  272. data/lib/hecks/grammar/expression.bluebook +15 -40
  273. data/lib/hecks/grammar/translation.bluebook +3 -6
  274. data/lib/hecks/grammar.rb +16 -41
  275. data/lib/hecks/hecks/adapters/codebase/codemods.rb +45 -0
  276. data/lib/hecks/hecks/adapters/codebase/conformance.rb +71 -0
  277. data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +202 -0
  278. data/lib/hecks/hecks/adapters/codebase/gem_registry.rb +80 -0
  279. data/lib/hecks/hecks/adapters/codebase/kernel_tables.rb +54 -0
  280. data/lib/hecks/hecks/adapters/codebase/language.rb +240 -0
  281. data/lib/hecks/hecks/adapters/codebase/npm_registry.rb +102 -0
  282. data/lib/hecks/hecks/adapters/codebase/publishing.rb +142 -0
  283. data/lib/hecks/hecks/adapters/codebase/regeneration.rb +67 -0
  284. data/lib/hecks/hecks/adapters/codebase/release_facts.rb +90 -0
  285. data/lib/hecks/hecks/adapters/codebase/ruby_child.rb +83 -0
  286. data/lib/hecks/hecks/adapters/codebase/secret_vault.rb +36 -0
  287. data/lib/hecks/hecks/adapters/codebase/source_tree.adapter +7 -0
  288. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +189 -0
  289. data/lib/hecks/hecks/adapters/codebase/sqlite_fixture.rb +71 -0
  290. data/lib/hecks/hecks/adapters/codebase/style.rb +171 -0
  291. data/lib/hecks/hecks/adapters/codebase/test_runner.rb +55 -0
  292. data/lib/hecks/hecks/adapters/codebase/test_suite.rb +165 -0
  293. data/lib/hecks/hecks/adapters/codebase/tree.rb +111 -0
  294. data/lib/hecks/hecks/adapters/console_capture.rb +79 -0
  295. data/lib/hecks/hecks/adapters/deploy_toolchain.adapter +7 -0
  296. data/lib/hecks/hecks/adapters/deploy_toolchain.rb +108 -0
  297. data/lib/hecks/hecks/adapters/git.adapter +7 -0
  298. data/lib/hecks/hecks/adapters/git.rb +102 -0
  299. data/lib/hecks/hecks/adapters/host_http.adapter +7 -0
  300. data/lib/hecks/hecks/adapters/host_http.rb +65 -0
  301. data/lib/hecks/hecks/adapters/in_process_boot.adapter +7 -0
  302. data/lib/hecks/hecks/adapters/in_process_boot.rb +197 -0
  303. data/lib/hecks/hecks/adapters/in_process_operations.rb +202 -0
  304. data/lib/hecks/hecks/adapters/journal_store/changes.rb +143 -0
  305. data/lib/hecks/hecks/adapters/journal_store/compaction.rb +145 -0
  306. data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +132 -0
  307. data/lib/hecks/hecks/adapters/journal_store/examination.rb +135 -0
  308. data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +163 -0
  309. data/lib/hecks/hecks/adapters/journal_store/readings.rb +187 -0
  310. data/lib/hecks/hecks/adapters/journal_store.adapter +7 -0
  311. data/lib/hecks/hecks/adapters/journal_store.rb +64 -0
  312. data/lib/hecks/hecks/adapters/local_files.adapter +7 -0
  313. data/lib/hecks/hecks/adapters/local_files.rb +46 -0
  314. data/lib/hecks/hecks/adapters/pg_admin.adapter +7 -0
  315. data/lib/hecks/hecks/adapters/pg_admin.rb +245 -0
  316. data/lib/hecks/hecks/adapters/process_pool.adapter +7 -0
  317. data/lib/hecks/hecks/adapters/process_pool.rb +119 -0
  318. data/lib/hecks/hecks/adapters/rust_toolchain.adapter +7 -0
  319. data/lib/hecks/hecks/adapters/rust_toolchain.rb +179 -0
  320. data/lib/hecks/hecks/adapters/rust_workspace.rb +150 -0
  321. data/lib/hecks/hecks/adapters/shell.adapter +7 -0
  322. data/lib/hecks/hecks/adapters/shell.rb +49 -0
  323. data/lib/hecks/hecks/adapters/terminal.adapter +7 -0
  324. data/lib/hecks/hecks/adapters/terminal.rb +74 -0
  325. data/lib/hecks/hecks/codebase.behaviors +172 -0
  326. data/lib/hecks/hecks/codebase.bluebook +2453 -0
  327. data/lib/hecks/hecks/context_map.hecksagon +13 -0
  328. data/lib/hecks/hecks/custodian.behaviors +179 -0
  329. data/lib/hecks/hecks/custodian.bluebook +1847 -0
  330. data/lib/hecks/hecks/deploy.behaviors +74 -0
  331. data/lib/hecks/hecks/deploy.world +12 -0
  332. data/lib/hecks/hecks/environments/memory.world +10 -0
  333. data/lib/hecks/hecks/governance.world +7 -0
  334. data/lib/hecks/hecks/hecks.behaviors +62 -0
  335. data/lib/hecks/hecks/hecks.bluebook +124 -0
  336. data/lib/hecks/hecks/hecks.hecksagon +390 -0
  337. data/lib/hecks/hecks/hecks.world +42 -0
  338. data/lib/hecks/ir.rb +14 -83
  339. data/lib/hecks/kernel_capabilities.rb +194 -0
  340. data/lib/hecks/language/adapter.bluebook +2 -7
  341. data/lib/hecks/language/bluebook/aggregate.bluebook +56 -221
  342. data/lib/hecks/language/bluebook/attaches/paging.bluebook +6 -15
  343. data/lib/hecks/language/bluebook/bluebook.bluebook +26 -83
  344. data/lib/hecks/language/bluebook/bluebook.hecksagon +3 -15
  345. data/lib/hecks/language/bluebook/command.bluebook +27 -144
  346. data/lib/hecks/language/bluebook/entity.bluebook +24 -97
  347. data/lib/hecks/language/bluebook/policy.bluebook +16 -67
  348. data/lib/hecks/language/bluebook/process_manager.bluebook +28 -151
  349. data/lib/hecks/language/bluebook/projection.bluebook +126 -60
  350. data/lib/hecks/language/bluebook/query.bluebook +19 -43
  351. data/lib/hecks/language/bluebook/shape.bluebook +23 -73
  352. data/lib/hecks/language/bluebook/syntax.bluebook +51 -262
  353. data/lib/hecks/language/bluebook/vocabulary.bluebook +83 -198
  354. data/lib/hecks/language/hecksagon/adapter_binding.bluebook +3 -10
  355. data/lib/hecks/language/hecksagon/domain_port.bluebook +14 -19
  356. data/lib/hecks/language/hecksagon/hecksagon.bluebook +26 -25
  357. data/lib/hecks/language/hecksagon/port_operation.bluebook +3 -14
  358. data/lib/hecks/language/oidc.json +35 -0
  359. data/lib/hecks/language/port.bluebook +4 -17
  360. data/lib/hecks/language/translation/translation.bluebook +3 -9
  361. data/lib/hecks/language/translation/translation_aggregate.bluebook +5 -24
  362. data/lib/hecks/language/world/wiring.bluebook +1 -3
  363. data/lib/hecks/language/world/world.bluebook +5 -7
  364. data/lib/hecks/literal.rb +8 -50
  365. data/lib/hecks/mcp_stdio_guard.rb +4 -41
  366. data/lib/hecks/naming.rb +29 -137
  367. data/lib/hecks/persistence_legacy_fixture/regenerate.rb +161 -0
  368. data/lib/hecks/persistence_legacy_fixture.rb +113 -0
  369. data/lib/hecks/ports/access_control.rb +7 -23
  370. data/lib/hecks/ports/agent/answers.rb +5 -23
  371. data/lib/hecks/ports/agent.rb +47 -172
  372. data/lib/hecks/ports/authentication.rb +12 -39
  373. data/lib/hecks/ports/authorization.rb +16 -43
  374. data/lib/hecks/ports/clock.rb +5 -45
  375. data/lib/hecks/ports/extraction.rb +7 -12
  376. data/lib/hecks/ports/identity_assignment.rb +8 -29
  377. data/lib/hecks/ports/identity_generation.rb +6 -29
  378. data/lib/hecks/ports/identity_resolution.rb +2 -10
  379. data/lib/hecks/ports/key_vault.rb +9 -45
  380. data/lib/hecks/ports/persistence/append_only.rb +61 -64
  381. data/lib/hecks/ports/persistence/binding_policy.rb +4 -14
  382. data/lib/hecks/ports/persistence/codec_boundary.rb +20 -80
  383. data/lib/hecks/ports/persistence/null_saga_store.rb +4 -20
  384. data/lib/hecks/ports/persistence/plugin.rb +13 -42
  385. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +44 -144
  386. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +15 -115
  387. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +20 -85
  388. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +6 -25
  389. data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +18 -24
  390. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +55 -181
  391. data/lib/hecks/ports/persistence/plugins/era/postgres_era/events.rb +77 -0
  392. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +12 -122
  393. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +28 -128
  394. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +73 -474
  395. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +31 -124
  396. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +45 -219
  397. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +13 -108
  398. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +19 -76
  399. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +9 -49
  400. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +6 -123
  401. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +23 -55
  402. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +14 -72
  403. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +7 -16
  404. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +43 -127
  405. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +13 -45
  406. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +131 -555
  407. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +13 -63
  408. data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +227 -0
  409. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +2 -17
  410. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +3 -14
  411. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +13 -58
  412. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -17
  413. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +9 -26
  414. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +50 -47
  415. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +18 -124
  416. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +34 -123
  417. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +5 -12
  418. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +3 -9
  419. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -14
  420. data/lib/hecks/ports/persistence/plugins/era/translation.rb +1 -0
  421. data/lib/hecks/ports/persistence/plugins/era.rb +4 -23
  422. data/lib/hecks/ports/persistence/postgres_dump/connection.rb +7 -23
  423. data/lib/hecks/ports/persistence/postgres_dump.rb +4 -35
  424. data/lib/hecks/ports/persistence/remote_runtime.rb +2 -24
  425. data/lib/hecks/ports/persistence/repository_factory.rb +8 -27
  426. data/lib/hecks/ports/persistence/state_codec.rb +47 -108
  427. data/lib/hecks/ports/persistence.rb +7 -14
  428. data/lib/hecks/ports/projection.rb +42 -84
  429. data/lib/hecks/ports/query/in_memory.rb +10 -58
  430. data/lib/hecks/ports/query/ordering.rb +11 -32
  431. data/lib/hecks/ports/query.rb +14 -37
  432. data/lib/hecks/ports.rb +2 -8
  433. data/lib/hecks/projection_files.rb +285 -0
  434. data/lib/hecks/projections/bootstrap_table.rb +12 -44
  435. data/lib/hecks/projections/deploy/fargate/assembly.rb +2 -15
  436. data/lib/hecks/projections/deploy/fargate/cdn.rb +1 -48
  437. data/lib/hecks/projections/deploy/fargate/cdn_default.rb +2 -11
  438. data/lib/hecks/projections/deploy/fargate/check.rb +5 -14
  439. data/lib/hecks/projections/deploy/fargate/containers.rb +3 -78
  440. data/lib/hecks/projections/deploy/fargate/extras.rb +3 -44
  441. data/lib/hecks/projections/deploy/fargate/monitoring.rb +0 -44
  442. data/lib/hecks/projections/deploy/fargate/settings.rb +2 -54
  443. data/lib/hecks/projections/deploy/fargate/yaml.rb +13 -58
  444. data/lib/hecks/projections/deploy/fargate.rb +40 -207
  445. data/lib/hecks/projections/deploy/lambda.rb +191 -708
  446. data/lib/hecks/projections/deploy/preview/access_yaml.rb +1 -33
  447. data/lib/hecks/projections/deploy/preview/containers.rb +6 -111
  448. data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
  449. data/lib/hecks/projections/deploy/preview/routing_yaml.rb +4 -56
  450. data/lib/hecks/projections/deploy/preview/script.rb +2 -14
  451. data/lib/hecks/projections/deploy/preview/settings.rb +3 -33
  452. data/lib/hecks/projections/deploy/preview/task_yaml.rb +2 -38
  453. data/lib/hecks/projections/deploy/preview/template.rb +3 -59
  454. data/lib/hecks/projections/deploy/preview.rb +2 -67
  455. data/lib/hecks/projections/deploy/scripts/deploy-service.sh.tmpl +1 -1
  456. data/lib/hecks/projections/deploy/scripts/expected-era.header +1 -1
  457. data/lib/hecks/projections/deploy/scripts/hosting.mk.tmpl +3 -3
  458. data/lib/hecks/projections/deploy/scripts/settings.rb +3 -25
  459. data/lib/hecks/projections/deploy/scripts/smoke-after-deploy.sh.tmpl +1 -1
  460. data/lib/hecks/projections/deploy/scripts.rb +4 -57
  461. data/lib/hecks/projections/deploy/shared.rb +48 -147
  462. data/lib/hecks/projections/deploy/smoke.rb +4 -61
  463. data/lib/hecks/projections/deploy/template_diff/comparer.rb +3 -25
  464. data/lib/hecks/projections/deploy/template_diff/loader.rb +2 -11
  465. data/lib/hecks/projections/deploy/template_diff.rb +5 -57
  466. data/lib/hecks/projections/diagrams.rb +22 -584
  467. data/lib/hecks/projections/glossary/html.rb +14 -130
  468. data/lib/hecks/projections/glossary/markdown.rb +8 -69
  469. data/lib/hecks/projections/glossary/mermaid.rb +9 -66
  470. data/lib/hecks/projections/glossary/sensitivity.rb +5 -34
  471. data/lib/hecks/projections/glossary/sentences.rb +14 -159
  472. data/lib/hecks/projections/glossary.rb +30 -222
  473. data/lib/hecks/projections/ir.rb +3 -10
  474. data/lib/hecks/projections/model/deviations.rb +16 -43
  475. data/lib/hecks/projections/model.rb +12 -51
  476. data/lib/hecks/projections/oidc.rb +12 -56
  477. data/lib/hecks/projections/parser_table.rb +13 -32
  478. data/lib/hecks/projections/reference.rb +4 -18
  479. data/lib/hecks/projections/rust_vocabulary.rb +13 -251
  480. data/lib/hecks/projections/shape.rb +3 -29
  481. data/lib/hecks/projections/statements.rb +7 -86
  482. data/lib/hecks/projections/vocabulary.rb +6 -22
  483. data/lib/hecks/projections.rb +2 -16
  484. data/lib/hecks/projector/cli_projector.rb +154 -400
  485. data/lib/hecks/projector/docs_projector.rb +10 -201
  486. data/lib/hecks/projector/exporter.rb +42 -134
  487. data/lib/hecks/projector/ir_projector.rb +2 -9
  488. data/lib/hecks/projector/narrate_projector.rb +15 -189
  489. data/lib/hecks/projector/target.rb +13 -76
  490. data/lib/hecks/projector.rb +30 -121
  491. data/lib/hecks/quality_control/adapters/agent.adapter +7 -0
  492. data/lib/hecks/quality_control/adapters/agent.rb +58 -0
  493. data/lib/hecks/quality_control/adapters/angle_tools.adapter +7 -0
  494. data/lib/hecks/quality_control/adapters/angle_tools.rb +15 -0
  495. data/lib/hecks/quality_control/adapters/clearance_recorder.rb +36 -0
  496. data/lib/hecks/quality_control/adapters/clearance_tools.adapter +7 -0
  497. data/lib/hecks/quality_control/adapters/clearance_tools.rb +17 -0
  498. data/lib/hecks/quality_control/adapters/git_pr.adapter +7 -0
  499. data/lib/hecks/quality_control/adapters/git_pr.rb +226 -0
  500. data/lib/hecks/quality_control/adapters/github_checks.adapter +9 -0
  501. data/lib/hecks/quality_control/adapters/github_checks.rb +83 -0
  502. data/lib/hecks/quality_control/adapters/github_ci_webhook.rb +56 -0
  503. data/lib/hecks/quality_control/adapters/github_issues.adapter +7 -0
  504. data/lib/hecks/quality_control/adapters/github_issues.rb +49 -0
  505. data/lib/hecks/quality_control/adapters/qa_tool.rb +59 -0
  506. data/lib/hecks/quality_control/adapters/sweep_tools.adapter +7 -0
  507. data/lib/hecks/quality_control/adapters/sweep_tools.rb +51 -0
  508. data/lib/hecks/quality_control/adapters/target_tools.adapter +7 -0
  509. data/lib/hecks/quality_control/adapters/target_tools.rb +43 -0
  510. data/lib/hecks/quality_control/cli/child.rb +45 -0
  511. data/lib/hecks/quality_control/cli/qa_concurrency_racer.rb +45 -0
  512. data/lib/hecks/quality_control/cli/qa_discover_external_domains.rb +248 -0
  513. data/lib/hecks/quality_control/cli/qa_domain_novelty.rb +163 -0
  514. data/lib/hecks/quality_control/cli/qa_generated_domains.rb +450 -0
  515. data/lib/hecks/quality_control/cli/qa_log_bug.rb +195 -0
  516. data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +220 -0
  517. data/lib/hecks/quality_control/cli/qa_open_pr.rb +305 -0
  518. data/lib/hecks/quality_control/cli/qa_postgres_migrate.rb +154 -0
  519. data/lib/hecks/quality_control/cli/qa_postgres_role.rb +159 -0
  520. data/lib/hecks/quality_control/cli/qa_pr_check.rb +232 -0
  521. data/lib/hecks/quality_control/cli/qa_seed_angles.rb +142 -0
  522. data/lib/hecks/quality_control/cli/qa_seed_targets.rb +69 -0
  523. data/lib/hecks/quality_control/cli/qa_sweep/all_mode.rb +280 -0
  524. data/lib/hecks/quality_control/cli/qa_sweep/checks.rb +329 -0
  525. data/lib/hecks/quality_control/cli/qa_sweep/dials.rb +92 -0
  526. data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +117 -0
  527. data/lib/hecks/quality_control/cli/qa_sweep/parsing.rb +175 -0
  528. data/lib/hecks/quality_control/cli/qa_sweep/release_mode.rb +60 -0
  529. data/lib/hecks/quality_control/cli/qa_sweep.rb +611 -0
  530. data/lib/hecks/quality_control/cli/qa_tick.rb +274 -0
  531. data/lib/hecks/quality_control/quality_control.bluebook +2224 -0
  532. data/lib/hecks/quality_control/quality_control.hecksagon +13 -0
  533. data/lib/hecks/quality_control/quality_control.ports.hecksagon +54 -0
  534. data/lib/hecks/query_ir.rb +346 -0
  535. data/lib/hecks/query_ir_mcp.rb +198 -0
  536. data/lib/hecks/query_specification/common/comparators.rb +5 -26
  537. data/lib/hecks/query_specification/common/comparison.rb +35 -143
  538. data/lib/hecks/query_specification/common/dsl.rb +8 -24
  539. data/lib/hecks/query_specification/common/null_policy.rb +20 -70
  540. data/lib/hecks/query_specification/common/options.rb +2 -6
  541. data/lib/hecks/query_specification/common/where_clause.rb +2 -10
  542. data/lib/hecks/query_specification/field_path.rb +37 -77
  543. data/lib/hecks/query_specification/hop_path.rb +12 -76
  544. data/lib/hecks/release/gem_pin.rb +2 -26
  545. data/lib/hecks/release/runner/ci_publisher.rb +5 -8
  546. data/lib/hecks/release/runner/clean_tree.rb +42 -0
  547. data/lib/hecks/release/runner/commands.rb +2 -4
  548. data/lib/hecks/release/runner/console.rb +6 -0
  549. data/lib/hecks/release/runner/gem_publisher.rb +15 -9
  550. data/lib/hecks/release/runner/npm_publisher.rb +9 -53
  551. data/lib/hecks/release/runner/preflight.rb +29 -20
  552. data/lib/hecks/release/runner/published.rb +8 -26
  553. data/lib/hecks/release/runner/tagger.rb +31 -23
  554. data/lib/hecks/release/runner.rb +60 -38
  555. data/lib/hecks/rendering.rb +4 -26
  556. data/lib/hecks/router/namespace_installer.rb +15 -39
  557. data/lib/hecks/router.rb +4 -8
  558. data/lib/hecks/runtime/adapter_lookup.rb +124 -0
  559. data/lib/hecks/runtime/aggregate_lock.rb +8 -33
  560. data/lib/hecks/runtime/boot_gates.rb +3 -18
  561. data/lib/hecks/runtime/caller.rb +19 -59
  562. data/lib/hecks/runtime/capability_graph.rb +5 -21
  563. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +16 -78
  564. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +27 -160
  565. data/lib/hecks/runtime/command_interpreter.rb +124 -306
  566. data/lib/hecks/runtime/command_rules/admissibility.rb +95 -366
  567. data/lib/hecks/runtime/command_rules/arithmetic.rb +66 -196
  568. data/lib/hecks/runtime/command_rules/authorization.rb +13 -45
  569. data/lib/hecks/runtime/command_rules/emission.rb +8 -21
  570. data/lib/hecks/runtime/command_rules/references.rb +23 -202
  571. data/lib/hecks/runtime/dependency_planning.rb +50 -104
  572. data/lib/hecks/runtime/dispatcher.rb +134 -243
  573. data/lib/hecks/runtime/entity_element.rb +40 -462
  574. data/lib/hecks/runtime/entity_interpreter.rb +64 -219
  575. data/lib/hecks/runtime/errors.rb +32 -115
  576. data/lib/hecks/runtime/event.rb +3 -25
  577. data/lib/hecks/runtime/identity.rb +18 -154
  578. data/lib/hecks/runtime/instance.rb +18 -150
  579. data/lib/hecks/runtime/interpreting.rb +14 -70
  580. data/lib/hecks/runtime/invocation.rb +28 -164
  581. data/lib/hecks/runtime/loader.rb +109 -194
  582. data/lib/hecks/runtime/outbox.rb +39 -262
  583. data/lib/hecks/runtime/policy_interpreter.rb +43 -202
  584. data/lib/hecks/runtime/port_operation_interpreter.rb +28 -97
  585. data/lib/hecks/runtime/query_interpreter.rb +122 -187
  586. data/lib/hecks/runtime/reaction_invocation.rb +36 -138
  587. data/lib/hecks/runtime/reaction_outcome.rb +89 -0
  588. data/lib/hecks/runtime/read_model_interpreter.rb +117 -208
  589. data/lib/hecks/runtime/rebuild_sweep.rb +8 -54
  590. data/lib/hecks/runtime/reference_hop.rb +8 -81
  591. data/lib/hecks/runtime/refusal_wording.rb +28 -87
  592. data/lib/hecks/runtime/registry/saga_persistence.rb +10 -102
  593. data/lib/hecks/runtime/registry/verification.rb +213 -227
  594. data/lib/hecks/runtime/registry/world_defaults.rb +4 -53
  595. data/lib/hecks/runtime/registry.rb +156 -247
  596. data/lib/hecks/runtime/remote_dispatcher.rb +68 -154
  597. data/lib/hecks/runtime/routing.rb +4 -15
  598. data/lib/hecks/runtime/saga_interpreter/correlation.rb +14 -61
  599. data/lib/hecks/runtime/saga_interpreter.rb +79 -263
  600. data/lib/hecks/runtime/saga_pending_dispatch.rb +2 -43
  601. data/lib/hecks/runtime/tenant_check.rb +6 -68
  602. data/lib/hecks/runtime/tenant_scope.rb +3 -28
  603. data/lib/hecks/runtime/value/admission.rb +16 -67
  604. data/lib/hecks/runtime/value/coercion.rb +162 -760
  605. data/lib/hecks/runtime/value/entity_list_coercion.rb +26 -219
  606. data/lib/hecks/runtime/value/field_checks.rb +172 -0
  607. data/lib/hecks/runtime/value.rb +15 -84
  608. data/lib/hecks/runtime.rb +35 -69
  609. data/lib/hecks/rust_build/conformance.rb +158 -0
  610. data/lib/hecks/rust_build/conformance_fuzz.rb +71 -0
  611. data/lib/hecks/rust_build/coverage.rb +218 -0
  612. data/lib/hecks/rust_build/native_build.rb +124 -0
  613. data/lib/hecks/rust_build/project_rust/root_mod.rb +120 -0
  614. data/lib/hecks/rust_build/project_rust.rb +332 -0
  615. data/lib/hecks/rust_build/wasm.rb +105 -0
  616. data/lib/hecks/rust_build/wasm_browser.rb +113 -0
  617. data/lib/hecks/rust_build.rb +144 -0
  618. data/lib/hecks/storehouse.rb +155 -582
  619. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +10 -34
  620. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +6 -23
  621. data/lib/hecks/three_zero/forms.yml +127 -0
  622. data/lib/hecks/three_zero.rb +12 -0
  623. data/lib/hecks/tools/argument_gate_matrix.rb +316 -0
  624. data/lib/hecks/tools/comment_style.rb +1070 -0
  625. data/lib/hecks/tools/corpus_report.rb +87 -0
  626. data/lib/hecks/tools/deploy_recipe.rb +255 -0
  627. data/lib/hecks/tools/deploy_recipe_lint.rb +282 -0
  628. data/lib/hecks/tools/drop_implicit_append_fields.rb +95 -0
  629. data/lib/hecks/tools/era_projection_backfill.rb +94 -0
  630. data/lib/hecks/tools/era_reattest.rb +146 -0
  631. data/lib/hecks/tools/evolve_run.rb +189 -0
  632. data/lib/hecks/tools/fuzz_sweep.rb +373 -0
  633. data/lib/hecks/tools/heki_compaction.rb +100 -0
  634. data/lib/hecks/tools/hoist_local_givens.rb +246 -0
  635. data/lib/hecks/tools/journal_compaction.rb +89 -0
  636. data/lib/hecks/tools/oidc_manifests.rb +70 -0
  637. data/lib/hecks/tools/query_ir_run.rb +81 -0
  638. data/lib/hecks/tools/regeneration_run.rb +166 -0
  639. data/lib/hecks/tools/rust_comment_style.rb +642 -0
  640. data/lib/hecks/tools/sequence_script.rb +62 -0
  641. data/lib/hecks/tools/tail_merge.rb +117 -0
  642. data/lib/hecks/tools/tenant_provisioning.rb +142 -0
  643. data/lib/hecks/tools/translation_audit.rb +208 -0
  644. data/lib/hecks/tools/translation_scaffold.rb +131 -0
  645. data/lib/hecks/tools.rb +86 -0
  646. data/lib/hecks/vendoring/git_environment.rb +2 -8
  647. data/lib/hecks/vendoring/git_source.rb +4 -10
  648. data/lib/hecks/vendoring.rb +7 -46
  649. data/lib/hecks/version.rb +3 -14
  650. data/lib/hecks.rb +52 -90
  651. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -46
  652. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +11 -80
  653. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +10 -99
  654. data/qa/settings.yml +66 -0
  655. data/rust/.cargo/config.toml +6 -0
  656. data/rust/.gitignore +2 -0
  657. data/rust/Cargo.lock +54 -0
  658. data/rust/Cargo.toml +61 -0
  659. data/rust/build/.cargo/config.toml +3 -0
  660. data/rust/build/.gitignore +1 -0
  661. data/rust/build/Cargo.lock +7 -0
  662. data/rust/build/Cargo.toml +19 -0
  663. data/rust/build/src/build_artifact.rs +47 -0
  664. data/rust/build/src/cargo_sync.rs +248 -0
  665. data/rust/build/src/json.rs +421 -0
  666. data/rust/build/src/lineage_pass.rs +285 -0
  667. data/rust/build/src/main.rs +51 -0
  668. data/rust/build/src/optional_pass.rs +150 -0
  669. data/rust/build/src/pipeline.rs +264 -0
  670. data/rust/build/src/reserved_names.rs +76 -0
  671. data/rust/build/src/resolve.rs +124 -0
  672. data/rust/build/src/root.rs +25 -0
  673. data/rust/build/src/sidecars.rs +73 -0
  674. data/rust/build/src/subprocess.rs +51 -0
  675. data/rust/build/src/tmp.rs +31 -0
  676. data/rust/codegen/.cargo/config.toml +3 -0
  677. data/rust/codegen/.gitignore +1 -0
  678. data/rust/codegen/Cargo.lock +7 -0
  679. data/rust/codegen/Cargo.toml +20 -0
  680. data/rust/codegen/src/attr.rs +32 -0
  681. data/rust/codegen/src/bridging.rs +481 -0
  682. data/rust/codegen/src/commands.rs +1226 -0
  683. data/rust/codegen/src/constraints.rs +109 -0
  684. data/rust/codegen/src/dependency_planning.rs +209 -0
  685. data/rust/codegen/src/domain_generator.rs +1288 -0
  686. data/rust/codegen/src/exemplar.rs +230 -0
  687. data/rust/codegen/src/expr_emitter.rs +87 -0
  688. data/rust/codegen/src/fielded.rs +192 -0
  689. data/rust/codegen/src/hecks_naming.rs +33 -0
  690. data/rust/codegen/src/json.rs +335 -0
  691. data/rust/codegen/src/json_codec.rs +697 -0
  692. data/rust/codegen/src/literal.rs +207 -0
  693. data/rust/codegen/src/main.rs +290 -0
  694. data/rust/codegen/src/manifest.rs +227 -0
  695. data/rust/codegen/src/mutations.rs +784 -0
  696. data/rust/codegen/src/naming.rs +286 -0
  697. data/rust/codegen/src/ports.rs +208 -0
  698. data/rust/codegen/src/prelude.rs +116 -0
  699. data/rust/codegen/src/queries.rs +786 -0
  700. data/rust/codegen/src/reactions.rs +532 -0
  701. data/rust/codegen/src/read_models.rs +697 -0
  702. data/rust/codegen/src/reference_specs.rs +40 -0
  703. data/rust/codegen/src/registry.rs +966 -0
  704. data/rust/codegen/src/reserved_names.rs +76 -0
  705. data/rust/codegen/src/shared.rs +93 -0
  706. data/rust/codegen/src/skip_reason.rs +25 -0
  707. data/rust/codegen/src/types.rs +362 -0
  708. data/rust/host/.cargo/config.toml +3 -0
  709. data/rust/host/.gitignore +1 -0
  710. data/rust/host/Cargo.lock +4297 -0
  711. data/rust/host/Cargo.toml +61 -0
  712. data/rust/host/HECKS_RELEASE +1 -0
  713. data/rust/host/rds-ca-bundle.pem +2736 -0
  714. data/rust/host/src/api.rs +1596 -0
  715. data/rust/host/src/approval.rs +652 -0
  716. data/rust/host/src/auth.rs +1287 -0
  717. data/rust/host/src/bin/expr_harness.rs +46 -0
  718. data/rust/host/src/bin/lineage_harness.rs +119 -0
  719. data/rust/host/src/bin/mint_harness.rs +135 -0
  720. data/rust/host/src/checkout.rs +265 -0
  721. data/rust/host/src/dispatch.rs +1443 -0
  722. data/rust/host/src/expr_json.rs +1117 -0
  723. data/rust/host/src/field_hints.rs +52 -0
  724. data/rust/host/src/ir.rs +733 -0
  725. data/rust/host/src/journal.rs +874 -0
  726. data/rust/host/src/lambda_client.rs +378 -0
  727. data/rust/host/src/log.rs +147 -0
  728. data/rust/host/src/main.rs +474 -0
  729. data/rust/host/src/mint.rs +1865 -0
  730. data/rust/host/src/payments/keystore.rs +330 -0
  731. data/rust/host/src/payments/tests.rs +1400 -0
  732. data/rust/host/src/payments.rs +767 -0
  733. data/rust/host/src/presentation.rs +698 -0
  734. data/rust/host/src/presentation_write.rs +1431 -0
  735. data/rust/host/src/query_step.rs +149 -0
  736. data/rust/host/src/rate_limit.rs +904 -0
  737. data/rust/host/src/reference_transform.rs +241 -0
  738. data/rust/host/src/reference_validate.rs +338 -0
  739. data/rust/host/src/resend.rs +296 -0
  740. data/rust/host/src/secrets.rs +106 -0
  741. data/rust/host/src/server.rs +663 -0
  742. data/rust/host/src/storage_shape.rs +175 -0
  743. data/rust/host/src/test_pg.rs +40 -0
  744. data/rust/host/src/ui_schema.rs +1351 -0
  745. data/rust/host/src/wasm_runner.rs +191 -0
  746. data/rust/host/src/web/newsletter/cooldown.rs +126 -0
  747. data/rust/host/src/web/newsletter/tests.rs +104 -0
  748. data/rust/host/src/web/newsletter.rs +310 -0
  749. data/rust/host/src/web/newsletter_send.rs +357 -0
  750. data/rust/host/src/web/registrations.rs +1505 -0
  751. data/rust/host/src/web.rs +2937 -0
  752. data/rust/lsp/.gitignore +1 -0
  753. data/rust/lsp/Cargo.lock +7 -0
  754. data/rust/lsp/Cargo.toml +16 -0
  755. data/rust/lsp/README.md +133 -0
  756. data/rust/lsp/src/diagnostics.rs +165 -0
  757. data/rust/lsp/src/json.rs +385 -0
  758. data/rust/lsp/src/main.rs +399 -0
  759. data/rust/lsp/src/outline.rs +177 -0
  760. data/rust/lsp/src/rpc.rs +67 -0
  761. data/rust/parser/.cargo/config.toml +5 -0
  762. data/rust/parser/.gitignore +1 -0
  763. data/rust/parser/Cargo.lock +7 -0
  764. data/rust/parser/Cargo.toml +21 -0
  765. data/rust/parser/src/build/closed_sets.rs +27 -0
  766. data/rust/parser/src/build/identity.rs +276 -0
  767. data/rust/parser/src/build/mod.rs +12 -0
  768. data/rust/parser/src/build/naming.rs +141 -0
  769. data/rust/parser/src/build/pattern_subset.rs +161 -0
  770. data/rust/parser/src/build/query_derive.rs +72 -0
  771. data/rust/parser/src/build/query_inference.rs +269 -0
  772. data/rust/parser/src/build/query_options.rs +100 -0
  773. data/rust/parser/src/build/read_model.rs +94 -0
  774. data/rust/parser/src/build/references.rs +32 -0
  775. data/rust/parser/src/canonical.rs +114 -0
  776. data/rust/parser/src/diag.rs +55 -0
  777. data/rust/parser/src/emit.rs +953 -0
  778. data/rust/parser/src/expr/ast_json.rs +147 -0
  779. data/rust/parser/src/expr/evaluator.rs +81 -0
  780. data/rust/parser/src/expr/mod.rs +84 -0
  781. data/rust/parser/src/expr/resolver.rs +497 -0
  782. data/rust/parser/src/ir.rs +387 -0
  783. data/rust/parser/src/keywords.rs +585 -0
  784. data/rust/parser/src/lex.rs +662 -0
  785. data/rust/parser/src/main.rs +252 -0
  786. data/rust/parser/src/parse/aggregate.rs +612 -0
  787. data/rust/parser/src/parse/chapter.rs +517 -0
  788. data/rust/parser/src/parse/command.rs +637 -0
  789. data/rust/parser/src/parse/domain_port.rs +127 -0
  790. data/rust/parser/src/parse/entity.rs +527 -0
  791. data/rust/parser/src/parse/file.rs +67 -0
  792. data/rust/parser/src/parse/hecksagon.rs +231 -0
  793. data/rust/parser/src/parse/lifecycle.rs +162 -0
  794. data/rust/parser/src/parse/mod.rs +1262 -0
  795. data/rust/parser/src/parse/policy.rs +87 -0
  796. data/rust/parser/src/parse/process_manager.rs +305 -0
  797. data/rust/parser/src/parse/query.rs +100 -0
  798. data/rust/parser/src/parse/read_model.rs +214 -0
  799. data/rust/parser/src/parse/value_object.rs +250 -0
  800. data/rust/parser/src/ruby_value.rs +422 -0
  801. data/rust/project/bridging.rb +257 -0
  802. data/rust/project/commands.rb +877 -0
  803. data/rust/project/constraints.rb +86 -0
  804. data/rust/project/dependency_planning.rb +178 -0
  805. data/rust/project/domain_generator.rb +980 -0
  806. data/rust/project/exemplar.rb +148 -0
  807. data/rust/project/expr_emitter.rb +72 -0
  808. data/rust/project/fielded.rb +141 -0
  809. data/rust/project/json_codec.rb +641 -0
  810. data/rust/project/mutations.rb +721 -0
  811. data/rust/project/naming.rb +205 -0
  812. data/rust/project/ports.rb +65 -0
  813. data/rust/project/queries.rb +493 -0
  814. data/rust/project/reactions.rb +317 -0
  815. data/rust/project/read_models.rb +690 -0
  816. data/rust/project/reference_specs.rb +27 -0
  817. data/rust/project/registry.rb +396 -0
  818. data/rust/project/skip_reason.rb +29 -0
  819. data/rust/project/types.rb +225 -0
  820. data/rust/project/write_if_changed.rb +83 -0
  821. data/rust/project.rb +30 -0
  822. data/rust/project_rust_pipeline.rb +383 -0
  823. data/rust/src/exemplar/commands.rs +279 -0
  824. data/rust/src/exemplar/constraints.rs +18 -0
  825. data/rust/src/exemplar/fielded.rs +280 -0
  826. data/rust/src/exemplar/json.rs +277 -0
  827. data/rust/src/exemplar/mod.rs +17 -0
  828. data/rust/src/exemplar/mutations.rs +80 -0
  829. data/rust/src/exemplar/queries.rs +33 -0
  830. data/rust/src/exemplar/reactions.rs +88 -0
  831. data/rust/src/exemplar/read_models.rs +48 -0
  832. data/rust/src/exemplar/registry.rs +166 -0
  833. data/rust/src/exemplar/types.rs +44 -0
  834. data/rust/src/kernel/attribute_shapes/composite.rs +88 -0
  835. data/rust/src/kernel/attribute_shapes/list.rs +18 -0
  836. data/rust/src/kernel/attribute_shapes/mod.rs +44 -0
  837. data/rust/src/kernel/attribute_shapes/optional.rs +9 -0
  838. data/rust/src/kernel/attribute_shapes/scalar.rs +39 -0
  839. data/rust/src/kernel/cli.rs +585 -0
  840. data/rust/src/kernel/dispatch.rs +1090 -0
  841. data/rust/src/kernel/expr.rs +458 -0
  842. data/rust/src/kernel/expression_operators/arithmetic.rs +128 -0
  843. data/rust/src/kernel/expression_operators/comparison.rs +48 -0
  844. data/rust/src/kernel/expression_operators/enumeration.rs +232 -0
  845. data/rust/src/kernel/expression_operators/logical.rs +13 -0
  846. data/rust/src/kernel/expression_operators/membership.rs +18 -0
  847. data/rust/src/kernel/expression_operators/mod.rs +68 -0
  848. data/rust/src/kernel/expression_operators/pattern_match.rs +73 -0
  849. data/rust/src/kernel/expression_operators/positional.rs +126 -0
  850. data/rust/src/kernel/expression_operators/presence.rs +101 -0
  851. data/rust/src/kernel/expression_operators/sign_test.rs +16 -0
  852. data/rust/src/kernel/expression_operators/sized.rs +35 -0
  853. data/rust/src/kernel/expression_operators/text.rs +79 -0
  854. data/rust/src/kernel/expression_operators/to_string.rs +18 -0
  855. data/rust/src/kernel/json.rs +779 -0
  856. data/rust/src/kernel/mod.rs +133 -0
  857. data/rust/src/kernel/named_query.rs +219 -0
  858. data/rust/src/kernel/orchestrate.rs +1810 -0
  859. data/rust/src/kernel/pattern.rs +307 -0
  860. data/rust/src/kernel/query_comparators.rs +206 -0
  861. data/rust/src/kernel/query_ordering.rs +103 -0
  862. data/rust/src/kernel/read_model.rs +914 -0
  863. data/rust/src/kernel/reference_lookup.rs +149 -0
  864. data/rust/src/kernel/repository.rs +468 -0
  865. data/rust/src/kernel/routing.rs +612 -0
  866. data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +118 -0
  867. data/rust/src/kernel/vocab/entity_dispatch_order.rs +114 -0
  868. data/rust/src/kernel/vocab/field_hint.rs +70 -0
  869. data/rust/src/kernel/vocab/mod.rs +20 -0
  870. data/rust/src/kernel/vocab/query_comparator.rs +65 -0
  871. data/rust/src/kernel/vocab/refusal_template.rs +1592 -0
  872. data/rust/src/lib.rs +8 -0
  873. data/rust/src/main.rs +16 -0
  874. data/rust/web/.gitignore +1 -0
  875. data/rust/web/Cargo.lock +119 -0
  876. data/rust/web/Cargo.toml +18 -0
  877. data/rust/web/src/lib.rs +8 -0
  878. metadata +478 -12
  879. data/lib/hecks/facade/cli_door.rb +0 -178
  880. data/lib/hecks/facade/cli_runner.rb +0 -271
  881. data/lib/hecks/facade/handle.rb +0 -293
  882. data/lib/hecks/facade/json_door.rb +0 -247
  883. data/lib/hecks/facade/surface/aggregate_door.rb +0 -222
  884. data/lib/hecks/facade/surface/chapter.rb +0 -116
  885. data/lib/hecks/facade/surface.rb +0 -61
  886. data/lib/hecks/mcp_door_scope.rb +0 -176
@@ -7,23 +7,13 @@ require_relative "record_renderer"
7
7
  require_relative "command_form_renderer"
8
8
  require_relative "query_form_renderer"
9
9
  require_relative "params"
10
- require_relative "../facade/command_request"
11
- require_relative "../facade/json_door"
10
+ require_relative "../doors/command_request"
11
+ require_relative "../doors/json_door"
12
12
 
13
13
  module Hecks
14
14
  module Forms
15
- # The content-negotiated router: `GET /Banking/Account/Overdrawn.html`
16
- # renders the query view built in this directory; the identical path
17
- # with no extension (or `.json`) dispatches the same ask through
18
- # `Runtime::Dispatcher#query` and answers with its raw result — one
19
- # route, two representations, exactly the "change the file format on
20
- # the route" mechanism this was built around
21
- # (docs/command-form-and-query-form-bluebook.md).
22
- #
23
- # A plain Rack app (`#call(env)`) — no Sinatra, no Rails. `rack` itself
24
- # is a lazy dependency the same way `pg`/`oauth2`/`aws-sdk-lambda` are
25
- # for their own adapters (see the Gemfile's own comment) : a project
26
- # that never boots this file never needs it installed.
15
+ # A Rack app that content-negotiates one route: `/Banking/Account/Overdrawn.html`
16
+ # renders HTML, and the same path with no extension or `.json` answers JSON.
27
17
  class App
28
18
  # Builds the Rack app for a configured app name, exposing exactly the chapters its
29
19
  # `Forms.configure` block declared.
@@ -99,18 +89,8 @@ module Hecks
99
89
  raise RouteNotFound, "#{domain.inspect} is not exposed by this app — declared chapters: #{@exposed.join(', ')}"
100
90
  end
101
91
 
102
- # H12 (docs/audits/2026-08-10-main-bug-audit.md) — only a literal
103
- # trailing ".html"/".json" counts as a format; every other dot in the
104
- # segment is just part of the identity. Splitting on the first "."
105
- # instead truncates any identity value containing a dot (an email
106
- # `identified_by { email.address }`, a decimal-ish reference — an
107
- # aggregate's identity is free-form unless its value object declares
108
- # a `pattern:`, see S3 in the same audit) at its own first dot, so
109
- # `reference.value=c.1` 404s everywhere: detail page, JSON view, and
110
- # its own index-table link. An identity that itself happens to end in
111
- # exactly ".html" or ".json" is still ambiguous with a real format
112
- # suffix — the same tension any extension-based content-negotiation
113
- # scheme has, and not what H12 is about.
92
+ # Only a trailing ".html"/".json" is a format; other dots belong to the identity
93
+ # (an email, say). An identity ending in ".html" or ".json" stays ambiguous.
114
94
  def split_format(segment)
115
95
  segment = segment.to_s
116
96
  return [Regexp.last_match(1), Regexp.last_match(2)] if segment =~ /\A(.*)\.(html|json)\z/
@@ -118,8 +98,6 @@ module Hecks
118
98
  [segment, "json"]
119
99
  end
120
100
 
121
- # ---- home -------------------------------------------------------
122
-
123
101
  def home(request)
124
102
  format = request.params["format"] || "html"
125
103
  chapters = @exposed.to_h { |name| [name, @registry.bluebook(name)] }.compact
@@ -128,8 +106,6 @@ module Hecks
128
106
  html("Exposed domains", IndexRenderer.render(chapters))
129
107
  end
130
108
 
131
- # ---- /:domain/:aggregate.:format ---------------------------------
132
-
133
109
  def aggregate_route(request, chapter, aggregate_name, format)
134
110
  aggregate = find_aggregate(chapter, aggregate_name)
135
111
  return respond(405, "text/plain", "GET only") unless request.get?
@@ -140,31 +116,17 @@ module Hecks
140
116
  breadcrumbs: [[chapter.name, "/"], [aggregate.hecks_name, nil]])
141
117
  else
142
118
  instances = @registry.repository(chapter.name, aggregate).all
143
- # id last — see Instance#to_h's own comment: an aggregate free
144
- # to declare its own attribute literally named `id` has that
145
- # attribute's own wrapped value sitting in `i.state[:id]`
146
- # already, which silently clobbers the correct bare identity
147
- # if `id:` is merged first.
119
+ # `id:` is merged last so an attribute named `id` in the state cannot clobber it.
148
120
  json(200, instances.map { |i| i.state.merge(id: i.id) })
149
121
  end
150
122
  end
151
123
 
152
- # ---- /:domain/:aggregate/:verb_or_id.:format ----------------------
153
-
154
124
  def verb_or_record_route(request, chapter, aggregate_name, verb_or_id, format)
155
125
  aggregate = find_aggregate(chapter, aggregate_name)
156
126
  domain = chapter.name
157
127
 
158
- # L11 (docs/audits/2026-08-10-main-bug-audit.md) — a record's own
159
- # id is free-form (S3) and can collide with one of its own
160
- # aggregate's command/query names ("Close", "Overdrawn", ...).
161
- # A GET for such an id must still be able to reach that record's
162
- # own detail page when a record with that literal id actually
163
- # exists — checking the verb first would mean a record unlucky
164
- # enough to be named after a real verb could never be viewed.
165
- # POST never means "view a record" at all (`record_route` only
166
- # ever answers GET), so command submission there is unambiguous
167
- # and matches the verb first.
128
+ # A record id is free-form and can equal a verb name ("Close"), so a GET checks for
129
+ # a record first. POST never views a record, so it matches the verb first.
168
130
  if request.get? && (instance = @registry.repository(domain, aggregate).find(verb_or_id))
169
131
  return record_route(request, domain, aggregate, verb_or_id, format, instance: instance)
170
132
  end
@@ -204,8 +166,7 @@ module Hecks
204
166
  def submit_command(request, domain, aggregate, command, action)
205
167
  raw, envelope = submitted_command(request, aggregate, command)
206
168
  result = @dispatcher.dispatch_flat("#{domain}::#{aggregate.hecks_name}.#{command.hecks_name}", envelope)
207
- # L12 — the id is free-form (S3), so it must be percent-encoded as
208
- # a path segment here, not just interpolated raw.
169
+ # The id is free-form, so it is percent-encoded as a path segment.
209
170
  redirect("/#{domain}/#{aggregate.hecks_name}/#{Escape.path(result.id)}.html")
210
171
  rescue *Runtime::DOMAIN_REFUSALS, ArgumentError, TypeError, JSON::ParserError => e
211
172
  status = e.is_a?(Runtime::NotFound) ? 404 : 422
@@ -218,8 +179,7 @@ module Hecks
218
179
 
219
180
  _, envelope = submitted_command(request, aggregate, command)
220
181
  result = @dispatcher.dispatch_flat("#{domain}::#{aggregate.hecks_name}.#{command.hecks_name}", envelope)
221
- # id last — same reasoning as the other JSON-serializing call
222
- # sites in this file (see aggregate_route's own comment).
182
+ # `id:` last, as in `aggregate_route`.
223
183
  json(201, result.state.merge(id: result.id))
224
184
  rescue *Runtime::DOMAIN_REFUSALS, ArgumentError, TypeError, JSON::ParserError => e
225
185
  status = e.is_a?(Runtime::NotFound) ? 404 : 422
@@ -255,30 +215,18 @@ module Hecks
255
215
  results, error = run_query(domain, aggregate, query, fields, asked)
256
216
  return json(422, { error: error.class.name.split("::").last, message: error.message }) if error
257
217
 
258
- # id last — same reasoning as the other JSON-serializing call
259
- # sites in this file (see aggregate_route's own comment).
218
+ # `id:` last, as in `aggregate_route`.
260
219
  json(200, results.map { |i| i.state.merge(id: i.id) })
261
220
  end
262
221
 
263
- # `Dispatcher#query` answers an array of plain hashes
264
- # (`{id:}.merge(state)`, flattened by `QueryInterpreter#call` on
265
- # every path it can take — native pushdown or the in-memory
266
- # fallback alike), not `Runtime::Instance`. Wrapped into `Record`
267
- # here, once, so every renderer downstream reads one shape whether
268
- # its records came from a query or straight off a repository.
222
+ # `Dispatcher#query` answers plain hashes, not `Runtime::Instance`; wrapping them
223
+ # in `Record` gives the renderers one shape.
269
224
  def run_query(domain, aggregate, query, fields, asked)
270
225
  args = Params.extract(fields, asked)
271
226
  rows = @dispatcher.query("#{domain}::#{aggregate.hecks_name}.#{query.hecks_name}", **args)
272
227
  [rows.map { |row| Record.new(row[:id], row.except(:id)) }, nil]
273
- # L10 (docs/audits/2026-08-10-main-bug-audit.md) — `Params.extract`
274
- # (params.rb's `extract_list`) reads a list-of-value-object line as
275
- # JSON (the honest fallback for a multi-attribute list element this
276
- # prototype's textarea doesn't build a second widget for). A caller
277
- # who types a non-JSON line into that field raises `JSON::ParserError`
278
- # before dispatch ever sees it. It is rescued here, as both command
279
- # submission paths rescue it (`submit_command`, `command_json`), so
280
- # a malformed list-of-VO query shows the same 422 every other
281
- # bad-input path shows rather than a 500.
228
+ # `Params.extract` parses a list-of-value-object line as JSON; rescuing
229
+ # `JSON::ParserError` turns a malformed line into a 422 rather than a 500.
282
230
  rescue *Runtime::DOMAIN_REFUSALS, ArgumentError, TypeError, JSON::ParserError => e
283
231
  [nil, e]
284
232
  end
@@ -288,8 +236,7 @@ module Hecks
288
236
 
289
237
  instance ||= @registry.repository(domain, aggregate).find(id)
290
238
  return not_found(aggregate, id, format) unless instance
291
- # id last — same reasoning as the other JSON-serializing call
292
- # sites in this file (see aggregate_route's own comment).
239
+ # `id:` last, as in `aggregate_route`.
293
240
  return json(200, instance.state.merge(id: instance.id)) if format != "html"
294
241
 
295
242
  html("#{domain}::#{aggregate.hecks_name} #{id}",
@@ -311,16 +258,16 @@ module Hecks
311
258
  end
312
259
 
313
260
  def command_envelope(command, args)
314
- Facade::CommandRequest.normalize(args, receiver: command_receiver(command), legacy_receiver: :id)
261
+ Doors::CommandRequest.normalize(args, receiver: command_receiver(command), legacy_receiver: :id)
315
262
  end
316
263
 
317
264
  def command_receiver(command) = command.creates? ? nil : :aggregate
318
265
 
319
266
  def submitted_command(request, aggregate, command)
320
267
  if request.media_type == "application/json"
321
- raw = Facade::JsonDoor.parse(request.body.read)
322
- envelope = Facade::JsonDoor.command_request(raw, receiver: command_receiver(command),
323
- legacy_receiver: :id)
268
+ raw = Doors::JsonDoor.parse(request.body.read)
269
+ envelope = Doors::JsonDoor.command_request(raw, receiver: command_receiver(command),
270
+ legacy_receiver: :id)
324
271
  return [raw, envelope]
325
272
  end
326
273
 
@@ -339,8 +286,6 @@ module Hecks
339
286
  raw.merge("to" => raw["id"])
340
287
  end
341
288
 
342
- # ---- rendering ----------------------------------------------------
343
-
344
289
  def html(title, body, breadcrumbs: [], status: 200)
345
290
  respond(status, "text/html; charset=utf-8", Page.render(title: title, body: body, breadcrumbs: breadcrumbs))
346
291
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../forms"
4
+ require_relative "port_argument"
5
+
6
+ module Hecks
7
+ module Forms
8
+ # The banking example, wired for the forms app: its bluebook, an in-memory store for every
9
+ # aggregate, and the `expose` sample that renders it.
10
+ #
11
+ # It is what `hecks present` serves. The `expose` sample stays out of `examples/`, since every
12
+ # entry there is scanned as a corpus member and must be a complete, model-checked domain.
13
+ module BankingPresentation
14
+ # The aggregates of the banking example, each held in memory.
15
+ AGGREGATES = %w[Customer Account SafeDepositBox Transfer ATMCard CardPayment ExternalTransfer
16
+ ScheduledPayment OnboardingCase Statement].freeze
17
+
18
+ module_function
19
+
20
+ # Builds the Rack app for the banking example.
21
+ #
22
+ # @param root [String] the checkout's root
23
+ # @return [#call] the app, for a Rack server
24
+ def app(root:)
25
+ registry = Hecks::Runtime::Registry.new
26
+ Hecks.with_registry(registry) { load_into(registry, root) }
27
+ registry.verify!
28
+ Hecks::Forms::App.for(registry: registry, app_name: "BankingConsole")
29
+ end
30
+
31
+ # @api private
32
+ def load_into(_registry, root)
33
+ %w[lib/hecks/ports/persistence.port lib/hecks/ports/extraction.port
34
+ lib/hecks/adapters/driven/memory.adapter lib/hecks/adapters/driven/prism.adapter].each do |file|
35
+ Kernel.load(File.join(root, file))
36
+ end
37
+ Hecks::Adapters::Folder.new.load_bluebooks(File.join(root, "examples/banking/bluebook"))
38
+ Kernel.load(File.join(root, "lib/hecks/forms/examples/banking_console.bluebook"))
39
+ Hecks.hecksagon("Banking") do
40
+ uses_framework "Governance"
41
+ AGGREGATES.each { |name| ::Banking.const_get(name).persisted_by("Memory") }
42
+ end
43
+ Hecks.hecksagon("Governance") do
44
+ ::Governance::RoleAssignment.persisted_by("Memory")
45
+ ::Governance::RoleTransition.persisted_by("Memory")
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -7,18 +7,14 @@ require_relative "params"
7
7
 
8
8
  module Hecks
9
9
  module Forms
10
- # Command -> the page body for its HTML form — the working half of
11
- # what `command_form.bluebook` names (see docs/command-form-and-query-
12
- # form-bluebook.md). Every command in every loaded, exposed bluebook
13
- # renders through here — nothing here is per-command code; see that
14
- # doc for the 1:1 rule this deliberately holds to for now.
10
+ # Renders a command as the page body of its HTML form; no per-command code
11
+ # (see docs/command-form-and-query-form-bluebook.md).
15
12
  module CommandFormRenderer
16
13
  # Lists every field a command's form shows: the `to` identity picker first (unless the
17
14
  # command creates its aggregate), then one field per command attribute.
18
15
  #
19
- # The same field list a POST handler needs to cast raw params against
20
- # (params.rb's `Params.extract`) — one derivation, so a page never
21
- # renders an input the submit handler doesn't also expect.
16
+ # The same list `Params.extract` casts a POST against, so a page never renders
17
+ # an input the submit handler does not expect.
22
18
  #
23
19
  # @param aggregate [Bluebook::Aggregate] the aggregate that declares the command
24
20
  # @param command [Bluebook::Command] the command the form submits
@@ -33,25 +29,14 @@ module Hecks
33
29
  # Renders the page body for one command: header, refusal banner, the form itself, and
34
30
  # the inspect panel.
35
31
  #
36
- # `registry` serves only to populate a `:reference` field's
37
- # `<select>` with real records (including the identity picker itself,
38
- # for a non-creating command — see `identity_field` below). `values`/
39
- # `error` carry a sticky re-render after a refused submission; leave
40
- # both nil for a fresh form. `prefill` carries values a caller
41
- # arrived with (typically `?to=...` off a record's own detail page) —
42
- # kept separate from `values` because a prefill is not an error retry
43
- # and should not be treated as one by a future reader of this code.
32
+ # `registry` populates `:reference` selects. `values` and `error` re-render after a
33
+ # refusal; `prefill` carries values the caller arrived with (`?to=...`), not a retry.
44
34
  #
45
35
  # @param registry [Runtime::Registry] the booted registry, read for reference options
46
- # @param domain [String] name of the domain (chapter) the aggregate belongs to
47
- # @param aggregate [Bluebook::Aggregate] the aggregate that declares the command
48
- # @param command [Bluebook::Command] the command the form submits
49
36
  # @param action [String] URL path the form posts to
50
- # @param values [Hash{String => Object}, nil] the raw submission to re-show after a
51
- # refusal, keyed by dotted field path; nil falls back to `prefill`
37
+ # @param values [Hash{String => Object}, nil] raw submission to re-show; nil uses `prefill`
52
38
  # @param error [Exception, nil] the refusal to show in the banner; nil for a fresh form
53
- # @param prefill [Hash{String => String}] values the caller arrived with, keyed by
54
- # dotted field path; ignored when `values` is given
39
+ # @param prefill [Hash{String => String}] values the caller arrived with, by dotted path
55
40
  # @return [String] the HTML page body, without the surrounding page chrome
56
41
  def self.render(registry:, domain:, aggregate:, command:, action:, values: nil, error: nil, prefill: {})
57
42
  all_fields = fields_for(aggregate, command)
@@ -130,12 +115,8 @@ module Hecks
130
115
  # Maps a refusal to per-field messages; always empty, so every refusal is shown in
131
116
  # the banner rather than beside a field.
132
117
  #
133
- # No structured field attribution exists on a domain refusal today
134
- # (it is a typed exception with a rendered message — see
135
- # docs/command-form-and-query-form-bluebook.md's note on
136
- # `RefusalWording`), so this returns empty rather than guessing which
137
- # field a message meant; the banner above carries the real text
138
- # instead of a misattributed hint.
118
+ # A domain refusal carries no field attribution, so the banner shows the message
119
+ # instead of guessing a field.
139
120
  #
140
121
  # @param _error [Exception, nil] the refusal raised by the submission; ignored
141
122
  # @return [Hash] always empty; keys would be dotted field paths
@@ -4,31 +4,13 @@ require_relative "../naming"
4
4
 
5
5
  module Hecks
6
6
  module Forms
7
- # A Field (field_shape.rb) -> the `<div class="field">...</div>` or
8
- # `<fieldset>...</fieldset>` markup for it. One renderer, called
9
- # recursively for a group's children, shared by CommandFormRenderer
10
- # (POST) and QueryFormRenderer (GET) — the same attribute shape asks
11
- # for the same input either way; only the surrounding `<form>`'s
12
- # method differs.
7
+ # Renders a Field (field_shape.rb) as form markup, recursing into a group's children.
8
+ # Shared by CommandFormRenderer (POST) and QueryFormRenderer (GET).
13
9
  module FieldRenderer
14
- # Renders one field, and recursively a group's children, as form markup with any held
15
- # value filled back in.
10
+ # Renders one field, filling any held value back in.
16
11
  #
17
- # `values` is the hash a sticky re-render (a rejected command,
18
- # a submitted query) carries back — read with the same dotted path a
19
- # Field already carries, via `dig`. `reference_options` maps a
20
- # `:reference` field's own path to `[[id, label], ...]`, built by the
21
- # caller (it needs a repository; this module stays pure markup).
22
- #
23
- # @param field [Forms::Field] the field to render
24
- # @param values [Hash{String, Symbol => Object}] held values, either flat and keyed by
25
- # dotted path or nested by path segment; see `dig`
26
- # @param errors [Hash{String => String}, nil] message per dotted field path; nil for none
27
- # @param reference_options [Hash{String => Array<Array>, nil}] `[id, label]` pairs per
28
- # `:reference` field path, as `ReferenceOptions.collect` builds them; a nil or empty
29
- # entry renders a plain text input
30
- # @return [String] HTML for the field: a `<fieldset>` for `:group` and `:money`, a
31
- # `<div class="field">` otherwise
12
+ # `reference_options` is built by the caller, since it needs a repository and
13
+ # this module stays pure markup.
32
14
  def self.render(field, values: {}, errors: nil, reference_options: {})
33
15
  case field.kind
34
16
  when :group then group(field, values, errors, reference_options, tag: "fieldset")
@@ -38,17 +20,6 @@ module Hecks
38
20
  end
39
21
  end
40
22
 
41
- # Renders a field's children inside one enclosing element, with the field's label as
42
- # its `<legend>`.
43
- #
44
- # @param field [Forms::Field] a `:group` or `:money` field carrying `children`
45
- # @param values [Hash{String, Symbol => Object}] held values; see `dig`
46
- # @param errors [Hash{String => String}, nil] message per dotted field path
47
- # @param reference_options [Hash{String => Array<Array>, nil}] `[id, label]` pairs per
48
- # `:reference` field path
49
- # @param tag [String] name of the enclosing element, such as `"fieldset"`
50
- # @param css [String, nil] class attribute for the enclosing element; nil for none
51
- # @return [String] HTML for the enclosing element and every child
52
23
  def self.group(field, values, errors, reference_options, tag:, css: nil)
53
24
  inner = field.children.map { |child| render(child, values: values, errors: errors, reference_options: reference_options) }
54
25
  <<~HTML
@@ -59,14 +30,7 @@ module Hecks
59
30
  HTML
60
31
  end
61
32
 
62
- # Renders a `:list` field as one textarea taking an element per line, which is the shape
63
- # `Params.extract_list` reads back.
64
- #
65
- # @param field [Forms::Field] a `:list` field whose first child describes one element
66
- # @param values [Hash{String, Symbol => Object}] held values; the list's entry may be an
67
- # Array, a newline-joined String, or absent
68
- # @param _errors [Hash{String => String}, nil] ignored; a list shows no per-field message
69
- # @return [String] HTML for the labelled textarea and its help line
33
+ # One textarea, an element per line: the shape `Params.extract_list` reads back.
70
34
  def self.list(field, values, _errors)
71
35
  item = field.children.first
72
36
  current = Array(dig(values, field.path)).join("\n")
@@ -80,15 +44,6 @@ module Hecks
80
44
  HTML
81
45
  end
82
46
 
83
- # Renders a single-value field with the control its `kind` calls for, wrapped with its
84
- # label, help text and error.
85
- #
86
- # @param field [Forms::Field] a leaf field
87
- # @param values [Hash{String, Symbol => Object}] held values; see `dig`
88
- # @param errors [Hash{String => String}, nil] message per dotted field path
89
- # @param reference_options [Hash{String => Array<Array>, nil}] `[id, label]` pairs per
90
- # `:reference` field path
91
- # @return [String] HTML for the `<div class="field">` block
92
47
  def self.leaf(field, values, errors, reference_options)
93
48
  value = dig(values, field.path)
94
49
  error = errors && errors[field.path]
@@ -103,14 +58,7 @@ module Hecks
103
58
  wrap(field, body, error)
104
59
  end
105
60
 
106
- # Wraps a rendered control with its label, help text and error message; a `:boolean`
107
- # gets no label here because `checkbox` renders its own beside the box.
108
- #
109
- # @param field [Forms::Field] the field the control belongs to
110
- # @param body [String] HTML of the control itself
111
- # @param error [String, nil] message shown as an alert; nil for none
112
- # @return [String] HTML for the `<div class="field">` block, classed `has-error` when
113
- # `error` is given
61
+ # A :boolean gets no label here because `checkbox` renders its own beside the box.
114
62
  def self.wrap(field, body, error)
115
63
  <<~HTML
116
64
  <div class="field#{' has-error' if error}">
@@ -122,13 +70,6 @@ module Hecks
122
70
  HTML
123
71
  end
124
72
 
125
- # Renders an `<input>` of the field's `html_type`, falling back to the field's default
126
- # for a missing value; a whole-cents number also gets a live preview span.
127
- #
128
- # @param field [Forms::Field] a leaf field
129
- # @param value [Object, nil] the held value, rendered with `to_s`; nil shows the default
130
- # @return [String] HTML for the input, followed by the preview `<span>` when the field
131
- # is a `:number` whose help text mentions cents
132
73
  def self.input(field, value)
133
74
  money = field.kind == :number && field.help.to_s.include?("cents")
134
75
  tag = Tag.void("input", id: dom_id(field.path), name: field.path, type: field.html_type,
@@ -140,23 +81,11 @@ module Hecks
140
81
  %(#{tag} <span id="#{dom_id(field.path)}-preview" class="mono help" aria-live="polite"></span>)
141
82
  end
142
83
 
143
- # Renders a `<textarea>` holding the value as escaped text; unlike `input`, the field's
144
- # default is not filled in.
145
- #
146
- # @param field [Forms::Field] a `:textarea` field
147
- # @param value [Object, nil] the held value, rendered with `to_s`; nil renders empty
148
- # @return [String] HTML for the textarea
149
84
  def self.textarea(field, value)
150
85
  %(<textarea id="#{dom_id(field.path)}" name="#{Escape.attr(field.path)}" #{aria(field)}>#{Escape.html(value)}</textarea>)
151
86
  end
152
87
 
153
- # Renders a checkbox preceded by a hidden `"0"` input of the same name, so an unticked
154
- # box still submits a value, with the field's label beside it.
155
- #
156
- # @param field [Forms::Field] a `:boolean` field
157
- # @param value [Boolean, String, nil] the held value; `true`, `"true"`, `"on"` and `"1"`
158
- # tick the box, and nil falls back to a default of exactly `true`
159
- # @return [String] HTML for the checkbox row
88
+ # The hidden "0" input makes an unticked box still submit a value.
160
89
  def self.checkbox(field, value)
161
90
  checked = value.nil? ? field.default == true : [true, "true", "on", "1"].include?(value)
162
91
  <<~HTML
@@ -168,13 +97,6 @@ module Hecks
168
97
  HTML
169
98
  end
170
99
 
171
- # Renders one radio button per option, pre-checking the one equal to the held value or,
172
- # failing that, to the field's default.
173
- #
174
- # @param field [Forms::Field] a `:radio` field whose `options` are `[value, label]` pairs
175
- # @param value [Object, nil] the held value, compared with `to_s`; nil falls back to the
176
- # default
177
- # @return [String] HTML for the `<div role="radiogroup">` block
178
100
  def self.radio_group(field, value)
179
101
  selected = value || field.default
180
102
  options = field.options.map do |option_value, option_label|
@@ -187,13 +109,6 @@ module Hecks
187
109
  %(<div class="radio-group" role="radiogroup">#{options.join}</div>)
188
110
  end
189
111
 
190
- # Renders a `<select>` over the field's fixed options, with a blank "—" entry first when
191
- # the field is optional.
192
- #
193
- # @param field [Forms::Field] a `:select` field whose `options` are `[value, label]` pairs
194
- # @param value [Object, nil] the held value, compared with `to_s`; nil falls back to the
195
- # default
196
- # @return [String] HTML for the select
197
112
  def self.select(field, value)
198
113
  selected = value || field.default
199
114
  options = field.options.map do |option_value, option_label|
@@ -204,16 +119,7 @@ module Hecks
204
119
  %(<select id="#{dom_id(field.path)}" name="#{Escape.attr(field.path)}" #{aria(field)}>#{blank}#{options.join}</select>)
205
120
  end
206
121
 
207
- # Renders a `<select>` of existing records for a `:reference` field, degrading to a plain
208
- # text id input when no records are on offer.
209
- #
210
- # @param field [Forms::Field] a `:reference` field; its `html_type` is set to `"text"`
211
- # in place when the text fallback is taken
212
- # @param value [Object, nil] the held id, compared with `to_s`; nil preselects the
213
- # disabled "choose one…" entry of a required field
214
- # @param options [Array<Array>, nil] `[id, label]` pairs to offer; nil or empty takes the
215
- # text fallback
216
- # @return [String] HTML for the select, or for the fallback `<input>`
122
+ # Falls back to a plain text id input when no records are on offer.
217
123
  def self.reference_select(field, value, options)
218
124
  return input(field.tap { |f| f.html_type = "text" }, value) unless options && !options.empty?
219
125
 
@@ -228,78 +134,33 @@ module Hecks
228
134
  %(<select id="#{dom_id(field.path)}" name="#{Escape.attr(field.path)}" #{aria(field)}>#{blank}#{rendered.join}</select>)
229
135
  end
230
136
 
231
- # Renders the asterisk that marks a required field's label or legend.
232
- #
233
- # @param field [Forms::Field] the field being labelled
234
- # @return [String] HTML for the mark, or `""` when the field is optional
235
137
  def self.required_mark(field) = field.required? ? %(<span class="required-mark" title="required">*</span>) : ""
236
138
 
237
- # Derives the element id of a field's control from its dotted path, such as
238
- # `"f-amount-cents"` for `"amount.cents"`.
239
- #
240
- # @param path [String, Symbol] the field's dotted path
241
- # @return [String] the id, `"f-"` followed by the path with dots turned to hyphens
139
+ # "amount.cents" -> "f-amount-cents".
242
140
  def self.dom_id(path) = "f-#{path.to_s.tr('.', '-')}"
243
141
 
244
- # Picks the `pattern` attribute an input carries, withholding it from a `:reference`
245
- # field, whose input takes a record id rather than the attribute's own value.
246
- #
247
- # @param field [Forms::Field] a leaf field
248
- # @return [String, nil] the regex source the attribute declares as `pattern:`; nil for a
249
- # `:reference` field or when none is declared
142
+ # A :reference input takes a record id, so the attribute's own pattern must not apply.
250
143
  def self.leaf_pattern(field) = field.kind == :reference ? nil : field.pattern
251
144
 
252
- # Names the preview span a whole-cents input's `data-money-cents` attribute points at.
253
- #
254
- # @param field [Forms::Field] the cents field
255
- # @return [String] a CSS id selector, such as `"#f-amount-cents-preview"`
256
145
  def self.money_preview_target(field) = "##{dom_id(field.path)}-preview"
257
146
 
258
- # Renders the `required` and `aria-describedby` attributes of a control as an
259
- # attribute string, for the controls assembled by hand rather than through `Tag.void`.
260
- #
261
- # @param field [Forms::Field] the field the control belongs to
262
- # @return [String] the rendered attributes, or `""` when the field is optional and has
263
- # no help text
147
+ # String form, for controls assembled by hand rather than through `Tag.void`.
264
148
  def self.aria(field)
265
149
  Tag.attrs(required: field.required?, aria_describedby: field.help ? "#{dom_id(field.path)}-help" : nil)
266
150
  end
267
151
 
268
- # Builds the `required` and `aria-describedby` attributes of a control as a Hash, to
269
- # splat into `Tag.void`.
270
- #
271
- # @param field [Forms::Field] the field the control belongs to
272
- # @return [Hash{Symbol => Boolean, String, nil}] `:required` and `:aria_describedby`;
273
- # the latter is the help span's element id, or nil when the field has no help text
152
+ # Hash form of `aria`, to splat into `Tag.void`.
274
153
  def self.aria_attrs(field)
275
154
  { required: field.required?, aria_describedby: field.help ? "#{dom_id(field.path)}-help" : nil }
276
155
  end
277
156
 
278
- # Reads the value held for a dotted field path out of either spelling a form's values
279
- # arrive in, flat or nested, without losing a stored `false`.
280
- #
281
- # `values` is a nested hash; `path` a dotted string using the same
282
- # segment spelling the hash keys are built from (`Params.nest` in
283
- # params.rb) — symbols one level down from a group, strings at the
284
- # flat top when a sticky POST re-render hands raw params back untouched.
285
- #
286
- # @param values [Hash{String, Symbol => Object}, Object] held values; anything that is
287
- # not a Hash, nil included, reads as nil
288
- # @param path [String, Symbol] the field's dotted path, such as `"amount.cents"`
289
- # @return [Object, nil] the held value exactly as stored (a raw form String, or a typed
290
- # value from a record's state); nil when no key on the path answers
157
+ # Reads the value held for a dotted path from either a flat or a nested values hash.
291
158
  def self.dig(values, path)
292
159
  return nil unless values.is_a?(Hash)
293
160
 
294
- # A sticky re-render after a refused submission hands back the raw
295
- # flat params (`{"amount.cents"=>"1050"}` — the same shape the form
296
- # posted, string values and all, dotted key intact) ; a prefill from
297
- # an existing record's own state hands back a nested hash instead
298
- # (`{amount: {cents: 1050}}`). Flat wins when both would answer,
299
- # since only the raw form is ever what the caller actually typed.
300
- # `key?` decides which spelling answers, at every step below —
301
- # never `||`, which would treat a genuinely-held `false` the
302
- # same as an absent key and fall through to `nil`.
161
+ # A sticky re-render hands back raw flat params ({"amount.cents"=>"1050"}); a prefill
162
+ # from a record's state is nested ({amount: {cents: 1050}}). Flat wins when both answer.
163
+ # Test with `key?`, never `||`, so a held `false` is not mistaken for an absent key.
303
164
  str = path.to_s
304
165
  return values[str] if values.key?(str)
305
166