hecks 2.8.0 → 3.0.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 (886) hide show
  1. checksums.yaml +4 -4
  2. data/exe/hecks +50 -7
  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 +32 -100
  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 +10 -32
  23. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +34 -108
  24. data/lib/hecks/adapters/driven/postgres/shared_connection.rb +180 -0
  25. data/lib/hecks/adapters/driven/postgres.rb +149 -136
  26. data/lib/hecks/adapters/driven/postgres_era.adapter +4 -15
  27. data/lib/hecks/adapters/driven/prism.rb +48 -38
  28. data/lib/hecks/adapters/driven/sql_query_builder.rb +21 -121
  29. data/lib/hecks/adapters/driven/sqlite/codec.rb +8 -40
  30. data/lib/hecks/adapters/driven/sqlite/projection.rb +26 -82
  31. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +40 -84
  32. data/lib/hecks/adapters/driven/sqlite.rb +128 -65
  33. data/lib/hecks/adapters/driven/tenant_provisioner.rb +76 -62
  34. data/lib/hecks/adapters/driven.rb +5 -36
  35. data/lib/hecks/adapters/driving/github_webhook.rb +8 -76
  36. data/lib/hecks/behaviors/dsl.rb +7 -25
  37. data/lib/hecks/behaviors/expectations.rb +32 -142
  38. data/lib/hecks/behaviors/ir.rb +7 -20
  39. data/lib/hecks/behaviors/rspec.rb +6 -17
  40. data/lib/hecks/behaviors/runner.rb +10 -34
  41. data/lib/hecks/behaviors.rb +4 -8
  42. data/lib/hecks/bench/cli.rb +62 -0
  43. data/lib/hecks/bench/environment.rb +71 -0
  44. data/lib/hecks/bench/postgres_probe.rb +30 -0
  45. data/lib/hecks/bench/report.rb +76 -0
  46. data/lib/hecks/bench/ruby_runner.rb +93 -0
  47. data/lib/hecks/bench/run.rb +55 -0
  48. data/lib/hecks/bench/rust_runner.rb +101 -0
  49. data/lib/hecks/bench/stats.rb +46 -0
  50. data/lib/hecks/bench/suite.rb +98 -0
  51. data/lib/hecks/bench/workload.rb +89 -0
  52. data/lib/hecks/bench.rb +17 -0
  53. data/lib/hecks/bluebook/aggregate.rb +9 -75
  54. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +9 -38
  55. data/lib/hecks/bluebook/assembly/build.rb +4 -15
  56. data/lib/hecks/bluebook/assembly/contract.rb +15 -136
  57. data/lib/hecks/bluebook/assembly/contracts.rb +56 -174
  58. data/lib/hecks/bluebook/assembly/marks.rb +31 -223
  59. data/lib/hecks/bluebook/assembly/specializer.rb +6 -52
  60. data/lib/hecks/bluebook/assembly.rb +6 -45
  61. data/lib/hecks/bluebook/attribute.rb +12 -61
  62. data/lib/hecks/bluebook/behaviour/aggregate.rb +39 -39
  63. data/lib/hecks/bluebook/behaviour/attribute.rb +3 -14
  64. data/lib/hecks/bluebook/behaviour/chapter.rb +14 -42
  65. data/lib/hecks/bluebook/behaviour/command.rb +14 -66
  66. data/lib/hecks/bluebook/behaviour/domain_port.rb +11 -20
  67. data/lib/hecks/bluebook/behaviour/entity.rb +7 -27
  68. data/lib/hecks/bluebook/behaviour/hexagon.rb +23 -41
  69. data/lib/hecks/bluebook/behaviour/lifecycle.rb +15 -40
  70. data/lib/hecks/bluebook/behaviour/policy.rb +23 -52
  71. data/lib/hecks/bluebook/behaviour/process_manager.rb +30 -57
  72. data/lib/hecks/bluebook/behaviour/read_model.rb +25 -31
  73. data/lib/hecks/bluebook/behaviour/traits.rb +27 -64
  74. data/lib/hecks/bluebook/behaviour/value_object.rb +5 -24
  75. data/lib/hecks/bluebook/capabilities.rb +17 -49
  76. data/lib/hecks/bluebook/chapter.rb +16 -50
  77. data/lib/hecks/bluebook/command.rb +23 -81
  78. data/lib/hecks/bluebook/domain_port.rb +49 -67
  79. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +50 -229
  80. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +50 -377
  81. data/lib/hecks/bluebook/dsl/attribute_collector.rb +33 -204
  82. data/lib/hecks/bluebook/dsl/binding_proxy.rb +16 -69
  83. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +96 -708
  84. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +63 -169
  85. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +3 -1
  86. data/lib/hecks/bluebook/dsl/command_builder.rb +74 -546
  87. data/lib/hecks/bluebook/dsl/const_shim.rb +8 -41
  88. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +42 -89
  89. data/lib/hecks/bluebook/dsl/entity_builder.rb +44 -330
  90. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +20 -269
  91. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +148 -197
  92. data/lib/hecks/bluebook/dsl/identity_declaration.rb +13 -88
  93. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +6 -20
  94. data/lib/hecks/bluebook/dsl/policy_builder.rb +16 -88
  95. data/lib/hecks/bluebook/dsl/port_builder.rb +2 -6
  96. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +22 -96
  97. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +31 -184
  98. data/lib/hecks/bluebook/dsl/query_builder.rb +38 -43
  99. data/lib/hecks/bluebook/dsl/read_model_builder.rb +75 -201
  100. data/lib/hecks/bluebook/dsl/rule_reference.rb +22 -163
  101. data/lib/hecks/bluebook/dsl/translation_builder.rb +28 -114
  102. data/lib/hecks/bluebook/dsl/value_object_builder.rb +10 -124
  103. data/lib/hecks/bluebook/dsl/word_gate.rb +16 -162
  104. data/lib/hecks/bluebook/dsl/world_builder.rb +46 -111
  105. data/lib/hecks/bluebook/entity.rb +13 -71
  106. data/lib/hecks/bluebook/expression/ast_json.rb +24 -190
  107. data/lib/hecks/bluebook/expression/ast_reader.rb +12 -54
  108. data/lib/hecks/bluebook/expression/canonical_form.rb +11 -88
  109. data/lib/hecks/bluebook/expression/evaluator.rb +47 -295
  110. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +30 -147
  111. data/lib/hecks/bluebook/expression/resolver.rb +39 -661
  112. data/lib/hecks/bluebook/hexagon.rb +18 -33
  113. data/lib/hecks/bluebook/lifecycle.rb +5 -16
  114. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -7
  115. data/lib/hecks/bluebook/meta_validator/judge.rb +111 -361
  116. data/lib/hecks/bluebook/meta_validator/plan.rb +33 -162
  117. data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -7
  118. data/lib/hecks/bluebook/meta_validator/readings.rb +56 -342
  119. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +25 -176
  120. data/lib/hecks/bluebook/meta_validator/shapes.rb +53 -267
  121. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +47 -306
  122. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +7 -43
  123. data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +246 -0
  124. data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -18
  125. data/lib/hecks/bluebook/meta_validator.rb +87 -352
  126. data/lib/hecks/bluebook/model_check/client_profile.rb +6 -21
  127. data/lib/hecks/bluebook/model_check.rb +122 -596
  128. data/lib/hecks/bluebook/pattern_subset.rb +49 -69
  129. data/lib/hecks/bluebook/process_manager.rb +13 -83
  130. data/lib/hecks/bluebook/project_loader.rb +3 -39
  131. data/lib/hecks/bluebook/project_register.rb +12 -60
  132. data/lib/hecks/bluebook/query.rb +27 -51
  133. data/lib/hecks/bluebook/read_model.rb +57 -81
  134. data/lib/hecks/bluebook/reference.rb +7 -41
  135. data/lib/hecks/bluebook/smoke_test.rb +9 -99
  136. data/lib/hecks/bluebook/synthesizer.rb +13 -62
  137. data/lib/hecks/bluebook/translation.rb +26 -62
  138. data/lib/hecks/bluebook/value_object.rb +11 -36
  139. data/lib/hecks/bluebook.rb +5 -22
  140. data/lib/hecks/cache_dir.rb +4 -23
  141. data/lib/hecks/canonical_json.rb +30 -0
  142. data/lib/hecks/chapters.rb +102 -0
  143. data/lib/hecks/cli/behaviors.rb +61 -0
  144. data/lib/hecks/cli/canonicalise.rb +25 -0
  145. data/lib/hecks/cli/check_engine_agreement.rb +32 -0
  146. data/lib/hecks/cli/check_era.rb +88 -0
  147. data/lib/hecks/cli/console.rb +66 -0
  148. data/lib/hecks/cli/deploy_template_diff.rb +40 -0
  149. data/lib/hecks/cli/doc_coverage.rb +32 -0
  150. data/lib/hecks/cli/document.rb +5 -23
  151. data/lib/hecks/cli/expression_projection.rb +41 -0
  152. data/lib/hecks/cli/follow.rb +88 -0
  153. data/lib/hecks/cli/history.rb +44 -0
  154. data/lib/hecks/cli/ir.rb +4 -6
  155. data/lib/hecks/cli/mcp.rb +5 -6
  156. data/lib/hecks/cli/model_check.rb +24 -88
  157. data/lib/hecks/cli/pattern_cases.rb +47 -0
  158. data/lib/hecks/cli/present.rb +39 -0
  159. data/lib/hecks/cli/project_cli.rb +223 -64
  160. data/lib/hecks/cli/project_diagrams.rb +4 -10
  161. data/lib/hecks/cli/project_glossary.rb +44 -0
  162. data/lib/hecks/cli/reference.rb +46 -0
  163. data/lib/hecks/cli/refresh_projections.rb +44 -0
  164. data/lib/hecks/cli/refresh_rspec_runtime_baseline.rb +150 -0
  165. data/lib/hecks/cli/release.rb +70 -0
  166. data/lib/hecks/cli/release_gem.rb +59 -0
  167. data/lib/hecks/cli/rspec_io_parallel_files.rb +137 -0
  168. data/lib/hecks/cli/rspec_shard_files.rb +66 -0
  169. data/lib/hecks/cli/run.rb +15 -61
  170. data/lib/hecks/cli/seed_semantics_corpus.rb +71 -0
  171. data/lib/hecks/cli/serve_query_ir_mcp.rb +30 -0
  172. data/lib/hecks/cli/shape.rb +70 -0
  173. data/lib/hecks/cli/smoke_http.rb +230 -0
  174. data/lib/hecks/cli/smoke_test.rb +7 -19
  175. data/lib/hecks/cli/spec_example.rb +27 -0
  176. data/lib/hecks/cli/statements.rb +32 -0
  177. data/lib/hecks/cli/stores.rb +4 -6
  178. data/lib/hecks/cli/stress_concurrency_specs.rb +177 -0
  179. data/lib/hecks/cli.rb +216 -73
  180. data/lib/hecks/codemod.rb +324 -0
  181. data/lib/hecks/construct.rb +8 -34
  182. data/lib/hecks/corpus.rb +423 -0
  183. data/lib/hecks/deploy/bluebook/deploy.bluebook +617 -158
  184. data/lib/hecks/deploy/bluebook/deploy.hecksagon +41 -11
  185. data/lib/hecks/deploy/oidc.json +82 -2
  186. data/lib/hecks/doc/reference.rb +351 -0
  187. data/lib/hecks/doors/cli_door.rb +196 -0
  188. data/lib/hecks/doors/cli_runner.rb +297 -0
  189. data/lib/hecks/{facade → doors}/command_request.rb +14 -34
  190. data/lib/hecks/doors/handle.rb +206 -0
  191. data/lib/hecks/doors/json_door.rb +132 -0
  192. data/lib/hecks/doors/launcher_options.rb +129 -0
  193. data/lib/hecks/{cli → doors}/mcp_door.rb +25 -120
  194. data/lib/hecks/doors/mcp_door_scope.rb +97 -0
  195. data/lib/hecks/doors/ruby_door/aggregate_door.rb +162 -0
  196. data/lib/hecks/doors/ruby_door/chapter.rb +72 -0
  197. data/lib/hecks/doors/ruby_door.rb +77 -0
  198. data/lib/hecks/doors.rb +61 -0
  199. data/lib/hecks/embryonaut_bluebook/lock.rb +3 -35
  200. data/lib/hecks/embryonaut_bluebook/shape.rb +1 -17
  201. data/lib/hecks/embryonaut_bluebook/vendor.rb +8 -53
  202. data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +4 -19
  203. data/lib/hecks/embryonaut_bluebook.rb +5 -72
  204. data/lib/hecks/engine_agreement.rb +156 -0
  205. data/lib/hecks/facade.rb +23 -51
  206. data/lib/hecks/forms/app.rb +21 -76
  207. data/lib/hecks/forms/banking_presentation.rb +50 -0
  208. data/lib/hecks/forms/command_form_renderer.rb +10 -29
  209. data/lib/hecks/forms/field_renderer.rb +17 -156
  210. data/lib/hecks/forms/field_shape.rb +31 -222
  211. data/lib/hecks/forms/html.rb +12 -47
  212. data/lib/hecks/forms/index_renderer.rb +1 -4
  213. data/lib/hecks/forms/page.rb +3 -9
  214. data/lib/hecks/forms/params.rb +14 -60
  215. data/lib/hecks/forms/port_argument.rb +2 -19
  216. data/lib/hecks/forms/query_form_renderer.rb +4 -79
  217. data/lib/hecks/forms/record_renderer.rb +8 -75
  218. data/lib/hecks/forms/record_table.rb +7 -50
  219. data/lib/hecks/forms/reference_options.rb +3 -30
  220. data/lib/hecks/forms/value_object_shape.rb +8 -35
  221. data/lib/hecks/forms.rb +5 -33
  222. data/lib/hecks/fqn.rb +5 -12
  223. data/lib/hecks/framework/bluebook/compliance.bluebook +16 -65
  224. data/lib/hecks/framework/bluebook/console_settings.bluebook +47 -134
  225. data/lib/hecks/framework/bluebook/framework.hecksagon +5 -14
  226. data/lib/hecks/framework/bluebook/governance.bluebook +13 -34
  227. data/lib/hecks/framework/bluebook/identity.bluebook +11 -26
  228. data/lib/hecks/framework/bluebook/privacy.bluebook +8 -33
  229. data/lib/hecks/framework.rb +11 -80
  230. data/lib/hecks/freezer.rb +5 -39
  231. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +393 -0
  232. data/lib/hecks/fuzzing/combination_miner.rb +86 -0
  233. data/lib/hecks/fuzzing/concurrent_dispatch.rb +214 -0
  234. data/lib/hecks/fuzzing/coverage_campaign.rb +143 -0
  235. data/lib/hecks/fuzzing/differential.rb +156 -0
  236. data/lib/hecks/fuzzing/domain_generator.rb +615 -0
  237. data/lib/hecks/fuzzing/era_boundary.rb +71 -0
  238. data/lib/hecks/fuzzing/form_census.rb +136 -0
  239. data/lib/hecks/fuzzing/generated_domain_check.rb +93 -0
  240. data/lib/hecks/fuzzing/invalid_value_generator.rb +64 -0
  241. data/lib/hecks/fuzzing/isolated_boot.rb +301 -0
  242. data/lib/hecks/fuzzing/nondeterministic.rb +51 -0
  243. data/lib/hecks/fuzzing/outside_world.rb +45 -0
  244. data/lib/hecks/fuzzing/persistence_parity.rb +102 -0
  245. data/lib/hecks/fuzzing/properties/corrections.rb +88 -0
  246. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +314 -0
  247. data/lib/hecks/fuzzing/properties/guards.rb +190 -0
  248. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +300 -0
  249. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +93 -0
  250. data/lib/hecks/fuzzing/properties/outbox.rb +89 -0
  251. data/lib/hecks/fuzzing/properties/querying.rb +175 -0
  252. data/lib/hecks/fuzzing/properties.rb +162 -0
  253. data/lib/hecks/fuzzing/qa_settings.rb +123 -0
  254. data/lib/hecks/fuzzing/replay.rb +505 -0
  255. data/lib/hecks/fuzzing/rotation_priority.rb +48 -0
  256. data/lib/hecks/fuzzing/rust_gap_manifest.rb +108 -0
  257. data/lib/hecks/fuzzing/self_consistency.rb +339 -0
  258. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +385 -0
  259. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +132 -0
  260. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +95 -0
  261. data/lib/hecks/fuzzing/sequence_generator/picker.rb +77 -0
  262. data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +109 -0
  263. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +237 -0
  264. data/lib/hecks/fuzzing/sequence_generator.rb +189 -0
  265. data/lib/hecks/fuzzing/shrinker.rb +166 -0
  266. data/lib/hecks/fuzzing/structural_skips.rb +33 -0
  267. data/lib/hecks/fuzzing/sweep_depth.rb +43 -0
  268. data/lib/hecks/fuzzing/target_capabilities.rb +163 -0
  269. data/lib/hecks/fuzzing/value_generator.rb +216 -0
  270. data/lib/hecks/fuzzing.rb +23 -0
  271. data/lib/hecks/grammar/evolve.rb +336 -0
  272. data/lib/hecks/grammar/expression.bluebook +15 -40
  273. data/lib/hecks/grammar/translation.bluebook +3 -6
  274. data/lib/hecks/grammar.rb +16 -41
  275. data/lib/hecks/hecks/adapters/codebase/codemods.rb +45 -0
  276. data/lib/hecks/hecks/adapters/codebase/conformance.rb +71 -0
  277. data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +202 -0
  278. data/lib/hecks/hecks/adapters/codebase/gem_registry.rb +80 -0
  279. data/lib/hecks/hecks/adapters/codebase/kernel_tables.rb +54 -0
  280. data/lib/hecks/hecks/adapters/codebase/language.rb +240 -0
  281. data/lib/hecks/hecks/adapters/codebase/npm_registry.rb +102 -0
  282. data/lib/hecks/hecks/adapters/codebase/publishing.rb +142 -0
  283. data/lib/hecks/hecks/adapters/codebase/regeneration.rb +67 -0
  284. data/lib/hecks/hecks/adapters/codebase/release_facts.rb +90 -0
  285. data/lib/hecks/hecks/adapters/codebase/ruby_child.rb +83 -0
  286. data/lib/hecks/hecks/adapters/codebase/secret_vault.rb +36 -0
  287. data/lib/hecks/hecks/adapters/codebase/source_tree.adapter +7 -0
  288. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +189 -0
  289. data/lib/hecks/hecks/adapters/codebase/sqlite_fixture.rb +71 -0
  290. data/lib/hecks/hecks/adapters/codebase/style.rb +171 -0
  291. data/lib/hecks/hecks/adapters/codebase/test_runner.rb +55 -0
  292. data/lib/hecks/hecks/adapters/codebase/test_suite.rb +165 -0
  293. data/lib/hecks/hecks/adapters/codebase/tree.rb +111 -0
  294. data/lib/hecks/hecks/adapters/console_capture.rb +79 -0
  295. data/lib/hecks/hecks/adapters/deploy_toolchain.adapter +7 -0
  296. data/lib/hecks/hecks/adapters/deploy_toolchain.rb +108 -0
  297. data/lib/hecks/hecks/adapters/git.adapter +7 -0
  298. data/lib/hecks/hecks/adapters/git.rb +102 -0
  299. data/lib/hecks/hecks/adapters/host_http.adapter +7 -0
  300. data/lib/hecks/hecks/adapters/host_http.rb +65 -0
  301. data/lib/hecks/hecks/adapters/in_process_boot.adapter +7 -0
  302. data/lib/hecks/hecks/adapters/in_process_boot.rb +197 -0
  303. data/lib/hecks/hecks/adapters/in_process_operations.rb +202 -0
  304. data/lib/hecks/hecks/adapters/journal_store/changes.rb +143 -0
  305. data/lib/hecks/hecks/adapters/journal_store/compaction.rb +145 -0
  306. data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +132 -0
  307. data/lib/hecks/hecks/adapters/journal_store/examination.rb +135 -0
  308. data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +163 -0
  309. data/lib/hecks/hecks/adapters/journal_store/readings.rb +187 -0
  310. data/lib/hecks/hecks/adapters/journal_store.adapter +7 -0
  311. data/lib/hecks/hecks/adapters/journal_store.rb +64 -0
  312. data/lib/hecks/hecks/adapters/local_files.adapter +7 -0
  313. data/lib/hecks/hecks/adapters/local_files.rb +46 -0
  314. data/lib/hecks/hecks/adapters/pg_admin.adapter +7 -0
  315. data/lib/hecks/hecks/adapters/pg_admin.rb +245 -0
  316. data/lib/hecks/hecks/adapters/process_pool.adapter +7 -0
  317. data/lib/hecks/hecks/adapters/process_pool.rb +119 -0
  318. data/lib/hecks/hecks/adapters/rust_toolchain.adapter +7 -0
  319. data/lib/hecks/hecks/adapters/rust_toolchain.rb +179 -0
  320. data/lib/hecks/hecks/adapters/rust_workspace.rb +150 -0
  321. data/lib/hecks/hecks/adapters/shell.adapter +7 -0
  322. data/lib/hecks/hecks/adapters/shell.rb +49 -0
  323. data/lib/hecks/hecks/adapters/terminal.adapter +7 -0
  324. data/lib/hecks/hecks/adapters/terminal.rb +74 -0
  325. data/lib/hecks/hecks/codebase.behaviors +172 -0
  326. data/lib/hecks/hecks/codebase.bluebook +2453 -0
  327. data/lib/hecks/hecks/context_map.hecksagon +13 -0
  328. data/lib/hecks/hecks/custodian.behaviors +179 -0
  329. data/lib/hecks/hecks/custodian.bluebook +1847 -0
  330. data/lib/hecks/hecks/deploy.behaviors +74 -0
  331. data/lib/hecks/hecks/deploy.world +12 -0
  332. data/lib/hecks/hecks/environments/memory.world +10 -0
  333. data/lib/hecks/hecks/governance.world +7 -0
  334. data/lib/hecks/hecks/hecks.behaviors +62 -0
  335. data/lib/hecks/hecks/hecks.bluebook +124 -0
  336. data/lib/hecks/hecks/hecks.hecksagon +390 -0
  337. data/lib/hecks/hecks/hecks.world +42 -0
  338. data/lib/hecks/ir.rb +14 -83
  339. data/lib/hecks/kernel_capabilities.rb +194 -0
  340. data/lib/hecks/language/adapter.bluebook +2 -7
  341. data/lib/hecks/language/bluebook/aggregate.bluebook +56 -221
  342. data/lib/hecks/language/bluebook/attaches/paging.bluebook +6 -15
  343. data/lib/hecks/language/bluebook/bluebook.bluebook +26 -83
  344. data/lib/hecks/language/bluebook/bluebook.hecksagon +3 -15
  345. data/lib/hecks/language/bluebook/command.bluebook +27 -144
  346. data/lib/hecks/language/bluebook/entity.bluebook +24 -97
  347. data/lib/hecks/language/bluebook/policy.bluebook +16 -67
  348. data/lib/hecks/language/bluebook/process_manager.bluebook +28 -151
  349. data/lib/hecks/language/bluebook/projection.bluebook +126 -60
  350. data/lib/hecks/language/bluebook/query.bluebook +19 -43
  351. data/lib/hecks/language/bluebook/shape.bluebook +23 -73
  352. data/lib/hecks/language/bluebook/syntax.bluebook +51 -262
  353. data/lib/hecks/language/bluebook/vocabulary.bluebook +83 -198
  354. data/lib/hecks/language/hecksagon/adapter_binding.bluebook +3 -10
  355. data/lib/hecks/language/hecksagon/domain_port.bluebook +14 -19
  356. data/lib/hecks/language/hecksagon/hecksagon.bluebook +26 -25
  357. data/lib/hecks/language/hecksagon/port_operation.bluebook +3 -14
  358. data/lib/hecks/language/oidc.json +35 -0
  359. data/lib/hecks/language/port.bluebook +4 -17
  360. data/lib/hecks/language/translation/translation.bluebook +3 -9
  361. data/lib/hecks/language/translation/translation_aggregate.bluebook +5 -24
  362. data/lib/hecks/language/world/wiring.bluebook +1 -3
  363. data/lib/hecks/language/world/world.bluebook +5 -7
  364. data/lib/hecks/literal.rb +8 -50
  365. data/lib/hecks/mcp_stdio_guard.rb +4 -41
  366. data/lib/hecks/naming.rb +29 -137
  367. data/lib/hecks/persistence_legacy_fixture/regenerate.rb +161 -0
  368. data/lib/hecks/persistence_legacy_fixture.rb +113 -0
  369. data/lib/hecks/ports/access_control.rb +7 -23
  370. data/lib/hecks/ports/agent/answers.rb +5 -23
  371. data/lib/hecks/ports/agent.rb +47 -172
  372. data/lib/hecks/ports/authentication.rb +12 -39
  373. data/lib/hecks/ports/authorization.rb +16 -43
  374. data/lib/hecks/ports/clock.rb +5 -45
  375. data/lib/hecks/ports/extraction.rb +7 -12
  376. data/lib/hecks/ports/identity_assignment.rb +8 -29
  377. data/lib/hecks/ports/identity_generation.rb +6 -29
  378. data/lib/hecks/ports/identity_resolution.rb +2 -10
  379. data/lib/hecks/ports/key_vault.rb +9 -45
  380. data/lib/hecks/ports/persistence/append_only.rb +61 -64
  381. data/lib/hecks/ports/persistence/binding_policy.rb +4 -14
  382. data/lib/hecks/ports/persistence/codec_boundary.rb +20 -80
  383. data/lib/hecks/ports/persistence/null_saga_store.rb +4 -20
  384. data/lib/hecks/ports/persistence/plugin.rb +13 -42
  385. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +44 -144
  386. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +15 -115
  387. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +20 -85
  388. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +6 -25
  389. data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +18 -24
  390. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +55 -181
  391. data/lib/hecks/ports/persistence/plugins/era/postgres_era/events.rb +77 -0
  392. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +12 -122
  393. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +28 -128
  394. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +73 -474
  395. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +31 -124
  396. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +45 -219
  397. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +13 -108
  398. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +19 -76
  399. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +9 -49
  400. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +6 -123
  401. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +23 -55
  402. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +14 -72
  403. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +7 -16
  404. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +43 -127
  405. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +13 -45
  406. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +131 -555
  407. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +13 -63
  408. data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +227 -0
  409. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +2 -17
  410. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +3 -14
  411. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +13 -58
  412. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -17
  413. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +9 -26
  414. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +50 -47
  415. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +18 -124
  416. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +34 -123
  417. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +5 -12
  418. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +3 -9
  419. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -14
  420. data/lib/hecks/ports/persistence/plugins/era/translation.rb +1 -0
  421. data/lib/hecks/ports/persistence/plugins/era.rb +4 -23
  422. data/lib/hecks/ports/persistence/postgres_dump/connection.rb +7 -23
  423. data/lib/hecks/ports/persistence/postgres_dump.rb +4 -35
  424. data/lib/hecks/ports/persistence/remote_runtime.rb +2 -24
  425. data/lib/hecks/ports/persistence/repository_factory.rb +8 -27
  426. data/lib/hecks/ports/persistence/state_codec.rb +47 -108
  427. data/lib/hecks/ports/persistence.rb +7 -14
  428. data/lib/hecks/ports/projection.rb +42 -84
  429. data/lib/hecks/ports/query/in_memory.rb +10 -58
  430. data/lib/hecks/ports/query/ordering.rb +11 -32
  431. data/lib/hecks/ports/query.rb +14 -37
  432. data/lib/hecks/ports.rb +2 -8
  433. data/lib/hecks/projection_files.rb +285 -0
  434. data/lib/hecks/projections/bootstrap_table.rb +12 -44
  435. data/lib/hecks/projections/deploy/fargate/assembly.rb +2 -15
  436. data/lib/hecks/projections/deploy/fargate/cdn.rb +1 -48
  437. data/lib/hecks/projections/deploy/fargate/cdn_default.rb +2 -11
  438. data/lib/hecks/projections/deploy/fargate/check.rb +5 -14
  439. data/lib/hecks/projections/deploy/fargate/containers.rb +3 -78
  440. data/lib/hecks/projections/deploy/fargate/extras.rb +3 -44
  441. data/lib/hecks/projections/deploy/fargate/monitoring.rb +0 -44
  442. data/lib/hecks/projections/deploy/fargate/settings.rb +2 -54
  443. data/lib/hecks/projections/deploy/fargate/yaml.rb +13 -58
  444. data/lib/hecks/projections/deploy/fargate.rb +40 -207
  445. data/lib/hecks/projections/deploy/lambda.rb +191 -708
  446. data/lib/hecks/projections/deploy/preview/access_yaml.rb +1 -33
  447. data/lib/hecks/projections/deploy/preview/containers.rb +6 -111
  448. data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
  449. data/lib/hecks/projections/deploy/preview/routing_yaml.rb +4 -56
  450. data/lib/hecks/projections/deploy/preview/script.rb +2 -14
  451. data/lib/hecks/projections/deploy/preview/settings.rb +3 -33
  452. data/lib/hecks/projections/deploy/preview/task_yaml.rb +2 -38
  453. data/lib/hecks/projections/deploy/preview/template.rb +3 -59
  454. data/lib/hecks/projections/deploy/preview.rb +2 -67
  455. data/lib/hecks/projections/deploy/scripts/deploy-service.sh.tmpl +1 -1
  456. data/lib/hecks/projections/deploy/scripts/expected-era.header +1 -1
  457. data/lib/hecks/projections/deploy/scripts/hosting.mk.tmpl +3 -3
  458. data/lib/hecks/projections/deploy/scripts/settings.rb +3 -25
  459. data/lib/hecks/projections/deploy/scripts/smoke-after-deploy.sh.tmpl +1 -1
  460. data/lib/hecks/projections/deploy/scripts.rb +4 -57
  461. data/lib/hecks/projections/deploy/shared.rb +48 -147
  462. data/lib/hecks/projections/deploy/smoke.rb +4 -61
  463. data/lib/hecks/projections/deploy/template_diff/comparer.rb +3 -25
  464. data/lib/hecks/projections/deploy/template_diff/loader.rb +2 -11
  465. data/lib/hecks/projections/deploy/template_diff.rb +5 -57
  466. data/lib/hecks/projections/diagrams.rb +22 -584
  467. data/lib/hecks/projections/glossary/html.rb +14 -130
  468. data/lib/hecks/projections/glossary/markdown.rb +8 -69
  469. data/lib/hecks/projections/glossary/mermaid.rb +9 -66
  470. data/lib/hecks/projections/glossary/sensitivity.rb +5 -34
  471. data/lib/hecks/projections/glossary/sentences.rb +14 -159
  472. data/lib/hecks/projections/glossary.rb +30 -222
  473. data/lib/hecks/projections/ir.rb +3 -10
  474. data/lib/hecks/projections/model/deviations.rb +16 -43
  475. data/lib/hecks/projections/model.rb +12 -51
  476. data/lib/hecks/projections/oidc.rb +12 -56
  477. data/lib/hecks/projections/parser_table.rb +13 -32
  478. data/lib/hecks/projections/reference.rb +4 -18
  479. data/lib/hecks/projections/rust_vocabulary.rb +13 -251
  480. data/lib/hecks/projections/shape.rb +3 -29
  481. data/lib/hecks/projections/statements.rb +7 -86
  482. data/lib/hecks/projections/vocabulary.rb +6 -22
  483. data/lib/hecks/projections.rb +2 -16
  484. data/lib/hecks/projector/cli_projector.rb +154 -400
  485. data/lib/hecks/projector/docs_projector.rb +10 -201
  486. data/lib/hecks/projector/exporter.rb +42 -134
  487. data/lib/hecks/projector/ir_projector.rb +2 -9
  488. data/lib/hecks/projector/narrate_projector.rb +15 -189
  489. data/lib/hecks/projector/target.rb +13 -76
  490. data/lib/hecks/projector.rb +30 -121
  491. data/lib/hecks/quality_control/adapters/agent.adapter +7 -0
  492. data/lib/hecks/quality_control/adapters/agent.rb +58 -0
  493. data/lib/hecks/quality_control/adapters/angle_tools.adapter +7 -0
  494. data/lib/hecks/quality_control/adapters/angle_tools.rb +15 -0
  495. data/lib/hecks/quality_control/adapters/clearance_recorder.rb +36 -0
  496. data/lib/hecks/quality_control/adapters/clearance_tools.adapter +7 -0
  497. data/lib/hecks/quality_control/adapters/clearance_tools.rb +17 -0
  498. data/lib/hecks/quality_control/adapters/git_pr.adapter +7 -0
  499. data/lib/hecks/quality_control/adapters/git_pr.rb +226 -0
  500. data/lib/hecks/quality_control/adapters/github_checks.adapter +9 -0
  501. data/lib/hecks/quality_control/adapters/github_checks.rb +83 -0
  502. data/lib/hecks/quality_control/adapters/github_ci_webhook.rb +56 -0
  503. data/lib/hecks/quality_control/adapters/github_issues.adapter +7 -0
  504. data/lib/hecks/quality_control/adapters/github_issues.rb +49 -0
  505. data/lib/hecks/quality_control/adapters/qa_tool.rb +59 -0
  506. data/lib/hecks/quality_control/adapters/sweep_tools.adapter +7 -0
  507. data/lib/hecks/quality_control/adapters/sweep_tools.rb +51 -0
  508. data/lib/hecks/quality_control/adapters/target_tools.adapter +7 -0
  509. data/lib/hecks/quality_control/adapters/target_tools.rb +43 -0
  510. data/lib/hecks/quality_control/cli/child.rb +45 -0
  511. data/lib/hecks/quality_control/cli/qa_concurrency_racer.rb +45 -0
  512. data/lib/hecks/quality_control/cli/qa_discover_external_domains.rb +248 -0
  513. data/lib/hecks/quality_control/cli/qa_domain_novelty.rb +163 -0
  514. data/lib/hecks/quality_control/cli/qa_generated_domains.rb +450 -0
  515. data/lib/hecks/quality_control/cli/qa_log_bug.rb +195 -0
  516. data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +220 -0
  517. data/lib/hecks/quality_control/cli/qa_open_pr.rb +305 -0
  518. data/lib/hecks/quality_control/cli/qa_postgres_migrate.rb +154 -0
  519. data/lib/hecks/quality_control/cli/qa_postgres_role.rb +159 -0
  520. data/lib/hecks/quality_control/cli/qa_pr_check.rb +232 -0
  521. data/lib/hecks/quality_control/cli/qa_seed_angles.rb +142 -0
  522. data/lib/hecks/quality_control/cli/qa_seed_targets.rb +69 -0
  523. data/lib/hecks/quality_control/cli/qa_sweep/all_mode.rb +280 -0
  524. data/lib/hecks/quality_control/cli/qa_sweep/checks.rb +329 -0
  525. data/lib/hecks/quality_control/cli/qa_sweep/dials.rb +92 -0
  526. data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +117 -0
  527. data/lib/hecks/quality_control/cli/qa_sweep/parsing.rb +175 -0
  528. data/lib/hecks/quality_control/cli/qa_sweep/release_mode.rb +60 -0
  529. data/lib/hecks/quality_control/cli/qa_sweep.rb +611 -0
  530. data/lib/hecks/quality_control/cli/qa_tick.rb +274 -0
  531. data/lib/hecks/quality_control/quality_control.bluebook +2224 -0
  532. data/lib/hecks/quality_control/quality_control.hecksagon +13 -0
  533. data/lib/hecks/quality_control/quality_control.ports.hecksagon +54 -0
  534. data/lib/hecks/query_ir.rb +346 -0
  535. data/lib/hecks/query_ir_mcp.rb +198 -0
  536. data/lib/hecks/query_specification/common/comparators.rb +5 -26
  537. data/lib/hecks/query_specification/common/comparison.rb +35 -143
  538. data/lib/hecks/query_specification/common/dsl.rb +8 -24
  539. data/lib/hecks/query_specification/common/null_policy.rb +20 -70
  540. data/lib/hecks/query_specification/common/options.rb +2 -6
  541. data/lib/hecks/query_specification/common/where_clause.rb +2 -10
  542. data/lib/hecks/query_specification/field_path.rb +37 -77
  543. data/lib/hecks/query_specification/hop_path.rb +12 -76
  544. data/lib/hecks/release/gem_pin.rb +2 -26
  545. data/lib/hecks/release/runner/ci_publisher.rb +5 -8
  546. data/lib/hecks/release/runner/clean_tree.rb +42 -0
  547. data/lib/hecks/release/runner/commands.rb +2 -4
  548. data/lib/hecks/release/runner/console.rb +6 -0
  549. data/lib/hecks/release/runner/gem_publisher.rb +15 -9
  550. data/lib/hecks/release/runner/npm_publisher.rb +9 -53
  551. data/lib/hecks/release/runner/preflight.rb +29 -20
  552. data/lib/hecks/release/runner/published.rb +8 -26
  553. data/lib/hecks/release/runner/tagger.rb +31 -23
  554. data/lib/hecks/release/runner.rb +60 -38
  555. data/lib/hecks/rendering.rb +4 -26
  556. data/lib/hecks/router/namespace_installer.rb +15 -39
  557. data/lib/hecks/router.rb +4 -8
  558. data/lib/hecks/runtime/adapter_lookup.rb +124 -0
  559. data/lib/hecks/runtime/aggregate_lock.rb +8 -33
  560. data/lib/hecks/runtime/boot_gates.rb +3 -18
  561. data/lib/hecks/runtime/caller.rb +19 -59
  562. data/lib/hecks/runtime/capability_graph.rb +5 -21
  563. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +16 -78
  564. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +27 -160
  565. data/lib/hecks/runtime/command_interpreter.rb +124 -306
  566. data/lib/hecks/runtime/command_rules/admissibility.rb +95 -366
  567. data/lib/hecks/runtime/command_rules/arithmetic.rb +66 -196
  568. data/lib/hecks/runtime/command_rules/authorization.rb +13 -45
  569. data/lib/hecks/runtime/command_rules/emission.rb +8 -21
  570. data/lib/hecks/runtime/command_rules/references.rb +23 -202
  571. data/lib/hecks/runtime/dependency_planning.rb +50 -104
  572. data/lib/hecks/runtime/dispatcher.rb +134 -243
  573. data/lib/hecks/runtime/entity_element.rb +40 -462
  574. data/lib/hecks/runtime/entity_interpreter.rb +64 -219
  575. data/lib/hecks/runtime/errors.rb +32 -115
  576. data/lib/hecks/runtime/event.rb +3 -25
  577. data/lib/hecks/runtime/identity.rb +18 -154
  578. data/lib/hecks/runtime/instance.rb +18 -150
  579. data/lib/hecks/runtime/interpreting.rb +14 -70
  580. data/lib/hecks/runtime/invocation.rb +28 -164
  581. data/lib/hecks/runtime/loader.rb +109 -194
  582. data/lib/hecks/runtime/outbox.rb +39 -262
  583. data/lib/hecks/runtime/policy_interpreter.rb +43 -202
  584. data/lib/hecks/runtime/port_operation_interpreter.rb +28 -97
  585. data/lib/hecks/runtime/query_interpreter.rb +122 -187
  586. data/lib/hecks/runtime/reaction_invocation.rb +36 -138
  587. data/lib/hecks/runtime/reaction_outcome.rb +89 -0
  588. data/lib/hecks/runtime/read_model_interpreter.rb +117 -208
  589. data/lib/hecks/runtime/rebuild_sweep.rb +8 -54
  590. data/lib/hecks/runtime/reference_hop.rb +8 -81
  591. data/lib/hecks/runtime/refusal_wording.rb +28 -87
  592. data/lib/hecks/runtime/registry/saga_persistence.rb +10 -102
  593. data/lib/hecks/runtime/registry/verification.rb +213 -227
  594. data/lib/hecks/runtime/registry/world_defaults.rb +4 -53
  595. data/lib/hecks/runtime/registry.rb +156 -247
  596. data/lib/hecks/runtime/remote_dispatcher.rb +68 -154
  597. data/lib/hecks/runtime/routing.rb +4 -15
  598. data/lib/hecks/runtime/saga_interpreter/correlation.rb +14 -61
  599. data/lib/hecks/runtime/saga_interpreter.rb +79 -263
  600. data/lib/hecks/runtime/saga_pending_dispatch.rb +2 -43
  601. data/lib/hecks/runtime/tenant_check.rb +6 -68
  602. data/lib/hecks/runtime/tenant_scope.rb +3 -28
  603. data/lib/hecks/runtime/value/admission.rb +16 -67
  604. data/lib/hecks/runtime/value/coercion.rb +162 -760
  605. data/lib/hecks/runtime/value/entity_list_coercion.rb +26 -219
  606. data/lib/hecks/runtime/value/field_checks.rb +172 -0
  607. data/lib/hecks/runtime/value.rb +15 -84
  608. data/lib/hecks/runtime.rb +35 -69
  609. data/lib/hecks/rust_build/conformance.rb +158 -0
  610. data/lib/hecks/rust_build/conformance_fuzz.rb +71 -0
  611. data/lib/hecks/rust_build/coverage.rb +218 -0
  612. data/lib/hecks/rust_build/native_build.rb +124 -0
  613. data/lib/hecks/rust_build/project_rust/root_mod.rb +120 -0
  614. data/lib/hecks/rust_build/project_rust.rb +332 -0
  615. data/lib/hecks/rust_build/wasm.rb +105 -0
  616. data/lib/hecks/rust_build/wasm_browser.rb +113 -0
  617. data/lib/hecks/rust_build.rb +144 -0
  618. data/lib/hecks/storehouse.rb +155 -582
  619. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +10 -34
  620. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +6 -23
  621. data/lib/hecks/three_zero/forms.yml +127 -0
  622. data/lib/hecks/three_zero.rb +12 -0
  623. data/lib/hecks/tools/argument_gate_matrix.rb +316 -0
  624. data/lib/hecks/tools/comment_style.rb +1070 -0
  625. data/lib/hecks/tools/corpus_report.rb +87 -0
  626. data/lib/hecks/tools/deploy_recipe.rb +255 -0
  627. data/lib/hecks/tools/deploy_recipe_lint.rb +282 -0
  628. data/lib/hecks/tools/drop_implicit_append_fields.rb +95 -0
  629. data/lib/hecks/tools/era_projection_backfill.rb +94 -0
  630. data/lib/hecks/tools/era_reattest.rb +146 -0
  631. data/lib/hecks/tools/evolve_run.rb +189 -0
  632. data/lib/hecks/tools/fuzz_sweep.rb +373 -0
  633. data/lib/hecks/tools/heki_compaction.rb +100 -0
  634. data/lib/hecks/tools/hoist_local_givens.rb +246 -0
  635. data/lib/hecks/tools/journal_compaction.rb +89 -0
  636. data/lib/hecks/tools/oidc_manifests.rb +70 -0
  637. data/lib/hecks/tools/query_ir_run.rb +81 -0
  638. data/lib/hecks/tools/regeneration_run.rb +166 -0
  639. data/lib/hecks/tools/rust_comment_style.rb +642 -0
  640. data/lib/hecks/tools/sequence_script.rb +62 -0
  641. data/lib/hecks/tools/tail_merge.rb +117 -0
  642. data/lib/hecks/tools/tenant_provisioning.rb +142 -0
  643. data/lib/hecks/tools/translation_audit.rb +208 -0
  644. data/lib/hecks/tools/translation_scaffold.rb +131 -0
  645. data/lib/hecks/tools.rb +86 -0
  646. data/lib/hecks/vendoring/git_environment.rb +2 -8
  647. data/lib/hecks/vendoring/git_source.rb +4 -10
  648. data/lib/hecks/vendoring.rb +7 -46
  649. data/lib/hecks/version.rb +3 -14
  650. data/lib/hecks.rb +52 -90
  651. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -46
  652. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +11 -80
  653. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +10 -99
  654. data/qa/settings.yml +66 -0
  655. data/rust/.cargo/config.toml +6 -0
  656. data/rust/.gitignore +2 -0
  657. data/rust/Cargo.lock +54 -0
  658. data/rust/Cargo.toml +61 -0
  659. data/rust/build/.cargo/config.toml +3 -0
  660. data/rust/build/.gitignore +1 -0
  661. data/rust/build/Cargo.lock +7 -0
  662. data/rust/build/Cargo.toml +19 -0
  663. data/rust/build/src/build_artifact.rs +47 -0
  664. data/rust/build/src/cargo_sync.rs +248 -0
  665. data/rust/build/src/json.rs +421 -0
  666. data/rust/build/src/lineage_pass.rs +285 -0
  667. data/rust/build/src/main.rs +51 -0
  668. data/rust/build/src/optional_pass.rs +150 -0
  669. data/rust/build/src/pipeline.rs +264 -0
  670. data/rust/build/src/reserved_names.rs +76 -0
  671. data/rust/build/src/resolve.rs +124 -0
  672. data/rust/build/src/root.rs +25 -0
  673. data/rust/build/src/sidecars.rs +73 -0
  674. data/rust/build/src/subprocess.rs +51 -0
  675. data/rust/build/src/tmp.rs +31 -0
  676. data/rust/codegen/.cargo/config.toml +3 -0
  677. data/rust/codegen/.gitignore +1 -0
  678. data/rust/codegen/Cargo.lock +7 -0
  679. data/rust/codegen/Cargo.toml +20 -0
  680. data/rust/codegen/src/attr.rs +32 -0
  681. data/rust/codegen/src/bridging.rs +481 -0
  682. data/rust/codegen/src/commands.rs +1226 -0
  683. data/rust/codegen/src/constraints.rs +109 -0
  684. data/rust/codegen/src/dependency_planning.rs +209 -0
  685. data/rust/codegen/src/domain_generator.rs +1288 -0
  686. data/rust/codegen/src/exemplar.rs +230 -0
  687. data/rust/codegen/src/expr_emitter.rs +87 -0
  688. data/rust/codegen/src/fielded.rs +192 -0
  689. data/rust/codegen/src/hecks_naming.rs +33 -0
  690. data/rust/codegen/src/json.rs +335 -0
  691. data/rust/codegen/src/json_codec.rs +697 -0
  692. data/rust/codegen/src/literal.rs +207 -0
  693. data/rust/codegen/src/main.rs +290 -0
  694. data/rust/codegen/src/manifest.rs +227 -0
  695. data/rust/codegen/src/mutations.rs +784 -0
  696. data/rust/codegen/src/naming.rs +286 -0
  697. data/rust/codegen/src/ports.rs +208 -0
  698. data/rust/codegen/src/prelude.rs +116 -0
  699. data/rust/codegen/src/queries.rs +786 -0
  700. data/rust/codegen/src/reactions.rs +532 -0
  701. data/rust/codegen/src/read_models.rs +697 -0
  702. data/rust/codegen/src/reference_specs.rs +40 -0
  703. data/rust/codegen/src/registry.rs +966 -0
  704. data/rust/codegen/src/reserved_names.rs +76 -0
  705. data/rust/codegen/src/shared.rs +93 -0
  706. data/rust/codegen/src/skip_reason.rs +25 -0
  707. data/rust/codegen/src/types.rs +362 -0
  708. data/rust/host/.cargo/config.toml +3 -0
  709. data/rust/host/.gitignore +1 -0
  710. data/rust/host/Cargo.lock +4297 -0
  711. data/rust/host/Cargo.toml +61 -0
  712. data/rust/host/HECKS_RELEASE +1 -0
  713. data/rust/host/rds-ca-bundle.pem +2736 -0
  714. data/rust/host/src/api.rs +1596 -0
  715. data/rust/host/src/approval.rs +652 -0
  716. data/rust/host/src/auth.rs +1287 -0
  717. data/rust/host/src/bin/expr_harness.rs +46 -0
  718. data/rust/host/src/bin/lineage_harness.rs +119 -0
  719. data/rust/host/src/bin/mint_harness.rs +135 -0
  720. data/rust/host/src/checkout.rs +265 -0
  721. data/rust/host/src/dispatch.rs +1443 -0
  722. data/rust/host/src/expr_json.rs +1117 -0
  723. data/rust/host/src/field_hints.rs +52 -0
  724. data/rust/host/src/ir.rs +733 -0
  725. data/rust/host/src/journal.rs +874 -0
  726. data/rust/host/src/lambda_client.rs +378 -0
  727. data/rust/host/src/log.rs +147 -0
  728. data/rust/host/src/main.rs +474 -0
  729. data/rust/host/src/mint.rs +1865 -0
  730. data/rust/host/src/payments/keystore.rs +330 -0
  731. data/rust/host/src/payments/tests.rs +1400 -0
  732. data/rust/host/src/payments.rs +767 -0
  733. data/rust/host/src/presentation.rs +698 -0
  734. data/rust/host/src/presentation_write.rs +1431 -0
  735. data/rust/host/src/query_step.rs +149 -0
  736. data/rust/host/src/rate_limit.rs +904 -0
  737. data/rust/host/src/reference_transform.rs +241 -0
  738. data/rust/host/src/reference_validate.rs +338 -0
  739. data/rust/host/src/resend.rs +296 -0
  740. data/rust/host/src/secrets.rs +106 -0
  741. data/rust/host/src/server.rs +663 -0
  742. data/rust/host/src/storage_shape.rs +175 -0
  743. data/rust/host/src/test_pg.rs +40 -0
  744. data/rust/host/src/ui_schema.rs +1351 -0
  745. data/rust/host/src/wasm_runner.rs +191 -0
  746. data/rust/host/src/web/newsletter/cooldown.rs +126 -0
  747. data/rust/host/src/web/newsletter/tests.rs +104 -0
  748. data/rust/host/src/web/newsletter.rs +310 -0
  749. data/rust/host/src/web/newsletter_send.rs +357 -0
  750. data/rust/host/src/web/registrations.rs +1505 -0
  751. data/rust/host/src/web.rs +2937 -0
  752. data/rust/lsp/.gitignore +1 -0
  753. data/rust/lsp/Cargo.lock +7 -0
  754. data/rust/lsp/Cargo.toml +16 -0
  755. data/rust/lsp/README.md +133 -0
  756. data/rust/lsp/src/diagnostics.rs +165 -0
  757. data/rust/lsp/src/json.rs +385 -0
  758. data/rust/lsp/src/main.rs +399 -0
  759. data/rust/lsp/src/outline.rs +177 -0
  760. data/rust/lsp/src/rpc.rs +67 -0
  761. data/rust/parser/.cargo/config.toml +5 -0
  762. data/rust/parser/.gitignore +1 -0
  763. data/rust/parser/Cargo.lock +7 -0
  764. data/rust/parser/Cargo.toml +21 -0
  765. data/rust/parser/src/build/closed_sets.rs +27 -0
  766. data/rust/parser/src/build/identity.rs +276 -0
  767. data/rust/parser/src/build/mod.rs +12 -0
  768. data/rust/parser/src/build/naming.rs +141 -0
  769. data/rust/parser/src/build/pattern_subset.rs +161 -0
  770. data/rust/parser/src/build/query_derive.rs +72 -0
  771. data/rust/parser/src/build/query_inference.rs +269 -0
  772. data/rust/parser/src/build/query_options.rs +100 -0
  773. data/rust/parser/src/build/read_model.rs +94 -0
  774. data/rust/parser/src/build/references.rs +32 -0
  775. data/rust/parser/src/canonical.rs +114 -0
  776. data/rust/parser/src/diag.rs +55 -0
  777. data/rust/parser/src/emit.rs +953 -0
  778. data/rust/parser/src/expr/ast_json.rs +147 -0
  779. data/rust/parser/src/expr/evaluator.rs +81 -0
  780. data/rust/parser/src/expr/mod.rs +84 -0
  781. data/rust/parser/src/expr/resolver.rs +497 -0
  782. data/rust/parser/src/ir.rs +387 -0
  783. data/rust/parser/src/keywords.rs +585 -0
  784. data/rust/parser/src/lex.rs +662 -0
  785. data/rust/parser/src/main.rs +252 -0
  786. data/rust/parser/src/parse/aggregate.rs +612 -0
  787. data/rust/parser/src/parse/chapter.rs +517 -0
  788. data/rust/parser/src/parse/command.rs +637 -0
  789. data/rust/parser/src/parse/domain_port.rs +127 -0
  790. data/rust/parser/src/parse/entity.rs +527 -0
  791. data/rust/parser/src/parse/file.rs +67 -0
  792. data/rust/parser/src/parse/hecksagon.rs +231 -0
  793. data/rust/parser/src/parse/lifecycle.rs +162 -0
  794. data/rust/parser/src/parse/mod.rs +1262 -0
  795. data/rust/parser/src/parse/policy.rs +87 -0
  796. data/rust/parser/src/parse/process_manager.rs +305 -0
  797. data/rust/parser/src/parse/query.rs +100 -0
  798. data/rust/parser/src/parse/read_model.rs +214 -0
  799. data/rust/parser/src/parse/value_object.rs +250 -0
  800. data/rust/parser/src/ruby_value.rs +422 -0
  801. data/rust/project/bridging.rb +257 -0
  802. data/rust/project/commands.rb +877 -0
  803. data/rust/project/constraints.rb +86 -0
  804. data/rust/project/dependency_planning.rb +178 -0
  805. data/rust/project/domain_generator.rb +980 -0
  806. data/rust/project/exemplar.rb +148 -0
  807. data/rust/project/expr_emitter.rb +72 -0
  808. data/rust/project/fielded.rb +141 -0
  809. data/rust/project/json_codec.rb +641 -0
  810. data/rust/project/mutations.rb +721 -0
  811. data/rust/project/naming.rb +205 -0
  812. data/rust/project/ports.rb +65 -0
  813. data/rust/project/queries.rb +493 -0
  814. data/rust/project/reactions.rb +317 -0
  815. data/rust/project/read_models.rb +690 -0
  816. data/rust/project/reference_specs.rb +27 -0
  817. data/rust/project/registry.rb +396 -0
  818. data/rust/project/skip_reason.rb +29 -0
  819. data/rust/project/types.rb +225 -0
  820. data/rust/project/write_if_changed.rb +83 -0
  821. data/rust/project.rb +30 -0
  822. data/rust/project_rust_pipeline.rb +383 -0
  823. data/rust/src/exemplar/commands.rs +279 -0
  824. data/rust/src/exemplar/constraints.rs +18 -0
  825. data/rust/src/exemplar/fielded.rs +280 -0
  826. data/rust/src/exemplar/json.rs +277 -0
  827. data/rust/src/exemplar/mod.rs +17 -0
  828. data/rust/src/exemplar/mutations.rs +80 -0
  829. data/rust/src/exemplar/queries.rs +33 -0
  830. data/rust/src/exemplar/reactions.rs +88 -0
  831. data/rust/src/exemplar/read_models.rs +48 -0
  832. data/rust/src/exemplar/registry.rs +166 -0
  833. data/rust/src/exemplar/types.rs +44 -0
  834. data/rust/src/kernel/attribute_shapes/composite.rs +88 -0
  835. data/rust/src/kernel/attribute_shapes/list.rs +18 -0
  836. data/rust/src/kernel/attribute_shapes/mod.rs +44 -0
  837. data/rust/src/kernel/attribute_shapes/optional.rs +9 -0
  838. data/rust/src/kernel/attribute_shapes/scalar.rs +39 -0
  839. data/rust/src/kernel/cli.rs +585 -0
  840. data/rust/src/kernel/dispatch.rs +1090 -0
  841. data/rust/src/kernel/expr.rs +458 -0
  842. data/rust/src/kernel/expression_operators/arithmetic.rs +128 -0
  843. data/rust/src/kernel/expression_operators/comparison.rs +48 -0
  844. data/rust/src/kernel/expression_operators/enumeration.rs +232 -0
  845. data/rust/src/kernel/expression_operators/logical.rs +13 -0
  846. data/rust/src/kernel/expression_operators/membership.rs +18 -0
  847. data/rust/src/kernel/expression_operators/mod.rs +68 -0
  848. data/rust/src/kernel/expression_operators/pattern_match.rs +73 -0
  849. data/rust/src/kernel/expression_operators/positional.rs +126 -0
  850. data/rust/src/kernel/expression_operators/presence.rs +101 -0
  851. data/rust/src/kernel/expression_operators/sign_test.rs +16 -0
  852. data/rust/src/kernel/expression_operators/sized.rs +35 -0
  853. data/rust/src/kernel/expression_operators/text.rs +79 -0
  854. data/rust/src/kernel/expression_operators/to_string.rs +18 -0
  855. data/rust/src/kernel/json.rs +779 -0
  856. data/rust/src/kernel/mod.rs +133 -0
  857. data/rust/src/kernel/named_query.rs +219 -0
  858. data/rust/src/kernel/orchestrate.rs +1810 -0
  859. data/rust/src/kernel/pattern.rs +307 -0
  860. data/rust/src/kernel/query_comparators.rs +206 -0
  861. data/rust/src/kernel/query_ordering.rs +103 -0
  862. data/rust/src/kernel/read_model.rs +914 -0
  863. data/rust/src/kernel/reference_lookup.rs +149 -0
  864. data/rust/src/kernel/repository.rs +468 -0
  865. data/rust/src/kernel/routing.rs +612 -0
  866. data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +118 -0
  867. data/rust/src/kernel/vocab/entity_dispatch_order.rs +114 -0
  868. data/rust/src/kernel/vocab/field_hint.rs +70 -0
  869. data/rust/src/kernel/vocab/mod.rs +20 -0
  870. data/rust/src/kernel/vocab/query_comparator.rs +65 -0
  871. data/rust/src/kernel/vocab/refusal_template.rs +1592 -0
  872. data/rust/src/lib.rs +8 -0
  873. data/rust/src/main.rs +16 -0
  874. data/rust/web/.gitignore +1 -0
  875. data/rust/web/Cargo.lock +119 -0
  876. data/rust/web/Cargo.toml +18 -0
  877. data/rust/web/src/lib.rs +8 -0
  878. metadata +478 -12
  879. data/lib/hecks/facade/cli_door.rb +0 -178
  880. data/lib/hecks/facade/cli_runner.rb +0 -271
  881. data/lib/hecks/facade/handle.rb +0 -293
  882. data/lib/hecks/facade/json_door.rb +0 -247
  883. data/lib/hecks/facade/surface/aggregate_door.rb +0 -222
  884. data/lib/hecks/facade/surface/chapter.rb +0 -116
  885. data/lib/hecks/facade/surface.rb +0 -61
  886. data/lib/hecks/mcp_door_scope.rb +0 -176
