hecks 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (373) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hecks/adapters/driven/claude_code.rb +72 -7
  3. data/lib/hecks/adapters/driven/d1.rb +187 -23
  4. data/lib/hecks/adapters/driven/folder.rb +83 -10
  5. data/lib/hecks/adapters/driven/google_authentication.rb +33 -12
  6. data/lib/hecks/adapters/driven/governance_authorization.rb +87 -18
  7. data/lib/hecks/adapters/driven/heki/journal.rb +13 -4
  8. data/lib/hecks/adapters/driven/heki/saga_store.rb +56 -10
  9. data/lib/hecks/adapters/driven/heki/snapshot.rb +1 -1
  10. data/lib/hecks/adapters/driven/heki.rb +106 -9
  11. data/lib/hecks/adapters/driven/identity_registry.rb +12 -2
  12. data/lib/hecks/adapters/driven/in_memory_ordering.rb +25 -3
  13. data/lib/hecks/adapters/driven/in_process_key_vault.adapter +3 -0
  14. data/lib/hecks/adapters/driven/in_process_key_vault.rb +53 -0
  15. data/lib/hecks/adapters/driven/lambda/client.rb +67 -14
  16. data/lib/hecks/adapters/driven/lambda.rb +82 -35
  17. data/lib/hecks/adapters/driven/local_storage.rb +83 -10
  18. data/lib/hecks/adapters/driven/memory.rb +205 -9
  19. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +21 -1
  20. data/lib/hecks/adapters/driven/postgres/codec.rb +27 -11
  21. data/lib/hecks/adapters/driven/postgres/outbox.rb +40 -2
  22. data/lib/hecks/adapters/driven/postgres/reconnect.rb +23 -7
  23. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +14 -14
  24. data/lib/hecks/adapters/driven/postgres.rb +175 -28
  25. data/lib/hecks/adapters/driven/prism.rb +50 -11
  26. data/lib/hecks/adapters/driven/secure_random_identity.rb +3 -0
  27. data/lib/hecks/adapters/driven/sql_query_builder.rb +34 -22
  28. data/lib/hecks/adapters/driven/sqlite/codec.rb +38 -10
  29. data/lib/hecks/adapters/driven/sqlite/projection.rb +60 -32
  30. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +12 -12
  31. data/lib/hecks/adapters/driven/sqlite.rb +181 -21
  32. data/lib/hecks/adapters/driven/system_clock.rb +3 -0
  33. data/lib/hecks/adapters/driven/tenant_provisioner.adapter +3 -0
  34. data/lib/hecks/adapters/driven/tenant_provisioner.rb +66 -0
  35. data/lib/hecks/adapters/driven.rb +6 -4
  36. data/lib/hecks/adapters/driving/github_webhook.rb +31 -18
  37. data/lib/hecks/behaviors/dsl.rb +60 -2
  38. data/lib/hecks/behaviors/expectations.rb +190 -29
  39. data/lib/hecks/behaviors/ir.rb +12 -1
  40. data/lib/hecks/behaviors/rspec.rb +9 -1
  41. data/lib/hecks/behaviors/runner.rb +21 -2
  42. data/lib/hecks/behaviors.rb +9 -1
  43. data/lib/hecks/bluebook/aggregate.rb +43 -13
  44. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +17 -10
  45. data/lib/hecks/bluebook/assembly/build.rb +26 -5
  46. data/lib/hecks/bluebook/assembly/contract.rb +98 -23
  47. data/lib/hecks/bluebook/assembly/contracts.rb +59 -52
  48. data/lib/hecks/bluebook/assembly/marks.rb +159 -30
  49. data/lib/hecks/bluebook/assembly/specializer.rb +38 -21
  50. data/lib/hecks/bluebook/assembly.rb +32 -14
  51. data/lib/hecks/bluebook/attribute.rb +26 -12
  52. data/lib/hecks/bluebook/behaviour/aggregate.rb +43 -11
  53. data/lib/hecks/bluebook/behaviour/attribute.rb +18 -5
  54. data/lib/hecks/bluebook/behaviour/chapter.rb +76 -5
  55. data/lib/hecks/bluebook/behaviour/command.rb +55 -25
  56. data/lib/hecks/bluebook/behaviour/domain_port.rb +27 -7
  57. data/lib/hecks/bluebook/behaviour/entity.rb +20 -8
  58. data/lib/hecks/bluebook/behaviour/hexagon.rb +30 -4
  59. data/lib/hecks/bluebook/behaviour/lifecycle.rb +27 -6
  60. data/lib/hecks/bluebook/behaviour/policy.rb +42 -17
  61. data/lib/hecks/bluebook/behaviour/process_manager.rb +39 -8
  62. data/lib/hecks/bluebook/behaviour/query.rb +6 -1
  63. data/lib/hecks/bluebook/behaviour/read_model.rb +29 -8
  64. data/lib/hecks/bluebook/behaviour/traits.rb +48 -12
  65. data/lib/hecks/bluebook/behaviour/value_object.rb +21 -9
  66. data/lib/hecks/bluebook/capabilities.rb +27 -0
  67. data/lib/hecks/bluebook/chapter.rb +51 -9
  68. data/lib/hecks/bluebook/command.rb +62 -17
  69. data/lib/hecks/bluebook/domain_port.rb +34 -9
  70. data/lib/hecks/bluebook/dsl/adapter_builder.rb +24 -0
  71. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +49 -49
  72. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +282 -123
  73. data/lib/hecks/bluebook/dsl/attribute_collector.rb +112 -75
  74. data/lib/hecks/bluebook/dsl/binding_proxy.rb +81 -2
  75. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +486 -117
  76. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +179 -47
  77. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
  78. data/lib/hecks/bluebook/dsl/command_builder.rb +284 -122
  79. data/lib/hecks/bluebook/dsl/const_shim.rb +46 -15
  80. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +90 -25
  81. data/lib/hecks/bluebook/dsl/entity_builder.rb +191 -61
  82. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
  83. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +130 -30
  84. data/lib/hecks/bluebook/dsl/identity_declaration.rb +38 -21
  85. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
  86. data/lib/hecks/bluebook/dsl/policy_builder.rb +86 -36
  87. data/lib/hecks/bluebook/dsl/port_builder.rb +38 -7
  88. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +56 -22
  89. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +111 -47
  90. data/lib/hecks/bluebook/dsl/query_builder.rb +37 -8
  91. data/lib/hecks/bluebook/dsl/read_model_builder.rb +127 -52
  92. data/lib/hecks/bluebook/dsl/rule_reference.rb +97 -43
  93. data/lib/hecks/bluebook/dsl/translation_builder.rb +150 -44
  94. data/lib/hecks/bluebook/dsl/value_object_builder.rb +68 -20
  95. data/lib/hecks/bluebook/dsl/word_gate.rb +59 -53
  96. data/lib/hecks/bluebook/dsl/world_builder.rb +51 -8
  97. data/lib/hecks/bluebook/entity.rb +40 -11
  98. data/lib/hecks/bluebook/expression/ast_json.rb +128 -36
  99. data/lib/hecks/bluebook/expression/ast_reader.rb +32 -3
  100. data/lib/hecks/bluebook/expression/canonical_form.rb +55 -16
  101. data/lib/hecks/bluebook/expression/evaluator.rb +221 -43
  102. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +54 -18
  103. data/lib/hecks/bluebook/expression/resolver.rb +369 -128
  104. data/lib/hecks/bluebook/hexagon.rb +35 -1
  105. data/lib/hecks/bluebook/lifecycle.rb +12 -1
  106. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +2 -1
  107. data/lib/hecks/bluebook/meta_validator/judge.rb +126 -108
  108. data/lib/hecks/bluebook/meta_validator/plan.rb +81 -46
  109. data/lib/hecks/bluebook/meta_validator/port_judge.rb +3 -2
  110. data/lib/hecks/bluebook/meta_validator/readings.rb +200 -50
  111. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +68 -41
  112. data/lib/hecks/bluebook/meta_validator/shapes.rb +166 -21
  113. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +286 -46
  114. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +11 -10
  115. data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -5
  116. data/lib/hecks/bluebook/meta_validator.rb +235 -139
  117. data/lib/hecks/bluebook/model_check.rb +434 -104
  118. data/lib/hecks/bluebook/pattern_subset.rb +32 -10
  119. data/lib/hecks/bluebook/policy.rb +15 -13
  120. data/lib/hecks/bluebook/process_manager.rb +27 -14
  121. data/lib/hecks/bluebook/project_discovery.rb +5 -0
  122. data/lib/hecks/bluebook/project_loader.rb +40 -0
  123. data/lib/hecks/bluebook/project_register.rb +50 -6
  124. data/lib/hecks/bluebook/query.rb +31 -4
  125. data/lib/hecks/bluebook/read_model.rb +35 -15
  126. data/lib/hecks/bluebook/reference.rb +26 -13
  127. data/lib/hecks/bluebook/smoke_test.rb +46 -23
  128. data/lib/hecks/bluebook/synthesizer.rb +46 -12
  129. data/lib/hecks/bluebook/translation.rb +34 -5
  130. data/lib/hecks/bluebook/value_object.rb +29 -11
  131. data/lib/hecks/bluebook.rb +5 -6
  132. data/lib/hecks/codemod.rb +138 -50
  133. data/lib/hecks/construct.rb +21 -7
  134. data/lib/hecks/corpus.rb +438 -0
  135. data/lib/hecks/deploy/bluebook/deploy.hecksagon +19 -0
  136. data/lib/hecks/doc/reference.rb +200 -31
  137. data/lib/hecks/embryonaut_bluebook.rb +38 -15
  138. data/lib/hecks/facade/cli_door.rb +69 -10
  139. data/lib/hecks/facade/cli_runner.rb +105 -24
  140. data/lib/hecks/facade/command_request.rb +23 -0
  141. data/lib/hecks/facade/handle.rb +155 -35
  142. data/lib/hecks/facade/json_door.rb +106 -25
  143. data/lib/hecks/facade/surface/aggregate_door.rb +50 -27
  144. data/lib/hecks/facade/surface/chapter.rb +26 -17
  145. data/lib/hecks/facade/surface.rb +16 -3
  146. data/lib/hecks/facade.rb +15 -4
  147. data/lib/hecks/forms/app.rb +46 -30
  148. data/lib/hecks/forms/command_form_renderer.rb +70 -9
  149. data/lib/hecks/forms/field_renderer.rb +142 -6
  150. data/lib/hecks/forms/field_shape.rb +183 -19
  151. data/lib/hecks/forms/html.rb +51 -7
  152. data/lib/hecks/forms/index_renderer.rb +14 -2
  153. data/lib/hecks/forms/page.rb +14 -0
  154. data/lib/hecks/forms/params.rb +120 -23
  155. data/lib/hecks/forms/port_argument.rb +14 -2
  156. data/lib/hecks/forms/query_form_renderer.rb +65 -2
  157. data/lib/hecks/forms/record_renderer.rb +60 -2
  158. data/lib/hecks/forms/record_table.rb +28 -1
  159. data/lib/hecks/forms/reference_options.rb +24 -0
  160. data/lib/hecks/forms/value_object_shape.rb +13 -3
  161. data/lib/hecks/forms.rb +24 -4
  162. data/lib/hecks/fqn.rb +59 -1
  163. data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
  164. data/lib/hecks/framework/bluebook/privacy.bluebook +155 -0
  165. data/lib/hecks/framework/oidc.json +15 -0
  166. data/lib/hecks/framework.rb +79 -25
  167. data/lib/hecks/freezer.rb +27 -11
  168. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +241 -106
  169. data/lib/hecks/fuzzing/combination_miner.rb +178 -0
  170. data/lib/hecks/fuzzing/concurrent_dispatch.rb +241 -45
  171. data/lib/hecks/fuzzing/coverage_campaign.rb +161 -0
  172. data/lib/hecks/fuzzing/differential.rb +192 -0
  173. data/lib/hecks/fuzzing/domain_generator.rb +871 -0
  174. data/lib/hecks/fuzzing/era_boundary.rb +68 -19
  175. data/lib/hecks/fuzzing/form_census.rb +121 -20
  176. data/lib/hecks/fuzzing/generated_domain_check.rb +171 -0
  177. data/lib/hecks/fuzzing/invalid_value_generator.rb +45 -6
  178. data/lib/hecks/fuzzing/isolated_boot.rb +137 -80
  179. data/lib/hecks/fuzzing/nondeterministic.rb +79 -0
  180. data/lib/hecks/fuzzing/persistence_parity.rb +111 -21
  181. data/lib/hecks/fuzzing/properties/corrections.rb +34 -9
  182. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +300 -51
  183. data/lib/hecks/fuzzing/properties/guards.rb +86 -42
  184. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +80 -32
  185. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +40 -35
  186. data/lib/hecks/fuzzing/properties/outbox.rb +70 -32
  187. data/lib/hecks/fuzzing/properties/querying.rb +82 -28
  188. data/lib/hecks/fuzzing/properties.rb +84 -51
  189. data/lib/hecks/fuzzing/qa_settings.rb +164 -0
  190. data/lib/hecks/fuzzing/replay.rb +241 -133
  191. data/lib/hecks/fuzzing/rotation_priority.rb +48 -28
  192. data/lib/hecks/fuzzing/rust_gap_manifest.rb +139 -0
  193. data/lib/hecks/fuzzing/self_consistency.rb +315 -137
  194. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +46 -40
  195. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +18 -11
  196. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +13 -12
  197. data/lib/hecks/fuzzing/sequence_generator/picker.rb +21 -12
  198. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +59 -32
  199. data/lib/hecks/fuzzing/sequence_generator.rb +158 -23
  200. data/lib/hecks/fuzzing/shrinker.rb +309 -0
  201. data/lib/hecks/fuzzing/structural_skips.rb +37 -130
  202. data/lib/hecks/fuzzing/sweep_depth.rb +12 -4
  203. data/lib/hecks/fuzzing/target_capabilities.rb +107 -29
  204. data/lib/hecks/fuzzing/value_generator.rb +110 -22
  205. data/lib/hecks/fuzzing.rb +5 -0
  206. data/lib/hecks/grammar/evolve.rb +188 -12
  207. data/lib/hecks/grammar.rb +53 -7
  208. data/lib/hecks/ir.rb +51 -20
  209. data/lib/hecks/language/bluebook/bluebook.bluebook +41 -0
  210. data/lib/hecks/language/bluebook/policy.bluebook +11 -1
  211. data/lib/hecks/language/bluebook/vocabulary.bluebook +348 -13
  212. data/lib/hecks/language/hecksagon/hecksagon.bluebook +11 -0
  213. data/lib/hecks/language/oidc.json +5 -0
  214. data/lib/hecks/literal.rb +41 -9
  215. data/lib/hecks/naming.rb +112 -31
  216. data/lib/hecks/ports/access_control.rb +53 -2
  217. data/lib/hecks/ports/agent/answers.rb +83 -6
  218. data/lib/hecks/ports/agent.rb +119 -35
  219. data/lib/hecks/ports/authentication.rb +44 -4
  220. data/lib/hecks/ports/authorization.rb +50 -11
  221. data/lib/hecks/ports/clock.rb +42 -23
  222. data/lib/hecks/ports/extraction.rb +16 -0
  223. data/lib/hecks/ports/identity_assignment.rb +23 -2
  224. data/lib/hecks/ports/identity_generation.rb +17 -3
  225. data/lib/hecks/ports/identity_resolution.rb +17 -1
  226. data/lib/hecks/ports/key_vault.port +6 -0
  227. data/lib/hecks/ports/key_vault.rb +58 -0
  228. data/lib/hecks/ports/loading.rb +4 -0
  229. data/lib/hecks/ports/persistence/append_only.rb +172 -8
  230. data/lib/hecks/ports/persistence/binding_policy.rb +34 -0
  231. data/lib/hecks/ports/persistence/codec_boundary.rb +178 -0
  232. data/lib/hecks/ports/persistence/execution.rb +4 -0
  233. data/lib/hecks/ports/persistence/null_saga_store.rb +12 -1
  234. data/lib/hecks/ports/persistence/plugin.rb +42 -4
  235. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +218 -25
  236. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +77 -9
  237. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +81 -24
  238. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +29 -18
  239. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +144 -60
  240. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +103 -8
  241. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +98 -23
  242. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +282 -109
  243. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +63 -25
  244. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +118 -66
  245. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +51 -28
  246. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +34 -5
  247. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +25 -12
  248. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +129 -34
  249. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +51 -6
  250. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +33 -9
  251. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +16 -0
  252. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +95 -6
  253. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +25 -2
  254. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +314 -90
  255. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +68 -10
  256. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +9 -3
  257. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +9 -2
  258. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +48 -8
  259. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +16 -1
  260. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +36 -5
  261. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +23 -3
  262. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +58 -19
  263. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +96 -5
  264. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +15 -0
  265. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +9 -1
  266. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -2
  267. data/lib/hecks/ports/persistence/plugins/era.rb +12 -2
  268. data/lib/hecks/ports/persistence/remote_runtime.rb +9 -2
  269. data/lib/hecks/ports/persistence/repository_factory.rb +23 -3
  270. data/lib/hecks/ports/persistence/state_codec.rb +319 -0
  271. data/lib/hecks/ports/persistence.rb +36 -1
  272. data/lib/hecks/ports/projection.rb +61 -7
  273. data/lib/hecks/ports/query/in_memory.rb +41 -8
  274. data/lib/hecks/ports/query/ordering.rb +21 -6
  275. data/lib/hecks/ports/query.rb +35 -0
  276. data/lib/hecks/ports.rb +1 -0
  277. data/lib/hecks/projections/bootstrap_table.rb +147 -0
  278. data/lib/hecks/projections/diagrams.rb +315 -79
  279. data/lib/hecks/projections/glossary/html.rb +91 -3
  280. data/lib/hecks/projections/glossary/markdown.rb +50 -3
  281. data/lib/hecks/projections/glossary/mermaid.rb +49 -1
  282. data/lib/hecks/projections/glossary/sections.rb +1 -1
  283. data/lib/hecks/projections/glossary/sentences.rb +110 -5
  284. data/lib/hecks/projections/glossary.rb +173 -31
  285. data/lib/hecks/projections/ir.rb +1 -1
  286. data/lib/hecks/projections/model/deviations.rb +62 -17
  287. data/lib/hecks/projections/model.rb +75 -21
  288. data/lib/hecks/projections/oidc.rb +45 -8
  289. data/lib/hecks/projections/parser_table.rb +53 -12
  290. data/lib/hecks/projections/reference.rb +15 -3
  291. data/lib/hecks/projections/rust_vocabulary.rb +646 -0
  292. data/lib/hecks/projections/shape.rb +10 -2
  293. data/lib/hecks/projections/statements.rb +69 -22
  294. data/lib/hecks/projections/vocabulary.rb +26 -9
  295. data/lib/hecks/projections.rb +5 -3
  296. data/lib/hecks/projector/cli_projector.rb +245 -37
  297. data/lib/hecks/projector/docs_projector.rb +154 -28
  298. data/lib/hecks/projector/exporter.rb +104 -29
  299. data/lib/hecks/projector/ir_projector.rb +7 -1
  300. data/lib/hecks/projector/narrate_projector.rb +144 -23
  301. data/lib/hecks/projector/target.rb +42 -18
  302. data/lib/hecks/projector.rb +86 -18
  303. data/lib/hecks/query_ir.rb +94 -47
  304. data/lib/hecks/query_specification/common/comparators.rb +19 -3
  305. data/lib/hecks/query_specification/common/comparison.rb +109 -27
  306. data/lib/hecks/query_specification/common/dsl.rb +65 -9
  307. data/lib/hecks/query_specification/common/null_policy.rb +61 -15
  308. data/lib/hecks/query_specification/common/null_semantics.rb +4 -0
  309. data/lib/hecks/query_specification/common/options.rb +25 -0
  310. data/lib/hecks/query_specification/field_path.rb +69 -15
  311. data/lib/hecks/query_specification/hop_path.rb +57 -20
  312. data/lib/hecks/query_specification/read_model/specification.rb +4 -0
  313. data/lib/hecks/rendering.rb +9 -3
  314. data/lib/hecks/router/namespace_installer.rb +16 -3
  315. data/lib/hecks/router.rb +56 -1
  316. data/lib/hecks/runtime/aggregate_lock.rb +20 -11
  317. data/lib/hecks/runtime/boot_gates.rb +21 -3
  318. data/lib/hecks/runtime/caller.rb +40 -8
  319. data/lib/hecks/runtime/capability_graph.rb +13 -2
  320. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +40 -41
  321. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +45 -67
  322. data/lib/hecks/runtime/command_interpreter.rb +195 -113
  323. data/lib/hecks/runtime/command_rules/admissibility.rb +231 -80
  324. data/lib/hecks/runtime/command_rules/arithmetic.rb +154 -96
  325. data/lib/hecks/runtime/command_rules/authorization.rb +38 -17
  326. data/lib/hecks/runtime/command_rules/emission.rb +18 -1
  327. data/lib/hecks/runtime/command_rules/references.rb +145 -55
  328. data/lib/hecks/runtime/command_rules.rb +3 -0
  329. data/lib/hecks/runtime/dependency_planning.rb +56 -11
  330. data/lib/hecks/runtime/dispatcher.rb +263 -92
  331. data/lib/hecks/runtime/entity_element.rb +304 -67
  332. data/lib/hecks/runtime/entity_interpreter.rb +149 -88
  333. data/lib/hecks/runtime/errors.rb +37 -23
  334. data/lib/hecks/runtime/event.rb +15 -10
  335. data/lib/hecks/runtime/identity.rb +91 -23
  336. data/lib/hecks/runtime/instance.rb +103 -18
  337. data/lib/hecks/runtime/interpreting.rb +24 -16
  338. data/lib/hecks/runtime/invocation.rb +358 -0
  339. data/lib/hecks/runtime/loader.rb +105 -19
  340. data/lib/hecks/runtime/outbox.rb +164 -26
  341. data/lib/hecks/runtime/policy_interpreter.rb +73 -60
  342. data/lib/hecks/runtime/port_operation_interpreter.rb +42 -19
  343. data/lib/hecks/runtime/query_interpreter.rb +93 -74
  344. data/lib/hecks/runtime/reaction_invocation.rb +73 -28
  345. data/lib/hecks/runtime/read_model_interpreter.rb +60 -44
  346. data/lib/hecks/runtime/rebuild_sweep.rb +32 -4
  347. data/lib/hecks/runtime/reference_hop.rb +48 -6
  348. data/lib/hecks/runtime/refusal_wording.rb +142 -115
  349. data/lib/hecks/runtime/registry/saga_persistence.rb +32 -21
  350. data/lib/hecks/runtime/registry/verification.rb +153 -28
  351. data/lib/hecks/runtime/registry.rb +202 -32
  352. data/lib/hecks/runtime/remote_dispatcher.rb +125 -24
  353. data/lib/hecks/runtime/routing.rb +36 -154
  354. data/lib/hecks/runtime/saga_interpreter/correlation.rb +25 -27
  355. data/lib/hecks/runtime/saga_interpreter.rb +90 -76
  356. data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
  357. data/lib/hecks/runtime/tenant_check.rb +33 -13
  358. data/lib/hecks/runtime/tenant_scope.rb +23 -5
  359. data/lib/hecks/runtime/value/admission.rb +75 -30
  360. data/lib/hecks/runtime/value/coercion.rb +555 -142
  361. data/lib/hecks/runtime/value/entity_list_coercion.rb +132 -60
  362. data/lib/hecks/runtime/value.rb +71 -21
  363. data/lib/hecks/runtime.rb +39 -7
  364. data/lib/hecks/storehouse.rb +368 -72
  365. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +130 -0
  366. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +32 -0
  367. data/lib/hecks/version.rb +3 -3
  368. data/lib/hecks/vocabulary.rb +205 -4
  369. data/lib/hecks.rb +91 -11
  370. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +19 -11
  371. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +24 -12
  372. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +38 -14
  373. metadata +28 -2
