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
@@ -1,23 +1,5 @@
1
- # A TOOLING-INTERNAL DOMAIN, not a business one — never attached via
2
- # `uses_framework`, never seen by an end-user domain. `bin/project_deploy`
3
- # boots this directory directly (Ports::Loading.bootstrap + load_domain,
4
- # the same pipeline lib/hecks/runtime/loader.rb uses, minus a
5
- # `.world`/`.hecksagon` this domain has no need for) and dispatches one
6
- # command against it before generating anything from a domain's own
7
- # `deployed_to("AwsLambda")` settings.
8
- #
9
- # WHY THIS EXISTS: `world.bluebook`'s `Wiring`/`Setting` is a DELIBERATELY
10
- # open, untyped map — "a verb's schema is not even contained in the file
11
- # being parsed" (syntax.bluebook's own header on this). That's the right
12
- # call for `Wiring` itself (an adapter verb's shape genuinely isn't the
13
- # world language's business), but it leaves `deployed_to`'s ACTUAL shape
14
- # validated nowhere — `bin/project_deploy` used to check it by hand
15
- # (`deploy_settings[:adapter] == "AwsLambda" or abort ...`,
16
- # `fetch(:region) { abort ... }`), the exact raw-Ruby-refusal pattern
17
- # every other kind of bluebook mistake in this codebase does NOT use.
18
- # This closes that gap the same way any other rule gets enforced here:
19
- # as a real command, real invariants, a real corpus-testable refusal —
20
- # not a hand-written abort string discovered only by running the script.
1
+ # Tooling-internal domain `bin/project_deploy` boots directly; never attached via `uses_framework`.
2
+ # It validates the `deployed_to` shape that `world.bluebook`'s untyped `Setting` map leaves open.
21
3
  Hecks.bluebook "Deploy" do
22
4
  aggregate "LambdaTarget" do
23
5
  description "One domain's validated AWS Lambda deploy target — what deployed_to(\"AwsLambda\") must resolve to before bin/project_deploy generates anything from it."
@@ -41,77 +23,39 @@ Hecks.bluebook "Deploy" do
41
23
  invariant("a region is named") { !value.to_s.empty? }
42
24
  end
43
25
 
44
- # "Postgres" (a plain AWS::RDS::DBInstance, Embryonaut's own live
45
- # choice — bin/project_deploy's Ruby-side default when a domain's
46
- # own deployed_to("AwsLambda") names none, so every domain
47
- # declared before this attribute existed keeps generating the
48
- # identical template it always has) or "Aurora" (Aurora Serverless
49
- # v2, Postgres-compatible — the SAME `Adapters::Postgres`/
50
- # `tokio_postgres` code on both the Ruby and rust/host sides
51
- # either way; this only changes which CloudFormation resource
52
- # bin/project_deploy emits, never a runtime adapter).
53
- # "Shared" — no RDS/VPC of this domain's own at all; it borrows
54
- # another already-deployed domain's instance instead (the
55
- # storehouse), isolated from that domain's own data by a native
56
- # Postgres SCHEMA (this domain's own lowercase name), not a
57
- # separate database. Requires a sibling `owner "OtherDomain"`
58
- # setting inside the SAME deployed_to("AwsLambda") block, naming
59
- # which already-deployed domain owns the shared instance —
60
- # bin/project_deploy checks for that setting itself (Ruby-level,
61
- # the same as `pg_version`/`google_oauth_present` below it), not
62
- # here: which domain owns the instance is a deploy-time fact, not
63
- # a business invariant this validated aggregate's job to hold.
26
+ # "Postgres" (plain RDS, the default when `deployed_to` names none) or "Aurora"
27
+ # (Serverless v2);
28
+ # only the emitted CloudFormation resource differs, never the runtime adapter. "Shared" borrows
29
+ # another domain's instance under its own Postgres schema and needs an `owner` setting, which
30
+ # `bin/project_deploy` checks, since the owning domain is a deploy-time fact.
64
31
  value_object "Database" do
65
32
  attribute :value, String, one_of: ["Postgres", "Aurora", "Shared"]
66
33
  end
67
34
 