@@ -2,82 +2,8 @@ require_relative "../projector"
2
2
 
3
3
  module Hecks
4
4
  module Projections
5
- # A domain's own shape, projected as Mermaid diagrams — the same
6
- # trick `Projections::Reference`/`DocsProjector` already play for
7
- # prose, one level further: a diagram generated from the
8
- # declaration can't drift from it the way a hand-drawn one
9
- # inevitably does, because there is no second copy to forget to
10
- # update.
11
- #
12
- # ## Why Mermaid, not Graphviz
13
- #
14
- # Mermaid, not Graphviz (the two considered) — every diagram type
15
- # below has a Mermaid form purpose-built for exactly what the
16
- # underlying construct already is (a `lifecycle` is a state
17
- # machine, `has_many`/`belongs_to` already speaks in cardinality,
18
- # `emits`/`trigger` already is a directed graph), and the output is
19
- # plain text that renders natively wherever this project's own docs
20
- # already live — GitHub markdown, this repo's generated docs, Claude
21
- # Artifacts — with no build step and no external binary. Graphviz's
22
- # dot format needs an actual render step (a `dot` binary, or a WASM
23
- # port) to become anything viewable, which is a real dependency this
24
- # repository's own discipline (see rust/parser's Cargo.toml: "no
25
- # dependency earns its way past std") would rather not take just to
26
- # draw a diagram.
27
- #
28
- # ## Diagram kinds
29
- #
30
- # Four diagram kinds, one file each per domain except lifecycles
31
- # (one per lifecycle-bearing construct, since that's how a reader
32
- # actually reaches for it — looking at one aggregate's states, not
33
- # every aggregate's at once):
34
- #
35
- # <Name>_lifecycle.mmd stateDiagram-v2 one per lifecycle
36
- # relationships.mmd erDiagram the whole domain's has_many/
37
- # has_one/belongs_to/reference_to
38
- # dispatch.mmd flowchart the whole domain's command
39
- # emits -> policy trigger chains
40
- # roles.mmd flowchart every role that issues a
41
- # command, wired to every
42
- # command it issues
43
- # ports.mmd flowchart every port operation, which
44
- # aggregate exposes it, which
45
- # aggregate it routes to: (if
46
- # any), and what it emits
47
- # read_models.mmd flowchart every read_model, and every
48
- # aggregate it's assembled
49
- # from — the read-side
50
- # complement to relationships.mmd
51
- # <Name>_surface.mmd flowchart one per aggregate/entity that
52
- # declares at least one command
53
- # or query — everything you can
54
- # do to it and ask about it,
55
- # and what each command writes,
56
- # in one place
57
- # <Name>_saga.mmd stateDiagram-v2 one per process_manager —
58
- # its own states, and what
59
- # each transition dispatches
60
- # elsewhere in the domain
61
- # frameworks.mmd flowchart every other domain this one
62
- # depends on — a shared
63
- # framework it `uses_framework`,
64
- # or a domain a policy reaches
65
- # `across` — the one diagram
66
- # here that looks outward past
67
- # this domain's own boundary
68
- #
69
- # ## Node and construct naming
70
- #
71
- # Construct names (aggregate/entity/command/event) are used bare,
72
- # unsanitized, as Mermaid node/entity ids — safe because this
73
- # language's own word grammar only ever admits simple CamelCase/
74
- # snake_case identifiers there (confirmed: no space or punctuation
75
- # appears in any real aggregate/command/event name across the corpus
76
- # this projects from). A `role:` string is free text, though — the
77
- # real corpus already has "Back office"/"Vault officer"/"Branch
78
- # clerk" — so `roles.mmd` is the one diagram here that sanitizes a
79
- # name into an id (`role_id`) while keeping the real string as the
80
- # node's own displayed label.
5
+ # Projects a domain's declaration as Mermaid diagrams, one file per diagram kind.
6
+ # Mermaid is plain text that renders in GitHub markdown with no build step or binary.
81
7
  module Diagrams
