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
data/lib/hecks/forms.rb CHANGED
@@ -1,41 +1,13 @@
1
- # The forms surface: `expose` (the DSL a `command_form.bluebook`/
2
- # `query_form.bluebook` will eventually become — see docs/
3
- # command-form-and-query-form-bluebook.md), the IR->HTML renderers, and
4
- # the Rack app that content-negotiates between them and a plain JSON
5
- # reading of the same dispatch. Not required by `require "hecks"`
6
- # itself — a project that never boots this file never pays for `rack`,
7
- # the same lazy-dependency discipline the Gemfile's own comment already
8
- # holds `pg`/`oauth2`/`aws-sdk-lambda` to.
9
- #
10
- # See docs/command-form-and-query-form-bluebook.md for the design this
11
- # implements and what it deliberately leaves for later.
1
+ # The forms surface: `expose`, the HTML renderers and the Rack app serving them.
2
+ # Not loaded by `require "hecks"`, so `rack` stays a lazy dependency.
12
3
  require "hecks"
13
4
  require_relative "forms/app"
14
5
 
15
6
  module Hecks
16
- # Per-chapter `expose` configuration, keyed by chapter name and looked up
17
- # by the Rack app at request time (`.config`) to decide which chapters'
18
- # forms are switched on at all — see the file header above for what
19
- # `expose` deliberately does and does not cover.
7
+ # Per-app `expose` configuration, looked up by the Rack app at request time.
20
8
  module Forms
21
- # `expose`'s own declaration — "which chapters does this app expose" —
22
- # kept outside the `Hecks.*` collector convention (`Hecks.bluebook`,
23
- # `Hecks.hecksagon`, ...) and outside `Runtime::Registry` entirely, on
24
- # purpose: a real language word goes through `syntax.bluebook` and
25
- # `MetaValidator` (see docs/implemented/guides/extending-hecks.md, "a new word is a
26
- # declared row before it is a line of Ruby") and is judged by
27
- # spec/syntax_conformance_spec.rb + spec/dsl_coverage_spec.rb — gates
28
- # this construct has not earned yet. An ordinary Ruby DSL, one level of
29
- # module state, no different in kind from a project's own initializer —
30
- # see docs/command-form-and-query-form-bluebook.md, "why this isn't
31
- # syntax.bluebook yet".
32
- #
33
- # One `expose` grants a whole chapter, not a command or a query
34
- # individually — the future `command_form.bluebook`/`query_form.bluebook`
35
- # words are per-declaration (one command, one query, its own form/view,
36
- # possibly its own overrides), which `expose` doesn't do today and was
37
- # never trying to; it's the coarse "turn this chapter's forms on at
38
- # all" switch those finer words will eventually sit inside.
9
+ # The chapters one app exposes. Plain module state, outside `syntax.bluebook` and
10
+ # the registry; one `expose` switches on a whole chapter, not a single command or query.
39
11
  class Config
40
12
  attr_reader :name, :exposes
41
13
 
data/lib/hecks/fqn.rb CHANGED
@@ -24,10 +24,9 @@ module Hecks
24
24
  new(realm: realm, domain: domain, version: version, aggregate: aggregate, verb: command, kind: :command)
25
25
  end
26
26
 
27
- # Builds the FQN of one query, either on an aggregate or domain-level.
27
+ # Builds the FQN of one query, on an aggregate or domain-level.
28
28
  #
29
- # @param realm [String, nil] deployment identity from the world, or nil for an
30
- # unrealmed address
29
+ # @param realm [String, nil] deployment identity from the world, or nil if unrealmed
31
30
  # @param domain [String] the domain name
32
31
  # @param query [String] the query's snake_case verb
33
32
  # @param aggregate [String, nil] the aggregate the query belongs to, or nil for
@@ -41,12 +40,8 @@ module Hecks
41
40
  new(realm: realm, domain: domain, version: version, aggregate: aggregate, verb: query, kind: :query)
42
41
  end
43
42
 
44
- # One order-dependent parse pipeline: split -> shape-validate -> dispatch
45
- # on segment count -> split domain/version -> classify kind -> cross-
46
- # field validate -> construct. Each step consumes locals (segments, verb,
47
- # kind) the step before it derived; splitting would mean threading all of
48
- # them back out as parameters/returns between new methods, for no
49
- # readability gain over reading the pipeline top to bottom once.
43
+ # Parses a `Realm::Domain@version::Aggregate.verb` address.
44
+ # One method on purpose: each step consumes locals the step before it derived.
50
45
  #
