hecks 2.7.0 → 2.9.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 (463) hide show
  1. checksums.yaml +4 -4
  2. data/exe/hecks +11 -0
  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 +18 -99
  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 +4 -30
  23. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +34 -108
  24. data/lib/hecks/adapters/driven/postgres.rb +145 -134
  25. data/lib/hecks/adapters/driven/postgres_era.adapter +4 -15
  26. data/lib/hecks/adapters/driven/prism.rb +7 -30
  27. data/lib/hecks/adapters/driven/sql_query_builder.rb +21 -121
  28. data/lib/hecks/adapters/driven/sqlite/codec.rb +8 -40
  29. data/lib/hecks/adapters/driven/sqlite/projection.rb +26 -82
  30. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +40 -84
  31. data/lib/hecks/adapters/driven/sqlite.rb +128 -65
  32. data/lib/hecks/adapters/driven/tenant_provisioner.rb +15 -37
  33. data/lib/hecks/adapters/driven.rb +5 -36
  34. data/lib/hecks/adapters/driving/github_webhook.rb +8 -76
  35. data/lib/hecks/behaviors/dsl.rb +7 -25
  36. data/lib/hecks/behaviors/expectations.rb +32 -142
  37. data/lib/hecks/behaviors/ir.rb +7 -20
  38. data/lib/hecks/behaviors/rspec.rb +6 -17
  39. data/lib/hecks/behaviors/runner.rb +10 -34
  40. data/lib/hecks/behaviors.rb +4 -8
  41. data/lib/hecks/bluebook/aggregate.rb +9 -75
  42. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +9 -38
  43. data/lib/hecks/bluebook/assembly/build.rb +4 -15
  44. data/lib/hecks/bluebook/assembly/contract.rb +15 -136
  45. data/lib/hecks/bluebook/assembly/contracts.rb +54 -174
  46. data/lib/hecks/bluebook/assembly/marks.rb +31 -223
  47. data/lib/hecks/bluebook/assembly/specializer.rb +6 -52
  48. data/lib/hecks/bluebook/assembly.rb +6 -45
  49. data/lib/hecks/bluebook/attribute.rb +12 -61
  50. data/lib/hecks/bluebook/behaviour/aggregate.rb +10 -39
  51. data/lib/hecks/bluebook/behaviour/attribute.rb +3 -14
  52. data/lib/hecks/bluebook/behaviour/chapter.rb +17 -42
  53. data/lib/hecks/bluebook/behaviour/command.rb +14 -66
  54. data/lib/hecks/bluebook/behaviour/domain_port.rb +4 -20
  55. data/lib/hecks/bluebook/behaviour/entity.rb +7 -27
  56. data/lib/hecks/bluebook/behaviour/hexagon.rb +23 -41
  57. data/lib/hecks/bluebook/behaviour/lifecycle.rb +15 -40
  58. data/lib/hecks/bluebook/behaviour/policy.rb +23 -52
  59. data/lib/hecks/bluebook/behaviour/process_manager.rb +30 -57
  60. data/lib/hecks/bluebook/behaviour/read_model.rb +38 -26
  61. data/lib/hecks/bluebook/behaviour/traits.rb +27 -64
  62. data/lib/hecks/bluebook/behaviour/value_object.rb +5 -24
  63. data/lib/hecks/bluebook/capabilities.rb +17 -49
  64. data/lib/hecks/bluebook/chapter.rb +10 -48
  65. data/lib/hecks/bluebook/command.rb +23 -81
  66. data/lib/hecks/bluebook/domain_port.rb +14 -63
  67. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +50 -229
  68. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +50 -377
  69. data/lib/hecks/bluebook/dsl/attribute_collector.rb +33 -204
  70. data/lib/hecks/bluebook/dsl/binding_proxy.rb +17 -65
  71. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +86 -708
  72. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +56 -170
  73. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +1 -0
  74. data/lib/hecks/bluebook/dsl/command_builder.rb +74 -546
  75. data/lib/hecks/bluebook/dsl/const_shim.rb +8 -41
  76. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +12 -87
  77. data/lib/hecks/bluebook/dsl/entity_builder.rb +44 -330
  78. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +20 -269
  79. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +26 -189
  80. data/lib/hecks/bluebook/dsl/identity_declaration.rb +13 -88
  81. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +6 -20
  82. data/lib/hecks/bluebook/dsl/policy_builder.rb +16 -88
  83. data/lib/hecks/bluebook/dsl/port_builder.rb +2 -6
  84. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +22 -96
  85. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +31 -184
  86. data/lib/hecks/bluebook/dsl/query_builder.rb +11 -42
  87. data/lib/hecks/bluebook/dsl/read_model_builder.rb +75 -201
  88. data/lib/hecks/bluebook/dsl/rule_reference.rb +22 -163
  89. data/lib/hecks/bluebook/dsl/translation_builder.rb +28 -114
  90. data/lib/hecks/bluebook/dsl/value_object_builder.rb +10 -124
  91. data/lib/hecks/bluebook/dsl/word_gate.rb +16 -162
  92. data/lib/hecks/bluebook/dsl/world_builder.rb +19 -106
  93. data/lib/hecks/bluebook/entity.rb +13 -71
  94. data/lib/hecks/bluebook/expression/ast_json.rb +24 -190
  95. data/lib/hecks/bluebook/expression/ast_reader.rb +12 -54
  96. data/lib/hecks/bluebook/expression/canonical_form.rb +11 -88
  97. data/lib/hecks/bluebook/expression/evaluator.rb +47 -295
  98. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +30 -147
  99. data/lib/hecks/bluebook/expression/resolver.rb +37 -660
  100. data/lib/hecks/bluebook/hexagon.rb +5 -28
  101. data/lib/hecks/bluebook/lifecycle.rb +5 -16
  102. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -7
  103. data/lib/hecks/bluebook/meta_validator/judge.rb +111 -361
  104. data/lib/hecks/bluebook/meta_validator/plan.rb +33 -162
  105. data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -7
  106. data/lib/hecks/bluebook/meta_validator/readings.rb +56 -342
  107. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +24 -176
  108. data/lib/hecks/bluebook/meta_validator/shapes.rb +54 -268
  109. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +47 -308
  110. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +7 -43
  111. data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -18
  112. data/lib/hecks/bluebook/meta_validator.rb +79 -349
  113. data/lib/hecks/bluebook/model_check/client_profile.rb +10 -91
  114. data/lib/hecks/bluebook/model_check.rb +102 -600
  115. data/lib/hecks/bluebook/pattern_subset.rb +11 -69
  116. data/lib/hecks/bluebook/process_manager.rb +13 -83
  117. data/lib/hecks/bluebook/project_loader.rb +3 -39
  118. data/lib/hecks/bluebook/project_register.rb +12 -60
  119. data/lib/hecks/bluebook/query.rb +9 -48
  120. data/lib/hecks/bluebook/read_model.rb +57 -81
  121. data/lib/hecks/bluebook/reference.rb +7 -41
  122. data/lib/hecks/bluebook/smoke_test.rb +8 -98
  123. data/lib/hecks/bluebook/synthesizer.rb +13 -62
  124. data/lib/hecks/bluebook/translation.rb +26 -62
  125. data/lib/hecks/bluebook/value_object.rb +11 -36
  126. data/lib/hecks/bluebook.rb +5 -22
  127. data/lib/hecks/cache_dir.rb +84 -0
  128. data/lib/hecks/cli/document.rb +86 -0
  129. data/lib/hecks/cli/ir.rb +45 -0
  130. data/lib/hecks/cli/mcp.rb +31 -0
  131. data/lib/hecks/cli/mcp_door.rb +366 -0
  132. data/lib/hecks/cli/model_check.rb +203 -0
  133. data/lib/hecks/cli/project_cli.rb +90 -0
  134. data/lib/hecks/cli/project_diagrams.rb +36 -0
  135. data/lib/hecks/cli/run.rb +173 -0
  136. data/lib/hecks/cli/smoke_test.rb +45 -0
  137. data/lib/hecks/cli/stores.rb +53 -0
  138. data/lib/hecks/cli.rb +160 -0
  139. data/lib/hecks/construct.rb +8 -34
  140. data/lib/hecks/deploy/bluebook/deploy.bluebook +33 -142
  141. data/lib/hecks/deploy/bluebook/deploy.hecksagon +2 -9
  142. data/lib/hecks/embryonaut_bluebook/lock.rb +3 -35
  143. data/lib/hecks/embryonaut_bluebook/shape.rb +1 -17
  144. data/lib/hecks/embryonaut_bluebook/vendor.rb +8 -53
  145. data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +4 -19
  146. data/lib/hecks/embryonaut_bluebook.rb +4 -71
  147. data/lib/hecks/facade/cli_door.rb +24 -106
  148. data/lib/hecks/facade/cli_runner.rb +28 -172
  149. data/lib/hecks/facade/command_request.rb +13 -33
  150. data/lib/hecks/facade/handle.rb +34 -121
  151. data/lib/hecks/facade/json_door.rb +31 -146
  152. data/lib/hecks/facade/surface/aggregate_door.rb +39 -104
  153. data/lib/hecks/facade/surface/chapter.rb +10 -54
  154. data/lib/hecks/facade/surface.rb +8 -23
  155. data/lib/hecks/facade.rb +10 -25
  156. data/lib/hecks/forms/app.rb +15 -70
  157. data/lib/hecks/forms/command_form_renderer.rb +10 -29
  158. data/lib/hecks/forms/field_renderer.rb +17 -156
  159. data/lib/hecks/forms/field_shape.rb +30 -222
  160. data/lib/hecks/forms/html.rb +12 -47
  161. data/lib/hecks/forms/index_renderer.rb +1 -4
  162. data/lib/hecks/forms/page.rb +3 -9
  163. data/lib/hecks/forms/params.rb +14 -60
  164. data/lib/hecks/forms/port_argument.rb +2 -19
  165. data/lib/hecks/forms/query_form_renderer.rb +4 -79
  166. data/lib/hecks/forms/record_renderer.rb +8 -75
  167. data/lib/hecks/forms/record_table.rb +7 -50
  168. data/lib/hecks/forms/reference_options.rb +3 -30
  169. data/lib/hecks/forms/value_object_shape.rb +8 -35
  170. data/lib/hecks/forms.rb +5 -33
  171. data/lib/hecks/fqn.rb +5 -12
  172. data/lib/hecks/framework/bluebook/compliance.bluebook +16 -65
  173. data/lib/hecks/framework/bluebook/console_settings.bluebook +47 -134
  174. data/lib/hecks/framework/bluebook/framework.hecksagon +5 -14
  175. data/lib/hecks/framework/bluebook/governance.bluebook +13 -34
  176. data/lib/hecks/framework/bluebook/identity.bluebook +11 -26
  177. data/lib/hecks/framework/bluebook/privacy.bluebook +8 -33
  178. data/lib/hecks/framework.rb +11 -80
  179. data/lib/hecks/freezer.rb +5 -39
  180. data/lib/hecks/grammar/expression.bluebook +15 -40
  181. data/lib/hecks/grammar/translation.bluebook +3 -6
  182. data/lib/hecks/grammar.rb +16 -41
  183. data/lib/hecks/ir.rb +14 -83
  184. data/lib/hecks/language/adapter.bluebook +2 -7
  185. data/lib/hecks/language/bluebook/aggregate.bluebook +56 -221
  186. data/lib/hecks/language/bluebook/attaches/paging.bluebook +6 -15
  187. data/lib/hecks/language/bluebook/bluebook.bluebook +15 -83
  188. data/lib/hecks/language/bluebook/bluebook.hecksagon +3 -15
  189. data/lib/hecks/language/bluebook/command.bluebook +27 -144
  190. data/lib/hecks/language/bluebook/entity.bluebook +24 -97
  191. data/lib/hecks/language/bluebook/policy.bluebook +16 -67
  192. data/lib/hecks/language/bluebook/process_manager.bluebook +28 -151
  193. data/lib/hecks/language/bluebook/projection.bluebook +126 -60
  194. data/lib/hecks/language/bluebook/query.bluebook +12 -43
  195. data/lib/hecks/language/bluebook/shape.bluebook +23 -73
  196. data/lib/hecks/language/bluebook/syntax.bluebook +51 -262
  197. data/lib/hecks/language/bluebook/vocabulary.bluebook +98 -197
  198. data/lib/hecks/language/hecksagon/adapter_binding.bluebook +3 -10
  199. data/lib/hecks/language/hecksagon/domain_port.bluebook +4 -19
  200. data/lib/hecks/language/hecksagon/hecksagon.bluebook +6 -25
  201. data/lib/hecks/language/hecksagon/port_operation.bluebook +3 -14
  202. data/lib/hecks/language/oidc.json +35 -0
  203. data/lib/hecks/language/port.bluebook +4 -17
  204. data/lib/hecks/language/translation/translation.bluebook +3 -9
  205. data/lib/hecks/language/translation/translation_aggregate.bluebook +5 -24
  206. data/lib/hecks/language/world/wiring.bluebook +1 -3
  207. data/lib/hecks/language/world/world.bluebook +5 -7
  208. data/lib/hecks/literal.rb +8 -50
  209. data/lib/hecks/mcp_door_scope.rb +95 -0
  210. data/lib/hecks/mcp_stdio_guard.rb +4 -41
  211. data/lib/hecks/naming.rb +29 -137
  212. data/lib/hecks/ports/access_control.rb +7 -23
  213. data/lib/hecks/ports/agent/answers.rb +5 -23
  214. data/lib/hecks/ports/agent.rb +47 -172
  215. data/lib/hecks/ports/authentication.rb +12 -39
  216. data/lib/hecks/ports/authorization.rb +16 -43
  217. data/lib/hecks/ports/clock.rb +5 -45
  218. data/lib/hecks/ports/extraction.rb +7 -12
  219. data/lib/hecks/ports/identity_assignment.rb +8 -29
  220. data/lib/hecks/ports/identity_generation.rb +6 -29
  221. data/lib/hecks/ports/identity_resolution.rb +2 -10
  222. data/lib/hecks/ports/key_vault.rb +9 -45
  223. data/lib/hecks/ports/persistence/append_only.rb +62 -65
  224. data/lib/hecks/ports/persistence/binding_policy.rb +4 -14
  225. data/lib/hecks/ports/persistence/codec_boundary.rb +20 -80
  226. data/lib/hecks/ports/persistence/null_saga_store.rb +4 -20
  227. data/lib/hecks/ports/persistence/plugin.rb +13 -42
  228. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +38 -141
  229. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +15 -115
  230. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +16 -84
  231. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +7 -26
  232. data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +3 -22
  233. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +55 -181
  234. data/lib/hecks/ports/persistence/plugins/era/postgres_era/events.rb +77 -0
  235. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +12 -122
  236. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +28 -128
  237. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +73 -474
  238. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +31 -124
  239. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +45 -219
  240. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +13 -108
  241. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +18 -75
  242. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +9 -49
  243. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +6 -123
  244. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +23 -55
  245. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +14 -72
  246. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +7 -16
  247. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +14 -115
  248. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +13 -45
  249. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +127 -554
  250. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +13 -63
  251. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +2 -17
  252. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +3 -14
  253. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +13 -58
  254. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -17
  255. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +9 -26
  256. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +9 -25
  257. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +22 -122
  258. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +34 -123
  259. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +5 -12
  260. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +3 -9
  261. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -14
  262. data/lib/hecks/ports/persistence/plugins/era.rb +4 -23
  263. data/lib/hecks/ports/persistence/postgres_dump/connection.rb +7 -23
  264. data/lib/hecks/ports/persistence/postgres_dump.rb +4 -35
  265. data/lib/hecks/ports/persistence/remote_runtime.rb +2 -24
  266. data/lib/hecks/ports/persistence/repository_factory.rb +8 -27
  267. data/lib/hecks/ports/persistence/state_codec.rb +23 -103
  268. data/lib/hecks/ports/persistence.rb +7 -14
  269. data/lib/hecks/ports/projection.rb +42 -84
  270. data/lib/hecks/ports/query/in_memory.rb +10 -58
  271. data/lib/hecks/ports/query/ordering.rb +11 -32
  272. data/lib/hecks/ports/query.rb +14 -37
  273. data/lib/hecks/ports.rb +2 -8
  274. data/lib/hecks/projections/bootstrap_table.rb +11 -43
  275. data/lib/hecks/projections/deploy/fargate/assembly.rb +2 -15
  276. data/lib/hecks/projections/deploy/fargate/cdn.rb +1 -48
  277. data/lib/hecks/projections/deploy/fargate/cdn_default.rb +2 -11
  278. data/lib/hecks/projections/deploy/fargate/check.rb +5 -14
  279. data/lib/hecks/projections/deploy/fargate/containers.rb +3 -78
  280. data/lib/hecks/projections/deploy/fargate/extras.rb +3 -44
  281. data/lib/hecks/projections/deploy/fargate/monitoring.rb +0 -44
  282. data/lib/hecks/projections/deploy/fargate/settings.rb +2 -54
  283. data/lib/hecks/projections/deploy/fargate/yaml.rb +13 -58
  284. data/lib/hecks/projections/deploy/fargate.rb +35 -202
  285. data/lib/hecks/projections/deploy/lambda.rb +151 -679
  286. data/lib/hecks/projections/deploy/preview/access_yaml.rb +1 -33
  287. data/lib/hecks/projections/deploy/preview/containers.rb +6 -111
  288. data/lib/hecks/projections/deploy/preview/routing_yaml.rb +4 -56
  289. data/lib/hecks/projections/deploy/preview/script.rb +2 -14
  290. data/lib/hecks/projections/deploy/preview/settings.rb +3 -33
  291. data/lib/hecks/projections/deploy/preview/task_yaml.rb +2 -38
  292. data/lib/hecks/projections/deploy/preview/template.rb +2 -58
  293. data/lib/hecks/projections/deploy/preview.rb +2 -67
  294. data/lib/hecks/projections/deploy/scripts/settings.rb +3 -25
  295. data/lib/hecks/projections/deploy/scripts.rb +4 -57
  296. data/lib/hecks/projections/deploy/shared.rb +47 -146
  297. data/lib/hecks/projections/deploy/smoke.rb +4 -61
  298. data/lib/hecks/projections/deploy/template_diff/comparer.rb +3 -25
  299. data/lib/hecks/projections/deploy/template_diff/loader.rb +2 -11
  300. data/lib/hecks/projections/deploy/template_diff.rb +5 -57
  301. data/lib/hecks/projections/diagrams.rb +20 -582
  302. data/lib/hecks/projections/glossary/html.rb +14 -130
  303. data/lib/hecks/projections/glossary/markdown.rb +8 -69
  304. data/lib/hecks/projections/glossary/mermaid.rb +9 -66
  305. data/lib/hecks/projections/glossary/sensitivity.rb +5 -34
  306. data/lib/hecks/projections/glossary/sentences.rb +14 -159
  307. data/lib/hecks/projections/glossary.rb +30 -222
  308. data/lib/hecks/projections/ir.rb +3 -10
  309. data/lib/hecks/projections/model/deviations.rb +15 -42
  310. data/lib/hecks/projections/model.rb +12 -51
  311. data/lib/hecks/projections/oidc.rb +12 -56
  312. data/lib/hecks/projections/parser_table.rb +9 -28
  313. data/lib/hecks/projections/reference.rb +9 -16
  314. data/lib/hecks/projections/rust_vocabulary.rb +12 -250
  315. data/lib/hecks/projections/shape.rb +3 -29
  316. data/lib/hecks/projections/statements.rb +7 -86
  317. data/lib/hecks/projections/vocabulary.rb +6 -22
  318. data/lib/hecks/projections.rb +2 -16
  319. data/lib/hecks/projector/cli_projector.rb +60 -375
  320. data/lib/hecks/projector/docs_projector.rb +10 -201
  321. data/lib/hecks/projector/exporter.rb +46 -138
  322. data/lib/hecks/projector/ir_projector.rb +2 -9
  323. data/lib/hecks/projector/narrate_projector.rb +15 -189
  324. data/lib/hecks/projector/target.rb +13 -76
  325. data/lib/hecks/projector.rb +30 -121
  326. data/lib/hecks/query_specification/common/comparators.rb +5 -26
  327. data/lib/hecks/query_specification/common/comparison.rb +35 -143
  328. data/lib/hecks/query_specification/common/dsl.rb +8 -24
  329. data/lib/hecks/query_specification/common/null_policy.rb +20 -70
  330. data/lib/hecks/query_specification/common/options.rb +2 -6
  331. data/lib/hecks/query_specification/common/where_clause.rb +2 -10
  332. data/lib/hecks/query_specification/field_path.rb +37 -77
  333. data/lib/hecks/query_specification/hop_path.rb +12 -76
  334. data/lib/hecks/release/gem_pin.rb +2 -26
  335. data/lib/hecks/release/runner/ci_publisher.rb +93 -0
  336. data/lib/hecks/release/runner/commands.rb +66 -0
  337. data/lib/hecks/release/runner/console.rb +52 -0
  338. data/lib/hecks/release/runner/gem_publisher.rb +43 -0
  339. data/lib/hecks/release/runner/git.rb +48 -0
  340. data/lib/hecks/release/runner/npm_publisher.rb +75 -0
  341. data/lib/hecks/release/runner/preflight.rb +104 -0
  342. data/lib/hecks/release/runner/published.rb +55 -0
  343. data/lib/hecks/release/runner/tagger.rb +80 -0
  344. data/lib/hecks/release/runner.rb +215 -0
  345. data/lib/hecks/rendering.rb +4 -26
  346. data/lib/hecks/router/namespace_installer.rb +12 -36
  347. data/lib/hecks/router.rb +4 -8
  348. data/lib/hecks/runtime/aggregate_lock.rb +8 -33
  349. data/lib/hecks/runtime/boot_gates.rb +3 -18
  350. data/lib/hecks/runtime/caller.rb +19 -59
  351. data/lib/hecks/runtime/capability_graph.rb +5 -21
  352. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +16 -78
  353. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +23 -160
  354. data/lib/hecks/runtime/command_interpreter.rb +125 -307
  355. data/lib/hecks/runtime/command_rules/admissibility.rb +95 -366
  356. data/lib/hecks/runtime/command_rules/arithmetic.rb +66 -196
  357. data/lib/hecks/runtime/command_rules/authorization.rb +13 -45
  358. data/lib/hecks/runtime/command_rules/emission.rb +8 -21
  359. data/lib/hecks/runtime/command_rules/references.rb +23 -202
  360. data/lib/hecks/runtime/dependency_planning.rb +30 -102
  361. data/lib/hecks/runtime/dispatcher.rb +82 -241
  362. data/lib/hecks/runtime/entity_element.rb +40 -462
  363. data/lib/hecks/runtime/entity_interpreter.rb +65 -220
  364. data/lib/hecks/runtime/errors.rb +32 -115
  365. data/lib/hecks/runtime/event.rb +3 -25
  366. data/lib/hecks/runtime/identity.rb +18 -154
  367. data/lib/hecks/runtime/instance.rb +18 -150
  368. data/lib/hecks/runtime/interpreting.rb +11 -70
  369. data/lib/hecks/runtime/invocation.rb +28 -164
  370. data/lib/hecks/runtime/loader.rb +48 -186
  371. data/lib/hecks/runtime/outbox.rb +38 -261
  372. data/lib/hecks/runtime/policy_interpreter.rb +38 -200
  373. data/lib/hecks/runtime/port_operation_interpreter.rb +26 -87
  374. data/lib/hecks/runtime/query_interpreter.rb +45 -187
  375. data/lib/hecks/runtime/reaction_invocation.rb +36 -138
  376. data/lib/hecks/runtime/read_model_interpreter.rb +139 -208
  377. data/lib/hecks/runtime/rebuild_sweep.rb +8 -54
  378. data/lib/hecks/runtime/reference_hop.rb +8 -81
  379. data/lib/hecks/runtime/refusal_wording.rb +8 -89
  380. data/lib/hecks/runtime/registry/saga_persistence.rb +10 -102
  381. data/lib/hecks/runtime/registry/verification.rb +52 -225
  382. data/lib/hecks/runtime/registry/world_defaults.rb +4 -53
  383. data/lib/hecks/runtime/registry.rb +103 -244
  384. data/lib/hecks/runtime/remote_dispatcher.rb +37 -152
  385. data/lib/hecks/runtime/routing.rb +4 -15
  386. data/lib/hecks/runtime/saga_interpreter/correlation.rb +14 -61
  387. data/lib/hecks/runtime/saga_interpreter.rb +79 -263
  388. data/lib/hecks/runtime/saga_pending_dispatch.rb +2 -43
  389. data/lib/hecks/runtime/tenant_check.rb +6 -68
  390. data/lib/hecks/runtime/tenant_scope.rb +3 -28
  391. data/lib/hecks/runtime/value/admission.rb +16 -67
  392. data/lib/hecks/runtime/value/coercion.rb +155 -641
  393. data/lib/hecks/runtime/value/entity_list_coercion.rb +26 -219
  394. data/lib/hecks/runtime/value.rb +12 -84
  395. data/lib/hecks/runtime.rb +17 -65
  396. data/lib/hecks/storehouse.rb +146 -567
  397. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +8 -32
  398. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +6 -23
  399. data/lib/hecks/vendoring/git_environment.rb +2 -8
  400. data/lib/hecks/vendoring/git_source.rb +4 -10
  401. data/lib/hecks/vendoring.rb +7 -46
  402. data/lib/hecks/version.rb +3 -14
  403. data/lib/hecks/vocabulary.rb +5 -0
  404. data/lib/hecks.rb +32 -82
  405. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -46
  406. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +11 -80
  407. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +10 -99
  408. metadata +30 -59
  409. data/lib/hecks/bench/cli.rb +0 -74
  410. data/lib/hecks/bench/environment.rb +0 -100
  411. data/lib/hecks/bench/postgres_probe.rb +0 -41
  412. data/lib/hecks/bench/report.rb +0 -116
  413. data/lib/hecks/bench/ruby_runner.rb +0 -151
  414. data/lib/hecks/bench/run.rb +0 -55
  415. data/lib/hecks/bench/rust_runner.rb +0 -165
  416. data/lib/hecks/bench/stats.rb +0 -75
  417. data/lib/hecks/bench/suite.rb +0 -172
  418. data/lib/hecks/bench/workload.rb +0 -137
  419. data/lib/hecks/bench.rb +0 -28
  420. data/lib/hecks/codemod.rb +0 -443
  421. data/lib/hecks/corpus.rb +0 -479
  422. data/lib/hecks/doc/reference.rb +0 -585
  423. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -684
  424. data/lib/hecks/fuzzing/combination_miner.rb +0 -178
  425. data/lib/hecks/fuzzing/concurrent_dispatch.rb +0 -469
  426. data/lib/hecks/fuzzing/coverage_campaign.rb +0 -161
  427. data/lib/hecks/fuzzing/differential.rb +0 -192
  428. data/lib/hecks/fuzzing/domain_generator.rb +0 -871
  429. data/lib/hecks/fuzzing/era_boundary.rb +0 -154
  430. data/lib/hecks/fuzzing/form_census.rb +0 -285
  431. data/lib/hecks/fuzzing/generated_domain_check.rb +0 -171
  432. data/lib/hecks/fuzzing/invalid_value_generator.rb +0 -125
  433. data/lib/hecks/fuzzing/isolated_boot.rb +0 -658
  434. data/lib/hecks/fuzzing/nondeterministic.rb +0 -79
  435. data/lib/hecks/fuzzing/persistence_parity.rb +0 -253
  436. data/lib/hecks/fuzzing/properties/corrections.rb +0 -125
  437. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +0 -688
  438. data/lib/hecks/fuzzing/properties/guards.rb +0 -398
  439. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +0 -399
  440. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +0 -137
  441. data/lib/hecks/fuzzing/properties/outbox.rb +0 -180
  442. data/lib/hecks/fuzzing/properties/querying.rb +0 -260
  443. data/lib/hecks/fuzzing/properties.rb +0 -267
  444. data/lib/hecks/fuzzing/qa_settings.rb +0 -164
  445. data/lib/hecks/fuzzing/replay.rb +0 -930
  446. data/lib/hecks/fuzzing/rotation_priority.rb +0 -114
  447. data/lib/hecks/fuzzing/rust_gap_manifest.rb +0 -139
  448. data/lib/hecks/fuzzing/self_consistency.rb +0 -825
  449. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +0 -532
  450. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +0 -171
  451. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +0 -127
  452. data/lib/hecks/fuzzing/sequence_generator/picker.rb +0 -116
  453. data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +0 -138
  454. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +0 -344
  455. data/lib/hecks/fuzzing/sequence_generator.rb +0 -311
  456. data/lib/hecks/fuzzing/shrinker.rb +0 -309
  457. data/lib/hecks/fuzzing/structural_skips.rb +0 -53
  458. data/lib/hecks/fuzzing/sweep_depth.rb +0 -61
  459. data/lib/hecks/fuzzing/target_capabilities.rb +0 -255
  460. data/lib/hecks/fuzzing/value_generator.rb +0 -340
  461. data/lib/hecks/fuzzing.rb +0 -23
  462. data/lib/hecks/grammar/evolve.rb +0 -524
  463. data/lib/hecks/query_ir.rb +0 -482
