hecks 1.3.0 → 1.4.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 (355) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hecks/adapters/driven/claude_code.rb +7 -7
  3. data/lib/hecks/adapters/driven/d1.rb +187 -23
  4. data/lib/hecks/adapters/driven/folder.rb +10 -10
  5. data/lib/hecks/adapters/driven/google_authentication.rb +8 -8
  6. data/lib/hecks/adapters/driven/governance_authorization.rb +31 -13
  7. data/lib/hecks/adapters/driven/heki/journal.rb +5 -4
  8. data/lib/hecks/adapters/driven/heki/saga_store.rb +5 -5
  9. data/lib/hecks/adapters/driven/heki.rb +13 -7
  10. data/lib/hecks/adapters/driven/identity_registry.rb +2 -2
  11. data/lib/hecks/adapters/driven/in_memory_ordering.rb +3 -3
  12. data/lib/hecks/adapters/driven/lambda/client.rb +34 -9
  13. data/lib/hecks/adapters/driven/lambda.rb +39 -33
  14. data/lib/hecks/adapters/driven/local_storage.rb +17 -10
  15. data/lib/hecks/adapters/driven/memory.rb +205 -9
  16. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +1 -1
  17. data/lib/hecks/adapters/driven/postgres/codec.rb +27 -11
  18. data/lib/hecks/adapters/driven/postgres/outbox.rb +40 -2
  19. data/lib/hecks/adapters/driven/postgres/reconnect.rb +23 -7
  20. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +14 -14
  21. data/lib/hecks/adapters/driven/postgres.rb +175 -28
  22. data/lib/hecks/adapters/driven/prism.rb +4 -4
  23. data/lib/hecks/adapters/driven/sql_query_builder.rb +34 -22
  24. data/lib/hecks/adapters/driven/sqlite/codec.rb +38 -10
  25. data/lib/hecks/adapters/driven/sqlite/projection.rb +60 -32
  26. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +12 -12
  27. data/lib/hecks/adapters/driven/sqlite.rb +181 -21
  28. data/lib/hecks/adapters/driven.rb +4 -4
  29. data/lib/hecks/adapters/driving/github_webhook.rb +18 -18
  30. data/lib/hecks/behaviors/dsl.rb +2 -2
  31. data/lib/hecks/behaviors/expectations.rb +23 -23
  32. data/lib/hecks/behaviors/ir.rb +1 -1
  33. data/lib/hecks/behaviors/rspec.rb +1 -1
  34. data/lib/hecks/behaviors/runner.rb +2 -2
  35. data/lib/hecks/behaviors.rb +1 -1
  36. data/lib/hecks/bluebook/aggregate.rb +13 -13
  37. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +10 -10
  38. data/lib/hecks/bluebook/assembly/build.rb +1 -1
  39. data/lib/hecks/bluebook/assembly/contract.rb +39 -16
  40. data/lib/hecks/bluebook/assembly/contracts.rb +40 -38
  41. data/lib/hecks/bluebook/assembly/marks.rb +18 -18
  42. data/lib/hecks/bluebook/assembly/specializer.rb +19 -21
  43. data/lib/hecks/bluebook/assembly.rb +9 -9
  44. data/lib/hecks/bluebook/attribute.rb +9 -9
  45. data/lib/hecks/bluebook/behaviour/aggregate.rb +11 -11
  46. data/lib/hecks/bluebook/behaviour/attribute.rb +5 -5
  47. data/lib/hecks/bluebook/behaviour/chapter.rb +23 -5
  48. data/lib/hecks/bluebook/behaviour/command.rb +23 -23
  49. data/lib/hecks/bluebook/behaviour/domain_port.rb +7 -7
  50. data/lib/hecks/bluebook/behaviour/entity.rb +8 -8
  51. data/lib/hecks/bluebook/behaviour/hexagon.rb +4 -4
  52. data/lib/hecks/bluebook/behaviour/lifecycle.rb +5 -5
  53. data/lib/hecks/bluebook/behaviour/policy.rb +12 -12
  54. data/lib/hecks/bluebook/behaviour/process_manager.rb +7 -7
  55. data/lib/hecks/bluebook/behaviour/query.rb +1 -1
  56. data/lib/hecks/bluebook/behaviour/read_model.rb +8 -8
  57. data/lib/hecks/bluebook/behaviour/traits.rb +12 -12
  58. data/lib/hecks/bluebook/behaviour/value_object.rb +6 -6
  59. data/lib/hecks/bluebook/capabilities.rb +27 -0
  60. data/lib/hecks/bluebook/chapter.rb +28 -9
  61. data/lib/hecks/bluebook/command.rb +12 -12
  62. data/lib/hecks/bluebook/domain_port.rb +9 -9
  63. data/lib/hecks/bluebook/dsl/adapter_builder.rb +24 -0
  64. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +49 -49
  65. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +96 -96
  66. data/lib/hecks/bluebook/dsl/attribute_collector.rb +41 -41
  67. data/lib/hecks/bluebook/dsl/binding_proxy.rb +22 -2
  68. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +111 -74
  69. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +48 -30
  70. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
  71. data/lib/hecks/bluebook/dsl/command_builder.rb +103 -103
  72. data/lib/hecks/bluebook/dsl/const_shim.rb +46 -15
  73. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +90 -25
  74. data/lib/hecks/bluebook/dsl/entity_builder.rb +56 -56
  75. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
  76. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +89 -30
  77. data/lib/hecks/bluebook/dsl/identity_declaration.rb +17 -17
  78. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
  79. data/lib/hecks/bluebook/dsl/policy_builder.rb +30 -21
  80. data/lib/hecks/bluebook/dsl/port_builder.rb +38 -7
  81. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +56 -22
  82. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +35 -35
  83. data/lib/hecks/bluebook/dsl/query_builder.rb +5 -5
  84. data/lib/hecks/bluebook/dsl/read_model_builder.rb +34 -34
  85. data/lib/hecks/bluebook/dsl/rule_reference.rb +41 -39
  86. data/lib/hecks/bluebook/dsl/translation_builder.rb +9 -9
  87. data/lib/hecks/bluebook/dsl/value_object_builder.rb +16 -16
  88. data/lib/hecks/bluebook/dsl/word_gate.rb +59 -53
  89. data/lib/hecks/bluebook/dsl/world_builder.rb +51 -8
  90. data/lib/hecks/bluebook/entity.rb +11 -11
  91. data/lib/hecks/bluebook/expression/ast_json.rb +20 -20
  92. data/lib/hecks/bluebook/expression/ast_reader.rb +3 -3
  93. data/lib/hecks/bluebook/expression/canonical_form.rb +9 -9
  94. data/lib/hecks/bluebook/expression/evaluator.rb +18 -18
  95. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +18 -18
  96. data/lib/hecks/bluebook/expression/resolver.rb +60 -62
  97. data/lib/hecks/bluebook/hexagon.rb +1 -1
  98. data/lib/hecks/bluebook/lifecycle.rb +1 -1
  99. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -1
  100. data/lib/hecks/bluebook/meta_validator/judge.rb +101 -87
  101. data/lib/hecks/bluebook/meta_validator/plan.rb +39 -39
  102. data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -2
  103. data/lib/hecks/bluebook/meta_validator/readings.rb +43 -43
  104. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +44 -37
  105. data/lib/hecks/bluebook/meta_validator/shapes.rb +25 -21
  106. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +145 -31
  107. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +6 -6
  108. data/lib/hecks/bluebook/meta_validator/world_judge.rb +5 -5
  109. data/lib/hecks/bluebook/meta_validator.rb +70 -70
  110. data/lib/hecks/bluebook/model_check.rb +170 -84
  111. data/lib/hecks/bluebook/pattern_subset.rb +9 -9
  112. data/lib/hecks/bluebook/policy.rb +15 -13
  113. data/lib/hecks/bluebook/process_manager.rb +14 -14
  114. data/lib/hecks/bluebook/project_register.rb +6 -6
  115. data/lib/hecks/bluebook/query.rb +4 -4
  116. data/lib/hecks/bluebook/read_model.rb +14 -14
  117. data/lib/hecks/bluebook/reference.rb +8 -8
  118. data/lib/hecks/bluebook/smoke_test.rb +19 -19
  119. data/lib/hecks/bluebook/synthesizer.rb +12 -12
  120. data/lib/hecks/bluebook/translation.rb +4 -4
  121. data/lib/hecks/bluebook/value_object.rb +6 -6
  122. data/lib/hecks/bluebook.rb +2 -2
  123. data/lib/hecks/codemod/legacy_dispatch_args.rb +299 -0
  124. data/lib/hecks/codemod/legacy_dispatch_recorder.rb +186 -0
  125. data/lib/hecks/codemod.rb +36 -35
  126. data/lib/hecks/construct.rb +6 -6
  127. data/lib/hecks/corpus.rb +317 -0
  128. data/lib/hecks/deprecation.rb +95 -0
  129. data/lib/hecks/doc/reference.rb +19 -19
  130. data/lib/hecks/embryonaut_bluebook.rb +11 -11
  131. data/lib/hecks/facade/cli_door.rb +69 -10
  132. data/lib/hecks/facade/cli_runner.rb +105 -24
  133. data/lib/hecks/facade/command_request.rb +23 -0
  134. data/lib/hecks/facade/handle.rb +79 -32
  135. data/lib/hecks/facade/json_door.rb +106 -25
  136. data/lib/hecks/facade/surface/aggregate_door.rb +42 -27
  137. data/lib/hecks/facade/surface/chapter.rb +26 -17
  138. data/lib/hecks/facade/surface.rb +16 -3
  139. data/lib/hecks/facade.rb +15 -4
  140. data/lib/hecks/forms/app.rb +46 -30
  141. data/lib/hecks/forms/command_form_renderer.rb +70 -9
  142. data/lib/hecks/forms/field_renderer.rb +142 -6
  143. data/lib/hecks/forms/field_shape.rb +180 -19
  144. data/lib/hecks/forms/html.rb +51 -7
  145. data/lib/hecks/forms/index_renderer.rb +14 -2
  146. data/lib/hecks/forms/params.rb +120 -23
  147. data/lib/hecks/forms/port_argument.rb +2 -2
  148. data/lib/hecks/forms/query_form_renderer.rb +2 -2
  149. data/lib/hecks/forms/record_renderer.rb +2 -2
  150. data/lib/hecks/forms/record_table.rb +1 -1
  151. data/lib/hecks/forms/value_object_shape.rb +3 -3
  152. data/lib/hecks/forms.rb +24 -4
  153. data/lib/hecks/fqn.rb +1 -1
  154. data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
  155. data/lib/hecks/framework.rb +48 -17
  156. data/lib/hecks/freezer.rb +11 -11
  157. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +90 -90
  158. data/lib/hecks/fuzzing/combination_miner.rb +119 -0
  159. data/lib/hecks/fuzzing/concurrent_dispatch.rb +140 -45
  160. data/lib/hecks/fuzzing/coverage_campaign.rb +118 -0
  161. data/lib/hecks/fuzzing/differential.rb +158 -0
  162. data/lib/hecks/fuzzing/domain_generator.rb +694 -0
  163. data/lib/hecks/fuzzing/era_boundary.rb +37 -18
  164. data/lib/hecks/fuzzing/form_census.rb +35 -20
  165. data/lib/hecks/fuzzing/generated_domain_check.rb +95 -0
  166. data/lib/hecks/fuzzing/invalid_value_generator.rb +6 -6
  167. data/lib/hecks/fuzzing/isolated_boot.rb +62 -62
  168. data/lib/hecks/fuzzing/nondeterministic.rb +67 -0
  169. data/lib/hecks/fuzzing/persistence_parity.rb +19 -21
  170. data/lib/hecks/fuzzing/properties/corrections.rb +9 -9
  171. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +154 -49
  172. data/lib/hecks/fuzzing/properties/guards.rb +42 -42
  173. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +32 -32
  174. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +22 -35
  175. data/lib/hecks/fuzzing/properties/outbox.rb +24 -24
  176. data/lib/hecks/fuzzing/properties/querying.rb +17 -17
  177. data/lib/hecks/fuzzing/properties.rb +64 -40
  178. data/lib/hecks/fuzzing/qa_settings.rb +152 -0
  179. data/lib/hecks/fuzzing/replay.rb +114 -114
  180. data/lib/hecks/fuzzing/rotation_priority.rb +10 -10
  181. data/lib/hecks/fuzzing/rust_gap_manifest.rb +113 -0
  182. data/lib/hecks/fuzzing/self_consistency.rb +141 -112
  183. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +35 -35
  184. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +18 -11
  185. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +13 -12
  186. data/lib/hecks/fuzzing/sequence_generator/picker.rb +21 -12
  187. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +58 -31
  188. data/lib/hecks/fuzzing/sequence_generator.rb +111 -23
  189. data/lib/hecks/fuzzing/shrinker.rb +197 -0
  190. data/lib/hecks/fuzzing/structural_skips.rb +23 -130
  191. data/lib/hecks/fuzzing/sweep_depth.rb +4 -4
  192. data/lib/hecks/fuzzing/target_capabilities.rb +49 -18
  193. data/lib/hecks/fuzzing/value_generator.rb +14 -14
  194. data/lib/hecks/fuzzing.rb +5 -0
  195. data/lib/hecks/grammar/evolve.rb +10 -10
  196. data/lib/hecks/grammar.rb +7 -7
  197. data/lib/hecks/ir.rb +13 -13
  198. data/lib/hecks/language/bluebook/bluebook.bluebook +41 -0
  199. data/lib/hecks/language/bluebook/policy.bluebook +11 -1
  200. data/lib/hecks/language/bluebook/vocabulary.bluebook +348 -13
  201. data/lib/hecks/language/oidc.json +5 -0
  202. data/lib/hecks/literal.rb +9 -9
  203. data/lib/hecks/naming.rb +25 -25
  204. data/lib/hecks/ports/access_control.rb +58 -2
  205. data/lib/hecks/ports/agent/answers.rb +83 -6
  206. data/lib/hecks/ports/agent.rb +119 -35
  207. data/lib/hecks/ports/authentication.rb +44 -4
  208. data/lib/hecks/ports/authorization.rb +53 -11
  209. data/lib/hecks/ports/clock.rb +42 -23
  210. data/lib/hecks/ports/extraction.rb +16 -0
  211. data/lib/hecks/ports/identity_assignment.rb +24 -2
  212. data/lib/hecks/ports/identity_generation.rb +17 -3
  213. data/lib/hecks/ports/identity_resolution.rb +18 -1
  214. data/lib/hecks/ports/loading.rb +4 -0
  215. data/lib/hecks/ports/persistence/append_only.rb +172 -8
  216. data/lib/hecks/ports/persistence/binding_policy.rb +34 -0
  217. data/lib/hecks/ports/persistence/codec_boundary.rb +178 -0
  218. data/lib/hecks/ports/persistence/execution.rb +4 -0
  219. data/lib/hecks/ports/persistence/null_saga_store.rb +12 -1
  220. data/lib/hecks/ports/persistence/plugin.rb +42 -4
  221. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +218 -25
  222. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +77 -9
  223. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +81 -24
  224. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +29 -18
  225. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +144 -60
  226. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +103 -8
  227. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +98 -23
  228. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +282 -109
  229. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +63 -25
  230. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +118 -66
  231. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +51 -28
  232. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +28 -5
  233. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +25 -12
  234. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +129 -34
  235. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +51 -6
  236. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +33 -9
  237. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +16 -0
  238. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +57 -4
  239. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +25 -2
  240. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +314 -90
  241. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +68 -10
  242. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +9 -3
  243. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +9 -2
  244. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +42 -8
  245. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +16 -1
  246. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +36 -5
  247. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +23 -3
  248. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +18 -19
  249. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +5 -5
  250. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +1 -1
  251. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -2
  252. data/lib/hecks/ports/persistence/plugins/era.rb +12 -2
  253. data/lib/hecks/ports/persistence/remote_runtime.rb +9 -2
  254. data/lib/hecks/ports/persistence/repository_factory.rb +23 -3
  255. data/lib/hecks/ports/persistence/state_codec.rb +319 -0
  256. data/lib/hecks/ports/persistence.rb +36 -1
  257. data/lib/hecks/ports/projection.rb +61 -7
  258. data/lib/hecks/ports/query/in_memory.rb +3 -3
  259. data/lib/hecks/ports/query/ordering.rb +6 -6
  260. data/lib/hecks/ports/query.rb +35 -0
  261. data/lib/hecks/projections/bootstrap_table.rb +112 -0
  262. data/lib/hecks/projections/diagrams.rb +75 -75
  263. data/lib/hecks/projections/glossary/html.rb +3 -3
  264. data/lib/hecks/projections/glossary/markdown.rb +3 -3
  265. data/lib/hecks/projections/glossary/mermaid.rb +1 -1
  266. data/lib/hecks/projections/glossary/sections.rb +1 -1
  267. data/lib/hecks/projections/glossary/sentences.rb +5 -5
  268. data/lib/hecks/projections/glossary.rb +15 -15
  269. data/lib/hecks/projections/ir.rb +1 -1
  270. data/lib/hecks/projections/model/deviations.rb +18 -17
  271. data/lib/hecks/projections/model.rb +25 -21
  272. data/lib/hecks/projections/oidc.rb +7 -7
  273. data/lib/hecks/projections/parser_table.rb +5 -5
  274. data/lib/hecks/projections/reference.rb +3 -3
  275. data/lib/hecks/projections/rust_vocabulary.rb +443 -0
  276. data/lib/hecks/projections/shape.rb +2 -2
  277. data/lib/hecks/projections/statements.rb +11 -11
  278. data/lib/hecks/projections/vocabulary.rb +9 -9
  279. data/lib/hecks/projections.rb +5 -3
  280. data/lib/hecks/projector/cli_projector.rb +29 -29
  281. data/lib/hecks/projector/docs_projector.rb +13 -13
  282. data/lib/hecks/projector/exporter.rb +42 -21
  283. data/lib/hecks/projector/ir_projector.rb +1 -1
  284. data/lib/hecks/projector/narrate_projector.rb +11 -11
  285. data/lib/hecks/projector/target.rb +13 -13
  286. data/lib/hecks/projector.rb +15 -15
  287. data/lib/hecks/query_ir.rb +47 -47
  288. data/lib/hecks/query_specification/common/comparators.rb +19 -3
  289. data/lib/hecks/query_specification/common/comparison.rb +109 -27
  290. data/lib/hecks/query_specification/common/dsl.rb +65 -9
  291. data/lib/hecks/query_specification/common/null_policy.rb +57 -13
  292. data/lib/hecks/query_specification/common/null_semantics.rb +4 -0
  293. data/lib/hecks/query_specification/common/options.rb +25 -0
  294. data/lib/hecks/query_specification/field_path.rb +69 -15
  295. data/lib/hecks/query_specification/hop_path.rb +57 -20
  296. data/lib/hecks/query_specification/read_model/specification.rb +4 -0
  297. data/lib/hecks/rendering.rb +3 -3
  298. data/lib/hecks/router/namespace_installer.rb +3 -3
  299. data/lib/hecks/router.rb +1 -1
  300. data/lib/hecks/runtime/aggregate_lock.rb +11 -11
  301. data/lib/hecks/runtime/boot_gates.rb +3 -3
  302. data/lib/hecks/runtime/caller.rb +8 -8
  303. data/lib/hecks/runtime/capability_graph.rb +2 -2
  304. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +22 -25
  305. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +42 -63
  306. data/lib/hecks/runtime/command_interpreter.rb +159 -102
  307. data/lib/hecks/runtime/command_rules/admissibility.rb +69 -69
  308. data/lib/hecks/runtime/command_rules/arithmetic.rb +137 -91
  309. data/lib/hecks/runtime/command_rules/authorization.rb +38 -17
  310. data/lib/hecks/runtime/command_rules/emission.rb +18 -1
  311. data/lib/hecks/runtime/command_rules/references.rb +35 -35
  312. data/lib/hecks/runtime/command_rules.rb +3 -0
  313. data/lib/hecks/runtime/dependency_planning.rb +11 -11
  314. data/lib/hecks/runtime/dispatcher.rb +283 -90
  315. data/lib/hecks/runtime/entity_element.rb +151 -67
  316. data/lib/hecks/runtime/entity_interpreter.rb +108 -82
  317. data/lib/hecks/runtime/errors.rb +19 -19
  318. data/lib/hecks/runtime/event.rb +6 -6
  319. data/lib/hecks/runtime/identity.rb +22 -22
  320. data/lib/hecks/runtime/instance.rb +39 -14
  321. data/lib/hecks/runtime/interpreting.rb +12 -12
  322. data/lib/hecks/runtime/invocation.rb +276 -0
  323. data/lib/hecks/runtime/loader.rb +14 -14
  324. data/lib/hecks/runtime/outbox.rb +23 -23
  325. data/lib/hecks/runtime/policy_interpreter.rb +54 -54
  326. data/lib/hecks/runtime/port_operation_interpreter.rb +22 -19
  327. data/lib/hecks/runtime/query_interpreter.rb +55 -64
  328. data/lib/hecks/runtime/reaction_invocation.rb +20 -20
  329. data/lib/hecks/runtime/read_model_interpreter.rb +40 -40
  330. data/lib/hecks/runtime/rebuild_sweep.rb +4 -4
  331. data/lib/hecks/runtime/reference_hop.rb +6 -6
  332. data/lib/hecks/runtime/refusal_wording.rb +92 -115
  333. data/lib/hecks/runtime/registry/saga_persistence.rb +21 -21
  334. data/lib/hecks/runtime/registry/verification.rb +36 -26
  335. data/lib/hecks/runtime/registry.rb +45 -28
  336. data/lib/hecks/runtime/remote_dispatcher.rb +38 -23
  337. data/lib/hecks/runtime/routing.rb +10 -153
  338. data/lib/hecks/runtime/saga_interpreter/correlation.rb +17 -17
  339. data/lib/hecks/runtime/saga_interpreter.rb +64 -64
  340. data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
  341. data/lib/hecks/runtime/tenant_check.rb +9 -9
  342. data/lib/hecks/runtime/tenant_scope.rb +5 -5
  343. data/lib/hecks/runtime/value/admission.rb +75 -30
  344. data/lib/hecks/runtime/value/coercion.rb +318 -127
  345. data/lib/hecks/runtime/value/entity_list_coercion.rb +34 -34
  346. data/lib/hecks/runtime/value.rb +21 -21
  347. data/lib/hecks/runtime.rb +7 -7
  348. data/lib/hecks/storehouse.rb +64 -64
  349. data/lib/hecks/version.rb +3 -3
  350. data/lib/hecks/vocabulary.rb +205 -4
  351. data/lib/hecks.rb +13 -11
  352. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +11 -11
  353. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +14 -12
  354. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +11 -11
  355. metadata +22 -2
