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,218 @@
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"=>"NothingToCorrect"}.freeze,
50
+ {"name"=>"NotFound"}.freeze,
51
+ {"name"=>"ProjectionAbsent"}.freeze,
52
+ {"name"=>"RemoteRefusal"}.freeze,
53
+ {"name"=>"TypeMismatch"}.freeze,
54
+ {"name"=>"Unauthorized"}.freeze,
55
+ {"name"=>"UnknownArgument"}.freeze,
56
+ {"name"=>"UnknownVerb"}.freeze
57
+ ].freeze,
58
+ "EntityDispatchOrder" => [
59
+ {"step"=>"refuse_unknown_arguments"}.freeze,
60
+ {"step"=>"refuse_absent_arguments"}.freeze,
61
+ {"step"=>"normalize_args"}.freeze,
62
+ {"step"=>"refuse_role_mismatch"}.freeze,
63
+ {"step"=>"resolve_references"}.freeze,
64
+ {"step"=>"hydrate_parent"}.freeze,
65
+ {"step"=>"locate_element"}.freeze,
66
+ {"step"=>"enforce_givens"}.freeze,
67
+ {"step"=>"admissible_transition"}.freeze,
68
+ {"step"=>"apply_mutations"}.freeze,
69
+ {"step"=>"advance_lifecycle"}.freeze,
70
+ {"step"=>"enforce_ensures"}.freeze,
71
+ {"step"=>"enforce_invariants"}.freeze,
72
+ {"step"=>"save"}.freeze,
73
+ {"step"=>"emit"}.freeze
74
+ ].freeze,
75
+ "FieldHint" => [
76
+ {"name"=>"email", "pattern"=>"email", "resolves_to"=>"html_type"}.freeze,
77
+ {"name"=>"url", "pattern"=>"\\b(url|uri|website|link)\\b", "resolves_to"=>"html_type"}.freeze,
78
+ {"name"=>"tel", "pattern"=>"phone|\\btel(ephone)?\\b", "resolves_to"=>"html_type"}.freeze,
79
+ {"name"=>"textarea", "pattern"=>"\\b(text|body|note|notes|description|message|comment)\\b", "resolves_to"=>"kind"}.freeze
80
+ ].freeze,
81
+ "IncludeHaystack" => [
82
+ {"type"=>"Array", "strategy"=>"membership"}.freeze,
83
+ {"type"=>"String", "strategy"=>"substring"}.freeze
84
+ ].freeze,
85
+ "LoadOrder" => [
86
+ {"glob"=>"*.port"}.freeze,
87
+ {"glob"=>"*.adapter"}.freeze,
88
+ {"glob"=>"*.bluebook"}.freeze,
89
+ {"glob"=>"translations/*.bluebook"}.freeze,
90
+ {"glob"=>"*.hecksagon"}.freeze,
91
+ {"glob"=>"*.world"}.freeze
92
+ ].freeze,
93
+ "MutationOp" => [
94
+ {"name"=>"set", "sign"=>""}.freeze,
95
+ {"name"=>"append", "sign"=>""}.freeze,
96
+ {"name"=>"increment", "sign"=>"1"}.freeze,
97
+ {"name"=>"decrement", "sign"=>"-1"}.freeze,
98
+ {"name"=>"multiply", "sign"=>""}.freeze,
99
+ {"name"=>"clamp", "sign"=>""}.freeze,
100
+ {"name"=>"remove", "sign"=>""}.freeze,
101
+ {"name"=>"delegate", "sign"=>""}.freeze,
102
+ {"name"=>"corrects", "sign"=>""}.freeze
103
+ ].freeze,
104
+ "NormalisationStrategy" => [
105
+ {"name"=>"collapse_whitespace"}.freeze,
106
+ {"name"=>"replace"}.freeze
107
+ ].freeze,
108
+ "Primitive" => [
109
+ {"name"=>"String"}.freeze,
110
+ {"name"=>"Integer"}.freeze,
111
+ {"name"=>"Float"}.freeze,
112
+ {"name"=>"TrueClass"}.freeze,
113
+ {"name"=>"FalseClass"}.freeze
114
+ ].freeze,
115
+ "QueryComparator" => [
116
+ {"name"=>"eq"}.freeze,
117
+ {"name"=>"ne"}.freeze,
118
+ {"name"=>"gt"}.freeze,
119
+ {"name"=>"gte"}.freeze,
120
+ {"name"=>"lt"}.freeze,
121
+ {"name"=>"lte"}.freeze,
122
+ {"name"=>"in"}.freeze,
123
+ {"name"=>"contains"}.freeze,
124
+ {"name"=>"none_in_state"}.freeze
125
+ ].freeze,
126
+ "RefusalTemplate" => [
127
+ {"refusal"=>"NotFound", "site"=>"creating_no_identity", "template"=>"{command} creates a {aggregate} — pass {identity}:"}.freeze,
128
+ {"refusal"=>"AlreadyExists", "site"=>"creating_duplicate", "template"=>"{command} creates a {aggregate} that already exists — {identity} {offered}"}.freeze,
129
+ {"refusal"=>"AlreadyExists", "site"=>"entity_duplicate", "template"=>"a {entity} already exists on {aggregate} — {identity} {offered}"}.freeze,
130
+ {"refusal"=>"NotFound", "site"=>"acting_no_identity", "template"=>"{command} acts on an existing {aggregate} — pass {identity}:"}.freeze,
131
+ {"refusal"=>"NotFound", "site"=>"record_missing", "template"=>"no {aggregate} with {identity} {offered}"}.freeze,
132
+ {"refusal"=>"NotFound", "site"=>"entity_parent_no_identity", "template"=>"{command} acts on a {aggregate}'s {entity} — pass {identity}:"}.freeze,
133
+ {"refusal"=>"UnknownVerb", "site"=>"entity_unknown", "template"=>"{aggregate} has no entity {entity}"}.freeze,
134
+ {"refusal"=>"NotFound", "site"=>"entity_element_no_identity", "template"=>"{command} acts on one {entity} — pass {identity}:"}.freeze,
135
+ {"refusal"=>"NotFound", "site"=>"entity_element_missing", "template"=>"no {entity} with {identity} {wants} on {aggregate} {parent_id}"}.freeze,
136
+ {"refusal"=>"NotFound", "site"=>"reference_target_missing", "template"=>"no {target} with {heads} {key}"}.freeze,
137
+ {"refusal"=>"NotFound", "site"=>"read_model_reference_missing", "template"=>"no {aggregate} with reference {offered}"}.freeze,
138
+ {"refusal"=>"TypeMismatch", "site"=>"read_model_object_reference", "template"=>"{query} refused — a reference is an id, and {field} arrived as an object"}.freeze,
139
+ {"refusal"=>"UnknownVerb", "site"=>"no_query", "template"=>"{aggregate} has no query {query}"}.freeze,
140
+ {"refusal"=>"UnknownVerb", "site"=>"entity_query_missing", "template"=>"{entity} has no query {query}"}.freeze,
141
+ {"refusal"=>"UnknownVerb", "site"=>"entity_holds_no_list", "template"=>"{aggregate} holds no list of {entity}"}.freeze,
142
+ {"refusal"=>"UnknownVerb", "site"=>"entity_no_command", "template"=>"{entity} has no command {command}"}.freeze,
143
+ {"refusal"=>"UnknownVerb", "site"=>"aggregate_no_command", "template"=>"{aggregate} has no command {command}"}.freeze,
144
+ {"refusal"=>"UnknownVerb", "site"=>"port_no_operation", "template"=>"{port} has no operation {operation}"}.freeze,
145
+ {"refusal"=>"UnknownVerb", "site"=>"no_domain", "template"=>"no domain {domain} loaded (verb {verb})"}.freeze,
146
+ {"refusal"=>"UnknownVerb", "site"=>"no_read_model", "template"=>"{domain} has no read model {query}"}.freeze,
147
+ {"refusal"=>"UnknownVerb", "site"=>"not_fully_qualified", "template"=>"{verb} is not a fully-qualified verb (Domain::Aggregate.Command)"}.freeze,
148
+ {"refusal"=>"UnknownVerb", "site"=>"no_aggregate", "template"=>"{domain} has no aggregate {aggregate}"}.freeze,
149
+ {"refusal"=>"LifecycleRefused", "site"=>"transition_blocked", "template"=>"{command} refused — {field} is {current}, and {command} moves it only from {allowed}"}.freeze,
150
+ {"refusal"=>"TypeMismatch", "site"=>"value_object_shape", "template"=>"{name} is a {type} — pass its fields as an object, not {offered}"}.freeze,
151
+ {"refusal"=>"TypeMismatch", "site"=>"reference_as_object", "template"=>"{command} refused — a reference is an id, and {attribute} arrived as an object{known_by}"}.freeze,
152
+ {"refusal"=>"TypeMismatch", "site"=>"multi_field_scalar", "template"=>"{type} has multiple fields and cannot stand in for a scalar"}.freeze,
153
+ {"refusal"=>"TypeMismatch", "site"=>"composite_identity", "template"=>"{type} is a composite identity — an identity must have exactly one field"}.freeze,
154
+ {"refusal"=>"TypeMismatch", "site"=>"numeric_field", "template"=>"{type}.{field} expects {expected}, got {offered}"}.freeze,
155
+ {"refusal"=>"TypeMismatch", "site"=>"non_finite_field", "template"=>"{type}.{field} must be a finite number, got {offered}"}.freeze,
156
+ {"refusal"=>"TypeMismatch", "site"=>"pattern_mismatch", "template"=>"{type}.{field} must match {pattern}, got {offered}"}.freeze,
157
+ {"refusal"=>"TypeMismatch", "site"=>"arithmetic_amount", "template"=>"{op} of {target} needs an Integer, got {offered}"}.freeze,
158
+ {"refusal"=>"TypeMismatch", "site"=>"arithmetic_current", "template"=>"{op} of {target} needs an Integer {target}, got {offered}"}.freeze,
159
+ {"refusal"=>"TypeMismatch", "site"=>"arithmetic_shared_field", "template"=>"{op} of {target} needs a value object with one shared Integer field"}.freeze,
160
+ {"refusal"=>"UnknownArgument", "site"=>"unknown_args", "template"=>"{command} does not declare {unknown} — it takes {declared}"}.freeze,
161
+ {"refusal"=>"AbsentArgument", "site"=>"absent_args", "template"=>"{command} was not given {absent} — it takes {declared}"}.freeze,
162
+ {"refusal"=>"InvariantViolation", "site"=>"closed_set_member", "template"=>"{type} admits {admitted} — got {offered}"}.freeze,
163
+ {"refusal"=>"InvariantViolation", "site"=>"value_object_invariant", "template"=>"{name} invariant violated — {description} (given {offered})"}.freeze,
164
+ {"refusal"=>"InvariantViolation", "site"=>"admits_declared_set", "template"=>"{name} admits {admits} — {admitted} — got {offered}"}.freeze,
165
+ {"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,
166
+ {"refusal"=>"Unauthorized", "site"=>"tenant_required", "template"=>"{query} declares authorize with tenant: {field} — pass {field}: to name which {field} this ask is scoped to"}.freeze,
167
+ {"refusal"=>"Unauthorized", "site"=>"role_mismatch", "template"=>"{command} refused — role: {role}, and the caller stated {caller_role}"}.freeze,
168
+ {"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,
169
+ {"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
170
+ ].freeze,
171
+ "SignTest" => [
172
+ {"name"=>"positive?", "compares_via"=>">"}.freeze,
173
+ {"name"=>"negative?", "compares_via"=>"<"}.freeze,
174
+ {"name"=>"zero?", "compares_via"=>"=="}.freeze
175
+ ].freeze,
176
+ "SizedType" => [
177
+ {"type"=>"Array"}.freeze,
178
+ {"type"=>"String"}.freeze,
179
+ {"type"=>"Hash"}.freeze
180
+ ].freeze,
181
+ "ToStringType" => [
182
+ {"type"=>"String"}.freeze,
183
+ {"type"=>"Integer"}.freeze,
184
+ {"type"=>"Float"}.freeze,
185
+ {"type"=>"TrueClass"}.freeze,
186
+ {"type"=>"FalseClass"}.freeze,
187
+ {"type"=>"NilClass"}.freeze
188
+ ].freeze,
189
+ "Trigger" => [
190
+ {"name"=>"refused"}.freeze
191
+ ].freeze,
192
+ "VocabularyName" => [
193
+
194
+ ].freeze
195
+ }.freeze
196
+
197
+ # THE TERMS — the first field of each row, which for a
198
+ # one-field vocabulary is the whole of it. Derived ONCE and
199
+ # frozen rather than mapped per call: these are constant
200
+ # tables, and a constant that allocates a new array every
201
+ # time it is read is not one.
202
+ TERMS = TABLES.transform_values { |rows| rows.map { |row| row.values.first }.freeze }.freeze
203
+
204
+ module_function
205
+
206
+ # Refuses an unknown name rather than answering nil: a set
207
+ # the language does not declare is a typo, not an empty set.
208
+ def fetch(name) = TERMS.fetch(name)
209
+
210
+ # The rows whole, for the vocabularies that carry more than
211
+ # a term — Comparison's own algebra, RefusalTemplate's text.
212
+ def rows(name) = TABLES.fetch(name)
213
+
214
+ def symbols(name) = fetch(name).map(&:to_sym)
215
+
216
+ def names = TABLES.keys
217
+ end
218
+ end
data/lib/hecks.rb CHANGED
@@ -1,6 +1,123 @@
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/adapters"
24
+ require_relative "hecks/projector"
25
+ # AFTER the projector registry and its `Target` mixin are both real —
26
+ # every target registers itself as it loads, so this require IS the
27
+ # installation of them.
28
+ require_relative "hecks/projections"
29
+ # AFTER `Projector` (dispatches against the `:cli` projection) and
30
+ # `Ports::Clock` (fills a staleness rule's `now` at the door) both exist.
31
+ require_relative "hecks/facade/cli_door"
32
+ require_relative "hecks/facade/cli_runner"
33
+ require_relative "hecks/storehouse"
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
+ #
92
+ # `as_of` and `scope` are OPTIONAL too, same shape — see
93
+ # `Runtime.as_caller`'s own header for what each does.
94
+ def as_caller(role:, actor_id: nil, as_of: nil, scope: nil, &block)
95
+ Runtime.as_caller(role: role, actor_id: actor_id, as_of: as_of, scope: scope, &block)
96
+ end
97
+
98
+ def bluebook(name, version: nil, &block)
99
+ collect(:add_bluebook, Bluebook::DSL::BluebookBuilder.build(name, version: version, &block))
100
+ end
101
+
102
+ def hecksagon(name, &block) = collect(:add_hecksagon, Bluebook::DSL::HecksagonBuilder.build(name, &block))
103
+ def port(name, &block) = collect(:add_port, Bluebook::DSL::PortBuilder.build(name, &block))
104
+ def adapter(name, &block) = collect(:add_adapter, Bluebook::DSL::AdapterBuilder.build(name, &block))
105
+ def world(name, &block) = collect(:add_world, Bluebook::DSL::WorldBuilder.build(name, &block))
106
+
107
+ def data_translation(name, from:, to:, &block)
108
+ collect(:add_translation, Bluebook::DSL::TranslationBuilder.build(name, from: from, to: to, &block))
109
+ end
110
+
111
+ private
112
+
113
+ def collect(method, item)
114
+ unless Runtime.current_registry
115
+ raise LoadOutsideBoot,
116
+ "declaration loaded outside a boot — use Hecks.boot(path) rather than requiring the file directly"
117
+ end
118
+
119
+ Runtime.current_registry.public_send(method, item)
120
+ item
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,90 @@
1
+ module RuboCop
2
+ module Cop
3
+ module Hecks
4
+ # FLAGS THE EXACT SHAPE behind 8+ real bugs already fixed one at a
5
+ # time across this codebase (Tiers 1-5): `holder[a] || holder[b]` —
6
+ # the SAME receiver looked up by two different keys, falling back to
7
+ # the second lookup whenever the first is falsy. `||` cannot tell a
8
+ # genuinely STORED `false` from a MISSING key — both are falsy in
9
+ # Ruby — so a real `false` sitting at `holder[a]` is silently
10
+ # discarded and `holder[b]` (usually absent, so `nil`) is returned
11
+ # instead. Every one of those 8+ instances was the identical
12
+ # AST shape with a different pair of keys (most commonly
13
+ # `h[k.to_sym] || h[k]`, reading a value that could arrive keyed
14
+ # either by symbol or by string off the wire).
15
+ #
16
+ # THE FIX THIS CODEBASE ALREADY CONVERGED ON — see
17
+ # `lib/hecks/query_specification/field_path.rb#read`, the shared
18
+ # digger this whole bug class got consolidated behind: check
19
+ # `key?` FIRST, never fall back through `||`.
20
+ #
21
+ # sym = segment.to_sym
22
+ # return current.key?(sym) ? current[sym] : current[segment]
23
+ #
24
+ # That method's own comment says it plainly: "`key?` first, never
25
+ # `||`, because `||` falls through a genuinely-stored `false` to
26
+ # the OTHER spelling (usually absent) and returns `nil` instead."
27
+ # This cop exists so the NEXT `holder[a] || holder[b]` gets caught
28
+ # mechanically, before it becomes bug #9, rather than found by
29
+ # hand again in a future audit.
30
+ #
31
+ # SCOPED TO THE `[]`/`[]` SHAPE ONLY, deliberately — a receiver
32
+ # method-call is compared by AST structure (`==`, which ignores
33
+ # source location), so `hash[a] || hash[b]` is flagged whether
34
+ # `hash` is a local variable, a method call, or a constant, but
35
+ # `a[k] || b[k]` (DIFFERENT receivers) and `value || default`
36
+ # (an ordinary default, not a second lookup at all) are both left
37
+ # alone — neither one can silently drop a stored `false` the way
38
+ # a same-receiver double-lookup can.
39
+ #
40
+ # @example
41
+ # # bad — a stored `false` at hash[:active] is discarded
42
+ # hash[:active] || hash["active"]
43
+ #
44
+ # # good
45
+ # hash.key?(:active) ? hash[:active] : hash["active"]
46
+ #
47
+ # # good — or the shared digger this codebase already has
48
+ # Hecks::QuerySpecification::FieldPath.dig(hash, "active")
49
+ class FallbackHashLookup < Base
50
+ MSG = "`%<receiver>s[...] || %<receiver>s[...]` falls back to the second lookup " \
51
+ "whenever the first is falsy — but `||` cannot tell a genuinely stored `false` " \
52
+ "apart from a missing key, so a real `false` at `%<receiver>s[%<lhs_key>s]` is " \
53
+ "silently discarded in favor of `%<receiver>s[%<rhs_key>s]` instead of being " \
54
+ "returned. Use `%<receiver>s.key?(%<lhs_key>s) ? %<receiver>s[%<lhs_key>s] : " \
55
+ "%<receiver>s[%<rhs_key>s]`, or a shared digger (see `key?` in " \
56
+ "`Hecks::QuerySpecification::FieldPath#read`), instead."
57
+
58
+ # @!method bracket_lookup(node)
59
+ def_node_matcher :bracket_lookup, "(send $_receiver :[] $_key)"
60
+
61
+ def on_or(node)
62
+ lhs_receiver, lhs_key = bracket_lookup(node.lhs)
63
+ return unless lhs_receiver
64
+
65
+ rhs_receiver, rhs_key = bracket_lookup(node.rhs)
66
+ return unless rhs_receiver
67
+
68
+ # THE STRUCTURAL EQUALITY CHECK — `==` on an AST node (from the
69
+ # `ast` gem `Node` this compiles down to) compares `type` and
70
+ # `children` recursively and ignores source location, so
71
+ # `hash[a] || hash[b]` matches even though the two `hash`
72
+ # sub-nodes are two distinct node OBJECTS parsed from two
73
+ # different source ranges. A different receiver on each side
74
+ # (`a[k] || b[k]`) fails this check and is correctly left alone.
75
+ return unless lhs_receiver == rhs_receiver
76
+
77
+ add_offense(
78
+ node,
79
+ message: format(
80
+ MSG,
81
+ receiver: lhs_receiver.source,
82
+ lhs_key: lhs_key.source,
83
+ rhs_key: rhs_key.source
84
+ )
85
+ )
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,128 @@
1
+ module RuboCop
2
+ module Cop
3
+ module Hecks
4
+ # FLAGS `hash[new] = hash.delete(old)` INSIDE A LOOP — a method that
5
+ # both READS and WRITES the same hash across a loop's iterations,
6
+ # in the specific rename/permutation shape this codebase has
7
+ # already lost real data to once.
8
+ #
9
+ # THE BUG THIS IS THE MECHANICAL FOLLOW-UP TO (M27,
10
+ # docs/audits/2026-08-10-main-bug-audit.md,
11
+ # docs/audits/2026-08-11-bug-triage.md; fixed in
12
+ # `Hecks::Ports::Persistence::Lineage#apply_renames`,
13
+ # lib/hecks/ports/persistence/plugins/era/lineage.rb): a rename
14
+ # rule set applied one rule at a time, `state[new_name] =
15
+ # state.delete(old_name)` per rule, against the SAME hash it was
16
+ # reading from — so a swap (`rename :a, to: :b` alongside `rename
17
+ # :b, to: :a`) on `{a: 1, b: 2}` collapsed to `{a: 1}`. The first
18
+ # rule wrote `b: 1` (clobbering the real `b: 2`) before the second
19
+ # rule ever got a chance to read the value that was supposed to
20
+ # move to `:a` — sequential edits stepping on each other, not the
21
+ # simultaneous permutation a rename set actually means. The same
22
+ # shape loses data identically for ANY chain longer than one link
23
+ # (`a->b->c->a`), not just a two-element swap.
24
+ #
25
+ # THE FIX THIS COP POINTS AT — snapshot every old key's value
26
+ # FIRST, delete all old keys, THEN write all new keys, so no
27
+ # rename in the pass ever reads a key this same pass has already
28
+ # written:
29
+ #
30
+ # snapshot = renames.filter_map { |old_name, new_name| [old_name, new_name, state[old_name]] if state.key?(old_name) }
31
+ # snapshot.each { |old_name, _new_name, _value| state.delete(old_name) }
32
+ # snapshot.each { |_old_name, new_name, value| state[new_name] = value }
33
+ #
34
+ # WHAT THIS COP DOES NOT ATTEMPT — the general checklist item is
35
+ # broader than any AST pattern can safely automate ("any method
36
+ # that both reads and writes the same collection across a loop"
37
+ # covers plenty of correct code too — accumulating into a result
38
+ # hash, memoizing into a cache). This cop stays narrow and
39
+ # precise: only the EXACT `recv[x] = recv.delete(y)` shape, same
40
+ # receiver both sides, inside something that iterates. A single
41
+ # rename outside any loop is not this bug (nothing else can have
42
+ # run first); `hash.store(...)` instead of `hash[...] =`, or
43
+ # `hash.delete(...)` whose result is not immediately written back
44
+ # into the SAME hash, are not this shape either and are left to
45
+ # the broader code-review checklist item, not this cop.
46
+ #
47
+ # @example
48
+ # # bad — one rule at a time against the hash it reads from
49
+ # renames.each do |old_name, new_name|
50
+ # state[new_name] = state.delete(old_name)
51
+ # end
52
+ #
53
+ # # good — snapshot first, then delete, then write: one permutation
54
+ # snapshot = renames.filter_map { |old_name, new_name| [old_name, new_name, state[old_name]] if state.key?(old_name) }
55
+ # snapshot.each { |old_name, _new_name, _value| state.delete(old_name) }
56
+ # snapshot.each { |_old_name, new_name, value| state[new_name] = value }
57
+ class SequentialHashRenameInLoop < Base
58
+ MSG = "`%<recv>s[new] = %<recv>s.delete(old)` inside a loop applies one rename at a time against the " \
59
+ "SAME hash it reads from — a swap (`{a: :b, b: :a}`) on `{a: 1, b: 2}` collapses to `{a: 1}` " \
60
+ "because the first rule's write clobbers the second rule's read target before it runs (the exact " \
61
+ "bug fixed for Lineage#apply_renames). Snapshot every old key's value FIRST, delete all old keys, " \
62
+ "then write all new keys, so the pass applies as one simultaneous permutation instead of a " \
63
+ "sequence of edits each stepping on the last."
64
+
65
+ RESTRICT_ON_SEND = [:[]=].freeze
66
+
67
+ LOOP_METHODS = %i[
68
+ each each_pair each_with_index each_with_object with_index
69
+ map collect flat_map each_entry each_key each_value
70
+ inject reduce each_slice each_cons
71
+ ].freeze
72
+
73
+ # `hash[new_name] = hash.delete(old_name)` parses as a `send`
74
+ # node (indexed assignment has no dedicated node type in Ruby's
75
+ # own grammar): `(send recv :[]= new_key (send recv2 :delete
76
+ # old_key))`. `$_recv`/`$_recv2` are captured (not merely
77
+ # matched) so `same_receiver?` below can compare them
78
+ # structurally — node-pattern has no built-in "same subtree
79
+ # twice" backreference, so the equality check is real Ruby, not
80
+ # the pattern itself.
81
+ def_node_matcher :rename_write?, <<~PATTERN
82
+ (send $_recv :[]= _new_key (send $_recv2 :delete _old_key))
83
+ PATTERN
84
+
85
+ def on_send(node)
86
+ rename_write?(node) do |recv, recv2|
87
+ next unless same_receiver?(recv, recv2)
88
+ next unless in_loop?(node)
89
+
90
+ add_offense(node, message: format(MSG, recv: recv.source))
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ # Structural equality (type + children, recursively) — exactly
97
+ # what `Parser::AST::Node#==` already gives, ignoring source
98
+ # location, so `state` vs `state` (two separate `send(nil,
99
+ # :state)` reader calls) and `@state` vs `@state` both count as
100
+ # "the same collection", not just an identical local variable.
101
+ def same_receiver?(recv, recv2)
102
+ recv == recv2
103
+ end
104
+
105
+ # Walks every ancestor looking for something that iterates —
106
+ # not just the immediate parent, since the offending assignment
107
+ # may sit one or more non-loop nodes deep inside a loop's block
108
+ # (an `if` guarding it, say) and still carries the exact same
109
+ # hazard.
110
+ def in_loop?(node)
111
+ node.each_ancestor(:block, :numblock, :for, :while, :until, :while_post, :until_post).any? do |ancestor|
112
+ loop_ancestor?(ancestor)
113
+ end
114
+ end
115
+
116
+ def loop_ancestor?(ancestor)
117
+ case ancestor.type
118
+ when :for, :while, :until, :while_post, :until_post
119
+ true
120
+ when :block, :numblock
121
+ send_node = ancestor.send_node
122
+ send_node.send_type? && LOOP_METHODS.include?(send_node.method_name)
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end