hecks 2.8.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 (404) hide show
  1. checksums.yaml +4 -4
  2. data/exe/hecks +4 -6
  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 +17 -98
  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 +5 -48
  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 +31 -141
  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 +14 -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 +25 -31
  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 +16 -69
  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 -194
  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 -111
  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 +53 -267
  109. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +43 -305
  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 +6 -21
  114. data/lib/hecks/bluebook/model_check.rb +100 -597
  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 +4 -23
  128. data/lib/hecks/cli/document.rb +3 -21
  129. data/lib/hecks/cli/ir.rb +3 -5
  130. data/lib/hecks/cli/mcp.rb +2 -3
  131. data/lib/hecks/cli/mcp_door.rb +19 -114
  132. data/lib/hecks/cli/model_check.rb +18 -85
  133. data/lib/hecks/cli/project_cli.rb +7 -40
  134. data/lib/hecks/cli/project_diagrams.rb +0 -7
  135. data/lib/hecks/cli/run.rb +12 -58
  136. data/lib/hecks/cli/smoke_test.rb +2 -16
  137. data/lib/hecks/cli/stores.rb +1 -2
  138. data/lib/hecks/cli.rb +18 -24
  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 +82 -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 +9 -90
  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 +61 -64
  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 +6 -25
  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 +18 -124
  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 +150 -678
  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 +2 -15
  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 +42 -134
  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 +3 -6
  336. data/lib/hecks/release/runner/commands.rb +2 -4
  337. data/lib/hecks/release/runner/gem_publisher.rb +2 -3
  338. data/lib/hecks/release/runner/npm_publisher.rb +6 -20
  339. data/lib/hecks/release/runner/preflight.rb +4 -19
  340. data/lib/hecks/release/runner/tagger.rb +2 -9
  341. data/lib/hecks/release/runner.rb +3 -32
  342. data/lib/hecks/rendering.rb +4 -26
  343. data/lib/hecks/router/namespace_installer.rb +12 -36
  344. data/lib/hecks/router.rb +4 -8
  345. data/lib/hecks/runtime/aggregate_lock.rb +8 -33
  346. data/lib/hecks/runtime/boot_gates.rb +3 -18
  347. data/lib/hecks/runtime/caller.rb +19 -59
  348. data/lib/hecks/runtime/capability_graph.rb +5 -21
  349. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +16 -78
  350. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +23 -160
  351. data/lib/hecks/runtime/command_interpreter.rb +124 -306
  352. data/lib/hecks/runtime/command_rules/admissibility.rb +95 -366
  353. data/lib/hecks/runtime/command_rules/arithmetic.rb +66 -196
  354. data/lib/hecks/runtime/command_rules/authorization.rb +13 -45
  355. data/lib/hecks/runtime/command_rules/emission.rb +8 -21
  356. data/lib/hecks/runtime/command_rules/references.rb +23 -202
  357. data/lib/hecks/runtime/dependency_planning.rb +30 -102
  358. data/lib/hecks/runtime/dispatcher.rb +82 -241
  359. data/lib/hecks/runtime/entity_element.rb +40 -462
  360. data/lib/hecks/runtime/entity_interpreter.rb +64 -219
  361. data/lib/hecks/runtime/errors.rb +32 -115
  362. data/lib/hecks/runtime/event.rb +3 -25
  363. data/lib/hecks/runtime/identity.rb +18 -154
  364. data/lib/hecks/runtime/instance.rb +18 -150
  365. data/lib/hecks/runtime/interpreting.rb +11 -70
  366. data/lib/hecks/runtime/invocation.rb +28 -164
  367. data/lib/hecks/runtime/loader.rb +48 -186
  368. data/lib/hecks/runtime/outbox.rb +38 -261
  369. data/lib/hecks/runtime/policy_interpreter.rb +38 -200
  370. data/lib/hecks/runtime/port_operation_interpreter.rb +26 -87
  371. data/lib/hecks/runtime/query_interpreter.rb +45 -187
  372. data/lib/hecks/runtime/reaction_invocation.rb +36 -138
  373. data/lib/hecks/runtime/read_model_interpreter.rb +117 -208
  374. data/lib/hecks/runtime/rebuild_sweep.rb +8 -54
  375. data/lib/hecks/runtime/reference_hop.rb +8 -81
  376. data/lib/hecks/runtime/refusal_wording.rb +8 -89
  377. data/lib/hecks/runtime/registry/saga_persistence.rb +10 -102
  378. data/lib/hecks/runtime/registry/verification.rb +52 -225
  379. data/lib/hecks/runtime/registry/world_defaults.rb +4 -53
  380. data/lib/hecks/runtime/registry.rb +99 -244
  381. data/lib/hecks/runtime/remote_dispatcher.rb +37 -152
  382. data/lib/hecks/runtime/routing.rb +4 -15
  383. data/lib/hecks/runtime/saga_interpreter/correlation.rb +14 -61
  384. data/lib/hecks/runtime/saga_interpreter.rb +79 -263
  385. data/lib/hecks/runtime/saga_pending_dispatch.rb +2 -43
  386. data/lib/hecks/runtime/tenant_check.rb +6 -68
  387. data/lib/hecks/runtime/tenant_scope.rb +3 -28
  388. data/lib/hecks/runtime/value/admission.rb +16 -67
  389. data/lib/hecks/runtime/value/coercion.rb +155 -641
  390. data/lib/hecks/runtime/value/entity_list_coercion.rb +26 -219
  391. data/lib/hecks/runtime/value.rb +12 -84
  392. data/lib/hecks/runtime.rb +17 -65
  393. data/lib/hecks/storehouse.rb +141 -568
  394. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +8 -32
  395. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +6 -23
  396. data/lib/hecks/vendoring/git_environment.rb +2 -8
  397. data/lib/hecks/vendoring/git_source.rb +4 -10
  398. data/lib/hecks/vendoring.rb +7 -46
  399. data/lib/hecks/version.rb +3 -14
  400. data/lib/hecks.rb +26 -81
  401. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -46
  402. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +11 -80
  403. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +10 -99
  404. metadata +3 -2