data/lib/hecks.rb CHANGED
@@ -22,16 +22,17 @@ require_relative "hecks/router"
22
22
  require_relative "hecks/runtime"
23
23
  require_relative "hecks/adapters"
24
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
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
27
  # installation of them.
28
28
  require_relative "hecks/projections"
29
- # AFTER `Projector` (dispatches against the `:cli` projection) and
29
+ # After `Projector` (dispatches against the `:cli` projection) and
30
30
  # `Ports::Clock` (fills a staleness rule's `now` at the door) both exist.
31
31
  require_relative "hecks/facade/cli_door"
32
32
  require_relative "hecks/facade/cli_runner"
33
33
  require_relative "hecks/storehouse"
34
34
  require_relative "hecks/framework"
35
+ require_relative "hecks/corpus"
35
36
  require_relative "hecks/embryonaut_bluebook"
36
37
 
37
38
  # The root namespace and public facade of the whole DSL/runtime: `Hecks.boot`/
@@ -44,14 +45,14 @@ module Hecks
44
45
  class LoadOutsideBoot < StandardError; end
45
46
 
46
47
  class << self
47
- # The loading words below collect into the registry the RUNTIME is
48
+ # The loading words below collect into the registry the runtime is
48
49
  # holding open ; booting and that ambient state belong to the runtime