51
46
  # @param text [String] a `Realm::Domain::Aggregate.verb` address, with domain
52
47
  # optionally `@version`-pinned and aggregate optional for a domain-level query
@@ -65,9 +60,7 @@ module Hecks
65
60
 
66
61
  realm, domain_spec, aggregate = case segments.length
67
62
  when 3 then segments
68
- # A two-segment lowercase address is the
69
- # public realm/domain form for a
70
- # domain-level read model.
63
+ # realm::domain.query is a domain-level read model
71
64
  when 2 then query_name?(verb) ? [segments[0], segments[1], nil] : [nil, *segments]
72
65
  else [nil, segments.first, nil]
73
66
  end
@@ -2,46 +2,16 @@ Hecks.bluebook "Compliance" do
2
2
  vision "Something elsewhere already acted to contain a risk; this domain tracks the human review that decides what happens next."
3
3
  supporting
4
4
 
5
- # LIVES HERE (lib/hecks/framework/bluebook/), NOT under some
6
- # domain's own examples/ directory, but that's a statement about
7
- # SOURCE SHARING, not about how it's consumed — unlike Governance/
8
- # Identity (which every real attaching domain only ever wants IN-
9
- # PROCESS), Compliance genuinely supports BOTH modes from the same
10
- # source: a domain that wants Review handled locally can
11
- # `uses_framework "Compliance"` exactly like those two; a domain that
12
- # wants it as a real, separately-deployed service — Banking's own
13
- # choice, examples/compliance/ — reaches it through `across
14
- # "Compliance"` on a policy instead, genuine cross-Lambda delivery.
15
- # Nothing about this bluebook's own declarations changes between the
16
- # two; only whether the CONSUMER merges it in or deploys it apart.
5
+ # Supports both modes from one source: `uses_framework "Compliance"` merges it in-process,
6
+ # or a separately deployed service is reached with `across "Compliance"` on a policy.
7
+ # examples/compliance/bluebook/compliance.bluebook symlinks here, since a symlink pointing
8
+ # outside lib/ does not survive `gem build`.
17
9
  #
18
- # examples/compliance/bluebook/compliance.bluebook is a SYMLINK back
19
- # to this file (not the other way around, as it briefly was in
20
- # 1.5.0) — a symlink pointing outside lib/ never survives `gem
21
- # build` (RubyGems drops it, warning "not supported on all
22
- # platforms"), so the real content has to live in the tree the
23
- # gemspec actually packages.
10
+ # Contain first, investigate second: the containing action (a freeze, a box surrender) has
11
+ # already happened elsewhere. This domain is told the fact and records the review after it.
24
12
  #
25
- # CONTAIN FIRST, INVESTIGATE SECOND — the real pattern this domain
26
- # exists for. Banking's own "Compliance officer" role already acts
27
- # directly and synchronously in three places (Customer.Suspend/
28
- # Reinstate, Account.Freeze/Unfreeze, OnboardingCase.Clear/Decline) —
29
- # this domain is deliberately NOT a fourth. By the time either
30
- # aggregate below is opened, the containing action (a freeze, a box
31
- # surrender) has ALREADY happened, unilaterally, elsewhere; this
32
- # domain never asks permission, it is TOLD a fact and gives the same
33
- # officer a place to record what they decided about it afterward.
34
- #
35
- # TWO AGGREGATES, NOT ONE — found live, designing this: Banking's own
36
- # `ReviewOnFreeze`/`ReviewOnBoxSurrender` policies forward their
37
- # triggering event's payload VERBATIM (neither declares a `with:`
38
- # projection, which policies have gained since), and the two events
39
- # carry genuinely different fields (`AccountFrozen`: `number`;
40
- # `BoxSurrendered`: `branch_code` + `box_number`). One shared
41
- # aggregate would need an artificial union of both shapes — and while
42
- # `with:` could now reshape either side into it, that would be
43
- # inventing a sameness the domain does not have. Two small aggregates,
44
- # each identified by its own subject's own natural key, need neither.
13
+ # One aggregate per triggering event: the events carry different fields, so a shared
14
+ # aggregate would need an artificial union of the shapes.
45
15
 
46
16
  aggregate "AccountFreezeReview" do
47
17
  description "The compliance review a frozen account gets, opened the moment Banking::Account.Freeze already happened."
@@ -60,12 +30,8 @@ Hecks.bluebook "Compliance" do
60
30
  transition "Escalate" => "escalated", from: "open"
61
31
  end
62
32
 