68
- # "None" (the default — nothing changes for a domain that names
69
- # neither this nor drops a `lambda_handler.rb`, e.g. Banking) or
70
- # "Rust" — the domain's OWN Lambda (rust/host) also serves a public
71
- # web UI in-process (rust/host/src/web.rs), reading HECKS_IR_PATH
72
- # (a plain-JSON sidecar bin/project_wasm writes beside the .wasm
73
- # artifact) and dispatching through the SAME in-process
74
- # dispatch::handle/read the internal {"verb"}/{"read"} path already
75
- # uses — no second Lambda, no VPC Interface Endpoint, no Ruby.
76
- # Deliberately NOT "Ruby" as a third member here: a Ruby WebFunction
77
- # is the pre-existing, SEPARATE `lambda_handler.rb`-file-presence
78
- # mechanism (Embryonaut's own), untouched by this attribute —
79
- # bin/project_deploy refuses generation outright if a domain
80
- # declares `web "Rust"` AND has a `lambda_handler.rb`, rather than
81
- # silently picking one.
35
+ # "None" (default) or "Rust": the domain's own Lambda (rust/host) also serves a public web UI
36
+ # in-process (rust/host/src/web.rs). "Ruby" is not a member: a Ruby WebFunction comes from a
37
+ # `lambda_handler.rb` file, and `bin/project_deploy` refuses `web "Rust"` alongside one.
82
38
  value_object "Web" do
83
39
  attribute :value, String, one_of: ["None", "Rust"]
84
40
  end
85
41
 
86
- # 128-10240 MB — AWS Lambda's own real ceiling and floor, not a
87
- # number this domain invented. A typo here used to fail deep inside
88
- # a real `sam deploy`, sometimes minutes in; now it refuses before
89
- # a single file is generated.
42
+ # 128-10240 MB, AWS Lambda's own floor and ceiling.
90
43
  value_object "Memory" do
91
44
  attribute :value, Integer
92
45
  invariant("memory is at least 128 MB") { value >= 128 }
93
46
  invariant("memory is at most 10240 MB") { value <= 10240 }
94
47
  end
95
48
 
96
- # 900s — Lambda's own real ceiling.
49
+ # Lambda's own ceiling of 900s.
97
50
  value_object "Timeout" do
98
51
  attribute :value, Integer
99
52
  invariant("a timeout is positive") { value.positive? }
100
53
  invariant("a timeout fits Lambda's own 900s ceiling") { value <= 900 }
101
54
  end
102
55
 
103
- # ALL FOUR REQUIRED — deliberately, not `optional: true` +
104
- # `default:`. Confirmed directly against CommandInterpreter's own
105
- # `normalize_args`/`coerce_declared_arguments` (interpreting.rb):
106
- # `next unless normalized.key?(attribute.name)` skips coercion
107
- # entirely for an absent key, so a command-level `default:` is
108
- # honored ONLY at the JSON/Rust-codegen boundary (`scalar_from_json_
109
- # expr`'s own domain), never by Ruby's own direct dispatch — a
110
- # `default:` here would be quietly false advertising. "What memory
111
- # defaults to when unspecified" stays exactly where it already
112
- # lived (bin/project_deploy's own `fetch(:memory, 512)`); this
113
- # command validates a FULLY-RESOLVED target, and bin/project_deploy
114
- # resolves the default before ever dispatching.
56
+ # All required, no `default:`: command-level defaults apply only at the JSON/Rust-codegen
57
+ # boundary, not Ruby's direct dispatch (interpreting.rb `normalize_args`). Defaults are
58
+ # resolved by `bin/project_deploy` before it dispatches.
115
59
  command "Declare" do
116
60
  goal "Validate one domain's AwsLambda deploy target before anything is generated from it"
117
61
 
@@ -133,11 +77,8 @@ Hecks.bluebook "Deploy" do
133
77
  end
134
78
  end
135
79
 
136
- # A sibling of `LambdaTarget`, not a shared shape with it — value objects
137
- # are not shared across aggregates in this DSL (`Tenant`'s own
138
- # `DomainName`/`Realm`/`Schema` below already make the same point), so
139
- # this aggregate carries its own copies of `DomainName`/`Region`/
140
- # `Database`/`Web` rather than reaching into `LambdaTarget`'s.
80
+ # A sibling of `LambdaTarget` with its own value objects, since value objects are not
81
+ # shared across aggregates.
141
82
  aggregate "FargateTarget" do
142
83
  description "One domain's validated AWS Fargate deploy target — what deployed_to(\"AwsFargate\") must resolve to before bin/project_deploy generates anything from it."
