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
@@ -35,12 +35,9 @@ module Hecks
35
35
  @aggregate = aggregate
36
36
  @path = resolve_path(settings, root)
37
37
  @journal_path = "#{@path}.journal"
38
- @events = []
39
- # The optional saga-persistence capability's own scoping (§2/§4)
40
- # — falls back to the aggregate's own name for a directly-
41
- # instantiated adapter (specs), same fallback shape Postgres's
42
- # own @domain already uses.
43
- @domain = (
38
+ @events = []
39
+ # Saga scope; falls back to the aggregate's name for a directly built adapter.
40
+ @domain = (
44
41
  if settings.key?(:domain)
45
42
  settings[:domain]
46
43
  elsif settings.key?("domain")
@@ -81,13 +78,7 @@ module Hecks
81
78
 
82
79
  # Answers a declared query specification against the projected records.
83
80
  #
84
- # `registry: context[:registry]` — Memory's own `query` already
85
- # threads this through; Heki's own never did, which made
86
- # `none_in_state?` (Ports::Query::InMemory) unconditionally
87
- # return `true` (its own graceful "no registry, no way to look
88
- # the target up" default) for every `none_in_state` where-clause
89
- # against a Heki-backed aggregate — silently excluding nothing,
90
- # always, no matter the actual target state.
81
+ # The registry is passed through so `none_in_state` clauses can look up their target.
91
82
  #
92
83
  # @param specification [QuerySpecification::Common::Options,
93
84
  # Bluebook::Behaviour::ReadModel::FilteredOptions] the declared query specification
@@ -113,17 +104,11 @@ module Hecks
113
104
 
114
105
  # Applies one journaled entry to the current-state snapshot.
115
106
  #
116
- # Reads fresh rather than trusting the memoized `store` — under
117
- # `with_lock`, another process may have projected a snapshot since
118
- # this one last read it, and mutating *its* stale copy would
119
- # overwrite that write on disk rather than layer on top of it.
107
+ # Reads fresh, not the memoized `store`: another process may have projected since,
108
+ # and a stale copy would overwrite that write.
120
109
  #
121
- # On a delete, though, the record `current.delete` finds is already
122
- # gone: every caller (`#delete` above, `AppendOnly#delete`) appends
123
- # before it projects, so the fresh read above has already replayed
124
- # this very entry off the journal. What the memoized `store` last
125
- # held — from before this call — is read up front, for the return
126
- # value only; it plays no part in what gets written.
110
+ # On a delete the fresh read has already replayed the entry, so the returned record
111
+ # comes from the memoized `store`, read up front.
127
112
  #
128
113
  # @param entry [Persistence::Entry] the save or delete to materialize
129
114
  # @return [Runtime::Instance, nil] on a save, the newly stored record; on a delete, the
@@ -178,10 +163,7 @@ module Hecks
178
163
  # @return [Array<Runtime::Event>] the adapter's in-memory event log, including `event`
179
164
  def record_event(event) = @events << event
180
165
 
181
- # ── the optional saga-persistence capability (§2) — Heki's own
182
- # shape (a sibling snapshot+journal file pair, `SagaStore`,
183
- # heki/saga_store.rb) rather than a table in a store this adapter
184
- # doesn't have.
166
+ # Checkpoints one saga instance in a sibling file pair (`SagaStore`).
185
167
  #
186
168
  # @param process_manager [String, Symbol] the process manager's name, compared as
187
169
  # `.to_s`
@@ -209,18 +191,15 @@ module Hecks
209
191
  saga_store.delete_saga(@domain, process_manager.to_s, correlation.to_s)
210
192
  end
211
193
 
212
- # Yields every checkpointed saga instance of this domain, for `Registry
213
- # #rehydrate_sagas!` to restore at boot.
194
+ # Yields every checkpointed saga instance of this domain, for boot-time rehydration.
214
195
  #
215
196
  # @yieldparam process_manager [String] the process manager's name
216
197
  # @yieldparam correlation [String] the instance's correlation value
217
198
  # @yieldparam state [String] the saga's state name
218
199
  # @yieldparam memory [Hash{Symbol => Object}] the saga's memory, Symbol keys at every
219
200
  # depth
220
- # @yieldparam completed_compensations [Array] the completed-compensation ledger, `[]`
221
- # when none was recorded
222
- # @return [Enumerator, Hash{String => Hash}] an enumerator over the same five values
223
- # when no block is given; otherwise `SagaStore`'s internal records Hash
201
+ # @yieldparam completed_compensations [Array] the completed-compensation ledger
202
+ # @return [Enumerator, Hash{String => Hash}] an enumerator when no block is given
224
203
  def each_saga(&) = saga_store.each_saga(@domain, &)
225
204
 
226
205
  private
@@ -229,8 +208,6 @@ module Hecks
229
208
  @saga_store ||= SagaStore.new(File.dirname(@path))
230
209
  end
231
210
 
232
- # `record` is the snapshot/journal's own string-keyed JSON — decoded
233
- # deep through the state codec, never symbolized one level by hand.
234
211
  def instance(id, record)
235
212
  Runtime::Instance.new(
236
213
  aggregate: @aggregate,
@@ -248,13 +225,7 @@ module Hecks
248
225
  replay_journal(snapshot)
249
226
  end
250
227
 
251
- # `dir: :default` — a bare Symbol, the framework's own convention
252
- # for "a declared value that resolves by convention, never a silent
253
- # fallback" — would otherwise crash `File.join` outright
254
- # (`TypeError: no implicit conversion of Symbol into String`):
255
- # `resolve_path` only ever checked for a missing `dir` setting,
256
- # never a Symbol one. Treated the same as no setting at all — falls
257
- # back to the existing "data" default, not a new special case.
228
+ # `dir: :default` means no setting; a Symbol would make `File.join` raise TypeError.
258
229
  def resolve_path(settings, root)
259
230
  declared =
260
231
  if settings.key?(:dir)
@@ -1,22 +1,13 @@
1
1
  module Hecks
2
2
  module Adapters
3
- # The `identity_resolution` port, fulfilled by the Identity framework
4
- # bluebook — same registry, same boot, so this is a dispatch against
5
- # records already sitting in the store this adapter is handed, not a
6
- # bridge to a second runtime. Same reasoning `GovernanceAuthorization`
7
- # gives for itself, one port over.
3
+ # The `identity_resolution` port, fulfilled by the Identity framework bluebook
4
+ # through a dispatch on the same registry.
8
5
  module IdentityRegistry
9
6
  module_function
10
7
 
11
8
  # Looks up the id of the identity an authenticated (issuer, subject) pair is linked to,
12
9
  # querying the Identity framework bluebook's own `ResolvedBy`.
13
10
  #
14
- # `nil` for a pair nothing has linked, the first match otherwise —
15
- # `ResolvedBy` is a lookup by the exact (issuer, subject) an
16
- # authenticated token carries, not a listing, so more than one row
17
- # would mean two links share a pair, which `Link`'s own natural
18
- # key already prevents by construction.
19
- #
20
11
  # @param registry [Runtime::Registry] the booted registry, queried for the linked
21
12
  # identity's id
22
13
  # @param issuer [String] the OIDC issuer that authenticated the caller, compared as a
@@ -24,9 +15,7 @@ module Hecks
24
15
  # @param subject [String] the OIDC subject the issuer vouches for, compared as a String
25
16
  # @return [String, nil] the linked identity's id, or nil if nothing has linked this pair
26
17
  def resolve(registry, issuer:, subject:)
27
- # The resolve verb this registry's declared identity provider names
28
- # (`provides "identity", resolve:`), never a hard-coded chapter —
29
- # same declared-not-named shape GovernanceAuthorization already holds.
18
+ # The verb comes from the declared identity provider (`provides "identity", resolve:`).
30
19
  provider = registry.bluebooks.values.find { |chapter| chapter.provides?(::Hecks::Bluebook::Capabilities::IDENTITY) }
31
20
  verb = provider&.provided_verb(::Hecks::Bluebook::Capabilities::IDENTITY, :resolve) ||
32
21
  "Identity::ExternalIdentifier.ResolvedBy"
@@ -6,32 +6,20 @@ require_relative "../../runtime/errors"
6
6
 
7
7
  module Hecks
8
8
  module Adapters
9
- # Shared by every adapter that holds decoded Ruby records rather than
10
- # running real SQL (Memory, Lambda, Heki) — the identical dotted-path
11
- # value-object member-picking Postgres/Sqlite/D1's own
12
- # order_expression does (numeric member wins, else the one-field
13
- # convention "value"), just walked in Ruby instead of compiled to a
14
- # JSONB/json_extract path, because there is no query engine
15
- # underneath any of these three to hide that walk inside.
9
+ # Ordering for adapters that hold decoded Ruby records (Memory, Lambda, Heki),
10
+ # mirroring the SQL adapters' value-object member picking.
16
11
  module InMemoryOrdering
17
12
  module_function
18
13
 
19
- # Orders decoded records by a declared attribute, then by id as the total-order
20
- # tiebreaker.
14
+ # Orders decoded records by a declared attribute, then by id as the tiebreaker.
21
15
  #
22
- # order_by is a runtime value (an HTTP query param, in the
23
- # console's case), not framework-authored bluebook source — see
24
- # postgres.rb's own all for the full reasoning. Whitelisted the
25
- # identical way before FieldPath.dig ever runs.
16
+ # `order_by` may come from an HTTP query param, so it is checked before `FieldPath.dig`.
26
17
  #
27
18
  # @param records [Array<Runtime::Instance>] the records to order
28
- # @param aggregate [Bluebook::Aggregate] the aggregate `records` belong to, checked for
29
- # `order_by`'s attribute
30
- # @param order_by [String, Symbol, nil] a dotted attribute path to sort by; nil returns
31
- # `records` unchanged
19
+ # @param aggregate [Bluebook::Aggregate] the aggregate `records` belong to
20
+ # @param order_by [String, Symbol, nil] a dotted attribute path; nil returns `records` as-is
32
21
  # @param direction [Symbol] `:asc` or `:desc`
33
- # @return [Array<Runtime::Instance>] `records`, ordered by `order_by` then id; unchanged
34
- # when `order_by` is nil
22
+ # @return [Array<Runtime::Instance>] `records`, ordered by `order_by` then id
35
23
  # @raise [Runtime::WiringError] if `order_by` names no attribute of `aggregate` and is
36
24
  # not its lifecycle field
37
25
  def ordered(records, aggregate:, order_by:, direction:)
@@ -68,13 +56,8 @@ module Hecks
68
56
  vo = aggregate.value_object(attribute.type)
69
57
  return field.to_s unless vo
70
58
 
71
- # Numeric member first, then the sole attribute whatever it is
72
- # named (single-attribute value objects strictly answer `.value`
73
- # — the same generalization `SqlQueryBuilder#query_expression`
74
- # makes for the column side, kept in lockstep so Memory and SQL
75
- # order the identical rows identically), and only then the bare
76
- # `value` convention — now purely a backstop for the multi-field
77
- # non-numeric shape neither rule can honestly pick a field for.
59
+ # Numeric member, else the sole attribute, else `value`; kept in lockstep with
60
+ # `SqlQueryBuilder#query_expression` so Memory and SQL order rows identically.
78
61
  member = vo.attributes.find { |a| %w[Integer Float].include?(a.type) }&.name ||
79
62
  vo.sole_attribute&.name || "value"
80
63
  "#{name}.#{member}"
@@ -2,20 +2,8 @@ require "securerandom"
2
2
 
3
3
  module Hecks
4
4
  module Adapters
5
- # The real `key_vault` fulfillment for a single running process — a
6
- # table of key material, keyed by an opaque reference never exposed to
7
- # a bluebook or an event. Cryptoshredding only works if the key
8
- # genuinely leaves memory when destroyed: `destroy` deletes the Hash
9
- # entry outright, not merely a flag, so a `fetch` afterward has
10
- # nothing left to decrypt with.
11
- #
12
- # **Not crash-durable** — a process restart loses every key this
13
- # adapter ever issued, along with every ciphertext under it. A
14
- # deployment that needs destruction to survive a restart (or to leave
15
- # a physical-destruction audit trail) backs this port with a real
16
- # `KMS` or `HSM` instead; this default asks for nothing external to
17
- # run, the same tradeoff `SecureRandomIdentity` already makes for
18
- # identity minting.
5
+ # In-process `key_vault` fulfillment: key material held under opaque references.
6
+ # Not crash-durable; use a KMS or HSM when destruction must survive a restart.
19
7
  module InProcessKeyVault
20
8
  module_function
21
9
 
@@ -3,48 +3,13 @@ require "json"
3
3
  module Hecks
4
4
  module Adapters
5
5
  class Lambda
6
- # **The thin AWS client** — shared by this adapter's own read methods
7
- # (Lambda#find/#all/#count/#query) and Runtime::RemoteDispatcher's
8
- # write methods. One Lambda invoke, one JSON round trip, nothing
9
- # domain-specific: neither caller needs to know an AWS SDK is
10
- # involved at all.
6
+ # The thin AWS client shared by this adapter's read methods and
7
+ # Runtime::RemoteDispatcher's writes: one Lambda invoke, one JSON round trip.
11
8
  #
12
- # ## Resolving the function name
13
- #
14
- # Function name is computed unless it is named — `"hecks-#{domain}"`,
15
- # lowercased, matches bin/project_deploy's own `stack_name`
16
- # exactly (bin/project_deploy: `stack_name = "hecks-#{domain_name}"`,
17
- # `domain_name = File.basename(domain)`). `domain` here is the
18
- # bluebook's own declared name (`Embryonaut`, not the directory);
19
- # today's real corpus has directory name == declared name
20
- # lowercased for every domain that deploys, so `.downcase` alone
21
- # reproduces the same string bin/project_deploy computes from the
22
- # directory.
23
- #
24
- # ## When the assumption breaks
25
- #
26
- # That assumption is not always true, and when it breaks nothing
27
- # about it is recoverable from here. `bin/project_deploy` honours
28
- # a `.world`'s own `stack_prefix`/`stack_name` — settings that
29
- # exist precisely so a domain whose AWS identity predates a rename
30
- # keeps targeting the stack that is actually live rather than
31
- # standing up a second, empty one beside it. A real one:
32
- # a client site deploys under a fixed stack name, and no
33
- # value of `domain` (or of `DOMAIN_NAME`, the deployed-Lambda
34
- # override callers already pass) can make this computation produce
35
- # a name with no dash after "hecks". Every Ruby-side read and
36
- # dispatch for that app has therefore been invoking a function
37
- # that does not exist — found live, and visible in the deployed
38
- # journal having never received a single row.
39
- #
40
- # ## Naming it explicitly
41
- #
42
- # So the name can be named, in the one place the rest of this
43
- # deployment is already described: the `.world`'s own
44
- # `persisted_by("Lambda")`/`dispatched_by("Lambda")` block, beside
45
- # `region`. Given, it wins outright; absent, the computation above
46
- # is unchanged, which is every domain whose stack name was never
47
- # pinned.
9
+ # The function name defaults to `"hecks-#{domain.downcase}"`, matching
10
+ # `bin/project_deploy`'s `stack_name` computation. A domain whose stack
11
+ # name doesn't follow that pattern must pass `function` explicitly, via
12
+ # a `.world`'s `persisted_by("Lambda")`/`dispatched_by("Lambda")` block.
48
13
  class Client
49
14
  # @param domain [String, Symbol] the bluebook's own declared domain name; computes
50
15
  # the function name when `function` is not given
@@ -58,20 +23,14 @@ module Hecks
58
23
  @client = Aws::Lambda::Client.new(region: region)
59
24
  end
60
25
 
61
- # The function this client actually invokes — read by
62
- # `Runtime::WiringError` messages and worth asserting on
63
- # directly, since "which function did we call" is otherwise
64
- # unanswerable from outside.
26
+ # The function this client actually invokes; used in `Runtime::WiringError`
27
+ # messages and worth asserting on directly in tests.
65
28
  attr_reader :function_name
66
29
 
67
30
  # Reads the whole domain's current state and event log from the remote function.
68
31
  #
69
- # **The whole domain, every time** — matches dispatch::read's own
70
- # rehydrate-the-full-journal design (Phase 1, rust/host). No
71
- # caching here, deliberately: memoizing this across calls once
72
- # silently served stale reads for a warm web process's whole
73
- # lifetime after a write happened elsewhere — see Lambda#instances's
74
- # own comment on the real, live bug that caught it.
32
+ # No caching here, deliberately: a memoized read would go stale the
33
+ # moment a write happens elsewhere in a long-lived process.
75
34
  #
76
35
  # @return [Hash{String => Object}] the parsed JSON response; the deployed function's
77
36
  # own top-level keys (`"instances"`, `"events"`, …)
@@ -6,24 +6,8 @@ require_relative "../../runtime/instance"
6
6
 
7
7
  module Hecks
8
8
  module Adapters
9
- # **The read-side half of lambda routing** — `persisted_by("Lambda")`'s
10
- # own adapter, resolved through the same `Ports::Persistence`
11
- # machinery `persisted_by("Postgres")`/`persisted_by("Memory")`
12
- # already use (`Runtime::Registry#repository`, `RepositoryFactory
13
- # .build`) — no new framework plumbing, just a new adapter class at
14
- # the name the existing lookup already expects. See
15
- # `Runtime::RemoteDispatcher` for the write-side half; the two
16
- # compose through one shared `Client` per domain rather than each
17
- # inventing its own AWS wiring.
18
- #
19
- # **Read-only, deliberately** — `append`/`project` raise rather than
20
- # silently no-op. A write reaching this class would mean
21
- # `Runtime::CommandInterpreter` ran locally against a Lambda-routed
22
- # domain, which is exactly the bypass `Runtime::RemoteDispatcher`
23
- # exists to prevent (business-rule validation must happen in Rust
24
- # for these domains, not be pre-computed here against an
25
- # incomplete local view). A loud failure here is the correct
26
- # outcome, not a bug to route around.
9
+ # Read-side adapter for `persisted_by("Lambda")`; writes go through Runtime::RemoteDispatcher.
10
+ # Read-only: `append`/`project` raise, since a local write would bypass remote validation.
27
11
  class Lambda
28
12
  include Ports::Persistence::RemoteRuntime
29
13
 
@@ -50,41 +34,11 @@ module Hecks
50
34
  aggregate.name
51
35
  end
52
36
  region = setting(settings, :region, "us-east-1")
53
- # Named, when this deployment's function isn't `hecks-<domain>`
54
- # — `Client`'s own comment has the real case that needs it.
55
- # Absent (every domain whose stack name was never pinned), the
56
- # computation below is exactly as it was.
37
+ # Explicit function name, for deployments whose function is not `hecks-<domain>`.
57
38
  function = setting(settings, :function, nil)
58
- # Two different "domain"s, deliberately not conflated: `domain`
59
- # (this aggregate's own bluebook name — "Identity", "Governance")
60
- # only ever prefixes the instances lookup, since that's how
61
- # rust/host's own `Store::instances()` keys every record
62
- # (`registry.rb`'s own `"#{a[:domain_name]}::#{a[:name]}#"`
63
- # dump format, unchanged by which chapter attached it). The
64
- # function to actually call is a different question: Governance
65
- # and Identity aggregates are compiled into the attaching
66
- # domain's own Lambda (one merged `Store` per target — Phase 0's
67
- # framework-bluebook work), never a function of their own, so
68
- # `settings[:domain]` is the wrong signal for `Client.new`.
69
- # `root` is the boot's own directory (`Registry#root`, shared by
70
- # every bluebook in one registry regardless of which one
71
- # attached it) — `File.basename(root)` reproduces the exact
72
- # same string `bin/project_deploy`'s own `stack_name` computes
73
- # from the domain path, so the two can never name two
74
- # different functions for the same deploy... on a local boot,
75
- # where `root` is a real project directory. Inside the deployed
76
- # Lambda itself `root` is always `/var/task` (every Lambda's own
77
- # fixed code root, regardless of domain) — `File.basename` gives
78
- # "task", not the domain name, and invokes the wrong function
79
- # entirely. A real, live AccessDeniedException on
80
- # "hecks-task" caught this: invisible through every earlier
81
- # phase's own verify step, all run from a local boot, until
82
- # WebFunction became the first Ruby process to ever make this
83
- # exact call from inside a deployed Lambda. `DOMAIN_NAME` (set
84
- # by bin/project_deploy's own WebFunction Environment) is the
85
- # real, unambiguous signal in that specific context; the
86
- # root-basename heuristic stays as the local-boot fallback,
87
- # unchanged.
39
+ # `domain` only prefixes the instances lookup; the function to call is named by
40
+ # DOMAIN_NAME (set in a deployed Lambda, where root is always /var/task), else by
41
+ # root's basename, which matches bin/project_deploy's stack name on a local boot.
88
42
  function_domain = ENV["DOMAIN_NAME"] || (root ? File.basename(root) : domain)
89
43
  @client = Client.new(domain: function_domain, region: region, function: function)
90
44
  @prefix = "#{domain}::#{aggregate.hecks_name}#"
@@ -128,21 +82,9 @@ module Hecks
128
82
  Ports::Query::InMemory.execute(instances.values, specification, args)
129
83
  end
130
84
 
131
- # `entries`/`append`/`project` — the append-only contract
132
- # `Ports::Persistence::AppendOnly` requires of every adapter
133
- # (raises at construction if any are missing). Answered by
134
- # `Ports::Persistence::RemoteRuntime`, included above: `entries` is
135
- # `[]` unconditionally (Lambda's own Postgres is already the
136
- # durable store — rust/host's rehydrate-and-replay journal, Phase
137
- # 1 — there is no local write-ahead log for `recover!` to replay),
138
- # `append`/`project` raise rather than silently no-op.
139
-
140
85
  # Reads a world setting under either a Symbol or a String key, with a fallback.
141
86
  #
142
- # A `.world` block's settings arrive symbol-keyed from the DSL and
143
- # string-keyed from a round-tripped export, so every read has to
144
- # accept both — one helper rather than the same five lines per
145
- # key.
87
+ # Settings arrive symbol-keyed from the DSL and string-keyed from a round-tripped export.
146
88
  #
147
89
  # @param settings [Hash] the world settings Hash to read from
148
90
  # @param key [Symbol] the setting name, tried as itself and as `key.to_s`
@@ -157,21 +99,8 @@ module Hecks
157
99
 
158
100
  private
159
101
 
160
- # Re-fetched every call, deliberately not memoized across them —
161
- # this adapter itself is long-lived (one instance per aggregate,
162
- # held by the registry `RUNTIME = Hecks.boot(...)` builds once
163
- # per Lambda web process — WebFunction's own top-level constant,
164
- # reused warm across every HTTP request that process serves, not
165
- # rebuilt per request the way memoizing here would wrongly assume).
166
- # A real, live bug caught this: a mutation
167
- # dispatched fine (RemoteDispatcher always calls the dispatch
168
- # Lambda fresh) and the very next `.all` on the same warm
169
- # container kept returning the state from before that mutation,
170
- # forever, until the container cold-started — memoizing here
171
- # made every write invisible to every read on a warm container.
172
- # Keyed by bare id (the part after "Domain::Aggregate#"), not the
173
- # full "Domain::Aggregate#id" string — callers already know which
174
- # aggregate they're asking this instance about.
102
+ # Never memoized: the adapter outlives requests on a warm container, so a cached read
103
+ # would hide every later write. Keyed by the bare id after "Domain::Aggregate#".
175
104
  def instances
176
105
  @client.read.fetch("instances", {}).filter_map do |key, state|
177
106
  next unless key.start_with?(@prefix)
@@ -180,10 +109,7 @@ module Hecks
180
109
  end.to_h
181
110
  end
182
111
 
183
- # Lambda's own JSON response is already Ruby-decoded with string
184
- # keys (plain `JSON.parse`, no `symbolize_names:`) — decoded through
185
- # the state codec (PR A3), the same IR-driven spelling every other
186
- # adapter's read produces.
112
+ # The response is string-keyed JSON; the state codec decodes it as every adapter's read does.
187
113
  def build_instance(id, state)
188
114
  Runtime::Instance.new(aggregate: @aggregate, id: id, state: Ports::Persistence::StateCodec.decode(@aggregate, state))
189
115
  end
@@ -5,36 +5,10 @@ require_relative "../../runtime/instance"
5
5
 
6
6
  module Hecks
7
7
  module Adapters
8
- # **A browser-hosted domain's own declared intent** — not a second Memory
9
- # wearing a different name. Ruby has no way to reach a real browser's
10
- # `window.localStorage` at all (it is per-tab, per-origin, JS-only,
11
- # unreachable over any network the way D1's own REST API is) — so
12
- # this Ruby-side adapter is honestly a stand-in: in-process, ephemeral,
13
- # mechanically identical to Memory. What earns it a name of its own
14
- # is what it declares, not what it happens to do in Ruby: `persisted_by
15
- # "LocalStorage"` says "this domain expects real, durable, single-
16
- # device storage the moment it's actually running where it's meant to
17
- # run" — the same distinction Heki (real local durability) already
18
- # draws against Memory (deliberately ephemeral, test/example-only),
19
- # one adapter over. `bin/console`, the fuzzer, `spec/`, `bin/
20
- # model_check` all get a domain that boots and behaves correctly
21
- # against this adapter; only a real browser gets the real durability.
22
- #
23
- # The real browser half lives outside this file entirely: `rust/web`'s
24
- # `dispatch(json)` (docs/implemented/decisions/0015) takes an optional
25
- # `"seed"` (the exact `"instances"` shape it also answers with) plus
26
- # `"steps"` — a host rehydrates from a prior snapshot and replays only
27
- # the new command(s), rather than the whole history every call. A
28
- # page bound to this adapter is expected to hold that snapshot in
29
- # `window.localStorage` itself (get on load, set after every
30
- # `dispatch`) — the seed/instances round trip is the adapter, once
31
- # you're in the one runtime that can actually reach the storage this
32
- # name promises.
8
+ # Declares browser-local storage; in Ruby it behaves like Memory (in-process, ephemeral).
9
+ # Only a real browser page, holding the rust/web `dispatch` snapshot, gets durability.
33
10
  class LocalStorage
34
- # Tenant-capable trivially, same reasoning as Memory's own — a
35
- # browser tab is exactly one origin, exactly one user; there is no
36
- # second tenant this in-process Hash could ever confuse a first
37
- # one with.
11
+ # Trivially tenant-capable, as Memory is: a browser tab is one origin and one user.
38
12
  #
39
13
  # @return [Boolean] true, always
40
14
  def self.tenant_capable? = true
@@ -79,10 +53,7 @@ module Hecks
79
53
 
80
54
  # Answers a declared query specification against the projected records.
81
55
  #
82
- # The decision the guide asks for, made explicitly: no compiled
83
- # dialect of its own, same as Heki/Memory — a personal-scale local
84
- # store answering by walking `all` is correct on day one, and
85
- # nothing about a browser tab's own data volume asks for pushdown.
56
+ # No compiled dialect, as with Heki and Memory: walking `all` suffices at tab-sized volumes.
86
57
  #
87
58
  # @param specification [QuerySpecification::Common::Options,
88
59
  # Bluebook::Behaviour::ReadModel::FilteredOptions] the declared query specification
@@ -181,13 +152,7 @@ module Hecks
181
152
 
182
153
  def copy(state) = Ports::Persistence::StateCodec.copy(@aggregate, state)
183
154
 
184
- # Not lineage_capable? — deliberately absent, the same trade Heki
185
- # makes and states plainly (writing-an-adapter.md's own section on
186
- # it): a domain bound here has no edge for its own shape to travel
187
- # across if it ever changes; that must be hand-migrated, or the
188
- # shape must not change. A browser-local personal store is exactly
189
- # the small-adapter case that guide names as a fine place to make
190
- # that trade.
155
+ # Deliberately not lineage_capable?, as with Heki: a shape change must be hand-migrated.
191
156
  end
192
157
  end
193
158
  end