63
- # SYSTEM-TRIGGERED, matching `AccountFrozen`'s own payload shape
64
- # field for field — `Banking::Account.AccountFrozen`'s `number` is
65
- # the ONLY field this ever receives (ReviewOnFreeze's own `on
66
- # "Account.AccountFrozen"`), so this declares exactly that and
67
- # nothing more; an unrecognized field would refuse at the JSON
68
- # boundary rather than silently drop it.
33
+ # System-triggered. Takes only `number`, the one field `AccountFrozen` carries; an
34
+ # unrecognized field is refused at the JSON boundary rather than dropped.
69
35
  command "Open" do
70
36
  role "System"
71
37
  goal "Register that a frozen account needs compliance review"
@@ -117,9 +83,7 @@ Hecks.bluebook "Compliance" do
117
83
  transition "Escalate" => "escalated", from: "open"
118
84
  end
119
85
 
120
- # Matches `BoxSurrendered`'s own payload field for field
121
- # (`ReviewOnBoxSurrender`'s own `on SafeDepositBox::BoxSurrendered`) — the same
122
- # discipline `AccountFreezeReview.Open` holds itself to, above.
86
+ # Takes exactly the fields `BoxSurrendered` carries, as `AccountFreezeReview.Open` does.
123
87
  command "Open" do
124
88
  role "System"
125
89
  goal "Register that a surrendered box needs compliance review"
@@ -149,18 +113,9 @@ Hecks.bluebook "Compliance" do
149
113
  end
150
114
  end
151
115
 
152
- # A THIRD SHAPE, NOT A UNION OF THE OTHER TWO — the same restraint the
153
- # header above holds Banking's own two events to. `Privacy::Marking`'s
154
- # own `Marked` event carries `domain`/`attribute_path`, matching
155
- # neither `AccountFrozen` (`number`) nor `BoxSurrendered`
156
- # (`branch_code`/`box_number`), so this gets its own aggregate rather
157
- # than an artificial union.
158
- #
159
- # REACHED VIA `translates`, NOT a `policy` this bluebook declares —
160
- # which foreign domain's event a consumer conforms to is a wiring
161
- # decision (see `HecksagonBuilder#translates`'s own header), so the
162
- # reaction lives in whichever `.hecksagon` attaches both Privacy and
163
- # Compliance, not here.
116
+ # A third shape, not a union of the other two: `Privacy::Marking`'s `Marked` event carries
117
+ # `domain`/`attribute_path`. Reached via `translates` in whichever `.hecksagon` attaches
118
+ # both Privacy and Compliance, since which foreign event to conform to is wiring.
164
119
  aggregate "PrivacyReview" do
165
120
  description "The compliance review a newly marked sensitive field gets, opened the moment Privacy::Marking.Mark already happened."
166
121
 
@@ -184,12 +139,8 @@ Hecks.bluebook "Compliance" do
184
139
  transition "Escalate" => "escalated", from: "open"
185
140
  end
186
141
 
187
- # SYSTEM-TRIGGERED, matching only `Privacy::Marking`'s own identity
188
- # fields — the `translates` block that reacts to `Marked` forwards
189
- # just `domain`/`attribute_path`, the same restraint
190
- # `AccountFreezeReview.Open`/`BoxSurrenderReview.Open` already hold
191
- # themselves to above; `category`/`readable_by` stay on the
192
- # marking itself, not duplicated here.
142
+ # System-triggered. Takes only `Marking`'s identity fields; `category`/`readable_by` stay
143
+ # on the marking.
193
144
  command "Open" do
194
145
  role "System"
195
146
  goal "Register that a newly marked sensitive field needs compliance review"
@@ -2,80 +2,38 @@ Hecks.bluebook "ConsoleSettings" do
2
2
  vision "A generic admin console's own curated UI facts — nav, columns, state tones, which rows a detail panel shows — as real dispatched data instead of a hand-rewritten YAML file. A UI opinion the DOMAIN it's pointed at has no way to answer, never a business record; see the console app's own presentation_config.rb for the fuller story."
3
3
  supporting
4
4
 
