hecks 1.3.0 → 1.5.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 (373) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hecks/adapters/driven/claude_code.rb +72 -7
  3. data/lib/hecks/adapters/driven/d1.rb +187 -23
  4. data/lib/hecks/adapters/driven/folder.rb +83 -10
  5. data/lib/hecks/adapters/driven/google_authentication.rb +33 -12
  6. data/lib/hecks/adapters/driven/governance_authorization.rb +87 -18
  7. data/lib/hecks/adapters/driven/heki/journal.rb +13 -4
  8. data/lib/hecks/adapters/driven/heki/saga_store.rb +56 -10
  9. data/lib/hecks/adapters/driven/heki/snapshot.rb +1 -1
  10. data/lib/hecks/adapters/driven/heki.rb +106 -9
  11. data/lib/hecks/adapters/driven/identity_registry.rb +12 -2
  12. data/lib/hecks/adapters/driven/in_memory_ordering.rb +25 -3
  13. data/lib/hecks/adapters/driven/in_process_key_vault.adapter +3 -0
  14. data/lib/hecks/adapters/driven/in_process_key_vault.rb +53 -0
  15. data/lib/hecks/adapters/driven/lambda/client.rb +67 -14
  16. data/lib/hecks/adapters/driven/lambda.rb +82 -35
  17. data/lib/hecks/adapters/driven/local_storage.rb +83 -10
  18. data/lib/hecks/adapters/driven/memory.rb +205 -9
  19. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +21 -1
  20. data/lib/hecks/adapters/driven/postgres/codec.rb +27 -11
  21. data/lib/hecks/adapters/driven/postgres/outbox.rb +40 -2
  22. data/lib/hecks/adapters/driven/postgres/reconnect.rb +23 -7
  23. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +14 -14
  24. data/lib/hecks/adapters/driven/postgres.rb +175 -28
  25. data/lib/hecks/adapters/driven/prism.rb +50 -11
  26. data/lib/hecks/adapters/driven/secure_random_identity.rb +3 -0
  27. data/lib/hecks/adapters/driven/sql_query_builder.rb +34 -22
  28. data/lib/hecks/adapters/driven/sqlite/codec.rb +38 -10
  29. data/lib/hecks/adapters/driven/sqlite/projection.rb +60 -32
  30. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +12 -12
  31. data/lib/hecks/adapters/driven/sqlite.rb +181 -21
  32. data/lib/hecks/adapters/driven/system_clock.rb +3 -0
  33. data/lib/hecks/adapters/driven/tenant_provisioner.adapter +3 -0
  34. data/lib/hecks/adapters/driven/tenant_provisioner.rb +66 -0
  35. data/lib/hecks/adapters/driven.rb +6 -4
  36. data/lib/hecks/adapters/driving/github_webhook.rb +31 -18
  37. data/lib/hecks/behaviors/dsl.rb +60 -2
  38. data/lib/hecks/behaviors/expectations.rb +190 -29
  39. data/lib/hecks/behaviors/ir.rb +12 -1
  40. data/lib/hecks/behaviors/rspec.rb +9 -1
  41. data/lib/hecks/behaviors/runner.rb +21 -2
  42. data/lib/hecks/behaviors.rb +9 -1
  43. data/lib/hecks/bluebook/aggregate.rb +43 -13
  44. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +17 -10
  45. data/lib/hecks/bluebook/assembly/build.rb +26 -5
  46. data/lib/hecks/bluebook/assembly/contract.rb +98 -23
  47. data/lib/hecks/bluebook/assembly/contracts.rb +59 -52
  48. data/lib/hecks/bluebook/assembly/marks.rb +159 -30
  49. data/lib/hecks/bluebook/assembly/specializer.rb +38 -21
  50. data/lib/hecks/bluebook/assembly.rb +32 -14
  51. data/lib/hecks/bluebook/attribute.rb +26 -12
  52. data/lib/hecks/bluebook/behaviour/aggregate.rb +43 -11
  53. data/lib/hecks/bluebook/behaviour/attribute.rb +18 -5
  54. data/lib/hecks/bluebook/behaviour/chapter.rb +76 -5
  55. data/lib/hecks/bluebook/behaviour/command.rb +55 -25
  56. data/lib/hecks/bluebook/behaviour/domain_port.rb +27 -7
  57. data/lib/hecks/bluebook/behaviour/entity.rb +20 -8
  58. data/lib/hecks/bluebook/behaviour/hexagon.rb +30 -4
  59. data/lib/hecks/bluebook/behaviour/lifecycle.rb +27 -6
  60. data/lib/hecks/bluebook/behaviour/policy.rb +42 -17
  61. data/lib/hecks/bluebook/behaviour/process_manager.rb +39 -8
  62. data/lib/hecks/bluebook/behaviour/query.rb +6 -1
  63. data/lib/hecks/bluebook/behaviour/read_model.rb +29 -8
  64. data/lib/hecks/bluebook/behaviour/traits.rb +48 -12
  65. data/lib/hecks/bluebook/behaviour/value_object.rb +21 -9
  66. data/lib/hecks/bluebook/capabilities.rb +27 -0
  67. data/lib/hecks/bluebook/chapter.rb +51 -9
  68. data/lib/hecks/bluebook/command.rb +62 -17
  69. data/lib/hecks/bluebook/domain_port.rb +34 -9
  70. data/lib/hecks/bluebook/dsl/adapter_builder.rb +24 -0
  71. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +49 -49
  72. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +282 -123
  73. data/lib/hecks/bluebook/dsl/attribute_collector.rb +112 -75
  74. data/lib/hecks/bluebook/dsl/binding_proxy.rb +81 -2
  75. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +486 -117
  76. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +179 -47
  77. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
  78. data/lib/hecks/bluebook/dsl/command_builder.rb +284 -122
  79. data/lib/hecks/bluebook/dsl/const_shim.rb +46 -15
  80. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +90 -25
  81. data/lib/hecks/bluebook/dsl/entity_builder.rb +191 -61
  82. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
  83. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +130 -30
  84. data/lib/hecks/bluebook/dsl/identity_declaration.rb +38 -21
  85. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
  86. data/lib/hecks/bluebook/dsl/policy_builder.rb +86 -36
  87. data/lib/hecks/bluebook/dsl/port_builder.rb +38 -7
  88. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +56 -22
  89. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +111 -47
  90. data/lib/hecks/bluebook/dsl/query_builder.rb +37 -8
  91. data/lib/hecks/bluebook/dsl/read_model_builder.rb +127 -52
  92. data/lib/hecks/bluebook/dsl/rule_reference.rb +97 -43
  93. data/lib/hecks/bluebook/dsl/translation_builder.rb +150 -44
  94. data/lib/hecks/bluebook/dsl/value_object_builder.rb +68 -20
  95. data/lib/hecks/bluebook/dsl/word_gate.rb +59 -53
  96. data/lib/hecks/bluebook/dsl/world_builder.rb +51 -8
  97. data/lib/hecks/bluebook/entity.rb +40 -11
  98. data/lib/hecks/bluebook/expression/ast_json.rb +128 -36
  99. data/lib/hecks/bluebook/expression/ast_reader.rb +32 -3
  100. data/lib/hecks/bluebook/expression/canonical_form.rb +55 -16
  101. data/lib/hecks/bluebook/expression/evaluator.rb +221 -43
  102. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +54 -18
  103. data/lib/hecks/bluebook/expression/resolver.rb +369 -128
  104. data/lib/hecks/bluebook/hexagon.rb +35 -1
  105. data/lib/hecks/bluebook/lifecycle.rb +12 -1
  106. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +2 -1
  107. data/lib/hecks/bluebook/meta_validator/judge.rb +126 -108
  108. data/lib/hecks/bluebook/meta_validator/plan.rb +81 -46
  109. data/lib/hecks/bluebook/meta_validator/port_judge.rb +3 -2
  110. data/lib/hecks/bluebook/meta_validator/readings.rb +200 -50
  111. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +68 -41
  112. data/lib/hecks/bluebook/meta_validator/shapes.rb +166 -21
  113. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +286 -46
  114. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +11 -10
  115. data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -5
  116. data/lib/hecks/bluebook/meta_validator.rb +235 -139
  117. data/lib/hecks/bluebook/model_check.rb +434 -104
  118. data/lib/hecks/bluebook/pattern_subset.rb +32 -10
  119. data/lib/hecks/bluebook/policy.rb +15 -13
  120. data/lib/hecks/bluebook/process_manager.rb +27 -14
  121. data/lib/hecks/bluebook/project_discovery.rb +5 -0
  122. data/lib/hecks/bluebook/project_loader.rb +40 -0
  123. data/lib/hecks/bluebook/project_register.rb +50 -6
  124. data/lib/hecks/bluebook/query.rb +31 -4
  125. data/lib/hecks/bluebook/read_model.rb +35 -15
  126. data/lib/hecks/bluebook/reference.rb +26 -13
  127. data/lib/hecks/bluebook/smoke_test.rb +46 -23
  128. data/lib/hecks/bluebook/synthesizer.rb +46 -12
  129. data/lib/hecks/bluebook/translation.rb +34 -5
  130. data/lib/hecks/bluebook/value_object.rb +29 -11
  131. data/lib/hecks/bluebook.rb +5 -6
  132. data/lib/hecks/codemod.rb +138 -50
  133. data/lib/hecks/construct.rb +21 -7
  134. data/lib/hecks/corpus.rb +438 -0
  135. data/lib/hecks/deploy/bluebook/deploy.hecksagon +19 -0
  136. data/lib/hecks/doc/reference.rb +200 -31
  137. data/lib/hecks/embryonaut_bluebook.rb +38 -15
  138. data/lib/hecks/facade/cli_door.rb +69 -10
  139. data/lib/hecks/facade/cli_runner.rb +105 -24
  140. data/lib/hecks/facade/command_request.rb +23 -0
  141. data/lib/hecks/facade/handle.rb +155 -35
  142. data/lib/hecks/facade/json_door.rb +106 -25
  143. data/lib/hecks/facade/surface/aggregate_door.rb +50 -27
  144. data/lib/hecks/facade/surface/chapter.rb +26 -17
  145. data/lib/hecks/facade/surface.rb +16 -3
  146. data/lib/hecks/facade.rb +15 -4
  147. data/lib/hecks/forms/app.rb +46 -30
  148. data/lib/hecks/forms/command_form_renderer.rb +70 -9
  149. data/lib/hecks/forms/field_renderer.rb +142 -6
  150. data/lib/hecks/forms/field_shape.rb +183 -19
  151. data/lib/hecks/forms/html.rb +51 -7
  152. data/lib/hecks/forms/index_renderer.rb +14 -2
  153. data/lib/hecks/forms/page.rb +14 -0
  154. data/lib/hecks/forms/params.rb +120 -23
  155. data/lib/hecks/forms/port_argument.rb +14 -2
  156. data/lib/hecks/forms/query_form_renderer.rb +65 -2
  157. data/lib/hecks/forms/record_renderer.rb +60 -2
  158. data/lib/hecks/forms/record_table.rb +28 -1
  159. data/lib/hecks/forms/reference_options.rb +24 -0
  160. data/lib/hecks/forms/value_object_shape.rb +13 -3
  161. data/lib/hecks/forms.rb +24 -4
  162. data/lib/hecks/fqn.rb +59 -1
  163. data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
  164. data/lib/hecks/framework/bluebook/privacy.bluebook +155 -0
  165. data/lib/hecks/framework/oidc.json +15 -0
  166. data/lib/hecks/framework.rb +79 -25
  167. data/lib/hecks/freezer.rb +27 -11
  168. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +241 -106
  169. data/lib/hecks/fuzzing/combination_miner.rb +178 -0
  170. data/lib/hecks/fuzzing/concurrent_dispatch.rb +241 -45
  171. data/lib/hecks/fuzzing/coverage_campaign.rb +161 -0
  172. data/lib/hecks/fuzzing/differential.rb +192 -0
  173. data/lib/hecks/fuzzing/domain_generator.rb +871 -0
  174. data/lib/hecks/fuzzing/era_boundary.rb +68 -19
  175. data/lib/hecks/fuzzing/form_census.rb +121 -20
  176. data/lib/hecks/fuzzing/generated_domain_check.rb +171 -0
  177. data/lib/hecks/fuzzing/invalid_value_generator.rb +45 -6
  178. data/lib/hecks/fuzzing/isolated_boot.rb +137 -80
  179. data/lib/hecks/fuzzing/nondeterministic.rb +79 -0
  180. data/lib/hecks/fuzzing/persistence_parity.rb +111 -21
  181. data/lib/hecks/fuzzing/properties/corrections.rb +34 -9
  182. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +300 -51
  183. data/lib/hecks/fuzzing/properties/guards.rb +86 -42
  184. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +80 -32
  185. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +40 -35
  186. data/lib/hecks/fuzzing/properties/outbox.rb +70 -32
  187. data/lib/hecks/fuzzing/properties/querying.rb +82 -28
  188. data/lib/hecks/fuzzing/properties.rb +84 -51
  189. data/lib/hecks/fuzzing/qa_settings.rb +164 -0
  190. data/lib/hecks/fuzzing/replay.rb +241 -133
  191. data/lib/hecks/fuzzing/rotation_priority.rb +48 -28
  192. data/lib/hecks/fuzzing/rust_gap_manifest.rb +139 -0
  193. data/lib/hecks/fuzzing/self_consistency.rb +315 -137
  194. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +46 -40
  195. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +18 -11
  196. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +13 -12
  197. data/lib/hecks/fuzzing/sequence_generator/picker.rb +21 -12
  198. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +59 -32
  199. data/lib/hecks/fuzzing/sequence_generator.rb +158 -23
  200. data/lib/hecks/fuzzing/shrinker.rb +309 -0
  201. data/lib/hecks/fuzzing/structural_skips.rb +37 -130
  202. data/lib/hecks/fuzzing/sweep_depth.rb +12 -4
  203. data/lib/hecks/fuzzing/target_capabilities.rb +107 -29
  204. data/lib/hecks/fuzzing/value_generator.rb +110 -22
  205. data/lib/hecks/fuzzing.rb +5 -0
  206. data/lib/hecks/grammar/evolve.rb +188 -12
  207. data/lib/hecks/grammar.rb +53 -7
  208. data/lib/hecks/ir.rb +51 -20
  209. data/lib/hecks/language/bluebook/bluebook.bluebook +41 -0
  210. data/lib/hecks/language/bluebook/policy.bluebook +11 -1
  211. data/lib/hecks/language/bluebook/vocabulary.bluebook +348 -13
  212. data/lib/hecks/language/hecksagon/hecksagon.bluebook +11 -0
  213. data/lib/hecks/language/oidc.json +5 -0
  214. data/lib/hecks/literal.rb +41 -9
  215. data/lib/hecks/naming.rb +112 -31
  216. data/lib/hecks/ports/access_control.rb +53 -2
  217. data/lib/hecks/ports/agent/answers.rb +83 -6
  218. data/lib/hecks/ports/agent.rb +119 -35
  219. data/lib/hecks/ports/authentication.rb +44 -4
  220. data/lib/hecks/ports/authorization.rb +50 -11
  221. data/lib/hecks/ports/clock.rb +42 -23
  222. data/lib/hecks/ports/extraction.rb +16 -0
  223. data/lib/hecks/ports/identity_assignment.rb +23 -2
  224. data/lib/hecks/ports/identity_generation.rb +17 -3
  225. data/lib/hecks/ports/identity_resolution.rb +17 -1
  226. data/lib/hecks/ports/key_vault.port +6 -0
  227. data/lib/hecks/ports/key_vault.rb +58 -0
  228. data/lib/hecks/ports/loading.rb +4 -0
  229. data/lib/hecks/ports/persistence/append_only.rb +172 -8
  230. data/lib/hecks/ports/persistence/binding_policy.rb +34 -0
  231. data/lib/hecks/ports/persistence/codec_boundary.rb +178 -0
  232. data/lib/hecks/ports/persistence/execution.rb +4 -0
  233. data/lib/hecks/ports/persistence/null_saga_store.rb +12 -1
  234. data/lib/hecks/ports/persistence/plugin.rb +42 -4
  235. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +218 -25
  236. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +77 -9
  237. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +81 -24
  238. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +29 -18
  239. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +144 -60
  240. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +103 -8
  241. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +98 -23
  242. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +282 -109
  243. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +63 -25
  244. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +118 -66
  245. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +51 -28
  246. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +34 -5
  247. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +25 -12
  248. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +129 -34
  249. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +51 -6
  250. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +33 -9
  251. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +16 -0
  252. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +95 -6
  253. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +25 -2
  254. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +314 -90
  255. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +68 -10
  256. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +9 -3
  257. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +9 -2
  258. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +48 -8
  259. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +16 -1
  260. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +36 -5
  261. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +23 -3
  262. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +58 -19
  263. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +96 -5
  264. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +15 -0
  265. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +9 -1
  266. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -2
  267. data/lib/hecks/ports/persistence/plugins/era.rb +12 -2
  268. data/lib/hecks/ports/persistence/remote_runtime.rb +9 -2
  269. data/lib/hecks/ports/persistence/repository_factory.rb +23 -3
  270. data/lib/hecks/ports/persistence/state_codec.rb +319 -0
  271. data/lib/hecks/ports/persistence.rb +36 -1
  272. data/lib/hecks/ports/projection.rb +61 -7
  273. data/lib/hecks/ports/query/in_memory.rb +41 -8
  274. data/lib/hecks/ports/query/ordering.rb +21 -6
  275. data/lib/hecks/ports/query.rb +35 -0
  276. data/lib/hecks/ports.rb +1 -0
  277. data/lib/hecks/projections/bootstrap_table.rb +147 -0
  278. data/lib/hecks/projections/diagrams.rb +315 -79
  279. data/lib/hecks/projections/glossary/html.rb +91 -3
  280. data/lib/hecks/projections/glossary/markdown.rb +50 -3
  281. data/lib/hecks/projections/glossary/mermaid.rb +49 -1
  282. data/lib/hecks/projections/glossary/sections.rb +1 -1
  283. data/lib/hecks/projections/glossary/sentences.rb +110 -5
  284. data/lib/hecks/projections/glossary.rb +173 -31
  285. data/lib/hecks/projections/ir.rb +1 -1
  286. data/lib/hecks/projections/model/deviations.rb +62 -17
  287. data/lib/hecks/projections/model.rb +75 -21
  288. data/lib/hecks/projections/oidc.rb +45 -8
  289. data/lib/hecks/projections/parser_table.rb +53 -12
  290. data/lib/hecks/projections/reference.rb +15 -3
  291. data/lib/hecks/projections/rust_vocabulary.rb +646 -0
  292. data/lib/hecks/projections/shape.rb +10 -2
  293. data/lib/hecks/projections/statements.rb +69 -22
  294. data/lib/hecks/projections/vocabulary.rb +26 -9
  295. data/lib/hecks/projections.rb +5 -3
  296. data/lib/hecks/projector/cli_projector.rb +245 -37
  297. data/lib/hecks/projector/docs_projector.rb +154 -28
  298. data/lib/hecks/projector/exporter.rb +104 -29
  299. data/lib/hecks/projector/ir_projector.rb +7 -1
  300. data/lib/hecks/projector/narrate_projector.rb +144 -23
  301. data/lib/hecks/projector/target.rb +42 -18
  302. data/lib/hecks/projector.rb +86 -18
  303. data/lib/hecks/query_ir.rb +94 -47
  304. data/lib/hecks/query_specification/common/comparators.rb +19 -3
  305. data/lib/hecks/query_specification/common/comparison.rb +109 -27
  306. data/lib/hecks/query_specification/common/dsl.rb +65 -9
  307. data/lib/hecks/query_specification/common/null_policy.rb +61 -15
  308. data/lib/hecks/query_specification/common/null_semantics.rb +4 -0
  309. data/lib/hecks/query_specification/common/options.rb +25 -0
  310. data/lib/hecks/query_specification/field_path.rb +69 -15
  311. data/lib/hecks/query_specification/hop_path.rb +57 -20
  312. data/lib/hecks/query_specification/read_model/specification.rb +4 -0
  313. data/lib/hecks/rendering.rb +9 -3
  314. data/lib/hecks/router/namespace_installer.rb +16 -3
  315. data/lib/hecks/router.rb +56 -1
  316. data/lib/hecks/runtime/aggregate_lock.rb +20 -11
  317. data/lib/hecks/runtime/boot_gates.rb +21 -3
  318. data/lib/hecks/runtime/caller.rb +40 -8
  319. data/lib/hecks/runtime/capability_graph.rb +13 -2
  320. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +40 -41
  321. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +45 -67
  322. data/lib/hecks/runtime/command_interpreter.rb +195 -113
  323. data/lib/hecks/runtime/command_rules/admissibility.rb +231 -80
  324. data/lib/hecks/runtime/command_rules/arithmetic.rb +154 -96
  325. data/lib/hecks/runtime/command_rules/authorization.rb +38 -17
  326. data/lib/hecks/runtime/command_rules/emission.rb +18 -1
  327. data/lib/hecks/runtime/command_rules/references.rb +145 -55
  328. data/lib/hecks/runtime/command_rules.rb +3 -0
  329. data/lib/hecks/runtime/dependency_planning.rb +56 -11
  330. data/lib/hecks/runtime/dispatcher.rb +263 -92
  331. data/lib/hecks/runtime/entity_element.rb +304 -67
  332. data/lib/hecks/runtime/entity_interpreter.rb +149 -88
  333. data/lib/hecks/runtime/errors.rb +37 -23
  334. data/lib/hecks/runtime/event.rb +15 -10
  335. data/lib/hecks/runtime/identity.rb +91 -23
  336. data/lib/hecks/runtime/instance.rb +103 -18
  337. data/lib/hecks/runtime/interpreting.rb +24 -16
  338. data/lib/hecks/runtime/invocation.rb +358 -0
  339. data/lib/hecks/runtime/loader.rb +105 -19
  340. data/lib/hecks/runtime/outbox.rb +164 -26
  341. data/lib/hecks/runtime/policy_interpreter.rb +73 -60
  342. data/lib/hecks/runtime/port_operation_interpreter.rb +42 -19
  343. data/lib/hecks/runtime/query_interpreter.rb +93 -74
  344. data/lib/hecks/runtime/reaction_invocation.rb +73 -28
  345. data/lib/hecks/runtime/read_model_interpreter.rb +60 -44
  346. data/lib/hecks/runtime/rebuild_sweep.rb +32 -4
  347. data/lib/hecks/runtime/reference_hop.rb +48 -6
  348. data/lib/hecks/runtime/refusal_wording.rb +142 -115
  349. data/lib/hecks/runtime/registry/saga_persistence.rb +32 -21
  350. data/lib/hecks/runtime/registry/verification.rb +153 -28
  351. data/lib/hecks/runtime/registry.rb +202 -32
  352. data/lib/hecks/runtime/remote_dispatcher.rb +125 -24
  353. data/lib/hecks/runtime/routing.rb +36 -154
  354. data/lib/hecks/runtime/saga_interpreter/correlation.rb +25 -27
  355. data/lib/hecks/runtime/saga_interpreter.rb +90 -76
  356. data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
  357. data/lib/hecks/runtime/tenant_check.rb +33 -13
  358. data/lib/hecks/runtime/tenant_scope.rb +23 -5
  359. data/lib/hecks/runtime/value/admission.rb +75 -30
  360. data/lib/hecks/runtime/value/coercion.rb +555 -142
  361. data/lib/hecks/runtime/value/entity_list_coercion.rb +132 -60
  362. data/lib/hecks/runtime/value.rb +71 -21
  363. data/lib/hecks/runtime.rb +39 -7
  364. data/lib/hecks/storehouse.rb +368 -72
  365. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +130 -0
  366. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +32 -0
  367. data/lib/hecks/version.rb +3 -3
  368. data/lib/hecks/vocabulary.rb +205 -4
  369. data/lib/hecks.rb +91 -11
  370. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +19 -11
  371. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +24 -12
  372. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +38 -14
  373. metadata +28 -2
