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,28 +8,8 @@ module Hecks
8
8
  module Projections
9
9
  module Deploy
10
10
  module Preview
11
- # Renders `preview.yaml`, the CloudFormation template of one
12
- # per-branch preview stack.
13
- #
14
- # A separate template rather than conditions threaded through the main
15
- # one, so a preview can never put the main stack's live resources in a
16
- # change set. The two share a task shape and nothing else.
17
- #
18
- # ## What a preview owns
19
- #
20
- # Per branch: a cluster, service and task, a load balancer, one image
21
- # repository per container, a session secret, any secrets a container
22
- # declares, a log group, a CloudFront distribution (default hostname,
23
- # nothing cached) and a database name on the shared instance, created
24
- # by a one-shot task before the service starts.
25
- #
26
- # ## What it borrows and leaves out
27
- #
28
- # It attaches to the owner stack's VPC, subnets and compute security
29
- # group and connects to the shared database instance, through the same
30
- # `Owning*` parameters the main template takes for `database "Shared"`.
31
- # It has no alarms, no notifications, no warmer and no custom domain:
32
- # nobody should be paged for a preview.
11
+ # Renders `preview.yaml`, one per-branch preview stack's CloudFormation template.
12
+ # Kept separate from the main one, so a preview can never enter a change set against it.
33
13
  module Template
34
14
  CACHING_DISABLED = "4135ea2d-6df8-44a3-9df3-4b5a84be39ad".freeze
35
15
  ALL_VIEWER = "216adef6-5c7f-47e4-b989-5492eafa07d3".freeze
@@ -38,10 +18,6 @@ module Hecks
38
18
 
39
19
  module_function
40
20
 
41
- # Renders the whole template.
42
- #
43
- # @param settings [Settings] the resolved preview settings
44
- # @return [String] the CloudFormation YAML, ending in one newline
45
21
  def render(settings)
46
22
  [
47
23
  header(settings), parameters(settings),
@@ -50,10 +26,6 @@ module Hecks
50
26
  ].join("\n") << "\n"
51
27
  end
52
28
 
53
- # Renders the file's leading comment and template description.
54
- #
55
- # @param settings [Settings] the resolved preview settings
56
- # @return [String] the header block
57
29
  def header(settings)
58
30
  <<~YAML.chomp
59
31
  # GENERATED by bin/project_deploy - re-run it to refresh this file rather than
@@ -75,10 +47,6 @@ module Hecks
75
47
  YAML
76
48
  end
77
49
 
78
- # Renders the parameters section.
79
- #
80
- # @param settings [Settings] the resolved preview settings
81
- # @return [String] the section, one image-tag parameter per container
82
50
  def parameters(settings)
83
51
  image_tags = settings.containers.map do |c|
84
52
  <<~YAML.chomp
@@ -90,9 +58,6 @@ module Hecks
90
58
  "Parameters:\n#{indent([base_parameters, *image_tags, desired_count_parameter].join("\n"), 2)}"
91
59
  end
92
60
 
93
- # Renders the parameters every preview takes: its name, database and the borrowed network.
94
- #
95
- # @return [String] the parameter blocks
96
61
  def base_parameters
97
62
  <<~YAML.chomp
98
63
  EnvName:
@@ -122,9 +87,6 @@ module Hecks
122
87
  YAML
123
88
  end
124
89
 
125
- # Renders the parameter `preview.sh` uses to start the stack at zero tasks.
126
- #
127
- # @return [String] the parameter block
128
90
  def desired_count_parameter
129
91
  <<~YAML.chomp
130
92
  # The repositories are created by this stack, so the first create has no image to
@@ -135,10 +97,6 @@ module Hecks
135
97
  YAML
136
98
  end
137
99
 
138
- # Collects every resource block of the template.
139
- #
140
- # @param settings [Settings] the resolved preview settings
141
- # @return [Array<String>] the resource blocks, in template order
142
100
  def resources(settings)
143
101
  [
144
102
  AccessYaml.secrets(settings), RoutingYaml.networking(settings), repositories(settings),
@@ -149,10 +107,6 @@ module Hecks
149
107
  ].flatten