5
- # A FRAMEWORK MEMBER LIKE IDENTITY/GOVERNANCE — attach with
6
- # `uses_framework "ConsoleSettings"` in the consuming app's own
7
- # `.hecksagon`, plus a `Hecks.hecksagon "ConsoleSettings" do ... end`
8
- # persistence binding (Heki is the common case — a small, local,
9
- # per-app settings store, no shared database needed). This used to
10
- # boot in a SECOND, independent registry (the console app's own
11
- # console_settings_domain.rb) specifically so no consuming app needed
12
- # any change at all — that property is real and now given up on
13
- # purpose, in favor of the one binding mechanism every other
14
- # framework member already uses, rather than this being the one
15
- # exception with its own private boot path.
16
- #
17
- # NOT named "Forms" — Hecks::Forms (lib/hecks/forms/) already
18
- # names a different, real thing: a dormant prototype (the working half
19
- # of command_form.bluebook/query_form.bluebook, see docs/command-form-
20
- # and-query-form-bluebook.md) that renders raw forms/query views
21
- # straight off any IR, zero config. This chapter is the opposite kind
22
- # of surface — a CURATED, hand-authored dashboard's own settings — and
23
- # picks a name that doesn't read as the same one.
24
- #
25
- # SCOPE: everything presentation.yml carries EXCEPT a collection's own
26
- # `after_create` (a dispatch chain with a literal-or-$field-or-
27
- # count_query arg shape — a harder design problem that shouldn't gate
28
- # everything else; Ruby-side PresentationConfig keeps validating it,
29
- # same as always, with nowhere yet to persist it). `overview.stats`
30
- # (Overview, below) and every `extra_json`/`identity_extra_json`
31
- # field throughout this chapter were BOTH originally scoped out on an
32
- # "confirmed unused in any real presentation.yml" basis that turned
33
- # out to be checked against the wrong file — the console app's own
34
- # fixture, never this app's actual, live one. Migrating the real file
35
- # found both very much in use.
5
+ # A framework member like Identity/Governance: attach with
6
+ # `uses_framework "ConsoleSettings"` in the consuming app's own `.hecksagon`,
7
+ # plus a `Hecks.hecksagon "ConsoleSettings" do ... end` persistence binding.
8
+
9
+ # Named ConsoleSettings, not "Forms" — Hecks::Forms (lib/hecks/forms/) names a
10
+ # different thing, a raw form/query renderer straight off any IR. This chapter
11
+ # is a curated, hand-authored dashboard's own settings.
12
+
13
+ # Scopes everything presentation.yml carries except a collection's own
14
+ # `after_create` dispatch chain (a harder arg shape, still validated in Ruby).
15
+ # `overview.stats` and the `extra_json`/`identity_extra_json` fields below are
16
+ # real, in-use fields, confirmed against the app's actual presentation.yml.
36
17
 
37
18
  aggregate "StateStyle" do
38
19
  description "One aggregate's one lifecycle state, styled — a tone and whether it needs attention. Every real state gets a row, even a bare one (PresentationConfig's own missing_state_entries! check), so Declare and the two Set commands are separate: a bare row is a real, valid thing to have."
39
20
 
40
21
  # `agg`, not `aggregate` — the bluebook DSL's own word for what's being
41
- # declared right now; naming an attribute that would shadow it invites
42
- # exactly the confusion ui_schema.rb's own identity-collision fix
43
- # (see [[project_generic_founder_app]]) already had to work around
44
- # once for `id`.
22
+ # declared; an attribute of that name would shadow it, the same collision
23
+ # ui_schema.rb already had to work around once for `id`.
45
24
  attribute :agg, StateStyleText
46
25
  attribute :state, StateStyleText
47
26
  attribute :tone, one_of("good", "warn", "danger", "muted", "accent"), optional: true
48
27
 
49
28
  identified_by :agg, :state
50
- # `Flag`, wrapping "true"/"false" — not a bare `String` and not
51
- # `TrueClass`. Two confirmed-by-trying reasons neither works directly
52
- # on an aggregate/command attribute:
53
- # - `TrueClass`/`FalseClass` are real, pre-existing Ruby constants,
54
- # so writing them here resolves through NORMAL Ruby constant
55
- # lookup rather than ever reaching the DSL's own ConstShim (unlike
56
- # a bareword like `Flag`, which has no such constant and so DOES
57
- # reach it) — the meta-validator has no separate case for the
58
- # literal class objects, so it goes looking for a declared value
59
- # object named "TrueClass" and fails. `Attribute::PRIMITIVES`
60
- # lists them, but no real bluebook in this codebase spells one.
61
- # - A bare primitive (`String`, `Integer`) directly on an
62
- # AGGREGATE or COMMAND attribute fails the identical way — every
63
- # real example in this codebase only ever uses `String`/`Integer`
64
- # INSIDE a `value_object do...end` block, never as a top-level
65
- # attribute's own type; reproduced with a 4-line isolated bluebook
66
- # before trusting it as a real rule, not a guess.
67
- # PresentationConfig's own Ruby glue converts "true"/"false" at the
68
- # boundary.
29
+ # `Flag` wraps "true"/"false" as a value object: `TrueClass` resolves as a
30
+ # real Ruby constant rather than reaching ConstShim, and a bare `String`/
31
+ # `Integer` fails the same way directly on an aggregate/command attribute.
69
32
  attribute :attention, Flag, optional: true