82
8
  extend Hecks::Projector::Target
83
9
 
@@ -137,66 +63,22 @@ module Hecks
137
63
  files
138
64
  end
139
65
 
140
- # ── shared ────────────────────────────────────────────────────────
141
-
142
- # Every aggregate and entity in this domain, flattened into one list —
143
- # everything a lifecycle/relationship/command diagram walks alike.
144
- #
145
- # An entity can carry its own lifecycle, relationship, or command
146
- # too — its own `lifecycle`/`reference_to`/`command` block,
147
- # addressed through its holding aggregate the same way
148
- # `DocsProjector` already treats an aggregate and its entities
149
- # alike. Walking both here means a domain's entity gaining any of
150
- # these needs no change to this file.
151
- #
152
- # @param bluebook [Bluebook::Chapter] the chapter to walk
153
- # @return [Array<Bluebook::Aggregate, Class>] every aggregate, and every entity
154
- # class (a `Bluebook::Entity` subclass) nested under it, in declaration order
66
+ # Every aggregate and entity in this domain, flattened into one list.
155
67
  def holders(bluebook)
156
68
  bluebook.aggregates.flat_map { |aggregate| [aggregate, *aggregate.entities] }
157
69
  end
158
70
 
159
- # The holders (see `holders`) that declare their own `lifecycle` block —
160
- # one diagram file is projected per lifecycle-bearing holder, not one
161
- # per domain.
162
- #
163
- # @param bluebook [Bluebook::Chapter] the chapter to walk
164
- # @return [Array<Bluebook::Aggregate, Class>] every holder with a non-nil
165
- # `lifecycle`
166
71
  def holders_with_lifecycle(bluebook) = holders(bluebook).select(&:lifecycle)