@@ -2,15 +2,16 @@ module Hecks
2
2
  module Bluebook
3
3
  # An attribute that points at another aggregate's head.
4
4
  #
5
- # This used to be the STRING `"Reference<Customer>"`, minted by
6
- # `AggregateBuilder#reference_to` and `CommandBuilder#cross_reference` from
7
- # a real constant that had just been handed in, and then parsed back apart
8
- # by a regex in the command interpreter, by string equality in the read-model
9
- # interpreter and the SQLite adapter, and by `delete_prefix` in the bluebook
10
- # builder. Five readers of a spelling one writer invented.
5
+ # Holds the target directly, and answers `resolve` with the target's
6
+ # Aggregate, rather than being spelled as the string `"Reference<Customer>"`
7
+ # that `AggregateBuilder#reference_to` and `CommandBuilder#cross_reference`
8
+ # would otherwise mint from the real constant just handed in. That string
9
+ # would need five separate readers to parse back apart — a regex in the
10
+ # command interpreter, string equality in the read-model interpreter and
11
+ # the SQLite adapter, and `delete_prefix` in the bluebook builder — one
12
+ # writer inventing a spelling, five readers reparsing it.
11
13
  #
12
- # It holds the TARGET instead, and answers `resolve` with the target's
13
- # Aggregate. Resolution is LAZY and deliberately so: `reference_to
14
+ # Resolution is lazy and deliberately so: `reference_to
14
15
  # Customer` may name an aggregate declared lower in the file — banking's