@@ -6,25 +6,10 @@ module Hecks
6
6
  module Adapters
7
7
  class PostgresEra
8
8
  class Lineage
9
- # CRUD over `hecks_eras`/`hecks_era_texts`/`hecks_approvals`: reading
10
- # and tamper-verifying held era text, archiving every frozen text
11
- # version, recording a Layer-3 approval, and minting an era's name
12
- # once (never recomputed after).
9
+ # CRUD over `hecks_eras`/`hecks_era_texts`/`hecks_approvals`: era text
10
+ # integrity, archiving, Layer-3 approvals, and once-only era naming.
13
11
  module EraStore
14
- # Lists every era the domain holds, oldest first, refusing any whose text was edited.
15
- #
16
- # Every held text is verified against its raw-byte digest on the
17
- # way out — an edited storage fact refuses rather than silently
18
- # reporting "no drift". This is not the era name (which hashes
19
- # the canonical projection, minted once, never recomputed): it
20
- # is a plain integrity check over bytes, and a row that carries
21
- # no digest yet is backfilled, not refused.
22
- #
23
- # @return [Array<Hash{Symbol => Object}>] one Hash per era in ordinal order, `[]` when
24
- # the domain holds none; keys are `:ordinal` (Integer), `:hash` and `:label` (String,
25
- # nil until `mint_name!` names the era), `:held_text` (String) and `:watermark`
26
- # (Integer journal ordinal the era was cut at, nil for era 1)
27
- # @raise [Runtime::WiringError] if a held text no longer matches its stored digest
12
+ # Verifies each held text's digest before returning it; a missing digest is backfilled.
28
13
  def eras