167
72
 
168
- # Renders the two-line `%%` comment banner every generated diagram file
169
- # opens with, warning against hand edits and naming how to regenerate it.
170
- #
171
- # `chapter_name` drives the re-run hint always — that's the one
172
- # argument `bin/project_diagrams` actually takes, regardless of
173
- # which single aggregate/entity `subject` happens to name. Passing
174
- # the wrong one here once already produced a real, committed
175
- # `Order_lifecycle.mmd` telling a reader to run
176
- # `bin/project_diagrams <domain-path> Order` — a chapter name
177
- # Hecks.boot has never heard of.
178
- #
179
- # @param chapter_name [String] the domain's chapter name, used in the re-run hint
180
- # @param subject [String] prose naming what this specific file was generated from
181
- # @return [String] the two-line `%%`-commented Mermaid banner, newline-terminated
73
+ # `chapter_name` drives the re-run hint: `hecks project_diagrams` takes a chapter
74
+ # name, not the subject's.
182
75
  def header(chapter_name, subject)
183
76
  <<~HEADER
184
- %% GENERATED by bin/project_diagrams from #{subject} — DO NOT EDIT BY HAND.
185
- %% Re-run `bin/project_diagrams <domain-path> #{chapter_name}` after any change.
77
+ %% GENERATED by hecks project_diagrams from #{subject} — DO NOT EDIT BY HAND.
78
+ %% Re-run `hecks project_diagrams <domain-path> #{chapter_name}` after any change.
186
79
  HEADER