150
108
  end
151
109
 
152
- # Renders one image repository per container, emptied when the stack is deleted.
153
- #
154
- # @param settings [Settings] the resolved preview settings
155
- # @return [Array<String>] the resource blocks
156
110
  def repositories(settings)
157
111
  settings.containers.map do |c|
158
112
  <<~YAML.chomp
@@ -165,10 +119,6 @@ module Hecks
165
119
  end
166
120
  end
167
121
 
168
- # Renders the cluster and the log group.
169
- #
170
- # @param settings [Settings] the resolved preview settings
171
- # @return [Array<String>] the two resource blocks
172
122
  def cluster_and_logs(settings)
173
123
  [
174
124
  <<~YAML.chomp,
@@ -187,9 +137,6 @@ module Hecks
187
137
  ]
188
138
  end
189
139
 
190
- # Renders the CDN distribution, which caches nothing.
191
- #
192
- # @return [String] the resource block
193
140
  def distribution
194
141
  <<~YAML.chomp
195
142
  # One behavior, nothing cached: a preview is for looking at a change, so no cache means
@@ -219,9 +166,6 @@ module Hecks
219
166
  YAML
220
167
  end
221
168
 
222
- # Renders the outputs `preview.sh` reads back.
223
- #
224
- # @return [String] the section
225
169
  def outputs
226
170
  <<~YAML.chomp
227
171
  Outputs:
@@ -5,73 +5,8 @@ require_relative "preview/script"
5
5
  module Hecks
6
6
  module Projections
7
7
  module Deploy
8
- # Per-branch preview stacks for a `deployed_to("AwsFargate")` domain: one
9
- # isolated copy of the stack per git branch, created and torn down by a
10
- # generated script.
11
- #
12
- # Opt-in. A domain with no `preview` setting gets exactly the files it
13
- # got before; `Fargate.call` asks `requested?` and merges in `call`'s
14
- # files only when the answer is yes.
15
- #
16
- # ## What is generated
17
- #
18
- # - `preview.yaml`: a separate CloudFormation template, so a preview never
19
- # appears in a change set against the live stack (see `Template`).
20
- # - `preview.sh`: `deploy` (create or update), `destroy`, `list`, `url`,
21
- # `name`, `ensure-database` and `login` (see `Script`).
22
- # - The database step is a one-shot task declared in `preview.yaml`
23
- # and run by `preview.sh`; there is no separate file, because the
24
- # instance is reachable only from inside the VPC.
25
- #
26
- # ## Settings
27
- #
28
- # ```ruby
29
- # deployed_to("AwsFargate") do
30
- # region "us-east-1"
31
- # preview do
32
- # containers [{ name: "web", port: 8080, host: true, default: true }]
33
- # end
34
- # end
35
- # ```
36
- #
37
- # `preview true` and a block with no settings accept every default. Keys:
38
- #
39
- # - `prefix`: stack, cluster, service and image repository name prefix.
40
- # Default `<stack_prefix>-<stack>-preview`.
41
- # - `alb_prefix`: load balancer name prefix, at most 11 characters.
42
- # Default the first 8 letters of the stack name plus `pv`.
43
- # - `owner_stack`: the stack whose VPC, subnets and compute security group
44
- # the preview attaches to. Default the `owner_stack` of a Shared main
45
- # stack, else the main stack.
46
- # - `database_stack`: the stack whose outputs name the shared database
47
- # instance. Default `owner_stack`.
48
- # - `database_endpoint_output`, `database_secret_output`: the output keys
49
- # holding the instance host and the master secret ARN. Defaults
50
- # `DatabaseEndpoint` and `DatabaseSecretArn`.
51
- # - `db_prefix`: a preview's database is `<db_prefix>_pv_<env>`. Default
52
- # the stack name, lowercased, at most 30 characters.
53
- # - `protected_databases`: names the database task refuses, added to the
54
- # main database, `postgres` and the two template databases. Default `[]`.
55
- # - `protected_branches`: branches `preview.sh` refuses. Default
56
- # `["main", "master"]`.
57
- # - `cpu`, `memory`: task size. Default the main stack's.
58
- # - `log_retention_days`: a value CloudWatch accepts. Default `7`.
59
- # - `session_cookie`: cookie name; sets `HECKS_SESSION_COOKIE` on the host
60
- # and is what `login` writes. Default the host's own default.
61
- # - `first_admin`: whether `preview.sh` signs the deployer up and offers
62
- # `login`. Default `true`.
63
- # - `signup_path`: the host route the signup posts to. Default `/signups`.
64
- # - `landing_path`: the page `login` points the browser at. Default `/`.
65
- # - `db_init_image`: image of the database task; needs `sh` and `psql`.
66
- # Default the public Postgres 16 alpine image.
67
- # - `containers`: the containers the preview task runs. Default the main
68
- # stack's container list; see `Containers` for the entry keys.
69
- #
70
- # ## Why a preview mints no era and needs no bastion
71
- #
72
- # The host mints era 1 itself on an empty database, and the database
73
- # task runs inside the VPC, so neither the main stack's bastion nor a
74
- # tunnel is involved.
8
+ # Per-branch preview stacks for a `deployed_to("AwsFargate")` domain, opt-in
9
+ # via a `preview` setting; the host mints era 1 itself, so no bastion is needed.
75
10
  module Preview