49
50
  # layer, so this module is their facade and Hecks::Runtime is where
50
51
  # they live.
51
52
  # `install_facade:` — see Runtime::Loader.boot. Defaults on; a caller
52
53
  # that only dispatches by FQN string can skip the global sugar.
53
54
  #
54
- # `environment:` — RECOVERED, not new: this parameter (and the
55
+ # `environment:` — recovered, not new: this parameter (and the
55
56
  # `environments/<name>.hecksagon` / `.world` overlay it loads —
56
57
  # see Adapters::Folder#load_domain) existed on a prior commit of
57
58
  # this repo (933d1dd), was vendored out to a real consumer
@@ -65,6 +66,16 @@ module Hecks
65
66
  # resolves its own env var name and passes the resulting string
66
67
  # straight through, e.g. `Hecks.boot(path, environment:
67
68
  # ENV.fetch("MYAPP_ENV", "development"))`.
69
+ #
70
+ # @param path [String] path to a domain directory, or a file inside one
71
+ # @param shared [String, nil] a shared-root override; see `Runtime::Loader.boot`
72
+ # @param install_facade [Boolean] whether to install the `Widget::Item.Add`-style
73
+ # Ruby facade constants for this boot
74
+ # @param environment [String, nil] the environment name whose
75
+ # `environments/<name>.hecksagon`/`.world` overlay, if present, loads after the
76
+ # domain's own
77
+ # @return [Runtime::Dispatcher, Runtime::RemoteDispatcher] the dispatcher bound
78
+ # to the booted domain
68
79
  def boot(path, shared: nil, install_facade: true, environment: nil)