@@ -0,0 +1,443 @@
1
+ require_relative "../projector"
2
+ require_relative "../runtime/refusal_wording"
3
+ require_relative "vocabulary"
4
+
5
+ module Hecks
6
+ module Projections
7
+ # **The language's closed sets, projected into the Rust kernel** — one
8
+ # `rust/src/kernel/vocab/<table>.rs` per Vocabulary table, each a
9
+ # `pub enum` plus `match`-based accessors with no wildcard arm, so a
10
+ # row the chapter gains is a compile error at every exhaustive match
11
+ # over it rather than a silent gap.
12
+ #
13
+ # Projector.call(:rust_vocabulary, bluebook: <the Bluebook chapter>)
14
+ # # => { "vocab/mod.rs" => "...", "vocab/refusal_template.rs" => "...", ... }
15
+ #
16
+ # Paths are relative to rust/src/kernel/; bin/project_rust_vocabulary
17
+ # writes them. This generalizes the old bin/project_refusal_wording,
18
+ # which read Runtime::RefusalWording::TEMPLATES — a hand-typed copy of
19
+ # the RefusalTemplate rows. This reads the chapter itself.
20
+ #
21
+ # **Only TABLES the kernel needs**. Each entry in TABLES names its enum
22
+ # and its generated extras; a table not listed is not projected. The
23
+ # enum names that differ from the table name keep existing Rust call
24
+ # sites (`RefusalSite`) and the dispatch-step names D1 consumes
25
+ # (`AggregateStep`, `EntityStep`).
26
+ #
27
+ # TYPED REFUSAL ARGUMENTS. The templates table also reads
28
+ # RefusalSiteArgument: every site gets a `<Variant>Args` struct whose
29
+ # fields are exactly the site's declared arguments (a `&str` per
30
+ # scalar, a `&[&str]` per list) and a `render_args` that formats each
31
+ # one by its row. `render` itself stays private to the generated
32
+ # module, so no call site can pass an argument list by hand — leaving
33
+ # one out does not compile. The generated test pins every site's
34
+ # `render_args` output (empty, single and multiple unsorted lists)
35
+ # against Runtime::RefusalWording.render_with, computed here in Ruby.
36
+ module RustVocabulary
37
+ extend Projector::Target
38
+
39
+ projects_as :rust_vocabulary, declares: "Vocabulary", emits: :files
40
+
41
+ GENERATOR = "bin/project_rust_vocabulary".freeze
42
+ SOURCE = "lib/hecks/language/bluebook/vocabulary.bluebook".freeze
43
+
44
+ # table name => enum name, variant-naming fields, file stem, kind.
45
+ # `:order` tables also get `ORDER` and `position`; `:templates`
46
+ # gets `render`, the typed `<Variant>Args` (read off the `arguments`
47
+ # table) and the placeholder and argument-rendering tests.
48
+ TABLES = {
49
+ "RefusalTemplate" => { enum: "RefusalSite", variant_from: %w[refusal site], file: "refusal_template",
50
+ kind: :templates, arguments: "RefusalSiteArgument" },
51
+ "QueryComparator" => { enum: "QueryComparator", variant_from: %w[name], file: "query_comparator", kind: :set },
52
+ "FieldHint" => { enum: "FieldHint", variant_from: %w[name], file: "field_hint", kind: :set },
53
+ "AggregateDispatchOrder" => { enum: "AggregateStep", variant_from: %w[step], file: "aggregate_dispatch_order",
54
+ kind: :order },
55
+ "EntityDispatchOrder" => { enum: "EntityStep", variant_from: %w[step], file: "entity_dispatch_order",
56
+ kind: :order }
57
+ }.freeze
58
+
59
+ RUST_KEYWORDS = %w[as async await break const continue crate dyn else enum extern false fn for if impl in let loop
60
+ match mod move mut pub ref return self static struct super trait true type unsafe use where
61
+ while abstract become box do final macro override priv typeof unsized virtual yield try].freeze
62
+
63
+ module_function
64
+
65
+ def call(bluebook:, options: {}) = render(bluebook)
66
+
67
+ def render(bluebook)
68
+ tables = Vocabulary.tables(bluebook)
69
+ files = TABLES.to_h do |table, spec|
70
+ rows = tables.fetch(table) { raise ArgumentError, "Vocabulary declares no #{table} table" }
71
+ arguments = spec[:arguments] && tables.fetch(spec[:arguments]) do
72
+ raise ArgumentError, "Vocabulary declares no #{spec[:arguments]} table"
73
+ end
74
+ ["vocab/#{spec[:file]}.rs", table_file(table, spec, rows, arguments)]
75
+ end
76
+ { "vocab/mod.rs" => mod_file }.merge(files)
77
+ end
78
+
79
+ def header(table)
80
+ <<~RUST
81
+ // GENERATED by #{GENERATOR} (Hecks::Projections::RustVocabulary,
82
+ // lib/hecks/projections/rust_vocabulary.rb) from Vocabulary::#{table}
83
+ // (#{SOURCE}).
84
+ // Do not hand-edit — re-run #{GENERATOR} instead.
85
+ RUST
86
+ end
87
+
88
+ def mod_file
89
+ mods = TABLES.values.map { |spec| "pub mod #{spec[:file]};" }.join("\n")
90
+ exports = TABLES.values.map { |spec| "pub use #{spec[:file]}::#{spec[:enum]};" }.join("\n")
91
+ <<~RUST
92
+ // GENERATED by #{GENERATOR} (Hecks::Projections::RustVocabulary,
93
+ // lib/hecks/projections/rust_vocabulary.rb) from the Vocabulary
94
+ // aggregate (#{SOURCE}).
95
+ // Do not hand-edit — re-run #{GENERATOR} instead.
96
+ //
97
+ // One module per projected table. Every enum below is matched
98
+ // exhaustively by its own accessors — no wildcard arm — so a row the
99
+ // language gains fails to compile until it is handled.
100
+
101
+ #{mods}
102
+
103
+ #{exports}
104
+ RUST
105
+ end
106
+
107
+ def table_file(table, spec, rows, argument_rows = nil)
108
+ variants = rows.map { |row| variant_name(row, spec[:variant_from]) }
109
+ duplicates = variants.tally.select { |_, count| count > 1 }.keys
110
+ raise ArgumentError, "duplicate #{spec[:enum]} variant(s): #{duplicates.join(', ')}" if duplicates.any?
111
+
112
+ enum = spec[:enum]
113
+ fields = rows.first.keys
114
+ by_site = spec[:kind] == :templates ? site_arguments(rows, argument_rows) : nil
115
+ body = [
116
+ header(table),
117
+ "#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]",
118
+ "pub enum #{enum} {",
119
+ variants.map { |variant| " #{variant}," },
120
+ "}",
121
+ "",
122
+ "impl #{enum} {",
123
+ constants(enum, variants, spec[:kind]),
124
+ fields.map { |field| accessor(enum, field, variants, rows) },
125
+ from_name(enum, spec[:kind]),
126
+ extras(enum, variants, spec[:kind]),
127
+ "}",
128
+ "",
129
+ by_site ? argument_types(enum, variants, rows, by_site) : [],
130
+ tests(enum, spec[:kind], variants, rows, by_site)
131
+ ]
132
+ "#{body.flatten.join("\n").rstrip}\n"
133
+ end
134
+
135
+ def constants(enum, variants, kind)
136
+ list = variants.map { |variant| " #{enum}::#{variant}," }
137
+ if kind == :order
138
+ [" /// The declared step order, first to last.",
139
+ " pub const ORDER: [#{enum}; #{variants.size}] = [", list, " ];", ""]
140
+ else
141
+ [" /// Every row, in declared order.",
142
+ " pub const ALL: &'static [#{enum}] = &[", list, " ];", ""]
143
+ end
144
+ end
145
+
146
+ def accessor(enum, field, variants, rows)
147
+ arms = variants.zip(rows).map { |variant, row| " #{enum}::#{variant} => #{rust_string(row.fetch(field))}," }
148
+ [" /// The row's declared `#{field}`.",
149
+ " pub fn #{accessor_name(field)}(&self) -> &'static str {",
150
+ " match self {",
151
+ arms,
152
+ " }",
153
+ " }",
154
+ ""]
155
+ end
156
+
157
+ # Built on the enum's own list and name accessor, not a string
158
+ # match, so it needs no wildcard arm either.
159
+ def from_name(enum, kind)
160
+ list = kind == :order ? "ORDER" : "ALL"
161
+ field = kind == :templates ? "site" : name_field(kind)
162
+ [" /// The row whose `#{field}` is `name`, if any.",
163
+ " pub fn from_name(name: &str) -> Option<#{enum}> {",
164
+ " #{enum}::#{list}.iter().copied().find(|row| row.#{accessor_name(field)}() == name)",
165
+ " }",
166
+ ""]
167
+ end
168
+
169
+ def extras(enum, variants, kind)
170
+ case kind
171
+ when :order
172
+ arms = variants.each_with_index.map { |variant, index| " #{enum}::#{variant} => #{index}," }
173
+ [" /// This step's index in `ORDER`.",
174
+ " pub fn position(&self) -> usize {",
175
+ " match self {",
176
+ arms,
177
+ " }",
178
+ " }"]
179
+ when :templates
180
+ [" /// `RefusalWording.substitute`: replace every `{key}` marker, in the",
181
+ " /// order given. A template is read, never evaluated. Private: call",
182
+ " /// sites go through a site's typed `<Variant>Args::render_args`,",
183
+ " /// which formats and supplies every declared argument.",
184
+ " fn render(&self, values: &[(&str, &str)]) -> String {",
185
+ " let mut text = self.template().to_string();",
186
+ " for (key, value) in values {",
187
+ " text = text.replace(&format!(\"{{{key}}}\"), value);",
188
+ " }",
189
+ " text",
190
+ " }"]
191
+ else
192
+ []
193
+ end
194
+ end
195
+
196
+ # [refusal, site] => that site's argument rows, in declared order —
197
+ # refused unless they name exactly the template's own placeholders,
198
+ # in the order each first appears, with only known formatting rules.
199
+ def site_arguments(rows, argument_rows)
200
+ grouped = argument_rows.group_by { |row| [row.fetch("refusal"), row.fetch("site")] }
201
+ orphans = grouped.keys - rows.map { |row| [row.fetch("refusal"), row.fetch("site")] }
202
+ raise ArgumentError, "RefusalSiteArgument rows name no RefusalTemplate: #{orphans.inspect}" if orphans.any?
203
+
204
+ rows.to_h do |row|
205
+ key = [row.fetch("refusal"), row.fetch("site")]
206
+ specs = grouped.fetch(key, [])
207
+ wants = row.fetch("template").scan(/\{(\w+)\}/).flatten.uniq
208
+ names = specs.map { |spec| spec.fetch("argument") }
209
+ unless names == wants
210
+ raise ArgumentError, "RefusalSiteArgument for #{key.join('/')} declares #{names.inspect}; " \
211
+ "its template's placeholders are #{wants.inspect}"
212
+ end
213
+ specs.each { |spec| check_rule!(key, spec) }
214
+ [key, specs]
215
+ end
216
+ end
217
+
218
+ def check_rule!(key, spec)
219
+ where = "#{key.join('/')}.#{spec.fetch('argument')}"
220
+ unless Runtime::RefusalWording::SHAPES.include?(spec.fetch("shape"))
221
+ raise ArgumentError, "#{where}: unknown shape #{spec['shape'].inspect}"
222
+ end
223
+ unless Runtime::RefusalWording::QUOTINGS.include?(spec.fetch("quoting"))
224
+ raise ArgumentError, "#{where}: unknown quoting #{spec['quoting'].inspect}"
225
+ end
226
+ return if %w[true false].include?(spec.fetch("sorted"))
227
+
228
+ raise ArgumentError, "#{where}: sorted must be \"true\" or \"false\""
229
+ end
230
+
231
+ def argument_types(enum, variants, rows, by_site)
232
+ support = [
233
+ "/// Ruby's `#inspect` of a name, as a refusal quotes it: `{:?}` on a",
234
+ "/// `&str`, the quoting every kernel call site used before",
235
+ "/// RefusalSiteArgument existed.",
236
+ "fn quoted(text: &str) -> String {",
237
+ " format!(\"{text:?}\")",
238
+ "}",
239
+ "",
240
+ "/// A list argument, written the way its RefusalSiteArgument row says:",
241
+ "/// sorted first (before quoting), each item quoted, then joined; an",
242
+ "/// empty list reads `when_empty`.",
243
+ "fn list(items: &[&str], sorted: bool, inspect: bool, separator: &str, when_empty: &str) -> String {",
244
+ " let mut items = items.to_vec();",
245
+ " if sorted {",
246
+ " items.sort_unstable();",
247
+ " }",
248
+ " if items.is_empty() {",
249
+ " return when_empty.to_string();",
250
+ " }",
251
+ " let write = |item: &&str| if inspect { quoted(item) } else { item.to_string() };",
252
+ " items.iter().map(write).collect::<Vec<_>>().join(separator)",
253
+ "}",
254
+ ""
255
+ ]
256
+ support + variants.zip(rows).flat_map do |variant, row|
257
+ args_struct(enum, variant, row, by_site.fetch([row.fetch("refusal"), row.fetch("site")]))
258
+ end
259
+ end
260
+
261
+ def args_struct(enum, variant, row, specs)
262
+ fields = specs.flat_map do |spec|
263
+ type = spec.fetch("shape") == "list" ? "&'a [&'a str]" : "&'a str"
264
+ [" /// #{rule_reading(spec)}", " pub #{rust_field(spec.fetch('argument'))}: #{type},"]
265
+ end
266
+ locals = specs.filter_map { |spec| formatted_local(spec) }
267
+ pairs = specs.map do |spec|
268
+ name = spec.fetch("argument")
269
+ value = formatted_local(spec) ? "#{local_name(name)}.as_str()" : "self.#{rust_field(name)}"
270
+ " (#{rust_string(name)}, #{value}),"
271
+ end
272
+ ["/// `#{enum}::#{variant}`'s arguments — `RefusalWording.render_site(" \
273
+ "#{rust_string(row.fetch('refusal'))}, #{rust_string(row.fetch('site'))}, ...)`.",
274
+ "#[derive(Debug, Clone, Copy)]",
275
+ "pub struct #{variant}Args<'a> {",
276
+ fields,
277
+ "}",
278
+ "",
279
+ "impl #{variant}Args<'_> {",
280
+ " /// The site's wording, every argument formatted by its declared row.",
281
+ " pub fn render_args(&self) -> String {",
282
+ locals.map { |line| " #{line}" },
283
+ " #{enum}::#{variant}.render(&[",
284
+ pairs,
285
+ " ])",
286
+ " }",
287
+ "}",
288
+ ""]
289
+ end
290
+
291
+ def formatted_local(spec)
292
+ name = spec.fetch("argument")
293
+ field = "self.#{rust_field(name)}"
294
+ if spec.fetch("shape") == "list"
295
+ "let #{local_name(name)} = list(#{field}, #{spec.fetch('sorted')}, #{spec.fetch('quoting') == 'inspect'}, " \
296
+ "#{rust_string(spec.fetch('separator'))}, #{rust_string(spec.fetch('when_empty'))});"
297
+ elsif spec.fetch("quoting") == "inspect"
298
+ "let #{local_name(name)} = quoted(#{field});"
299
+ end
300
+ end
301
+
302
+ def rule_reading(spec)
303
+ quoting = spec.fetch("quoting") == "inspect" ? ", quoted" : ""
304
+ return "scalar#{quoting}" unless spec.fetch("shape") == "list"
305
+
306
+ sorted = spec.fetch("sorted") == "true" ? ", sorted" : ""
307
+ "list#{sorted}#{quoting}, joined #{spec.fetch('separator').inspect}, empty reads #{spec.fetch('when_empty').inspect}"
308
+ end
309
+
310
+ def local_name(argument) = "#{argument}_text"
311
+
312
+ def rust_field(argument) = RUST_KEYWORDS.include?(argument) ? "r##{argument}" : argument
313
+
314
+ def tests(enum, kind, variants = [], rows = [], by_site = nil)
315
+ list = kind == :order ? "ORDER" : "ALL"
316
+ name = accessor_name(kind == :templates ? "site" : name_field(kind))
317
+ lines = ["#[cfg(test)]",
318
+ "mod tests {",
319
+ " use super::*;",
320
+ ""]
321
+ unless kind == :templates
322
+ lines += [" #[test]",
323
+ " fn every_row_is_found_by_its_own_name() {",
324
+ " for row in #{enum}::#{list}.iter() {",
325
+ " assert_eq!(#{enum}::from_name(row.#{name}()), Some(*row));",
326
+ " }",
327
+ " }",
328
+ ""]
329
+ end
330
+ lines += order_test(enum) if kind == :order
331
+ if kind == :templates
332
+ lines += placeholder_test(enum)
333
+ lines += [""] + render_args_test(variants, rows, by_site)
334
+ end
335
+ lines.pop while lines.last == ""
336
+ lines + ["}"]
337
+ end
338
+
339
+ def order_test(enum)
340
+ [" #[test]",
341
+ " fn position_is_the_index_in_order() {",
342
+ " for (index, step) in #{enum}::ORDER.iter().enumerate() {",
343
+ " assert_eq!(step.position(), index);",
344
+ " }",
345
+ " }",
346
+ ""]
347
+ end
348
+
349
+ # Placeholders are read off each template's own text, never a second
350
+ # hand-kept list.
351
+ def placeholder_test(enum)
352
+ [" #[test]",
353
+ " fn every_site_renders_with_no_leftover_placeholder() {",
354
+ " for site in #{enum}::ALL {",
355
+ " let template = site.template();",
356
+ " let mut keys: Vec<String> = Vec::new();",
357
+ " let mut current: Option<String> = None;",
358
+ " for ch in template.chars() {",
359
+ " match (ch, current.as_mut()) {",
360
+ " ('{', _) => current = Some(String::new()),",
361
+ " ('}', Some(key)) => {",
362
+ " keys.push(key.clone());",
363
+ " current = None;",
364
+ " }",
365
+ " (_, Some(key)) => key.push(ch),",
366
+ " (_, None) => {}",
367
+ " }",
368
+ " }",
369
+ " let dummies: Vec<(String, String)> = keys.iter().map(|k| (k.clone(), format!(\"<{k}>\"))).collect();",
370
+ " let pairs: Vec<(&str, &str)> = dummies.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect();",
371
+ " let rendered = site.render(&pairs);",
372
+ " assert!(",
373
+ " !rendered.contains('{') && !rendered.contains('}'),",
374
+ " \"{site:?} left an unrendered placeholder behind: {rendered:?}\"",
375
+ " );",
376
+ " }",
377
+ " }"]
378
+ end
379
+
380
+ # THE RUBY ORACLE, PINNED. Every site renders through its typed
381
+ # `render_args` for each edge case a list argument has — empty, one
382
+ # item, several out of order — and must equal what
383
+ # Runtime::RefusalWording.render_with answers for the same values,
384
+ # computed here at generation time. Scalars carry a `"` so quoting
385
+ # is compared too.
386
+ def render_args_test(variants, rows, by_site)
387
+ asserts = variants.zip(rows).flat_map do |variant, row|
388
+ specs = by_site.fetch([row.fetch("refusal"), row.fetch("site")])
389
+ argument_cases(specs).map do |arguments|
390
+ expected = Runtime::RefusalWording.render_with(row.fetch("template"), specs, arguments)
391
+ fields = specs.map do |spec|
392
+ value = arguments.fetch(spec.fetch("argument").to_sym)
393
+ rust_value = value.is_a?(Array) ? "&[#{value.map { |item| rust_string(item) }.join(', ')}]" : rust_string(value)
394
+ "#{rust_field(spec.fetch('argument'))}: #{rust_value}"
395
+ end
396
+ [" assert_eq!(",
397
+ " #{variant}Args { #{fields.join(', ')} }.render_args(),",
398
+ " #{rust_string(expected)}",
399
+ " );"]
400
+ end
401
+ end
402
+ [" #[test]",
403
+ " fn render_args_matches_ruby_render_site() {",
404
+ asserts,
405
+ " }"].flatten
406
+ end
407
+
408
+ LIST_CASES = [[], ["only \"one\""], %w[zeta alpha mid]].freeze
409
+
410
+ def argument_cases(specs)
411
+ cases = specs.any? { |spec| spec.fetch("shape") == "list" } ? LIST_CASES : [nil]
412
+ cases.map do |items|
413
+ specs.to_h do |spec|
414
+ name = spec.fetch("argument")
415
+ [name.to_sym, spec.fetch("shape") == "list" ? items : "#{name} \"x\""]
416
+ end
417
+ end
418
+ end
419
+
420
+ def name_field(kind) = kind == :order ? "step" : "name"
421
+
422
+ # `refusal` is a Rust-safe word but reads ambiguously beside the
423
+ # kernel's own `Refusal` enum; the accessor says what it answers.
424
+ def accessor_name(field) = field == "refusal" ? "refusal_class" : field
425
+
426
+ def variant_name(row, fields)
427
+ name = fields.map { |field| pascal(row.fetch(field)) }.join
428
+ raise ArgumentError, "#{name.inspect} is not a Rust variant name" unless name.match?(/\A[A-Z][A-Za-z0-9]*\z/)
429
+
430
+ name
431
+ end
432
+
433
+ def pascal(text) = text.to_s.split("_").map { |part| part[0].to_s.upcase + part[1..].to_s }.join
434
+
435
+ # A Rust string literal: only `\`, `"` and newlines need escaping in
436
+ # the text any vocabulary row holds.
437
+ def rust_string(text)
438
+ escaped = text.to_s.gsub("\\") { "\\\\" }.gsub('"') { "\\\"" }.gsub("\n") { "\\n" }
439
+ "\"#{escaped}\""
440
+ end
441
+ end
442
+ end
443
+ end
@@ -3,11 +3,11 @@ require_relative "../projector"
3
3
 