29
14
  @db.exec_params(
30
15
  "SELECT ordinal, hash, label, held_text, watermark, held_digest, held_projection::text " \
@@ -37,15 +22,7 @@ module Hecks
37
22
  end
38
23
  end
39
24
 
40
- # Reads one era's stored row without running the integrity check that `eras` applies.
41
- #
42
- # The unverified row — what bin/reattest_era shows an operator
43
- # after the integrity check fires.
44
- #
45
- # @param ordinal [Integer] ordinal of the era to read
46
- # @return [Hash{Symbol => String, nil}, nil] `:held_text`, `:held_digest` (SHA-256 hex,
47
- # nil if never stored), `:hash` (nil until minted) and `:held_projection` (JSON text,
48
- # nil if never stored); nil when the domain holds no such era
25
+ # Skips the integrity check `eras` runs, so bin/reattest_era can show the raw row first.
49
26
  def raw_era(ordinal)
50
27
  rows = @db.exec_params(
51
28
  "SELECT held_text, held_digest, hash, held_projection::text FROM hecks_eras WHERE domain = $1 AND ordinal = $2",
@@ -57,17 +34,7 @@ module Hecks
57
34
  held_projection: rows[0]["held_projection"] }
58
35
  end
59
36
 
60
- # Accepts an era's held text as it now stands, replacing its stored digest.
61
- #
62
- # The recovery path: tamper-evidence (against accident and
63
- # drift, not adversaries) gets resolved by a human acknowledging
64
- # the text as it now stands — recorded in an append-only
65
- # attestation table, never by a bare psql `UPDATE`.
66
- #
67
- # @param ordinal [Integer] ordinal of the era to re-attest
68
- # @return [String] the new SHA-256 hex digest of the held text
69
- # @raise [Runtime::WiringError] if the domain holds no era with that ordinal
70
- # @raise [PG::Error] if Postgres refuses the attestation DDL or a write
37
+ # Resolves drift via an append-only attestation record — never patch the digest.
71
38
  def reattest!(ordinal)
72
39
  era = raw_era(ordinal)
73
40
  raise Runtime::WiringError, "#{@domain} holds no era #{ordinal} to re-attest" unless era
@@ -94,19 +61,7 @@ module Hecks
94
61
  fresh
95
62
  end
96
63
 
97
- # Checks one held text against its stored digest, storing the digest if the row has none.
98
- #
99
- # A text that passes (or that had no digest to fail against) also gets
100
- # `backfill_frozen_facts!`; a text that fails gets nothing written.
101
- #
102
- # @param ordinal [Integer] ordinal of the era the text belongs to
103
- # @param text [String] the era's held bluebook source text
104
- # @param stored_digest [String, nil] SHA-256 hex digest stored with the row; nil when
105
- # the row carries none yet
106
- # @param stored_projection_json [String, nil] the row's stored projection as JSON text;
107
- # nil when the row carries none yet
108
- # @return [void]
109
- # @raise [Runtime::WiringError] if `stored_digest` is present and does not match `text`
64
+ # A row with no stored digest yet is backfilled, not refused; only a mismatch raises.
110
65
  def verify_integrity!(ordinal, text, stored_digest, stored_projection_json)
111
66
  digest = Digest::SHA256.hexdigest(text)
112
67
  if stored_digest.nil?
@@ -125,17 +80,7 @@ module Hecks
125
80
  raise Runtime::WiringError, Runtime::EraTamper.refusal(domain: @domain, ordinal: ordinal)
126
81
  end
127
82
 
128
- # Fills in the archive copy and the stored projection an era row is missing.
129
- #
130
- # A verified-authentic text backfills what a row may lack: its
131
- # projection and its archive copy. Never run on a text that
132
- # failed verification — that would bless the edit.
133
- #
134
- # @param ordinal [Integer] ordinal of the era the text belongs to
135
- # @param text [String] the era's verified held source text
136
- # @param stored_projection_json [String, nil] the row's stored projection as JSON text;
137
- # nil makes this derive one from `text` and store it, unless `text` does not load
138
- # @return [void]
83
+ # Call only on already-verified text; this would bless an unverified edit.
139
84
  def backfill_frozen_facts!(ordinal, text, stored_projection_json)
140
85
  archive_text!(ordinal, text)
141
86
  return if stored_projection_json
@@ -150,27 +95,12 @@ module Hecks
150
95
  )