69
80
  Runtime.boot(path, shared: shared, install_facade: install_facade, environment: environment)
70
81
  end
@@ -73,12 +84,30 @@ module Hecks
73
84
  # `.hecksagon`, optionally a `.world`), loaded in place from wherever
74
85
  # they actually live — see Runtime::Loader.boot_files's own header for
75
86
  # why this exists beside `boot` rather than as a special case of it.
87
+ #
88
+ # @param paths [String, Array<String>] one or more file paths within the domain
89
+ # to load, instead of the whole directory
90
+ # @param shared [String, nil] a shared-root override; see `Runtime::Loader.boot_files`
91
+ # @param install_facade [Boolean] whether to install the `Widget::Item.Add`-style
92
+ # Ruby facade constants for this boot
93
+ # @param environment [String, nil] the environment name passed through to the
94
+ # selected-file loader
95
+ # @return [Runtime::Dispatcher, Runtime::RemoteDispatcher] the dispatcher bound
96
+ # to the booted domain
76
97
  def boot_files(paths, shared: nil, install_facade: true, environment: nil)
77
98
  Runtime.boot_files(paths, shared: shared, install_facade: install_facade, environment: environment)
78
99
  end
79
100
 
101
+ # Binds the ambient registry for the duration of the block.
102
+ #
103
+ # @param registry [Runtime::Registry] the registry to make current for the block
104
+ # @yield the code that should see `registry` as `current_registry`
105
+ # @return [Object] the block's result
80
106
  def with_registry(registry, &) = Runtime.with_registry(registry, &)
81
107
 
108
+ # Reads the ambient registry a declaration is currently landing in.
109
+ #
110
+ # @return [Runtime::Registry, nil] the current registry, or nil outside a boot
82
111
  def current_registry = Runtime.current_registry
83
112
 
84
113
  # Bind who is dispatching for the duration of the block — checked
@@ -86,25 +115,50 @@ module Hecks
86
115
  # default), a command's role stays exactly what it is without this:
87
116
  # decoration.
88
117
  #
89
- # `actor_id` is OPTIONAL — a caller naming only a role is checked by
118
+ # `actor_id` is optional — a caller naming only a role is checked by
90
119
  # string equality against the command's own `role`, exactly as
91
- # before. A caller that also names WHO it is lets the check run
120
+ # before. A caller that also names who it is lets the check run
92
121
  # against a real Governance `RoleAssignment` instead, once the
93
122
  # command's domain has Governance attached — see
94
123
  # `CommandRules::Authorization`'s own header.
95
124
  #
96
- # `as_of` and `scope` are OPTIONAL too, same shape — see
125
+ # `as_of` and `scope` are optional too, same shape — see
97
126
  # `Runtime.as_caller`'s own header for what each does.