143
84
 
@@ -161,60 +102,40 @@ Hecks.bluebook "Deploy" do
161
102
  invariant("a region is named") { !value.to_s.empty? }
162
103
  end
163
104
 
164
- # A positive integer, nothing more — AWS's real cpu/memory
165
- # combinations (256 cpu units needs 512-2048 MB of `memory`, say)
166
- # are a pairing `ECS` itself enforces at deploy time; encoding that
167
- # whole table here would duplicate AWS's own rules for no benefit
168
- # this generator needs. This only catches a typo (zero, negative)
169
- # before a single file is generated, the same floor `Memory`/`Port`
170
- # below hold for their own ranges.
105
+ # Positive only: ECS enforces AWS's cpu/memory pairings at deploy time, so this just
106
+ # catches a typo.
171
107
  value_object "Cpu" do
172
108
  attribute :value, Integer
173
109
  invariant("cpu is positive") { value.positive? }
174
110
  end
175
111
 
176
- # Fargate's own valid range, not Lambda's 128-10240 MB —
177
- # `LambdaTarget`'s own `Memory` value object (above) is scoped to
178
- # that aggregate alone, so this is a distinct value object under the
179
- # same name rather than a shared one. A plain sanity floor and
180
- # ceiling, not AWS's own cpu-paired table (see `Cpu`'s own comment).
112
+ # Fargate's own range, not Lambda's 128-10240 MB. A sanity floor and ceiling, not AWS's
113
+ # cpu-paired table (see `Cpu`).
181
114
  value_object "Memory" do
182
115
  attribute :value, Integer
183
116
  invariant("memory is positive") { value.positive? }
184
117
  invariant("memory is at most 122880 MB") { value <= 122880 }
185
118
  end
186
119
 
187
- # Same shape and reasoning as `LambdaTarget`'s own `Database` —
188
- # "Postgres"/"Aurora" provision this domain's own RDS instance;
189
- # "Shared" borrows another already-deployed domain's instance,
190
- # isolated by a native Postgres schema. See that value object's own
191
- # comment for the full reasoning; it applies unchanged here.
120
+ # As `LambdaTarget`'s `Database`.
192
121
  value_object "Database" do
193
122
  attribute :value, String, one_of: ["Postgres", "Aurora", "Shared"]
194
123
  end
195
124
 
196
- # Same shape and reasoning as `LambdaTarget`'s own `Web` — "Rust"
197
- # means the domain's own task also serves its public web UI
198
- # in-process, over the port this aggregate's own `Port` attribute
199
- # declares.
125
+ # As `LambdaTarget`'s `Web`; "Rust" serves the web UI in-process on this aggregate's `Port`.
200
126
  value_object "Web" do
201
127
  attribute :value, String, one_of: ["None", "Rust"]
202
128
  end
203
129
 
204
- # The container port the task listens on and the target group
205
- # routes to — a real TCP port, not an arbitrary integer.
130
+ # The container port the task listens on and the target group routes to.
206
131
  value_object "Port" do
207
132
  attribute :value, Integer
208
133
  invariant("a port is at least 1") { value >= 1 }
209
134
  invariant("a port is at most 65535") { value <= 65535 }
210
135
  end
211
136
 
212
- # All seven required, for the identical reason `LambdaTarget.Declare`
213
- # requires all of its own attributes — see that command's own
214
- # comment on `normalize_args`/`coerce_declared_arguments`. This
215
- # validates a fully-resolved target, after `bin/project_deploy`'s own
216
- # defaulting (`cpu`/`memory`/`port` each fall back to a Ruby-side
217
- # default before this ever dispatches), not instead of it.
137
+ # All seven required for the same reason as `LambdaTarget.Declare`; this validates a
138
+ # target already resolved by `bin/project_deploy`'s defaults.
218
139
  command "Declare" do
219
140
  goal "Validate one domain's AwsFargate deploy target before anything is generated from it"
220
141
 
@@ -238,24 +159,8 @@ Hecks.bluebook "Deploy" do
238
159
  end
239
160
  end
240
161
 