15
16
  # Account points at Customer and survives only because Customer happens to
16
17
  # be written above — so the edge cannot be resolved at declaration time.
@@ -29,13 +30,20 @@ module Hecks
29
30
  # up to the chapter, stamped once every sibling has been read.
30
31
  attr_accessor :declared_in
31
32
 
33
+ # @param target_name [Module, String, Symbol] the aggregate constant this
34
+ # reference points at, or its already-spelled name
32
35
  def initialize(target_name)
33
36
  @target_name = Naming.demodulise(target_name).to_s
34
37
  end
35
38
 
36
39
  # The Aggregate this points at, or nil when the target belongs to
37
- # ANOTHER domain — a cross-domain target may legitimately not be loaded,
40
+ # another domain — a cross-domain target may legitimately not be loaded,
38
41
  # the same reading `across` policies get.
42
+ #
43
+ # @return [Bluebook::Aggregate, nil] the target aggregate, or `nil` when
44
+ # `declared_in`'s own chapter does not declare one by this name
45
+ # @raise [DSL::Malformed] if `declared_in` is unset, so there is no
46
+ # chapter to resolve the target against
39
47
  def resolve
40
48
  unless declared_in
41
49
  raise DSL::Malformed,
@@ -48,16 +56,18 @@ module Hecks
48
56
  end