127
+ #
128
+ # @param role [String, Symbol] the role to check the caller against
129
+ # @param actor_id [String, nil] who is calling, checked against a real Governance
130
+ # `RoleAssignment` when given; string-equality only against `role` when nil
131
+ # @param as_of [Integer, nil] Unix epoch seconds to check a matching `RoleAssignment`'s
132
+ # own `starts_at` against; unchecked when nil
133
+ # @param scope [String, nil] the scope to check a matching `RoleAssignment`'s own
134
+ # `scope` against; unchecked when nil
135
+ # @yield the code to run with this caller bound
136
+ # @return [Object] the block's result
98
137
  def as_caller(role:, actor_id: nil, as_of: nil, scope: nil, &)
99
138
  Runtime.as_caller(role: role, actor_id: actor_id, as_of: as_of, scope: scope, &)
100
139
  end
101
140
 
141
+ # Declares a chapter — a `.bluebook` file's top-level word.
142
+ #
143
+ # @param name [String] the chapter's declared name
144
+ # @param version [String, nil] the chapter's pinned version, or nil for unversioned
145
+ # @yield the chapter's body, evaluated against a `Bluebook::DSL::BluebookBuilder`
146
+ # @return [Bluebook::Chapter] the built, judged chapter
147
+ # @raise [LoadOutsideBoot] if called outside `Hecks.boot`/`.boot_files`
148
+ # @raise [Bluebook::DSL::Malformed] if the chapter fails the language's own judgment
102
149
  def bluebook(name, version: nil, &)
103
150
  collect(:add_bluebook, Bluebook::DSL::BluebookBuilder.build(name, version: version, &))
104
151
  end
105
152
 
153
+ # Declares a hecksagon — a `.hecksagon` file's top-level word, binding a
154
+ # domain's aggregates to adapters.
155
+ #
156
+ # @param name [String, Symbol] the domain's name the hecksagon binds
157
+ # @yield the hecksagon's body, evaluated against a `Bluebook::DSL::HecksagonBuilder`
158
+ # @return [Bluebook::Hecksagon] the built hecksagon
159
+ # @raise [LoadOutsideBoot] if called outside `Hecks.boot`/`.boot_files`
106
160
  def hecksagon(name, &) = collect(:add_hecksagon, Bluebook::DSL::HecksagonBuilder.build(name, &))
107
- # REPOINTED TO DomainPortBuilder — the migration DomainPort's own
161
+ # Repointed to DomainPortBuilder — the migration DomainPort's own
108
162
  # class comment names as its goal, now landed for the top-level
109
163
  # `.port` file callers too (the aggregate-scoped `Thing.port(...)`,
110
164
  # binding_proxy.rb, already went through this builder). Every real
@@ -115,19 +169,45 @@ module Hecks
115
169
  # identity check) — a pure repoint, no behavior change for any
116
170
  # existing caller reading `.verb`/`.signal` off what comes back.
117
171
  #
118
- # `legacy_bare_port: true` — the ONE real semantic gap this repoint
172
+ # `legacy_bare_port: true` — the one real semantic gap this repoint
119
173
  # would otherwise open: `PortBuilder#build` never refused a
120
174
  # completely empty build (no verb, no signal even), a real shape
121
175
  # dsl_spec.rb's own "a port" tests exercise (`signal`-only, no
122
176
  # `verb`). `DomainPortBuilder`'s own "declares no verb and no
123
- # operations" refusal is real and correct for its OTHER two callers
177
+ # operations" refusal is real and correct for its other two callers
124
178
  # (`BindingProxy#port`, `HecksagonBuilder#port_impl`) — only this,
125
179
  # the literal top-level `.port` file entry point, keeps the older,
126
180
  # looser rule (see `DomainPortBuilder#initialize`'s own comment).
127
181
  def port(name, &) = collect(:add_port, Bluebook::DSL::DomainPortBuilder.build(name, legacy_bare_port: true, &))
182
+
183
+ # Declares an adapter — an `.adapter` file's top-level word.
184
+ #
185
+ # @param name [String] the adapter's name
186
+ # @yield the adapter's body, evaluated against a `Bluebook::DSL::AdapterBuilder`
187
+ # @return [Bluebook::Adapter] the built, judged adapter
188
+ # @raise [LoadOutsideBoot] if called outside `Hecks.boot`/`.boot_files`
128
189
  def adapter(name, &) = collect(:add_adapter, Bluebook::DSL::AdapterBuilder.build(name, &))
190
+
191
+ # Declares a world — a `.world` file's top-level word, naming a domain's
192
+ # deployment realm.
193
+ #
194
+ # @param name [String, Symbol] the domain's name the world describes
195
+ # @yield the world's body, evaluated against a `Bluebook::DSL::WorldBuilder`
196
+ # @return [Bluebook::World] the built, judged world
197
+ # @raise [LoadOutsideBoot] if called outside `Hecks.boot`/`.boot_files`
129
198
  def world(name, &) = collect(:add_world, Bluebook::DSL::WorldBuilder.build(name, &))
130
199
 
200
+ # Declares a data translation — a `.translation` file's top-level word,
201
+ # migrating one domain's stored shape from one era to the next.
202
+ #
203
+ # @param name [String, Symbol] the domain this translation carries forward
204
+ # @param from [String, Symbol] the origin era
205
+ # @param to [String, Symbol] the destination era
206
+ # @yield the translation's body, evaluated against a
207
+ # `Bluebook::DSL::TranslationBuilder`
208
+ # @return [Bluebook::Translation] the built, judged translation
209
+ # @raise [LoadOutsideBoot] if called outside `Hecks.boot`/`.boot_files`
210
+ # @raise [Bluebook::DSL::Malformed] if `name`, `from`, or `to` is empty
131
211
  def data_translation(name, from:, to:, &)
132
212
  collect(:add_translation, Bluebook::DSL::TranslationBuilder.build(name, from: from, to: to, &))
133
213
  end
@@ -1,11 +1,11 @@
1
1
  module RuboCop
2
2
  module Cop
3
3
  module Hecks
4
- # FLAGS THE EXACT SHAPE behind 8+ real bugs already fixed one at a
4
+ # Flags the exact shape behind 8+ real bugs already fixed one at a
5
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
6
+ # the same receiver looked up by two different keys, falling back to
7
7
  # the second lookup whenever the first is falsy. `||` cannot tell a
8
- # genuinely STORED `false` from a MISSING key — both are falsy in
8
+ # genuinely stored `false` from a missing key — both are falsy in
9
9
  # Ruby — so a real `false` sitting at `holder[a]` is silently
10
10
  # discarded and `holder[b]` (usually absent, so `nil`) is returned
11
11
  # instead. Every one of those 8+ instances was the identical
@@ -13,26 +13,30 @@ module RuboCop
13
13
  # `h[k.to_sym] || h[k]`, reading a value that could arrive keyed
14
14
  # either by symbol or by string off the wire).
15
15
  #
16
- # THE FIX THIS CODEBASE ALREADY CONVERGED ON — see
16
+ # ## The fix
17
+ #
18
+ # The fix this codebase already converged on — see
17
19
  # `lib/hecks/query_specification/field_path.rb#read`, the shared
18
20
  # digger this whole bug class got consolidated behind: check
19
- # `key?` FIRST, never fall back through `||`.
21
+ # `key?` first, never fall back through `||`.
20
22
  #
21
23
  # sym = segment.to_sym
22
24
  # return current.key?(sym) ? current[sym] : current[segment]
23
25
  #
24
26
  # That method's own comment says it plainly: "`key?` first, never
25
27
  # `||`, 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
+ # the other spelling (usually absent) and returns `nil` instead."
29
+ # This cop exists so the next `holder[a] || holder[b]` gets caught
28
30
  # mechanically, before it becomes bug #9, rather than found by