241
- # `bin/project_tenant`'S OWN VALIDATED SHAPE — same split LambdaTarget
242
- # draws with `bin/project_deploy`: this aggregate validates, that
243
- # script GENERATES/ACTS (writes `environments/<slug>.world`, ensures
244
- # the Postgres schema, boots once to prove it). Deliberately NOT a
245
- # saga/process manager with durable runtime state — every step
246
- # `bin/project_tenant` takes is already idempotent by construction
247
- # (`CREATE SCHEMA IF NOT EXISTS` — PostgresEra#connect_for's own,
248
- # File.write of the overlay — a plain overwrite, `Hecks.boot` itself),
249
- # the same "generator script, not a business process" shape
250
- # `bin/project_deploy` already is; a saga's own resumability earns
251
- # its cost coordinating a real distributed transaction across
252
- # multiple ports with real failure boundaries (Payments::Payment's
253
- # own PaymentGateway, say) — three local, idempotent, single-process
254
- # steps have no such boundary to resume across. A durable, reactable
255
- # RUNTIME fact ("this tenant exists, is active, is suspended") is a
256
- # different, later, business-facing thing — deliberately not built
257
- # here, and deliberately not named Deploy::Tenant to avoid colliding
258
- # with this validation-only aggregate should it ever land.
162
+ # The validated shape of `bin/project_tenant`, which does the generating and provisioning.
163
+ # Not a saga: each of its steps is idempotent and local, so there is nothing to resume.
259
164
  aggregate "Tenant" do
260
165
  description "One tenant's validated identity — which realm and Postgres schema `bin/project_tenant` provisions before booting a domain directory under Runtime.boot's own `environment:` overlay for it."
261
166
 
@@ -266,11 +171,8 @@ Hecks.bluebook "Deploy" do
266
171
  attribute :realm, Realm
267
172
  attribute :schema, Schema
268
173
 
269
- # LOWERCASE, NO SPACES — a slug becomes both the `environments/
270
- # <slug>.world` filename and (by convention, not enforced here —
271
- # `schema` is its own, separately-declared field precisely so a
272
- # tenant CAN choose a different schema name than its slug)
273
- # ordinarily the schema name too.
174
+ # Lowercase, no spaces: a slug becomes the `environments/<slug>.world` filename and, by
175
+ # convention only, the schema name (`schema` is separate so a tenant can differ).
274
176
  value_object "Slug" do
275
177
  attribute :value, String, pattern: '^[a-z][a-z0-9_]*$'
276
178
  invariant("a tenant slug is named") { !value.to_s.empty? }
@@ -286,20 +188,9 @@ Hecks.bluebook "Deploy" do
286
188
  invariant("a tenant realm is named") { !value.to_s.empty? }
287
189
  end
288
190
 
289
- # POSTGRES'S OWN REAL IDENTIFIER RULES, loosely — lowercase (this
290
- # adapter never quotes a mixed-case identifier back out consistently
291
- # once created), letters/digits/underscores, starting with a letter.
292
- # NOT a byte-length ceiling too (Postgres's own real 63-byte
293
- # NAMEDATALEN limit) — the expression grammar invariants compile
294
- # through admits a narrow arithmetic/comparison subset, not
295
- # arbitrary method dispatch (`bytesize` included; found live,
296
- # writing this: `Bluebook::Expression::Evaluator` tries to resolve
297
- # a dotted chain as a FIELD lookup, not a method call, the same
298
- # "admits + but not -, no parentheses" narrowness
299
- # embryonaut_bluebooks/payments' own Refund command already
300
- # documents). Postgres itself refuses a too-long identifier loudly
301
- # on its own; not worth fighting the expression grammar for a
302
- # corner case this rare.
191
+ # Lowercase, letters/digits/underscores, starting with a letter. Postgres's 63-byte
192
+ # identifier limit is not checked: the invariant expression grammar has no `bytesize`, and
193
+ # Postgres refuses a too-long identifier itself.
303
194
  value_object "Schema" do
304
195
  attribute :value, String, pattern: '^[a-z][a-z0-9_]*$'
305
196
  invariant("a schema is named") { !value.to_s.empty? }
@@ -2,15 +2,8 @@
2
2
  Hecks.hecksagon "Deploy" do
3
3
  persisted_by "Memory"
4
4
 