76
11
  module_function
77
12
 
@@ -2,21 +2,8 @@ module Hecks
2
2
  module Projections
3
3
  module Deploy
4
4
  module Scripts
5
- # The `deployed_to("AwsFargate")` settings the hosting scripts read,
6
- # with their defaults and the check each value must pass.
7
- #
8
- # Every value ends up inside a shell script or a Makefile, so each one
9
- # is refused unless it matches a conservative pattern for what it names
10
- # (a region, a cluster, a GitHub repository). That check is what lets
11
- # the renderers write values without per-context escaping.
12
- #
13
- # The containers, their ECR repositories and image-tag parameters, and
14
- # the default cluster and service names are not settings here. They come
15
- # from the template generator's own resolved plan
16
- # (`Fargate::Settings::Plan`), so a script can only name what the
17
- # generated `template.yaml` defines.
18
- #
19
- # The keys are documented on `Scripts`.
5
+ # The `deployed_to("AwsFargate")` settings the hosting scripts read, checked
6
+ # against a conservative pattern so a renderer can splice one in without escaping.
20
7
  class Settings
21
8
  RESOURCE = /\A[a-zA-Z0-9][a-zA-Z0-9_-]*\z/
22
9
  REGION = /\A[a-z]{2}(-[a-z]+)+-\d\z/
@@ -34,16 +21,7 @@ module Hecks
34
21
  attr_reader :region, :stack, :cluster, :service, :containers, :hecks_release, :hecks_source,
35
22
  :hecks_cache_dir, :smoke_repo, :smoke_workflow, :smoke_ref, :expected_eras, :public_url
36
23
 
37
- # Reads and checks every hosting setting.
38
- #
39
- # @param deploy_settings [Hash{Symbol => Object}] the world's
40
- # `deployed_to("AwsFargate")` settings
41
- # @param plan [Fargate::Settings::Plan] the template generator's resolved settings:
42
- # its `layout` holds the containers, its `names` the cluster and service
43
- # @param stack_name [String] the CloudFormation stack
44
- # @param region [String] the validated AWS region
45
- # @raise [ArgumentError] if `hecks_release` is missing, or a value
46
- # does not match the pattern for what it names
24
+ # `plan.layout` holds the containers; `plan.names` the cluster and service.
47
25
  def initialize(deploy_settings:, plan:, stack_name:, region:)
48
26
  @raw = deploy_settings
49
27
  @region = check(:region, region, REGION)
@@ -4,64 +4,11 @@ require_relative "scripts/settings"
4
4
  module Hecks
5
5
  module Projections
6
6
  module Deploy
