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,8 +2,11 @@ Hecks.bluebook "Bluebook" do
2
2
  aggregate "Vocabulary" do
3
3
  # These are DECLARATIONS, not runtime state — a language's admitted sets are
4
4
  # fixed when the language is defined, which is what one_of means. They carry
5
- # no commands on purpose : nothing dispatches a vocabulary, the conformance
6
- # check READS them from the IR and holds the runtime's hand-typed tables to them.
5
+ # no commands on purpose : nothing dispatches a vocabulary. They are READ
6
+ # from the IR and projected — bin/project_vocabulary writes the Ruby tables
7
+ # (lib/hecks/vocabulary.rb) the runtime's constants read, and
8
+ # bin/project_rust_vocabulary writes rust/src/kernel/vocab/ — so the
9
+ # runtimes carry no hand-typed copies to drift.
7
10
  #
8
11
  # Every one of these is a set the runtime must agree with, and every one has
9
12
  # already drifted or leaked at least once :
@@ -171,9 +174,17 @@ Hecks.bluebook "Bluebook" do
171
174
  # fires for a creating command, advance_lifecycle only when a transition
172
175
  # applies — so this is the order a command that does both follows,
173
176
  # confirmed by tracing a real dispatch, not read off the source.
177
+ #
178
+ # decode_arguments leads both dispatch orders: turning the caller's raw
179
+ # facts into a command's declared arguments, before any argument gate
180
+ # judges them. A no-op in Ruby today (routing already hands the
181
+ # interpreter a decoded hash) and in the Rust kernel's dispatch loop
182
+ # (generated `from_json` still decodes and gates in one pass) — declared
183
+ # now so the Rust kernel's step enum has a place for a generated decoder
184
+ # to move into, rather than the order being implied by generated code.
174
185
  value_object "AggregateDispatchOrder" do
175
186
  attribute :step, String,