187
80
  end
188
81
 
189
- # ── lifecycle -> stateDiagram-v2 ─────────────────────────────────
190
-
191
- # Renders one holder's own declared `lifecycle` as a Mermaid
192
- # `stateDiagram-v2` — its default starting state, and one edge per
193
- # transition, labeled by the command that causes it.
194
- #
195
- # @param bluebook [Bluebook::Chapter] the chapter `holder` belongs to, for the
196
- # file's re-run banner
197
- # @param holder [Bluebook::Aggregate, Class] the aggregate or entity class (a
198
- # `Bluebook::Entity` subclass) whose `lifecycle` to draw
199
- # @return [String] the rendered Mermaid `stateDiagram-v2` source
200
82
  def lifecycle_diagram(bluebook, holder)
201
83
  lifecycle = holder.lifecycle
202
84
  edges = lifecycle.transitions.flat_map do |command_name, transition|
@@ -211,28 +93,8 @@ module Hecks
211
93
  MERMAID
212
94
  end
213
95
 
214
- # ── relationships -> erDiagram ───────────────────────────────────
215
-
216
- # Renders the whole domain's declared `has_many`/`has_one`/`belongs_to`/
217
- # `reference_to` attributes as a Mermaid `erDiagram`, one edge per
218
- # reference attribute across every holder.
219
- #
220
- # Standard crow's-foot reading, the same convention every ORM's own
221
- # ERD generator (Rails' erd gem included) already uses:
222
- # `has_many`/`has_one` are read from the owning side — one Holder
223
- # relates to many/one Target. `belongs_to`/`reference_to` are read
224
- # from the target's side instead — one Target can be pointed at by
225
- # many Holders — because a bare reference carries no promise about
226
- # how many holders point back at it; "many" is the honest default
227
- # absent a declared uniqueness rule this language doesn't expose.
228
- # `optional?` only ever softens the side that can genuinely be
229
- # absent (a nilable reference, an empty has_one) — never the
230
- # crow's-foot "many" marker, which is a structural fact independent
231
- # of any one instance's optionality.
232
- #
233
- # @param bluebook [Bluebook::Chapter] the chapter to project
234
- # @return [String, nil] the rendered Mermaid `erDiagram` source, or `nil` if the
235
- # domain declares no reference attribute at all
96
+ # `has_many`/`has_one` read from the owning side; `belongs_to`/`reference_to` read
97
+ # from the target's side, since a bare reference does not promise uniqueness.
236
98
  def relationship_diagram(bluebook)