49
57
 
50
58
  # The IR spelling, the one the export carries.
59
+ #
60
+ # @return [String] `"Reference<TargetName>"`
51
61
  def to_s = "Reference<#{@target_name}>"
52
62
  def inspect = "#<Reference #{@target_name}>"
53
63
 
54
- # VALUE EQUALITY, not identity — without this, two references to
55
- # the SAME target, parsed from two SEPARATE bluebook reads (era
64
+ # Value equality, not identity — without this, two references to
65
+ # the same target, parsed from two separate bluebook reads (era
56
66
  # N's own boot and a held era's own shadow reconstruction,
57
67
  # coverage_check.rb's own comparison), are different objects and
58
68
  # compare unequal by Ruby's default `==`. `EraGuard::ShapeDiff
59
69
  # #diff_type`'s `held_type != current_type` check then reads as
60
- # true for EVERY reference_to attribute on EVERY mint, regardless
70
+ # true for every reference_to attribute on every mint, regardless
61
71
  # of whether the reference actually changed — a real refusal for
62
72
  # attributes nothing about. `declared_in` (which aggregate carries
63
73
  # this reference) is deliberately excluded: the same reference
@@ -65,7 +75,10 @@ module Hecks
65
75
  # from separately-parsed bluebooks with structurally different
66
76
  # (if same-shaped) owning aggregates, and `declared_in` is a
67
77
  # cross-reference for `resolve`, not part of what this attribute