29
31
  # hand again in a future audit.
30
32
  #
31
- # SCOPED TO THE `[]`/`[]` SHAPE ONLY, deliberately — a receiver
33
+ # ## Scope
34
+ #
35
+ # Scoped to the `[]`/`[]` shape only, deliberately — a receiver
32
36
  # method-call is compared by AST structure (`==`, which ignores
33
37
  # source location), so `hash[a] || hash[b]` is flagged whether
34
38
  # `hash` is a local variable, a method call, or a constant, but
35
- # `a[k] || b[k]` (DIFFERENT receivers) and `value || default`
39
+ # `a[k] || b[k]` (different receivers) and `value || default`
36
40
  # (an ordinary default, not a second lookup at all) are both left
37
41
  # alone — neither one can silently drop a stored `false` the way
38
42
  # a same-receiver double-lookup can.
@@ -58,6 +62,10 @@ module RuboCop
58
62
  # @!method bracket_lookup(node)
59
63
  def_node_matcher :bracket_lookup, "(send $_receiver :[] $_key)"
60
64
 
65
+ # Flags an `a[k] || a[k2]` double bracket-lookup on the same receiver.
66
+ #
67
+ # @param node [RuboCop::AST::OrNode] the `||` node being visited
68
+ # @return [void]
61
69
  def on_or(node)
62
70
  lhs_receiver, lhs_key = bracket_lookup(node.lhs)
63
71
  return unless lhs_receiver
@@ -65,11 +73,11 @@ module RuboCop
65
73
  rhs_receiver, rhs_key = bracket_lookup(node.rhs)
66
74
  return unless rhs_receiver
67
75
 
68
- # THE STRUCTURAL EQUALITY CHECK — `==` on an AST node (from the
76
+ # **The structural equality check** — `==` on an AST node (from the
69
77
  # `ast` gem `Node` this compiles down to) compares `type` and
70
78
  # `children` recursively and ignores source location, so
71
79
  # `hash[a] || hash[b]` matches even though the two `hash`
72
- # sub-nodes are two distinct node OBJECTS parsed from two
80
+ # sub-nodes are two distinct node objects parsed from two
73
81
  # different source ranges. A different receiver on each side
74
82
  # (`a[k] || b[k]`) fails this check and is correctly left alone.
75
83
  return unless lhs_receiver == rhs_receiver
@@ -1,47 +1,54 @@
1
1
  module RuboCop
2
2
  module Cop
3
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,
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
6
  # in the specific rename/permutation shape this codebase has
7
7
  # already lost real data to once.
8
8
  #
9
- # THE BUG THIS IS THE MECHANICAL FOLLOW-UP TO (M27,
9
+ # ## The bug this follows up on
10
+ #
11
+ # The bug this is the mechanical follow-up to (M27,
10
12
  # docs/audits/2026-08-10-main-bug-audit.md,
11
13
  # docs/audits/2026-08-11-bug-triage.md; fixed in
12
14
  # `Hecks::Ports::Persistence::Lineage#apply_renames`,
13
15
  # lib/hecks/ports/persistence/plugins/era/lineage.rb): a rename
14
16
  # rule set applied one rule at a time, `state[new_name] =
15
- # state.delete(old_name)` per rule, against the SAME hash it was
17
+ # state.delete(old_name)` per rule, against the same hash it was
16
18
  # reading from — so a swap (`rename :a, to: :b` alongside `rename
17
19
  # :b, to: :a`) on `{a: 1, b: 2}` collapsed to `{a: 1}`. The first
18
20
  # rule wrote `b: 1` (clobbering the real `b: 2`) before the second
19
21
  # rule ever got a chance to read the value that was supposed to
20
22
  # move to `:a` — sequential edits stepping on each other, not the
21
23
  # simultaneous permutation a rename set actually means. The same
22
- # shape loses data identically for ANY chain longer than one link
24
+ # shape loses data identically for any chain longer than one link
23
25
  # (`a->b->c->a`), not just a two-element swap.
24
26
  #
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
+ # ## The fix this cop points at
28
+ #
29
+ # Snapshot every old key's value
30
+ # first, delete all old keys, then write all new keys, so no
27
31
  # rename in the pass ever reads a key this same pass has already
28
32
  # written:
29
33
  #
30
- # snapshot = renames.filter_map { |old_name, new_name| [old_name, new_name, state[old_name]] if state.key?(old_name) }
34
+ # snapshot = renames.filter_map { |old_name, new_name| [old_name, new_name, state[old_name]]
35
+ # if state.key?(old_name) }
31
36
  # snapshot.each { |old_name, _new_name, _value| state.delete(old_name) }
32
37
  # snapshot.each { |_old_name, new_name, value| state[new_name] = value }
33
38
  #
34
- # WHAT THIS COP DOES NOT ATTEMPT — the general checklist item is
39
+ # ## What this cop does not attempt
40
+ #
41
+ # The general checklist item is
35
42
  # broader than any AST pattern can safely automate ("any method
36
43
  # that both reads and writes the same collection across a loop"
37
44
  # covers plenty of correct code too — accumulating into a result
38
45
  # hash, memoizing into a cache). This cop stays narrow and
39
- # precise: only the EXACT `recv[x] = recv.delete(y)` shape, same
46
+ # precise: only the exact `recv[x] = recv.delete(y)` shape, same
40
47
  # receiver both sides, inside something that iterates. A single
41
48
  # rename outside any loop is not this bug (nothing else can have
42
49
  # run first); `hash.store(...)` instead of `hash[...] =`, or
43
50
  # `hash.delete(...)` whose result is not immediately written back
44
- # into the SAME hash, are not this shape either and are left to
51
+ # into the same hash, are not this shape either and are left to
45
52
  # the broader code-review checklist item, not this cop.
46
53
  #
47
54
  # @example
@@ -51,7 +58,8 @@ module RuboCop
51
58
  # end
52
59
  #
53
60
  # # 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) }
61
+ # snapshot = renames.filter_map { |old_name, new_name| [old_name, new_name, state[old_name]]
62
+ # if state.key?(old_name) }
55
63
  # snapshot.each { |old_name, _new_name, _value| state.delete(old_name) }
56
64
  # snapshot.each { |_old_name, new_name, value| state[new_name] = value }
57
65
  class SequentialHashRenameInLoop < Base
@@ -82,6 +90,10 @@ module RuboCop
82
90
  (send $_recv :[]= _new_key (send $_recv2 :delete _old_key))
83
91
  PATTERN
84
92
 
93
+ # Flags a `recv[new] = recv.delete(old)` write inside a loop, same receiver both sides.
94
+ #
95
+ # @param node [RuboCop::AST::SendNode] the `[]=` send node being visited
96
+ # @return [void]
85
97
  def on_send(node)
86
98
  rename_write?(node) do |recv, recv2|
87
99
  next unless same_receiver?(recv, recv2)
@@ -1,7 +1,7 @@
1
1
  module RuboCop
2
2
  module Cop
3
3
  module Hecks
4
- # FLAGS PLAIN `@ivar` MUTATION inside a class this codebase already
4
+ # Flags plain `@ivar` mutation inside a class this codebase already
5
5
  # knows is a thread-shared singleton — one `Dispatcher`/`Registry`
6
6
  # instance lives for the life of a boot and is dispatched through
7
7
  # from every thread of a Puma worker pool (or any other multi-
@@ -9,29 +9,35 @@ module RuboCop
9
9
  # shared, mutable state with no per-thread or mutex-guarded
10
10
  # isolation at all.
11
11
  #