5
- # THE IMPERATIVE HALF, BEHIND A REAL PORT — `bin/project_tenant`
6
- # dispatches this `asks` once `Tenant.Declare` has already validated
7
- # the request; `TenantProvisioner` (lib/hecks/adapters/driven/
8
- # tenant_provisioner.rb) writes the overlay file and answers or
9
- # refuses. `database`/`adapter`/`directory` are declared here, not on
10
- # `Tenant` itself — CLI-only facts this validated request never
11
- # needed to hold as its own state. `directory` is the real
12
- # filesystem path being provisioned — NOT re-derivable from `Tenant`'s
13
- # own `domain` (its declared NAME, e.g. "Scratch", never a path).
5
+ # `bin/project_tenant` dispatches this after `Tenant.Declare` validates; `directory` is a real
6
+ # filesystem path, which `Tenant`'s declared `domain` name cannot supply.
14
7
  Deploy::Tenant.port "TenantProvisioning" do
15
8
  asks "Provision", to: Tenant do
16
9
  attribute :database, DatabaseName
@@ -2,31 +2,10 @@ require "digest"
2
2
 
3
3
  module Hecks
4
4
  module EmbryonautBluebook
5
- # What a vendored package records about the release it came from:
6
- # `vendor/embryonaut_bluebooks/<name>/bluebook.lock`.
7
- #
8
- # ## File format
9
- #
10
- # One `key: value` line each for `package`, `version`, `tag`, `commit`
11
- # and `digest`, then one `shape: <Domain> <label>` line per domain:
12
- #
13
- # package: payments
14
- # version: 1.2.0
15
- # tag: payments-v1.2.0
16
- # commit: <full commit id the tag points at>
17
- # digest: <sha256 of the vendored *.bluebook files>
18
- # shape: Payments d33c23
19
- #
20
- # The digest is the sha256 of the `<sha256> <name>` lines of every
21
- # top-level `*.bluebook` file, sorted by name. The source repository's own
22
- # `bin/bluebook_digest` prints the same number for the tagged release, which
23
- # is how a vendored copy is proven to be exactly what it claims to be.
5
+ # The digest here must match the source repo's own bin/bluebook_digest
6
+ # output for the same tag, or a vendored copy cannot be trusted.
24
7
  Lock = Struct.new(:package, :version, :tag, :commit, :digest, :shape, keyword_init: true) do
25
- # Computes the content digest of a directory's `*.bluebook` files.
26
- #
27
- # @param dir [String] the directory to measure
28
- # @return [String, nil] 64 lowercase hex characters, nil when the directory holds
29
- # no bluebook file
8
+ # Sorted by name so the digest never depends on directory listing order.
30
9
  def self.digest_of(dir)
31
10
  names = Dir.children(dir).select { |name| name.end_with?(".bluebook") }.sort
32
11
  return nil if names.empty?
@@ -35,20 +14,12 @@ module Hecks
35
14
  Digest::SHA256.hexdigest(lines.join)
36
15
  end
37
16
 
38
- # Reads a lock file.
39
- #
40
- # @param path [String] the `bluebook.lock` path
41
- # @return [Lock, nil] the parsed lock, nil when the file does not exist
42
17
  def self.read(path)
43
18
  return nil unless File.file?(path)
44
19
 
45
20
  parse(File.read(path))
46
21
  end
47
22
 
48
- # Parses a lock's text.
49
- #
50
- # @param text [String] the file's content
51
- # @return [Lock] the lock, with `shape` as an Array of lines (empty when none)
52
23
  def self.parse(text)
53
24
  fields = { shape: [] }
54
25
  text.each_line do |line|
@@ -60,9 +31,6 @@ module Hecks
60
31
  new(**fields.slice(*members))
61
32
  end
62
33
 
63
- # Renders the lock as the text written to `bluebook.lock`.
64
- #
65
- # @return [String] the lines described above, newline terminated
66
34
  def to_s
67
35
  header = %i[package version tag commit digest].map { |key| "#{key}: #{self[key]}\n" }
68
36
  (header + Array(shape).map { |line| "shape: #{line}\n" }).join
@@ -2,21 +2,10 @@ require_relative "../ports/persistence/plugins/era/storage_shape"
2
2
 
3
3
  module Hecks
4
4
  module EmbryonautBluebook
5
- # The storage-shape labels of a directory of `*.bluebook` files.
6
- #
7
- # A label is what `PostgresEra` mints an era from, so a vendored package
8
- # whose label changed is one whose next deploy mints a new era. This reads
9
- # the files the way the era guard reads a held era's text: parse only, no
10
- # boot, no wiring, no database, each file loaded into a scratch registry.
5
+ # A label change here means a vendored package's next deploy mints a new PostgresEra.
11
6
  module Shape