4
4
  module Hecks
5
5
  module Projections
6
- # THE STORAGE SHAPE of one bluebook — the same structural form
6
+ # The storage shape of one bluebook — the same structural form
7
7
  # `StorageShape.mint_hash` hashes to name an era, which is what makes
8
8
  # a bump/no-bump question answerable by diffing two of these.
9
9
  #
10
- # THE RETROFIT, and the point of it: `Runtime::StorageShape.project`
10
+ # The retrofit, and the point of it: `Runtime::StorageShape.project`
11
11
  # was written long before this framework existed, and it already
12
12
  # takes exactly one bluebook, already returns a plain Hash, already
13
13
  # touches no disk and no live runtime — it even already uses the
@@ -2,7 +2,7 @@ require_relative "../projector"
2
2
 
3
3
  module Hecks
4
4
  module Projections
5
- # A DOMAIN'S OWN DECLARED FACTS, PROJECTED AS PLAIN ENGLISH SENTENCES
5
+ # A domain's own declared facts, projected as plain english sentences
6
6
  # — one atomic, independently-checkable statement per fact, not a
7
7
  # flowing document (`Projections::Reference`/`DocsProjector` already
8
8
  # do that job well; this is a flat list a caller can iterate, diff,
@@ -10,25 +10,25 @@ module Hecks
10
10
  # a sanity check that the domain still says what someone thinks it