70
33
 
71
- # WHATEVER A STATE ENTRY CARRIES THAT ISN'T tone/attention — a
72
- # display `label` (e.g. Client's own "active" reading "Active
73
- # client", not the raw state name) or a `note`, confirmed live as
74
- # real, in-use fields this schema didn't model. Serialized, not
75
- # decomposed — the same verified-then-opaque reasoning Overview's
76
- # own `where_json` already documents: PresentationConfig's own
77
- # reshape/write is where the real key set is enumerated and
78
- # subtracted from, in Ruby, not here.
34
+ # Carries whatever a state entry has beyond tone/attention (e.g. a display
35
+ # `label` or a `note`) — serialized, not decomposed; PresentationConfig's
36
+ # own reshape/write enumerates the real key set in Ruby, not here.
79
37
  attribute :extra_json, StateStyleExtra, optional: true
80
38
 
81
39
  value_object "StateStyleText" do
@@ -87,10 +45,8 @@ Hecks.bluebook "ConsoleSettings" do
87
45
  attribute :value, String
88
46
  end
89
47
 
90
- # A bare `String` fails directly on an aggregate/command attribute
91
- # (StateStyleText's own comment gives the confirmed-by-trying
92
- # reason) — this wraps extra_json's own opaque JSON text the same
93
- # way every other scalar field in this chapter already wraps one.
48
+ # Wraps extra_json's opaque JSON text — a bare `String` fails directly on
49
+ # an aggregate/command attribute (see the `Flag` comment above).
94
50
  value_object "StateStyleExtra" do
95
51
  attribute :value, String
96
52
  end
@@ -158,23 +114,17 @@ Hecks.bluebook "ConsoleSettings" do
158
114
  attribute :identity_strategy, one_of("slug", "sequence", "port"), optional: true
159
115
  attribute :identity_source, SettingsText, optional: true
160
116
  attribute :identity_prefix, SettingsText, optional: true
161
- # `pad` (a sequence strategy's own zero-padding width, e.g. "C-004"
162
- # vs "C-4") confirmed live as real, in-use — not modeled as its own
163
- # attribute for the same reason `identity_source`/`identity_prefix`
164
- # aren't a closed set either: whatever a real identity rule carries
165
- # beyond field/strategy/source/prefix, opaque, same as extra_json
166
- # below.
117
+ # `pad` (a sequence strategy's zero-padding width, e.g. "C-004" vs "C-4")
118
+ # is real but unmodeled here, same as identity_source/identity_prefix —
119
+ # carried opaquely in identity_extra_json below.
167
120
  attribute :identity_extra_json, CollectionExtra, optional: true
168
121
  attribute :columns, list_of(Column)
169
122
  attribute :detail_fields, list_of(DetailField)
170
123
  attribute :detail_field_columns, list_of(DetailFieldColumn)
171
124
  attribute :preconditions, list_of(Precondition)
172
125
  attribute :field_formats, list_of(FieldFormat)
173
- # WHATEVER A COLLECTION ENTRY CARRIES beyond the fields above —
174
- # `noun_sing`, `create_label`, `nav_group` confirmed live as real,
175
- # in-use fields this schema didn't model (10/8/2 real occurrences
176
- # respectively). Same verified-then-opaque reasoning as StateStyle's
177
- # own extra_json.
126
+ # Carries real, in-use fields this schema doesn't model (e.g. `noun_sing`,
127
+ # `create_label`, `nav_group`) — opaque, same as StateStyle's extra_json.
178
128
  attribute :extra_json, CollectionExtra, optional: true
179
129
 
180
130
  value_object "SettingsText" do
@@ -186,21 +136,15 @@ Hecks.bluebook "ConsoleSettings" do
186
136
  attribute :value, Integer
187
137
  end
188
138
 
189
- # A bare `String` fails directly on an aggregate/command attribute —
190
- # see StateStyleExtra's own comment. Shared by both extra_json and
191
- # identity_extra_json; both are opaque JSON text, no reason for two
192
- # separate wrapper types.
139
+ # Shared by extra_json and identity_extra_json — both opaque JSON text, no
140
+ # reason for two wrapper types. A bare `String` fails on an attribute directly.
193
141
  value_object "CollectionExtra" do
