hecks 0.2.0 → 0.3.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 (408) hide show
  1. checksums.yaml +5 -5
  2. data/lib/hecks/adapters/driven/claude_code.adapter +3 -0
  3. data/lib/hecks/adapters/driven/claude_code.rb +127 -0
  4. data/lib/hecks/adapters/driven/d1.adapter +12 -0
  5. data/lib/hecks/adapters/driven/d1.rb +403 -0
  6. data/lib/hecks/adapters/driven/folder.adapter +3 -0
  7. data/lib/hecks/adapters/driven/folder.rb +199 -0
  8. data/lib/hecks/adapters/driven/google_authentication.adapter +3 -0
  9. data/lib/hecks/adapters/driven/google_authentication.rb +100 -0
  10. data/lib/hecks/adapters/driven/governance_authorization.adapter +3 -0
  11. data/lib/hecks/adapters/driven/governance_authorization.rb +62 -0
  12. data/lib/hecks/adapters/driven/heki/journal.rb +51 -0
  13. data/lib/hecks/adapters/driven/heki/saga_store.rb +0 -0
  14. data/lib/hecks/adapters/driven/heki/snapshot.rb +38 -0
  15. data/lib/hecks/adapters/driven/heki.adapter +4 -0
  16. data/lib/hecks/adapters/driven/heki.rb +148 -0
  17. data/lib/hecks/adapters/driven/identity_registry.adapter +3 -0
  18. data/lib/hecks/adapters/driven/identity_registry.rb +26 -0
  19. data/lib/hecks/adapters/driven/in_memory_ordering.rb +51 -0
  20. data/lib/hecks/adapters/driven/lambda/client.rb +63 -0
  21. data/lib/hecks/adapters/driven/lambda.adapter +4 -0
  22. data/lib/hecks/adapters/driven/lambda.rb +131 -0
  23. data/lib/hecks/adapters/driven/memory.adapter +3 -0
  24. data/lib/hecks/adapters/driven/memory.rb +103 -0
  25. data/lib/hecks/adapters/driven/mock_stripe_adapter.adapter +3 -0
  26. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +28 -0
  27. data/lib/hecks/adapters/driven/postgres/codec.rb +80 -0
  28. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +168 -0
  29. data/lib/hecks/adapters/driven/postgres.adapter +5 -0
  30. data/lib/hecks/adapters/driven/postgres.rb +391 -0
  31. data/lib/hecks/adapters/driven/postgres_era/lineage/era_store.rb +171 -0
  32. data/lib/hecks/adapters/driven/postgres_era/lineage/field_cache.rb +190 -0
  33. data/lib/hecks/adapters/driven/postgres_era/lineage/head_compiler.rb +429 -0
  34. data/lib/hecks/adapters/driven/postgres_era/lineage/mint_transaction.rb +166 -0
  35. data/lib/hecks/adapters/driven/postgres_era/lineage/provisioning.rb +286 -0
  36. data/lib/hecks/adapters/driven/postgres_era/lineage/resumable_backfill.rb +168 -0
  37. data/lib/hecks/adapters/driven/postgres_era/lineage/tail_merge.rb +163 -0
  38. data/lib/hecks/adapters/driven/postgres_era/lineage/transform_installer.rb +114 -0
  39. data/lib/hecks/adapters/driven/postgres_era/lineage.rb +137 -0
  40. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/coverage_check.rb +89 -0
  41. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/era_resolver.rb +82 -0
  42. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/merge_coordinator.rb +43 -0
  43. data/lib/hecks/adapters/driven/postgres_era/lineage_manager/minter.rb +132 -0
  44. data/lib/hecks/adapters/driven/postgres_era/lineage_manager.rb +74 -0
  45. data/lib/hecks/adapters/driven/postgres_era.adapter +6 -0
  46. data/lib/hecks/adapters/driven/postgres_era.rb +671 -0
  47. data/lib/hecks/adapters/driven/prism.adapter +3 -0
  48. data/lib/hecks/adapters/driven/prism.rb +80 -0
  49. data/lib/hecks/adapters/driven/secure_random_identity.adapter +3 -0
  50. data/lib/hecks/adapters/driven/secure_random_identity.rb +14 -0
  51. data/lib/hecks/adapters/driven/sql_query_builder.rb +221 -0
  52. data/lib/hecks/adapters/driven/sqlite/codec.rb +63 -0
  53. data/lib/hecks/adapters/driven/sqlite/projection.rb +105 -0
  54. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +180 -0
  55. data/lib/hecks/adapters/driven/sqlite.adapter +9 -0
  56. data/lib/hecks/adapters/driven/sqlite.rb +285 -0
  57. data/lib/hecks/adapters/driven/system_clock.adapter +3 -0
  58. data/lib/hecks/adapters/driven/system_clock.rb +14 -0
  59. data/lib/hecks/adapters/driven.rb +36 -0
  60. data/lib/hecks/adapters.rb +6 -0
  61. data/lib/hecks/behaviors/dsl.rb +96 -0
  62. data/lib/hecks/behaviors/expectations.rb +280 -0
  63. data/lib/hecks/behaviors/ir.rb +31 -0
  64. data/lib/hecks/behaviors/rspec.rb +42 -0
  65. data/lib/hecks/behaviors/runner.rb +96 -0
  66. data/lib/hecks/behaviors.rb +25 -0
  67. data/lib/hecks/bluebook/aggregate.rb +108 -0
  68. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +134 -0
  69. data/lib/hecks/bluebook/assembly/build.rb +48 -0
  70. data/lib/hecks/bluebook/assembly/contract.rb +112 -0
  71. data/lib/hecks/bluebook/assembly/contracts.rb +409 -0
  72. data/lib/hecks/bluebook/assembly/marks.rb +227 -0
  73. data/lib/hecks/bluebook/assembly/specializer.rb +70 -0
  74. data/lib/hecks/bluebook/assembly.rb +78 -0
  75. data/lib/hecks/bluebook/attribute.rb +96 -0
  76. data/lib/hecks/bluebook/behaviour/aggregate.rb +83 -0
  77. data/lib/hecks/bluebook/behaviour/attribute.rb +27 -0
  78. data/lib/hecks/bluebook/behaviour/chapter.rb +72 -0
  79. data/lib/hecks/bluebook/behaviour/command.rb +116 -0
  80. data/lib/hecks/bluebook/behaviour/domain_port.rb +25 -0
  81. data/lib/hecks/bluebook/behaviour/entity.rb +59 -0
  82. data/lib/hecks/bluebook/behaviour/hexagon.rb +50 -0
  83. data/lib/hecks/bluebook/behaviour/lifecycle.rb +51 -0
  84. data/lib/hecks/bluebook/behaviour/policy.rb +52 -0
  85. data/lib/hecks/bluebook/behaviour/process_manager.rb +38 -0
  86. data/lib/hecks/bluebook/behaviour/query.rb +10 -0
  87. data/lib/hecks/bluebook/behaviour/read_model.rb +29 -0
  88. data/lib/hecks/bluebook/behaviour/traits.rb +81 -0
  89. data/lib/hecks/bluebook/behaviour/value_object.rb +33 -0
  90. data/lib/hecks/bluebook/chapter.rb +68 -0
  91. data/lib/hecks/bluebook/command.rb +124 -0
  92. data/lib/hecks/bluebook/domain_port.rb +102 -0
  93. data/lib/hecks/bluebook/dsl/adapter_builder.rb +34 -0
  94. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +906 -0
  95. data/lib/hecks/bluebook/dsl/attribute_collector.rb +327 -0
  96. data/lib/hecks/bluebook/dsl/binding_proxy.rb +71 -0
  97. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +1018 -0
  98. data/lib/hecks/bluebook/dsl/command_builder.rb +626 -0
  99. data/lib/hecks/bluebook/dsl/const_shim.rb +81 -0
  100. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +79 -0
  101. data/lib/hecks/bluebook/dsl/entity_builder.rb +305 -0
  102. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +366 -0
  103. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +163 -0
  104. data/lib/hecks/bluebook/dsl/identity_declaration.rb +191 -0
  105. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +44 -0
  106. data/lib/hecks/bluebook/dsl/malformed.rb +7 -0
  107. data/lib/hecks/bluebook/dsl/policy_builder.rb +120 -0
  108. data/lib/hecks/bluebook/dsl/port_builder.rb +30 -0
  109. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +142 -0
  110. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +207 -0
  111. data/lib/hecks/bluebook/dsl/query_builder.rb +113 -0
  112. data/lib/hecks/bluebook/dsl/read_model_builder.rb +233 -0
  113. data/lib/hecks/bluebook/dsl/rule_reference.rb +172 -0
  114. data/lib/hecks/bluebook/dsl/translation_builder.rb +243 -0
  115. data/lib/hecks/bluebook/dsl/value_object_builder.rb +178 -0
  116. data/lib/hecks/bluebook/dsl/word_gate.rb +221 -0
  117. data/lib/hecks/bluebook/dsl/world_builder.rb +77 -0
  118. data/lib/hecks/bluebook/dsl.rb +45 -0
  119. data/lib/hecks/bluebook/entity.rb +103 -0
  120. data/lib/hecks/bluebook/expression/canonical_form.rb +55 -0
  121. data/lib/hecks/bluebook/expression/evaluator.rb +261 -0
  122. data/lib/hecks/bluebook/expression/projection.json +170 -0
  123. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +233 -0
  124. data/lib/hecks/bluebook/expression/resolver.rb +630 -0
  125. data/lib/hecks/bluebook/expression.rb +13 -0
  126. data/lib/hecks/bluebook/hexagon.rb +60 -0
  127. data/lib/hecks/bluebook/lifecycle.rb +42 -0
  128. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +54 -0
  129. data/lib/hecks/bluebook/meta_validator/judge.rb +559 -0
  130. data/lib/hecks/bluebook/meta_validator/plan.rb +332 -0
  131. data/lib/hecks/bluebook/meta_validator/port_judge.rb +47 -0
  132. data/lib/hecks/bluebook/meta_validator/readings.rb +350 -0
  133. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +312 -0
  134. data/lib/hecks/bluebook/meta_validator/shapes.rb +246 -0
  135. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +255 -0
  136. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +138 -0
  137. data/lib/hecks/bluebook/meta_validator/world_judge.rb +78 -0
  138. data/lib/hecks/bluebook/meta_validator.rb +452 -0
  139. data/lib/hecks/bluebook/model_check.rb +318 -0
  140. data/lib/hecks/bluebook/pattern_subset.rb +120 -0
  141. data/lib/hecks/bluebook/policy.rb +41 -0
  142. data/lib/hecks/bluebook/process_manager.rb +86 -0
  143. data/lib/hecks/bluebook/project_discovery.rb +30 -0
  144. data/lib/hecks/bluebook/project_loader.rb +40 -0
  145. data/lib/hecks/bluebook/project_register.rb +82 -0
  146. data/lib/hecks/bluebook/query.rb +61 -0
  147. data/lib/hecks/bluebook/read_model.rb +109 -0
  148. data/lib/hecks/bluebook/reference.rb +74 -0
  149. data/lib/hecks/bluebook/smoke_test.rb +166 -0
  150. data/lib/hecks/bluebook/synthesizer.rb +95 -0
  151. data/lib/hecks/bluebook/translation.rb +92 -0
  152. data/lib/hecks/bluebook/value_object.rb +58 -0
  153. data/lib/hecks/bluebook.rb +74 -0
  154. data/lib/hecks/codemod.rb +342 -0
  155. data/lib/hecks/construct.rb +71 -0
  156. data/lib/hecks/deploy/bluebook/deploy.bluebook +219 -0
  157. data/lib/hecks/deploy/bluebook/deploy.hecksagon +4 -0
  158. data/lib/hecks/deploy/oidc.json +18 -0
  159. data/lib/hecks/doc/reference.rb +389 -0
  160. data/lib/hecks/embryonaut_bluebook.rb +75 -0
  161. data/lib/hecks/facade/cli_door.rb +116 -0
  162. data/lib/hecks/facade/cli_runner.rb +190 -0
  163. data/lib/hecks/facade/command_request.rb +105 -0
  164. data/lib/hecks/facade/handle.rb +173 -0
  165. data/lib/hecks/facade/json_door.rb +154 -0
  166. data/lib/hecks/facade/surface/aggregate_door.rb +185 -0
  167. data/lib/hecks/facade/surface/chapter.rb +107 -0
  168. data/lib/hecks/facade/surface.rb +48 -0
  169. data/lib/hecks/facade.rb +44 -0
  170. data/lib/hecks/forms/app.rb +300 -0
  171. data/lib/hecks/forms/command_form_renderer.rb +113 -0
  172. data/lib/hecks/forms/examples/banking_console.bluebook +3 -0
  173. data/lib/hecks/forms/field_renderer.rb +170 -0
  174. data/lib/hecks/forms/field_shape.rb +232 -0
  175. data/lib/hecks/forms/html.rb +53 -0
  176. data/lib/hecks/forms/index_renderer.rb +35 -0
  177. data/lib/hecks/forms/page.rb +157 -0
  178. data/lib/hecks/forms/params.rb +132 -0
  179. data/lib/hecks/forms/query_form_renderer.rb +114 -0
  180. data/lib/hecks/forms/record_renderer.rb +115 -0
  181. data/lib/hecks/forms/record_table.rb +63 -0
  182. data/lib/hecks/forms/reference_options.rb +30 -0
  183. data/lib/hecks/forms/value_object_shape.rb +46 -0
  184. data/lib/hecks/forms.rb +54 -0
  185. data/lib/hecks/fqn.rb +94 -0
  186. data/lib/hecks/framework/bluebook/compliance.bluebook +1 -0
  187. data/lib/hecks/framework/bluebook/console_settings.bluebook +489 -0
  188. data/lib/hecks/framework/bluebook/framework.hecksagon +32 -0
  189. data/lib/hecks/framework/bluebook/governance.bluebook +130 -0
  190. data/lib/hecks/framework/bluebook/identity.bluebook +90 -0
  191. data/lib/hecks/framework/oidc.json +39 -0
  192. data/lib/hecks/framework.rb +90 -0
  193. data/lib/hecks/freezer.rb +67 -0
  194. data/lib/hecks/fuzzing/invalid_value_generator.rb +86 -0
  195. data/lib/hecks/fuzzing/isolated_boot.rb +92 -0
  196. data/lib/hecks/fuzzing/properties.rb +1146 -0
  197. data/lib/hecks/fuzzing/replay.rb +635 -0
  198. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +93 -0
  199. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +54 -0
  200. data/lib/hecks/fuzzing/sequence_generator/picker.rb +96 -0
  201. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +185 -0
  202. data/lib/hecks/fuzzing/sequence_generator.rb +130 -0
  203. data/lib/hecks/fuzzing/value_generator.rb +177 -0
  204. data/lib/hecks/fuzzing.rb +12 -0
  205. data/lib/hecks/grammar/evolve.rb +291 -0
  206. data/lib/hecks/grammar/expression.bluebook +417 -0
  207. data/lib/hecks/grammar/expression_operators.json +1152 -0
  208. data/lib/hecks/grammar/grammar.hecksagon +20 -0
  209. data/lib/hecks/grammar/oidc.json +69 -0
  210. data/lib/hecks/grammar/translation.bluebook +203 -0
  211. data/lib/hecks/grammar.rb +178 -0
  212. data/lib/hecks/ir.rb +126 -0
  213. data/lib/hecks/language/adapter.bluebook +116 -0
  214. data/lib/hecks/language/bluebook/aggregate.bluebook +581 -0
  215. data/lib/hecks/language/bluebook/attaches/paging.bluebook +76 -0
  216. data/lib/hecks/language/bluebook/bluebook.bluebook +256 -0
  217. data/lib/hecks/language/bluebook/bluebook.hecksagon +24 -0
  218. data/lib/hecks/language/bluebook/command.bluebook +437 -0
  219. data/lib/hecks/language/bluebook/entity.bluebook +316 -0
  220. data/lib/hecks/language/bluebook/policy.bluebook +172 -0
  221. data/lib/hecks/language/bluebook/process_manager.bluebook +264 -0
  222. data/lib/hecks/language/bluebook/projection.bluebook +267 -0
  223. data/lib/hecks/language/bluebook/query.bluebook +245 -0
  224. data/lib/hecks/language/bluebook/shape.bluebook +292 -0
  225. data/lib/hecks/language/bluebook/syntax.bluebook +447 -0
  226. data/lib/hecks/language/bluebook/vocabulary.bluebook +364 -0
  227. data/lib/hecks/language/hecksagon/adapter_binding.bluebook +51 -0
  228. data/lib/hecks/language/hecksagon/domain_port.bluebook +76 -0
  229. data/lib/hecks/language/hecksagon/hecksagon.bluebook +131 -0
  230. data/lib/hecks/language/hecksagon/port_operation.bluebook +102 -0
  231. data/lib/hecks/language/oidc.json +313 -0
  232. data/lib/hecks/language/port.bluebook +92 -0
  233. data/lib/hecks/language/translation/translation.bluebook +110 -0
  234. data/lib/hecks/language/translation/translation_aggregate.bluebook +267 -0
  235. data/lib/hecks/language/world/wiring.bluebook +62 -0
  236. data/lib/hecks/language/world/world.bluebook +84 -0
  237. data/lib/hecks/literal.rb +125 -0
  238. data/lib/hecks/naming.rb +121 -0
  239. data/lib/hecks/ports/access_control.port +4 -0
  240. data/lib/hecks/ports/access_control.rb +62 -0
  241. data/lib/hecks/ports/agent/answers.rb +104 -0
  242. data/lib/hecks/ports/agent.port +4 -0
  243. data/lib/hecks/ports/agent.rb +167 -0
  244. data/lib/hecks/ports/authentication.port +4 -0
  245. data/lib/hecks/ports/authentication.rb +50 -0
  246. data/lib/hecks/ports/authorization.port +4 -0
  247. data/lib/hecks/ports/authorization.rb +59 -0
  248. data/lib/hecks/ports/clock.port +4 -0
  249. data/lib/hecks/ports/clock.rb +62 -0
  250. data/lib/hecks/ports/extraction.port +4 -0
  251. data/lib/hecks/ports/extraction.rb +37 -0
  252. data/lib/hecks/ports/identity_assignment.port +4 -0
  253. data/lib/hecks/ports/identity_assignment.rb +45 -0
  254. data/lib/hecks/ports/identity_generation.port +4 -0
  255. data/lib/hecks/ports/identity_generation.rb +49 -0
  256. data/lib/hecks/ports/identity_resolution.port +4 -0
  257. data/lib/hecks/ports/identity_resolution.rb +40 -0
  258. data/lib/hecks/ports/loading.port +4 -0
  259. data/lib/hecks/ports/loading.rb +13 -0
  260. data/lib/hecks/ports/persistence/append_only.rb +102 -0
  261. data/lib/hecks/ports/persistence/binding_policy.rb +56 -0
  262. data/lib/hecks/ports/persistence/execution.rb +18 -0
  263. data/lib/hecks/ports/persistence/lineage.rb +277 -0
  264. data/lib/hecks/ports/persistence/null_saga_store.rb +25 -0
  265. data/lib/hecks/ports/persistence/remote_runtime.rb +42 -0
  266. data/lib/hecks/ports/persistence/repository_factory.rb +30 -0
  267. data/lib/hecks/ports/persistence.port +4 -0
  268. data/lib/hecks/ports/persistence.rb +39 -0
  269. data/lib/hecks/ports/projection.port +4 -0
  270. data/lib/hecks/ports/projection.rb +76 -0
  271. data/lib/hecks/ports/query/in_memory.rb +60 -0
  272. data/lib/hecks/ports/query/ordering.rb +41 -0
  273. data/lib/hecks/ports/query.rb +36 -0
  274. data/lib/hecks/ports.rb +27 -0
  275. data/lib/hecks/projections/diagrams.rb +413 -0
  276. data/lib/hecks/projections/ir.rb +18 -0
  277. data/lib/hecks/projections/model/deviations.rb +98 -0
  278. data/lib/hecks/projections/model.rb +145 -0
  279. data/lib/hecks/projections/oidc.rb +110 -0
  280. data/lib/hecks/projections/parser_table.rb +159 -0
  281. data/lib/hecks/projections/reference.rb +38 -0
  282. data/lib/hecks/projections/shape.rb +31 -0
  283. data/lib/hecks/projections/statements.rb +110 -0
  284. data/lib/hecks/projections/vocabulary.rb +100 -0
  285. data/lib/hecks/projections.rb +32 -0
  286. data/lib/hecks/projector/cli_projector.rb +437 -0
  287. data/lib/hecks/projector/docs_projector.rb +321 -0
  288. data/lib/hecks/projector/exporter.rb +141 -0
  289. data/lib/hecks/projector/ir_projector.rb +18 -0
  290. data/lib/hecks/projector/narrate_projector.rb +243 -0
  291. data/lib/hecks/projector/target.rb +97 -0
  292. data/lib/hecks/projector.rb +186 -0
  293. data/lib/hecks/query_ir.rb +392 -0
  294. data/lib/hecks/query_specification/common/authorization_spec.rb +9 -0
  295. data/lib/hecks/query_specification/common/comparators.rb +25 -0
  296. data/lib/hecks/query_specification/common/comparison.rb +174 -0
  297. data/lib/hecks/query_specification/common/cursor_spec.rb +9 -0
  298. data/lib/hecks/query_specification/common/dsl.rb +58 -0
  299. data/lib/hecks/query_specification/common/inspection_spec.rb +9 -0
  300. data/lib/hecks/query_specification/common/limit_spec.rb +9 -0
  301. data/lib/hecks/query_specification/common/null_policy.rb +80 -0
  302. data/lib/hecks/query_specification/common/null_semantics.rb +11 -0
  303. data/lib/hecks/query_specification/common/offset_spec.rb +9 -0
  304. data/lib/hecks/query_specification/common/options.rb +32 -0
  305. data/lib/hecks/query_specification/common/order_by.rb +9 -0
  306. data/lib/hecks/query_specification/common/specification.rb +10 -0
  307. data/lib/hecks/query_specification/common/where_clause.rb +9 -0
  308. data/lib/hecks/query_specification/field_path.rb +89 -0
  309. data/lib/hecks/query_specification/hop_path.rb +130 -0
  310. data/lib/hecks/query_specification/read_model/specification.rb +18 -0
  311. data/lib/hecks/query_specification.rb +14 -0
  312. data/lib/hecks/rendering.rb +48 -0
  313. data/lib/hecks/router/namespace_installer.rb +157 -0
  314. data/lib/hecks/router.rb +70 -0
  315. data/lib/hecks/runtime/caller.rb +50 -0
  316. data/lib/hecks/runtime/capability_graph.rb +44 -0
  317. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +124 -0
  318. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +248 -0
  319. data/lib/hecks/runtime/command_interpreter.rb +413 -0
  320. data/lib/hecks/runtime/command_rules/admissibility.rb +307 -0
  321. data/lib/hecks/runtime/command_rules/arithmetic.rb +254 -0
  322. data/lib/hecks/runtime/command_rules/authorization.rb +64 -0
  323. data/lib/hecks/runtime/command_rules/emission.rb +34 -0
  324. data/lib/hecks/runtime/command_rules/references.rb +181 -0
  325. data/lib/hecks/runtime/command_rules.rb +28 -0
  326. data/lib/hecks/runtime/dependency_planning.rb +231 -0
  327. data/lib/hecks/runtime/dispatcher.rb +286 -0
  328. data/lib/hecks/runtime/entity_element.rb +253 -0
  329. data/lib/hecks/runtime/entity_interpreter.rb +228 -0
  330. data/lib/hecks/runtime/era_check.rb +136 -0
  331. data/lib/hecks/runtime/era_guard/shape_diff.rb +128 -0
  332. data/lib/hecks/runtime/era_guard.rb +193 -0
  333. data/lib/hecks/runtime/era_tamper.rb +61 -0
  334. data/lib/hecks/runtime/errors.rb +100 -0
  335. data/lib/hecks/runtime/event.rb +51 -0
  336. data/lib/hecks/runtime/identity.rb +128 -0
  337. data/lib/hecks/runtime/instance.rb +120 -0
  338. data/lib/hecks/runtime/interpreting.rb +69 -0
  339. data/lib/hecks/runtime/loader.rb +143 -0
  340. data/lib/hecks/runtime/policy_interpreter.rb +366 -0
  341. data/lib/hecks/runtime/port_operation_interpreter.rb +210 -0
  342. data/lib/hecks/runtime/query_interpreter.rb +254 -0
  343. data/lib/hecks/runtime/reaction_invocation.rb +245 -0
  344. data/lib/hecks/runtime/read_model_interpreter.rb +266 -0
  345. data/lib/hecks/runtime/rebuild_sweep.rb +74 -0
  346. data/lib/hecks/runtime/reference_hop.rb +99 -0
  347. data/lib/hecks/runtime/refusal_wording.rb +124 -0
  348. data/lib/hecks/runtime/registry/saga_persistence.rb +70 -0
  349. data/lib/hecks/runtime/registry/verification.rb +158 -0
  350. data/lib/hecks/runtime/registry.rb +218 -0
  351. data/lib/hecks/runtime/remote_dispatcher.rb +143 -0
  352. data/lib/hecks/runtime/routing.rb +96 -0
  353. data/lib/hecks/runtime/saga_interpreter/correlation.rb +97 -0
  354. data/lib/hecks/runtime/saga_interpreter.rb +301 -0
  355. data/lib/hecks/runtime/storage_shape.rb +120 -0
  356. data/lib/hecks/runtime/tenant_check.rb +84 -0
  357. data/lib/hecks/runtime/tenant_scope.rb +55 -0
  358. data/lib/hecks/runtime/value/admission.rb +112 -0
  359. data/lib/hecks/runtime/value/coercion.rb +500 -0
  360. data/lib/hecks/runtime/value/invariant_violation.rb +5 -0
  361. data/lib/hecks/runtime/value.rb +125 -0
  362. data/lib/hecks/runtime.rb +95 -0
  363. data/lib/hecks/translation/audit/approval_digest.rb +31 -0
  364. data/lib/hecks/translation/audit/layer_one.rb +37 -0
  365. data/lib/hecks/translation/audit/layer_two.rb +77 -0
  366. data/lib/hecks/translation/audit/unfed_report.rb +42 -0
  367. data/lib/hecks/translation/audit.rb +70 -0
  368. data/lib/hecks/translation/reattest.rb +72 -0
  369. data/lib/hecks/translation/rule_compiler.rb +120 -0
  370. data/lib/hecks/translation/scaffold/differ.rb +183 -0
  371. data/lib/hecks/translation/scaffold/renderer.rb +41 -0
  372. data/lib/hecks/translation/scaffold/writer.rb +28 -0
  373. data/lib/hecks/translation/scaffold.rb +30 -0
  374. data/lib/hecks/translation.rb +11 -0
  375. data/lib/hecks/version.rb +16 -0
  376. data/lib/hecks/vocabulary.rb +213 -0
  377. data/lib/hecks.rb +118 -6
  378. metadata +401 -222
  379. data/bin/hecks +0 -7
  380. data/bin/hecks-package +0 -65
  381. data/bin/hecks_console +0 -12
  382. data/bin/hecks_serverless +0 -6
  383. data/lib/cli/build.rb +0 -14
  384. data/lib/cli/command_runner.rb +0 -28
  385. data/lib/cli/console.rb +0 -10
  386. data/lib/cli/generate.rb +0 -37
  387. data/lib/cli/hecks-cli.rb +0 -27
  388. data/lib/cli/test.rb +0 -57
  389. data/lib/console/commands.rb +0 -8
  390. data/lib/console/hecks-console.rb +0 -1
  391. data/lib/packager/README.md +0 -0
  392. data/lib/packager/app_runner.rb +0 -21
  393. data/lib/packager/args.rb +0 -26
  394. data/lib/packager/compatibility/fixnum.rb +0 -6
  395. data/lib/packager/hecks.rb +0 -39
  396. data/lib/packager/query_runner.rb +0 -21
  397. data/lib/packager/resources/Dockerfile +0 -11
  398. data/lib/packager/resources/app_binary +0 -7
  399. data/lib/packager/resources/bundle_config +0 -3
  400. data/lib/packager/resources/traveling-ruby-20150715-2.2.2-linux-x86_64.tar.gz +0 -0
  401. data/lib/packager/resources/traveling-ruby-20150715-2.2.2-osx.tar.gz +0 -0
  402. data/lib/serverless/Domain +0 -32
  403. data/lib/serverless/cli.rb +0 -75
  404. data/lib/serverless/resources/command_name.js +0 -5
  405. data/lib/serverless/resources/environment.js +0 -7
  406. data/lib/serverless/resources/handler.js.tt +0 -28
  407. data/lib/serverless/resources/run_binary.js +0 -22
  408. data/lib/serverless/resources/serverless.yml +0 -20