11
11
  # says).
12
12
  #
13
- # NEVER INVENTS A SENTENCE FROM NOTHING — `DocsProjector`'s own
13
+ # **Never invents a sentence from nothing** — `DocsProjector`'s own
14
14
  # discipline, held here too: a relationship's sentence is built
15
- # MECHANICALLY from its own declared shape (holder, target,
15
+ # mechanically from its own declared shape (holder, target,
16
16
  # relationship kind — the same facts `Projections::Diagrams`'s own
17
- # relationship_edge already reads), and an invariant's sentence IS
17
+ # relationship_edge already reads), and an invariant's sentence is
18
18
  # the domain author's own `description`, capitalized and
19
19
  # punctuated, never paraphrased. If a fact has no author-written
20
20
  # description and no unambiguous mechanical phrasing, it doesn't
21
21
  # get a sentence here — a wrong sentence is worse than a missing
22
22
  # one.
23
23
  #
24
- # REACHABLE THE SAME WAY EVERY PROJECTION ALREADY IS, no new facade
24
+ # Reachable the same way every projection already is, no new facade
25
25
  # wiring needed: `Pizzas.project(Projections::Statements)` —
26
26
  # `Facade::Surface::Chapter#project`'s own comment already settled
27
27
  # this ("anything genuinely needing the graph is a projector, and a