194
142
  attribute :value, String
195
143
  end
196
144
 
197
- # A list/table column. `sortable`/`sort_default` are optional — most
198
- # columns take the frontend's own default (sortable, no forced order).
199
- # `sort_default` stays a plain String, not a closed set — narrow
200
- # enough (and PresentationConfig.validate! already checks it in
201
- # Ruby) that a dedicated `one_of: [...]` (S3, ADR 0025 — the field's
202
- # own keyword now, no sibling value object or `admits:` indirection
203
- # needed) still isn't worth it for this one field.
145
+ # A list/table column. `sortable`/`sort_default` are optional (most columns
146
+ # take the frontend's default). `sort_default` stays a plain String rather
147
+ # than a closed `one_of` (ADR 0025) — PresentationConfig.validate! checks it.
204
148
  value_object "Column" do
205
149
  attribute :field, String
206
150
  attribute :sortable, String, optional: true # "true"/"false" — see StateStyle.attention's own comment
@@ -211,14 +155,9 @@ Hecks.bluebook "ConsoleSettings" do
211
155
  attribute :extra_json, String, optional: true
212
156
  end
213
157
 
214
- # One field the detail panel shows. `state_filter` only means anything
215
- # on an entity-backed rows field (ui_schema.rb's own apply_row_state_
216
- # filter checks this at read time, same as it does today) — nothing
217
- # here refuses a state_filter on the wrong shape of field, since that
218
- # check needs the APP's own live IR, which this domain can't see; it
219
- # stays a Ruby-side PresentationConfig.validate! check, same as always.
220
- # `display` is a plain String for the same nested-`one_of` collision
221
- # reason `sort_default` is, above.
158
+ # One field the detail panel shows. `state_filter` only applies to an
159
+ # entity-backed rows field; nothing here enforces that, since it needs the
160
+ # app's own live IR — PresentationConfig.validate! checks it in Ruby.
222
161
  value_object "DetailField" do
223
162
  attribute :field, String
224
163
  attribute :display, String, optional: true
@@ -229,10 +168,9 @@ Hecks.bluebook "ConsoleSettings" do
229
168
  attribute :extra_json, String, optional: true
230
169
  end
231
170
 
232
- # A rows-shaped detail field's own sub-column filter, FLATTENED to one
233
- # row per (field, column) pair rather than nested inside DetailField —
234
- # grouped back by `field` on the way out. The same flattening
235
- # Query::AskOption already uses for a compound option's own parts.
171
+ # A rows-shaped detail field's sub-column filter, flattened to one row per
172
+ # (field, column) pair and grouped back by `field` on the way out — the
173
+ # same flattening Query::AskOption uses for a compound option's parts.
236
174
  value_object "DetailFieldColumn" do
237
175
  attribute :field, String
238
176
  attribute :column, String
@@ -397,15 +335,9 @@ Hecks.bluebook "ConsoleSettings" do
397
335
  end
398
336
  end
399
337
 
400
- # THE DASHBOARD'S OWN STAT CARDS — domain-WIDE, not per-aggregate the
401
- # way Collection is (a stat can name any collection, or none in
402
- # particular), so this is a SINGLETON: exactly one row, ever, for the
403
- # whole console. `attribute :key, OverviewKey` holds a literal constant
404
- # ("overview") rather than anything a person chooses — the same reason
405
- # a creating command needs SOME identity to hydrate against (hydrate's
406
- # own "an identity that is invented is neither derived from the record
407
- # nor permanently associated with it" comment), just with the value
408
- # fixed instead of derived from what's being declared.
338
+ # Domain-wide, not per-aggregate: exactly one row, ever, for the whole
339
+ # console. `key` holds a literal constant ("overview") rather than anything
340
+ # chosen, since a creating command still needs some identity to hydrate against.
409
341
  aggregate "Overview" do
410
342
  description "The console's own dashboard stat cards, whole. One row, ever — `key` is always the literal 'overview', never chosen. Every stat gets replaced as a whole list on save, the same 'replace whole, never patch one' convention Collection's own columns/detail_fields/preconditions/field_formats already use."
411
343
 
@@ -420,20 +352,9 @@ Hecks.bluebook "ConsoleSettings" do
420
352
  invariant("is the one true singleton key") { value == "overview" }
421
353
  end
422
354
 