176
- one_of: ["refuse_unknown_arguments", "refuse_absent_arguments", "normalize_args",
187
+ one_of: ["decode_arguments", "refuse_unknown_arguments", "refuse_absent_arguments", "normalize_args",
177
188
  "refuse_role_mismatch", "resolve_references", "hydrate", "enforce_givens",
178
189
  "admissible_transition", "assign_creation_attributes", "apply_mutations",
179
190
  "advance_lifecycle", "delegate_to_entity", "enforce_ensures", "enforce_invariants",
@@ -197,7 +208,7 @@ Hecks.bluebook "Bluebook" do
197
208
  # both interpreters), even though nothing in the real corpus does yet.
198
209
  value_object "EntityDispatchOrder" do
199
210
  attribute :step, String,
200
- one_of: ["refuse_unknown_arguments", "refuse_absent_arguments", "normalize_args",
211
+ one_of: ["decode_arguments", "refuse_unknown_arguments", "refuse_absent_arguments", "normalize_args",
201
212
  "refuse_role_mismatch", "resolve_references", "hydrate_parent",
202
213
  "locate_element", "enforce_givens", "admissible_transition", "apply_mutations",
203
214
  "advance_lifecycle", "enforce_ensures", "enforce_invariants", "save", "emit"]
@@ -219,11 +230,10 @@ Hecks.bluebook "Bluebook" do
219
230
  # declared them. Everything here is different in kind: a LANGUAGE-LEVEL
220
231
  # refusal, the same wording for every domain, not authored per-bluebook —
221
232
  # closer to Comparison/SignTest beside it than to a given. So it follows
222
- # THEIR pattern, not given's: declared here for a human to read and a
223
- # conformance spec to hold equal, and a hand-typed table
224
- # (RefusalWording) is what a dispatch
225
- # actually reads — the same reason Evaluator::SIGN_TEST_OPERATORS is
226
- # hand-typed rather than read live off this very chapter at every call.
233
+ # THEIR pattern, not given's: declared here, and projected into the
234
+ # generated tables a dispatch actually reads (Runtime::RefusalWording in
235
+ # Ruby, kernel::vocab::RefusalSite in Rust) rather than read live off
236
+ # this very chapter at every call.
227
237
  #
228
238
  # `{name}` markers are placeholders — plain text substitution, not
229
239
  # expression syntax ; a template is read, never evaluated. Two call
@@ -351,6 +361,303 @@ Hecks.bluebook "Bluebook" do
351
361
  "or read {reference}.{remote_field} directly if this rule cannot wait"
352
362
  end
353
363
 
364
+ # THE ARGUMENTS EACH RefusalTemplate TAKES, AND HOW EACH ONE IS WRITTEN.
365
+ # A template says WHERE a value goes; this says WHICH values a site
366
+ # takes (in the order its placeholders first appear) and how each is
367
+ # rendered before it goes there — so a caller hands over the raw value
368
+ # (a list of names, a name) rather than a string it formatted itself.
369
+ # BUG#134 was exactly the hand-formatted kind: Ruby's `declared_reading`
370
+ # fell back to "none" for an empty list, the Rust generators joined
371
+ # the list themselves and never did.
372
+ #
373
+ # Read by Runtime::RefusalWording.render_site (a missing or extra
374
+ # argument refuses to render at all) and projected into
375
+ # rust/src/kernel/vocab/refusal_template.rs as one typed args struct
376
+ # per site (`render_args` — a missing argument does not compile).
377
+ #
378
+ # shape — "scalar" (one value) or "list" (a collection of them)
379
+ # quoting — "none" (the value's own text) or "inspect" (Ruby's
380
+ # `#inspect`; Rust's `{:?}`), per value or per item
381
+ # separator — what a list's items are joined with
382
+ # sorted — "true" sorts a list's items first (before quoting)
383
+ # when_empty — what an empty list renders as
384
+ #
385
+ # Recorded from what the Ruby call sites did when this table was
386
+ # introduced; no wording changed. A value whose rendering depends on
387
+ # its runtime type (`Rendering.describe`, `offered.inspect` of an
388
+ # arbitrary value) stays a caller-rendered "none" scalar, as do the
389
+ # identity readings (`identity`, `heads`, `wants`) the Rust side already
390
+ # joins once at codegen time or inside `extract_wants`.
391
+ value_object "RefusalSiteArgument" do
392
+ attribute :refusal, String, admits: "Vocabulary::DomainRefusal"
393
+ attribute :site, String
394
+ attribute :argument, String
395
+ attribute :shape, String
396
+ attribute :quoting, String
397
+ attribute :separator, String
398
+ attribute :sorted, String
399
+ attribute :when_empty, String
400
+
401
+ member refusal: "NotFound", site: "creating_no_identity", argument: "command",
402
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
403
+ member refusal: "NotFound", site: "creating_no_identity", argument: "aggregate",
404
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
405
+ member refusal: "NotFound", site: "creating_no_identity", argument: "identity",
406
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
407
+ member refusal: "AlreadyExists", site: "creating_duplicate", argument: "command",
408
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
409
+ member refusal: "AlreadyExists", site: "creating_duplicate", argument: "aggregate",
410
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
411
+ member refusal: "AlreadyExists", site: "creating_duplicate", argument: "identity",
412
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
413
+ member refusal: "AlreadyExists", site: "creating_duplicate", argument: "offered",
414
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
415
+ member refusal: "AlreadyExists", site: "entity_duplicate", argument: "entity",
416
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
417
+ member refusal: "AlreadyExists", site: "entity_duplicate", argument: "aggregate",
418
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
419
+ member refusal: "AlreadyExists", site: "entity_duplicate", argument: "identity",
420
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
421
+ # One described value per identity head (MutationApplier), or the
422
+ # one offered element identity (EntityListCoercion).
423
+ member refusal: "AlreadyExists", site: "entity_duplicate", argument: "offered",
424
+ shape: "list", quoting: "none", separator: ", ", sorted: "false", when_empty: ""
425
+ member refusal: "NotFound", site: "acting_no_identity", argument: "command",
426
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
427
+ member refusal: "NotFound", site: "acting_no_identity", argument: "aggregate",
428
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
429
+ member refusal: "NotFound", site: "acting_no_identity", argument: "identity",
430
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
431
+ member refusal: "NotFound", site: "record_missing", argument: "aggregate",
432
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
433
+ member refusal: "NotFound", site: "record_missing", argument: "identity",
434
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
435
+ member refusal: "NotFound", site: "record_missing", argument: "offered",
436
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
437
+ member refusal: "NotFound", site: "entity_parent_no_identity", argument: "command",
438
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
439
+ member refusal: "NotFound", site: "entity_parent_no_identity", argument: "aggregate",
440
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
441
+ member refusal: "NotFound", site: "entity_parent_no_identity", argument: "entity",
442
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
443
+ member refusal: "NotFound", site: "entity_parent_no_identity", argument: "identity",
444
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
445
+ member refusal: "UnknownVerb", site: "entity_unknown", argument: "aggregate",
446
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
447
+ member refusal: "UnknownVerb", site: "entity_unknown", argument: "entity",
448
+ shape: "scalar", quoting: "inspect", separator: "", sorted: "false", when_empty: ""
449
+ member refusal: "NotFound", site: "entity_element_no_identity", argument: "command",
450
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
451
+ member refusal: "NotFound", site: "entity_element_no_identity", argument: "entity",
452
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
453
+ member refusal: "NotFound", site: "entity_element_no_identity", argument: "identity",
454
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
455
+ member refusal: "NotFound", site: "entity_element_missing", argument: "entity",
456
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
457
+ member refusal: "NotFound", site: "entity_element_missing", argument: "identity",
458
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
459
+ member refusal: "NotFound", site: "entity_element_missing", argument: "wants",
460
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
461
+ member refusal: "NotFound", site: "entity_element_missing", argument: "aggregate",
462
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
463
+ member refusal: "NotFound", site: "entity_element_missing", argument: "parent_id",
464
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
465
+ member refusal: "NotFound", site: "reference_target_missing", argument: "target",
466
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
467
+ member refusal: "NotFound", site: "reference_target_missing", argument: "heads",
468
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
469
+ member refusal: "NotFound", site: "reference_target_missing", argument: "key",
470
+ shape: "scalar", quoting: "inspect", separator: "", sorted: "false", when_empty: ""
471
+ member refusal: "NotFound", site: "read_model_reference_missing", argument: "aggregate",
472
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
473
+ member refusal: "NotFound", site: "read_model_reference_missing", argument: "offered",
474
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
475
+ member refusal: "TypeMismatch", site: "read_model_object_reference", argument: "query",
476
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
477
+ member refusal: "TypeMismatch", site: "read_model_object_reference", argument: "field",
478
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
479
+ member refusal: "UnknownVerb", site: "no_query", argument: "aggregate",
480
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
481
+ member refusal: "UnknownVerb", site: "no_query", argument: "query",
482
+ shape: "scalar", quoting: "inspect", separator: "", sorted: "false", when_empty: ""
483
+ member refusal: "UnknownVerb", site: "entity_query_missing", argument: "entity",
484
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
485
+ member refusal: "UnknownVerb", site: "entity_query_missing", argument: "query",
486
+ shape: "scalar", quoting: "inspect", separator: "", sorted: "false", when_empty: ""
487
+ member refusal: "UnknownVerb", site: "entity_holds_no_list", argument: "aggregate",
488
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
489
+ member refusal: "UnknownVerb", site: "entity_holds_no_list", argument: "entity",
490
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
491
+ member refusal: "UnknownVerb", site: "entity_no_command", argument: "entity",
492
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
493
+ member refusal: "UnknownVerb", site: "entity_no_command", argument: "command",
494
+ shape: "scalar", quoting: "inspect", separator: "", sorted: "false", when_empty: ""
495
+ member refusal: "UnknownVerb", site: "aggregate_no_command", argument: "aggregate",
496
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
497
+ member refusal: "UnknownVerb", site: "aggregate_no_command", argument: "command",
498
+ shape: "scalar", quoting: "inspect", separator: "", sorted: "false", when_empty: ""
499
+ member refusal: "UnknownVerb", site: "port_no_operation", argument: "port",
500
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
501
+ member refusal: "UnknownVerb", site: "port_no_operation", argument: "operation",
502
+ shape: "scalar", quoting: "inspect", separator: "", sorted: "false", when_empty: ""
503
+ member refusal: "UnknownVerb", site: "no_domain", argument: "domain",
504
+ shape: "scalar", quoting: "inspect", separator: "", sorted: "false", when_empty: ""
505
+ member refusal: "UnknownVerb", site: "no_domain", argument: "verb",
506
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
507
+ member refusal: "UnknownVerb", site: "no_read_model", argument: "domain",
508
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
509
+ member refusal: "UnknownVerb", site: "no_read_model", argument: "query",
510
+ shape: "scalar", quoting: "inspect", separator: "", sorted: "false", when_empty: ""
511
+ member refusal: "UnknownVerb", site: "not_fully_qualified", argument: "verb",
512
+ shape: "scalar", quoting: "inspect", separator: "", sorted: "false", when_empty: ""
513
+ member refusal: "UnknownVerb", site: "no_aggregate", argument: "domain",
514
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
515
+ member refusal: "UnknownVerb", site: "no_aggregate", argument: "aggregate",
516
+ shape: "scalar", quoting: "inspect", separator: "", sorted: "false", when_empty: ""
517
+ member refusal: "LifecycleRefused", site: "transition_blocked", argument: "command",
518
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
519
+ member refusal: "LifecycleRefused", site: "transition_blocked", argument: "field",
520
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
521
+ member refusal: "LifecycleRefused", site: "transition_blocked", argument: "current",
522
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
523
+ member refusal: "LifecycleRefused", site: "transition_blocked", argument: "allowed",
524
+ shape: "list", quoting: "inspect", separator: " or ", sorted: "false", when_empty: ""
525
+ member refusal: "TypeMismatch", site: "value_object_shape", argument: "name",
526
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
527
+ member refusal: "TypeMismatch", site: "value_object_shape", argument: "type",
528
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
529
+ member refusal: "TypeMismatch", site: "value_object_shape", argument: "offered",
530
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
531
+ member refusal: "TypeMismatch", site: "reference_wrong_shape", argument: "command",
532
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
533
+ member refusal: "TypeMismatch", site: "reference_wrong_shape", argument: "attribute",
534
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
535
+ member refusal: "TypeMismatch", site: "reference_wrong_shape", argument: "offered",
536
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
537
+ member refusal: "TypeMismatch", site: "reference_wrong_shape", argument: "known_by",
538
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
539
+ member refusal: "TypeMismatch", site: "multi_field_scalar", argument: "type",
540
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
541
+ member refusal: "TypeMismatch", site: "composite_identity", argument: "type",
542
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
543
+ member refusal: "TypeMismatch", site: "numeric_field", argument: "type",
544
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
545
+ member refusal: "TypeMismatch", site: "numeric_field", argument: "field",
546
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
547
+ member refusal: "TypeMismatch", site: "numeric_field", argument: "expected",
548
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
549
+ member refusal: "TypeMismatch", site: "numeric_field", argument: "offered",
550
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
551
+ member refusal: "TypeMismatch", site: "non_finite_field", argument: "type",
552
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
553
+ member refusal: "TypeMismatch", site: "non_finite_field", argument: "field",
554
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
555
+ member refusal: "TypeMismatch", site: "non_finite_field", argument: "offered",
556
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
557
+ member refusal: "TypeMismatch", site: "integer_range", argument: "type",
558
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
559
+ member refusal: "TypeMismatch", site: "integer_range", argument: "field",
560
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
561
+ member refusal: "TypeMismatch", site: "integer_range", argument: "offered",
562
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
563
+ member refusal: "TypeMismatch", site: "pattern_mismatch", argument: "type",
564
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
565
+ member refusal: "TypeMismatch", site: "pattern_mismatch", argument: "field",
566
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
567
+ member refusal: "TypeMismatch", site: "pattern_mismatch", argument: "pattern",
568
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
569
+ member refusal: "TypeMismatch", site: "pattern_mismatch", argument: "offered",
570
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
571
+ member refusal: "TypeMismatch", site: "arithmetic_amount", argument: "op",
572
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
573
+ member refusal: "TypeMismatch", site: "arithmetic_amount", argument: "target",
574
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
575
+ member refusal: "TypeMismatch", site: "arithmetic_amount", argument: "offered",
576
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
577
+ member refusal: "TypeMismatch", site: "arithmetic_current", argument: "op",
578
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
579
+ member refusal: "TypeMismatch", site: "arithmetic_current", argument: "target",
580
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
581
+ member refusal: "TypeMismatch", site: "arithmetic_current", argument: "offered",
582
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
583
+ member refusal: "TypeMismatch", site: "arithmetic_shared_field", argument: "op",
584
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
585
+ member refusal: "TypeMismatch", site: "arithmetic_shared_field", argument: "target",
586
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
587
+ member refusal: "UnknownArgument", site: "unknown_args", argument: "command",
588
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
589
+ member refusal: "UnknownArgument", site: "unknown_args", argument: "unknown",
590
+ shape: "list", quoting: "none", separator: ", ", sorted: "true", when_empty: ""
591
+ member refusal: "UnknownArgument", site: "unknown_args", argument: "declared",
592
+ shape: "list", quoting: "none", separator: ", ", sorted: "false", when_empty: "none"
593
+ member refusal: "AbsentArgument", site: "absent_args", argument: "command",
594
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
595
+ member refusal: "AbsentArgument", site: "absent_args", argument: "absent",
596
+ shape: "list", quoting: "none", separator: ", ", sorted: "true", when_empty: ""
597
+ member refusal: "AbsentArgument", site: "absent_args", argument: "declared",
598
+ shape: "list", quoting: "none", separator: ", ", sorted: "false", when_empty: "none"
599
+ member refusal: "InvariantViolation", site: "closed_set_member", argument: "type",
600
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
601
+ member refusal: "InvariantViolation", site: "closed_set_member", argument: "admitted",
602
+ shape: "list", quoting: "inspect", separator: ", ", sorted: "false", when_empty: ""
603
+ member refusal: "InvariantViolation", site: "closed_set_member", argument: "offered",
604
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
605
+ member refusal: "InvariantViolation", site: "value_object_invariant", argument: "name",
606
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
607
+ member refusal: "InvariantViolation", site: "value_object_invariant", argument: "description",
608
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
609
+ member refusal: "InvariantViolation", site: "value_object_invariant", argument: "offered",
610
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
611
+ member refusal: "InvariantViolation", site: "admits_declared_set", argument: "name",
612
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
613
+ member refusal: "InvariantViolation", site: "admits_declared_set", argument: "admits",
614
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
615
+ member refusal: "InvariantViolation", site: "admits_declared_set", argument: "admitted",
616
+ shape: "list", quoting: "inspect", separator: ", ", sorted: "false", when_empty: ""
617
+ member refusal: "InvariantViolation", site: "admits_declared_set", argument: "offered",
618
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
619
+ member refusal: "InvariantViolation", site: "undeclared_set", argument: "name",
620
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
621
+ member refusal: "InvariantViolation", site: "undeclared_set", argument: "admits",
622
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
623
+ member refusal: "Unauthorized", site: "tenant_required", argument: "query",
624
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
625
+ member refusal: "Unauthorized", site: "tenant_required", argument: "field",
626
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
627
+ member refusal: "Unauthorized", site: "role_mismatch", argument: "command",
628
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
629
+ member refusal: "Unauthorized", site: "role_mismatch", argument: "role",
630
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
631
+ member refusal: "Unauthorized", site: "role_mismatch", argument: "caller_role",
632
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
633
+ member refusal: "Unauthorized", site: "cross_tenant_reference", argument: "aggregate",
634
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
635
+ member refusal: "Unauthorized", site: "cross_tenant_reference", argument: "field",
636
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
637
+ member refusal: "Unauthorized", site: "cross_tenant_reference", argument: "tenant",
638
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
639
+ member refusal: "Unauthorized", site: "cross_tenant_reference", argument: "attribute",
640
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
641
+ member refusal: "Unauthorized", site: "cross_tenant_reference", argument: "target",
642
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
643
+ member refusal: "Unauthorized", site: "cross_tenant_reference", argument: "target_field",
644
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
645
+ member refusal: "Unauthorized", site: "cross_tenant_reference", argument: "other",
646
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
647
+ member refusal: "AttributeAbsent", site: "absent_read", argument: "aggregate",
648
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
649
+ member refusal: "AttributeAbsent", site: "absent_read", argument: "field",
650
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
651
+ member refusal: "ProjectionAbsent", site: "absent_read", argument: "aggregate",
652
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
653
+ member refusal: "ProjectionAbsent", site: "absent_read", argument: "field",
654
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
655
+ member refusal: "ProjectionAbsent", site: "absent_read", argument: "reference",
656
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
657
+ member refusal: "ProjectionAbsent", site: "absent_read", argument: "remote_field",
658
+ shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
659
+ end
660
+
354
661
  # SagaInterpreter::REFUSED.
355
662
  #
356
663
  # A leg answers an EVENT — `on "AccountDebited"` — or it answers one of these,
@@ -373,10 +680,9 @@ Hecks.bluebook "Bluebook" do
373
680
  # rendering convention, the same guess for every domain, not
374
681
  # authored per-bluebook — closer to Comparison/SignTest than to a
375
682
  # given. `pattern` is the regex SOURCE, no delimiters or flags
376
- # (Ruby's own `Regexp#source`) — spec/field_hint_conformance_spec.rb
377
- # holds FieldShape's four hand-typed constants equal to this, both
378
- # directions, the same conformance shape refusal_wording_
379
- # conformance_spec.rb already holds RefusalTemplate to.
683
+ # (Ruby's own `Regexp#source`) — FieldShape builds its four constants
684
+ # off these rows, and bin/project_field_hints writes the Rust host's
685
+ # copy from them, so neither side is typed by hand.
380
686
  #
381
687
  # `resolves_to` names WHICH Field attribute a match sets — `html_type`
382
688
  # for email/url/tel (three candidates, only one wins, checked in that
@@ -392,5 +698,34 @@ Hecks.bluebook "Bluebook" do
392
698
  member name: "tel", pattern: 'phone|\btel(ephone)?\b', resolves_to: "html_type"
393
699
  member name: "textarea", pattern: '\b(text|body|note|notes|description|message|comment)\b', resolves_to: "kind"
394
700
  end
701
+
702
+ # The Rust keywords (strict + reserved) a generated identifier must not
703
+ # collide with — RustProjection::Projector::RUST_KEYWORDS (rust/project/
704
+ # naming.rb) reads this, and bin/project_reserved_names projects it into
705
+ # rust/codegen/src/reserved_names.rs, so the Ruby and Rust generators
706
+ # escape (a field: `r#type`) or refuse (a module name) the SAME words.
707
+ # Declared once here because the two hand-typed copies were held equal
708
+ # by nothing but a comment.
709
+ value_object "RustReservedWord" do
710
+ attribute :word, String,
711
+ one_of: ["as", "break", "const", "continue", "crate", "dyn", "else", "enum", "extern", "false",
712
+ "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod", "move", "mut", "pub",
713
+ "ref", "return", "self", "Self", "static", "struct", "super", "trait", "true", "type",
714
+ "unsafe", "use", "where", "while", "abstract", "become", "box", "do", "final", "macro",
715
+ "override", "priv", "typeof", "unsized", "virtual", "yield", "try"]
716
+ end
717
+
718
+ # The keys a domain's own name must not take, because that name doubles
719
+ # as a Cargo `[features]` key: every key rust/Cargo.toml already uses
720
+ # outside `[features]` (`[package]`, `[lib]`, `[[bin]]`) plus `default`,
721
+ # which Cargo reserves for the auto-enabled feature set. Read by
722
+ # RustProjection::Projector::CARGO_RESERVED_DOMAIN_NAMES and projected by
723
+ # bin/project_reserved_names, same as RustReservedWord above.
724
+ value_object "CargoReservedName" do
725
+ attribute :name, String,
726
+ one_of: ["name", "version", "edition", "path", "authors", "license", "description", "default",
727
+ "features", "package", "lib", "bin", "dependencies", "dev-dependencies",
728
+ "build-dependencies", "workspace"]
729
+ end
395
730
  end
396
731
  end
@@ -100,6 +100,16 @@ Hecks.bluebook "Hecksagon" do
100
100
  member word: "uses_framework", context: "Hecksagon", body: "none", inner: "", opens: "", fills: "framework_members"
101
101
  member word: "uses_embryonaut_bluebook", context: "Hecksagon", body: "none", inner: "", opens: "", fills: "vendored_bluebooks"
102
102
  member word: "port", context: "Hecksagon", body: "keywords", inner: "DomainPort", opens: "DomainPort", fills: "", calls: "port_impl"
103
+ # A CROSS-DOMAIN REACTION, WIRED HERE INSTEAD OF THE BLUEBOOK — same
104
+ # underlying `Policy` shape a `policy` block already builds
105
+ # (`inner`/`opens: "Policy"` reuses that context's own `on`/`trigger`
106
+ # wholesale, zero new inner vocabulary), but declared as a wiring
107
+ # decision (which FOREIGN domain's event this chapter conforms to),
108
+ # the same kind of decision `port`/`uses_framework` already are.
109
+ # No `calls:` — `HecksagonBuilder#translates` is an ordinary method,
110
+ # the same shape `subscribe`/`uses_framework` already are, reached
111
+ # by Ruby's own method lookup before `word_gate` ever runs.
112
+ member word: "translates", context: "Hecksagon", body: "keywords", inner: "Policy", opens: "Policy", fills: ""
103
113
 
104
114
  end
105
115
 
@@ -125,6 +135,7 @@ Hecks.bluebook "Hecksagon" do
125
135
  member keyword: "uses_framework", context: "Hecksagon", at: "1", named: "", kind: "text", required: "true", fills: "framework_members"
126
136
  member keyword: "uses_embryonaut_bluebook", context: "Hecksagon", at: "1", named: "", kind: "text", required: "true", fills: "vendored_bluebooks"
127
137
  member keyword: "port", context: "Hecksagon", at: "1", named: "", kind: "text", required: "true", fills: "name"
138
+ member keyword: "translates", context: "Hecksagon", at: "1", named: "", kind: "text", required: "true", fills: "name"
128
139
 
129
140
  end
130
141
  end
@@ -76,6 +76,11 @@
76
76
  "verb": "Bluebook::Bluebook.Normalise",
77
77
  "role": "Language"
78
78
  },
79
+ {
80
+ "scope": "bluebook:bluebook.provide",
81
+ "verb": "Bluebook::Bluebook.Provide",
82
+ "role": "Language"
83
+ },
79
84
  {
80
85
  "scope": "bluebook:command.acts_on",
81
86
  "verb": "Bluebook::Command.ActsOn",
data/lib/hecks/literal.rb CHANGED
@@ -1,31 +1,31 @@
1
1
  module Hecks
2
- # THE ONE SPELLING FOR A CAPTURED RUBY LITERAL ON THE WIRE.
2
+ # The one spelling for a captured Ruby literal on the wire.
3
3
  #
4
4
  # Several `to_h` fields hold a value the author wrote in a bluebook —
5
5
  # `where(status: { eq: "open" })`, `then_set append: { direction: { value:
6
6
  # "credit" } }`, `dispatch ... with: { number: :source }`, an attribute's
7
- # `default:` — as TEXT, because the export has to stand on its own and a
7
+ # `default:` — as text, because the export has to stand on its own and a
8
8
  # String field cannot say "this one was a Symbol". Every such field is the
9
9
  # same question and now gets the same answer.
10
10
  #
11
11
  # It was three answers before, and they disagreed on both axes that matter.
12
- # `Mutation#appended_fields` spelled a Symbol BARE and everything else
13
- # `inspect`; `QuerySpecification.render_value` spelled a Symbol with a COLON
12
+ # `Mutation#appended_fields` spelled a Symbol bare and everything else
13
+ # `inspect`; `QuerySpecification.render_value` spelled a Symbol with a colon
14
14
  # and everything else `to_s`. So `:amount` crossed as "amount" in a mutation
15
15
  # and ":amount" in a saga binding — the same value, two spellings, and each
16
16
  # reader had to know which field it was looking at. Worse, `to_s`/`inspect`
17
- # on a Hash is Ruby's OWN rendering, which moved under us: 3.3 writes
17
+ # on a Hash is Ruby's own rendering, which moved under us: 3.3 writes
18
18
  # `{:value=>"credit"}` and 3.4 writes `{value: "credit"}` for the identical
19
19
  # Hash, so the wire format was silently pinned to an interpreter version.
20
20
  #
21
- # SELF-DESCRIBING IS THE RULE, stated once here rather than inherited from
21
+ # Self-describing is the rule, stated once here rather than inherited from
22
22
  # whatever `inspect` happens to do: a symbol wears its colon, a string wears
23
23
  # its quotes, a hash wears `{key: value}` braces, a list wears its brackets,
24
24
  # and a number, a boolean and nil are bare. `read` is the exact inverse, and
25
25
  # is the only thing that should ever take one of these strings apart.
26
- # A MUTATION SOURCE THAT READS THE RECORD'S OWN STATE — `sets :positions,
26
+ # A mutation source that reads the record's own state — `sets :positions,
27
27
  # append: { knights: state(:knights) }`. A bare Symbol in a mutation
28
- # source always names a command ARGUMENT (and imports it as one when
28
+ # source always names a command argument (and imports it as one when
29
29
  # the target's own field carries the same name — `CommandBuilder
30
30
  # #resolve_append_fields!`); before this there was no way to say "the
31
31
  # value this field already holds", so a command could not snapshot its
@@ -43,6 +43,12 @@ module Hecks
43
43
  module Literal
44
44
  module_function
45
45
 
46
+ # Turns a Ruby value into its self-describing wire spelling.
47
+ #
48
+ # @param value [Object] value to render: nil, Symbol, String, StateRef, true,
49
+ # false, Integer, Float, Hash, or Array (recursively)
50
+ # @return [String] the self-describing spelling `read` can parse back
51
+ # @raise [ArgumentError] if `value` is a type with no pinned literal spelling
46
52
  def render(value)
47
53
  case value
48
54
  when nil then "nil"
@@ -67,6 +73,9 @@ module Hecks
67
73
  # `return`; splitting them into named predicates would just rename
68
74
  # each line without changing what it does, and would separate this
69
75
  # method from the `render` it is the deliberate mirror of.
76
+ # @param text [String, #to_s] wire spelling produced by `render`, or a bare word
77
+ # @return [Object] nil, true, false, Integer, Float, Symbol, StateRef, String,
78
+ # Hash, or Array — or `text` itself, stripped, when it matches no known spelling
70
79
  # rubocop:disable-next Metrics/CyclomaticComplexity
71
80
  # rubocop:disable-next Metrics/PerceivedComplexity
72
81
  def read(text)
@@ -87,12 +96,28 @@ module Hecks
87
96
 
88
97
  ESCAPED = { '"' => '\\"', "\\" => "\\\\" }.freeze
89
98
 
99
+ # Wraps `text` in double quotes, escaping embedded quotes and backslashes.
100
+ #
101
+ # @param text [String] raw text to quote
102
+ # @return [String] the quoted, escaped spelling
90
103
  def quote(text) = "\"#{text.gsub(/["\\]/) { |char| ESCAPED[char] }}\""