7
- # The hosting scripts an AWS Fargate deploy ships beside its template:
8
- # the steps an operator runs after `make deploy` has created the stack.
9
- # `Fargate.call` adds them when the domain's
10
- # `deployed_to("AwsFargate")` block opts in, and leaves its file map
11
- # untouched otherwise.
12
- #
13
- # ## What it emits
14
- #
15
- # - `deploy-service.sh` reads the service's active task definition,
16
- # pushes a local image under a never-reused tag, swaps one container's
17
- # image, and syncs that container's CloudFormation `*ImageTag`
18
- # parameter with `--use-previous-template`, checking afterwards that no
19
- # other parameter changed.
20
- # - `smoke-after-deploy.sh` waits for the roll to settle, then dispatches
21
- # a GitHub workflow and reports its result. Exit codes 20 to 23 are its
22
- # own.
23
- # - `hosting.mk` is included by the generated `Makefile`. It pins the
24
- # Hecks release the domain image is built from (`HECKS_ROOT` resolves
25
- # to a cached checkout of that release's tag, never to a path on one
26
- # machine) and adds the `deploy-service`, `smoke-after-deploy` and
27
- # `check-era` targets.
28
- # - `expected-era` is the allow-list `bin/check_era` compares a host's
29
- # `GET /version` era against.
30
- #
31
- # ## Settings
32
- #
33
- # Read from `deployed_to("AwsFargate")`. Everything but `hosting_scripts`
34
- # and `hecks_release` has a default; the ones the stack template already
35
- # names (`stack_name`, `stack_prefix`, `region`) are not repeated here.
36
- #
37
- # - `hosting_scripts` opts in to every file above, when `true`. Off by default.
38
- # - `hecks_release` is the Hecks release the domain image is built from,
39
- # such as `"2.5.1"`. Required.
40
- # - `hecks_source` is the repository URL the release tag is fetched from.
41
- # Defaults to the Hecks repository.
42
- # - `hecks_cache_dir` is where release checkouts are kept, a Make expression.
43
- # Defaults to `$(HOME)/.cache/hecks`.
44
- # - `ecs_cluster` and `ecs_service` name the cluster and service. They default to the
45
- # names the generated template gives them: the stack name, or the `names` override.
46
- # - `smoke_repo` is the GitHub `owner/name` holding the smoke workflow, and
47
- # `smoke_workflow` its file name. With neither set, `REPO` and `WORKFLOW` must
48
- # come from the environment.
49
- # - `smoke_ref` is the ref the workflow is dispatched on. Defaults to `main`.
50
- # - `expected_eras` lists the era ids `expected-era` holds. Defaults to none, so a
51
- # host is only checked to report an era.
52
- # - `public_url` is the host URL `make check-era` requests. Without it, `URL` must
53
- # be passed to `make`.
54
- #
55
- # ## Names come from the template
56
- #
57
- # The containers, their ECR repositories and their image-tag parameters are not
58
- # settings of the scripts. `deploy-service.sh` accepts exactly the containers the
59
- # template's `Fargate::Containers::Layout` holds (the domain container, then each
60
- # entry of the world's `containers`), and for each one pushes to the repository and
61
- # syncs the parameter that layout names: the values the template renders. Rename them
62
- # with `domain_container` and `containers` (see `Fargate`), and the scripts, and the
63
- # `Makefile`'s `deploy`, follow.
7
+ # The hosting scripts an AWS Fargate deploy ships beside its template: the steps an
8
+ # operator runs after `make deploy` has created the stack.
64
9
  module Scripts
10
+ # Container names, repositories and image-tag parameters come from `plan`, not
11
+ # from a setting here; rename them in `Fargate::Settings::Plan` and these scripts follow.
65
12
  SCRIPT_DIR = File.join(__dir__, "scripts").freeze
66
13
 
67
14
  module_function
@@ -1,57 +1,22 @@
1
1
  module Hecks
2
2
  module Projections
3
3
  module Deploy