@@ -8,52 +8,23 @@ require_relative "expected_era"
8
8
 
9
9
  module Hecks
10
10
  module Runtime
11
- # The boot-time era gate, run for the adapters that have eras — the
12
- # lineage-capable ones. An era is a fact about stored data that some
13
- # adapter can carry across a shape change; an adapter that cannot
14
- # translate has no era to hold, and holding one for it would record
15
- # history about data nothing can act on.
16
- #
17
- # The whole domain is handed to the capable adapter's own era check
18
- # (Postgres: hold, recognize, mint, or refuse toward the scaffold),
19
- # which keeps its era facts as rows beside its data. That
20
- # co-location is load-bearing — a watermark only means something
21
- # against the journal it was cut from, and an approval recorded
22
- # anywhere but the reviewed database binds to nothing.
23
- #
24
- # The capability is asked of the adapter, never of its name: a
25
- # second adapter that grows an era story declares lineage_capable?
26
- # and era_check! and needs no change here.
27
- #
28
- # Detection and identity are separate jobs: this check never hashes
29
- # anything — era names come from the store, already minted (by the
30
- # scaffold) or absent. Refusal wordings are contract, pinned by the
31
- # corpus.
11
+ # The boot-time era gate for lineage-capable adapters (Postgres today):
12
+ # each capable adapter's own era_check! decides hold, recognize, mint, or refuse.
32
13
  module EraCheck
33
14
  module_function
34
15
 
35
16
  # Runs both halves of the era gate over every bluebook in a registry.
36
17
  #
37
- # Each bluebook's own source, not one file read once and reused
38
- # for every bluebook in the registry — true as long as a domain
39
- # directory only ever held exactly one, and silently wrong the
40
- # moment `uses_framework` made a second, differently-sourced
41
- # bluebook (Governance, Identity — `lib/hecks/framework/bluebook/`,
42
- # not the domain's own directory) share a boot with the first.
43
- # Caught the hard way: three domains booted together, one real
44
- # source text (the domain's own), and every other domain's era-1
45
- # held that text under its own name — a shadow-parse of it later
46
- # reconstructs a completely different shape, and every boot after
47
- # the first refuses toward a scaffold that was never the real
48
- # drift.
18
+ # Looks up each bluebook's own source separately: `uses_framework` can share a boot between
19
+ # the domain's own bluebook and a differently-sourced one, so one file read once and reused
20
+ # for every bluebook would attribute the wrong text.
49
21
  #
50
22
  # @param registry [Runtime::Registry] the registry being booted
51
- # @param directory [String] path of the domain's own bluebook directory, searched for
52
- # each bluebook's `.bluebook` source
23
+ # @param directory [String] the domain's bluebook directory, searched for each source
53
24
  # @return [void]
54
- # @raise [Runtime::WiringError] if a compute rule is bound to an adapter that is not
55
- # lineage-capable, a persistence binding cannot be resolved, a lineage-bound bluebook
56
- # has no findable source, or the adapter's own `era_check!` refuses the boot
25
+ # @raise [Runtime::WiringError] if a compute rule is bound to a non-lineage-capable adapter,
26
+ # a persistence binding cannot be resolved, a lineage-bound bluebook has no findable
27
+ # source, or `era_check!` refuses the boot
57
28
  def check!(registry, directory)
58
29
  check_compute_rules_for_registry!(registry)
59
30
  check_lineage!(registry, directory)
@@ -61,12 +32,8 @@ module Hecks
61
32
 
62
33
  # Refuses the boot when any bluebook's compute rule is bound away from Postgres.
63
34
  #
64
- # The domain-agnostic half, split out for ADR 0031's boot-gate
65
- # registry: a compute rule requires Postgres whatever adapter is
66
- # actually bound, so this must run for every registry, the same way
67
- # `registry.verify!` does — it is not conditional on any adapter
68
- # being lineage-capable, and must never be skipped by
69
- # `check_lineage!`'s own capability gate below.
35
+ # Runs unconditionally for every registry, unlike check_lineage!'s
36
+ # capability-gated pass below (ADR 0031).
70
37
  #
71
38
  # @param registry [Runtime::Registry] the registry whose bluebooks are all checked
72
39
  # @return [void]
@@ -78,11 +45,8 @@ module Hecks
78
45
 
79
46
  # Hands every bluebook, with its own source text, to its adapter's era check.
80
47
  #
81
- # The capability-gated half — ADR 0031's registered `:era_check`
82
- # gate. Registration is conditional on `lineage_capable_registry?`;
83
- # `check_bluebook!` below still carries its own per-bluebook
84
- # `lineage_capable?` return-early, unchanged, for a registry with a
85
- # mix of lineage-capable and plain-adapter bluebooks.
48
+ # Capability-gated (ADR 0031): check_bluebook! below still returns
49
+ # early for any bluebook whose own binding isn't lineage-capable.
86
50
  #