151
96
  end
152
97
 
153
- # Reports the ordinal of the newest era the domain holds.
154
- #
155
- # @return [Integer] the highest held ordinal, or 1 when no era is held yet
156
- # @raise [Runtime::WiringError] if a held text no longer matches its stored digest
157
98
  def current_era
158
99
  held = eras
159
100
  held.empty? ? 1 : held.last[:ordinal]
160
101
  end
161
102
 
162
- # Holds a domain's first source text as era 1 and makes era 1 the writable era.
163
- #
164
- # Call only while the domain holds no era — every caller checks `eras.empty?` first.
165
- # An existing era 1 row is kept (`ON CONFLICT DO NOTHING`), but `advance_era!(1)` runs
166
- # regardless, so calling this on a domain past era 1 would roll the write fence back.
167
- #
168
- # @param text [String] the bluebook source text to freeze as era 1
169
- # @param projection [Hash{String => Object}, nil] the storage-shape projection of the
170
- # bluebook, as `Runtime::StorageShape.project` returns it; nil stores none
171
- # @return [void]
172
- # @raise [PG::Error] if Postgres refuses the insert or the policy change, as it does
173
- # for a role that does not own the journal
103
+ # Call only while the domain holds no era; this always advances the write fence to era 1.
174
104
  def hold_first!(text, projection: nil)