4
- # Plumbing `Lambda` and `Fargate` both need — VPC/subnet/security-group
5
- # resources, RDS/Aurora, `bastion.yaml`, and a cross-domain policy's
6
- # own least-privilege invoke grant are the same problem (get a
7
- # domain's own Postgres instance stood up and reachable for the one
8
- # boot that mints era 1, and let a domain call another domain's
9
- # Lambda) whichever compute target ships the domain's own code.
10
- #
11
- # Plain module functions, not a registered `Projector::Target` — this
12
- # has no `deployed_to(...)` block of its own to generate from, only
13
- # helpers the two real targets call with the facts they have already
14
- # resolved (`db_id`, `aurora`, `google_oauth_present`, and so on).
15
- #
16
- # Every `*_table` method here answers the same `key`/`var`/`name`
17
- # strings regardless of which target built them — `stack_outputs`'
18
- # own `"FunctionSecurityGroupId"` key, in particular, is what a
19
- # Shared-mode domain looks up from its owner's stack (`bin/project_deploy`'s
20
- # own Shared-mode branch), and that lookup has to succeed the same
21
- # way whether the owner is a `Lambda` or a `Fargate` deploy.
4
+ # VPC, RDS/Aurora, bastion and cross-domain invoke plumbing shared by the
5
+ # `Lambda` and `Fargate` deploy targets. Plain module functions, not a Target.
22
6
  module Shared
23
7
  module_function
24
8
 
25
- # The stack↔bastion contract — one shared table each of
26
- # `template.yaml`'s Outputs, `bastion.yaml`'s Parameters, and a
27
- # Makefile's own eval/`--parameter-overrides` lines reads from,
28
- # instead of three to four independent string literals per fact
29
- # with nothing checking they agree. `var` is the Make variable
30
- # each output becomes.
9
+ # The stack-to-bastion contract: one table that `template.yaml` Outputs,
10
+ # `bastion.yaml` Parameters and the Makefile overrides all read from.
11
+ # Empty when `shared`, since such a domain provisions no VPC/RDS of its own.
31
12
  #
32
- # Empty when `shared` — a Shared-mode domain provisions no
33
- # VPC/RDS of its own to expose at all (`bastion_parameters`,
34
- # `bastion_yaml`, and era-minting are all skipped for the same
35
- # reason: there's nothing here for a bastion to reach). A
36
- # Shared-mode domain's own stack is never an owner, so nothing
37
- # downstream ever needs to look these up from it either.
38
- #
39
- # @param shared [Boolean] whether this domain borrows another domain's
40
- # RDS instance (`database "Shared"`) rather than provisioning its own
41
- # @param db_id [String] the RDS/VPC resources' own shared logical-id prefix
42
- # @param db_ref_id [String] the logical id `.Endpoint`/`.MasterUserSecret` resolve
43
- # against — `db_id` itself for plain RDS, `"#{db_id}Cluster"` for Aurora
44
- # @param secret_intrinsic [String] the rendered `!Ref`/`!GetAtt` expression for
45
- # this domain's own database secret
46
- # @param compute_security_group_ref [String] the `!Ref` expression for the
47
- # compute resource's own security group (a Lambda function's, an `ECS`
48
- # service's) — reused by a Shared-mode domain elsewhere to attach its own
49
- # compute to this domain's VPC without minting a security group of its own
50
- # @param google_oauth_present [Boolean] whether this domain's own NAT
51
- # Gateway/public-subnet resources exist, and so whether `PublicSubnetId`/
52
- # `BastionSubnetId` are real outputs to expose
53
- # @return [Array<Hash{Symbol => String}>] frozen; each entry a `{key:, var:,
54
- # ref:}` triple
13
+ # @param shared [Boolean] whether this domain borrows another domain's RDS instance
14
+ # @param db_id [String] shared logical-id prefix of the RDS/VPC resources
15
+ # @param db_ref_id [String] logical id `.Endpoint` resolves against (Cluster for Aurora)
16
+ # @param secret_intrinsic [String] rendered `!Ref`/`!GetAtt` for the database secret
17
+ # @param compute_security_group_ref [String] `!Ref` for the compute's security group
18
+ # @param google_oauth_present [Boolean] whether the NAT/public-subnet resources exist
19
+ # @return [Array<Hash{Symbol => String}>] frozen `{key:, var:, ref:}` entries
55
20
  def stack_outputs(shared:, db_id:, db_ref_id:, secret_intrinsic:, compute_security_group_ref:, google_oauth_present:)