87
51
  # @param registry [Runtime::Registry] the registry being booted
88
52
  # @param directory [String] path of the domain's own bluebook directory
@@ -98,12 +62,9 @@ module Hecks
98
62
 
99
63
  # Decides whether a registry binds anything that carries eras.
100
64
  #
101
- # The `:era_check` gate's own registration predicate: true iff at
102
- # least one bluebook's own anchor (first) aggregate resolves to a
103
- # lineage-capable adapter — mirrors `check_bluebook!`'s existing
104
- # per-bluebook anchor check, just asked once, up front, of the
105
- # whole registry, so a registry with nothing lineage-capable bound
106
- # anywhere never registers the gate at all.
65
+ # Mirrors check_bluebook!'s own per-bluebook anchor check, asked
66
+ # once up front so a registry with nothing lineage-capable never
67
+ # registers the `:era_check` gate at all.
107
68
  #
108
69
  # @param registry [Runtime::Registry] the registry whose bluebooks are asked
109
70
  # @return [Boolean] true when some bluebook's first aggregate is bound to a
@@ -121,51 +82,15 @@ module Hecks
121
82
 
122
83
  # Reads the source text one bluebook was declared in, wherever that source lives.
123
84
  #
124
- # The domain's own directory first, matched by name — a real app's
125
- # directory may hold more than one file once `uses_framework`
126
- # exists, so ".first" alone cannot be trusted, the exact
127
- # way it silently wasn't the day this was found: three domains
128
- # booted together, one real source text read once (the domain's
129
- # own, ".first"'d), and every other domain's era-1 held that text
130
- # under its own name — a later shadow-parse of it reconstructs a
131
- # completely different shape, and every boot after the first
132
- # refuses toward a scaffold that was never the real drift.
133
- #
134
- # A single-file directory whose one file names something else
135
- # falls back to it anyway (a fixture may legitimately name its
136
- # file differently from the `Hecks.bluebook` it declares) — unless
137
- # this bluebook is a known framework member or a vendored
138
- # embryonaut bluebook (see `vendored_source_for` below), in which
139
- # case that one file is certainly some other domain's, not this
140
- # one's, and the real registry is asked instead — the only other
141
- # two places a bluebook in this registry could have come from, per
142
- # `uses_framework` and `uses_embryonaut_bluebook`.
143
- #
144
- # The bug this guards against, found live: `Framework.members` was
145
- # the only exclusion checked here, so a domain attaching a
146
- # vendored bluebook instead (`uses_embryonaut_bluebook`, which has
147
- # no equivalent registry — see embryonaut_bluebook.rb's own
148
- # header) fell straight through the single-file fallback: a
149
- # directory holding exactly one `.bluebook` file (the target
150
- # domain's own) handed that same text back for the vendored
151
- # bluebook's era check too. PostgresEra minted era 1 for the
152
- # vendored domain with the target's own source stamped as its
153
- # `held_text` — the label computed from it, too — so the very
154
- # next boot re-derived the vendored domain's real shape, found it
155
- # didn't match what got (wrongly) stored, and refused to boot
156
- # toward a scaffold for drift that never actually happened.
157
- # `registry:` is what lets this check the one thing
158
- # `Framework.members` cannot: whether some hecksagon in this
159
- # registry declared `uses_embryonaut_bluebook` for this exact
160
- # bluebook name, the same way `EmbryonautBluebook.load!` itself
161
- # already resolves the vendored package's own directory.
85
+ # Matched by declared name, not position: a domain directory using
86
+ # `uses_framework` may hold more than one file, and a framework
87
+ # member or vendored bluebook is excluded from the single-file
88
+ # fallback so its own real source is read, not a sibling's.
162
89
  #
163
90
  # @param bluebook [Bluebook::Chapter] the bluebook whose source is wanted
164
91
  # @param directory [String] path of the domain's own bluebook directory
165
- # @param registry [Runtime::Registry, nil] the registry asked whether some hecksagon
166
- # vendored this bluebook; nil skips the vendored lookup
167
- # @return [String, nil] the UTF-8 text of every matching file joined with `"\n"`; nil
168
- # when no source file could be found
92
+ # @param registry [Runtime::Registry, nil] nil skips the vendored-bluebook lookup
93
+ # @return [String, nil] UTF-8 text of every matching file joined with `"\n"`, or nil
169
94
  def source_text_for(bluebook, directory, registry: nil)
170
95
  domain_files = Dir[File.join(directory, "*.bluebook")]
171
96
  own = domain_files.select { |path| declares_bluebook?(path, bluebook.name) }
@@ -190,13 +115,8 @@ module Hecks
190
115
 
191
116
  # Picks the files to read for a bluebook no file in the domain directory declares.
192
117
  #
193
- # Only reached once nothing in the domain's own directory actually
194
- # declares this bluebook by name. Three remaining sources, in
195
- # order: a genuinely single-file domain directory whose one file
196
- # just happens to name something else (but only when this
197
- # bluebook isn't already known to come from somewhere else
198
- # entirely — the exact guard the bug below was missing half of);
199
- # a framework member; a vendored embryonaut bluebook.
118
+ # Tries, in order: the domain's own single remaining file, a framework member, then a
119
+ # vendored embryonaut bluebook.
200
120
  #