28
28
  # projector is given it"). `bin/statements` is a thin, optional
29
29
  # convenience for reaching the same call from a shell.
30
30
  #
31
- # MVP SCOPE: "has many"/"has a"/"belongs to"/"references" sentences
31
+ # MVP scope: "has many"/"has a"/"belongs to"/"references" sentences
32
32
  # for every list or relationship attribute, and every invariant's
33
33
  # own description (aggregate-level and every nested value object's),
34
34
  # verbatim. Lifecycle transitions and command given/ensures read the
@@ -50,7 +50,7 @@ module Hecks
50
50
  attribute_statements(holder) + invariant_statements(holder)
51
51
  end
52
52
 
53
- # "MANY" READS THE SAME WAY REGARDLESS OF WHAT'S BEHIND IT — a
53
+ # "MANY" reads the same way regardless of what's behind it — a
54
54
  # `has_many` relationship to another aggregate and a plain
55
55
  # `list_of(Topping)` value-object attribute are the same idea to
56
56
  # someone reading the domain in English ("an Order has many
@@ -77,17 +77,17 @@ module Hecks
77
77
  end
78
78
  end
79
79
 
80
- # "AN ACCOUNT", NOT "A ACCOUNT" — every subject/object noun here is
80
+ # "An account", not "A ACCOUNT" — every subject/object noun here is
81
81
  # a bare construct name (`ATMCard`, `ExternalTransfer`, `Account`),