175
105
  @db.exec_params(
176
106
  "INSERT INTO hecks_eras (domain, ordinal, held_text, held_digest, held_projection) " \
@@ -178,20 +108,11 @@ module Hecks
178
108
  [@domain, text, Digest::SHA256.hexdigest(text), projection && JSON.generate(projection)]
179
109
  )
180
110
  archive_text!(1, text)
181
- # Era 1 is the current era from its first moment — established
182
- # here, not left to whichever role happens to boot first.
111
+ # Set explicitly here, not left to whichever role happens to boot first.
183
112
  advance_era!(1)
184
113
  end
185
114
 
186
- # Copies one version of an era's text into `hecks_era_texts`, keyed by its digest.
187
- #
188
- # A version already archived is left alone (`ON CONFLICT DO NOTHING`), so every
189
- # distinct text an era ever held survives a re-attestation.
190
- #
191
- # @param ordinal [Integer] ordinal of the era the text belongs to
192
- # @param text [String] the source text to archive
193
- # @return [void]
194
- # @raise [PG::Error] if Postgres refuses the insert
115
+ # Left alone if already archived, so re-attestation never duplicates a text version.
195
116
  def archive_text!(ordinal, text)
196
117
  @db.exec_params(
197
118
  "INSERT INTO hecks_era_texts (domain, ordinal, digest, held_text) VALUES ($1, $2, $3, $4) " \
@@ -200,19 +121,7 @@ module Hecks
200
121
  )