68
- # itself IS.
78
+ # itself is.
79
+ #
80
+ # @param other [Object] the value to compare against
81
+ # @return [Boolean] whether `other` is a `Reference` to the same target
69
82
  def ==(other) = other.is_a?(Reference) && target_name == other.target_name
70
83
  alias eql? ==
71
84
  def hash = [self.class, target_name].hash
@@ -4,10 +4,10 @@ require "fileutils"
4
4
 
5
5
  module Hecks
6
6
  module Bluebook
7
- # BOOTS A REAL BLUEBOOK AND ACTUALLY DISPATCHES AGAINST IT — the
7
+ # Boots a real bluebook and actually dispatches against it — the
8
8
  # sibling `bin/model_check` never had: that tool proves a bluebook
9
- # is STRUCTURALLY sound (no dead states, no unreachable transitions)
10
- # without ever running a single command; this proves it WORKS,
9
+ # is structurally sound (no dead states, no unreachable transitions)
10
+ # without ever running a single command; this proves it works,
11
11
  # catching exactly the class of bug static analysis structurally
12
12
  # cannot see. Built and proved inside `bin/interview`'s own `smoke`
13
13
  # command, where it first found two real bugs — a lifecycle field
@@ -15,20 +15,24 @@ module Hecks
15
15
  # missing `Command.ActsOn` — that reloaded clean, checked clean,
16
16
  # and only broke once something actually dispatched against them.
17
17
  #
18
- # SYNTHESIZED ARGS, NEVER RANDOM (`Synthesizer`) — a String becomes
18
+ # Synthesized args, never random (`Synthesizer`) — a String becomes
19
19
  # a fixed marker, a closed set uses its own first admitted member, a
20
20
  # reference reuses whatever this same run already minted for that
21
- # target. ONE call per declared command, ONE call per declared
21
+ # target. One call per declared command, one call per declared
22
22
  # report, per created root; every failure is collected and reported
23
23
  # together rather than stopping at the first.
24
24
  #
25
- # WHAT THIS CANNOT CATCH: a call that dispatches cleanly but answers
26
- # WRONG — the read-model join bug this exact tool's own first user
27
- # hit is the textbook case, a query that silently returned an empty
28
- # array rather than raising. Smoke-testing proves nothing crashes;
29
- # it does not prove the answer is the right one.
25
+ # ## What this cannot catch
30
26
  #
31
- # A FAILURE HERE IS NOT NECESSARILY A DOMAIN BUG, either — it may
27
+ # A call that dispatches cleanly but answers wrong — the read-model join
28
+ # bug this exact tool's own first user hit is the textbook case, a query
29
+ # that silently returned an empty array rather than raising.
30
+ # Smoke-testing proves nothing crashes; it does not prove the answer is
31
+ # the right one.
32
+ #
33
+ # ## False positives from a naive synthesizer
34
+ #
35
+ # A failure here is not necessarily a domain bug, either — it may
32
36
  # just be `Synthesizer`'s own naive values (0, an empty list,
33
37
  # "smoke-test") not satisfying a real invariant this simple
34
38
  # generator was never built to reason about (a positive amount, a
@@ -54,11 +58,11 @@ module Hecks
54
58
  # per-domain realm resolution requires and a plain boot never
55
59
  # did. Reports are reachable the identical way regardless of
56
60
  # boot path — `dispatcher.query("Domain.report_name", **args)`
57
- # is the SAME `Dispatcher#query` either boot hands back; Router
58
- # was never actually required for this, only its OWN namespace
59
- # SUGAR (`Domain.report_name(...)` as a bare method call) was.
61
+ # is the same `Dispatcher#query` either boot hands back; Router
62
+ # was never actually required for this, only its own namespace
63
+ # sugar (`Domain.report_name(...)` as a bare method call) was.
60
64
  #
61
- # NEVER BOOTS `dir`'S OWN REAL BINDINGS — measured, not a
65
+ # Never boots `dir`'s own real bindings — measured, not a
62
66
  # precaution taken on spec: pointed at `examples/pizzas` (a real,
63
67
  # persistent store carrying real accumulated records), a
64
68
  # synthesized `CreatePizza` collided with an actual pre-existing
@@ -73,7 +77,7 @@ module Hecks
73
77
  # `install_facade: false` — this only ever dispatches by FQN string
74
78
  # (below), so it never needs the `Widget::Item.Add(...)` Ruby
75
79
  # sugar `Hecks.boot` installs by default. Skipping it matters
76
- # here specifically: that sugar lands as a BARE global constant on
80
+ # here specifically: that sugar lands as a bare global constant on
77
81
  # `Object` per domain and per aggregate name, with no scoping and
78
82
  # no cleanup — and this tool boots throwaway domains under
79
83
  # whatever generic names the caller's `.bluebook` happens to use
@@ -82,6 +86,11 @@ module Hecks
82
86
  # measured, not hypothetical: it once left a stale `Widget` constant
83
87
  # that corrupted an unrelated spec's own unrelated use of the same
84
88
  # bare name.
89
+ #
90
+ # @param dir [String] path to a bootable hecks app directory — a saved
91
+ # domain, or a throwaway one rendered into a temp dir for this purpose
92
+ # @return [Array<Failure>] every dispatch or query failure collected,
93
+ # empty when `dir` declares no bluebook or nothing failed
85
94
  def call(dir)
86
95
  Dir.mktmpdir("hecks-smoke-") do |scratch|
87
96
  isolate!(dir, scratch)
@@ -93,9 +102,9 @@ module Hecks
93
102
  end
94
103
  end
95
104
 
96
- # ONLY THE `.bluebook`, NEVER `.world` EITHER — a real world's own
105
+ # Only the `.bluebook`, never `.world` either — a real world's own
97
106
  # settings (`persisted_by("Heki") { dir "..." }`) are keyed to the
98
- # REAL adapter it names, not to Memory; copied verbatim, they
107
+ # real adapter it names, not to Memory; copied verbatim, they
99
108
  # apply to the wrong binding and refuse with a `WiringError`
100
109
  # ("Memory does not declare :dir") — measured, not assumed, the
101
110
  # first time this ran against Banking. A plain `Hecks.boot`
@@ -105,6 +114,11 @@ module Hecks
105
114
  # excluded — those are earlier, superseded versions of the same
106
115
  # domain; smoke-testing the current declaration is the point, not
107
116
  # its whole history.
117
+ #
118
+ # @param dir [String] path to the source hecks app directory to isolate
119
+ # @param scratch [String] path to the throwaway directory to copy the
120
+ # `.bluebook` files into
121
+ # @return [void]
108
122
  def isolate!(dir, scratch)
109
123
  source = Adapters::Folder.new.bluebook_directory(dir)
110
124
  target = File.join(scratch, "bluebook")
@@ -112,8 +126,8 @@ module Hecks
112
126
  Dir.glob(File.join(source, "*.bluebook")).each { |file| FileUtils.cp(file, target) }
113
127
  end
114
128
 
115
- # AGGREGATES WALKED IN DECLARATION ORDER, on purpose — so whatever
116
- # a LATER aggregate's own shape needs from an EARLIER one (a
129
+ # Aggregates walked in declaration order, on purpose — so whatever
130
+ # a later aggregate's own shape needs from an earlier one (a
117
131
  # `TripItem` needing a real `PackingItem`, say) already exists by