201
121
  # @param bluebook [Bluebook::Chapter] the bluebook whose source is wanted
202
122
  # @param directory [String] path of the domain's own bluebook directory
@@ -222,13 +142,8 @@ module Hecks
222
142
 
223
143
  # Recovers the vendored package name behind a bluebook, if some hecksagon vendored it.
224
144
  #
225
- # The name `uses_embryonaut_bluebook` was actually called with —
226
- # recovered from whichever hecksagon in this registry recorded it
227
- # (`HecksagonBuilder#uses_embryonaut_bluebook`'s own
228
- # `@vendored_bluebooks`), matched the same way
229
- # `EmbryonautBluebook.load!` itself decides idempotency: the
230
- # vendored name Pascal-cases to this bluebook's own declared name.
231
- # Nil for an ordinary bluebook nothing ever vendored.
145
+ # Matched by Pascal-casing: the vendored name Pascal-cases to this
146
+ # bluebook's own declared name.
232
147
  #
233
148
  # @param registry [Runtime::Registry] the registry whose hecksagons are searched
234
149
  # @param bluebook_name [String] the bluebook's declared Pascal-case name
@@ -244,17 +159,9 @@ module Hecks
244
159
 
245
160
  # Lists the `.bluebook` files a vendored embryonaut package ships.
246
161
  #
247
- # The same path `EmbryonautBluebook.load!` itself resolves from —
248
- # `<registry.root>/vendor/embryonaut_bluebooks/<name>/bluebook/`,
249
- # rebuilt here from `directory` (the domain's own bluebook
250
- # directory, always `registry.root`'s immediate child — see
251
- # `Runtime::Loader.boot`) rather than threading `registry.root`
252
- # through as a second parameter. Every `.bluebook` file the
253
- # package ships, in the same `Dir.glob` order actually loaded at
254
- # boot time — order matters here because this text is later
255
- # re-parsed whole (`EraCheck::shadow`) to reconstruct the shape a
256
- # held era claims, and that reconstruction must see the files in
257
- # the order that actually built the live shape.
162
+ # Listed in `Dir.glob` order, matching load order at boot, since a
163
+ # later reconstruction of this text depends on seeing files in the
164
+ # order that built the live shape.
258
165
  #
259
166
  # @param directory [String] path of the domain's own bluebook directory, whose parent
260
167
  # holds `vendor/embryonaut_bluebooks`
@@ -269,16 +176,12 @@ module Hecks
269
176
 
270
177
  # Hands one bluebook to its adapter's `era_check!`, when that adapter carries eras.
271
178
  #
272
- # The bluebook's first aggregate is the anchor: its binding alone decides
273
- # which adapter is asked, and a bluebook with no aggregates, or one bound
274
- # to an adapter that is not lineage-capable, is skipped without a word.
179
+ # The first aggregate is the anchor: its binding alone decides the adapter asked.
275
180
  #
276
181
  # @param registry [Runtime::Registry] the registry being booted
277
182
  # @param bluebook [Bluebook::Chapter] the bluebook to check
278
- # @param current_text [String, nil] the bluebook's source, as `source_text_for` found
279
- # it; nil means no source file was found
280
- # @param directory [String, nil] the domain's bluebook directory, named in the
281
- # missing-source refusal and passed through to the adapter
183
+ # @param current_text [String, nil] the bluebook's source; nil means none was found
184
+ # @param directory [String, nil] the domain's bluebook directory, named in the refusal
282
185
  # @return [void]
283
186
  # @raise [Runtime::WiringError] if the first aggregate's binding cannot be resolved,
284
187
  # `current_text` is nil for a lineage-bound bluebook, or the adapter's `era_check!`
@@ -305,10 +208,8 @@ module Hecks
305
208
 
306
209
  # Refuses the boot when one bluebook's compute rule is bound away from Postgres.
307
210
  #
308
- # The per-rule capability gate — not an era fact, and so it
309
- # survives on every adapter: a compute rule's SQL is its only
310
- # implementation, so an aggregate carrying one cannot boot
311
- # anywhere but Postgres, whatever any shape comparison would say.
211
+ # A compute rule's SQL is its only implementation, so it cannot
212
+ # boot on any adapter but Postgres, independent of shape checks.
312
213
  #
313
214
  # @param registry [Runtime::Registry] the registry holding the declared translations
314
215
  # and the aggregate's bindings
@@ -343,17 +244,13 @@ module Hecks
343
244
 
344
245
  # Asks a named adapter whether it carries eras.
345
246
  #
346
- # The capability idiom: an adapter class that answers
347
- # lineage_capable? with true carries eras and may act on drift
348
- # (translate, fork, merge). Postgres alone does today; the seam is
349
- # what lets a second one arrive without touching this file.
247
+ # Postgres alone answers true today; the seam lets a second
248
+ # lineage-capable adapter arrive without touching this file.
350
249
  #
351
250
  # @param registry [Runtime::Registry] the registry whose `adapters` must list the name
352
251
  # @param adapter_name [String] the adapter's name, as `adapter_for` returns it