201
122
  end
202
123
 
203
- # Records that a human approved a translation edge's audit samples.
204
- #
205
- # A Layer-3 approval, recorded in the database it was reviewed
206
- # against — bound to the edge's parsed content and the journal's
207
- # high-water ordinal at review time. The latest row for a shape
208
- # pair wins (re-approval supersedes).
209
- #
210
- # @param from [String] label of the era shape the edge leaves
211
- # @param to [String] label of the era shape the edge leads to
212
- # @param edge_digest [String] SHA-256 hex digest of the edge's parsed content, as
213
- # `Translation::Audit.edge_digest` computes it
214
- # @return [void]
215
- # @raise [PG::Error] if Postgres refuses the insert
124
+ # Always inserts a new row; `approval_for` picks the latest for a shape pair.
216
125
  def record_approval!(from:, to:, edge_digest:)
217
126
  @db.exec_params(
218
127
  "INSERT INTO hecks_approvals (domain, from_label, to_label, edge_digest, reviewed_ordinal) " \
@@ -221,13 +130,6 @@ module Hecks
221
130
  )
222
131
  end
223
132
 
224
- # Finds the most recent approval recorded for the edge between two era shapes.
225
- #
226
- # @param from [String] label of the era shape the edge leaves
227
- # @param to [String] label of the era shape the edge leads to
228
- # @return [Hash{Symbol => Object}, nil] `:edge_digest` (String, SHA-256 hex of the edge
229
- # that was reviewed) and `:reviewed_ordinal` (Integer, the journal's highest ordinal at
230
- # review time); nil when no approval is recorded for the pair
231
133
  def approval_for(from:, to:)