423
- # `where_json` carries the stat's own recursive filter expression
424
- # (`not:` wrapping another whole where clause, `state:`/
425
- # `has_related:`/`not_has_related:` combined by implicit AND) —
426
- # serialized, not decomposed into the bluebook's own closed
427
- # attribute grammar. That tree is genuinely open-ended (arbitrary
428
- # nesting depth via `not:`, an arbitrary literal attribute name as
429
- # a where key for anything that isn't `state`), and hecks's
430
- # attribute types have no way to express "arbitrary nested map" at
431
- # all — the same reason `sort_default`/`display`/`format` above stay
432
- # plain Strings rather than a nested `one_of`. PresentationConfig's
433
- # own `validate_where!`/`validate_has_related!` still walk and check
434
- # the REAL structure, in Ruby, against the app's own live IR, before
435
- # this is ever serialized and dispatched — this field is the
436
- # verified-then-opaque storage, not an unchecked one.
355
+ # `where_json` carries the stat's recursive filter expression, serialized
356
+ # rather than decomposed — the tree is open-ended and hecks's attribute
357
+ # types can't express it. PresentationConfig.validate_where! checks it in Ruby.
437
358
  value_object "Stat" do
438
359
  attribute :label, String
439
360
  attribute :collection, String
@@ -464,17 +385,9 @@ Hecks.bluebook "ConsoleSettings" do
464
385
  end
465
386
  end
466
387
 
467
- # BOTH ROOTLESS — bulk, no root record, no id argument at dispatch —
468
- # and grouped by an aggregate's own full identity, which is what makes
469
- # each leaf exactly one row rather than an arbitrary collision (see
470
- # `group_by`'s own reference doc). PresentationConfig.rb's own read
471
- # side (`reshape_states`/`reshape_collections`) dispatches these
472
- # instead of hand-rolling the unwrap/grouping in Ruby — see its own
473
- # header for what's still Ruby (the detail_field_columns/detail_fields
474
- # join, which isn't a report concern at all — two list attributes on
475
- # the SAME record, not two aggregates — and Overview's own stat cards,
476
- # which are read straight off Overview.find, a singleton needing
477
- # neither grouping nor unwrapping either).
388
+ # Both read models are rootless (bulk, no id at dispatch) and grouped by an
389
+ # aggregate's full identity, so each leaf is exactly one row. Dispatched from
390
+ # PresentationConfig's read side rather than hand-rolled in Ruby.
478
391
  read_model "Styles" do
479
392
  description "Every StateStyle row, nested by the aggregate and state it styles."
480
393
  include StateStyle
@@ -1,21 +1,13 @@
1
- # The framework members, bound so they can boot standing alone —
2
- # `Hecks.boot("lib/hecks/framework")` rather than only as something
3
- # another domain attached with `uses_framework`.
4
- #
5
- # `uses_framework` does NOT read this file (Framework.load! loads a
6
- # member's bluebook and nothing else, deliberately — see framework.rb),
7
- # so an attaching domain still declares its own binding for these
8
- # aggregates, exactly as before. This only answers the case where the
9
- # framework is the domain being booted.
1
+ # The framework members bound to Memory so they can boot standing alone with
2
+ # `Hecks.boot("lib/hecks/framework")`. `uses_framework` does not read this file.
10
3
  Hecks.hecksagon "Compliance" do
11
- # Every command here declares `role "Compliance officer"` — only real
12
- # access control once Governance can check it against something (S8).
4
+ # Every command declares `role "Compliance officer"`; the role check needs Governance.
13
5
  uses_framework "Governance"
14
6
  persisted_by "Memory"
15
7
  end
16
8
 
17
9
  Hecks.hecksagon "ConsoleSettings" do
18
- # Same reason as Compliance, above — S8.
10
+ # Same reason as Compliance.
19
11
  uses_framework "Governance"
20
12
  persisted_by "Memory"
21
13
  end
@@ -25,8 +17,7 @@ Hecks.hecksagon "Governance" do
25
17
  end
26
18
 
27
19
  Hecks.hecksagon "Identity" do
28
- # `Identity::Identity.Register` declares `role "Identity registrar"` —
29
- # same reason as Compliance/ConsoleSettings, above — S8.
20
+ # `Identity.Register` declares `role "Identity registrar"`; same reason as Compliance.
30
21
  uses_framework "Governance"
31
22
  persisted_by "Memory"
32
23
  end
@@ -2,10 +2,9 @@ Hecks.bluebook "Governance" do
2
2
  vision "Organizational authority is explicit, reviewable, and historically explainable."
3
3
  supporting
4
4
 