118
132
  # the time that aggregate's own turn comes, the same reason
119
133
  # `Synthesizer#args_for`'s `created` map is threaded through in
@@ -123,6 +137,14 @@ module Hecks
123
137
  # documents; splitting would turn that ordering into an implicit contract on
124
138
  # parameter/return passing instead of one visible sequence.
125
139
  # rubocop:disable-next Metrics/AbcSize
140
+ #
141
+ # @param dispatcher [Runtime::Dispatcher, Runtime::RemoteDispatcher] the
142
+ # booted dispatcher to dispatch synthesized commands and queries through
143
+ # @param domain [String] the domain name to smoke-test, a key of
144
+ # `dispatcher.registry.bluebooks`
145
+ # @return [Array<Failure>] every dispatch or query failure collected, in
146
+ # declaration order; empty when `domain` names no loaded chapter or
147
+ # nothing failed
126
148
  def smoke_domain(dispatcher, domain)
127
149
  chapter = dispatcher.registry.bluebook(domain)
128
150
  return [] unless chapter
@@ -136,7 +158,7 @@ module Hecks
136
158
  creating.each do |command|
137
159
  args = Synthesizer.args_for(chapter, aggregate, command, created)
138
160
  begin
139
- result = dispatcher.dispatch("#{domain}::#{aggregate.name}.#{command.hecks_name}", **args)
161
+ result = dispatcher.dispatch_flat("#{domain}::#{aggregate.name}.#{command.hecks_name}", args)
140
162
  created[aggregate.name] = result.instance.id
141
163
  rescue StandardError => e