56
21
  return [].freeze if shared
57
22
 
@@ -60,44 +25,25 @@ module Hecks
60
25
  { key: "DbSecurityGroupId", var: "DB_SG_ID", ref: "!Ref #{db_id}SecurityGroup" },
61
26
  { key: "DatabaseEndpoint", var: "DB_HOST", ref: "!GetAtt #{db_ref_id}.Endpoint.Address" },
62
27
  { key: "DatabaseSecretArn", var: "DB_SECRET_ARN", ref: secret_intrinsic },
63
- # Not consumed by anything in this domain's own generated output —
64
- # a "Shared"-mode domain elsewhere looks these two up live to
65
- # attach its own compute to this domain's VPC/security group
66
- # without minting either itself.
28
+ # Read live by a Shared-mode domain to attach its compute to this VPC.
67
29
  { key: "FunctionSecurityGroupId", var: "FN_SG_ID", ref: compute_security_group_ref },
68
30
  { key: "PrivateSubnetAId", var: "PRIVATE_SUBNET_A_ID", ref: "!Ref #{db_id}SubnetA" },
69
31
  { key: "PrivateSubnetBId", var: "PRIVATE_SUBNET_B_ID", ref: "!Ref #{db_id}SubnetB" },
70
- # Only when google_oauth_present — #{db_id}PublicSubnet (and the
71
- # Internet Gateway it's attached through) only exist as resources
72
- # at all when the NAT Gateway block does. `bastion.yaml` needs
73
- # this: a VPC accepts only one attached Internet Gateway, and one
74
- # already exists once this is true — its own temporary IGW+subnet
75
- # would collide (a real, live "Resource.AlreadyAssociated").
32
+ # A VPC accepts one Internet Gateway; `bastion.yaml` must reuse this subnet's
33
+ # rather than mint its own (Resource.AlreadyAssociated).
76
34
  *(google_oauth_present ? [{ key: "PublicSubnetId", var: "PUBLIC_SUBNET_ID", ref: "!Ref #{db_id}PublicSubnet" }] : []),
77
- # A separate subnet from PublicSubnetId, same shared route
78
- # table/Internet Gateway -- #{db_id}PublicSubnet always lands in
79
- # AZ index 0 (`!Select [0, !GetAZs '']`), and a real, live
80
- # CREATE_FAILED caught this account unable to launch any EC2
81
- # instance there at all ("Your requested instance type ... is
82
- # not supported in your requested Availability Zone
83
- # (us-east-1a)") -- NatGateway and RDS aren't EC2 instances and
84
- # never hit this, only BastionInstance does. AZ index 1 instead,
85
- # matching #{db_id}SubnetB's own choice.
35
+ # Separate AZ-1 subnet: EC2 instances cannot launch in AZ index 0 on this
36
+ # account, which only the bastion hits.
86
37
  *(google_oauth_present ? [{ key: "BastionSubnetId", var: "BASTION_SUBNET_ID", ref: "!Ref #{db_id}BastionPublicSubnet" }] : []),
87
38
  ].freeze
88
39
  end
89
40
 
90
- # `bastion.yaml`'s own Parameters — deliberately allowed to rename
91
- # (`RdsSecurityGroupId` reads better inside `bastion.yaml` than the
92
- # stack output's own `DbSecurityGroupId`), which is exactly the kind
93
- # of rename that can silently drift between the two files without
94
- # this shared table.
41
+ # `bastion.yaml`'s Parameters, kept in one table so renames cannot drift
42
+ # from the stack outputs.
95
43
  #
96
- # @param shared [Boolean] whether this domain borrows another domain's
97
- # RDS instance — see `stack_outputs`' own comment
44
+ # @param shared [Boolean] see `stack_outputs`
98
45
  # @param google_oauth_present [Boolean] see `stack_outputs`
99
- # @return [Array<Hash{Symbol => String}>] frozen; each entry a `{name:,
100
- # from_output:, type:}` triple
46
+ # @return [Array<Hash{Symbol => String}>] frozen `{name:, from_output:, type:}` entries
101
47
  def bastion_parameters(shared:, google_oauth_present:)