82
82
  # never free text, so the ordinary "starts with a vowel LETTER"
83
83
  # heuristic is safe: this language's own naming never produces the
84
84
  # English exceptions that heuristic gets wrong (an "hour", a
85
85
  # "university") because a construct name is always spelled as a
86
86
  # plain word, never an abbreviation read letter-by-letter or a
87
- # word starting with a consonant LETTER but a vowel SOUND.
87
+ # word starting with a consonant letter but a vowel sound.
88
88
  def article(word) = word.to_s.match?(/\A[AEIOUaeiou]/) ? "An" : "A"
89
89
 
90
- # INVARIANTS LIVE IN TWO PLACES — directly on the holder (an
90
+ # Invariants live in two places — directly on the holder (an
91
91
  # aggregate-level rule, checked after every command) and on every
92
92
  # value object nested inside it — `DocsProjector#rules_of`'s own
93
93
  # `value_object_for` lookup is the precedent for walking both.
@@ -97,7 +97,7 @@ module Hecks
97
97
  (own + nested).map { |invariant| invariant_statement(invariant) }
98
98
  end
99
99
 
100
- # THE DOMAIN AUTHOR'S OWN WORDS, CAPITALIZED AND PUNCTUATED
100
+ # The domain author's own words, capitalized and punctuated
101
101
  # nothing else. `invariant("a pizza is named")` already reads as
