hecks 0.2.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (419) 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 +427 -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 +101 -0
  12. data/lib/hecks/adapters/driven/heki/journal.rb +56 -0
  13. data/lib/hecks/adapters/driven/heki/saga_store.rb +99 -0
  14. data/lib/hecks/adapters/driven/heki/snapshot.rb +65 -0
  15. data/lib/hecks/adapters/driven/heki.adapter +4 -0
  16. data/lib/hecks/adapters/driven/heki.rb +179 -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 +145 -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 +88 -0
  28. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +207 -0
  29. data/lib/hecks/adapters/driven/postgres.adapter +5 -0
  30. data/lib/hecks/adapters/driven/postgres.rb +438 -0
  31. data/lib/hecks/adapters/driven/postgres_era.adapter +17 -0
  32. data/lib/hecks/adapters/driven/prism.adapter +3 -0
  33. data/lib/hecks/adapters/driven/prism.rb +80 -0
  34. data/lib/hecks/adapters/driven/secure_random_identity.adapter +3 -0
  35. data/lib/hecks/adapters/driven/secure_random_identity.rb +14 -0
  36. data/lib/hecks/adapters/driven/sql_query_builder.rb +221 -0
  37. data/lib/hecks/adapters/driven/sqlite/codec.rb +80 -0
  38. data/lib/hecks/adapters/driven/sqlite/projection.rb +172 -0
  39. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +192 -0
  40. data/lib/hecks/adapters/driven/sqlite.adapter +9 -0
  41. data/lib/hecks/adapters/driven/sqlite.rb +306 -0
  42. data/lib/hecks/adapters/driven/system_clock.adapter +3 -0
  43. data/lib/hecks/adapters/driven/system_clock.rb +14 -0
  44. data/lib/hecks/adapters/driven.rb +54 -0
  45. data/lib/hecks/adapters.rb +6 -0
  46. data/lib/hecks/behaviors/dsl.rb +125 -0
  47. data/lib/hecks/behaviors/expectations.rb +340 -0
  48. data/lib/hecks/behaviors/ir.rb +31 -0
  49. data/lib/hecks/behaviors/rspec.rb +42 -0
  50. data/lib/hecks/behaviors/runner.rb +96 -0
  51. data/lib/hecks/behaviors.rb +25 -0
  52. data/lib/hecks/bluebook/aggregate.rb +108 -0
  53. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +134 -0
  54. data/lib/hecks/bluebook/assembly/build.rb +48 -0
  55. data/lib/hecks/bluebook/assembly/contract.rb +112 -0
  56. data/lib/hecks/bluebook/assembly/contracts.rb +438 -0
  57. data/lib/hecks/bluebook/assembly/marks.rb +228 -0
  58. data/lib/hecks/bluebook/assembly/specializer.rb +70 -0
  59. data/lib/hecks/bluebook/assembly.rb +91 -0
  60. data/lib/hecks/bluebook/attribute.rb +96 -0
  61. data/lib/hecks/bluebook/behaviour/aggregate.rb +83 -0
  62. data/lib/hecks/bluebook/behaviour/attribute.rb +27 -0
  63. data/lib/hecks/bluebook/behaviour/chapter.rb +72 -0
  64. data/lib/hecks/bluebook/behaviour/command.rb +116 -0
  65. data/lib/hecks/bluebook/behaviour/domain_port.rb +25 -0
  66. data/lib/hecks/bluebook/behaviour/entity.rb +59 -0
  67. data/lib/hecks/bluebook/behaviour/hexagon.rb +50 -0
  68. data/lib/hecks/bluebook/behaviour/lifecycle.rb +68 -0
  69. data/lib/hecks/bluebook/behaviour/policy.rb +52 -0
  70. data/lib/hecks/bluebook/behaviour/process_manager.rb +51 -0
  71. data/lib/hecks/bluebook/behaviour/query.rb +10 -0
  72. data/lib/hecks/bluebook/behaviour/read_model.rb +29 -0
  73. data/lib/hecks/bluebook/behaviour/traits.rb +81 -0
  74. data/lib/hecks/bluebook/behaviour/value_object.rb +33 -0
  75. data/lib/hecks/bluebook/chapter.rb +78 -0
  76. data/lib/hecks/bluebook/command.rb +124 -0
  77. data/lib/hecks/bluebook/domain_port.rb +102 -0
  78. data/lib/hecks/bluebook/dsl/adapter_builder.rb +34 -0
  79. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +1018 -0
  80. data/lib/hecks/bluebook/dsl/attribute_collector.rb +348 -0
  81. data/lib/hecks/bluebook/dsl/binding_proxy.rb +71 -0
  82. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +1087 -0
  83. data/lib/hecks/bluebook/dsl/command_builder.rb +714 -0
  84. data/lib/hecks/bluebook/dsl/const_shim.rb +81 -0
  85. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +79 -0
  86. data/lib/hecks/bluebook/dsl/entity_builder.rb +430 -0
  87. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +366 -0
  88. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +163 -0
  89. data/lib/hecks/bluebook/dsl/identity_declaration.rb +191 -0
  90. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +44 -0
  91. data/lib/hecks/bluebook/dsl/malformed.rb +7 -0
  92. data/lib/hecks/bluebook/dsl/policy_builder.rb +135 -0
  93. data/lib/hecks/bluebook/dsl/port_builder.rb +39 -0
  94. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +142 -0
  95. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +306 -0
  96. data/lib/hecks/bluebook/dsl/query_builder.rb +113 -0
  97. data/lib/hecks/bluebook/dsl/read_model_builder.rb +233 -0
  98. data/lib/hecks/bluebook/dsl/rule_reference.rb +173 -0
  99. data/lib/hecks/bluebook/dsl/translation_builder.rb +243 -0
  100. data/lib/hecks/bluebook/dsl/value_object_builder.rb +178 -0
  101. data/lib/hecks/bluebook/dsl/word_gate.rb +228 -0
  102. data/lib/hecks/bluebook/dsl/world_builder.rb +117 -0
  103. data/lib/hecks/bluebook/dsl.rb +45 -0
  104. data/lib/hecks/bluebook/entity.rb +103 -0
  105. data/lib/hecks/bluebook/expression/canonical_form.rb +123 -0
  106. data/lib/hecks/bluebook/expression/evaluator.rb +304 -0
  107. data/lib/hecks/bluebook/expression/projection.json +218 -0
  108. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +233 -0
  109. data/lib/hecks/bluebook/expression/resolver.rb +781 -0
  110. data/lib/hecks/bluebook/expression.rb +13 -0
  111. data/lib/hecks/bluebook/hexagon.rb +60 -0
  112. data/lib/hecks/bluebook/lifecycle.rb +42 -0
  113. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +54 -0
  114. data/lib/hecks/bluebook/meta_validator/judge.rb +621 -0
  115. data/lib/hecks/bluebook/meta_validator/plan.rb +332 -0
  116. data/lib/hecks/bluebook/meta_validator/port_judge.rb +51 -0
  117. data/lib/hecks/bluebook/meta_validator/readings.rb +360 -0
  118. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +351 -0
  119. data/lib/hecks/bluebook/meta_validator/shapes.rb +266 -0
  120. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +255 -0
  121. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +138 -0
  122. data/lib/hecks/bluebook/meta_validator/world_judge.rb +78 -0
  123. data/lib/hecks/bluebook/meta_validator.rb +542 -0
  124. data/lib/hecks/bluebook/model_check.rb +445 -0
  125. data/lib/hecks/bluebook/pattern_subset.rb +184 -0
  126. data/lib/hecks/bluebook/policy.rb +41 -0
  127. data/lib/hecks/bluebook/process_manager.rb +128 -0
  128. data/lib/hecks/bluebook/project_discovery.rb +30 -0
  129. data/lib/hecks/bluebook/project_loader.rb +40 -0
  130. data/lib/hecks/bluebook/project_register.rb +82 -0
  131. data/lib/hecks/bluebook/query.rb +61 -0
  132. data/lib/hecks/bluebook/read_model.rb +109 -0
  133. data/lib/hecks/bluebook/reference.rb +74 -0
  134. data/lib/hecks/bluebook/smoke_test.rb +166 -0
  135. data/lib/hecks/bluebook/synthesizer.rb +95 -0
  136. data/lib/hecks/bluebook/translation.rb +92 -0
  137. data/lib/hecks/bluebook/value_object.rb +66 -0
  138. data/lib/hecks/bluebook.rb +74 -0
  139. data/lib/hecks/codemod.rb +342 -0
  140. data/lib/hecks/construct.rb +71 -0
  141. data/lib/hecks/deploy/bluebook/deploy.bluebook +219 -0
  142. data/lib/hecks/deploy/bluebook/deploy.hecksagon +4 -0
  143. data/lib/hecks/deploy/oidc.json +18 -0
  144. data/lib/hecks/doc/reference.rb +410 -0
  145. data/lib/hecks/embryonaut_bluebook.rb +75 -0
  146. data/lib/hecks/facade/cli_door.rb +119 -0
  147. data/lib/hecks/facade/cli_runner.rb +190 -0
  148. data/lib/hecks/facade/command_request.rb +105 -0
  149. data/lib/hecks/facade/handle.rb +173 -0
  150. data/lib/hecks/facade/json_door.rb +166 -0
  151. data/lib/hecks/facade/surface/aggregate_door.rb +185 -0
  152. data/lib/hecks/facade/surface/chapter.rb +107 -0
  153. data/lib/hecks/facade/surface.rb +48 -0
  154. data/lib/hecks/facade.rb +44 -0
  155. data/lib/hecks/forms/app.rb +341 -0
  156. data/lib/hecks/forms/command_form_renderer.rb +113 -0
  157. data/lib/hecks/forms/examples/banking_console.bluebook +3 -0
  158. data/lib/hecks/forms/field_renderer.rb +177 -0
  159. data/lib/hecks/forms/field_shape.rb +232 -0
  160. data/lib/hecks/forms/html.rb +84 -0
  161. data/lib/hecks/forms/index_renderer.rb +35 -0
  162. data/lib/hecks/forms/page.rb +157 -0
  163. data/lib/hecks/forms/params.rb +161 -0
  164. data/lib/hecks/forms/port_argument.rb +46 -0
  165. data/lib/hecks/forms/query_form_renderer.rb +114 -0
  166. data/lib/hecks/forms/record_renderer.rb +119 -0
  167. data/lib/hecks/forms/record_table.rb +68 -0
  168. data/lib/hecks/forms/reference_options.rb +30 -0
  169. data/lib/hecks/forms/value_object_shape.rb +46 -0
  170. data/lib/hecks/forms.rb +54 -0
  171. data/lib/hecks/fqn.rb +94 -0
  172. data/lib/hecks/framework/bluebook/compliance.bluebook +1 -0
  173. data/lib/hecks/framework/bluebook/console_settings.bluebook +489 -0
  174. data/lib/hecks/framework/bluebook/framework.hecksagon +32 -0
  175. data/lib/hecks/framework/bluebook/governance.bluebook +145 -0
  176. data/lib/hecks/framework/bluebook/identity.bluebook +90 -0
  177. data/lib/hecks/framework/oidc.json +39 -0
  178. data/lib/hecks/framework.rb +90 -0
  179. data/lib/hecks/freezer.rb +67 -0
  180. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +527 -0
  181. data/lib/hecks/fuzzing/invalid_value_generator.rb +86 -0
  182. data/lib/hecks/fuzzing/isolated_boot.rb +286 -0
  183. data/lib/hecks/fuzzing/properties.rb +1192 -0
  184. data/lib/hecks/fuzzing/replay.rb +668 -0
  185. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +93 -0
  186. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +80 -0
  187. data/lib/hecks/fuzzing/sequence_generator/picker.rb +96 -0
  188. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +250 -0
  189. data/lib/hecks/fuzzing/sequence_generator.rb +131 -0
  190. data/lib/hecks/fuzzing/value_generator.rb +200 -0
  191. data/lib/hecks/fuzzing.rb +12 -0
  192. data/lib/hecks/grammar/evolve.rb +324 -0
  193. data/lib/hecks/grammar/expression.bluebook +420 -0
  194. data/lib/hecks/grammar/expression_operators.json +1648 -0
  195. data/lib/hecks/grammar/grammar.hecksagon +20 -0
  196. data/lib/hecks/grammar/oidc.json +69 -0
  197. data/lib/hecks/grammar/translation.bluebook +203 -0
  198. data/lib/hecks/grammar.rb +178 -0
  199. data/lib/hecks/ir.rb +126 -0
  200. data/lib/hecks/language/adapter.bluebook +116 -0
  201. data/lib/hecks/language/bluebook/aggregate.bluebook +581 -0
  202. data/lib/hecks/language/bluebook/attaches/paging.bluebook +76 -0
  203. data/lib/hecks/language/bluebook/bluebook.bluebook +256 -0
  204. data/lib/hecks/language/bluebook/bluebook.hecksagon +24 -0
  205. data/lib/hecks/language/bluebook/command.bluebook +471 -0
  206. data/lib/hecks/language/bluebook/entity.bluebook +392 -0
  207. data/lib/hecks/language/bluebook/policy.bluebook +189 -0
  208. data/lib/hecks/language/bluebook/process_manager.bluebook +381 -0
  209. data/lib/hecks/language/bluebook/projection.bluebook +267 -0
  210. data/lib/hecks/language/bluebook/query.bluebook +245 -0
  211. data/lib/hecks/language/bluebook/shape.bluebook +292 -0
  212. data/lib/hecks/language/bluebook/syntax.bluebook +448 -0
  213. data/lib/hecks/language/bluebook/vocabulary.bluebook +379 -0
  214. data/lib/hecks/language/hecksagon/adapter_binding.bluebook +51 -0
  215. data/lib/hecks/language/hecksagon/domain_port.bluebook +76 -0
  216. data/lib/hecks/language/hecksagon/hecksagon.bluebook +131 -0
  217. data/lib/hecks/language/hecksagon/port_operation.bluebook +102 -0
  218. data/lib/hecks/language/oidc.json +333 -0
  219. data/lib/hecks/language/port.bluebook +120 -0
  220. data/lib/hecks/language/translation/translation.bluebook +110 -0
  221. data/lib/hecks/language/translation/translation_aggregate.bluebook +267 -0
  222. data/lib/hecks/language/world/wiring.bluebook +62 -0
  223. data/lib/hecks/language/world/world.bluebook +84 -0
  224. data/lib/hecks/literal.rb +125 -0
  225. data/lib/hecks/naming.rb +174 -0
  226. data/lib/hecks/ports/access_control.port +9 -0
  227. data/lib/hecks/ports/access_control.rb +62 -0
  228. data/lib/hecks/ports/agent/answers.rb +104 -0
  229. data/lib/hecks/ports/agent.port +8 -0
  230. data/lib/hecks/ports/agent.rb +167 -0
  231. data/lib/hecks/ports/authentication.port +6 -0
  232. data/lib/hecks/ports/authentication.rb +50 -0
  233. data/lib/hecks/ports/authorization.port +7 -0
  234. data/lib/hecks/ports/authorization.rb +62 -0
  235. data/lib/hecks/ports/clock.port +5 -0
  236. data/lib/hecks/ports/clock.rb +62 -0
  237. data/lib/hecks/ports/extraction.port +5 -0
  238. data/lib/hecks/ports/extraction.rb +37 -0
  239. data/lib/hecks/ports/identity_assignment.port +5 -0
  240. data/lib/hecks/ports/identity_assignment.rb +45 -0
  241. data/lib/hecks/ports/identity_generation.port +5 -0
  242. data/lib/hecks/ports/identity_generation.rb +49 -0
  243. data/lib/hecks/ports/identity_resolution.port +5 -0
  244. data/lib/hecks/ports/identity_resolution.rb +40 -0
  245. data/lib/hecks/ports/loading.port +4 -0
  246. data/lib/hecks/ports/loading.rb +13 -0
  247. data/lib/hecks/ports/persistence/append_only.rb +138 -0
  248. data/lib/hecks/ports/persistence/binding_policy.rb +56 -0
  249. data/lib/hecks/ports/persistence/execution.rb +23 -0
  250. data/lib/hecks/ports/persistence/null_saga_store.rb +25 -0
  251. data/lib/hecks/ports/persistence/plugin.rb +54 -0
  252. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +169 -0
  253. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +128 -0
  254. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +161 -0
  255. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +61 -0
  256. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +304 -0
  257. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +171 -0
  258. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +190 -0
  259. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +478 -0
  260. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +166 -0
  261. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +314 -0
  262. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +168 -0
  263. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +170 -0
  264. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +134 -0
  265. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +137 -0
  266. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +89 -0
  267. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +85 -0
  268. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +43 -0
  269. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +132 -0
  270. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +74 -0
  271. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +758 -0
  272. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +120 -0
  273. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +31 -0
  274. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +46 -0
  275. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +102 -0
  276. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +46 -0
  277. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +70 -0
  278. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +72 -0
  279. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +120 -0
  280. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +183 -0
  281. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +41 -0
  282. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +28 -0
  283. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +30 -0
  284. data/lib/hecks/ports/persistence/plugins/era/translation.rb +11 -0
  285. data/lib/hecks/ports/persistence/plugins/era.rb +48 -0
  286. data/lib/hecks/ports/persistence/remote_runtime.rb +42 -0
  287. data/lib/hecks/ports/persistence/repository_factory.rb +30 -0
  288. data/lib/hecks/ports/persistence.port +4 -0
  289. data/lib/hecks/ports/persistence.rb +39 -0
  290. data/lib/hecks/ports/projection.port +4 -0
  291. data/lib/hecks/ports/projection.rb +96 -0
  292. data/lib/hecks/ports/query/in_memory.rb +60 -0
  293. data/lib/hecks/ports/query/ordering.rb +41 -0
  294. data/lib/hecks/ports/query.rb +36 -0
  295. data/lib/hecks/ports.rb +27 -0
  296. data/lib/hecks/projections/diagrams.rb +642 -0
  297. data/lib/hecks/projections/ir.rb +18 -0
  298. data/lib/hecks/projections/model/deviations.rb +98 -0
  299. data/lib/hecks/projections/model.rb +145 -0
  300. data/lib/hecks/projections/oidc.rb +110 -0
  301. data/lib/hecks/projections/parser_table.rb +159 -0
  302. data/lib/hecks/projections/reference.rb +38 -0
  303. data/lib/hecks/projections/shape.rb +44 -0
  304. data/lib/hecks/projections/statements.rb +110 -0
  305. data/lib/hecks/projections/vocabulary.rb +100 -0
  306. data/lib/hecks/projections.rb +32 -0
  307. data/lib/hecks/projector/cli_projector.rb +446 -0
  308. data/lib/hecks/projector/docs_projector.rb +321 -0
  309. data/lib/hecks/projector/exporter.rb +158 -0
  310. data/lib/hecks/projector/ir_projector.rb +18 -0
  311. data/lib/hecks/projector/narrate_projector.rb +243 -0
  312. data/lib/hecks/projector/target.rb +97 -0
  313. data/lib/hecks/projector.rb +186 -0
  314. data/lib/hecks/query_ir.rb +411 -0
  315. data/lib/hecks/query_specification/common/authorization_spec.rb +9 -0
  316. data/lib/hecks/query_specification/common/comparators.rb +25 -0
  317. data/lib/hecks/query_specification/common/comparison.rb +174 -0
  318. data/lib/hecks/query_specification/common/cursor_spec.rb +9 -0
  319. data/lib/hecks/query_specification/common/dsl.rb +58 -0
  320. data/lib/hecks/query_specification/common/inspection_spec.rb +9 -0
  321. data/lib/hecks/query_specification/common/limit_spec.rb +9 -0
  322. data/lib/hecks/query_specification/common/null_policy.rb +92 -0
  323. data/lib/hecks/query_specification/common/null_semantics.rb +11 -0
  324. data/lib/hecks/query_specification/common/offset_spec.rb +9 -0
  325. data/lib/hecks/query_specification/common/options.rb +32 -0
  326. data/lib/hecks/query_specification/common/order_by.rb +9 -0
  327. data/lib/hecks/query_specification/common/specification.rb +10 -0
  328. data/lib/hecks/query_specification/common/where_clause.rb +9 -0
  329. data/lib/hecks/query_specification/field_path.rb +107 -0
  330. data/lib/hecks/query_specification/hop_path.rb +132 -0
  331. data/lib/hecks/query_specification/read_model/specification.rb +18 -0
  332. data/lib/hecks/query_specification.rb +14 -0
  333. data/lib/hecks/rendering.rb +48 -0
  334. data/lib/hecks/router/namespace_installer.rb +157 -0
  335. data/lib/hecks/router.rb +70 -0
  336. data/lib/hecks/runtime/aggregate_lock.rb +45 -0
  337. data/lib/hecks/runtime/boot_gates.rb +41 -0
  338. data/lib/hecks/runtime/caller.rb +66 -0
  339. data/lib/hecks/runtime/capability_graph.rb +44 -0
  340. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +135 -0
  341. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +260 -0
  342. data/lib/hecks/runtime/command_interpreter.rb +497 -0
  343. data/lib/hecks/runtime/command_rules/admissibility.rb +357 -0
  344. data/lib/hecks/runtime/command_rules/arithmetic.rb +260 -0
  345. data/lib/hecks/runtime/command_rules/authorization.rb +65 -0
  346. data/lib/hecks/runtime/command_rules/emission.rb +34 -0
  347. data/lib/hecks/runtime/command_rules/references.rb +189 -0
  348. data/lib/hecks/runtime/command_rules.rb +28 -0
  349. data/lib/hecks/runtime/dependency_planning.rb +245 -0
  350. data/lib/hecks/runtime/dispatcher.rb +301 -0
  351. data/lib/hecks/runtime/entity_element.rb +253 -0
  352. data/lib/hecks/runtime/entity_interpreter.rb +299 -0
  353. data/lib/hecks/runtime/errors.rb +122 -0
  354. data/lib/hecks/runtime/event.rb +51 -0
  355. data/lib/hecks/runtime/identity.rb +156 -0
  356. data/lib/hecks/runtime/instance.rb +175 -0
  357. data/lib/hecks/runtime/interpreting.rb +90 -0
  358. data/lib/hecks/runtime/loader.rb +184 -0
  359. data/lib/hecks/runtime/policy_interpreter.rb +366 -0
  360. data/lib/hecks/runtime/port_operation_interpreter.rb +210 -0
  361. data/lib/hecks/runtime/query_interpreter.rb +286 -0
  362. data/lib/hecks/runtime/reaction_invocation.rb +253 -0
  363. data/lib/hecks/runtime/read_model_interpreter.rb +341 -0
  364. data/lib/hecks/runtime/rebuild_sweep.rb +74 -0
  365. data/lib/hecks/runtime/reference_hop.rb +99 -0
  366. data/lib/hecks/runtime/refusal_wording.rb +126 -0
  367. data/lib/hecks/runtime/registry/saga_persistence.rb +142 -0
  368. data/lib/hecks/runtime/registry/verification.rb +246 -0
  369. data/lib/hecks/runtime/registry.rb +279 -0
  370. data/lib/hecks/runtime/remote_dispatcher.rb +143 -0
  371. data/lib/hecks/runtime/routing.rb +96 -0
  372. data/lib/hecks/runtime/saga_interpreter/correlation.rb +97 -0
  373. data/lib/hecks/runtime/saga_interpreter.rb +503 -0
  374. data/lib/hecks/runtime/saga_pending_dispatch.rb +45 -0
  375. data/lib/hecks/runtime/tenant_check.rb +84 -0
  376. data/lib/hecks/runtime/tenant_scope.rb +55 -0
  377. data/lib/hecks/runtime/value/admission.rb +130 -0
  378. data/lib/hecks/runtime/value/coercion.rb +565 -0
  379. data/lib/hecks/runtime/value/invariant_violation.rb +5 -0
  380. data/lib/hecks/runtime/value.rb +125 -0
  381. data/lib/hecks/runtime.rb +107 -0
  382. data/lib/hecks/storehouse.rb +632 -0
  383. data/lib/hecks/version.rb +16 -0
  384. data/lib/hecks/vocabulary.rb +218 -0
  385. data/lib/hecks.rb +123 -6
  386. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +90 -0
  387. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +128 -0
  388. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +160 -0
  389. metadata +412 -222
  390. data/bin/hecks +0 -7
  391. data/bin/hecks-package +0 -65
  392. data/bin/hecks_console +0 -12
  393. data/bin/hecks_serverless +0 -6
  394. data/lib/cli/build.rb +0 -14
  395. data/lib/cli/command_runner.rb +0 -28
  396. data/lib/cli/console.rb +0 -10
  397. data/lib/cli/generate.rb +0 -37
  398. data/lib/cli/hecks-cli.rb +0 -27
  399. data/lib/cli/test.rb +0 -57
  400. data/lib/console/commands.rb +0 -8
  401. data/lib/console/hecks-console.rb +0 -1
  402. data/lib/packager/README.md +0 -0
  403. data/lib/packager/app_runner.rb +0 -21
  404. data/lib/packager/args.rb +0 -26
  405. data/lib/packager/compatibility/fixnum.rb +0 -6
  406. data/lib/packager/hecks.rb +0 -39
  407. data/lib/packager/query_runner.rb +0 -21
  408. data/lib/packager/resources/Dockerfile +0 -11
  409. data/lib/packager/resources/app_binary +0 -7
  410. data/lib/packager/resources/bundle_config +0 -3
  411. data/lib/packager/resources/traveling-ruby-20150715-2.2.2-linux-x86_64.tar.gz +0 -0
  412. data/lib/packager/resources/traveling-ruby-20150715-2.2.2-osx.tar.gz +0 -0
  413. data/lib/serverless/Domain +0 -32
  414. data/lib/serverless/cli.rb +0 -75
  415. data/lib/serverless/resources/command_name.js +0 -5
  416. data/lib/serverless/resources/environment.js +0 -7
  417. data/lib/serverless/resources/handler.js.tt +0 -28
  418. data/lib/serverless/resources/run_binary.js +0 -22
  419. data/lib/serverless/resources/serverless.yml +0 -20