142
164
  failures << Failure.new(domain: domain, aggregate: aggregate.name, command: command.hecks_name,
@@ -146,7 +168,7 @@ module Hecks
146
168
 
147
169
  noncreating.each do |nc_command|
148
170
  nc_args = Synthesizer.args_for(chapter, aggregate, nc_command, created).merge(id: created[aggregate.name])
149
- dispatcher.dispatch("#{domain}::#{aggregate.name}.#{nc_command.hecks_name}", **nc_args)
171
+ dispatcher.dispatch_flat("#{domain}::#{aggregate.name}.#{nc_command.hecks_name}", nc_args)
150
172
  rescue StandardError => e
151
173
  failures << Failure.new(domain: domain, aggregate: aggregate.name, command: nc_command.hecks_name,
152
174
  error: "#{e.class}: #{e.message}")
@@ -156,7 +178,8 @@ module Hecks
156
178
 
157
179
  chapter.read_models.each do |model|
158
180
  root_id = created[model.reference_target]
159
- next unless root_id # nothing was created for this root — nothing to smoke-test yet, not a failure
181
+ # Nothing was created for this root — nothing to smoke-test yet, not a failure.
182
+ next unless root_id
160
183
 
161
184
  dispatcher.query("#{domain}.#{model.query_name}", model.reference_name => root_id)
162
185
  rescue StandardError => e
@@ -1,12 +1,12 @@
1
1
  module Hecks
2
2
  module Bluebook
3
- # ONE SYNTHESIZED ARGUMENT PER DECLARED ATTRIBUTE — never a random
3
+ # **One synthesized argument per declared attribute** — never a random
4
4
  # guess. Reads only real `IR` (`Aggregate`, `Command`,
5
- # `Attribute`, `ValueObject`), so it works against ANY
5
+ # `Attribute`, `ValueObject`), so it works against any
6
6
  # loaded bluebook, not one particular domain: a String becomes a
7
- # fixed marker; an Integer becomes 0; a closed set uses ITS OWN
7
+ # fixed marker; an Integer becomes 0; a closed set uses its own
8
8
  # first admitted member (never a value a real `one_of` would
9
- # legitimately refuse); a reference uses whatever id the CALLER
9
+ # legitimately refuse); a reference uses whatever id the caller
10
10
  # already knows for that target (`created`), so a command whose
11
11
  # own shape depends on an earlier-created record — a `TripItem`
12
12
  # needing a real `PackingItem`, say — gets something real to work
@@ -16,8 +16,8 @@ module Hecks
16
16
  # got built and proved — the logic never actually knew anything
17
17
  # about the Interview domain or any one app; it only ever read IR.
18
18
  module Synthesizer
19
- # THE ONLY TYPE NAMES THAT ARE EVER TRULY SCALAR — anything else
20
- # a value object's own field names is ANOTHER value object,
19
+ # The only type names that are ever truly scalar — anything else
20
+ # a value object's own field names is another value object,
21
21
  # nested (`Pizza.price_cents: Price`, itself wrapping `cents:
22
22
  # Integer` — a real, ordinary shape, not an edge case). Missing
23
23
  # this the first time round meant a nested field silently
@@ -26,7 +26,7 @@ module Hecks
26
26
  # only by testing against Pizzas instead of re-testing against
27
27
  # the one simpler domain this was first built for, where every
28
28
  # value object happened to be single-field and primitive-typed.
29
- # THE SAME LIST `Attribute::PRIMITIVES` holds, referenced rather
29
+ # The same list `Attribute::PRIMITIVES` holds, referenced rather
30
30
  # than repeated. It was a second, byte-identical copy — and only
31
31
  # the Attribute one is held to `vocabulary.bluebook` by
32
32
  # spec/vocabulary_conformance_spec, so this copy could drift from
@@ -38,10 +38,21 @@ module Hecks
38
38
  # `chapter`/`aggregate` are the real `Bluebook`/`Aggregate`
39
39
  # the command belongs to — needed to resolve a value object type
40
40
  # name back to its own declared shape. `created` maps an
41
- # aggregate NAME to a real id already minted for it earlier in
41
+ # aggregate name to a real id already minted for it earlier in
42
42
  # the same run; a reference whose target isn't in there yet gets
43
43
  # a placeholder instead of failing outright, since the caller may
44
44
  # not care about that particular argument's real value.
45
+ #
46
+ # @param chapter [Bluebook::Chapter] the chapter the command's aggregate
47
+ # belongs to, needed to resolve a value object type name back to its
48
+ # own declared shape
49
+ # @param aggregate [Bluebook::Aggregate] the aggregate `command` belongs to
50
+ # @param command [Class] the command class (a `Bluebook::Command`
51
+ # subclass) to synthesize arguments for
52
+ # @param created [Hash{String => Object}] aggregate name mapped to a real
53
+ # id already minted for it earlier in this same run
54
+ # @return [Hash{Symbol => Object}] one synthesized value per declared
55
+ # attribute, keyed by attribute name
45
56
  def args_for(chapter, aggregate, command, created = {})
46
57
  command.attributes.to_h do |attribute|
47
58
  if attribute.reference?
@@ -52,13 +63,23 @@ module Hecks
52
63
  end
53
64
  end
54
65
 
55
- # A value object's OWN synthesized value — its first admitted
66
+ # A value object's own synthesized value — its first admitted
56
67
  # member if it's a closed set (the one value guaranteed not to be
57
68
  # refused), otherwise one synthesized scalar per declared field.
58
69
  # Falls back to searching every aggregate in the chapter, the
59
70
  # same tolerance `bin/interview shape`'s own lookup already
60
71
  # needed: a value object referenced by name doesn't have to be
61
- # declared on the SAME aggregate using it.
72
+ # declared on the same aggregate using it.
73
+ #
74
+ # @param chapter [Bluebook::Chapter] the chapter to search when
75
+ # `type_name` is not declared on `aggregate` itself
76
+ # @param aggregate [Bluebook::Aggregate] the aggregate `type_name` is
77
+ # looked up on first
78
+ # @param type_name [String] the value object's declared type name
79
+ # @return [String, Hash{Symbol => Object}] the string `"smoke-test"` when
80
+ # no such value object is declared; otherwise a Hash of one value per
81
+ # field — the closed set's own first admitted member's fields, or one
82
+ # freshly synthesized scalar per declared field
62
83
  def value_for(chapter, aggregate, type_name)
63
84
  value_object = aggregate.value_object(type_name) ||
64
85
  chapter.aggregates.filter_map { |a| a.value_object(type_name) }.first
@@ -72,17 +93,30 @@ module Hecks
72
93
 
73
94
  # A single field's own synthesized value — a plain scalar if its
74
95
  # type is one of the true primitives, otherwise that type names
75
- # ANOTHER value object, resolved the identical way `value_for`
96
+ # another value object, resolved the identical way `value_for`
76
97
  # resolves any other one (its own closed set or its own nested
77
98
  # fields, however deep that nesting actually goes).
99
+ #
100
+ # @param chapter [Bluebook::Chapter] see `value_for`
101
+ # @param aggregate [Bluebook::Aggregate] see `value_for`
102
+ # @param type_name [String] the field's declared type name
103
+ # @return [Integer, Float, true, false, String, Hash{Symbol => Object}] a
104
+ # bare scalar when `type_name` is a true primitive (see `scalar_for`),
105
+ # otherwise `value_for`'s own result for the nested value object it names
78
106
  def field_value_for(chapter, aggregate, type_name)
79
107
  PRIMITIVES.include?(type_name.to_s) ? scalar_for(type_name) : value_for(chapter, aggregate, type_name)
80
108
  end
81
109
 
82
110
  # A bare primitive's own synthesized value. Never used for a
83
- # closed set's OWN field (that's `value_for`'s job, reading the
111
+ # closed set's own field (that's `value_for`'s job, reading the
84
112
  # set's real first member) — only for a plain scalar field with
85
113
  # no declared vocabulary to respect.
114
+ #
115
+ # @param primitive [String, Symbol] the primitive type name, such as
116
+ # `"Integer"` or `"String"`
117
+ # @return [Integer, Float, false, String] `0` for `"Integer"`, `0.0` for
118
+ # `"Float"`, `false` for `"TrueClass"`/`"FalseClass"`, or the string
119
+ # `"smoke-test"` for anything else
86
120
  def scalar_for(primitive)
87
121
  case primitive.to_s
88
122
  when "Integer" then 0
@@ -5,7 +5,7 @@ module Hecks
5
5
  # Paths are dotted ("price.cents") for a VO member, bare otherwise.
6
6
  TranslationMove = Struct.new(:from, :to)
7
7
 
8
- # One field whose VALUE, not just its name or position, changed —
8
+ # One field whose value, not just its name or position, changed —
9
9
  # an old value has nothing in common with a new one, so the only
10
10
  # honest way to bridge it is a declared, exhaustive lookup table.
11
11
  # Paths follow `TranslationMove`'s convention (dotted reaches a VO
@@ -16,10 +16,10 @@ module Hecks
16
16
  # rubocop:disable-next Lint/StructNewOverride
17
17
  TranslationConvert = Struct.new(:from, :to, :values)
18
18
 
19
- # A value object's or entity's own TYPE NAME changed with its member
19
+ # A value object's or entity's own type name changed with its member
20
20
  # structure unchanged — the one drift `rename`/`move` cannot express,
21
21
  # because the attribute kept its name and only the type it points at
22
- # was renamed. Mirrors `was:` one level deeper.
22
+ # changed name. Mirrors `was:` one level deeper.
23
23
  TranslationRetype = Struct.new(:from, :to)
24
24
 
25
25
  # A computed transform — rescale, reformat, split, merge — whose only
@@ -29,7 +29,7 @@ module Hecks
29
29
  # every other adapter.
30
30
  TranslationCompute = Struct.new(:from, :to, :sql)
31
31
 
32
- # The aggregate's own IDENTITY changed what it's computed from — not
32
+ # The aggregate's own identity changed what it's computed from — not
33
33
  # a field crossing a boundary (that's `move`), a value objects's type
34
34
  # name (`retype`), or a value transform (`compute`): the record's own
35
35
  # key. No `from:`/`to:` path, unlike every other rule here, because
@@ -41,7 +41,7 @@ module Hecks
41
41
  TranslationRekey = Struct.new(:sql)
42
42
 
43
43
  # A newly added, required attribute with no source in old data at
44
- # all — not a rename, move, or convert, all of which need a FROM
44
+ # all — not a rename, move, or convert, all of which need a from
45
45
  # path in the old shape. `default` is the value an existing record
46
46
  # reads until the next command against it writes a real one; unlike
47
47
  # `compute`, this is adapter-agnostic — applied in-process by
@@ -57,6 +57,23 @@ module Hecks
57
57
  class TranslationAggregate
58
58
  attr_reader :name, :was, :renames, :moves, :converts, :drops, :retypes, :computes, :rekeys, :backfills
59
59
 
60
+ # @param name [String, Symbol] the aggregate's name in the destination era
61
+ # @param was [String, Symbol, nil] the aggregate's name in the origin era, or `nil`
62
+ # if it was not renamed
63
+ # @param renames [Hash{Symbol => Symbol}] each renamed field, old name to new name
64
+ # @param moves [Array<Bluebook::TranslationMove>] fields crossing a value-object
65
+ # boundary
66
+ # @param converts [Array<Bluebook::TranslationConvert>] fields whose value is
67
+ # remapped through a declared lookup table
68
+ # @param drops [Array<Symbol>] fields deliberately not carried forward
69
+ # @param retypes [Array<Bluebook::TranslationRetype>] value object or entity type
70
+ # renames with their member structure unchanged
71
+ # @param computes [Array<Bluebook::TranslationCompute>] fields computed by a SQL
72
+ # expression evaluated only inside the compiled Postgres head
73
+ # @param rekeys [Array<Bluebook::TranslationRekey>] SQL expressions that recompute
74
+ # the aggregate's own identity
75
+ # @param backfills [Array<Bluebook::TranslationBackfill>] newly added required
76
+ # fields with no source in old data
60
77
  def initialize(name:, was: nil, renames: {}, moves: [], converts: [], drops: [], retypes: [],
61
78
  computes: [], rekeys: [], backfills: [])
62
79
  @name = name.to_s
@@ -81,6 +98,13 @@ module Hecks
81
98
  class Translation
82
99
  attr_reader :domain, :from, :to, :aggregates, :retired
83
100
 
101
+ # @param domain [String, Symbol] the domain this translation carries forward
102
+ # @param from [String, Symbol] the origin era
103
+ # @param to [String, Symbol] the destination era
104
+ # @param aggregates [Array<Bluebook::TranslationAggregate>] each aggregate's own
105
+ # translation rules
106
+ # @param retired [Array<String>] the names of aggregates gone outright in the
107
+ # destination era, rather than renamed
84
108
  def initialize(domain:, from:, to:, aggregates: [], retired: [])
85
109
  @domain = domain.to_s
86
110
  @from = from
@@ -89,6 +113,11 @@ module Hecks
89
113
  @retired = retired
90
114
  end
91
115
 
116
+ # Finds one aggregate's own translation rules by its destination-era name.
117
+ #
118
+ # @param name [String, Symbol] the aggregate's name in the destination era
119
+ # @return [Bluebook::TranslationAggregate, nil] the aggregate's translation, or
120
+ # `nil` if `name` carries no translation rules
92
121
  def for_aggregate(name) = @aggregates.find { |aggregate| aggregate.name == name.to_s }
93
122
  end
94
123
  end
@@ -5,7 +5,7 @@ module Hecks
5
5
  module Bluebook
6
6
  Invariant = Struct.new(:description, :canonical, :predicate, :ast, keyword_init: true)
7
7
 
8
- # A value object — a DECLARATION HOLDER, never instantiated.
8
+ # A value object — a declaration holder, never instantiated.
9
9
  #
10
10
  # `ValueObjectBuilder` returns an anonymous subclass whose singleton
11
11
  # carries the declaration : attributes, invariants, members. Nothing ever
@@ -16,11 +16,11 @@ module Hecks
16
16
  #
17
17
  # `to_h` does not move. It is a byte-for-byte contract pinned by the
18
18
  # golden fixtures, so it keeps spelling the short declared name, which
19
- # is what `hecks_name` carries. DECLARATIONS IN THE GRAPH, STRINGS IN THE
20
- # EXPORT.
19
+ # is what `hecks_name` carries. Declarations in the graph, strings in the
20
+ # export.
21
21
  class ValueObject
22
22
  extend Construct
23
- # EXTENDED, not included — this construct is a class, so its
23
+ # Extended, not included — this construct is a class, so its
24
24
  # emission is a class method. See Hecks::IR's own note on
25
25
  # the two shapes.
26
26
  extend Hecks::IR
@@ -29,7 +29,7 @@ module Hecks
29
29
  emits_ir(
30
30
  name: :hecks_name,
31
31
  attributes: many(:attributes),
32
- # `ast:` — a JSON-serializable rendering of the SAME predicate
32
+ # `ast:` — a JSON-serializable rendering of the same predicate
33
33
  # `canonical` already spells as text, alongside it rather than
34
34
  # replacing it (`canonical` stays the human-facing/doctest-facing
35
35
  # form; parsing it back would just re-derive what `ast` already
@@ -39,14 +39,14 @@ module Hecks
39
39
  # has no kernel crate to parse `canonical` with.
40
40
  invariants: -> { invariants.map { |rule| Expression::AstJson.rule_row(rule) } },
41
41
  closed_set: :closed_set?,
42
- # THE FIELD NAME IS STRINGIFIED, NEVER THE VALUE. A `member` row can
42
+ # The field name is stringified, never the value. A `member` row can
43
43
  # hold any of the scalar types an attribute declares — `Integer 84`
44
44
  # (`StatementFrequency#retention_months`, statements.bluebook), not
45
- # only `String` — and `value.to_s` used to erase that on the way
46
- # out, so `84` and `"84"` (a member some other row might
47
- # legitimately spell as text) became indistinguishable once they
48
- # reached `to_h`. The declared name still moves (`field.to_s`) —
49
- # that half was never a Ruby object with a type to lose.
45
+ # only `String` — and stringifying it on the way out would erase
46
+ # that: `84` and `"84"` (a member some other row might legitimately
47
+ # spell as text) would become indistinguishable once they reached
48
+ # `to_h`. The declared name still moves (`field.to_s`) — that half
49
+ # was never a Ruby object with a type to lose.
50
50
  members: -> { members.map { |member| member.map { |field, value| [field.to_s, value] } } }
51
51
  )
52
52
 
@@ -55,6 +55,17 @@ module Hecks
55
55
 
56
56
  # One declared shape — a subclass rather than an instance, so the thing
57
57
  # the bluebook declares and the thing Ruby holds are one object.
58
+ #
59
+ # @param name [String, Symbol] the value object's declared type name
60
+ # @param attributes [Array<Bluebook::Attribute>] the value object's declared
61
+ # fields
62
+ # @param invariants [Array<Bluebook::Invariant>] the rules checked against every
63
+ # instance of this value object
64
+ # @param members [Array<Hash{Symbol => Object}>] the declared `one_of` members, one
65
+ # row of field values per member
66
+ # @param closed_set [Boolean] whether a `one_of` was declared, even with no
67
+ # members; defaults to whether `members` is non-empty
68
+ # @return [Class] the minted shape class (a `Bluebook::ValueObject` subclass)
58
69
  def declare(name:, attributes: [], invariants: [], members: [], closed_set: !members.empty?)
59
70
  shape = Class.new(self)
60
71
  shape.hecks_name = name.to_s
@@ -63,6 +74,13 @@ module Hecks
63
74
  shape
64
75
  end
65
76
 
77
+ # Assigns what the language declares onto this shape class.
78
+ #
79
+ # @param attributes [Array<Bluebook::Attribute>] see `declare`
80
+ # @param invariants [Array<Bluebook::Invariant>] see `declare`
81
+ # @param members [Array<Hash{Symbol => Object}>] see `declare`
82
+ # @param closed_set [Boolean] see `declare`
83
+ # @return [void]
66
84
  def absorb(attributes:, invariants:, members:, closed_set:)
67
85
  @attributes = attributes
68
86
  @invariants = invariants
@@ -7,13 +7,12 @@
7
7
  # DSL (dsl/), and the meta-validator that judges a chapter against the
8
8
  # language's own grammar (meta_validator.rb + meta_validator/).
9
9
  #
10
- # The require order below preserves the boot order the flat list in
11
- # lib/hecks.rb used to spell: expression and IR first (pure
12
- # declarations), assembly's collaborators before its face, the DSL before
13
- # the meta-validator that its builders call at build time.
10
+ # The require order below matters: expression and IR load first (pure
11
+ # declarations), assembly's collaborators load before its face, and the DSL
12
+ # loads before the meta-validator that its builders call at build time.
14
13
 
15
14
  module Hecks
16
- # Declared as a CLASS, not a module — `Hecks::Bluebook` IS a
15
+ # Declared as a class, not a module — `Hecks::Bluebook` is a
17
16
  # chapter (bluebook/chapter.rb carries its body). Everything a chapter
18
17
  # declares nests under it, as do the ways to build one (`DSL`) and to
19
18
  # judge one (`MetaValidator`). Reopening this anywhere must say `class`.
@@ -29,7 +28,7 @@ require_relative "literal"
29
28
  require_relative "query_specification"
30
29
 
31
30
  require_relative "bluebook/expression"
32
- # THE MODEL ITSELF — the chapter class's own body first, then everything
31
+ # **The model itself** — the chapter class's own body first, then everything
33
32
  # a chapter declares. Order matters only for reading: each is a bag of
34
33
  # declarations with no load-time cross-references.
35
34
  require_relative "bluebook/chapter"