237
99
  edges = holders(bluebook).flat_map do |holder|
238
100
  holder.attributes.select(&:reference?).map { |attribute| relationship_edge(holder, attribute) }
@@ -243,14 +105,6 @@ module Hecks
243
105
  "#{header(bluebook.name, subject)}erDiagram\n#{edges.join("\n")}\n"
244
106
  end
245
107
 
246
- # One erDiagram edge for one holder's own declared reference attribute,
247
- # in the DSL word's own crow's-foot reading (see `relationship_diagram`).
248
- #
249
- # @param holder [Bluebook::Aggregate, Class] the aggregate or entity class (a
250
- # `Bluebook::Entity` subclass) that declares `attribute`
251
- # @param attribute [Bluebook::Attribute] a reference attribute (`attribute.reference?`
252
- # is true); its `relationship` names which DSL word minted it
253
- # @return [String] the rendered `erDiagram` edge line
254
108
  def relationship_edge(holder, attribute)
255
109
  target = attribute.type.target_name
256
110
  case attribute.relationship
@@ -263,20 +117,6 @@ module Hecks
263
117
  end
264
118
  end
265
119
 
266
- # ── dispatch -> flowchart ─────────────────────────────────────────
267
-
268
- # A command node, stadium-shaped (`(["..."])`); an event node,
269
- # hexagonal (`{{"..."}}`) — one visual vocabulary for "a thing
270
- # someone does" versus "a fact that happened", matching the
271
- # language's own verb/event distinction. Command ids are qualified
272
- # by their owning aggregate (`cmd_Order_Purchase`) since two
273
- # aggregates may share a command name; event ids are bare
274
- # (`evt_PizzaCreated`) since an event is this domain's own
275
- # addressing key, the same way `policy.on_event` reaches it.
276
- #
277
- # @param bluebook [Bluebook::Chapter] the chapter to project
278
- # @return [String, nil] the rendered Mermaid flowchart source, or `nil` if the
279
- # domain declares no command `emits` and no policy at all
280
120
  def dispatch_diagram(bluebook)
281
121
  lines = []
282
122
 
@@ -295,33 +135,11 @@ module Hecks
295
135
  "#{header(bluebook.name, subject)}flowchart LR\n#{lines.uniq.join("\n")}\n"
296
136
  end
297
137
 
298
- # One flowchart edge from a command to one event it declares it `emits`.
299
- #
300
- # @param holder [Bluebook::Aggregate, Class] the aggregate or entity class (a
301
- # `Bluebook::Entity` subclass) that declares `command`
302
- # @param command [Class] the command class (a `Bluebook::Command` subclass) that
303
- # emits `event`
304
- # @param event [String] the emitted event's name
305
- # @return [String] the rendered `-->|emits|` edge line
306
138
  def emits_edge(holder, command, event)