12
7
  module_function
13
8
 
14
- # Measures every domain a directory's bluebook files declare.
15
- #
16
- # @param dir [String] a directory holding `*.bluebook` files
17
- # @return [Array<String>] one `"<Domain> <label>"` line per domain, sorted by domain name
18
- # @raise [Vendoring::Error] if the directory holds no bluebook file, or the files do
19
- # not load, the refusal a boot would raise
20
9
  def labels(dir)
21
10
  files = Dir[File.join(dir, "*.bluebook")]
22
11
  raise Vendoring::Error, "no *.bluebook files in #{dir}" if files.empty?
@@ -26,11 +15,6 @@ module Hecks
26
15
  end
27
16
  end
28
17
 
29
- # Loads bluebook files into a registry of their own.
30
- #
31
- # @param files [Array<String>] absolute bluebook file paths, in load order
32
- # @return [Runtime::Registry] the scratch registry holding what the files declared
33
- # @raise [Vendoring::Error] if any file fails to load
34
18
  def load_all(files)
35
19
  scratch = Runtime::Registry.new
36
20
  library = Ports::Loading.bootstrap
@@ -5,32 +5,8 @@ require_relative "shape"
5
5
 
6
6
  module Hecks
7
7
  module EmbryonautBluebook
8
- # Vendors one package of the bluebook registry into a consuming project.
9
- #
10
- # ## What it does
11
- #
12
- # Pins the package's top-level `bluebook/*.bluebook` files from one commit
13
- # of a local source repository into
14
- # `<root>/vendor/embryonaut_bluebooks/<name>/bluebook/`, where `load!`
15
- # resolves them. Only bluebook files are taken: a `.hecksagon`, `.port` or
16
- # `.adapter` is a wiring decision for whoever deploys, never part of the
17
- # vendored package.
18
- #
19
- # ## Releases and bare commits
20
- #
21
- # The source repository tags each release `<name>-v<X.Y.Z>` on a commit
22
- # whose `<name>/bluebook.yml` says that version. A release pin (`ref` nil for
23
- # the newest tag, `"1.2.0"`, or the tag name) also writes `bluebook.lock`,
24
- # which records version, tag, commit, content digest and storage shape, and
25
- # holds two refusals, because a production project is bound to `PostgresEra`:
26
- #
27
- # - a version lower than the vendored one, unless `allow_downgrade`;
28
- # - a storage-shape change that does not raise the version by at least a
29
- # minor, so a new era shows in the version number and not only in a hash.
30
- #
31
- # Any other commit-ish pins that commit and writes only the marker, with no
32
- # lock and no version checks. Either way nothing on disk changes when a
33
- # pin is refused or its files do not load.
8
+ # Pins a package's `bluebook/*.bluebook` files from a source repo commit; a
9
+ # release pin also refuses a downgrade or an era-breaking storage-shape change.
34
10
  class Vendor
35
11
  # A release version, `X.Y.Z`.
36
12
  VERSION = /\A\d+\.\d+\.\d+\z/
@@ -38,33 +14,17 @@ module Hecks
38
14
  # A package name is a directory name and a bluebook name stem.
39
15
  NAME = /\A[a-z][a-z0-9_]*\z/
40
16
 
41
- # What a vendoring changed.
42
- #
43
- # @!attribute [r] previous_version
44
- # @return [String, nil] the version recorded before, nil when there was none
45
- # @!attribute [r] previous_shape
46
- # @return [Array<String>, nil] the shape lines measured before, nil when there was
47
- # no earlier copy
17
+ # What a vendoring changed; previous_version/previous_shape are nil when
18
+ # there was no earlier vendored copy.
48
19
  Result = Struct.new(:package, :commit, :version, :tag, :digest, :shape,
49
20
  :previous_version, :previous_shape, :dir, keyword_init: true) do
50
- # Says whether this was a release pin, which writes a lock.
51
- #
52
- # @return [Boolean] true for a release, false for a bare commit
53
21
  def release? = !tag.nil?
54
22
 