353
- # @return [Boolean] the adapter's own `lineage_capable?` answer; false when the registry
354
- # does not list the adapter, the adapter does not respond to `lineage_capable?`, or
355
- # any `StandardError` is raised on the way (including an adapter with no Ruby
356
- # implementation)
252
+ # @return [Boolean] the adapter's own answer; false if unlisted, not lineage-capable, or
253
+ # any `StandardError` is raised
357
254
  def lineage_capable?(registry, adapter_name)
358
255
  adapter_class = registry.adapters[adapter_name] && registry.adapter_class(adapter_name)
359
256
  adapter_class.respond_to?(:lineage_capable?) && adapter_class.lineage_capable?
@@ -1,37 +1,18 @@
1
1
  module Hecks
2
2
  module Runtime
3
3
  module EraGuard
4
- # The pure half: computing an aggregate's structural signature and
5
- # diffing two eras' shapes. No file I/O — everything here answers
6
- # from the IR it is handed, which is why the Postgres mint path can
7
- # call it too.
4
+ # Structural signature and diff of aggregate shapes, computed from the IR alone
5
+ # (no file I/O), so the Postgres mint path can call it too.
8
6
  module ShapeDiff
9
- # Computes an aggregate's structural signature, for comparing two eras of it.
10
- #
11
- # @param aggregate [Bluebook::Aggregate] the aggregate to describe
12
- # @return [Array<Array>] one `[name, signature]` pair per attribute, sorted by name;
13
- # `name` is a Symbol and `signature` is whatever `attribute_signature` returns
7
+ # Structural signature of an aggregate: sorted `[name, signature]` pairs per attribute.
14
8
  def shape(aggregate)
15
9
  aggregate.attributes.map do |attribute|
16
10
  [attribute.name, attribute_signature(aggregate, attribute.type)]
17
11
  end.sort_by(&:first)
18
12
  end
19
13
 
20
- # Expands a declared type into a signature that exposes its members.
21
- #
22
- # A plain type name for a primitive; `[type_name, member_signatures]`
23
- # for a value object or entity, walked recursively — so two
24
- # attributes with the same declared type name but a different
25
- # internal shape are never mistaken for unchanged.
26
- #
27
- # @param aggregate [Bluebook::Aggregate] the aggregate whose value objects and entities
28
- # are searched for `type_name`
29
- # @param type_name [String, Bluebook::Reference] the attribute's declared type
30
- # @param seen [Array<String, Bluebook::Reference>] types already being expanded, which
31
- # stops a self-referencing type from recursing forever
32
- # @return [String, Bluebook::Reference, Array] `type_name` itself for a primitive, a
33
- # reference, or a type already in `seen`; otherwise `[type_name, members]`, where
34
- # `members` is an Array of `[member_name, signature]` pairs sorted by member name
14
+ # Expands a declared type so same-named types with different members never compare equal.
15
+ # Primitives and references come back as-is; `seen` stops self-referencing types.
35
16
  def attribute_signature(aggregate, type_name, seen = [])
36
17
  container = nested_type(aggregate, type_name)
37
18
  return type_name if container.nil? || seen.include?(type_name)
@@ -42,42 +23,13 @@ module Hecks
42
23
  [type_name, members]
43
24
  end
44
25
 
45
- # Looks a type name up among an aggregate's value objects, then its entities.
46
- #
47
- # @param aggregate [Bluebook::Aggregate] the aggregate to search
48
- # @param type_name [String, Bluebook::Reference] the declared type to find
49
- # @return [Bluebook::ValueObject, Bluebook::Entity, nil] the matching declaration; nil
50
- # when the type is a primitive, a reference, or nothing this aggregate declares
51
26
  def nested_type(aggregate, type_name)
52
27
  aggregate.value_object(type_name) || aggregate.entities.find { |entity| entity.name == type_name }
53
28
  end
54
29
 
55
- # Lists the new attributes that would leave an existing record with a required gap.
56
- #
57
- # Attributes present now that the held shape never had — the
58
- # addition side of drift, which `uncovered_attributes` below never
59
- # looks at, since it only walks the held shape (vanish/retype).
60
- # Most additions are free (ADR 0025, "Added attributes and
61
- # absence"): a default:, a list_of (frozen []), or a value object
62
- # whose fields all default fill an existing record automatically
63
- # via `Instance.hydrate_with_defaults`. Only the fourth case — a
64
- # non-optional attribute with no way to fill itself — can leave an
65
- # existing record with the field genuinely absent, and that is
66
- # what this reports: unfilled by a declared translation's own
67
- # `backfill`, or by a move/convert that lands an old field inside
68
- # this brand-new attribute (`Lineage#fills?` — the destination-
69
- # side question, never `explains?`'s source-side one, since a
70
- # rename from Crate to Bin can introduce a top-level attribute
71
- # name that never existed to have "vanished").
72
- #
73
- # @param aggregate [Bluebook::Aggregate] the aggregate as currently declared
74
- # @param held_aggregate [Bluebook::Aggregate] the same aggregate as the held era's text
75
- # declares it
76
- # @param lineage [Ports::Persistence::Lineage, nil] the edge's rules for this aggregate; nil
77
- # when the edge declares none, so nothing can fill an addition
78
- # @return [Array<Symbol>] names of new, non-optional attributes with no default, no
79
- # list cardinality, no fully-defaulted value object and no rule filling them; `[]`
80
- # when every addition is safe
30
+ # Names of new, non-optional attributes that no default, list, defaulted value object
31
+ # or translation rule (`Lineage#fills?`) can fill on an existing record.
32
+ # Destination-side check: a rename can add a name that never existed to "vanish".
81
33
  def unsafe_additions(aggregate, held_aggregate, lineage)