232
134
  rows = @db.exec_params(
233
135
  "SELECT edge_digest, reviewed_ordinal FROM hecks_approvals " \
@@ -239,16 +141,7 @@ module Hecks
239
141
  { edge_digest: rows[0]["edge_digest"], reviewed_ordinal: rows[0]["reviewed_ordinal"].to_i }
240
142
  end
241
143
 
242
- # Stores an era's hash and label, only if the era has no hash yet.
243
- #
244
- # Era identity is minted once and stored; nothing ever recomputes
245
- # a stored name to verify it.
246
- #
247
- # @param ordinal [Integer] ordinal of the era to name
248
- # @param hash [String] the era's shape hash, SHA-256 hex
249
- # @param label [String] the short label edges and refusals use, a prefix of `hash`
250
- # @return [void]
251
- # @raise [PG::Error] if Postgres refuses the update
144
+ # Sets hash/label only once; nothing ever recomputes a stored name to verify it.
252
145
  def mint_name!(ordinal, hash, label)
253
146
  @db.exec_params(
254
147
  "UPDATE hecks_eras SET hash = $3, label = $4, canon_form = $5 " \
@@ -257,9 +150,6 @@ module Hecks
257
150
  )
258
151
  end
259
152
 
260
- # Reads the journal's high-water mark across every era's partition.
261
- #
262
- # @return [Integer] the highest ordinal in the journal, or 0 when the journal is empty
263
153
  def last_ordinal
264
154
  @db.exec("SELECT COALESCE(max(ordinal), 0) AS o FROM #{quoted_journal}")[0]["o"].to_i
265
155
  end
@@ -4,105 +4,39 @@ module Hecks
4
4
  module Adapters
5
5
  class PostgresEra
6
6
  class Lineage
7
- # The per-`where`-field read cache that lets a declared query find
8
- # its ids without reducing the whole aggregate first.
7
+ # Per-`where`-field cache tables that let a declared query find ids without reducing
8
+ # the whole aggregate. Holds (id, ordinal, value); lists are not cached
9
+ # (see `eligible_for_cache?`).
9
10
  #
10
- # ## Why a cache and not an index
11
- #
12
- # **The read-side of the era workaround**. Once a domain mints a
13
- # second era, `head_view` is a `DISTINCT ON`/`UNION ALL`
14
- # reduction — and a `where` clause on anything but `id` (its own
15
- # partition key) cannot be pushed through that reduction, so every
16
- # declared query pays the cost of reducing the whole aggregate
17
- # before it can filter anything (see docs/implemented/postgres-era-adapter-
18
- # split-plan.md's own trigger section — this is a real
19
- # SQL-semantics wall, not a missing index; adding one to
20
- # `head_view` itself changes nothing, because the planner cannot
21
- # prove a predicate on a non-partition column commutes with
22
- # `DISTINCT ON` without risking a stale row winning).
23
- #
24
- # ## The table
25
- #
26
- # **The workaround**: one narrow table per declared `where`-field
27
- # (never `order_by`-only fields — sorting the reduced output is
28
- # not blocked by the reduction; only filtering is), holding
29
- # exactly (id, ordinal, value) — the current extracted value for
30
- # every live id, maintained transactionally by `append` alongside
31
- # the head snapshot it already upserts (same ordinal-guard idiom:
32
- # `WHERE ordinal < EXCLUDED.ordinal`). A query whose sole `where`
33
- # is on a cached field never runs the reduction at all: `SELECT id
34
- # FROM <field>_cache WHERE value <op> $1`, then `SELECT id, state
35
- # FROM head_view WHERE id = ANY($ids)` — safe through the
36
- # reduction because `id` is its own partition key, unlike the
37
- # original field.
38
- #
39
- # ## What it does not cover
40
- #
41
- # Scoped to non-list fields only, same reasoning Track A (plain
42
- # Postgres) and Track B (Sqlite/D1) both landed on independently
43
- # for their own automatic indexing: this codebase's `contains`
44
- # compiles to element-membership (`jsonb_array_elements`/
45
- # `json_each` + `EXISTS`), which a (id, ordinal, value) cache table
46
- # keyed on one scalar value per id has no way to represent — a
47
- # list field would need one row per (id, element), a different
48
- # shape this plan does not build. `PostgresEra#eligible_for_cache?`
49
- # is where that boundary lives.
11
+ # A `where` on a non-`id` field cannot be pushed through the `DISTINCT ON` reduction in
12
+ # `head_view`, so a query on a cached field reads `<field>_cache` first, then `head_view`
13
+ # by `id` (its partition key).
50
14
  module FieldCache
51
15
  # Names the cache table for one field of one aggregate in one era.
52
16
  #
53
- # Deterministic and collision-free regardless of field-path
54
- # length or storage-name length — Postgres identifiers cap at 63
55
- # bytes, and a value-object member path (`compliance.review.
56
- # deadline_at`) combined with a long storage name could exceed
57
- # that if built any less defensively. Not meant to be
58
- # human-readable; `hecks_backfill_progress`/catalog lookups are
59
- # always driven by this same computed name, never typed by hand.
60
- #
61
- # `@domain` is part of the hash input — this table is exposed to
62
- # the exact same collision this whole file's siblings
63
- # (`head_view`/`head_snapshot`/`matview`, `lineage.rb`) are
64
- # domain-qualified against in docs/decisions/0059: without it,
65
- # two domains bound to PostgresEra against the same database,
66
- # each with an aggregate sharing a storage_name and a cached
67
- # where-field of the same name, derive the identical
68
- # `hecks_fc_<hash>` table and silently share cached rows across
69
- # domains. Already fully hashed, so folding the domain in costs
70
- # nothing readability could lose.
17
+ # Hashed to stay under Postgres's 63-byte identifier limit; `@domain` is hashed in
18
+ # so two domains sharing a database never share a table (ADR 0059).
71
19
  #
72
20
  # @param storage_name [String] the aggregate's snake-cased storage name
73
21
  # @param era [Integer] ordinal of the era the cache belongs to
74
- # @param field [String] the cached `where` field, a dotted path for a
75
- # value-object member such as `"compliance.review.deadline_at"`
22
+ # @param field [String] the cached `where` field, dotted for a value-object member
76
23
  # @return [String] unquoted table name, `hecks_fc_` plus 20 hex characters
77
24
  def field_cache(storage_name, era, field)
78
25
  "hecks_fc_#{Digest::SHA256.hexdigest("#{@domain}\0#{storage_name}\0#{era}\0#{field}")[0, 20]}"
79
26
  end
80
27
 
81
- # Creates one field's cache table if it is missing, then backfills it
82
- # from the current head until the backfill is recorded complete.
28
+ # Creates one field's cache table if missing, then backfills it from the current head.
83
29
  #
84
- # Self-healing, same idiom as `ensure_head_snapshot!`: cheap,
85
- # unconditional, safe to call on every boot. Creation is a
86
- # short-held lock (table doesn't exist yet — nothing to block);
87
- # backfill runs outside it, chunk by chunk, each chunk its own
88
- # short lock — never one transaction spanning the scan, per
89
- # principle 1. `value_expression` is the exact SQL
90
- # `PostgresEra#query_expression(field)` already compiles for
91
- # this field, applied against a `state` column this module's own
92
- # SQL always makes available in scope (see `field_cache_source_sql`)
93
- # — one source of truth for "what does this field mean", not a
94
- # second hand-rolled copy that could silently drift from what a
95
- # live query actually filters on.
30
+ # Safe to call on every boot. Creation holds a short lock; the backfill runs outside it,
31
+ # one chunk per lock, never one transaction over the scan. `value_expression` is the
32
+ # SQL `query_expression(field)` compiles, so cache and live queries agree on the value.
96
33
  #
97
34
  # @param storage_name [String] the aggregate's snake-cased storage name
98
35
  # @param era [Integer] ordinal of the era the cache belongs to
99
36
  # @param field [String] the cached `where` field, dotted for a value-object member
100
- # @param value_expression [String] SQL expression that extracts the field's
101
- # value as text from a jsonb column named `state`
37
+ # @param value_expression [String] SQL extracting the value as text from jsonb `state`
102
38
  # @return [String] unquoted name of the cache table, as `field_cache` derives it
103
39
  # @raise [PG::Error] if Postgres refuses the DDL, a source read, or an upsert
104
- # @raise [Runtime::WiringError] if `era` is past 1, a chunk is still to be
105
- # backfilled, and a held era's text fails its digest check (see `eras`)
106
40
  def ensure_field_cache!(storage_name, era, field, value_expression)
107
41
  name = field_cache(storage_name, era, field)
108
42
  unless table_exists?(name)
@@ -124,19 +58,15 @@ module Hecks
124
58
  name
125
59
  end
126
60
 
127
- # Fills an existing cache table from the reduced head, one committed chunk
128
- # of `ResumableBackfill::CHUNK_SIZE` ids at a time, resuming from the cursor
129
- # held in `hecks_backfill_progress` and doing nothing once it is complete.
61
+ # Fills an existing cache table from the reduced head, one committed chunk at a time,
62
+ # resuming from the cursor in `hecks_backfill_progress`; a no-op once complete.
130
63
  #
131
64
  # @param name [String] unquoted cache table name, from `field_cache`
132
65
  # @param storage_name [String] the aggregate's snake-cased storage name
133
66
  # @param era [Integer] ordinal of the era whose head is read
134
- # @param value_expression [String] SQL expression that extracts the field's
135
- # value as text from a jsonb column named `state`
67
+ # @param value_expression [String] SQL extracting the value as text from jsonb `state`
136
68
  # @return [void]
137
69
  # @raise [PG::Error] if Postgres refuses a source read or an upsert
138
- # @raise [Runtime::WiringError] if `era` is past 1, a chunk is still to be
139
- # backfilled, and a held era's text fails its digest check (see `eras`)
140
70
  def backfill_field_cache!(name, storage_name, era, value_expression)
141
71
  chunked_backfill!(
142
72
  name,
@@ -153,27 +83,16 @@ module Hecks
153
83
  )
154
84
  end
155
85
 
156
- # Upserts one id's cached value from the state being written, leaving the
157
- # row alone when it already carries an equal or newer ordinal.
86
+ # Upserts one id's cached value from the state being written, unless the row already
87
+ # carries an equal or newer ordinal.
158
88
  #
159
- # **The live-write side** — called from `append`, inside the same
160
- # transaction as the journal insert and the head-snapshot
161
- # upsert, for every field this aggregate has a cache table for.
162
- # `state_json` is the entry's own new state (already the thing
163
- # `append` is about to write) — bound as a literal jsonb value
164
- # and aliased `state`, so `value_expression` (built to read a
165
- # column literally named `state`) evaluates identically here and
166
- # in every backfill/query-time use, with no separate Ruby-side
167
- # re-implementation of "how to pick a value-object's member" to
168
- # keep in sync.
89
+ # Runs in the journal insert's transaction; `state_json` binds as jsonb `state`.
169
90
  #
170
91
  # @param name [String] unquoted cache table name, from `field_cache`
171
92
  # @param id [String] the aggregate id the entry belongs to
172
- # @param ordinal [String, Integer] the journal ordinal the entry was written
173
- # at; `append` passes the decimal text Postgres returned from its INSERT
93
+ # @param ordinal [String, Integer] the journal ordinal the entry was written at
174
94
  # @param state_json [String] the entry's encoded state, as JSON text
175
- # @param value_expression [String] SQL expression that extracts the field's
176
- # value as text from a jsonb column named `state`
95
+ # @param value_expression [String] SQL extracting the value as text from jsonb `state`
177
96
  # @return [void]
178
97
  # @raise [PG::Error] if Postgres refuses the upsert
179
98
  def upsert_field_cache_row!(name, id, ordinal, state_json, value_expression)
@@ -186,38 +105,21 @@ module Hecks
186
105
  SQL
187
106
  end
188
107
 
189
- # Removes a deleted id's row from one cache table, so a cached-field query
190
- # stops offering that id. Called from `append` for a delete entry, inside the
191
- # same transaction as the journal insert and the head-snapshot tombstone.
108
+ # Removes a deleted id's row so a cached-field query stops offering it. Called from
109
+ # `append` for a delete entry, in the same transaction as the journal insert.
192
110
  #
193
111
  # @param name [String] unquoted cache table name, from `field_cache`
194
112
  # @param id [String] the aggregate id whose row is removed
195
113
  # @return [void]
196
- # @raise [PG::Error] if Postgres refuses the delete
197
114
  def delete_field_cache_row!(name, id)
198
115
  @db.exec_params("DELETE FROM #{quote(name)} WHERE id = $1", [id])
199
116
  end
200
117
 
201
118
  private
202
119
 
203
- # The reduced (id, ordinal, state) source a field cache backfills
204
- # from — not the raw journal (unlike `backfill_head_snapshot!`'s
205
- # own source): a field cache reflects the current head value,
206
- # which for era > 1 can come from either this era's own live
207
- # writes or an untouched-this-era id whose value still comes
208
- # from the translated ancestor tail. Era 1 has no ancestor tail
209
- # (the snapshot table is the head, verbatim); era N reduces the
210
- # same two sources `compile_head!`'s own final view definition
211
- # unions (head_compiler.rb) — this is deliberately the identical
212
- # shape, ordinal kept in the SELECT list instead of dropped,
213
- # because a backfilled cache row needs a real ordinal to guard
214
- # against a concurrent live write the same way every other
215
- # upsert in this adapter already does. Ordinals are safe to
216
- # compare across that union because they come from one spanning
217
- # sequence per domain (see lineage.rb's own header comment) —
218
- # an ancestor-era ordinal is always numerically smaller than any
219
- # current-era one, so the ordinary `ordinal <` guard already
220
- # means the right thing with no era-aware special case.
120
+ # The reduced (id, ordinal, state) source a cache backfills from. Unlike the head
121
+ # snapshot's source it reduces the ancestor tail too (era N > 1 mirrors `compile_head!`)
122
+ # with ordinal kept; ordinals share one sequence per domain, so `ordinal <` holds.
221
123
  def field_cache_source_sql(storage_name, era, value_expression)
222
124
  era = era.to_i
223
125
  if era == 1
@@ -231,9 +133,7 @@ module Hecks
231
133
  if view && view_exists?(view)
232
134
  "SELECT ordinal, aggregate_id AS id, state FROM #{quote(view)} WHERE operation = 'save'"
233
135
  else
234
- # No compiled ancestor matview yet (e.g. this era was just
235
- # minted and compile_head! hasn't run for this aggregate) —
236
- # the live snapshot side alone is the whole truth so far.
136
+ # No compiled ancestor matview yet (era just minted): snapshot side only.
237
137
  "SELECT NULL::bigint AS ordinal, NULL::text AS id, NULL::jsonb AS state WHERE FALSE"
238
138
  end
239
139