102
102
  # a sentence; this is the entire transformation.
103
103
  def invariant_statement(invariant)
@@ -2,20 +2,20 @@ require_relative "../projector"
2
2
 
3
3
  module Hecks
4
4
  module Projections
5
- # THE CLOSED SETS, PROJECTED — lib/hecks/vocabulary.rb rendered
5
+ # **The closed sets, projected** — lib/hecks/vocabulary.rb rendered
6
6
  # from whichever chapter declares a `Vocabulary` aggregate, which in
7
7
  # practice is the language projecting its own tables.
8
8
  #
9
9
  # Projector.call(:vocabulary, bluebook: <the Bluebook chapter>)
10
10
  #
11
- # A PROJECTION RATHER THAN A BIN/ SCRIPT, and that correction is the
11
+ # A projection rather than a bin/ script, and that correction is the
12
12
  # point of it. This was first written as its own program with its own
13
13
  # call shape, beside `bin/project_parser_table`, `bin/reference` and
14
14
  # `bin/expression_projection` — four tools doing "canonical IR in,
15
15
  # external artifact out" four different ways, which is the exact
16
16
  # thing the projector registry exists to stop.
17
17
  #
18
- # Reads the chapter's JUDGED IR, so what is written out is what the
18
+ # Reads the chapter's judged IR, so what is written out is what the
19
19
  # language actually holds rather than what a builder happened to