55
- # Says whether the storage shape moved since the earlier copy.
56
- #
57
- # @return [Boolean] true when an earlier copy existed and its shape differs
58
23
  def shape_changed? = !previous_shape.nil? && previous_shape != shape
59
24
  end
60
25
 
61
- # @param name [String, Symbol] the package's directory name, such as `"payments"`
62
- # @param from [String] path of the local source repository
63
- # @param ref [String, nil] `X.Y.Z`, a release tag name, any commit-ish, or nil for the
64
- # newest release
65
- # @param root [String] the consuming project's root
66
- # @param allow_downgrade [Boolean] whether a release older than the vendored one is allowed
67
- # @raise [Vendoring::Error] if the name is not a plain package name
26
+ # `ref` accepts `X.Y.Z`, a release tag name, any commit-ish, or nil for
27
+ # the newest release.
68
28
  def initialize(name, from:, root:, ref: nil, allow_downgrade: false)
69
29
  @name = name.to_s
70
30
  raise Vendoring::Error, "#{name.inspect} is not a package name" unless @name.match?(NAME)
@@ -76,10 +36,6 @@ module Hecks
76
36
  end
77
37
 
78
38
  # Pins the package and reports what changed.
79
- #
80
- # @return [Result] the commit, version and shape now vendored
81
- # @raise [Vendoring::Error] if the release or commit is not found, the tagged commit
82
- # disagrees with its own version, the files do not load, or a refusal above applies
83
39
  def call
84
40
  tag, version = release
85
41
  previous_version, previous_shape = existing
@@ -99,9 +55,8 @@ module Hecks
99
55
 
100
56
  def package_dir = File.join(@root, "vendor", "embryonaut_bluebooks", @name)
101
57
 
102
- # Resolves what `ref` asks for into a release tag and its version.
103
- #
104
- # @return [Array(String, String), nil] tag and version for a release, nil for a bare commit
58
+ # Resolves what `ref` asks for into a release tag and its version, or nil
59
+ # for a bare commit.
105
60
  def release
106
61
  tag = release_tag
107
62
  return nil unless tag
@@ -3,21 +3,10 @@ require_relative "vendor"
3
3
 
4
4
  module Hecks
5
5
  module EmbryonautBluebook
6
- # The command line behind `bin/vendor_bluebook`.
6
+ # The command line behind `bin/vendor_bluebook`; lives in the gem so a
7
+ # consuming project reaches it through its own bundle, no script to copy.
7
8
  #
8
- # It lives in the gem, not in a script, so a consuming project reaches it
9
- # through its own bundle without a copy of the script:
10
- #
11
- # bundle exec ruby -rhecks \
12
- # -e 'exit Hecks::EmbryonautBluebook::VendorCli.run(ARGV)' payments@1.2.0
13
- #
14
- # ## Usage
15
- #
16
- # vendor_bluebook <package>[@<version-or-commit>] [--from <path>] [--root <path>]
17
- #
18
- # `--from` defaults to `$EMBRYONAUT_BLUEBOOKS_SRC` and `--root` to the current
19
- # directory. `ALLOW_DOWNGRADE=1` in the environment permits a release older
20
- # than the vendored one.
9
+ # vendor_bluebook <package>[@<version-or-commit>] [--from path] [--root path]
21
10
  module VendorCli
22
11
  USAGE = "usage: vendor_bluebook <package>[@<version-or-commit>] [--from PATH] [--root PATH]".freeze
23
12
 
@@ -44,11 +33,7 @@ module Hecks
44
33
  1
45
34
  end
46
35
 
47
- # Reads the options into a hash and picks out the package argument.
48
- #
49
- # @param argv [Array<String>] the command-line arguments
50
- # @param options [Hash{Symbol => String}] receives `:from` and `:root` when given
51
- # @return [String, nil] the one positional argument, nil unless exactly one was given
36
+ # nil unless argv held exactly one positional argument.
52
37
  def self.parse(argv, options)
53
38
  parser = OptionParser.new
54
39
  parser.on("--from PATH") { |path| options[:from] = path }
@@ -1,75 +1,6 @@
1
1
  module Hecks