102
48
  return [].freeze if shared
103
49
 
@@ -109,16 +55,9 @@ module Hecks
109
55
  ].freeze
110
56
  end
111
57
 
112
- # A generation-time assertion, not a bluebook refusal — this is the
113
- # generator catching its own bug immediately, before writing a
114
- # single file, instead of producing a `bastion.yaml` whose Parameter
115
- # nothing could ever fill.
58
+ # Generation-time check that every bastion parameter maps to a stack output.
116
59
  #
117
- # @param bastion_parameters [Array<Hash{Symbol => String}>] as `bastion_parameters` builds
118
- # @param stack_outputs [Array<Hash{Symbol => String}>] as `stack_outputs` builds
119
- # @return [void]
120
- # @raise [RuntimeError] if any parameter names a `from_output` absent from
121
- # `stack_outputs`
60
+ # @raise [RuntimeError] if a parameter names a `from_output` absent from `stack_outputs`
122
61
  def check_bastion_parameters!(bastion_parameters, stack_outputs)
123
62
  bastion_parameters.each do |param|
124
63
  stack_outputs.any? { |o| o[:key] == param[:from_output] } or
@@ -126,27 +65,13 @@ module Hecks
126
65
  end
127
66
  end
128
67
 
129
- # Renders the least-privilege IAM grant a cross-domain policy's own
130
- # invoke needs — one `lambda:InvokeFunction` statement per declared
131
- # `across:` target, appended to the compute role's existing
132
- # `Policies:` list. `rust/host`'s own `lambda_client.rs`
133
- # (`AwsLambdaInvoker`) is the one piece of a cross-domain dispatch
134
- # path that needs this at all, regardless of which compute target
135
- # ships the calling domain's own code — a Fargate task and a Lambda
136
- # function both reach another domain's dispatch Lambda the same way.
137
- #
138
- # Declared, not deployed: an ARN naming a function that does not
139
- # exist yet is still valid IAM policy — the call simply fails with
140
- # `ResourceNotFoundException` instead of `AccessDeniedException`
141
- # until the target domain is deployed for real.
68
+ # Renders one `lambda:InvokeFunction` IAM statement per `across:` target,
69
+ # to append to the compute role's `Policies:` list.
70
+ # An ARN for a not-yet-deployed function is valid IAM; the call just fails until then.
142
71
  #
143
- # @param targets [Array<String>] the domain names this stack's `across:` targets
144
- # declare
145
- # @param base [String] the marker line's own rendered indentation whitespace;
146
- # everything below is built relative to it
147
- # @return [String] the rendered comment plus one `Statement`-shaped IAM policy
148
- # entry granting invoke on each target's function ARN, ending in exactly one
149
- # trailing newline; an empty string if `targets` is empty
72
+ # @param targets [Array<String>] domain names declared by `across:`
73
+ # @param base [String] indentation of the marker line
74
+ # @return [String] the YAML ending in one newline; empty if `targets` is empty
150
75
  def cross_domain_invoke_policy_yaml(targets, base)
151
76
  return "" if targets.empty?
152
77
 
@@ -173,32 +98,18 @@ module Hecks
173
98
  ].join("\n") + "\n"
174
99
  end
175
100
 
176
- # Renders one domain's own private VPC, subnets, RDS/Aurora Postgres
177
- # instance, and the two security groups pairing it with its compute
178
- # — everything `Lambda` and `Fargate` both need to give a
179
- # non-`"Shared"`-mode domain a real, reachable Postgres endpoint.
180
- # Dedented flush-left, the same shape a caller's own enclosing
181
- # `<<~` heredoc expects to reindent as it splices this in.
101
+ # Renders a domain's private VPC, subnets, RDS/Aurora Postgres and the two
102
+ # security groups pairing it with its compute, dedented flush-left.
103
+ # The compute group has no inbound rule; it only needs egress to the database.
182
104
  #