12
- # THIS IS THE MECHANICAL FOLLOW-UP to a real bug already fixed here
12
+ # ## The bug this follows up on
13
+ #
14
+ # This is the mechanical follow-up to a real bug already fixed here
13
15
  # (see `dispatcher.rb`'s own `#reenter` comment, and
14
- # `spec/runtime/dispatcher_spec.rb`): `@reaction_depth` used to be a
15
- # plain ivar on `Dispatcher`, so two threads' concurrent top-level
16
- # dispatches corrupted each other's view of "how deep into a
16
+ # `spec/runtime/dispatcher_spec.rb`): a plain `@reaction_depth` ivar on
17
+ # `Dispatcher` let two threads' concurrent top-level
18
+ # dispatches corrupt each other's view of "how deep into a
17
19
  # reaction cascade am I". The fix moved that one ivar to
18
20
  # `Thread.current[:hecks_reaction_depth]`. This cop exists so the
19
- # NEXT plain ivar someone adds to either class gets flagged before
21
+ # next plain ivar someone adds to either class gets flagged before
20
22
  # it becomes the next instance of the same bug, rather than after.
21
23
  #
22
- # SCOPED NARROWLY ON PURPOSE — by class name
24
+ # ## Scoped narrowly on purpose
25
+ #
26
+ # By class name
23
27
  # (`Hecks::Runtime::Dispatcher`/`Hecks::Runtime::Registry`), not by
24
28
  # blanket-flagging every ivar mutation in the codebase. Most classes
25
- # in this codebase are NOT shared across threads (a fresh value
29
+ # in this codebase are not shared across threads (a fresh value
26
30
  # object per call, a builder used once at boot) and ivar mutation
27
31
  # there is completely ordinary Ruby with no hazard behind it at
28
32
  # all — flagging it everywhere would be noise nobody trusts, the
29
33
  # same reasoning `.rubocop.yml`'s own header gives for every other
30
34
  # cop in this repo.
31
35
  #
32
- # WHAT COUNTS AS "PLAIN": `@ivar = ...`, `@ivar ||= ...`, `@ivar +=
36
+ # ## What counts as "plain"
37
+ #
38
+ # `@ivar = ...`, `@ivar ||= ...`, `@ivar +=
33
39
  # ...`, `@ivar << ...`, `@ivar[k] = v`. `initialize` is exempt — an
34
- # ivar being SET UP for the first time, before any other thread can
40
+ # ivar being set up for the first time, before any other thread can
35
41
  # possibly hold a reference to this object, is not the hazard (see
36
42
  # `Registry#initialize`'s own `@saga_mutex = Mutex.new`, which this
37
43
  # cop must not flag). `Thread.current[...]`-backed state and a
@@ -67,9 +73,13 @@ module RuboCop
67
73
 
68
74
  RESTRICT_ON_SEND = [:<<, :[]=].freeze
69
75
 
76
+ # Flags a plain `@ivar = ...` assignment inside a thread-shared class.
77
+ #
78
+ # @param node [RuboCop::AST::IvasgnNode] the ivar-assignment node being visited
79
+ # @return [void]
70
80
  def on_ivasgn(node)
71
- # A PLAIN `@x = 1` parses as `(ivasgn :@x (int 1))` — two
72
- # children. The commissioner also visits the BARE `(ivasgn :@x)`
81
+ # A plain `@x = 1` parses as `(ivasgn :@x (int 1))` — two
82
+ # children. The commissioner also visits the bare `(ivasgn :@x)`
73
83
  # node nested one level inside an `op_asgn`/`or_asgn` (`@x += 1`,
74
84
  # `@x ||= 1`) as its own `ivasgn` node with only one child — that
75
85
  # one is handled by `on_op_asgn`/`on_or_asgn` below instead, so
@@ -79,9 +89,14 @@ module RuboCop
79
89
  check(node, node.children.first)
80
90
  end
81
91
 
92
+ # Flags a plain `@ivar += ...` (or similar) compound assignment inside a
93
+ # thread-shared class.
94
+ #
95
+ # @param node [RuboCop::AST::OpAsgnNode] the compound-assignment node being visited
96
+ # @return [void]
82
97
  def on_op_asgn(node)
83
98
  # `@x += 1` parses as `(op_asgn (ivasgn :@x) :+ (int 1))` — the
84
- # target is an `ivasgn` node carrying JUST the name (no value
99
+ # target is an `ivasgn` node carrying just the name (no value
85
100
  # child, unlike a plain `@x = 1`), not an `ivar` node.
86
101
  ivar_node = node.children.first
87
102
  return unless ivar_node.is_a?(RuboCop::AST::Node) && ivar_node.ivasgn_type?
@@ -89,17 +104,26 @@ module RuboCop
89
104
  check(node, ivar_node.children.first)
90
105
  end
91
106
 
107
+ # Flags a plain `@ivar ||= ...` assignment inside a thread-shared class.
108
+ #
109
+ # @param node [RuboCop::AST::OrAsgnNode] the `||=` assignment node being visited
110
+ # @return [void]
92
111
  def on_or_asgn(node)
93
112
  on_op_asgn(node)
94
113
  end
95
114
 
115
+ # Flags a plain `@ivar << ...` or `@ivar[k] = v` mutation inside a thread-shared
116
+ # class.
117
+ #
118
+ # @param node [RuboCop::AST::SendNode] the `<<` or `[]=` send node being visited
119
+ # @return [void]
96
120
  def on_send(node)
97
121
  return unless RESTRICT_ON_SEND.include?(node.method_name)
98
122
 
99
123
  receiver = node.receiver
100
124
  return unless receiver
101
125
 
102
- # `@ivar << x` — the receiver IS the ivar.
126
+ # `@ivar << x` — the receiver is the ivar.
103
127
  # `@ivar[k] = v` — the receiver is a `send(@ivar, :[], k)`, whose
104
128
  # own receiver is the ivar (`node.receiver.receiver`).
105
129
  ivar_node = if receiver.ivar_type?
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hecks
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Young
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-09-13 00:00:00.000000000 Z
11
+ date: 2026-09-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: prism
@@ -55,6 +55,8 @@ files:
55
55
  - lib/hecks/adapters/driven/identity_registry.adapter
56
56
  - lib/hecks/adapters/driven/identity_registry.rb
57
57
  - lib/hecks/adapters/driven/in_memory_ordering.rb
58
+ - lib/hecks/adapters/driven/in_process_key_vault.adapter
59
+ - lib/hecks/adapters/driven/in_process_key_vault.rb
58
60
  - lib/hecks/adapters/driven/lambda.adapter
59
61
  - lib/hecks/adapters/driven/lambda.rb
60
62
  - lib/hecks/adapters/driven/lambda/client.rb
@@ -83,6 +85,8 @@ files:
83
85
  - lib/hecks/adapters/driven/sqlite/schema_builder.rb
84
86
  - lib/hecks/adapters/driven/system_clock.adapter
85
87
  - lib/hecks/adapters/driven/system_clock.rb
88
+ - lib/hecks/adapters/driven/tenant_provisioner.adapter
89
+ - lib/hecks/adapters/driven/tenant_provisioner.rb
86
90
  - lib/hecks/adapters/driving/github_webhook.rb
87
91
  - lib/hecks/behaviors.rb
88
92
  - lib/hecks/behaviors/dsl.rb
@@ -114,6 +118,7 @@ files:
114
118
  - lib/hecks/bluebook/behaviour/read_model.rb
115
119
  - lib/hecks/bluebook/behaviour/traits.rb
116
120
  - lib/hecks/bluebook/behaviour/value_object.rb