20
20
  # produce.
21
21
  module Vocabulary
@@ -24,10 +24,10 @@ module Hecks
24
24
  projects_as :vocabulary, declares: "Vocabulary"
25
25
 
26
26
  HEADER = <<~RUBY.freeze
27
- # GENERATED — projected from the language's own Vocabulary aggregate
27
+ # Generated — projected from the language's own Vocabulary aggregate
28
28
  # (lib/hecks/language/bluebook/vocabulary.bluebook).
29
29
  #
30
- # DO NOT EDIT. spec/vocabulary_table_spec.rb re-projects this in memory
30
+ # Do not edit. spec/vocabulary_table_spec.rb re-projects this in memory
31
31
  # and refuses a diff, so an edit here fails the ordinary suite.
32
32
  #
33
33
  # Plain data on purpose — no requires, no dependency on the model —
@@ -42,9 +42,9 @@ module Hecks
42
42
  # has nothing to vary.
43
43
  def call(bluebook:, options: {}) = render(bluebook)
44
44
 
45
- # FULL ROWS, not just the first field of each.
45
+ # Full rows, not just the first field of each.
46
46
  #
47
- # Most vocabularies ARE one-field lists and the field is the term.
47
+ # Most vocabularies are one-field lists and the field is the term.
48
48
  # Several are not: `Comparison` declares the algebra each operator
49
49
  # computes with, `RefusalTemplate` an error plus its key and text.
50
50
  # Taking the first field of those produced a list of thirty-nine
@@ -69,8 +69,8 @@ module Hecks
69
69
  #{rows.join(",\n")}
70
70
  }.freeze
71
71
 
72
- # THE TERMS — the first field of each row, which for a
73
- # one-field vocabulary is the whole of it. Derived ONCE and
72
+ # The terms — the first field of each row, which for a
73
+ # one-field vocabulary is the whole of it. Derived once and
74
74
  # frozen rather than mapped per call: these are constant
75
75
  # tables, and a constant that allocates a new array every
76
76
  # time it is read is not one.
@@ -1,7 +1,7 @@
1
1
  require_relative "projector"
2
2
 
3
3
  module Hecks
4
- # THE TARGETS A DOMAIN CAN BE PROJECTED INTO, as constants rather than
4
+ # The targets a domain can be projected into, as constants rather than
5
5
  # bare symbols — `Pizzas.project(Projections::OIDC)`.
6
6
  #
7
7
  # A constant is worth the namespace for two reasons a Symbol cannot
@@ -10,9 +10,9 @@ module Hecks
10
10
  # carry its own documentation and defaults.
11
11
  #
12
12
  # Namespaced rather than top-level because `IR` is already taken —
13
- # `Hecks::Bluebook` is the MODEL (`Bluebook::Command`, and the
13
+ # `Hecks::Bluebook` is the model (`Bluebook::Command`, and the
14
14
  # chapter class itself).
15
- # `Projections::IR` is the projection OF that model, a different thing
15
+ # `Projections::IR` is the projection of that model, a different thing
16
16
  # that would be genuinely confusing under the same bare name.
17
17
  #
18
18
  # `include Hecks::Projections` gets the short spelling where the
@@ -26,6 +26,8 @@ require_relative "projections/shape"
26
26
  require_relative "projections/oidc"
27
27
  require_relative "projections/vocabulary"
28
28
  require_relative "projections/parser_table"
29
+ require_relative "projections/bootstrap_table"
30
+ require_relative "projections/rust_vocabulary"
29
31
  require_relative "projections/reference"
30
32
  require_relative "projections/model"
31
33
  require_relative "projections/diagrams"