183
- # `compute_logical_id`'s own security group carries no inbound rule
184
- # at all — neither a Lambda function nor a Fargate task/`ALB` target
185
- # group receives traffic over this VPC-attached `ENI` directly, only
186
- # egress to the database (and, when `google_oauth_present`, to the
187
- # internet for a real OAuth token exchange).
188
- #
189
- # @param db_id [String] the RDS/VPC resources' own shared logical-id prefix
190
- # @param db_name [String] the database identifier RDS/Aurora provisions
191
- # @param infra_name [String] the domain's own AWS-facing name, quoted in the
192
- # `DBSubnetGroupDescription`
105
+ # @param db_id [String] shared logical-id prefix of the RDS/VPC resources
106
+ # @param db_name [String] database identifier RDS/Aurora provisions
107
+ # @param infra_name [String] AWS-facing domain name, quoted in the subnet group description
193
108
  # @param aurora [Boolean] Aurora Serverless v2 when true, plain RDS otherwise
194
- # @param google_oauth_present [Boolean] whether a NAT Gateway, public subnet, and
195
- # the compute security group's own internet egress rule are needed
196
- # @param compute_logical_id [String] the compute resource's own logical id,
197
- # naming its security group and egress rules
198
- # @param compute_description [String] the compute security group's own
199
- # `GroupDescription` text — what actually terminates the `ENI` this ingress
200
- # pairs with
201
- # @return [String] the rendered CloudFormation Resources, flush-left
109
+ # @param google_oauth_present [Boolean] whether to add a NAT Gateway and internet egress
110
+ # @param compute_logical_id [String] logical id naming the compute's security group
111
+ # @param compute_description [String] the compute security group's `GroupDescription`
112
+ # @return [String] the rendered CloudFormation Resources
202
113
  def vpc_and_database_yaml(db_id:, db_name:, infra_name:, aurora:, google_oauth_present:, compute_logical_id:, compute_description:)
203
114
  <<~OWNDB.rstrip
204
115
  #{db_id}Vpc:
@@ -466,25 +377,15 @@ module Hecks
466
377
  OWNDB
467
378
  end
468
379
 
469
- # Renders the temporary era-minting bastion — a standalone
470
- # CloudFormation template, deployed and destroyed by `make
471
- # mint-era`, never merged into the main stack. SSM Session Manager
472
- # only, never SSH: no key pair, no inbound security group rule at
473
- # all — the only way in is `aws ssm start-session`, itself gated by
474
- # the caller's own IAM permissions.
475
- #
476
- # Callers skip this entirely when `shared` — a Shared-mode domain
477
- # provisions no RDS/VPC of its own for a bastion to reach, and era-
478
- # minting for it reuses its owner's own already-standing bastion
479
- # path instead.
380
+ # Renders the temporary era-minting bastion: a standalone template deployed
381
+ # and destroyed by `make mint-era`. SSM Session Manager only, no inbound rules.
382
+ # Callers skip this when `shared`, as there is no RDS/VPC for it to reach.
480
383
  #
481
- # @param domain [String] the domain directory's path, named in the header comment
482
- # @param infra_name [String] the domain's own AWS-facing name
483
- # @param stack_name [String] the main stack's own name, tagged onto the bastion instance
484
- # @param db_id [String] the RDS/VPC resources' own shared logical-id prefix, as
485
- # `vpc_and_database_yaml` names them
486
- # @param google_oauth_present [Boolean] whether the main stack already has its own
487
- # public subnet/Internet Gateway to reuse, rather than minting a second one
384
+ # @param domain [String] domain directory path, named in the header comment
385
+ # @param infra_name [String] AWS-facing domain name
386
+ # @param stack_name [String] main stack name, tagged onto the instance
387
+ # @param db_id [String] shared logical-id prefix, as `vpc_and_database_yaml` names it
388
+ # @param google_oauth_present [Boolean] reuse the stack's public subnet, mint none
488
389
  # @param bastion_parameters [Array<Hash{Symbol => String}>] as `bastion_parameters` builds
489
390
  # @return [String] the rendered CloudFormation template
490
391
  def bastion_yaml(domain:, infra_name:, stack_name:, db_id:, google_oauth_present:, bastion_parameters:)