5
- # WHAT THIS CHAPTER ANSWERS FOR EVERY DOMAIN THAT ATTACHES IT. The role
6
- # check at dispatch, boot's refusal of an ungoverned `role`, the
7
- # authorization adapter and the fuzzer's grant steering all read these
8
- # three verbs from here, rather than recognising "Governance" by name.
5
+ # What this chapter answers for every attaching domain: the role check at dispatch, boot's
6
+ # refusal of an ungoverned `role`, the authorization adapter and the fuzzer's grant
7
+ # steering read these three verbs from here rather than recognising "Governance" by name.
9
8
  provides "authorization",
10
9
  assignments: "RoleAssignment.AssignmentsForActor",
11
10
  grant: "RoleAssignment.Assign",
@@ -14,12 +13,9 @@ Hecks.bluebook "Governance" do
14
13
  aggregate "RoleAssignment" do
15
14
  description "One actor holding one role, over one scope, from one point in time — revoked by setting when it ended, never deleted."
16
15
 
17
- # THE IDENTITY IS THE EVENT, not the (actor, role) pair — an actor can
18
- # hold the same role again later, after a prior assignment ended, and
19
- # that is a SECOND real assignment, not a state to overwrite. No UUID
20
- # adapter exists yet in this codebase to mint a synthetic id, and none
21
- # is needed: `starts_at` already makes "this actor started holding this
22
- # role at this time" unique on its own.
16
+ # The identity is the event, not the (actor, role) pair: an actor can hold a role again
17
+ # after a prior assignment ended, and that is a second assignment. `starts_at` makes each
18
+ # one unique.
23
19
  attribute :actor_id, IdentityId
24
20
  attribute :role_name, RoleName
25
21
  attribute :scope, Scope
@@ -78,21 +74,10 @@ Hecks.bluebook "Governance" do
78
74
  end
79
75
  end
80
76
 
81
- # WHO HOLDS A ROLE is `RoleAssignment`'s question. This is a different one:
82
- # may role X ACT AS role Y — the fact `act_as` (the plan's §2) needs before
83
- # a caller is allowed to dispatch under a role it does not itself hold.
84
- # Same event-log shape as RoleAssignment : a natural key, `ends_at` for
85
- # revocation, no delete — and, like RoleAssignment, THE IDENTITY IS THE
86
- # GRANT, not the (from_role, to_role) pair. `starts_at` used to be
87
- # missing from here — the one field RoleAssignment's own identity adds
88
- # beyond its own (actor, role) pair specifically so a second, later
89
- # assignment is a SECOND real record rather than a collision. Without
90
- # it, `identified_by :from_role, :to_role` alone made a revoked pair's
91
- # own identity permanently occupied: `Grant` is a creating command, the
92
- # same (from_role, to_role) pair is the SAME id forever, and a repeat
93
- # `Grant` after a `Revoke` was refused as `AlreadyExists` no matter how
94
- # long ago the prior grant ended — an absorbing "revoked" state this
95
- # very comment already claimed didn't exist.
77
+ # Whether role X may act as role Y, which `act_as` needs before a caller dispatches under a
78
+ # role it does not hold. Same event-log shape as RoleAssignment, and the identity is the
79
+ # grant: without `starts_at`, a revoked pair would occupy its id forever and a repeat
80
+ # `Grant` would be refused as `AlreadyExists`.
96
81
  aggregate "RoleTransition" do
97
82
  description "Role X is authorized to act as role Y, from one point in time — until revoked, and grantable again afterward, exactly like RoleAssignment."
98
83
 
@@ -135,15 +120,9 @@ Hecks.bluebook "Governance" do
135
120
  emits RoleTransitionRevoked
136
121
  end
137
122
 
138
- # EVERY GRANT OF THIS PAIR, current or historical — same deferral
139
- # `AssignmentsForActor` already makes for RoleAssignment: the where
140
- # clause has no way to ask "and ends_at is absent", so the query
141
- # answers with every row and the CALLER reads `ends_at` to tell a
142
- # live grant from a revoked one, exactly as it already must for
143
- # every assignment. Can answer MORE than one row now that `starts_at`
144
- # is part of the identity — a pair granted again after a revocation is
145
- # a SECOND row, not an overwrite of the first, the same way a
146
- # re-assigned actor/role is for RoleAssignment.
123
+ # Every grant of this pair, current or historical: `where` cannot ask "ends_at is absent",
124
+ # so the caller reads `ends_at` to tell a live grant from a revoked one. A pair granted
125
+ # again after a revocation is a second row.
147
126
  query "Allowed" do
148
127
  description "Every role transition granted for this exact pair, current or historical."
149
128
  attribute :from_role, RoleName