@@ -0,0 +1,166 @@
1
+ require_relative "synthesizer"
2
+ require "tmpdir"
3
+ require "fileutils"
4
+
5
+ module Hecks
6
+ module Bluebook
7
+ # BOOTS A REAL BLUEBOOK AND ACTUALLY DISPATCHES AGAINST IT — the
8
+ # sibling `bin/model_check` never had: that tool proves a bluebook
9
+ # is STRUCTURALLY sound (no dead states, no unreachable transitions)
10
+ # without ever running a single command; this proves it WORKS,
11
+ # catching exactly the class of bug static analysis structurally
12
+ # cannot see. Built and proved inside `bin/interview`'s own `smoke`
13
+ # command, where it first found two real bugs — a lifecycle field
14
+ # colliding with an explicit attribute, and a non-creating command
15
+ # missing `Command.ActsOn` — that reloaded clean, checked clean,
16
+ # and only broke once something actually dispatched against them.
17
+ #
18
+ # SYNTHESIZED ARGS, NEVER RANDOM (`Synthesizer`) — a String becomes
19
+ # a fixed marker, a closed set uses its own first admitted member, a
20
+ # reference reuses whatever this same run already minted for that
21
+ # target. ONE call per declared command, ONE call per declared
22
+ # report, per created root; every failure is collected and reported
23
+ # together rather than stopping at the first.
24
+ #
25
+ # WHAT THIS CANNOT CATCH: a call that dispatches cleanly but answers
26
+ # WRONG — the read-model join bug this exact tool's own first user
27
+ # hit is the textbook case, a query that silently returned an empty
28
+ # array rather than raising. Smoke-testing proves nothing crashes;
29
+ # it does not prove the answer is the right one.
30
+ #
31
+ # A FAILURE HERE IS NOT NECESSARILY A DOMAIN BUG, either — it may
32
+ # just be `Synthesizer`'s own naive values (0, an empty list,
33
+ # "smoke-test") not satisfying a real invariant this simple
34
+ # generator was never built to reason about (a positive amount, a
35
+ # non-empty topping list, an email pattern). Confirmed against
36
+ # Banking and Pizzas: both real domains, both showing exactly that
37
+ # class of failure, neither one an actual bug. Building a
38
+ # synthesizer that respects arbitrary declared invariants is real
39
+ # constraint-aware generation — a separate, larger tool than this.
40
+ module SmokeTest
41
+ Failure = Struct.new(:domain, :aggregate, :command, :error, keyword_init: true) do
42
+ def to_s = "#{domain}::#{aggregate}.#{command}: #{error}"
43
+ end
44
+
45
+ module_function
46
+
47
+ # `dir` is any real, bootable hecks app directory — a saved
48
+ # domain, or a throwaway one a caller rendered into a temp dir
49
+ # for exactly this purpose. Plain `Hecks.boot`, not
50
+ # `Router.boot` — proven all night, and it works against a
51
+ # domain with framework-attached chapters (`uses_framework
52
+ # "Governance"`, say) that `Router.boot` refuses outright: those
53
+ # chapters carry no `.world` of their own, which `Router`'s
54
+ # per-domain realm resolution requires and a plain boot never
55
+ # did. Reports are reachable the identical way regardless of
56
+ # boot path — `dispatcher.query("Domain.report_name", **args)`
57
+ # is the SAME `Dispatcher#query` either boot hands back; Router
58
+ # was never actually required for this, only its OWN namespace
59
+ # SUGAR (`Domain.report_name(...)` as a bare method call) was.
60
+ #
61
+ # NEVER BOOTS `dir`'S OWN REAL BINDINGS — measured, not a
62
+ # precaution taken on spec: pointed at `examples/pizzas` (a real,
63
+ # persistent store carrying real accumulated records), a
64
+ # synthesized `CreatePizza` collided with an actual pre-existing
65
+ # record literally named "smoke-test" — a coincidence this time,
66
+ # data pollution the next. `isolate!` copies only the `.bluebook`
67
+ # (and `.world`, for its realm) into a throwaway directory and
68
+ # deliberately leaves the real `.hecksagon` behind — with none
69
+ # present, `Ports::Persistence::BindingPolicy.default_binding`
70
+ # binds every aggregate to Memory on its own, no settings needed.
71
+ # Whatever `dir` is really bound to in production is never
72
+ # touched.
73
+ # `install_facade: false` — this only ever dispatches by FQN string
74
+ # (below), so it never needs the `Widget::Item.Add(...)` Ruby
75
+ # sugar `Hecks.boot` installs by default. Skipping it matters
76
+ # here specifically: that sugar lands as a BARE global constant on
77
+ # `Object` per domain and per aggregate name, with no scoping and
78
+ # no cleanup — and this tool boots throwaway domains under
79
+ # whatever generic names the caller's `.bluebook` happens to use
80
+ # ("Widget", "Item", "Tag" in this tool's own specs). Installing
81
+ # the facade would leak those names into the rest of the process —
82
+ # measured, not hypothetical: it once left a stale `Widget` constant
83
+ # that corrupted an unrelated spec's own unrelated use of the same
84
+ # bare name.
85
+ def call(dir)
86
+ Dir.mktmpdir("hecks-smoke-") do |scratch|
87
+ isolate!(dir, scratch)
88
+ dispatcher = Hecks.boot(scratch, install_facade: false)
89
+ domain = dispatcher.registry.bluebooks.keys.first
90
+ next [] unless domain
91
+
92
+ smoke_domain(dispatcher, domain)
93
+ end
94
+ end
95
+
96
+ # ONLY THE `.bluebook`, NEVER `.world` EITHER — a real world's own
97
+ # settings (`persisted_by("Heki") { dir "..." }`) are keyed to the
98
+ # REAL adapter it names, not to Memory; copied verbatim, they
99
+ # apply to the wrong binding and refuse with a `WiringError`
100
+ # ("Memory does not declare :dir") — measured, not assumed, the
101
+ # first time this ran against Banking. A plain `Hecks.boot`
102
+ # needs no realm to address anything (unlike `Router.boot`), so
103
+ # leaving the world out entirely is the correct minimal isolation,
104
+ # not a workaround. `translations/*.bluebook` also deliberately
105
+ # excluded — those are earlier, superseded versions of the same
106
+ # domain; smoke-testing the current declaration is the point, not
107
+ # its whole history.
108
+ def isolate!(dir, scratch)
109
+ source = Adapters::Folder.new.bluebook_directory(dir)
110
+ target = File.join(scratch, "bluebook")
111
+ FileUtils.mkdir_p(target)
112
+ Dir.glob(File.join(source, "*.bluebook")).each { |file| FileUtils.cp(file, target) }
113
+ end
114
+
115
+ # AGGREGATES WALKED IN DECLARATION ORDER, on purpose — so whatever
116
+ # a LATER aggregate's own shape needs from an EARLIER one (a
117
+ # `TripItem` needing a real `PackingItem`, say) already exists by
118
+ # the time that aggregate's own turn comes, the same reason
119
+ # `Synthesizer#args_for`'s `created` map is threaded through in
120
+ # this same order.
121
+ def smoke_domain(dispatcher, domain)
122
+ chapter = dispatcher.registry.bluebook(domain)
123
+ return [] unless chapter
124
+
125
+ created = {}
126
+ failures = []
127
+
128
+ chapter.aggregates.each do |aggregate|
129
+ creating, noncreating = aggregate.commands.partition(&:creates?)
130
+
131
+ creating.each do |command|
132
+ args = Synthesizer.args_for(chapter, aggregate, command, created)
133
+ begin
134
+ result = dispatcher.dispatch("#{domain}::#{aggregate.name}.#{command.hecks_name}", **args)
135
+ created[aggregate.name] = result.instance.id
136
+ rescue StandardError => e
137
+ failures << Failure.new(domain: domain, aggregate: aggregate.name, command: command.hecks_name,
138
+ error: "#{e.class}: #{e.message}")
139
+ next
140
+ end
141
+
142
+ noncreating.each do |nc_command|
143
+ nc_args = Synthesizer.args_for(chapter, aggregate, nc_command, created).merge(id: created[aggregate.name])
144
+ dispatcher.dispatch("#{domain}::#{aggregate.name}.#{nc_command.hecks_name}", **nc_args)
145
+ rescue StandardError => e
146
+ failures << Failure.new(domain: domain, aggregate: aggregate.name, command: nc_command.hecks_name,
147
+ error: "#{e.class}: #{e.message}")
148
+ end
149
+ end
150
+ end
151
+
152
+ chapter.read_models.each do |model|
153
+ root_id = created[model.reference_target]
154
+ next unless root_id # nothing was created for this root — nothing to smoke-test yet, not a failure
155
+
156
+ dispatcher.query("#{domain}.#{model.query_name}", model.reference_name => root_id)
157
+ rescue StandardError => e
158
+ failures << Failure.new(domain: domain, aggregate: model.name, command: "report(#{model.name})",
159
+ error: "#{e.class}: #{e.message}")
160
+ end
161
+
162
+ failures
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,95 @@
1
+ module Hecks
2
+ module Bluebook
3
+ # ONE SYNTHESIZED ARGUMENT PER DECLARED ATTRIBUTE — never a random
4
+ # guess. Reads only real `IR` (`Aggregate`, `Command`,
5
+ # `Attribute`, `ValueObject`), so it works against ANY
6
+ # loaded bluebook, not one particular domain: a String becomes a
7
+ # fixed marker; an Integer becomes 0; a closed set uses ITS OWN
8
+ # first admitted member (never a value a real `one_of` would
9
+ # legitimately refuse); a reference uses whatever id the CALLER
10
+ # already knows for that target (`created`), so a command whose
11
+ # own shape depends on an earlier-created record — a `TripItem`
12
+ # needing a real `PackingItem`, say — gets something real to work
13
+ # with instead of a guess that would only ever fail.
14
+ #
15
+ # Extracted from `bin/interview`'s own smoke test, where this first
16
+ # got built and proved — the logic never actually knew anything
17
+ # about the Interview domain or any one app; it only ever read IR.
18
+ module Synthesizer
19
+ # THE ONLY TYPE NAMES THAT ARE EVER TRULY SCALAR — anything else
20
+ # a value object's own field names is ANOTHER value object,
21
+ # nested (`Pizza.price_cents: Price`, itself wrapping `cents:
22
+ # Integer` — a real, ordinary shape, not an edge case). Missing
23
+ # this the first time round meant a nested field silently
24
+ # synthesized as the bare string `"smoke-test"` instead of the
25
+ # real nested structure a command actually declares — caught
26
+ # only by testing against Pizzas instead of re-testing against
27
+ # the one simpler domain this was first built for, where every
28
+ # value object happened to be single-field and primitive-typed.
29
+ # THE SAME LIST `Attribute::PRIMITIVES` holds, referenced rather
30
+ # than repeated. It was a second, byte-identical copy — and only
31
+ # the Attribute one is held to `vocabulary.bluebook` by
32
+ # spec/vocabulary_conformance_spec, so this copy could drift from
33
+ # the language and nothing would say so.
34
+ PRIMITIVES = Attribute::PRIMITIVES
35
+
36
+ module_function
37
+
38
+ # `chapter`/`aggregate` are the real `Bluebook`/`Aggregate`
39
+ # the command belongs to — needed to resolve a value object type
40
+ # name back to its own declared shape. `created` maps an
41
+ # aggregate NAME to a real id already minted for it earlier in
42
+ # the same run; a reference whose target isn't in there yet gets
43
+ # a placeholder instead of failing outright, since the caller may
44
+ # not care about that particular argument's real value.
45
+ def args_for(chapter, aggregate, command, created = {})
46
+ command.attributes.to_h do |attribute|
47
+ if attribute.reference?
48
+ [attribute.name, created.fetch(attribute.type.target_name, "smoke-test-id")]
49
+ else
50
+ [attribute.name, value_for(chapter, aggregate, attribute.type)]
51
+ end
52
+ end
53
+ end
54
+
55
+ # A value object's OWN synthesized value — its first admitted
56
+ # member if it's a closed set (the one value guaranteed not to be
57
+ # refused), otherwise one synthesized scalar per declared field.
58
+ # Falls back to searching every aggregate in the chapter, the
59
+ # same tolerance `bin/interview shape`'s own lookup already
60
+ # needed: a value object referenced by name doesn't have to be
61
+ # declared on the SAME aggregate using it.
62
+ def value_for(chapter, aggregate, type_name)
63
+ value_object = aggregate.value_object(type_name) ||
64
+ chapter.aggregates.filter_map { |a| a.value_object(type_name) }.first
65
+ return "smoke-test" unless value_object
66
+
67
+ return value_object.members.first.to_h if value_object.respond_to?(:closed_set?) && value_object.closed_set? && value_object.members.any?
68
+
69
+ value_object.attributes.to_h { |field| [field.name, field_value_for(chapter, aggregate, field.type)] }
70
+ end
71
+
72
+ # A single field's own synthesized value — a plain scalar if its
73
+ # type is one of the true primitives, otherwise that type names
74
+ # ANOTHER value object, resolved the identical way `value_for`
75
+ # resolves any other one (its own closed set or its own nested
76
+ # fields, however deep that nesting actually goes).
77
+ def field_value_for(chapter, aggregate, type_name)
78
+ PRIMITIVES.include?(type_name.to_s) ? scalar_for(type_name) : value_for(chapter, aggregate, type_name)
79
+ end
80
+
81
+ # A bare primitive's own synthesized value. Never used for a
82
+ # closed set's OWN field (that's `value_for`'s job, reading the
83
+ # set's real first member) — only for a plain scalar field with
84
+ # no declared vocabulary to respect.
85
+ def scalar_for(primitive)
86
+ case primitive.to_s
87
+ when "Integer" then 0
88
+ when "Float" then 0.0
89
+ when "TrueClass", "FalseClass" then false
90
+ else "smoke-test"
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,92 @@
1
+ module Hecks
2
+ module Bluebook
3
+ # One field crossing a value-object boundary: a scalar becoming a VO
4
+ # member, a VO member becoming a scalar, or a rename across the move.
5
+ # Paths are dotted ("price.cents") for a VO member, bare otherwise.
6
+ TranslationMove = Struct.new(:from, :to)
7
+
8
+ # One field whose VALUE, not just its name or position, changed —
9
+ # an old value has nothing in common with a new one, so the only
10
+ # honest way to bridge it is a declared, exhaustive lookup table.
11
+ # Paths follow `TranslationMove`'s convention (dotted reaches a VO
12
+ # member); `values` maps every old-era value that can appear to its
13
+ # new-era replacement.
14
+ TranslationConvert = Struct.new(:from, :to, :values)
15
+
16
+ # A value object's or entity's own TYPE NAME changed with its member
17
+ # structure unchanged — the one drift `rename`/`move` cannot express,
18
+ # because the attribute kept its name and only the type it points at
19
+ # was renamed. Mirrors `was:` one level deeper.
20
+ TranslationRetype = Struct.new(:from, :to)
21
+
22
+ # A computed transform — rescale, reformat, split, merge — whose only
23
+ # implementation is the SQL expression itself, evaluated exclusively
24
+ # inside the compiled Postgres head. No in-process reference exists
25
+ # to check it against; any aggregate carrying one refuses to boot on
26
+ # every other adapter.
27
+ TranslationCompute = Struct.new(:from, :to, :sql)
28
+
29
+ # The aggregate's own IDENTITY changed what it's computed from — not
30
+ # a field crossing a boundary (that's `move`), a value objects's type
31
+ # name (`retype`), or a value transform (`compute`): the record's own
32
+ # key. No `from:`/`to:` path, unlike every other rule here, because
33
+ # nothing is being consumed from or moved into `state` — the state
34
+ # a record already holds is untouched; only what identifies it is
35
+ # recomputed. Same SQL-only, Postgres-only, no-in-process-reference
36
+ # shape `compute` already has, and for the same reason: there is
37
+ # nothing to check this against outside the compiled head.
38
+ TranslationRekey = Struct.new(:sql)
39
+
40
+ # A newly added, required attribute with no source in old data at
41
+ # all — not a rename, move, or convert, all of which need a FROM
42
+ # path in the old shape. `default` is the value an existing record
43
+ # reads until the next command against it writes a real one; unlike
44
+ # `compute`, this is adapter-agnostic — applied in-process by
45
+ # `Lineage#translate`, the same as rename/move/drop, because there is
46
+ # nothing to compute, only a value to declare.
47
+ TranslationBackfill = Struct.new(:name, :default)
48
+
49
+ # One aggregate's part of a translation: which attributes were
50
+ # renamed, moved, converted, or deliberately dropped on the way from
51
+ # the old era to the new one. `drops` exists so data loss is a
52
+ # declared decision, not a field that quietly stopped being
53
+ # explained.
54
+ class TranslationAggregate
55
+ attr_reader :name, :was, :renames, :moves, :converts, :drops, :retypes, :computes, :rekeys, :backfills
56
+
57
+ def initialize(name:, was: nil, renames: {}, moves: [], converts: [], drops: [], retypes: [],
58
+ computes: [], rekeys: [], backfills: [])
59
+ @name = name.to_s
60
+ @was = was&.to_s
61
+ @renames = renames
62
+ @moves = moves
63
+ @converts = converts
64
+ @drops = drops
65
+ @retypes = retypes
66
+ @computes = computes
67
+ @rekeys = rekeys
68
+ @backfills = backfills
69
+ end
70
+ end
71
+
72
+ # A declared map from one era of a domain's storage shape to the
73
+ # next. Renames and moves — the smallest slice that proves the
74
+ # concept: old records are translated when they are replayed, never
75
+ # rewritten. `retired` acknowledges aggregates that are gone
76
+ # outright — not renamed — so their disappearance is a decision,
77
+ # not an accident.
78
+ class Translation
79
+ attr_reader :domain, :from, :to, :aggregates, :retired
80
+
81
+ def initialize(domain:, from:, to:, aggregates: [], retired: [])
82
+ @domain = domain.to_s
83
+ @from = from
84
+ @to = to
85
+ @aggregates = aggregates
86
+ @retired = retired
87
+ end
88
+
89
+ def for_aggregate(name) = @aggregates.find { |aggregate| aggregate.name == name.to_s }
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,66 @@
1
+ require_relative "behaviour/value_object"
2
+
3
+ module Hecks
4
+ module Bluebook
5
+ Invariant = Struct.new(:description, :canonical, :predicate, keyword_init: true)
6
+
7
+ # A value object — a DECLARATION HOLDER, never instantiated.
8
+ #
9
+ # `ValueObjectBuilder` returns an anonymous subclass whose singleton
10
+ # carries the declaration : attributes, invariants, members. Nothing ever
11
+ # calls `.new` on one — coercion builds `Runtime::Value` wrappers, and
12
+ # invariants run as canonical text through the Evaluator. The runtime
13
+ # reaches a shape through `aggregate.value_object(name)`, the IR's own
14
+ # finder ; there is no constant nesting and no second index.
15
+ #
16
+ # `to_h` does not move. It is a byte-for-byte contract pinned by the
17
+ # golden fixtures, so it keeps spelling the short declared name, which
18
+ # is what `hecks_name` carries. DECLARATIONS IN THE GRAPH, STRINGS IN THE
19
+ # EXPORT.
20
+ class ValueObject
21
+ extend Construct
22
+ # EXTENDED, not included — this construct is a class, so its
23
+ # emission is a class method. See Hecks::IR's own note on
24
+ # the two shapes.
25
+ extend Hecks::IR
26
+ extend Behaviour::ValueObject
27
+
28
+ emits_ir(
29
+ name: :hecks_name,
30
+ attributes: many(:attributes),
31
+ invariants: -> { invariants.map { |rule| { description: rule.description, canonical: rule.canonical } } },
32
+ closed_set: :closed_set?,
33
+ # THE FIELD NAME IS STRINGIFIED, NEVER THE VALUE. A `member` row can
34
+ # hold any of the scalar types an attribute declares — `Integer 84`
35
+ # (`StatementFrequency#retention_months`, statements.bluebook), not
36
+ # only `String` — and `value.to_s` used to erase that on the way
37
+ # out, so `84` and `"84"` (a member some other row might
38
+ # legitimately spell as text) became indistinguishable once they
39
+ # reached `to_h`. The declared name still moves (`field.to_s`) —
40
+ # that half was never a Ruby object with a type to lose.
41
+ members: -> { members.map { |member| member.map { |field, value| [field.to_s, value] } } }
42
+ )
43
+
44
+ class << self
45
+ attr_reader :attributes, :invariants, :members
46
+
47
+ # One declared shape — a subclass rather than an instance, so the thing
48
+ # the bluebook declares and the thing Ruby holds are one object.
49
+ def declare(name:, attributes: [], invariants: [], members: [], closed_set: !members.empty?)
50
+ shape = Class.new(self)
51
+ shape.hecks_name = name.to_s
52
+ shape.absorb(attributes: attributes, invariants: invariants,
53
+ members: members, closed_set: closed_set)
54
+ shape
55
+ end
56
+
57
+ def absorb(attributes:, invariants:, members:, closed_set:)
58
+ @attributes = attributes
59
+ @invariants = invariants
60
+ @members = members
61
+ @closed_set = closed_set
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,74 @@
1
+ # Hecks::Bluebook
2
+ #
3
+ # Everything a .bluebook file becomes on its way to running: the expression
4
+ # language (expression/), the model itself (the chapter class plus
5
+ # everything it declares), the assembly that turns
6
+ # declarations back into that graph (assembly.rb + assembly/), the authoring
7
+ # DSL (dsl/), and the meta-validator that judges a chapter against the
8
+ # language's own grammar (meta_validator.rb + meta_validator/).
9
+ #
10
+ # The require order below preserves the boot order the flat list in
11
+ # lib/hecks.rb used to spell: expression and IR first (pure
12
+ # declarations), assembly's collaborators before its face, the DSL before
13
+ # the meta-validator that its builders call at build time.
14
+
15
+ module Hecks
16
+ # Declared as a CLASS, not a module — `Hecks::Bluebook` IS a
17
+ # chapter (bluebook/chapter.rb carries its body). Everything a chapter
18
+ # declares nests under it, as do the ways to build one (`DSL`) and to
19
+ # judge one (`MetaValidator`). Reopening this anywhere must say `class`.
20
+ module Bluebook
21
+ end
22
+ end
23
+
24
+ # What the chapter's own files lean on at class-body level (`extend
25
+ # Construct`, the assembly's QuerySpecification marks) — required here
26
+ # because those files' contents are frozen and cannot say so themselves.
27
+ require_relative "construct"
28
+ require_relative "literal"
29
+ require_relative "query_specification"
30
+
31
+ require_relative "bluebook/expression"
32
+ # THE MODEL ITSELF — the chapter class's own body first, then everything
33
+ # a chapter declares. Order matters only for reading: each is a bag of
34
+ # declarations with no load-time cross-references.
35
+ require_relative "bluebook/chapter"
36
+ require_relative "bluebook/reference"
37
+ require_relative "bluebook/attribute"
38
+ require_relative "bluebook/value_object"
39
+ require_relative "bluebook/command"
40
+ require_relative "bluebook/lifecycle"
41
+ require_relative "bluebook/query"
42
+ require_relative "bluebook/read_model"
43
+ require_relative "bluebook/entity"
44
+ require_relative "bluebook/domain_port"
45
+ require_relative "bluebook/policy"
46
+ require_relative "bluebook/process_manager"
47
+ require_relative "bluebook/aggregate"
48
+ require_relative "bluebook/hexagon"
49
+ require_relative "bluebook/translation"
50
+
51
+ require_relative "bluebook/assembly/contract"
52
+ require_relative "bluebook/assembly/contracts"
53
+ require_relative "bluebook/assembly/specializer"
54
+ require_relative "bluebook/assembly/build"
55
+ require_relative "bluebook/assembly/marks"
56
+ require_relative "bluebook/assembly/aggregate_assembly"
57
+ require_relative "bluebook/assembly"
58
+
59
+ require_relative "bluebook/project_loader"
60
+
61
+ require_relative "bluebook/dsl"
62
+ require_relative "bluebook/pattern_subset"
63
+
64
+ require_relative "bluebook/meta_validator"
65
+ require_relative "bluebook/meta_validator/plan"
66
+ require_relative "bluebook/meta_validator/readings"
67
+ require_relative "bluebook/meta_validator/judge"
68
+ require_relative "bluebook/meta_validator/shapes"
69
+ require_relative "bluebook/meta_validator/reconstruction"
70
+ require_relative "bluebook/meta_validator/world_judge"
71
+ require_relative "bluebook/meta_validator/port_judge"
72
+ require_relative "bluebook/meta_validator/adapter_judge"
73
+ require_relative "bluebook/meta_validator/translation_judge"
74
+ require_relative "bluebook/meta_validator/syntax_boot"