82
34
  held_names = held_aggregate.attributes.map(&:name)
83
35
 
@@ -88,17 +40,7 @@ module Hecks
88
40
  .map(&:name)
89
41
  end
90
42
 
91
- # Decides whether a new attribute could be genuinely absent from an existing record.
92
- #
93
- # The four-row table, as a predicate over the fourth row only — the
94
- # other three (default:, list_of, a fully-defaulted value object)
95
- # all fill an existing record for free and never reach here.
96
- #
97
- # @param aggregate [Bluebook::Aggregate] the aggregate declaring the attribute, searched
98
- # for the value object its type may name
99
- # @param attribute [Bluebook::Attribute] the newly added attribute
100
- # @return [Boolean] false when the attribute is optional, has a default, is a list, or
101
- # is a value object whose every field has a default; true otherwise
43
+ # True when a new attribute could be genuinely absent from an existing record (ADR 0025).
102
44
  def possibly_absent?(aggregate, attribute)
103
45
  return false if attribute.optional?
104
46
  return false unless attribute.default.nil?
@@ -110,24 +52,8 @@ module Hecks
110
52
  true
111
53
  end
112
54
 
113
- # Lists the held paths that vanished or changed type with no rule explaining them.
114
- #
115
- # Paths the translation needs to explain: attributes that vanished
116
- # by name, attributes that kept their name but changed type (a
117
- # `convert` is what lets that be declared at all), and — recursing
118
- # into a same-named, same-typed value object — its own members
119
- # vanishing or changing type one level down, reported as a dotted
120
- # path ("price.currency"). A pure addition, at any depth, never
121
- # needs covering; only vanish-or-retype does, matching the
122
- # top-level rule at every depth.
123
- #
124
- # @param aggregate [Bluebook::Aggregate] the aggregate as currently declared
125
- # @param held_aggregate [Bluebook::Aggregate] the same aggregate as the held era's text
126
- # declares it
127
- # @param lineage [Ports::Persistence::Lineage, nil] the edge's rules for this aggregate; nil
128
- # when the edge declares none, so every vanished or retyped path is reported
129
- # @return [Array<String>] bare attribute names and dotted member paths such as
130
- # `"price.currency"`; `[]` when the whole diff is explained
55
+ # Held attribute paths that vanished or changed type with no rule explaining them,
56
+ # recursing into value objects as dotted paths ("price.currency"). Additions never count.
131
57
  def uncovered_attributes(aggregate, held_aggregate, lineage)
132
58
  paths = held_aggregate.attributes.flat_map do |held_attribute|
133
59
  current_attribute = aggregate.attribute(held_attribute.name)
@@ -142,38 +68,12 @@ module Hecks
142
68
  end
143
69
 
144
70
  # Compares one path's held and current types, recursing into shared members.
145
- #
146
- # `held_type`/`current_type` are type names, resolved against each
147
- # side's own value_object and entity declarations — neither is ever
148
- # nested in the DSL, only in the type graph, so both are always
149
- # looked up flat off their respective aggregate. A `list_of` entity
150
- # is only reached here to detect a member vanish-or-retype; there is
151
- # no per-element translation machinery yet (`move`/`convert`/`drop`
152
- # only reach into a single nested hash, not each element of an
153
- # array) — the only way to satisfy a refusal on an entity path
154
- # today is a top-level `drop` of the whole list attribute, which
155
- # `explains?` already recognizes as covering everything nested
156
- # under it. Blunt, but loud beats silent.
157
- #
158
- # @param path [String] the bare or dotted path being compared, reported on a mismatch
159
- # @param held_type [String, Bluebook::Reference] the type the held era declares there
160
- # @param current_type [String, Bluebook::Reference] the type declared there now
161
- # @param held_aggregate [Bluebook::Aggregate] the held aggregate, searched for
162
- # `held_type`'s declaration
163
- # @param aggregate [Bluebook::Aggregate] the current aggregate, searched for
164
- # `current_type`'s declaration
165
- # @param lineage [Ports::Persistence::Lineage, nil] the edge's rules, asked whether a retype
166
- # pairs the two type names; nil means no retype is accepted
167
- # @param seen [Array<String, Bluebook::Reference>] current types already being
168
- # expanded, which stops a self-referencing type from recursing forever
169
- # @return [Array<String>] `[path]` when the types differ with no retype declared; the
170
- # dotted paths of members that vanished or changed type beneath it; `[]` when
171
- # nothing differs or either type is not a value object or entity
71
+ # A `list_of` entity is diffed only for member drift; no per-element translation exists, so
72
+ # only a top-level `drop` of the whole list satisfies it.
172
73
  def diff_type(path, held_type, current_type, held_aggregate, aggregate, lineage, seen = [])
173
74
  if held_type != current_type
174
- # A declared retype says the two type names mean the same shape
175
- # — accept the pair, but still recurse into the members so a
176
- # member drift hiding beneath the rename is caught by name.
75
+ # A declared retype accepts the pair but members are still compared, so drift
76
+ # hidden under the rename is caught.
177
77
  return [path] unless lineage&.retype?(held_type, current_type)
178
78
  end
179
79
  return [] if seen.include?(current_type)