91
104
 
105
+ # Tells whether `raw` is a double-quoted literal.
106
+ #
107
+ # @param raw [String] wire text to check
108
+ # @return [Boolean]
92
109
  def quoted?(raw) = raw.length >= 2 && raw.start_with?('"') && raw.end_with?('"')
93
110
 
111
+ # Strips the surrounding quotes from a quoted literal and unescapes it.
112
+ #
113
+ # @param raw [String] a quoted literal, as `quoted?` would confirm
114
+ # @return [String] the unescaped text inside the quotes
94
115
  def unquote(raw) = raw[1..-2].gsub(/\\(.)/) { ::Regexp.last_match(1) }
95
116
 
117
+ # Parses a `{key: value, ...}` wire literal.
118
+ #
119
+ # @param raw [String] text starting with `{` and ending with `}`
120
+ # @return [Hash{Symbol => Object}] the parsed hash, values read recursively via `read`
96
121
  def read_hash(raw)
97
122
  split_items(raw[1..-2]).to_h do |item|
98
123
  key, _, held = item.partition(":")
@@ -100,12 +125,19 @@ module Hecks
100
125
  end
101
126
  end
102
127
 
128
+ # Parses a `[value, ...]` wire literal.
129
+ #
130
+ # @param raw [String] text starting with `[` and ending with `]`
131
+ # @return [Array<Object>] the parsed values, each read recursively via `read`
103
132
  def read_array(raw) = split_items(raw[1..-2]).map { |item| read(item) }
104
133
 
105
- # Split on the commas that are actually SEPARATORS — never one inside a
134
+ # Split on the commas that are actually separators — never one inside a
106
135
  # quoted string or a nested brace/bracket. Scanned rather than
107
136
  # `String#split(", ")`, which tore `"a, b"` in half and lost the second
108
137
  # field of anything nested.
138
+ #
139
+ # @param body [String] the text between a literal's outer braces or brackets
140
+ # @return [Array<String>] each item's raw text, stripped, with empty items dropped
109
141
  def split_items(body)
110
142
  items = []
111
143
  current = +""