121
+ - lib/hecks/bluebook/capabilities.rb
117
122
  - lib/hecks/bluebook/chapter.rb
118
123
  - lib/hecks/bluebook/command.rb
119
124
  - lib/hecks/bluebook/domain_port.rb
@@ -125,6 +130,7 @@ files:
125
130
  - lib/hecks/bluebook/dsl/binding_proxy.rb
126
131
  - lib/hecks/bluebook/dsl/bluebook_builder.rb
127
132
  - lib/hecks/bluebook/dsl/bluebook_builder/validation.rb
133
+ - lib/hecks/bluebook/dsl/bootstrap_table.rb
128
134
  - lib/hecks/bluebook/dsl/command_builder.rb
129
135
  - lib/hecks/bluebook/dsl/const_shim.rb
130
136
  - lib/hecks/bluebook/dsl/domain_port_builder.rb
@@ -183,6 +189,7 @@ files:
183
189
  - lib/hecks/bluebook/value_object.rb
184
190
  - lib/hecks/codemod.rb
185
191
  - lib/hecks/construct.rb
192
+ - lib/hecks/corpus.rb
186
193
  - lib/hecks/deploy/bluebook/deploy.bluebook
187
194
  - lib/hecks/deploy/bluebook/deploy.hecksagon
188
195
  - lib/hecks/deploy/oidc.json
@@ -220,15 +227,22 @@ files:
220
227
  - lib/hecks/framework/bluebook/framework.hecksagon
221
228
  - lib/hecks/framework/bluebook/governance.bluebook
222
229
  - lib/hecks/framework/bluebook/identity.bluebook
230
+ - lib/hecks/framework/bluebook/privacy.bluebook
223
231
  - lib/hecks/framework/oidc.json
224
232
  - lib/hecks/freezer.rb
225
233
  - lib/hecks/fuzzing.rb
226
234
  - lib/hecks/fuzzing/bounded_exhaustive_expressions.rb
235
+ - lib/hecks/fuzzing/combination_miner.rb
227
236
  - lib/hecks/fuzzing/concurrent_dispatch.rb
237
+ - lib/hecks/fuzzing/coverage_campaign.rb
238
+ - lib/hecks/fuzzing/differential.rb
239
+ - lib/hecks/fuzzing/domain_generator.rb
228
240
  - lib/hecks/fuzzing/era_boundary.rb
229
241
  - lib/hecks/fuzzing/form_census.rb
242
+ - lib/hecks/fuzzing/generated_domain_check.rb
230
243
  - lib/hecks/fuzzing/invalid_value_generator.rb
231
244
  - lib/hecks/fuzzing/isolated_boot.rb
245
+ - lib/hecks/fuzzing/nondeterministic.rb
232
246
  - lib/hecks/fuzzing/persistence_parity.rb
233
247
  - lib/hecks/fuzzing/properties.rb
234
248
  - lib/hecks/fuzzing/properties/corrections.rb
@@ -238,8 +252,10 @@ files:
238
252
  - lib/hecks/fuzzing/properties/lifecycle_and_replay.rb
239
253
  - lib/hecks/fuzzing/properties/outbox.rb
240
254
  - lib/hecks/fuzzing/properties/querying.rb
255
+ - lib/hecks/fuzzing/qa_settings.rb
241
256
  - lib/hecks/fuzzing/replay.rb
242
257
  - lib/hecks/fuzzing/rotation_priority.rb
258
+ - lib/hecks/fuzzing/rust_gap_manifest.rb
243
259
  - lib/hecks/fuzzing/self_consistency.rb
244
260
  - lib/hecks/fuzzing/sequence_generator.rb
245
261
  - lib/hecks/fuzzing/sequence_generator/adversary.rb
@@ -247,6 +263,7 @@ files:
247
263
  - lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb
248
264
  - lib/hecks/fuzzing/sequence_generator/picker.rb
249
265
  - lib/hecks/fuzzing/sequence_generator/step_builder.rb
266
+ - lib/hecks/fuzzing/shrinker.rb
250
267
  - lib/hecks/fuzzing/structural_skips.rb
251
268
  - lib/hecks/fuzzing/sweep_depth.rb
252
269
  - lib/hecks/fuzzing/target_capabilities.rb
@@ -305,12 +322,15 @@ files:
305
322
  - lib/hecks/ports/identity_generation.rb
306
323
  - lib/hecks/ports/identity_resolution.port
307
324
  - lib/hecks/ports/identity_resolution.rb
325
+ - lib/hecks/ports/key_vault.port
326
+ - lib/hecks/ports/key_vault.rb
308
327
  - lib/hecks/ports/loading.port
309
328
  - lib/hecks/ports/loading.rb
310
329
  - lib/hecks/ports/persistence.port
311
330
  - lib/hecks/ports/persistence.rb
312
331
  - lib/hecks/ports/persistence/append_only.rb
313
332
  - lib/hecks/ports/persistence/binding_policy.rb
333
+ - lib/hecks/ports/persistence/codec_boundary.rb
314
334
  - lib/hecks/ports/persistence/execution.rb
315
335
  - lib/hecks/ports/persistence/null_saga_store.rb
316
336
  - lib/hecks/ports/persistence/plugin.rb
@@ -350,12 +370,14 @@ files:
350
370
  - lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb
351
371
  - lib/hecks/ports/persistence/remote_runtime.rb
352
372
  - lib/hecks/ports/persistence/repository_factory.rb
373
+ - lib/hecks/ports/persistence/state_codec.rb
353
374
  - lib/hecks/ports/projection.port
354
375
  - lib/hecks/ports/projection.rb
355
376
  - lib/hecks/ports/query.rb
356
377
  - lib/hecks/ports/query/in_memory.rb
357
378
  - lib/hecks/ports/query/ordering.rb
358
379
  - lib/hecks/projections.rb
380
+ - lib/hecks/projections/bootstrap_table.rb
359
381
  - lib/hecks/projections/diagrams.rb
360
382
  - lib/hecks/projections/glossary.rb
361
383
  - lib/hecks/projections/glossary/html.rb
@@ -371,6 +393,7 @@ files:
371
393
  - lib/hecks/projections/oidc.rb
372
394
  - lib/hecks/projections/parser_table.rb
373
395
  - lib/hecks/projections/reference.rb
396
+ - lib/hecks/projections/rust_vocabulary.rb
374
397
  - lib/hecks/projections/shape.rb
375
398
  - lib/hecks/projections/statements.rb
376
399
  - lib/hecks/projections/vocabulary.rb
@@ -426,6 +449,7 @@ files:
426
449
  - lib/hecks/runtime/identity.rb
427
450
  - lib/hecks/runtime/instance.rb
428
451
  - lib/hecks/runtime/interpreting.rb
452
+ - lib/hecks/runtime/invocation.rb
429
453
  - lib/hecks/runtime/loader.rb
430
454
  - lib/hecks/runtime/outbox.rb
431
455
  - lib/hecks/runtime/policy_interpreter.rb
@@ -452,6 +476,8 @@ files:
452
476
  - lib/hecks/runtime/value/entity_list_coercion.rb
453
477
  - lib/hecks/runtime/value/invariant_violation.rb
454
478
  - lib/hecks/storehouse.rb
479
+ - lib/hecks/tenancy/bluebook/tenancy.bluebook
480
+ - lib/hecks/tenancy/bluebook/tenancy.hecksagon
455
481
  - lib/hecks/version.rb
456
482
  - lib/hecks/vocabulary.rb
457
483
  - lib/rubocop/cop/hecks/fallback_hash_lookup.rb