@@ -0,0 +1,267 @@
1
+ Hecks.bluebook "Translation" do
2
+ aggregate "TranslationAggregate" do
3
+ description "One aggregate's own move from one pinned era to the next — a table of rules, or a deliberate acknowledgment that a rule is still unresolved."
4
+
5
+ belongs_to Translation, as: :translation_ref
6
+
7
+ identified_by TranslationAggregateName, as: :name
8
+
9
+ # `TranslationAggregateName` — a DEDICATED type for the identity
10
+ # field, not the shared `TranslationName` every non-identity field
11
+ # below uses. Found live, not by inspection: reusing one value-
12
+ # object type for BOTH an aggregate's own identity field and a
13
+ # sibling non-identity field on the same aggregate broke the
14
+ # single-field-VO identity-unwrap path — the stored `id` came back
15
+ # as a stringified Hash (`"{:value=>\"Account\"}"`) instead of the
16
+ # bare scalar (`"Account"`), so a self-reference to this aggregate
17
+ # could never find the record `Declare` had just created. Reusing
18
+ # the SAME type across MULTIPLE identity fields (Translation's own
19
+ # `TranslationEraName`, for both `from` and `to`) is unaffected —
20
+ # confirmed directly, not assumed — so the real trigger is identity
21
+ # + non-identity sharing, not reuse itself. A genuine framework bug,
22
+ # out of scope to fix here; every other self-hosted construct in
23
+ # this language already avoids it by convention (a dedicated type
24
+ # per identity field), which this now matches too.
25
+ attribute :was, TranslationName, optional: true
26
+
27
+ value_object "TranslationAggregateName" do
28
+ attribute :value, String
29
+ invariant("a translation aggregate names itself") { !value.to_s.empty? }
30
+ end
31
+
32
+ # The parent arrives as an ordinary value fact at declaration time. Its
33
+ # fields mirror Translation's compound identity, while `translation_ref`
34
+ # above retains the resulting canonical identity as a structural edge.
35
+ value_object "TranslationIdentity" do
36
+ attribute :domain, TranslationDomainName
37
+ attribute :from, TranslationEraName
38
+ attribute :to, TranslationEraName
39
+ end
40
+
41
+ attribute :renames, list_of(TranslationRename)
42
+ attribute :moves, list_of(TranslationMove)
43
+ attribute :converts, list_of(TranslationConvert)
44
+ attribute :drops, list_of(TranslationName)
45
+ attribute :retypes, list_of(TranslationRetype)
46
+ attribute :computes, list_of(TranslationCompute)
47
+ attribute :rekeys, list_of(TranslationRekey)
48
+ attribute :backfills, list_of(TranslationBackfill)
49
+
50
+ # Field names throughout mirror lib/hecks/bluebook/translation.rb
51
+ # EXACTLY (`Struct.new(:from, :to)` etc.) — the real Ruby records
52
+ # TranslationJudge dispatches from, not the builder's own local
53
+ # parameter names (`move(old_path, to:)`'s `old_path` becomes the
54
+ # struct's own `from`).
55
+ value_object "TranslationRename" do
56
+ attribute :from, TranslationName
57
+ attribute :to, TranslationName
58
+ end
59
+
60
+ value_object "TranslationMove" do
61
+ attribute :from, TranslationName
62
+ attribute :to, TranslationName
63
+ end
64
+
65
+ value_object "TranslationConvert" do
66
+ attribute :from, TranslationName
67
+ attribute :to, TranslationName
68
+ attribute :values, TranslationLiteralText
69
+ end
70
+
71
+ value_object "TranslationRetype" do
72
+ attribute :from, TranslationName
73
+ attribute :to, TranslationName
74
+ end
75
+
76
+ value_object "TranslationCompute" do
77
+ attribute :from, TranslationName
78
+ attribute :to, TranslationName
79
+ attribute :sql, TranslationLiteralText
80
+ end
81
+
82
+ value_object "TranslationRekey" do
83
+ attribute :sql, TranslationLiteralText
84
+ end
85
+
86
+ value_object "TranslationBackfill" do
87
+ attribute :name, TranslationName
88
+ attribute :default, TranslationLiteralText
89
+ end
90
+
91
+ command "Declare" do
92
+ role "Deployment"
93
+ goal "Open one aggregate's own move from one pinned era to the next"
94
+
95
+ attribute :translation_ref, TranslationIdentity
96
+ attribute :name, TranslationAggregateName
97
+ attribute :was, TranslationName, optional: true
98
+
99
+ sets :translation_ref
100
+ sets :name
101
+ sets :was
102
+
103
+ emits "TranslationAggregateDeclared"
104
+ end
105
+
106
+ command "AddRename" do
107
+ role "Deployment"
108
+ goal "Move data at an old field name to its new one, unchanged"
109
+
110
+ attribute :from, TranslationName
111
+ attribute :to, TranslationName
112
+
113
+ sets :renames, append: { from: :from, to: :to }
114
+
115
+ emits "TranslationRenameAdded"
116
+ end
117
+
118
+ command "AddMove" do
119
+ role "Deployment"
120
+ goal "Move data at an old path to a new one, unchanged"
121
+
122
+ attribute :from, TranslationName
123
+ attribute :to, TranslationName
124
+
125
+ sets :moves, append: { from: :from, to: :to }
126
+
127
+ emits "TranslationMoveAdded"
128
+ end
129
+
130
+ command "AddConvert" do
131
+ role "Deployment"
132
+ goal "Move data at an old path to a new one, translating its own values through an exhaustive table"
133
+
134
+ attribute :from, TranslationName
135
+ attribute :to, TranslationName
136
+ attribute :values, TranslationLiteralText
137
+
138
+ sets :converts, append: { from: :from, to: :to, values: :values }
139
+
140
+ emits "TranslationConvertAdded"
141
+ end
142
+
143
+ command "AddDrop" do
144
+ role "Deployment"
145
+ goal "Acknowledge, deliberately, that a field's own data does not survive"
146
+
147
+ attribute :value, TranslationName
148
+
149
+ sets :drops, append: { value: :value }
150
+
151
+ emits "TranslationDropAdded"
152
+ end
153
+
154
+ command "AddRetype" do
155
+ role "Deployment"
156
+ goal "Declare that an old type name and a new one mean the same shape"
157
+
158
+ attribute :from, TranslationName
159
+ attribute :to, TranslationName
160
+
161
+ sets :retypes, append: { from: :from, to: :to }
162
+
163
+ emits "TranslationRetypeAdded"
164
+ end
165
+
166
+ command "AddCompute" do
167
+ role "Deployment"
168
+ goal "Move data at an old path to a new one, transformed by a real SQL expression"
169
+
170
+ attribute :from, TranslationName
171
+ attribute :to, TranslationName
172
+ attribute :sql, TranslationLiteralText
173
+
174
+ sets :computes, append: { from: :from, to: :to, sql: :sql }
175
+
176
+ emits "TranslationComputeAdded"
177
+ end
178
+
179
+ command "AddRekey" do
180
+ role "Deployment"
181
+ goal "Recompute this aggregate's own identity by a real SQL expression"
182
+
183
+ attribute :sql, TranslationLiteralText
184
+
185
+ sets :rekeys, append: { sql: :sql }
186
+
187
+ emits "TranslationRekeyAdded"
188
+ end
189
+
190
+ command "AddBackfill" do
191
+ role "Deployment"
192
+ goal "Name a newly required attribute and the value an existing record reads until written for real"
193
+
194
+ attribute :name, TranslationName
195
+ attribute :default, TranslationLiteralText
196
+
197
+ sets :backfills, append: { name: :name, default: :default }
198
+
199
+ emits "TranslationBackfillAdded"
200
+ end
201
+
202
+ # HOW TRANSLATIONAGGREGATE IS SPELLED. These rows live with the concept
203
+ # they describe; SyntaxBoot discovers every aggregate-local seed table.
204
+ # Contexts represented here: TranslationAggregate.
205
+ value_object "KeywordSeed" do
206
+ attribute :word, String
207
+ attribute :context, String, admits: "Syntax::Context"
208
+ attribute :body, String, admits: "Syntax::Body"
209
+ attribute :inner, String
210
+ attribute :opens, String
211
+ attribute :fills, String
212
+ attribute :status, String, default: "admitted", admits: "Syntax::Status"
213
+ attribute :was, String
214
+ attribute :resolves_via, String, default: "", admits: "Syntax::ResolutionScope"
215
+ attribute :disambiguator, String, default: "", admits: "Syntax::Disambiguator"
216
+
217
+ member word: "rename", context: "TranslationAggregate", body: "none", inner: "", opens: "", fills: "renames", calls: "rename_impl"
218
+ member word: "move", context: "TranslationAggregate", body: "none", inner: "", opens: "", fills: "moves", calls: "move_impl"
219
+ member word: "convert", context: "TranslationAggregate", body: "none", inner: "", opens: "", fills: "converts", calls: "convert_impl"
220
+ member word: "drop", context: "TranslationAggregate", body: "none", inner: "", opens: "", fills: "drops"
221
+ member word: "retype", context: "TranslationAggregate", body: "none", inner: "", opens: "", fills: "retypes", calls: "retype_impl"
222
+ member word: "compute", context: "TranslationAggregate", body: "none", inner: "", opens: "", fills: "computes", calls: "compute_impl"
223
+ member word: "rekey", context: "TranslationAggregate", body: "none", inner: "", opens: "", fills: "rekeys", calls: "rekey_impl"
224
+ member word: "backfill", context: "TranslationAggregate", body: "none", inner: "", opens: "", fills: "backfills", calls: "backfill_impl"
225
+ member word: "unresolved", context: "TranslationAggregate", body: "none", inner: "", opens: "", fills: "", calls: "unresolved_impl"
226
+
227
+ end
228
+
229
+ value_object "ArgumentSeed" do
230
+ attribute :keyword, String
231
+ attribute :context, String, admits: "Syntax::Context"
232
+ attribute :at, String
233
+ attribute :named, String
234
+ attribute :kind, String, admits: "Syntax::ArgumentKind"
235
+ attribute :required, String
236
+ attribute :fills, String
237
+ attribute :selects, String
238
+ attribute :pair_key_fills, String
239
+ attribute :pair_value_fills, String
240
+ attribute :pairs_shape, String, admits: "Syntax::PairsShape"
241
+ attribute :status, String, default: "admitted", admits: "Syntax::Status"
242
+ attribute :variadic, String
243
+ attribute :coerce, String
244
+ attribute :blank_message, String
245
+
246
+ member keyword: "rename", context: "TranslationAggregate", at: "1", named: "", kind: "symbol", required: "true", fills: "old_name"
247
+ member keyword: "rename", context: "TranslationAggregate", at: "", named: "to", kind: "symbol", required: "true", fills: "to"
248
+ member keyword: "move", context: "TranslationAggregate", at: "1", named: "", kind: "text", required: "true", fills: "old_path"
249
+ member keyword: "move", context: "TranslationAggregate", at: "", named: "to", kind: "text", required: "true", fills: "to"
250
+ member keyword: "convert", context: "TranslationAggregate", at: "1", named: "", kind: "text", required: "true", fills: "old_path"
251
+ member keyword: "convert", context: "TranslationAggregate", at: "", named: "to", kind: "text", required: "true", fills: "to"
252
+ member keyword: "convert", context: "TranslationAggregate", at: "", named: "values", kind: "literal", required: "true", fills: "values"
253
+ member keyword: "drop", context: "TranslationAggregate", at: "1", named: "", kind: "symbol", required: "true", fills: "name", blank_message: "a drop needs a name"
254
+ member keyword: "retype", context: "TranslationAggregate", at: "1", named: "", kind: "text", required: "true", fills: "old_type"
255
+ member keyword: "retype", context: "TranslationAggregate", at: "", named: "to", kind: "text", required: "true", fills: "to"
256
+ member keyword: "compute", context: "TranslationAggregate", at: "1", named: "", kind: "text", required: "true", fills: "old_path"
257
+ member keyword: "compute", context: "TranslationAggregate", at: "", named: "to", kind: "text", required: "true", fills: "to"
258
+ member keyword: "compute", context: "TranslationAggregate", at: "", named: "sql", kind: "text", required: "true", fills: "sql"
259
+ member keyword: "rekey", context: "TranslationAggregate", at: "", named: "sql", kind: "text", required: "true", fills: "sql"
260
+ member keyword: "backfill", context: "TranslationAggregate", at: "1", named: "", kind: "symbol", required: "true", fills: "name"
261
+ member keyword: "backfill", context: "TranslationAggregate", at: "", named: "default", kind: "literal", required: "true", fills: "default"
262
+ member keyword: "unresolved", context: "TranslationAggregate", at: "1", named: "", kind: "symbol", required: "true", fills: "name"
263
+ member keyword: "unresolved", context: "TranslationAggregate", at: "", named: "candidates", kind: "list", required: "false", fills: "candidates"
264
+
265
+ end
266
+ end
267
+ end
@@ -0,0 +1,62 @@
1
+ Hecks.bluebook "World" do
2
+ aggregate "Wiring" do
3
+ description "One adapter binding inside a world — the verb it answers, the adapter it names, and the values that adapter needs. Its own root because a world's settings are an open map, which no value object can hold."
4
+
5
+ # `as:` is required here — Wiring ALSO holds an ordinary `world` text
6
+ # attribute below (the same string, but a value rather than a reference),
7
+ # so the bare reference name would collide with it.
8
+ belongs_to World, as: :world_ref
9
+
10
+ # THE WORLD IT IS IN, AND THE VERB IT ANSWERS. One world binds one adapter per
11
+ # verb — that is what a binding IS — so the verb names the wiring, and the
12
+ # world says whose.
13
+ identified_by :world, :verb
14
+
15
+ attribute :world, WiringText
16
+ attribute :verb, WiringText
17
+ attribute :adapter, WiringText
18
+ attribute :settings, list_of(Setting)
19
+
20
+ value_object "WiringText" do
21
+ attribute :value, String
22
+ end
23
+
24
+ value_object "Setting" do
25
+ attribute :key, String
26
+ attribute :value, String
27
+ end
28
+
29
+ command "Declare" do
30
+ role "Deployment"
31
+ goal "Bind one adapter in a world"
32
+
33
+ attribute :world_ref, WorldName
34
+ attribute :world, WiringText
35
+ attribute :verb, WiringText
36
+ attribute :adapter, WiringText
37
+
38
+ given("a wiring names its verb") { !verb.value.to_s.empty? }
39
+
40
+ sets :world_ref
41
+ sets :world
42
+ sets :verb
43
+ sets :adapter
44
+
45
+ emits "WiringDeclared"
46
+ end
47
+
48
+ command "Set" do
49
+ role "Deployment"
50
+ goal "Give one value the bound adapter needs"
51
+
52
+ attribute :key, WiringText
53
+ attribute :value, WiringText
54
+
55
+ given("a setting names its key") { !key.value.to_s.empty? }
56
+
57
+ sets :settings, append: { key: :key, value: :value }
58
+
59
+ emits "SettingGiven"
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,84 @@
1
+ Hecks.bluebook "World" do
2
+ aggregate "World" do
3
+ description "One .world — the per-deployment values a domain's adapters need. A sibling artifact to the bluebook, not part of it : the same domain runs in many worlds."
4
+
5
+ identified_by WorldName, as: :domain
6
+
7
+ attribute :realm, Realm
8
+ attribute :latest, Latest
9
+
10
+ value_object "WorldName" do
11
+ attribute :value, String
12
+ invariant("a world names its domain") { !value.to_s.empty? }
13
+ end
14
+
15
+ value_object "Realm" do
16
+ attribute :value, String
17
+ invariant("a realm says something") { !value.to_s.empty? }
18
+ end
19
+
20
+ value_object "Latest" do
21
+ attribute :value, String
22
+ invariant("a latest version says something") { !value.to_s.empty? }
23
+ end
24
+
25
+ command "Declare" do
26
+ role "Deployment"
27
+ goal "Open a world for a domain"
28
+
29
+ attribute :domain, WorldName
30
+ attribute :realm, Realm, optional: true
31
+ attribute :latest, Latest, optional: true
32
+
33
+ sets :domain
34
+ sets :realm
35
+ sets :latest
36
+
37
+ emits "WorldDeclared"
38
+ end
39
+
40
+ # HOW WORLD IS SPELLED. These rows live with the concept
41
+ # they describe; SyntaxBoot discovers every aggregate-local seed table.
42
+ # Contexts represented here: File, World.
43
+ value_object "KeywordSeed" do
44
+ attribute :word, String
45
+ attribute :context, String, admits: "Syntax::Context"
46
+ attribute :body, String, admits: "Syntax::Body"
47
+ attribute :inner, String
48
+ attribute :opens, String
49
+ attribute :fills, String
50
+ attribute :status, String, default: "admitted", admits: "Syntax::Status"
51
+ attribute :was, String
52
+ attribute :resolves_via, String, default: "", admits: "Syntax::ResolutionScope"
53
+ attribute :disambiguator, String, default: "", admits: "Syntax::Disambiguator"
54
+
55
+ member word: "world", context: "File", body: "keywords", inner: "World", opens: "World", fills: ""
56
+ member word: "realm", context: "World", body: "none", inner: "", opens: "", fills: "realm", calls: "realm_impl"
57
+ member word: "latest", context: "World", body: "none", inner: "", opens: "", fills: "latest", calls: "latest_impl"
58
+
59
+ end
60
+
61
+ value_object "ArgumentSeed" do
62
+ attribute :keyword, String
63
+ attribute :context, String, admits: "Syntax::Context"
64
+ attribute :at, String
65
+ attribute :named, String
66
+ attribute :kind, String, admits: "Syntax::ArgumentKind"
67
+ attribute :required, String
68
+ attribute :fills, String
69
+ attribute :selects, String
70
+ attribute :pair_key_fills, String
71
+ attribute :pair_value_fills, String
72
+ attribute :pairs_shape, String, admits: "Syntax::PairsShape"
73
+ attribute :status, String, default: "admitted", admits: "Syntax::Status"
74
+ attribute :variadic, String
75
+ attribute :coerce, String
76
+ attribute :blank_message, String
77
+
78
+ member keyword: "world", context: "File", at: "1", named: "", kind: "text", required: "true", fills: "domain"
79
+ member keyword: "realm", context: "World", at: "1", named: "", kind: "text", required: "true", fills: "realm"
80
+ member keyword: "latest", context: "World", at: "1", named: "", kind: "text", required: "true", fills: "latest"
81
+
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,125 @@
1
+ module Hecks
2
+ # THE ONE SPELLING FOR A CAPTURED RUBY LITERAL ON THE WIRE.
3
+ #
4
+ # Several `to_h` fields hold a value the author wrote in a bluebook —
5
+ # `where(status: { eq: "open" })`, `then_set append: { direction: { value:
6
+ # "credit" } }`, `dispatch ... with: { number: :source }`, an attribute's
7
+ # `default:` — as TEXT, because the export has to stand on its own and a
8
+ # String field cannot say "this one was a Symbol". Every such field is the
9
+ # same question and now gets the same answer.
10
+ #
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
14
+ # and everything else `to_s`. So `:amount` crossed as "amount" in a mutation
15
+ # and ":amount" in a saga binding — the same value, two spellings, and each
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
18
+ # `{:value=>"credit"}` and 3.4 writes `{value: "credit"}` for the identical
19
+ # Hash, so the wire format was silently pinned to an interpreter version.
20
+ #
21
+ # SELF-DESCRIBING IS THE RULE, stated once here rather than inherited from
22
+ # whatever `inspect` happens to do: a symbol wears its colon, a string wears
23
+ # its quotes, a hash wears `{key: value}` braces, a list wears its brackets,
24
+ # and a number, a boolean and nil are bare. `read` is the exact inverse, and
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,
27
+ # append: { knights: state(:knights) }`. A bare Symbol in a mutation
28
+ # source always names a command ARGUMENT (and imports it as one when
29
+ # the target's own field carries the same name — `CommandBuilder
30
+ # #resolve_append_fields!`); before this there was no way to say "the
31
+ # value this field already holds", so a command could not snapshot its
32
+ # own record — chess's threefold repetition needs exactly that, a
33
+ # position copied off the board every ply. Spelled `state(:name)` on
34
+ # the wire, read back by `Literal.read`; classified `kind: "state"` in
35
+ # a set's own source (`Behaviour::Mutation#classified_source`).
36
+ StateRef = Struct.new(:name) do
37
+ def to_s = "state(:#{name})"
38
+ end
39
+
40
+ module Literal
41
+ module_function
42
+
43
+ def render(value)
44
+ case value
45
+ when nil then "nil"
46
+ when StateRef then value.to_s
47
+ when true, false then value.to_s
48
+ when Symbol then ":#{value}"
49
+ when Integer, Float then value.to_s
50
+ when String then quote(value)
51
+ when Hash then "{#{value.map { |key, held| "#{key}: #{render(held)}" }.join(', ')}}"
52
+ when Array then "[#{value.map { |held| render(held) }.join(', ')}]"
53
+ else
54
+ raise ArgumentError, "#{value.class} has no pinned literal spelling — teach Literal.render one " \
55
+ "rather than letting #to_s decide it"
56
+ end
57
+ end
58
+
59
+ # Read one back. Tolerant of a bare word on purpose: the language stores a
60
+ # closed set's members and a few hand-written fields as plain text that was
61
+ # never rendered, and those must stay the strings they are.
62
+ def read(text)
63
+ raw = text.to_s.strip
64
+ return nil if raw.empty? || raw == "nil"
65
+ return true if raw == "true"
66
+ return false if raw == "false"
67
+ return raw.to_i if raw.match?(/\A-?\d+\z/)
68
+ return raw.to_f if raw.match?(/\A-?\d+\.\d+\z/)
69
+ return raw[1..].to_sym if raw.start_with?(":")
70
+ return StateRef.new(raw[7..-2].to_sym) if raw.match?(/\Astate\(:[A-Za-z_][A-Za-z0-9_]*\)\z/)
71
+ return unquote(raw) if quoted?(raw)
72
+ return read_hash(raw) if raw.start_with?("{") && raw.end_with?("}")
73
+ return read_array(raw) if raw.start_with?("[") && raw.end_with?("]")
74
+
75
+ raw
76
+ end
77
+
78
+ ESCAPED = { '"' => '\\"', "\\" => "\\\\" }.freeze
79
+
80
+ def quote(text) = "\"#{text.gsub(/["\\]/) { |char| ESCAPED[char] }}\""
81
+
82
+ def quoted?(raw) = raw.length >= 2 && raw.start_with?('"') && raw.end_with?('"')
83
+
84
+ def unquote(raw) = raw[1..-2].gsub(/\\(.)/) { ::Regexp.last_match(1) }
85
+
86
+ def read_hash(raw)
87
+ split_items(raw[1..-2]).to_h do |item|
88
+ key, _, held = item.partition(":")
89
+ [key.strip.to_sym, read(held)]
90
+ end
91
+ end
92
+
93
+ def read_array(raw) = split_items(raw[1..-2]).map { |item| read(item) }
94
+
95
+ # Split on the commas that are actually SEPARATORS — never one inside a
96
+ # quoted string or a nested brace/bracket. Scanned rather than
97
+ # `String#split(", ")`, which tore `"a, b"` in half and lost the second
98
+ # field of anything nested.
99
+ def split_items(body)
100
+ items = []
101
+ current = +""
102
+ depth = 0
103
+ quoting = false
104
+ escaping = false
105
+
106
+ body.each_char do |char|
107
+ current << char
108
+ next (escaping = false) if escaping
109
+ next (escaping = true) if quoting && char == "\\"
110
+ next (quoting = !quoting) if char == '"'
111
+ next if quoting
112
+
113
+ depth += 1 if "{[".include?(char)
114
+ depth -= 1 if "}]".include?(char)
115
+ next unless char == "," && depth.zero?
116
+
117
+ current.chop!
118
+ items << current
119
+ current = +""
120
+ end
121
+ items << current
122
+ items.map(&:strip).reject(&:empty?)
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,121 @@
1
+ module Hecks
2
+ module Naming
3
+ # WHAT SEPARATES THE PARTS OF A DERIVED IDENTITY.
4
+ #
5
+ # An identity of several parts is their JOIN, and the join has to be spelled the
6
+ # same everywhere or two readers name two different records off one declaration.
7
+ # It was spelled three ways at once here — "::" for an aggregate under a
8
+ # chapter, "." for everything under an aggregate, "#" for the three that keyed
9
+ # off position — and each was written where it happened to be needed. Once the
10
+ # runtime derived the same identity, the runtime made a FOURTH, and a reference
11
+ # that resolved by string comparison found nothing.
12
+ IDENTITY_JOIN = ":".freeze
13
+
14
+ module_function
15
+
16
+ # The parts of an identity, joined in declaration order.
17
+ def identity(parts) = Array(parts).join(IDENTITY_JOIN)
18
+
19
+ def demodulise(type)
20
+ type.to_s.split("::").last.to_s
21
+ end
22
+
23
+ # snake_case -> PascalCase. The name a synthesised closed-set value object
24
+ # takes when an attribute declares one inline. The derivation is part of
25
+ # the IR contract: the same bluebook must always produce the same name.
26
+ def pascal(text)
27
+ text.to_s.split("_").map { |part| part.sub(/\A(.)/) { Regexp.last_match(1).upcase } }.join
28
+ end
29
+
30
+ def snake(text)
31
+ text.to_s
32
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
33
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
34
+ .downcase
35
+ end
36
+
37
+ # The name a COLLECTION of something takes.
38
+ #
39
+ # There were two of these and one was wrong. A read model's gathered heads
40
+ # derived their name with a bare `"#{snake(target)}s"`, so the meta-domain's
41
+ # own whole-bluebook read model handed back `querys`, `entitys`, `policys`
42
+ # and `dispatchs` — and every check was green, because the checks compared
43
+ # the wrong rule against itself. Agreement is not correctness; it never was.
44
+ #
45
+ # So: one pluraliser, three rules, and every collection name flows through it.
46
+ def plural(text)
47
+ word = text.to_s
48
+ return "#{word[0..-2]}ies" if word.match?(/[^aeiou]y\z/)
49
+ return "#{word}es" if word.match?(/(s|x|z|ch|sh)\z/)
50
+
51
+ "#{word}s"
52
+ end
53
+
54
+ # `has_many`'s undo — the plural WRITTEN, back to the singular the target
55
+ # aggregate is actually named. Deliberately the crude half of a pair: `plural`
56
+ # above earns its precision (three suffix rules) because getting a COLLECTION
57
+ # name wrong reads as a typo forever ; this only ever recovers a name someone
58
+ # already wrote as a real aggregate, so "ies -> y, trailing s dropped" is the
59
+ # whole rule — enough for `has_many Invoices` to resolve to the aggregate
60
+ # actually named Invoice.
61
+ def singularize(text)
62
+ word = text.to_s
63
+ return "#{word[0..-4]}y" if word.length > 3 && word.end_with?("ies")
64
+ return word[0..-2] if word.length > 1 && word.end_with?("s")
65
+
66
+ word
67
+ end
68
+
69
+ def reference_key(type)
70
+ snake(demodulise(type)).to_sym
71
+ end
72
+
73
+ def split_dotted(dotted)
74
+ first, second = dotted.to_s.split(".", 2)
75
+ [first.to_s, second.to_s]
76
+ end
77
+
78
+ def qualifier(dotted)
79
+ text = dotted.to_s
80
+ text.include?(".") ? text.split(".", 2).first : nil
81
+ end
82
+
83
+ def unqualified(dotted)
84
+ text = dotted.to_s
85
+ text.include?(".") ? text.split(".", 2).last : text
86
+ end
87
+
88
+ def split_verb(verb)
89
+ path, command = verb.to_s.split(".", 2)
90
+ domain, aggregate = path.to_s.split("::", 2)
91
+ return nil unless domain && aggregate && command
92
+
93
+ [domain, aggregate, command]
94
+ end
95
+
96
+ # `trigger Account::Debit` / `dispatch Account::Debit` — a bare
97
+ # CONSTANT reference (`ConstShim`'s own `ScopedConstant`, S0b), not
98
+ # text (ADR 0025, "events and reactions" — command references become
99
+ # first-class). Ruby's `::` joins EVERY segment the same way a
100
+ # constant path always does, but a command's own `hecks_fqn` joins
101
+ # its aggregate with `.` (`Construct#hecks_separator`'s default,
102
+ # only an AGGREGATE overrides it to `::`) — so only the LAST `::`
103
+ # becomes a `.`; everything before it (the chapter, when a domain is
104
+ # spelled at all: `Banking::Account::Debit`) stays `::`-joined.
105
+ #
106
+ # A STRING PASSES THROUGH UNCHANGED, on purpose — legacy era text
107
+ # (S0a's own shadow-parsed spelling) already mixes `::` (domain) and
108
+ # `.` (command) correctly on its own, e.g. `"Banking::Account.Debit"`,
109
+ # and re-splitting that by content rather than by TYPE would corrupt
110
+ # it (its own last `::` sits between the domain and the aggregate,
111
+ # not the aggregate and the command). Only an actual constant object
112
+ # — never seen holding a `.` of its own — needs the rewrite at all.
113
+ def command_ref(value)
114
+ return value.to_s if value.is_a?(::String) || value.is_a?(::Symbol)
115
+
116
+ text = value.to_s
117
+ path, _, command = text.rpartition("::")
118
+ path.empty? ? text : "#{path}.#{command}"
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,4 @@
1
+ Hecks.port "access_control" do
2
+ verb "granted_by"
3
+ signal :reply
4
+ end