307
139
  %( #{command_node(holder.hecks_name, command.hecks_name)} -->|emits| #{event_node(event)})
308
140
  end
309
141
 
310
- # `on_event` is sometimes aggregate-qualified
311
- # (`"Account.AccountFrozen"`) and sometimes bare
312
- # (`"CustomerSuspended"`) in the real corpus — `emits` never is,
313
- # so this always matches against the bare tail, the same
314
- # normalization a reader has to do by eye today.
315
- #
316
- # A trigger crossing into another domain (`policy.target_domain`)
317
- # still draws — the target command just has no incoming `emits`
318
- # edge of its own here, which honestly shows "dispatch continues
319
- # elsewhere" rather than silently dropping the edge. The label
320
- # names which domain, so that's not a dead end on the page either.
321
- #
322
- # @param policy [Bluebook::Policy] the policy to draw as a trigger edge
323
- # @return [String] the rendered `-->|triggers|` (or `-->|triggers in <domain>|`)
324
- # edge line
142
+ # `on_event` may be aggregate-qualified while `emits` never is, so match on the bare tail.
325
143
  def trigger_edge(policy)
326
144
  bare_event = policy.on_event.to_s.split(".").last
327
145
  aggregate_name, command_name = policy.trigger_command.to_s.split(".", 2)
@@ -329,39 +147,13 @@ module Hecks
329
147
  %( #{event_node(bare_event)} -->|#{label}| #{command_node(aggregate_name, command_name)})
330
148
  end
331
149
 
332
- # Mermaid stadium-shaped node id and label for one command, qualified by
333
- # its owning aggregate since two aggregates may share a command name.
334
- #
335
- # @param aggregate_name [String] the command's owning aggregate/entity name
336
- # @param command_name [String] the command's own name
337
- # @return [String] the rendered `cmd_<aggregate>_<command>(["..."])` node
150
+ # Qualified by aggregate since two aggregates may share a command name.
338
151
  def command_node(aggregate_name, command_name)
339
152
  %(cmd_#{aggregate_name}_#{command_name}(["#{aggregate_name}.#{command_name}"]))
340
153
  end
341
154
 
342
- # Mermaid hexagonal node id and label for one event, addressed bare since
343
- # an event is this domain's own addressing key.
344
- #
345
- # @param event_name [String] the event's own name
346
- # @return [String] the rendered `evt_<event>{{"..."}}` node
347
155
  def event_node(event_name) = %(evt_#{event_name}{{"#{event_name}"}})
348
156
 
349
- # ── roles -> flowchart ────────────────────────────────────────────
350
-
351
- # Renders every command's declared `role` as a Mermaid flowchart, one
352
- # edge per role issuing a command.
353
- #
354
- # Who issues what, across the whole domain — data no existing
355
- # projection draws at all today (the reference pages' own
356
- # `command_entry` only ever prints a command's role as a single
357
- # line of prose, never assembled across commands). A command with
358
- # no declared `role` draws nothing — there is no fact to state.
359
- # Circle-shaped so a role reads as "who" beside `dispatch.mmd`'s
360
- # stadium ("what someone does") and hexagon ("what happened").
361
- #
362
- # @param bluebook [Bluebook::Chapter] the chapter to project
363
- # @return [String, nil] the rendered Mermaid flowchart source, or `nil` if no
364
- # command in the domain declares a `role`
365
157
  def roles_diagram(bluebook)
366
158
  lines = holders(bluebook).flat_map do |holder|
367
159
  holder.commands.select(&:role).map { |command| role_edge(holder, command) }
@@ -372,68 +164,16 @@ module Hecks
372
164
  "#{header(bluebook.name, subject)}flowchart LR\n#{lines.uniq.join("\n")}\n"
373
165
  end
374
166
 
375
- # One flowchart edge from a command's declared role to the command
376
- # itself.
377
- #
378
- # @param holder [Bluebook::Aggregate, Class] the aggregate or entity class (a
379
- # `Bluebook::Entity` subclass) that declares `command`
380
- # @param command [Class] the command class (a `Bluebook::Command` subclass); its
381
- # `role` is drawn
382
- # @return [String] the rendered `-->|issues|` edge line
383
167
  def role_edge(holder, command)
384
168
  %( #{role_node(command.role)} -->|issues| #{command_node(holder.hecks_name, command.hecks_name)})
385
169
  end
386
170
 
387
- # Mermaid circular node id and label for one role, keeping the real
388
- # free-text role string as the displayed label (see `role_id`).
389
- #
390
- # @param role_name [String] the command's declared `role` text
391
- # @return [String] the rendered `role_<id>((<role>))` node
392
171
  def role_node(role_name) = %(#{role_id(role_name)}((#{role_name})))
393
172
 
394
- # Sanitizes a free-text role name into a legal Mermaid node id.
395
- #
396
- # A role name is free text ("Back office", "Vault officer") —
397
- # unlike every other name this file uses as a bare id, this one
398
- # has to be sanitized to become a legal Mermaid identifier. The
399
- # real string still appears as the node's own label
400
- # (`role_node`); only the id is mangled.
401
- #
402
- # @param role_name [String] the command's declared `role` text
403
- # @return [String] `role_` followed by `role_name` with every run of
404
- # non-alphanumeric characters collapsed to a single underscore
173
+ # A role is free text ("Back office"), so only its id is sanitized; the label keeps the text.
405
174
  def role_id(role_name) = "role_#{role_name.to_s.gsub(/[^A-Za-z0-9]+/, '_')}"
406
175
 
407
- # ── ports -> flowchart ───────────────────────────────────────────
408
-
409
- # A port operation is a boundary translation, not a verb or a fact —
410
- # its own reference page says so plainly ("the builder behind it
411
- # defines no `given` or `sets`, so an operation cannot read
412
- # aggregate state or mutate a record itself"), so it gets a third
413
- # shape, a trapezoid, beside `dispatch.mmd`'s stadium/hexagon
414
- # vocabulary. An aggregate drawn as a `to:` target is a cylinder —
415
- # state landing somewhere, the same reason a data store gets one
416
- # in an ordinary flowchart.
417
- #
418
- # Two edge kinds per operation: a dotted "exposes" edge from the
419
- # aggregate the port hangs off (always present — a port always
420
- # belongs to exactly one aggregate), and a solid "to:" edge to
421
- # whichever aggregate the operation itself names as its receiver
422
- # (present only when `to:` is declared — an operation with no
423
- # declared target has no destination of its own to draw).
424
- # `emits` reuses `dispatch.mmd`'s own `event_node` unchanged — the
425
- # same fact, reached from a different direction.
426
- #
427
- # `bluebook.aggregates`, not the shared `holders` — unlike a
428
- # lifecycle/relationship/command, a port belongs to an aggregate
429
- # only; an entity has no `ports` method at all (confirmed: calling
430
- # it raises, it isn't just always empty), so walking entities here
431
- # the way every other diagram in this file does would crash on
432
- # the first entity-bearing domain.
433
- #
434
- # @param bluebook [Bluebook::Chapter] the chapter to project
435
- # @return [String, nil] the rendered Mermaid flowchart source, or `nil` if the
436
- # domain declares no port operation at all
176
+ # Walks `bluebook.aggregates`, not `holders`: an entity has no `ports` method.
437
177
  def ports_diagram(bluebook)
438
178
  lines = bluebook.aggregates.flat_map do |holder|
439
179
  holder.ports.flat_map { |port| port.operations.map { |operation| port_edges(holder, port, operation) } }
@@ -445,14 +185,6 @@ module Hecks
445
185
  "#{header(bluebook.name, subject)}flowchart LR\n#{lines.uniq.join("\n")}\n"
446
186
  end
447
187
 
448
- # All of one port operation's own edges: the aggregate that exposes it,
449
- # its optional `to:` routing target, and one edge per event it `emits`.
450
- #
451
- # @param holder [Bluebook::Aggregate] the aggregate `port` is attached to
452
- # @param port [Bluebook::DomainPort] the port `operation` belongs to
453
- # @param operation [Bluebook::PortOperation] the operation to draw
454
- # @return [Array<String>] the rendered edge lines: one "exposes" edge, plus a
455
- # "to:" edge when `operation.to` is declared, plus one "emits" edge per event
456
188
  def port_edges(holder, port, operation)
457
189
  op = port_operation_node(holder.hecks_name, port.name, operation.hecks_name)
458
190
  edges = [" #{holder.hecks_name}[(#{holder.hecks_name})] -.->|exposes| #{op}"]
@@ -461,53 +193,11 @@ module Hecks
461
193
  edges
462
194
  end
463
195
 
464
- # Mermaid trapezoidal node id and label for one port operation, qualified
465
- # by both its owning aggregate and its owning port.
466
- #
467
- # @param aggregate_name [String] the operation's owning aggregate name
468
- # @param port_name [String] the operation's owning port name
469
- # @param operation_name [String] the operation's own name
470
- # @return [String] the rendered `op_<aggregate>_<port>_<operation>[/"..."/]` node
471
196
  def port_operation_node(aggregate_name, port_name, operation_name)
472
197
  id = "op_#{aggregate_name}_#{port_name}_#{operation_name}"
473
198
  %(#{id}[/"#{port_name}.#{operation_name}"/])
474
199
  end
475
200
 
476
- # ── read models -> flowchart ─────────────────────────────────────
477
-
478
- # Renders the whole domain's declared `read_models` as a Mermaid
479
- # flowchart, one edge per aggregate a read model is assembled from.
480
- #
481
- # The read-side complement to `relationships.mmd` — that diagram
482
- # shows how aggregates reference each other for writes
483
- # (`has_many`/`belongs_to`/`reference_to`); this shows how a
484
- # `read_model` assembles data for reads, from
485
- # `aggregate_heads` — the same list `where`/`group_by`/`order_by`
486
- # all operate over, and the one fact every read_model has
487
- # regardless of whether it's rooted (`reference_target`) or
488
- # gathers heads with no root at all (a rootless read model, real
489
- # in the corpus: `AccountsByKind`).
490
- #
491
- # A read model is a subroutine shape (`[[...]]`, "a predefined
492
- # process") — a fourth shape, beside `ports.mmd`'s trapezoid and
493
- # `dispatch.mmd`'s stadium/hexagon: not a verb, not a fact, not a
494
- # boundary translation, but a standing, reusable view. Every
495
- # aggregate it draws from is a cylinder — the same "state lands
496
- # somewhere" shape `ports.mmd`'s `to:` target already uses, and
497
- # the same bare id, so an aggregate feeding several read_models
498
- # (real in banking: `Account` feeds four) merges into one node
499
- # across the whole diagram.
500
- #
501
- # The label names the shape of the answer, not just the name —
502
- # `(count)`/`(median: field)` for the two real aggregations in the
503
- # corpus, nothing appended for an ordinary row-returning
504
- # read_model. Still MVP scope: `where`/`group_by`/`order_by`
505
- # aren't drawn at all yet — real facts, not invented, just not
506
- # this diagram's job yet.
507
- #
508
- # @param bluebook [Bluebook::Chapter] the chapter to project
509
- # @return [String, nil] the rendered Mermaid flowchart source, or `nil` if the
510
- # domain declares no `read_model` at all
511
201
  def read_model_diagram(bluebook)
512
202
  lines = bluebook.read_models.flat_map { |read_model| read_model_edges(read_model) }
513
203
  return nil if lines.empty?
@@ -516,36 +206,16 @@ module Hecks
516
206
  "#{header(bluebook.name, subject)}flowchart LR\n#{lines.uniq.join("\n")}\n"
517
207
  end
518
208
 
519
- # One edge per aggregate one read model is assembled from, into that
520
- # read model's own subroutine-shaped node.
521
- #
522
- # @param read_model [Bluebook::ReadModel] the read model to draw
523
- # @return [Array<String>] the rendered edge lines, one per entry in
524
- # `read_model.to_h[:aggregate_heads]`
525
209
  def read_model_edges(read_model)
526
210
  shape = read_model.to_h
527
211
  node = %(rm_#{shape[:name]}[["#{read_model_label(shape)}"]])
528
212
  Array(shape[:aggregate_heads]).map do |head|
529
- # **Quoted, not bare** — an edge label containing `[` or `]`
530
- # (`accounts[]`, marking the "many" side) breaks Mermaid's own
531
- # `|label|` parser outright if left unquoted: it reads the
532
- # `[` as the start of a new node shape mid-label, not text.
533
- # Confirmed live against the real parser before this quoting
534
- # existed — every other edge label in this file happens to be
535
- # a bare word or already-quoted string, so this is the one
536
- # spot that needed it.
213
+ # Quoted because an unquoted `[` in an edge label (`accounts[]`) breaks Mermaid's parser.
537
214
  label = head[:many] ? "#{head[:as]}[]" : head[:as]
538
215
  %( #{head[:aggregate]}[(#{head[:aggregate]})] -->|"#{label}"| #{node})
539
216
  end
540
217
  end
541
218
 
542
- # The read model's own subroutine node label — its name, with
543
- # `(count)`/`(median: field)` appended for the two aggregations this
544
- # diagram states.
545
- #
546
- # @param shape [Hash{Symbol => Object}] a `Bluebook::ReadModel#to_h` result;
547
- # reads `:name`, `:count` and `:median_field`
548
- # @return [String] the node's display label
549
219
  def read_model_label(shape)
550
220
  return "#{shape[:name]} (count)" if shape[:count]
551
221
  return "#{shape[:name]} (median: #{shape[:median_field]})" if shape[:median_field]
@@ -553,61 +223,6 @@ module Hecks
553
223
  shape[:name]
554
224
  end
555
225
 
556
- # ── surface -> flowchart ─────────────────────────────────────────
557
-
558
- # Renders one holder's own commands (and what each writes) and queries
559
- # as a Mermaid flowchart.
560
- #
561
- # "What can I do to this, what can I ask about it" — one file per
562
- # holder, unlike every other diagram here: `dispatch.mmd` already
563
- # shows a command's own onward reaction chain, but never an
564
- # aggregate's own full command/query menu in one place, and
565
- # `roles.mmd` shows who issues a command without saying what else
566
- # that same aggregate answers. This is the one diagram meant to
567
- # be read starting from the aggregate, not from a verb or a fact.
568
- #
569
- # A query is a diamond — a fifth shape, beside `dispatch.mmd`'s
570
- # stadium/hexagon, `ports.mmd`'s trapezoid, and `read_models.mmd`'s
571
- # subroutine: a question with an answer, not a verb that changes
572
- # anything. Command edges are solid ("does"); query edges are
573
- # dotted ("asks") — the same solid/dotted split `ports.mmd`
574
- # already uses for "routes to:" versus "exposes".
575
- #
576
- # A write target is a plain rectangle — a sixth shape, the first
577
- # here with no special bracket at all: an attribute is the
578
- # smallest, most passive thing this vocabulary names, a single
579
- # field living inside the cylinder rather than a bounded thing of
580
- # its own. `command.mutations` (`sets`/`increment`/`decrement`/
581
- # `append`) was invisible everywhere before this — not just in a
582
- # diagram, in any projection, including the prose ones — despite
583
- # being the single densest fact in the whole IR (53 real
584
- # mutations across pizzas + banking). `dispatch.mmd` draws what a
585
- # command emits; this draws what it writes, the other half of
586
- # "what actually happens" a command never showed before.
587
- #
588
- # The same attribute node merges across commands — real in
589
- # banking: `Account.Credit` and `Account.Debit` both point at the
590
- # same `balance` node, the same "one node, several incoming
591
- # edges" merge `read_models.mmd` already does for an aggregate
592
- # fed by several read_models.
593
- #
594
- # The label names the real source, not just the verb — an
595
- # increment/decrement/set almost always takes its value from an
596
- # argument, but not always the same-named one: real in banking,
597
- # `Account.Credit`'s own `balance` is incremented by its
598
- # `amount` argument, and `LedgerEntry.Amend`'s own `amount` is
599
- # incremented by its `adjustment` argument. A literal source
600
- # (pizzas' own `Order.Purchase` sets `status` to the literal
601
- # `"sold"`, not an argument at all) is named as verbatim as
602
- # every other fact in this file. `append`'s own fields carry no
603
- # single source at all — its own field names are the fact worth
604
- # stating (real: `Order.AddTopping` appends `name, amount`).
605
- #
606
- # @param bluebook [Bluebook::Chapter] the chapter `holder` belongs to, for the
607
- # file's re-run banner
608
- # @param holder [Bluebook::Aggregate, Class] the aggregate or entity class (a
609
- # `Bluebook::Entity` subclass) whose commands and queries to draw
610
- # @return [String] the rendered Mermaid flowchart source
611
226
  def surface_diagram(bluebook, holder)
612
227
  lines = holder.commands.map do |command|
613
228
  " #{holder.hecks_name}[(#{holder.hecks_name})] -->|does| #{command_node(holder.hecks_name, command.hecks_name)}"
@@ -625,25 +240,10 @@ module Hecks
625
240
  "#{header(bluebook.name, subject)}flowchart LR\n#{lines.uniq.join("\n")}\n"
626
241
  end
627
242
 
628
- # Mermaid diamond-shaped node id and label for one query, qualified by
629
- # its owning aggregate.
630
- #
631
- # @param aggregate_name [String] the query's owning aggregate/entity name
632
- # @param query_name [String] the query's own name
633
- # @return [String] the rendered `qry_<aggregate>_<query>{"..."}` node
634
243
  def query_node(aggregate_name, query_name)
635
244
  %(qry_#{aggregate_name}_#{query_name}{"#{aggregate_name}.#{query_name}"})
636
245
  end
637
246
 
638
- # One flowchart edge from a command to one attribute it mutates,
639
- # labeled with what the mutation writes (see `mutation_label`).
640
- #
641
- # @param holder [Bluebook::Aggregate, Class] the aggregate or entity class (a
642
- # `Bluebook::Entity` subclass) that declares `command` and the mutated attribute
643
- # @param command [Class] the command class (a `Bluebook::Command` subclass) that
644
- # declares `mutation`
645
- # @param mutation [Bluebook::Mutation] the mutation to draw
646
- # @return [String] the rendered `-->|"..."|` edge line
647
247
  def mutation_edge(holder, command, mutation)
648
248
  shape = mutation.to_h
649
249
  label = mutation_label(shape)
@@ -651,101 +251,27 @@ module Hecks
651
251
  %( #{command_node(holder.hecks_name, command.hecks_name)} -->|"#{label}"| #{target})
652
252
  end
653
253
 
654
- # The edge label naming what one mutation writes — the op, and either
655
- # its bound field names (an append/delegate/corrects) or its value
656
- # source (see `mutation_source_detail`).
657
- #
658
- # @param shape [Hash{Symbol => Object}] a `Bluebook::Mutation#to_h` result;
659
- # reads `:op`, and either `:fields` or `:source`
660
- # @return [String] the rendered `"<op>s: <detail>"` label text
661
254
  def mutation_label(shape)
662
255
  verb = "#{shape[:op]}s"
663
- # `fields:` (not `source:`) is the multi-binding shape
664
- # (`Mutation#to_h`'s own `[:append, :delegate, :corrects]`
665
- # branch) — checked by the key's presence, not by re-listing
666
- # which ops use it a second time here, the same lesson
667
- # `Change.op`'s own `admits: Vocabulary::MutationOp` already
668
- # drew (command.bluebook's own comment): a second list of "the
669
- # ops that mean multi-binding" is exactly the kind of copy that
670
- # drifts — `:delegate` already carried this shape with nothing
671
- # here reading it correctly, caught only once `:corrects` gave
672
- # banking's own real diagrams a fields:-shaped mutation to
673
- # actually render.
256
+ # Keyed on `fields:` being present, not on a list of multi-binding ops.
674
257
  detail = shape[:fields] ? shape[:fields].keys.join(", ") : mutation_source_detail(shape[:source])
675
258
  "#{verb}: #{detail}"
676
259
  end
677
260
 
678
- # A literal value can contain a double quote of its own — real in
679
- # banking: `Customer.Reinstate` sets `standing` to a rendered
680
- # value-object literal, `{:value=>"good"}`, whose own embedded `"`
681
- # broke this label's outer `|"..."|` quoting outright (caught by
682
- # running the real generated output through mermaid.parse(), not
683
- # by eye — the same way `read_models.mmd`'s own unquoted `[]` bug
684
- # was caught). Swapped for a single quote here rather than
685
- # escaped, the same "state it, don't invent it, just make it
686
- # legal Mermaid" trade `read_models.mmd`'s own quoting fix made.
687
- #
688
- # @param source [Hash{Symbol => Object}] a classified mutation source: `{kind:
689
- # "literal", value:}`, `{kind: "argument", name:}`, or `{kind: "state", name:}`
690
- # @return [String] `source[:value]` quoted for a literal, `source[:name]` for an
691
- # argument, or `source[:kind]` itself for any other kind
261
+ # A literal can contain `"`, which breaks the label's `|"..."|` quoting; swap it for `'`.
692
262
  def mutation_source_detail(source)
693
263
  case source[:kind]
694
264
  when "literal" then "'#{source[:value].to_s.tr('"', "'")}'"
695
265
  when "argument" then source[:name]
696
266
  else
697
- # A source kind this file has no real corpus example of yet — named, not hidden.
698
267
  source[:kind]
699
268
  end
700
269
  end
701
270
 
702
- # Mermaid plain-rectangle node id and label for one attribute a
703
- # mutation writes, qualified by its owning holder.
704
- #
705
- # @param holder_name [String] the attribute's owning aggregate/entity name
706
- # @param attribute_name [Symbol, String] the mutated attribute's own name
707
- # @return [String] the rendered `attr_<holder>_<attribute>[<attribute>]` node
708
271
  def attribute_node(holder_name, attribute_name)
709
272
  %(attr_#{holder_name}_#{attribute_name}[#{attribute_name}])
710
273
  end
711
274
 
712
- # ── sagas -> stateDiagram-v2 ─────────────────────────────────────
713
-
714
- # A saga has a lifecycle too — the same `stateDiagram-v2` shape
715
- # `lifecycle_diagram` already draws, one file per process_manager
716
- # the same way lifecycle is one file per lifecycle-bearing holder.
717
- # What's different is the label: a lifecycle's own edge is labeled
718
- # by the command that causes it (an aggregate transitions because
719
- # something was done to it); a saga's edge is labeled by the event
720
- # that causes it (a saga advances because something happened,
721
- # possibly nowhere near the saga itself) — the same command/event
722
- # split `dispatch.mmd`'s own stadium/hexagon vocabulary already
723
- # draws, here spent on which noun labels a stateDiagram-v2 edge
724
- # instead.
725
- #
726
- # The label also names what the transition dispatches — a fact no
727
- # existing diagram states for a saga at all: a lifecycle's own
728
- # edge only ever names the one command that caused it; a saga's
729
- # edge can fire several commands at once (real in banking:
730
- # Settlement's own AccountDebited handler dispatches both
731
- # Transfer.Debited and Account.Credit). Confirmed real in the
732
- # corpus: no saga dispatch ever declares a `to:`/`target_domain`
733
- # of its own (unlike a policy's `across`) — every command a saga
734
- # fires lands inside its own bluebook chapter, so this never needs
735
- # `dispatch.mmd`'s own "triggers in X" cross-domain label.
736
- #
737
- # The compensating leg reads like any other — its own trigger is
738
- # the literal string "refused" (`ProcessManager::REFUSED`, this
739
- # language's own Trigger vocabulary), not invented text: a
740
- # dispatch declined is exactly as real a cause of a state
741
- # transition as an event announced, and the diagram states it
742
- # exactly as verbatim as every other edge here does.
743
- #
744
- # @param bluebook [Bluebook::Chapter] the chapter `saga` belongs to, for the
745
- # file's re-run banner
746
- # @param saga [Bluebook::ProcessManager] the process manager whose states and
747
- # dispatches to draw
748
- # @return [String] the rendered Mermaid `stateDiagram-v2` source
749
275
  def saga_diagram(bluebook, saga)
750
276
  edges = saga.handlers.map { |handler| saga_edge(handler, saga) }
751
277
 
@@ -758,32 +284,11 @@ module Hecks
758
284
  MERMAID
759
285
  end
760
286
 
761
- # One stateDiagram-v2 edge for one saga handler, labeled by the event
762
- # that causes it and what it dispatches.
763
- #
764
- # The `REFUSED` edge's own dispatch list is partly derived now —
765
- # per-dispatch saga compensation (`compensates`) moved a saga's own
766
- # compensating dispatches off the hand-written `on :refused` leg
767
- # and onto whichever forward dispatch each one undoes, so
768
- # `handler.dispatches` alone would render an empty compensating
769
- # edge for any saga using it — accurate to the declaration, wrong
770
- # about what the runtime actually does at refusal (it derives and
771
- # fires every declared `compensates`, newest first). `saga` is
772
- # passed through for exactly this — only the `REFUSED` handler needs
773
- # it, every other edge's own `handler.dispatches` already says
774
- # everything real about it.
775
- #
776
- # @param handler [Bluebook::ProcessManagerHandler] the handler row to draw as
777
- # an edge
778
- # @param saga [Bluebook::ProcessManager] the process manager `handler` belongs
779
- # to, needed only to derive the `REFUSED` edge's compensating dispatches
780
- # @return [String] the rendered `-->` stateDiagram-v2 edge line
287
+ # `saga` is needed only for the `REFUSED` edge, whose compensating dispatches are
288
+ # derived from the forward dispatches' `compensates`, not written on the handler.
781
289
  def saga_edge(handler, saga)
782
290
  label = handler.event_type
783
- # Derived first, then the hand-written body — the same order
784
- # `SagaInterpreter#unwind` actually runs them in (every
785
- # completed leg's own `compensates` before this leg's own
786
- # hand-written dispatches), not declaration order on the page.
291
+ # Derived compensations first, matching the order `SagaInterpreter#unwind` runs them.
787
292
  dispatched = handler.event_type == Bluebook::ProcessManager::REFUSED ? derived_compensations(saga) : []
788
293
  dispatched += handler.dispatches.map(&:command_name)
789
294
  label += " / dispatches #{dispatched.join(', ')}" unless dispatched.empty?
@@ -791,70 +296,13 @@ module Hecks
791
296
  " #{handler.from_state} --> #{handler.to_state}: #{label}"
792
297
  end
793
298
 
794
- # Every `compensates` any forward dispatch in this saga declares,
795
- # declaration order — the same commands `SagaInterpreter#unwind`
796
- # derives and fires (newest-first, at actual refusal time; this
797
- # diagram states them in declaration order, since it draws the
798
- # saga's own shape, not one instance's own runtime history).
799
- #
800
- # @param saga [Bluebook::ProcessManager] the process manager whose declared
801
- # compensations to list
802
- # @return [Array<String>] the name of each compensating command declared anywhere
803
- # in `saga`, in declaration order
299
+ # Listed in declaration order; at refusal the runtime fires them newest first.
804
300
  def derived_compensations(saga)
805
301
  saga.handlers.flat_map { |handler| handler.dispatches.filter_map { |dispatch| dispatch.compensates&.command_name } }
806
302
  end
807
303
 
808
- # ── frameworks -> flowchart ─────────────────────────────────────
809
-
810
- # Renders the whole domain's declared `uses_framework` attachments and
811
- # cross-domain policy targets as a Mermaid flowchart.
812
- #
813
- # Every other diagram in this file stays inside one domain's own
814
- # boundary — this is the one that steps outside it. A real domain
815
- # depends on another domain's own aggregates in exactly two ways:
816
- # `uses_framework "X"` in its `.hecksagon` (`Hecksagon#framework_
817
- # members`), which loads X's whole bluebook into this registry,
818
- # unconditionally, the moment this domain boots; or a policy's own
819
- # `across "X"` (`Policy#target_domain`), which only reaches X when
820
- # the policy's declared event actually fires. Same underlying
821
- # fact `dispatch.mmd`'s own `trigger_edge` already draws from the
822
- # command's side ("triggers in X") — this draws it again from the
823
- # domain's side, next to the structural `uses_framework` fact
824
- # `dispatch.mmd` never sees at all (that lives in the `.hecksagon`,
825
- # which no other diagram here is handed).
826
- #
827
- # Neither this domain nor each dependency gets the holders() treatment
828
- # — a whole domain is drawn as one cylinder, the same "a bounded,
829
- # addressable thing" shape every other diagram here already spends
830
- # on a single aggregate, just scaled up one level: a domain is a
831
- # bigger box the same kind of box lives inside.
832
- #
833
- # Dotted for `attaches`, solid for `reaches across` — the reverse
834
- # of which fact is "always true" between the two: attaching a
835
- # framework is a standing declaration, true every time this domain
836
- # boots, so it gets the same dotted "this always belongs" treatment
837
- # `ports.mmd` gives an aggregate's own `-.->|exposes|` edge.
838
- # Reaching across only happens when a real policy actually fires —
839
- # the same solid edge `dispatch.mmd`'s own `trigger_edge` already
840
- # draws for the identical fact, kept solid here so the same
841
- # relationship reads the same way in both diagrams.
842
- #
843
- # `options[:hecksagon]` is the one diagram in this file that needs
844
- # more than `bluebook` — `framework_members` lives on the
845
- # `Hecksagon`, a sibling IR object `bin/project_diagrams` already
846
- # has in hand (`registry.hecksagon(chapter_name)`) but `bluebook`
847
- # itself carries no reference to. No hecksagon handed in (an older
848
- # caller, or a spec that doesn't care) just means no frameworks.mmd
849
- # — same "nothing to state" skip every other diagram here already
850
- # takes when its own underlying data is empty.
851
- #
852
- # @param bluebook [Bluebook::Chapter] the chapter to project
853
- # @param hecksagon [Bluebook::Hecksagon, nil] the chapter's `.hecksagon`, holding
854
- # its `framework_members`, or `nil` if the caller has none to hand in
855
- # @return [String, nil] the rendered Mermaid flowchart source, or `nil` if
856
- # `hecksagon` is `nil`, or the domain declares no `uses_framework` and no
857
- # cross-domain policy target
304
+ # `hecksagon` is a separate argument because `bluebook` carries no reference to it;
305
+ # without one there is no frameworks diagram.
858
306
  def frameworks_diagram(bluebook, hecksagon)
859
307
  return nil unless hecksagon
860
308
 
@@ -867,16 +315,6 @@ module Hecks
867
315
  "#{header(bluebook.name, subject)}flowchart LR\n#{lines.uniq.join("\n")}\n"
868
316
  end
869
317
 
870
- # One flowchart edge from this domain to a dependency, dotted for a
871
- # standing `attaches` and solid for a `reaches across` that only fires
872
- # when a policy does.
873
- #
874
- # @param from [String] this domain's own name
875
- # @param label [String] the edge label, `"attaches"` or `"reaches across"`
876
- # @param to [String, Symbol] the dependency domain's name
877
- # @param dotted [Boolean] whether to render a dotted (`true`) or solid (`false`)
878
- # arrow
879
- # @return [String] the rendered edge line
880
318
  def domain_edge(from, label, to, dotted:)
881
319
  arrow = dotted ? "-.->" : "-->"
882
320
  %( #{from}[(#{from})] #{arrow}|#{label}| #{to}[(#{to})])