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,183 @@
1
+ require_relative "../../runtime/storage_shape"
2
+
3
+ module Hecks
4
+ module Translation
5
+ module Scaffold
6
+ # Diff two eras' storage-shape projections into the edge's
7
+ # declarations — confident rules by unique signature pairing,
8
+ # everything ambiguous left unresolved for a human.
9
+ module Differ
10
+ # Diff two bluebook IRs into the edge's declarations.
11
+ def diff(held_bluebook, current_bluebook)
12
+ held_shapes = projections(held_bluebook)
13
+ current_shapes = projections(current_bluebook)
14
+
15
+ matched = match_aggregates(held_shapes, current_shapes)
16
+ aggregates = current_bluebook.aggregates.filter_map do |aggregate|
17
+ held_name = matched[:pairs][aggregate.name]
18
+ next unless held_name
19
+
20
+ rules = attribute_rules(held_shapes[held_name], current_shapes[aggregate.name])
21
+ was = held_name == aggregate.name ? nil : held_name
22
+ next if rules.empty? && was.nil?
23
+
24
+ ScaffoldedAggregate.new(name: aggregate.name, was: was, rules: rules)
25
+ end
26
+
27
+ { aggregates: aggregates, retired: matched[:retired], unclaimed: matched[:unclaimed] }
28
+ end
29
+
30
+ def projections(bluebook)
31
+ Runtime::StorageShape.project(bluebook)["aggregates"].to_h { |shape| [shape["name"], shape] }
32
+ end
33
+
34
+ # A vanished aggregate whose full shape reappears under exactly one
35
+ # new name was renamed. `retired` is only confident when NOTHING
36
+ # remains it could plausibly have become — a vanished aggregate
37
+ # beside an unmatched new one might be a rename-plus-reshape, and
38
+ # writing `retired` there would be a guess that strands data.
39
+ # Anything ambiguous stays unclaimed, and the coverage gate names
40
+ # it until a human decides.
41
+ def match_aggregates(held_shapes, current_shapes)
42
+ pairs = {}
43
+ current_shapes.each_key { |name| pairs[name] = held_shapes.key?(name) ? name : nil }
44
+
45
+ vanished = held_shapes.keys - current_shapes.keys
46
+ appeared = current_shapes.keys.select { |name| pairs[name].nil? }
47
+ unclaimed = []
48
+ vanished.each do |old_name|
49
+ old_shape = held_shapes[old_name].merge("name" => nil)
50
+ candidates = appeared.select { |name| current_shapes[name].merge("name" => nil) == old_shape }
51
+ if candidates.size == 1
52
+ pairs[candidates.first] = old_name
53
+ appeared -= candidates
54
+ else
55
+ unclaimed << old_name
56
+ end
57
+ end
58
+
59
+ retired = appeared.empty? ? unclaimed : []
60
+ { pairs: pairs, retired: retired, unclaimed: retired.empty? ? unclaimed : [] }
61
+ end
62
+
63
+ # Every changed path in one aggregate, resolved into rules by
64
+ # signature matching over the FULL path set — top-level names and
65
+ # dotted members alike. Unique signature pair: a rename (both
66
+ # top-level) or a move (any dotted end). Same path, same members,
67
+ # new type name: a retype. Anything else: unresolved, carrying its
68
+ # type-compatible candidates (an empty list is the arrow toward
69
+ # compute, or drop).
70
+ def attribute_rules(held_shape, current_shape)
71
+ rules = []
72
+ rules << identity_hint(held_shape, current_shape)
73
+ rules.compact!
74
+ held_attrs = (held_shape["attributes"] || []).to_h { |attribute| [attribute["name"], attribute] }
75
+ current_attrs = (current_shape["attributes"] || []).to_h { |attribute| [attribute["name"], attribute] }
76
+
77
+ # retype pass: same attribute name, same member structure, the
78
+ # TYPE's own name changed
79
+ (held_attrs.keys & current_attrs.keys).each do |name|
80
+ held = held_attrs[name]
81
+ current = current_attrs[name]
82
+ next if held == current
83
+ next unless container?(held) && container?(current)
84
+ next unless held["type"]["members"] == current["type"]["members"] && held["list"] == current["list"]
85
+
86
+ rules << { kind: :retype, from: held["type"]["type"], to: current["type"]["type"] }
87
+ end
88
+ retyped = rules.filter_map { |rule| rule[:from] if rule[:kind] == :retype }
89
+
90
+ vanished = vanished_paths(held_attrs, current_attrs, retyped)
91
+ appeared = appeared_paths(held_attrs, current_attrs, retyped)
92
+
93
+ vanished.each do |path, signature|
94
+ matches = appeared.select { |_, candidate| candidate == signature }.keys
95
+ taken = rules.filter_map { |rule| rule[:to] if %i[rename move].include?(rule[:kind]) }
96
+ matches -= taken
97
+ if matches.size == 1 && vanished.count { |_, other| other == signature } == 1
98
+ target = matches.first
99
+ kind = path.include?(".") || target.include?(".") ? :move : :rename
100
+ rules << { kind: kind, from: path, to: target }
101
+ else
102
+ candidates = matches.empty? ? compatible_candidates(appeared, signature) : matches
103
+ rules << { kind: :unresolved, from: path, candidates: candidates }
104
+ end
105
+ end
106
+ rules
107
+ end
108
+
109
+ # Held paths that need explaining: whole attributes that vanished,
110
+ # and members that vanished (or changed type) inside a kept
111
+ # attribute — mirroring exactly what EraGuard will demand coverage
112
+ # for.
113
+ def vanished_paths(held_attrs, current_attrs, retyped)
114
+ paths = {}
115
+ held_attrs.each do |name, held|
116
+ next if retyped.include?(container?(held) ? held["type"]["type"] : nil)
117
+
118
+ current = current_attrs[name]
119
+ if current.nil?
120
+ paths[name] = held["type"]
121
+ next
122
+ end
123
+ next if held == current
124
+
125
+ held_members = members_of(held)
126
+ current_members = members_of(current)
127
+ held_members.each do |member, signature|
128
+ paths["#{name}.#{member}"] = signature if current_members[member] != signature
129
+ end
130
+ end
131
+ paths
132
+ end
133
+
134
+ def appeared_paths(held_attrs, current_attrs, retyped)
135
+ paths = {}
136
+ current_attrs.each do |name, current|
137
+ next if retyped.include?(container?(current) ? current["type"]["type"] : nil)
138
+
139
+ held = held_attrs[name]
140
+ if held.nil?
141
+ paths[name] = current["type"]
142
+ next
143
+ end
144
+ next if held == current
145
+
146
+ held_members = members_of(held)
147
+ members_of(current).each do |member, signature|
148
+ paths["#{name}.#{member}"] = signature if held_members[member] != signature
149
+ end
150
+ end
151
+ paths
152
+ end
153
+
154
+ # THE ONE THING `attribute_rules` COULD NOT SEE BEFORE — the shape
155
+ # projection already carries `"identity"` (`StorageShape
156
+ # .project_aggregate`), it was simply never read here. An
157
+ # unresolved placeholder, not a guess: `coverage_check.rb`'s own
158
+ # `check_identity_unchanged!` is the real gate this only hints
159
+ # toward, the same "tool proactively guides you" pattern the
160
+ # generic unresolved message already gives unfed fields.
161
+ def identity_hint(held_shape, current_shape)
162
+ return if held_shape["identity"] == current_shape["identity"]
163
+
164
+ { kind: :unresolved, from: :identity, candidates: [] }
165
+ end
166
+
167
+ def compatible_candidates(appeared, signature)
168
+ appeared.select { |_, candidate| scalar_of(candidate) == scalar_of(signature) }.keys
169
+ end
170
+
171
+ def scalar_of(signature) = signature.is_a?(Hash) ? signature["members"] : signature
172
+
173
+ def members_of(attribute)
174
+ return {} unless container?(attribute)
175
+
176
+ attribute["type"]["members"].to_h { |member| [member["name"], member["type"]] }
177
+ end
178
+
179
+ def container?(attribute) = attribute["type"].is_a?(Hash)
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,41 @@
1
+ module Hecks
2
+ module Translation
3
+ module Scaffold
4
+ # Render an edge into .bluebook text — confident rules inline,
5
+ # ambiguities as parse-refusing `unresolved` constructs, never
6
+ # comments, so an unresolved file can only boot into a refusal.
7
+ module Renderer
8
+ def render(edge)
9
+ lines = ["Hecks.data_translation #{edge.domain.inspect}, from: #{edge.from.inspect}, to: #{edge.to.inspect} do"]
10
+ edge.aggregates.each do |aggregate|
11
+ header = " aggregate #{aggregate.name.inspect}"
12
+ header += ", was: #{aggregate.was.inspect}" if aggregate.was
13
+ if aggregate.rules.empty?
14
+ lines << "#{header} do"
15
+ else
16
+ lines << "#{header} do"
17
+ aggregate.rules.each { |rule| lines << " #{render_rule(rule)}" }
18
+ end
19
+ lines << " end"
20
+ end
21
+ edge.retired.each { |name| lines << " retired #{name.inspect}" }
22
+ lines << "end"
23
+ "#{lines.join("\n")}\n"
24
+ end
25
+
26
+ def render_rule(rule)
27
+ case rule[:kind]
28
+ when :rename then "rename :#{rule[:from]}, to: :#{rule[:to]}"
29
+ when :move then "move #{rule[:from].inspect}, to: #{rule[:to].inspect}"
30
+ when :retype then "retype #{rule[:from].inspect}, to: #{rule[:to].inspect}"
31
+ when :unresolved
32
+ candidates = rule[:candidates].map { |candidate| render_path(candidate) }.join(", ")
33
+ "unresolved #{render_path(rule[:from])}, candidates: [#{candidates}]"
34
+ end
35
+ end
36
+
37
+ def render_path(path) = path.to_s.include?(".") ? path.to_s.inspect : ":#{path}"
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,28 @@
1
+ require "fileutils"
2
+
3
+ module Hecks
4
+ module Translation
5
+ module Scaffold
6
+ # Put the rendered edge on disk, regenerating in place when a file
7
+ # for the same shape pair already exists.
8
+ module Writer
9
+ # The edge file, regenerated in place when one for the same shape
10
+ # pair already exists (matched textually — an unresolved file
11
+ # cannot be LOADED to ask, that being the whole point of
12
+ # unresolved).
13
+ def write!(directory, edge)
14
+ translations_dir = File.join(directory, "translations")
15
+ FileUtils.mkdir_p(translations_dir)
16
+
17
+ existing = Dir[File.join(translations_dir, "*.bluebook")].find do |path|
18
+ text = File.read(path)
19
+ text.include?("from: #{edge.from.inspect}") && text.include?("to: #{edge.to.inspect}")
20
+ end
21
+ path = existing || File.join(translations_dir, "#{edge.ordinal}-#{edge.label}.bluebook")
22
+ File.write(path, render(edge))
23
+ path
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ require_relative "scaffold/differ"
2
+ require_relative "scaffold/renderer"
3
+ require_relative "scaffold/writer"
4
+
5
+ module Hecks
6
+ module Translation
7
+ # The scaffold writes translations; humans resolve ambiguity. It
8
+ # diffs the held era's storage-shape projection against the current
9
+ # one (scaffold/differ.rb) and WRITES the edge file
10
+ # (scaffold/renderer.rb, scaffold/writer.rb): confident rules inline
11
+ # (unique signature pairs → rename/move, aggregate renames → `was:`,
12
+ # type renames with identical members → retype, aggregates gone
13
+ # without a successor → retired), ambiguities as parse-refusing
14
+ # `unresolved` constructs — never comments, so an unresolved file can
15
+ # only boot into a refusal, never a guess. It NEVER proposes a
16
+ # `compute` (a computation takes domain judgment no mechanical diff
17
+ # can infer) — an empty-candidate `unresolved` is what points the
18
+ # author there. It never proposes a `drop` either: data loss is a
19
+ # decision, not a default. Re-running regenerates the file, matched
20
+ # by shape pair.
21
+ module Scaffold
22
+ Edge = Struct.new(:domain, :from, :to, :ordinal, :label, :aggregates, :retired, keyword_init: true)
23
+ ScaffoldedAggregate = Struct.new(:name, :was, :rules, keyword_init: true)
24
+
25
+ extend Differ
26
+ extend Renderer
27
+ extend Writer
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,11 @@
1
+ module Hecks
2
+ # The era-translation toolchain: scaffold drafts a translation from two
3
+ # eras' shapes, audit judges a written one, reattest re-seals it.
4
+ module Translation
5
+ end
6
+ end
7
+
8
+ require_relative "translation/rule_compiler"
9
+ require_relative "translation/scaffold"
10
+ require_relative "translation/audit"
11
+ require_relative "translation/reattest"
@@ -0,0 +1,16 @@
1
+ module Hecks
2
+ # ISOLATED, ON PURPOSE — the gemspec reads this file directly rather
3
+ # than `require_relative "lib/hecks"` (the whole framework),
4
+ # specifically so evaluating the gemspec never triggers ANY of this
5
+ # gem's own dependencies (prism among them) before Bundler has even
6
+ # resolved what to install. A real, live chicken-and-egg bug caught
7
+ # deploying to a Ruby version that doesn't bundle prism for free
8
+ # (AWS Lambda's ruby3.2): the gemspec's own `require_relative
9
+ # "lib/hecks"` pulled in adapters/driven/prism.rb's
10
+ # unconditional `require "prism"`, which failed before Bundler ever
11
+ # read what the gemspec itself declared as a dependency — declaring
12
+ # it there, or even in the consuming Gemfile, never closed the gap,
13
+ # because gemspec evaluation happens before anything Bundler
14
+ # resolves is actually loadable yet.
15
+ VERSION = "0.3.0"
16
+ end
@@ -0,0 +1,213 @@
1
+ # GENERATED — projected from the language's own Vocabulary aggregate
2
+ # (lib/hecks/language/bluebook/vocabulary.bluebook).
3
+ #
4
+ # DO NOT EDIT. spec/vocabulary_table_spec.rb re-projects this in memory
5
+ # and refuses a diff, so an edit here fails the ordinary suite.
6
+ #
7
+ # Plain data on purpose — no requires, no dependency on the model —
8
+ # because several of these sets are read while a bluebook is still
9
+ # being parsed. A table that needed the framework to load could not
10
+ # be the table the framework loads with.
11
+
12
+ module Hecks
13
+ module Vocabulary
14
+ TABLES = {
15
+ "AggregateDispatchOrder" => [
16
+ {"step"=>"refuse_unknown_arguments"}.freeze,
17
+ {"step"=>"refuse_absent_arguments"}.freeze,
18
+ {"step"=>"normalize_args"}.freeze,
19
+ {"step"=>"refuse_role_mismatch"}.freeze,
20
+ {"step"=>"resolve_references"}.freeze,
21
+ {"step"=>"hydrate"}.freeze,
22
+ {"step"=>"enforce_givens"}.freeze,
23
+ {"step"=>"admissible_transition"}.freeze,
24
+ {"step"=>"assign_creation_attributes"}.freeze,
25
+ {"step"=>"apply_mutations"}.freeze,
26
+ {"step"=>"advance_lifecycle"}.freeze,
27
+ {"step"=>"delegate_to_entity"}.freeze,
28
+ {"step"=>"enforce_ensures"}.freeze,
29
+ {"step"=>"enforce_invariants"}.freeze,
30
+ {"step"=>"save"}.freeze,
31
+ {"step"=>"emit"}.freeze
32
+ ].freeze,
33
+ "Comparison" => [
34
+ {"symbol"=>">=", "compares_less_than"=>"true", "compares_equal"=>"false", "negated"=>"true"}.freeze,
35
+ {"symbol"=>"<=", "compares_less_than"=>"true", "compares_equal"=>"true", "negated"=>"false"}.freeze,
36
+ {"symbol"=>"<", "compares_less_than"=>"true", "compares_equal"=>"false", "negated"=>"false"}.freeze,
37
+ {"symbol"=>">", "compares_less_than"=>"true", "compares_equal"=>"true", "negated"=>"true"}.freeze,
38
+ {"symbol"=>"==", "compares_less_than"=>"false", "compares_equal"=>"true", "negated"=>"false"}.freeze,
39
+ {"symbol"=>"!=", "compares_less_than"=>"false", "compares_equal"=>"true", "negated"=>"true"}.freeze
40
+ ].freeze,
41
+ "DomainRefusal" => [
42
+ {"name"=>"AbsentArgument"}.freeze,
43
+ {"name"=>"AlreadyExists"}.freeze,
44
+ {"name"=>"AttributeAbsent"}.freeze,
45
+ {"name"=>"EnsuresNotMet"}.freeze,
46
+ {"name"=>"GivenNotMet"}.freeze,
47
+ {"name"=>"InvariantViolation"}.freeze,
48
+ {"name"=>"LifecycleRefused"}.freeze,
49
+ {"name"=>"NotFound"}.freeze,
50
+ {"name"=>"ProjectionAbsent"}.freeze,
51
+ {"name"=>"RemoteRefusal"}.freeze,
52
+ {"name"=>"TypeMismatch"}.freeze,
53
+ {"name"=>"Unauthorized"}.freeze,
54
+ {"name"=>"UnknownArgument"}.freeze,
55
+ {"name"=>"UnknownVerb"}.freeze
56
+ ].freeze,
57
+ "EntityDispatchOrder" => [
58
+ {"step"=>"normalize_args"}.freeze,
59
+ {"step"=>"refuse_role_mismatch"}.freeze,
60
+ {"step"=>"resolve_references"}.freeze,
61
+ {"step"=>"hydrate_parent"}.freeze,
62
+ {"step"=>"locate_element"}.freeze,
63
+ {"step"=>"enforce_givens"}.freeze,
64
+ {"step"=>"admissible_transition"}.freeze,
65
+ {"step"=>"apply_mutations"}.freeze,
66
+ {"step"=>"advance_lifecycle"}.freeze,
67
+ {"step"=>"enforce_ensures"}.freeze,
68
+ {"step"=>"enforce_invariants"}.freeze,
69
+ {"step"=>"save"}.freeze,
70
+ {"step"=>"emit"}.freeze
71
+ ].freeze,
72
+ "FieldHint" => [
73
+ {"name"=>"email", "pattern"=>"email", "resolves_to"=>"html_type"}.freeze,
74
+ {"name"=>"url", "pattern"=>"\\b(url|uri|website|link)\\b", "resolves_to"=>"html_type"}.freeze,
75
+ {"name"=>"tel", "pattern"=>"phone|\\btel(ephone)?\\b", "resolves_to"=>"html_type"}.freeze,
76
+ {"name"=>"textarea", "pattern"=>"\\b(text|body|note|notes|description|message|comment)\\b", "resolves_to"=>"kind"}.freeze
77
+ ].freeze,
78
+ "IncludeHaystack" => [
79
+ {"type"=>"Array", "strategy"=>"membership"}.freeze,
80
+ {"type"=>"String", "strategy"=>"substring"}.freeze
81
+ ].freeze,
82
+ "LoadOrder" => [
83
+ {"glob"=>"*.port"}.freeze,
84
+ {"glob"=>"*.adapter"}.freeze,
85
+ {"glob"=>"*.bluebook"}.freeze,
86
+ {"glob"=>"translations/*.bluebook"}.freeze,
87
+ {"glob"=>"*.hecksagon"}.freeze,
88
+ {"glob"=>"*.world"}.freeze
89
+ ].freeze,
90
+ "MutationOp" => [
91
+ {"name"=>"set", "sign"=>""}.freeze,
92
+ {"name"=>"append", "sign"=>""}.freeze,
93
+ {"name"=>"increment", "sign"=>"1"}.freeze,
94
+ {"name"=>"decrement", "sign"=>"-1"}.freeze,
95
+ {"name"=>"multiply", "sign"=>""}.freeze,
96
+ {"name"=>"clamp", "sign"=>""}.freeze,
97
+ {"name"=>"remove", "sign"=>""}.freeze,
98
+ {"name"=>"delegate", "sign"=>""}.freeze
99
+ ].freeze,
100
+ "NormalisationStrategy" => [
101
+ {"name"=>"collapse_whitespace"}.freeze,
102
+ {"name"=>"replace"}.freeze
103
+ ].freeze,
104
+ "Primitive" => [
105
+ {"name"=>"String"}.freeze,
106
+ {"name"=>"Integer"}.freeze,
107
+ {"name"=>"Float"}.freeze,
108
+ {"name"=>"TrueClass"}.freeze,
109
+ {"name"=>"FalseClass"}.freeze
110
+ ].freeze,
111
+ "QueryComparator" => [
112
+ {"name"=>"eq"}.freeze,
113
+ {"name"=>"ne"}.freeze,
114
+ {"name"=>"gt"}.freeze,
115
+ {"name"=>"gte"}.freeze,
116
+ {"name"=>"lt"}.freeze,
117
+ {"name"=>"lte"}.freeze,
118
+ {"name"=>"in"}.freeze,
119
+ {"name"=>"contains"}.freeze,
120
+ {"name"=>"none_in_state"}.freeze
121
+ ].freeze,
122
+ "RefusalTemplate" => [
123
+ {"refusal"=>"NotFound", "site"=>"creating_no_identity", "template"=>"{command} creates a {aggregate} — pass {identity}:"}.freeze,
124
+ {"refusal"=>"AlreadyExists", "site"=>"creating_duplicate", "template"=>"{command} creates a {aggregate} that already exists — {identity} {offered}"}.freeze,
125
+ {"refusal"=>"AlreadyExists", "site"=>"entity_duplicate", "template"=>"a {entity} already exists on {aggregate} — {identity} {offered}"}.freeze,
126
+ {"refusal"=>"NotFound", "site"=>"acting_no_identity", "template"=>"{command} acts on an existing {aggregate} — pass {identity}:"}.freeze,
127
+ {"refusal"=>"NotFound", "site"=>"record_missing", "template"=>"no {aggregate} with {identity} {offered}"}.freeze,
128
+ {"refusal"=>"NotFound", "site"=>"entity_parent_no_identity", "template"=>"{command} acts on a {aggregate}'s {entity} — pass {identity}:"}.freeze,
129
+ {"refusal"=>"UnknownVerb", "site"=>"entity_unknown", "template"=>"{aggregate} has no entity {entity}"}.freeze,
130
+ {"refusal"=>"NotFound", "site"=>"entity_element_no_identity", "template"=>"{command} acts on one {entity} — pass {identity}:"}.freeze,
131
+ {"refusal"=>"NotFound", "site"=>"entity_element_missing", "template"=>"no {entity} with {identity} {wants} on {aggregate} {parent_id}"}.freeze,
132
+ {"refusal"=>"NotFound", "site"=>"reference_target_missing", "template"=>"no {target} with {heads} {key}"}.freeze,
133
+ {"refusal"=>"NotFound", "site"=>"read_model_reference_missing", "template"=>"no {aggregate} with reference {offered}"}.freeze,
134
+ {"refusal"=>"TypeMismatch", "site"=>"read_model_object_reference", "template"=>"{query} refused — a reference is an id, and {field} arrived as an object"}.freeze,
135
+ {"refusal"=>"UnknownVerb", "site"=>"no_query", "template"=>"{aggregate} has no query {query}"}.freeze,
136
+ {"refusal"=>"UnknownVerb", "site"=>"entity_query_missing", "template"=>"{entity} has no query {query}"}.freeze,
137
+ {"refusal"=>"UnknownVerb", "site"=>"entity_holds_no_list", "template"=>"{aggregate} holds no list of {entity}"}.freeze,
138
+ {"refusal"=>"UnknownVerb", "site"=>"entity_no_command", "template"=>"{entity} has no command {command}"}.freeze,
139
+ {"refusal"=>"UnknownVerb", "site"=>"aggregate_no_command", "template"=>"{aggregate} has no command {command}"}.freeze,
140
+ {"refusal"=>"UnknownVerb", "site"=>"port_no_operation", "template"=>"{port} has no operation {operation}"}.freeze,
141
+ {"refusal"=>"UnknownVerb", "site"=>"no_domain", "template"=>"no domain {domain} loaded (verb {verb})"}.freeze,
142
+ {"refusal"=>"UnknownVerb", "site"=>"no_read_model", "template"=>"{domain} has no read model {query}"}.freeze,
143
+ {"refusal"=>"UnknownVerb", "site"=>"not_fully_qualified", "template"=>"{verb} is not a fully-qualified verb (Domain::Aggregate.Command)"}.freeze,
144
+ {"refusal"=>"UnknownVerb", "site"=>"no_aggregate", "template"=>"{domain} has no aggregate {aggregate}"}.freeze,
145
+ {"refusal"=>"LifecycleRefused", "site"=>"transition_blocked", "template"=>"{command} refused — {field} is {current}, and {command} moves it only from {allowed}"}.freeze,
146
+ {"refusal"=>"TypeMismatch", "site"=>"value_object_shape", "template"=>"{name} is a {type} — pass its fields as an object, not {offered}"}.freeze,
147
+ {"refusal"=>"TypeMismatch", "site"=>"reference_as_object", "template"=>"{command} refused — a reference is an id, and {attribute} arrived as an object{known_by}"}.freeze,
148
+ {"refusal"=>"TypeMismatch", "site"=>"multi_field_scalar", "template"=>"{type} has multiple fields and cannot stand in for a scalar"}.freeze,
149
+ {"refusal"=>"TypeMismatch", "site"=>"composite_identity", "template"=>"{type} is a composite identity — an identity must have exactly one field"}.freeze,
150
+ {"refusal"=>"TypeMismatch", "site"=>"numeric_field", "template"=>"{type}.{field} expects {expected}, got {offered}"}.freeze,
151
+ {"refusal"=>"TypeMismatch", "site"=>"pattern_mismatch", "template"=>"{type}.{field} must match {pattern}, got {offered}"}.freeze,
152
+ {"refusal"=>"TypeMismatch", "site"=>"arithmetic_amount", "template"=>"{op} of {target} needs an Integer, got {offered}"}.freeze,
153
+ {"refusal"=>"TypeMismatch", "site"=>"arithmetic_current", "template"=>"{op} of {target} needs an Integer {target}, got {offered}"}.freeze,
154
+ {"refusal"=>"TypeMismatch", "site"=>"arithmetic_shared_field", "template"=>"{op} of {target} needs a value object with one shared Integer field"}.freeze,
155
+ {"refusal"=>"UnknownArgument", "site"=>"unknown_args", "template"=>"{command} does not declare {unknown} — it takes {declared}"}.freeze,
156
+ {"refusal"=>"AbsentArgument", "site"=>"absent_args", "template"=>"{command} was not given {absent} — it takes {declared}"}.freeze,
157
+ {"refusal"=>"InvariantViolation", "site"=>"closed_set_member", "template"=>"{type} admits {admitted} — got {offered}"}.freeze,
158
+ {"refusal"=>"InvariantViolation", "site"=>"value_object_invariant", "template"=>"{name} invariant violated — {description} (given {offered})"}.freeze,
159
+ {"refusal"=>"InvariantViolation", "site"=>"admits_declared_set", "template"=>"{name} admits {admits} — {admitted} — got {offered}"}.freeze,
160
+ {"refusal"=>"InvariantViolation", "site"=>"undeclared_set", "template"=>"{name} admits {admits}, which this chapter does not declare — a closed set is named Aggregate::SetName, and it must be one the bluebook actually holds"}.freeze,
161
+ {"refusal"=>"Unauthorized", "site"=>"tenant_required", "template"=>"{query} declares authorize with tenant: {field} — pass {field}: to name which {field} this ask is scoped to"}.freeze,
162
+ {"refusal"=>"Unauthorized", "site"=>"role_mismatch", "template"=>"{command} refused — role: {role}, and the caller stated {caller_role}"}.freeze,
163
+ {"refusal"=>"AttributeAbsent", "site"=>"absent_read", "template"=>"{aggregate} {field} is absent on this record — declared, not optional, and added since it was written. Backfill it in a translation (backfill :{field}, default: ...), or declare it optional: true"}.freeze,
164
+ {"refusal"=>"ProjectionAbsent", "site"=>"absent_read", "template"=>"{aggregate} {field} is not yet projected on this record — declared via projects :{field}, but no rebuild sweep has populated it. Run the sweep, or read {reference}.{remote_field} directly if this rule cannot wait"}.freeze
165
+ ].freeze,
166
+ "SignTest" => [
167
+ {"name"=>"positive?", "compares_via"=>">"}.freeze,
168
+ {"name"=>"negative?", "compares_via"=>"<"}.freeze,
169
+ {"name"=>"zero?", "compares_via"=>"=="}.freeze
170
+ ].freeze,
171
+ "SizedType" => [
172
+ {"type"=>"Array"}.freeze,
173
+ {"type"=>"String"}.freeze,
174
+ {"type"=>"Hash"}.freeze
175
+ ].freeze,
176
+ "ToStringType" => [
177
+ {"type"=>"String"}.freeze,
178
+ {"type"=>"Integer"}.freeze,
179
+ {"type"=>"Float"}.freeze,
180
+ {"type"=>"TrueClass"}.freeze,
181
+ {"type"=>"FalseClass"}.freeze,
182
+ {"type"=>"NilClass"}.freeze
183
+ ].freeze,
184
+ "Trigger" => [
185
+ {"name"=>"refused"}.freeze
186
+ ].freeze,
187
+ "VocabularyName" => [
188
+
189
+ ].freeze
190
+ }.freeze
191
+
192
+ # THE TERMS — the first field of each row, which for a
193
+ # one-field vocabulary is the whole of it. Derived ONCE and
194
+ # frozen rather than mapped per call: these are constant
195
+ # tables, and a constant that allocates a new array every
196
+ # time it is read is not one.
197
+ TERMS = TABLES.transform_values { |rows| rows.map { |row| row.values.first }.freeze }.freeze
198
+
199
+ module_function
200
+
201
+ # Refuses an unknown name rather than answering nil: a set
202
+ # the language does not declare is a typo, not an empty set.
203
+ def fetch(name) = TERMS.fetch(name)
204
+
205
+ # The rows whole, for the vocabularies that carry more than
206
+ # a term — Comparison's own algebra, RefusalTemplate's text.
207
+ def rows(name) = TABLES.fetch(name)
208
+
209
+ def symbols(name) = fetch(name).map(&:to_sym)
210
+
211
+ def names = TABLES.keys
212
+ end
213
+ end
data/lib/hecks.rb CHANGED
@@ -1,6 +1,118 @@
1
- # frozen_string_literal: true
2
- require 'thor'
3
- require 'hecks-adapters-dynamodb'
4
- require 'hecks-plugins-json-validator'
5
- require_relative 'cli/hecks-cli'
6
- require_relative 'console/hecks-console'
1
+ require_relative "hecks/version"
2
+ # The closed sets the runtime computes with, generated from
3
+ # vocabulary.bluebook. Plain data, required first, because some of
4
+ # them are read while a bluebook is still being parsed.
5
+ require_relative "hecks/vocabulary"
6
+ require_relative "hecks/rendering"
7
+ require_relative "hecks/naming"
8
+ require_relative "hecks/fqn"
9
+ require_relative "hecks/freezer"
10
+ require_relative "hecks/construct"
11
+ # Before `bluebook` — every construct under `Bluebook` includes or
12
+ # extends this to declare what it emits.
13
+ require_relative "hecks/ir"
14
+ require_relative "hecks/literal"
15
+ require_relative "hecks/facade"
16
+ require_relative "hecks/query_specification"
17
+
18
+ require_relative "hecks/ports"
19
+ require_relative "hecks/bluebook"
20
+ require_relative "hecks/router"
21
+
22
+ require_relative "hecks/runtime"
23
+ require_relative "hecks/translation"
24
+ require_relative "hecks/adapters"
25
+ require_relative "hecks/projector"
26
+ # AFTER the projector registry and its `Target` mixin are both real —
27
+ # every target registers itself as it loads, so this require IS the
28
+ # installation of them.
29
+ require_relative "hecks/projections"
30
+ # AFTER `Projector` (dispatches against the `:cli` projection) and
31
+ # `Ports::Clock` (fills a staleness rule's `now` at the door) both exist.
32
+ require_relative "hecks/facade/cli_door"
33
+ require_relative "hecks/facade/cli_runner"
34
+ require_relative "hecks/framework"
35
+ require_relative "hecks/embryonaut_bluebook"
36
+
37
+ module Hecks
38
+ class LoadOutsideBoot < StandardError; end
39
+
40
+ class << self
41
+ attr_reader :current_registry
42
+
43
+ # The loading words below collect into the registry the RUNTIME is
44
+ # holding open ; booting and that ambient state belong to the runtime
45
+ # layer, so this module is their facade and Hecks::Runtime is where
46
+ # they live.
47
+ # `install_facade:` — see Runtime::Loader.boot. Defaults on; a caller
48
+ # that only dispatches by FQN string can skip the global sugar.
49
+ #
50
+ # `environment:` — RECOVERED, not new: this parameter (and the
51
+ # `environments/<name>.hecksagon` / `.world` overlay it loads —
52
+ # see Adapters::Folder#load_domain) existed on a prior commit of
53
+ # this repo (933d1dd), was vendored out to a real consumer
54
+ # (lifeadelics/domain), and was then lost from this repo's own
55
+ # history (no branch here reaches that commit). Ported forward
56
+ # from the consumer's vendor snapshot — the only surviving copy —
57
+ # and generalized: the original only loaded a `.hecksagon`
58
+ # overlay; this also loads a same-named `.world` overlay, both
59
+ # merged into the base rather than replacing it (Registry#add_hecksagon
60
+ # / #add_world). hecks never reads ENV itself — a caller
61
+ # resolves its own env var name and passes the resulting string
62
+ # straight through, e.g. `Hecks.boot(path, environment:
63
+ # ENV.fetch("MYAPP_ENV", "development"))`.
64
+ def boot(path, shared: nil, install_facade: true, environment: nil)
65
+ Runtime.boot(path, shared: shared, install_facade: install_facade, environment: environment)
66
+ end
67
+
68
+ # `paths` — an explicit list of files to boot (a `.bluebook`, its
69
+ # `.hecksagon`, optionally a `.world`), loaded in place from wherever
70
+ # they actually live — see Runtime::Loader.boot_files's own header for
71
+ # why this exists beside `boot` rather than as a special case of it.
72
+ def boot_files(paths, shared: nil, install_facade: true, environment: nil)
73
+ Runtime.boot_files(paths, shared: shared, install_facade: install_facade, environment: environment)
74
+ end
75
+
76
+ def with_registry(registry, &block) = Runtime.with_registry(registry, &block)
77
+
78
+ def current_registry = Runtime.current_registry
79
+
80
+ # Bind who is dispatching for the duration of the block — checked
81
+ # against a command's declared `role`, if it has one. Unbound (the
82
+ # default), a command's role stays exactly what it is without this:
83
+ # decoration.
84
+ #
85
+ # `actor_id` is OPTIONAL — a caller naming only a role is checked by
86
+ # string equality against the command's own `role`, exactly as
87
+ # before. A caller that also names WHO it is lets the check run
88
+ # against a real Governance `RoleAssignment` instead, once the
89
+ # command's domain has Governance attached — see
90
+ # `CommandRules::Authorization`'s own header.
91
+ def as_caller(role:, actor_id: nil, &block) = Runtime.as_caller(role: role, actor_id: actor_id, &block)
92
+
93
+ def bluebook(name, version: nil, &block)
94
+ collect(:add_bluebook, Bluebook::DSL::BluebookBuilder.build(name, version: version, &block))
95
+ end
96
+
97
+ def hecksagon(name, &block) = collect(:add_hecksagon, Bluebook::DSL::HecksagonBuilder.build(name, &block))
98
+ def port(name, &block) = collect(:add_port, Bluebook::DSL::PortBuilder.build(name, &block))
99
+ def adapter(name, &block) = collect(:add_adapter, Bluebook::DSL::AdapterBuilder.build(name, &block))
100
+ def world(name, &block) = collect(:add_world, Bluebook::DSL::WorldBuilder.build(name, &block))
101
+
102
+ def data_translation(name, from:, to:, &block)
103
+ collect(:add_translation, Bluebook::DSL::TranslationBuilder.build(name, from: from, to: to, &block))
104
+ end
105
+
106
+ private
107
+
108
+ def collect(method, item)
109
+ unless Runtime.current_registry
110
+ raise LoadOutsideBoot,
111
+ "declaration loaded outside a boot — use Hecks.boot(path) rather than requiring the file directly"
112
+ end
113
+
114
+ Runtime.current_registry.public_send(method, item)
115
+ item
116
+ end
117
+ end
118
+ end