@@ -6,23 +6,8 @@ require_relative "../../../../runtime/registry"
6
6
 
7
7
  module Hecks
8
8
  module Runtime
9
- # The shape-drift coverage primitives — ADR 0032. Not a driver: nothing
10
- # here walks a registry or reads/writes a held snapshot on its own.
11
- # `PostgresEra::LineageManager::CoverageCheck` calls `uncovered_
12
- # attributes`/`unsafe_additions`/`refuse_uncovered!`/`refuse_unsafe_
13
- # addition!`/`check_vanished_aggregates!` directly, per translation
14
- # edge, over its own DB-held shapes; `Translation::Reattest` and
15
- # `PostgresEra::LineageManager` call `shadow_parse` directly, to read
16
- # historical bluebook text under old grammar defaults. Both are real,
17
- # independent, currently-shipped consumers.
18
- #
19
- # There is deliberately no top-level `check!`/`check_bluebook!` here
20
- # walking a registry and reading/writing a held snapshot under
21
- # `data/eras/*.bluebook`: it would duplicate, on its own, the same
22
- # per-aggregate walk `CoverageCheck` already performs against
23
- # `PostgresEra`'s own DB-held shapes, and nothing in production would
24
- # call it — an unwired driver is not kept, per ADR 0032. If one is
25
- # wanted, it is built informed by `CoverageCheck`'s real orchestration.
9
+ # Shape-drift coverage primitives (ADR 0032). Not a driver: callers own
10
+ # walking a registry and reading/writing held snapshots; nothing here does either.
26
11
  module EraGuard
27
12
  extend ShapeDiff
28
13
 
@@ -30,20 +15,16 @@ module Hecks
30
15
 
31
16
  # Refuses the boot when a held aggregate is gone and no translation says where it went.
32
17
  #
33
- # An aggregate that existed in the held text and answers to no
34
- # current name — renamed silently, with nothing declaring `was:` to
35
- # explain where its data went — is exactly the disease this guards
36
- # against, and a plain per-aggregate diff would never see it: the
37
- # current aggregate simply has no held counterpart to compare to.
18
+ # A held aggregate matching no current name has no counterpart for a
19
+ # plain per-aggregate diff to compare against; only a `was:` or
20
+ # `retired` translation can explain it.
38
21
  #
39
- # @param registry [Runtime::Registry] the registry whose declared translations are
40
- # searched for a `was:` or a `retired` entry naming the held aggregate
22
+ # @param registry [Runtime::Registry] the registry searched for a `was:`/`retired` entry
41
23
  # @param bluebook [Bluebook::Chapter] the bluebook booting now
42
24
  # @param held_bluebook [Bluebook::Chapter] the bluebook parsed from the held era's text
43
25
  # @return [void]
44
- # @raise [Runtime::WiringError] if a held aggregate matches no current aggregate by
45
- # name, no current aggregate's translation declares it as `was:`, and no translation
46
- # for this domain retires it
26
+ # @raise [Runtime::WiringError] if a held aggregate matches no current aggregate, no
27
+ # `was:` declares it, and no translation retires it
47
28
  def check_vanished_aggregates!(registry, bluebook, held_bluebook)
48
29
  held_bluebook.aggregates.each do |held_aggregate|
49
30
  claimed = bluebook.aggregates.any? do |aggregate|
@@ -125,65 +106,16 @@ module Hecks
125
106
  end
126
107
  end
127
108
 
128
- # Parses held source into its own IR, in a scratch registry so a past
129
- # era's text never touches the one actually booting.
109
+ # Parses held source into its own IR, in a throwaway registry.
130
110
  #
131
- # Normal parse first, shadow only as a fallback — never shadow-parsing
132
- # unconditionally. A handful of DSL
133
- # defaults fork on `MetaValidator.shadow_parsing?` for a reason
134
- # that has nothing to do with syntax the live grammar cannot
135
- # read at all (`identified_by { }`, `belongs_to`, `has_one`,
136
- # `has_many` — genuinely removed spellings, exactly what shadow-
137
- # parsing exists to keep readable): `reference_to`'s own default
138
- # mint name (`default_reference_name`, attribute_collector.rb)
139
- # is bare under ADR 0025 and `_id`-suffixed under shadow mode, and
140
- # that fork applies even to text using nothing but current, live
141
- # syntax. Held text minted under the current grammar — every real
142
- # era in this corpus today, since nothing has ever minted a second
143
- # one — parses fine normally; under unconditional shadow mode only
144
- # the reference-naming default differs, so it silently
145
- # reconstructs a different shape (and hash) than a fresh parse of
146
- # the identical text — the same text hashing two different ways
147
- # depending on which code path read it, breaking `ensure_named!`'s
148
- # own from/to edge lookup with a spurious "no translation edge
149
- # covers it" refusal that has nothing to do with any real
150
- # translation gap.
151
- #
152
- # A normal parse can only ever succeed on text the live grammar
153
- # fully understands — there is no way for it to silently produce a
154
- # wrong-but-plausible answer for genuinely legacy text, since every
155
- # removed spelling refuses loudly (`Malformed`) rather than
156
- # degrading. So: try normal first — if the ordinary grammar reads
157
- # this text without complaint, that is the canonical, unambiguous
158
- # interpretation, the same one `label_of`/`mint_hash` on the same
159
- # source text always computes, whoever's asking. Only on a
160
- # `Malformed` refusal — the one signal that actually means "this
161
- # spelling is not in the live grammar" — fall back to the legacy
162
- # grammar. Any other exception (a
163
- # genuine syntax error, an unrelated validation refusal) propagates
164
- # unchanged; swallowing it here to retry under shadow mode would
165
- # risk masking a real defect in the held text behind a confusing
166
- # second failure instead of the original, more specific one.
167
- #
168
- # `MetaValidator.while_shadow_parsing` (ADR 0025, docs/dsl-work-
169
- # slices.md's S0a) is what makes the fallback a legacy grammar
170
- # rather than just a second copy of today's: it stops
171
- # `BluebookBuilder.build` from judging this text against the
172
- # grammar as it stands now, which is the one thing that would make
173
- # a removed spelling refuse history the day it is removed from
174
- # live source. The scratch registry is throwaway either way —
175
- # nothing here is dispatched against or exposed to the real one —
176
- # so skipping the judge/assemble round-trip changes nothing this
177
- # method reads: `shape`, `uncovered_attributes`, and friends only
178
- # ever ask the built IR for its own structure.
111
+ # Tries the live grammar first, falling back to the shadow one only
112
+ # on `Malformed` — the one signal meaning "not in the live grammar"
113
+ # (ADR 0025); any other exception propagates unchanged.
179
114
  #