2
- # A vendored, external bluebook.
3
- #
4
- # ## What it is
5
- #
6
- # Same shape as Framework (framework.rb), for members that don't ship
7
- # inside hecks's own lib/ at all: a separate, independently-versioned
8
- # package (github.com/chrisyoung/embryonaut_bluebooks) that a consuming
9
- # project vendors into its own checkout, the same way a project already
10
- # vendors hecks itself (bin/vendor_hecks, vendor/hecks/).
11
- #
12
- # ## Recovered, not rebuilt
13
- #
14
- # This module and its `uses_embryonaut_bluebook`
15
- # DSL word (hecksagon_builder.rb) were built on a prior commit of this
16
- # repo (933d1dd), vendored out to a real consumer (a client site's domain,
17
- # for embryonaut_bluebooks/payments), and then lost from this repo's own
18
- # reachable history — a hard reset or rebase left no branch containing
19
- # that commit. The consumer's vendor snapshot (a `git archive` of that
20
- # commit, committed into their repo) was the only surviving copy; this
21
- # file is ported forward from it, checked against current `main`'s own
22
- # conventions rather than copied wholesale, since the two trees had
23
- # otherwise diverged for weeks in both directions.
24
- #
25
- # ## Resolution
26
- #
27
- # Resolved from the consuming registry's own root, not this gem's
28
- # __dir__ — Framework::ROOT can be a fixed, `__dir__`-relative constant
29
- # because framework members ship inside this gem; an embryonaut bluebook
30
- # ships inside the consumer's own checkout instead, at
31
- # `<registry.root>/vendor/embryonaut_bluebooks/<name>/bluebook/`. There
32
- # is no fixed answer until a registry (and its root) actually exists, so
33
- # this resolves lazily, per call — the same reason `uses_framework`
34
- # itself only runs at hecksagon-build time, when a real registry is
35
- # current.
36
- #
37
- # ## Load order
38
- #
39
- # Every `.bluebook` file in the package, sorted — not just one. Unlike a
40
- # framework member (one file, named by its own stem), a vendored package
41
- # can span several bluebook files that reopen the same `Hecks.bluebook`
42
- # (embryonaut_bluebooks/payments/bluebook/{payment,payments,policies}
43
- # .bluebook all reopen "Payments"). Load order matters — policies
44
- # .bluebook names `Payment::Succeed` and needs the aggregate already
45
- # built — and a plain alphabetical sort already gives the right order:
46
- # payment < payments < policies, the same reason that package's own
47
- # files are named to fall in that order in the first place.
48
- #
49
- # ## Scope
50
- #
51
- # Only the bluebook files — same restriction Framework draws, same
52
- # reason: a `.hecksagon`/`.port`/`.adapter` is a wiring decision
53
- # (persistence, which processor adapter is bound) that belongs to
54
- # whoever is deploying, never baked into the vendored package itself.
55
- # embryonaut_bluebooks/payments ships its own mock `.hecksagon` for its
56
- # own spec suite; a consumer declares its own separate
57
- # `Hecks.hecksagon "Payments" do ... end` to bind real storage/adapters
58
- # — see Framework's own comment for the fuller reasoning, identical here.
59
- #
60
- # ## Idempotency
61
- #
62
- # Idempotent the same way `Framework.load!` is — checked against the
63
- # bluebook this package actually declares (`Naming.pascal("payments")`
64
- # => "Payments"), not a separate ledger. A vendored package's directory
65
- # name and its declared `Hecks.bluebook` name are the one convention
66
- # this reuses from Framework rather than reinventing.
67
- #
68
- # ## Vendoring
69
- #
70
- # `vendor!` is the other direction: it pins a package from a local checkout
71
- # of the registry into a consuming project's own `vendor/` tree, and is the
72
- # one implementation behind `bin/vendor_bluebook` (see Vendoring and Vendor).
2
+ # A vendored, external bluebook: same shape as Framework, but for a
3
+ # package that ships outside hecks's own lib/ (see .load! and .vendor!).
73
4
  module EmbryonautBluebook
74
5
  autoload :Lock, File.join(__dir__, "embryonaut_bluebook/lock")
75
6
  autoload :Shape, File.join(__dir__, "embryonaut_bluebook/shape")
@@ -103,6 +34,8 @@ module Hecks
103
34
  "vendor it with bin/vendor_bluebook #{name}"
104
35
  end
105
36
 
37
+ # Loaded in the glob's alphabetical order; a package with several
38
+ # files that reopen the same bluebook is named to rely on that.
106
39
  files.each { |file| Kernel.load(file) }
107
40
  end
108
41