180
115
  # @param source [String] the held bluebook text to evaluate
181
- # @param path [String] the file path the text is evaluated as, which the predicate
182
- # extractor reads from disk; callers pass a tempfile holding the same text
183
- # @return [Bluebook::Chapter, nil] the first bluebook the text declares; nil when it
184
- # declares none
185
- # @raise [Bluebook::DSL::Malformed] if the text parses under neither the live grammar
186
- # nor the legacy one
116
+ # @param path [String] the file path reported as the text's origin
117
+ # @return [Bluebook::Chapter, nil] the first bluebook declared; nil when it declares none
118
+ # @raise [Bluebook::DSL::Malformed] if neither grammar accepts the text
187
119
  # @raise [SyntaxError] if the text is not valid Ruby
188
120
  def shadow_parse(source, path)
189
121
  parse_bluebook(source, path, shadow: false)
@@ -196,7 +128,7 @@ module Hecks
196
128
  # @param source [String] the bluebook text to evaluate
197
129
  # @param path [String] the file path reported to `Kernel.eval` as the text's origin
198
130
  # @param shadow [Boolean] true evaluates inside `MetaValidator.while_shadow_parsing`
199
- # (the legacy grammar); false evaluates under the live grammar
131
+ # (the shadow grammar); false evaluates under the live grammar
200
132
  # @return [Bluebook::Chapter, nil] the first bluebook the text registered in the scratch
201
133
  # registry; nil when it declares none
202
134
  # @raise [Bluebook::DSL::Malformed] if the chosen grammar refuses the text
@@ -5,26 +5,11 @@ require_relative "storage_shape"
5
5
 
6
6
  module Hecks
7
7
  module Runtime
8
- # The refusal wording for a held era whose digest does not match its
9
- # frozen text — the digest mismatch alone is what detects tampering (a
10
- # plain SHA256 comparison over raw bytes, unrelated to any of this);
11
- # this only supplies the wording once that's already fired.
8
+ # Wording for the boot refusal when a held era's frozen text was
9
+ # edited after freezing (detected elsewhere via a digest mismatch).
12
10
  #
13
- # Lives here, not under `Ports::Persistence` — `project` below directly
14
- # calls `Runtime::EraGuard.shadow_parse`/`Runtime::StorageShape.project`
15
- # (DSL-execution machinery), the "an adapter/port reaches into the
16
- # runtime instead of being handed already-computed data" shape that
17
- # has caused trouble here before. A capability this dependent on the
18
- # runtime is a runtime-owned one that the Postgres adapter calls, not
19
- # a ports-level module that happens to reach sideways into it.
20
- #
21
- # Every tamper refusal reaches the same generic wording, cosmetic edit
22
- # or real shape change alike: telling the two apart by re-parsing the
23
- # edited text is a pure quality-of-message nicety, not a safety
24
- # property, and an edit that cannot be classified has to fall to the
25
- # generic wording anyway. An operator judges "did this matter"
26
- # themselves, reading the still-archived original — an anomalous
27
- # recovery moment already, not a normal boot path.
11
+ # Lives here, not under `Ports::Persistence`, because `project` below
12
+ # calls runtime DSL-execution machinery (`EraGuard`, `StorageShape`) directly.
28
13
  module EraTamper
29
14
  module_function
30
15
 
@@ -40,12 +25,8 @@ module Hecks
40
25
 
41
26
  # Parses a bluebook text and returns its storage-shape projection.
42
27
  #
43
- # The projection is JSON-normalized for structural comparison
44
- # against a stored projection; nil when the text does not load.
45
- # Always parsed through a fresh tempfile, never the held file's own
46
- # path: the predicate extractor caches source by path, and a held
47
- # path whose content has changed (the very situation this module
48
- # exists for) would hand it stale lines.
28
+ # Always parsed from a fresh tempfile, never the held file's own path,
29
+ # since the predicate extractor caches source by path and would see stale lines there.
49
30
  #
50
31
  # @param text [String] the bluebook source to parse
51
32
  # @param _source_path [String, nil] ignored; the text is always parsed from a tempfile