hecks 1.3.0 → 1.4.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 (355) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hecks/adapters/driven/claude_code.rb +7 -7
  3. data/lib/hecks/adapters/driven/d1.rb +187 -23
  4. data/lib/hecks/adapters/driven/folder.rb +10 -10
  5. data/lib/hecks/adapters/driven/google_authentication.rb +8 -8
  6. data/lib/hecks/adapters/driven/governance_authorization.rb +31 -13
  7. data/lib/hecks/adapters/driven/heki/journal.rb +5 -4
  8. data/lib/hecks/adapters/driven/heki/saga_store.rb +5 -5
  9. data/lib/hecks/adapters/driven/heki.rb +13 -7
  10. data/lib/hecks/adapters/driven/identity_registry.rb +2 -2
  11. data/lib/hecks/adapters/driven/in_memory_ordering.rb +3 -3
  12. data/lib/hecks/adapters/driven/lambda/client.rb +34 -9
  13. data/lib/hecks/adapters/driven/lambda.rb +39 -33
  14. data/lib/hecks/adapters/driven/local_storage.rb +17 -10
  15. data/lib/hecks/adapters/driven/memory.rb +205 -9
  16. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +1 -1
  17. data/lib/hecks/adapters/driven/postgres/codec.rb +27 -11
  18. data/lib/hecks/adapters/driven/postgres/outbox.rb +40 -2
  19. data/lib/hecks/adapters/driven/postgres/reconnect.rb +23 -7
  20. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +14 -14
  21. data/lib/hecks/adapters/driven/postgres.rb +175 -28
  22. data/lib/hecks/adapters/driven/prism.rb +4 -4
  23. data/lib/hecks/adapters/driven/sql_query_builder.rb +34 -22
  24. data/lib/hecks/adapters/driven/sqlite/codec.rb +38 -10
  25. data/lib/hecks/adapters/driven/sqlite/projection.rb +60 -32
  26. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +12 -12
  27. data/lib/hecks/adapters/driven/sqlite.rb +181 -21
  28. data/lib/hecks/adapters/driven.rb +4 -4
  29. data/lib/hecks/adapters/driving/github_webhook.rb +18 -18
  30. data/lib/hecks/behaviors/dsl.rb +2 -2
  31. data/lib/hecks/behaviors/expectations.rb +23 -23
  32. data/lib/hecks/behaviors/ir.rb +1 -1
  33. data/lib/hecks/behaviors/rspec.rb +1 -1
  34. data/lib/hecks/behaviors/runner.rb +2 -2
  35. data/lib/hecks/behaviors.rb +1 -1
  36. data/lib/hecks/bluebook/aggregate.rb +13 -13
  37. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +10 -10
  38. data/lib/hecks/bluebook/assembly/build.rb +1 -1
  39. data/lib/hecks/bluebook/assembly/contract.rb +39 -16
  40. data/lib/hecks/bluebook/assembly/contracts.rb +40 -38
  41. data/lib/hecks/bluebook/assembly/marks.rb +18 -18
  42. data/lib/hecks/bluebook/assembly/specializer.rb +19 -21
  43. data/lib/hecks/bluebook/assembly.rb +9 -9
  44. data/lib/hecks/bluebook/attribute.rb +9 -9
  45. data/lib/hecks/bluebook/behaviour/aggregate.rb +11 -11
  46. data/lib/hecks/bluebook/behaviour/attribute.rb +5 -5
  47. data/lib/hecks/bluebook/behaviour/chapter.rb +23 -5
  48. data/lib/hecks/bluebook/behaviour/command.rb +23 -23
  49. data/lib/hecks/bluebook/behaviour/domain_port.rb +7 -7
  50. data/lib/hecks/bluebook/behaviour/entity.rb +8 -8
  51. data/lib/hecks/bluebook/behaviour/hexagon.rb +4 -4
  52. data/lib/hecks/bluebook/behaviour/lifecycle.rb +5 -5
  53. data/lib/hecks/bluebook/behaviour/policy.rb +12 -12
  54. data/lib/hecks/bluebook/behaviour/process_manager.rb +7 -7
  55. data/lib/hecks/bluebook/behaviour/query.rb +1 -1
  56. data/lib/hecks/bluebook/behaviour/read_model.rb +8 -8
  57. data/lib/hecks/bluebook/behaviour/traits.rb +12 -12
  58. data/lib/hecks/bluebook/behaviour/value_object.rb +6 -6
  59. data/lib/hecks/bluebook/capabilities.rb +27 -0
  60. data/lib/hecks/bluebook/chapter.rb +28 -9
  61. data/lib/hecks/bluebook/command.rb +12 -12
  62. data/lib/hecks/bluebook/domain_port.rb +9 -9
  63. data/lib/hecks/bluebook/dsl/adapter_builder.rb +24 -0
  64. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +49 -49
  65. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +96 -96
  66. data/lib/hecks/bluebook/dsl/attribute_collector.rb +41 -41
  67. data/lib/hecks/bluebook/dsl/binding_proxy.rb +22 -2
  68. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +111 -74
  69. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +48 -30
  70. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
  71. data/lib/hecks/bluebook/dsl/command_builder.rb +103 -103
  72. data/lib/hecks/bluebook/dsl/const_shim.rb +46 -15
  73. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +90 -25
  74. data/lib/hecks/bluebook/dsl/entity_builder.rb +56 -56
  75. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
  76. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +89 -30
  77. data/lib/hecks/bluebook/dsl/identity_declaration.rb +17 -17
  78. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
  79. data/lib/hecks/bluebook/dsl/policy_builder.rb +30 -21
  80. data/lib/hecks/bluebook/dsl/port_builder.rb +38 -7
  81. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +56 -22
  82. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +35 -35
  83. data/lib/hecks/bluebook/dsl/query_builder.rb +5 -5
  84. data/lib/hecks/bluebook/dsl/read_model_builder.rb +34 -34
  85. data/lib/hecks/bluebook/dsl/rule_reference.rb +41 -39
  86. data/lib/hecks/bluebook/dsl/translation_builder.rb +9 -9
  87. data/lib/hecks/bluebook/dsl/value_object_builder.rb +16 -16
  88. data/lib/hecks/bluebook/dsl/word_gate.rb +59 -53
  89. data/lib/hecks/bluebook/dsl/world_builder.rb +51 -8
  90. data/lib/hecks/bluebook/entity.rb +11 -11
  91. data/lib/hecks/bluebook/expression/ast_json.rb +20 -20
  92. data/lib/hecks/bluebook/expression/ast_reader.rb +3 -3
  93. data/lib/hecks/bluebook/expression/canonical_form.rb +9 -9
  94. data/lib/hecks/bluebook/expression/evaluator.rb +18 -18
  95. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +18 -18
  96. data/lib/hecks/bluebook/expression/resolver.rb +60 -62
  97. data/lib/hecks/bluebook/hexagon.rb +1 -1
  98. data/lib/hecks/bluebook/lifecycle.rb +1 -1
  99. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -1
  100. data/lib/hecks/bluebook/meta_validator/judge.rb +101 -87
  101. data/lib/hecks/bluebook/meta_validator/plan.rb +39 -39
  102. data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -2
  103. data/lib/hecks/bluebook/meta_validator/readings.rb +43 -43
  104. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +44 -37
  105. data/lib/hecks/bluebook/meta_validator/shapes.rb +25 -21
  106. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +145 -31
  107. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +6 -6
  108. data/lib/hecks/bluebook/meta_validator/world_judge.rb +5 -5
  109. data/lib/hecks/bluebook/meta_validator.rb +70 -70
  110. data/lib/hecks/bluebook/model_check.rb +170 -84
  111. data/lib/hecks/bluebook/pattern_subset.rb +9 -9
  112. data/lib/hecks/bluebook/policy.rb +15 -13
  113. data/lib/hecks/bluebook/process_manager.rb +14 -14
  114. data/lib/hecks/bluebook/project_register.rb +6 -6
  115. data/lib/hecks/bluebook/query.rb +4 -4
  116. data/lib/hecks/bluebook/read_model.rb +14 -14
  117. data/lib/hecks/bluebook/reference.rb +8 -8
  118. data/lib/hecks/bluebook/smoke_test.rb +19 -19
  119. data/lib/hecks/bluebook/synthesizer.rb +12 -12
  120. data/lib/hecks/bluebook/translation.rb +4 -4
  121. data/lib/hecks/bluebook/value_object.rb +6 -6
  122. data/lib/hecks/bluebook.rb +2 -2
  123. data/lib/hecks/codemod/legacy_dispatch_args.rb +299 -0
  124. data/lib/hecks/codemod/legacy_dispatch_recorder.rb +186 -0
  125. data/lib/hecks/codemod.rb +36 -35
  126. data/lib/hecks/construct.rb +6 -6
  127. data/lib/hecks/corpus.rb +317 -0
  128. data/lib/hecks/deprecation.rb +95 -0
  129. data/lib/hecks/doc/reference.rb +19 -19
  130. data/lib/hecks/embryonaut_bluebook.rb +11 -11
  131. data/lib/hecks/facade/cli_door.rb +69 -10
  132. data/lib/hecks/facade/cli_runner.rb +105 -24
  133. data/lib/hecks/facade/command_request.rb +23 -0
  134. data/lib/hecks/facade/handle.rb +79 -32
  135. data/lib/hecks/facade/json_door.rb +106 -25
  136. data/lib/hecks/facade/surface/aggregate_door.rb +42 -27
  137. data/lib/hecks/facade/surface/chapter.rb +26 -17
  138. data/lib/hecks/facade/surface.rb +16 -3
  139. data/lib/hecks/facade.rb +15 -4
  140. data/lib/hecks/forms/app.rb +46 -30
  141. data/lib/hecks/forms/command_form_renderer.rb +70 -9
  142. data/lib/hecks/forms/field_renderer.rb +142 -6
  143. data/lib/hecks/forms/field_shape.rb +180 -19
  144. data/lib/hecks/forms/html.rb +51 -7
  145. data/lib/hecks/forms/index_renderer.rb +14 -2
  146. data/lib/hecks/forms/params.rb +120 -23
  147. data/lib/hecks/forms/port_argument.rb +2 -2
  148. data/lib/hecks/forms/query_form_renderer.rb +2 -2
  149. data/lib/hecks/forms/record_renderer.rb +2 -2
  150. data/lib/hecks/forms/record_table.rb +1 -1
  151. data/lib/hecks/forms/value_object_shape.rb +3 -3
  152. data/lib/hecks/forms.rb +24 -4
  153. data/lib/hecks/fqn.rb +1 -1
  154. data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
  155. data/lib/hecks/framework.rb +48 -17
  156. data/lib/hecks/freezer.rb +11 -11
  157. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +90 -90
  158. data/lib/hecks/fuzzing/combination_miner.rb +119 -0
  159. data/lib/hecks/fuzzing/concurrent_dispatch.rb +140 -45
  160. data/lib/hecks/fuzzing/coverage_campaign.rb +118 -0
  161. data/lib/hecks/fuzzing/differential.rb +158 -0
  162. data/lib/hecks/fuzzing/domain_generator.rb +694 -0
  163. data/lib/hecks/fuzzing/era_boundary.rb +37 -18
  164. data/lib/hecks/fuzzing/form_census.rb +35 -20
  165. data/lib/hecks/fuzzing/generated_domain_check.rb +95 -0
  166. data/lib/hecks/fuzzing/invalid_value_generator.rb +6 -6
  167. data/lib/hecks/fuzzing/isolated_boot.rb +62 -62
  168. data/lib/hecks/fuzzing/nondeterministic.rb +67 -0
  169. data/lib/hecks/fuzzing/persistence_parity.rb +19 -21
  170. data/lib/hecks/fuzzing/properties/corrections.rb +9 -9
  171. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +154 -49
  172. data/lib/hecks/fuzzing/properties/guards.rb +42 -42
  173. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +32 -32
  174. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +22 -35
  175. data/lib/hecks/fuzzing/properties/outbox.rb +24 -24
  176. data/lib/hecks/fuzzing/properties/querying.rb +17 -17
  177. data/lib/hecks/fuzzing/properties.rb +64 -40
  178. data/lib/hecks/fuzzing/qa_settings.rb +152 -0
  179. data/lib/hecks/fuzzing/replay.rb +114 -114
  180. data/lib/hecks/fuzzing/rotation_priority.rb +10 -10
  181. data/lib/hecks/fuzzing/rust_gap_manifest.rb +113 -0
  182. data/lib/hecks/fuzzing/self_consistency.rb +141 -112
  183. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +35 -35
  184. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +18 -11
  185. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +13 -12
  186. data/lib/hecks/fuzzing/sequence_generator/picker.rb +21 -12
  187. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +58 -31
  188. data/lib/hecks/fuzzing/sequence_generator.rb +111 -23
  189. data/lib/hecks/fuzzing/shrinker.rb +197 -0
  190. data/lib/hecks/fuzzing/structural_skips.rb +23 -130
  191. data/lib/hecks/fuzzing/sweep_depth.rb +4 -4
  192. data/lib/hecks/fuzzing/target_capabilities.rb +49 -18
  193. data/lib/hecks/fuzzing/value_generator.rb +14 -14
  194. data/lib/hecks/fuzzing.rb +5 -0
  195. data/lib/hecks/grammar/evolve.rb +10 -10
  196. data/lib/hecks/grammar.rb +7 -7
  197. data/lib/hecks/ir.rb +13 -13
  198. data/lib/hecks/language/bluebook/bluebook.bluebook +41 -0
  199. data/lib/hecks/language/bluebook/policy.bluebook +11 -1
  200. data/lib/hecks/language/bluebook/vocabulary.bluebook +348 -13
  201. data/lib/hecks/language/oidc.json +5 -0
  202. data/lib/hecks/literal.rb +9 -9
  203. data/lib/hecks/naming.rb +25 -25
  204. data/lib/hecks/ports/access_control.rb +58 -2
  205. data/lib/hecks/ports/agent/answers.rb +83 -6
  206. data/lib/hecks/ports/agent.rb +119 -35
  207. data/lib/hecks/ports/authentication.rb +44 -4
  208. data/lib/hecks/ports/authorization.rb +53 -11
  209. data/lib/hecks/ports/clock.rb +42 -23
  210. data/lib/hecks/ports/extraction.rb +16 -0
  211. data/lib/hecks/ports/identity_assignment.rb +24 -2
  212. data/lib/hecks/ports/identity_generation.rb +17 -3
  213. data/lib/hecks/ports/identity_resolution.rb +18 -1
  214. data/lib/hecks/ports/loading.rb +4 -0
  215. data/lib/hecks/ports/persistence/append_only.rb +172 -8
  216. data/lib/hecks/ports/persistence/binding_policy.rb +34 -0
  217. data/lib/hecks/ports/persistence/codec_boundary.rb +178 -0
  218. data/lib/hecks/ports/persistence/execution.rb +4 -0
  219. data/lib/hecks/ports/persistence/null_saga_store.rb +12 -1
  220. data/lib/hecks/ports/persistence/plugin.rb +42 -4
  221. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +218 -25
  222. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +77 -9
  223. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +81 -24
  224. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +29 -18
  225. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +144 -60
  226. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +103 -8
  227. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +98 -23
  228. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +282 -109
  229. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +63 -25
  230. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +118 -66
  231. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +51 -28
  232. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +28 -5
  233. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +25 -12
  234. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +129 -34
  235. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +51 -6
  236. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +33 -9
  237. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +16 -0
  238. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +57 -4
  239. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +25 -2
  240. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +314 -90
  241. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +68 -10
  242. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +9 -3
  243. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +9 -2
  244. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +42 -8
  245. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +16 -1
  246. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +36 -5
  247. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +23 -3
  248. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +18 -19
  249. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +5 -5
  250. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +1 -1
  251. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -2
  252. data/lib/hecks/ports/persistence/plugins/era.rb +12 -2
  253. data/lib/hecks/ports/persistence/remote_runtime.rb +9 -2
  254. data/lib/hecks/ports/persistence/repository_factory.rb +23 -3
  255. data/lib/hecks/ports/persistence/state_codec.rb +319 -0
  256. data/lib/hecks/ports/persistence.rb +36 -1
  257. data/lib/hecks/ports/projection.rb +61 -7
  258. data/lib/hecks/ports/query/in_memory.rb +3 -3
  259. data/lib/hecks/ports/query/ordering.rb +6 -6
  260. data/lib/hecks/ports/query.rb +35 -0
  261. data/lib/hecks/projections/bootstrap_table.rb +112 -0
  262. data/lib/hecks/projections/diagrams.rb +75 -75
  263. data/lib/hecks/projections/glossary/html.rb +3 -3
  264. data/lib/hecks/projections/glossary/markdown.rb +3 -3
  265. data/lib/hecks/projections/glossary/mermaid.rb +1 -1
  266. data/lib/hecks/projections/glossary/sections.rb +1 -1
  267. data/lib/hecks/projections/glossary/sentences.rb +5 -5
  268. data/lib/hecks/projections/glossary.rb +15 -15
  269. data/lib/hecks/projections/ir.rb +1 -1
  270. data/lib/hecks/projections/model/deviations.rb +18 -17
  271. data/lib/hecks/projections/model.rb +25 -21
  272. data/lib/hecks/projections/oidc.rb +7 -7
  273. data/lib/hecks/projections/parser_table.rb +5 -5
  274. data/lib/hecks/projections/reference.rb +3 -3
  275. data/lib/hecks/projections/rust_vocabulary.rb +443 -0
  276. data/lib/hecks/projections/shape.rb +2 -2
  277. data/lib/hecks/projections/statements.rb +11 -11
  278. data/lib/hecks/projections/vocabulary.rb +9 -9
  279. data/lib/hecks/projections.rb +5 -3
  280. data/lib/hecks/projector/cli_projector.rb +29 -29
  281. data/lib/hecks/projector/docs_projector.rb +13 -13
  282. data/lib/hecks/projector/exporter.rb +42 -21
  283. data/lib/hecks/projector/ir_projector.rb +1 -1
  284. data/lib/hecks/projector/narrate_projector.rb +11 -11
  285. data/lib/hecks/projector/target.rb +13 -13
  286. data/lib/hecks/projector.rb +15 -15
  287. data/lib/hecks/query_ir.rb +47 -47
  288. data/lib/hecks/query_specification/common/comparators.rb +19 -3
  289. data/lib/hecks/query_specification/common/comparison.rb +109 -27
  290. data/lib/hecks/query_specification/common/dsl.rb +65 -9
  291. data/lib/hecks/query_specification/common/null_policy.rb +57 -13
  292. data/lib/hecks/query_specification/common/null_semantics.rb +4 -0
  293. data/lib/hecks/query_specification/common/options.rb +25 -0
  294. data/lib/hecks/query_specification/field_path.rb +69 -15
  295. data/lib/hecks/query_specification/hop_path.rb +57 -20
  296. data/lib/hecks/query_specification/read_model/specification.rb +4 -0
  297. data/lib/hecks/rendering.rb +3 -3
  298. data/lib/hecks/router/namespace_installer.rb +3 -3
  299. data/lib/hecks/router.rb +1 -1
  300. data/lib/hecks/runtime/aggregate_lock.rb +11 -11
  301. data/lib/hecks/runtime/boot_gates.rb +3 -3
  302. data/lib/hecks/runtime/caller.rb +8 -8
  303. data/lib/hecks/runtime/capability_graph.rb +2 -2
  304. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +22 -25
  305. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +42 -63
  306. data/lib/hecks/runtime/command_interpreter.rb +159 -102
  307. data/lib/hecks/runtime/command_rules/admissibility.rb +69 -69
  308. data/lib/hecks/runtime/command_rules/arithmetic.rb +137 -91
  309. data/lib/hecks/runtime/command_rules/authorization.rb +38 -17
  310. data/lib/hecks/runtime/command_rules/emission.rb +18 -1
  311. data/lib/hecks/runtime/command_rules/references.rb +35 -35
  312. data/lib/hecks/runtime/command_rules.rb +3 -0
  313. data/lib/hecks/runtime/dependency_planning.rb +11 -11
  314. data/lib/hecks/runtime/dispatcher.rb +283 -90
  315. data/lib/hecks/runtime/entity_element.rb +151 -67
  316. data/lib/hecks/runtime/entity_interpreter.rb +108 -82
  317. data/lib/hecks/runtime/errors.rb +19 -19
  318. data/lib/hecks/runtime/event.rb +6 -6
  319. data/lib/hecks/runtime/identity.rb +22 -22
  320. data/lib/hecks/runtime/instance.rb +39 -14
  321. data/lib/hecks/runtime/interpreting.rb +12 -12
  322. data/lib/hecks/runtime/invocation.rb +276 -0
  323. data/lib/hecks/runtime/loader.rb +14 -14
  324. data/lib/hecks/runtime/outbox.rb +23 -23
  325. data/lib/hecks/runtime/policy_interpreter.rb +54 -54
  326. data/lib/hecks/runtime/port_operation_interpreter.rb +22 -19
  327. data/lib/hecks/runtime/query_interpreter.rb +55 -64
  328. data/lib/hecks/runtime/reaction_invocation.rb +20 -20
  329. data/lib/hecks/runtime/read_model_interpreter.rb +40 -40
  330. data/lib/hecks/runtime/rebuild_sweep.rb +4 -4
  331. data/lib/hecks/runtime/reference_hop.rb +6 -6
  332. data/lib/hecks/runtime/refusal_wording.rb +92 -115
  333. data/lib/hecks/runtime/registry/saga_persistence.rb +21 -21
  334. data/lib/hecks/runtime/registry/verification.rb +36 -26
  335. data/lib/hecks/runtime/registry.rb +45 -28
  336. data/lib/hecks/runtime/remote_dispatcher.rb +38 -23
  337. data/lib/hecks/runtime/routing.rb +10 -153
  338. data/lib/hecks/runtime/saga_interpreter/correlation.rb +17 -17
  339. data/lib/hecks/runtime/saga_interpreter.rb +64 -64
  340. data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
  341. data/lib/hecks/runtime/tenant_check.rb +9 -9
  342. data/lib/hecks/runtime/tenant_scope.rb +5 -5
  343. data/lib/hecks/runtime/value/admission.rb +75 -30
  344. data/lib/hecks/runtime/value/coercion.rb +318 -127
  345. data/lib/hecks/runtime/value/entity_list_coercion.rb +34 -34
  346. data/lib/hecks/runtime/value.rb +21 -21
  347. data/lib/hecks/runtime.rb +7 -7
  348. data/lib/hecks/storehouse.rb +64 -64
  349. data/lib/hecks/version.rb +3 -3
  350. data/lib/hecks/vocabulary.rb +205 -4
  351. data/lib/hecks.rb +13 -11
  352. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +11 -11
  353. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +14 -12
  354. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +11 -11
  355. metadata +22 -2
@@ -8,18 +8,18 @@ require_relative "projector"
8
8
  require_relative "runtime/errors"
9
9
 
10
10
  module Hecks
11
- # THE BUS, NOT A DOOR — `docs/hecks-survey-what-we-wish-we-had.md` and
11
+ # **The bus, not a door** — `docs/hecks-survey-what-we-wish-we-had.md` and
12
12
  # `docs/future-features.md` both name the sibling project's own
13
13
  # "Storehouse" the single highest-priority gap this repo had: "no
14
- # per-command tool... the bluebook IS the contract, the [door] just
15
- # projects it." This module IS that bus, borrowing its name too — the
14
+ # per-command tool... the bluebook is the contract, the [door] just
15
+ # projects it." This module is that bus, borrowing its name too — the
16
16
  # dispatch/query/state/... surface below is a pure function of a
17
17
  # `Runtime::Dispatcher` plus plain Ruby arguments, no IO, no protocol
18
- # awareness. `bin/hecks_mcp_door` is ONE projection of it — MCP over
18
+ # awareness. `bin/hecks_mcp_door` is one projection of it — MCP over
19
19
  # stdio — not the whole thing; a plain CLI door, an HTTP door, a second
20
20
  # transport of any shape, would sit beside it on the exact same bus,
21
21
  # sharing the same audit log and caller-identity handling, without
22
- # ever needing to speak MCP. (This module used to BE named `McpDoor`
22
+ # ever needing to speak MCP. (This module used to be named `McpDoor`
23
23
  # and live under `Facade` — that conflated the bus with its one built
24
24
  # transport; this file is the rename, not a rewrite.)
25
25
  #
@@ -30,21 +30,21 @@ module Hecks
30
30
  # dispatch — issue a command (dry_run: preview, steps: batch)
31
31
  # query — ask a question
32
32
  # state — read what is actually stored, no verb involved
33
- # events — what HAPPENED, with payloads, to one record — not just its
34
- # current state; events THIS BUS witnessed, sourced from
33
+ # events — what happened, with payloads, to one record — not just its
34
+ # current state; events this bus witnessed, sourced from
35
35
  # its own audit log, not a full event-sourcing replay
36
36
  # history — the full append-only journal, not just current state
37
37
  # behaviors — run a domain's hand-curated `.behaviors` examples
38
38
  # follow — tail this bus's own dispatch/query/state audit log
39
39
  #
40
- # `dispatch`/`query`/`state` EACH TAKE A `summary` — the survey's "every
40
+ # `dispatch`/`query`/`state` each take a `summary` — the survey's "every
41
41
  # audit row carries human intent for free" — and `dispatch`/`query`
42
42
  # additionally take an optional `source:` (`SOURCE_TAGS`, the survey's
43
- # `SourceTag`: who is calling) AND an optional `role:`/`actor_id:` —
43
+ # `SourceTag`: who is calling) and an optional `role:`/`actor_id:` —
44
44
  # a real caller identity, bound for the call's duration via `Hecks.
45
45
  # as_caller`, checked against a `role`-gated command's own declared
46
46
  # role (`CommandRules::Authorization`) rather than merely documented by
47
- # `describe`. `dispatch` REQUIRES it for any command that declares a
47
+ # `describe`. `dispatch` requires it for any command that declares a
48
48
  # role — `require_caller_for_role_gated!` refuses an unbound dispatch
49
49
  # against one rather than silently running it unchecked; `query`'s own
50
50
  # authorization runs on a separate mechanism (`Runtime::TenantScope`)
@@ -54,21 +54,21 @@ module Hecks
54
54
  # header for what that does and does not guarantee. Every call through
55
55
  # those three, plus a dry run, is appended to a per-domain JSONL audit
56
56
  # log (`record!`) `follow` tails back — a record of what the caller
57
- # SAID it was, not independently verified identity. `catalog`/
57
+ # said it was, not independently verified identity. `catalog`/
58
58
  # `describe`/`validate`/`domains`/`history`/`behaviors`/`events` need
59
59
  # neither — they change nothing and commit nothing to any log.
60
60
  #
61
- # A CALLER HANDS IN AN ALREADY-BOOTED `runtime`, the same division of
61
+ # A caller hands in an already-booted `runtime`, the same division of
62
62
  # labor `Facade::CliRunner` already keeps against `bin/run`: booting a
63
63
  # domain from a path is IO the calling `bin/` script owns, this stays a
64
64
  # pure function of a `Runtime::Dispatcher` plus plain Ruby arguments —
65
65
  # no different from `CliRunner.call(runtime:, argv:)` itself.
66
66
  # `validate` and `domains` are the two exceptions: `validate`'s whole
67
67
  # job is to attempt the boot and report whether it survived, so it
68
- # takes the domain PATH instead and boots it; `domains` has no
68
+ # takes the domain path instead and boots it; `domains` has no
69
69
  # domain to be handed one of yet, that's what it's answering.
70
70
  #
71
- # NO NEW VOCABULARY otherwise. Every method here composes doors that
71
+ # No new vocabulary otherwise. Every method here composes doors that
72
72
  # already exist — `Projector.call(:cli, ...)` for verb/question alias
73
73
  # resolution (the identical table `CliRunner` itself resolves against),
74
74
  # `Projector.call(:docs, ...)` for `describe`, `Facade::JsonDoor` for
@@ -82,21 +82,21 @@ module Hecks
82
82
  module Storehouse
83
83
  module_function
84
84
 
85
- # THE SAME CLOSED SET `docs/hecks-survey-what-we-wish-we-had.md`'s
86
- # `SourceTag` names — WHO dispatched, not WHAT. Optional: a caller
85
+ # The same closed set `docs/hecks-survey-what-we-wish-we-had.md`'s
86
+ # `SourceTag` names — who dispatched, not what. Optional: a caller
87
87
  # that omits it gets `source: nil` recorded, honestly, rather than a
88
88
  # guessed default.
89
89
  SOURCE_TAGS = %w[process-manager operator hook sidequest-agent cascade daemon].freeze
90
90
 
91
- # THE BUS'S OWN AUDIT TRAIL — a JSONL file per domain, one line per
91
+ # **The bus's own audit trail** — a JSONL file per domain, one line per
92
92
  # `dispatch`/`query`/`state`/dry-run call, `follow` tails it back.
93
- # `tmp/`, not the domain's own directory: this is the BUS's record
93
+ # `tmp/`, not the domain's own directory: this is the bus's record
94
94
  # of what was asked of it, not part of the domain's own persisted
95
95
  # state, and `tmp/` is already gitignored for exactly this kind of
96
96
  # local, disposable-but-useful-while-it-lasts file.
97
97
  LOG_ROOT = File.expand_path("../../tmp/storehouse", __dir__)
98
98
 
99
- # THE ROOT EVERY `domain:`/`under:` MUST RESOLVE UNDER — the project
99
+ # The root every `domain:`/`under:` must resolve under — the project
100
100
  # directory by default, `HECKS_STOREHOUSE_ROOT` to widen or move it.
101
101
  # `Hecks.boot` `Kernel.load`s the `.hecksagon`/`.bluebook`/`.world`
102
102
  # files a domain path resolves to, and those are Ruby, not a data
@@ -108,7 +108,7 @@ module Hecks
108
108
  # project tree.
109
109
  BOOT_ROOT = File.expand_path(ENV["HECKS_STOREHOUSE_ROOT"] || File.expand_path("../..", __dir__))
110
110
 
111
- # REFUSED, NOT SILENTLY CLAMPED — a path outside `BOOT_ROOT` is either
111
+ # **Refused, not silently clamped** — a path outside `BOOT_ROOT` is either
112
112
  # an honest mistake (a relative path typed against the wrong cwd) or
113
113
  # the exact thing this check exists to catch, and both deserve the
114
114
  # same clear refusal rather than a silent rewrite to something the
@@ -124,7 +124,7 @@ module Hecks
124
124
 
125
125
  # ── shared resolution helpers ────────────────────────────────────
126
126
 
127
- # THE ONE BLUEBOOK A DOMAIN DIRECTORY BOOTS. Every `bin/*` script that
127
+ # The one bluebook a domain directory boots. Every `bin/*` script that
128
128
  # projects a whole-domain CLI or doc set makes this same assumption
129
129
  # (`Facade::CliRunner#call`'s own `bluebook = runtime.registry.
130
130
  # bluebooks.values.first`) — one `.hecksagon` names one chapter.
@@ -139,7 +139,7 @@ module Hecks
139
139
  "known: #{bluebook.aggregates.map(&:hecks_name).sort.join(', ')}"
140
140
  end
141
141
 
142
- # THE SAME ALIAS TABLE `CliRunner` RESOLVES A TYPED WORD AGAINST — a
142
+ # The same alias table `CliRunner` resolves a typed word against — a
143
143
  # short name when it's unambiguous, the qualified `Aggregate.Verb`
144
144
  # form always. Shared here so `dispatch` and `query` (and their error
145
145
  # messages) never drift from what a human typing `bin/run` sees.
@@ -166,7 +166,7 @@ module Hecks
166
166
  raise Runtime::TypeMismatch, "source: #{source.inspect} is not one of #{SOURCE_TAGS.join(', ')}"
167
167
  end
168
168
 
169
- # `actor_id` NAMES WHO, `role` NAMES WHAT THEY HOLD — `Hecks.
169
+ # `actor_id` names who, `role` names what they hold — `Hecks.
170
170
  # as_caller` requires the latter always, the former is additive
171
171
  # (`Runtime::Caller::Current`'s own shape). An `actor_id` with no
172
172
  # `role` would silently do nothing rather than bind a real caller,
@@ -178,18 +178,18 @@ module Hecks
178
178
  raise Runtime::TypeMismatch, "actor_id: requires role: too — a caller names WHO through WHICH role they hold"
179
179
  end
180
180
 
181
- # BOUND FOR THE DURATION OF ONE CALL, THEN GONE — `Hecks.as_caller`
181
+ # Bound for the duration of one call, then gone — `Hecks.as_caller`
182
182
  # is itself a `Thread.current`-scoped `ensure`-guarded block, so
183
183
  # nothing here needs its own cleanup. `role: nil` yields unbound —
184
184
  # for `query`, exactly as before: `CommandRules::Authorization#
185
- # refuse_role_mismatch` is OPT-IN on the domain side (`return unless
185
+ # refuse_role_mismatch` is opt-in on the domain side (`return unless
186
186
  # caller`), and query authorization runs on a wholly separate
187
187
  # mechanism (`authorize policy, tenant: :field`, checked against an
188
188
  # explicit `tenant:` argument — see `Runtime::TenantScope`), so
189
- # binding a caller around a query has no effect on it TODAY; it is
189
+ # binding a caller around a query has no effect on it today; it is
190
190
  # still accepted here, for symmetry and for the audit log, against
191
191
  # the day a read model does check `Caller.current`. For `dispatch`,
192
- # `require_caller_for_role_gated!` (below) now refuses BEFORE this
192
+ # `require_caller_for_role_gated!` (below) now refuses before this
193
193
  # is ever reached when the command declares a role and no caller is
194
194
  # bound — so an unbound `dispatch` here means either the command
195
195
  # declares no role at all, or a caller-side check let it through.
@@ -199,14 +199,14 @@ module Hecks
199
199
  Hecks.as_caller(role: role, actor_id: actor_id, &block)
200
200
  end
201
201
 
202
- # THE FAIL-OPEN HALF `with_caller` ITSELF CANNOT CLOSE — ADR 0025's
203
- # Governance RBAC work fixed WHAT a *bound* role is checked against
202
+ # The fail-open half `with_caller` itself cannot close — ADR 0025's
203
+ # Governance RBAC work fixed what a *bound* role is checked against
204
204
  # (a live `Governance::RoleAssignment` lookup instead of a bare
205
205
  # string match), but changed nothing about a caller who binds no
206
206
  # role at all: `refuse_role_mismatch` `return`s immediately when
207
207
  # `Caller.current` is nil, so a bus caller who simply omits `role:`
208
208
  # sails past a role-gated command unchecked, not denied. That is a
209
- # property of THIS BUS choosing to dispatch unbound, not of the
209
+ # property of this bus choosing to dispatch unbound, not of the
210
210
  # domain rule — `bin/run`, the human CLI, has no such gap because a
211
211
  # human always dispatches through a real `Hecks.as_caller` binding
212
212
  # upstream of it. Refusing here, before `with_caller`/`dispatch` are
@@ -220,17 +220,17 @@ module Hecks
220
220
  "(role:/actor_id:) is bound; dispatching it unbound is refused, not silently unchecked"
221
221
  end
222
222
 
223
- # `dry_run?` (Runtime::Dispatcher) understands only the OLD flat
223
+ # `dry_run?` (Runtime::Dispatcher) understands only the old flat
224
224
  # legacy args shape — no to:/with: envelope, `route:` never passed
225
225
  # (its own header explains why: built directly against
226
226
  # CommandInterpreter/EntityInterpreter's pre-envelope contract,
227
227
  # never updated because nothing else needed it to be — a real
228
228
  # record of history, not a defect this bus should paper over
229
229
  # silently). `Facade::CommandRequest`/`spec[:legacy_receiver]`
230
- # already know how to NAME that same flat shape for every receiver
230
+ # already know how to name that same flat shape for every receiver
231
231
  # kind (a bare id under one string key for :aggregate, a
232
232
  # {aggregate:, entity:} pair of keys for :entity) — this is the one
233
- # door back INTO it.
233
+ # door back into it.
234
234
  def flatten_legacy(envelope, receiver, legacy_receiver)
235
235
  facts = envelope[:with] || {}
236
236
  return facts unless envelope.key?(:to)
@@ -251,8 +251,8 @@ module Hecks
251
251
  File.join(LOG_ROOT, "#{domain_name.to_s.gsub(/[^A-Za-z0-9_-]/, '_')}.jsonl")
252
252
  end
253
253
 
254
- # NEVER FAILS A REAL CALL BECAUSE ITS OWN AUDIT LOG COULDN'T BE
255
- # WRITTEN — a full disk or a permissions problem is a `follow`
254
+ # Never fails a real call because its own audit log couldn't be
255
+ # written — a full disk or a permissions problem is a `follow`
256
256
  # feature going dark, not a reason to refuse the dispatch/query/
257
257
  # state call that was actually asked for.
258
258
  def record!(domain_name, tool:, summary:, source:, outcome:, verb: nil, role: nil, actor_id: nil)
@@ -269,9 +269,9 @@ module Hecks
269
269
 
270
270
  # ── the three that drive it ────────────────────────────────────────
271
271
 
272
- # `dry_run: true` ANSWERS A DIFFERENT QUESTION than a real dispatch
272
+ # `dry_run: true` answers a different question than a real dispatch
273
273
  # does — "would this succeed", not "here is what happened" — so a
274
- # domain refusal is the legitimate, complete ANSWER (`ok: true,
274
+ # domain refusal is the legitimate, complete answer (`ok: true,
275
275
  # would_succeed: false`), not a failed call. A malformed request
276
276
  # (unknown command, a bad args shape) is still a failed call
277
277
  # (`ok: false`) either way — it never reached the domain to be asked.
@@ -308,7 +308,7 @@ module Hecks
308
308
  end
309
309
 
310
310
  def real_dispatch(runtime, spec, envelope, summary)
311
- result = runtime.dispatch(spec[:verb], **envelope)
311
+ result = runtime.dispatch_flat(spec[:verb], envelope)
312
312
  ok(summary: summary,
313
313
  id: result.id,
314
314
  state: result.state.nil? ? nil : Facade::JsonDoor.materialize(result.state),
@@ -323,8 +323,8 @@ module Hecks
323
323
  ok(summary: summary, would_succeed: false, error: e.message)
324
324
  end
325
325
 
326
- # ONE CALL, MANY STEPS — the survey's own `bin/run <domain> script`
327
- # shape, so an agent issuing a known SEQUENCE of commands (open an
326
+ # **One call, many steps** — the survey's own `bin/run <domain> script`
327
+ # shape, so an agent issuing a known sequence of commands (open an
328
328
  # account, then fund it) pays one round trip instead of N. Every step
329
329
  # goes through `dispatch` itself — same resolution, same audit log
330
330
  # line per step, same summary/source stamped on all of them since
@@ -370,11 +370,11 @@ module Hecks
370
370
  ok(summary: summary, rows: rows.map { |row| Facade::JsonDoor.materialize(row) }).merge(verb: spec[:verb])
371
371
  end
372
372
 
373
- # WHAT IS ACTUALLY STORED — no verb, no interpretation, the repository
373
+ # **What is actually stored** — no verb, no interpretation, the repository
374
374
  # itself. `id:` given answers one record (`NotFound` when it names
375
375
  # nothing); omitted answers every record the aggregate currently
376
376
  # holds. This is the difference `query` can't cover: a query answers a
377
- # DECLARED question, and an aggregate that never declared "list
377
+ # declared question, and an aggregate that never declared "list
378
378
  # everything" has no query this could reuse.
379
379
  def state(runtime:, aggregate:, summary:, id: nil, source: nil)
380
380
  bluebook = bluebook_for(runtime)
@@ -405,7 +405,7 @@ module Hecks
405
405
 
406
406
  # ── the four zoom levels ─────────────────────────────────────────
407
407
 
408
- # ZOOM LEVEL ZERO — every domain directory a root actually holds,
408
+ # **Zoom level zero** — every domain directory a root actually holds,
409
409
  # discovered rather than typed from memory. Every other tool takes
410
410
  # `domain:` as a directory it assumes the caller already knows; this
411
411
  # is how a caller who doesn't finds out. `Adapters::Folder#domain?`
@@ -422,7 +422,7 @@ module Hecks
422
422
  ok(under: under, domains: found.map { |name| File.join(under, name) })
423
423
  end
424
424
 
425
- # ZOOM LEVEL ONE — every aggregate this domain declares, and every
425
+ # **Zoom level one** — every aggregate this domain declares, and every
426
426
  # command/query name each answers to, snake_cased exactly as
427
427
  # `dispatch`/`query` want it. Enough to pick a target; `describe` is
428
428
  # the next level down for what one of them actually takes.
@@ -439,7 +439,7 @@ module Hecks
439
439
  refused(e)
440
440
  end
441
441
 
442
- # ZOOM LEVEL TWO — the exact same usage document a human gets from
442
+ # **Zoom level two** — the exact same usage document a human gets from
443
443
  # `bin/docs <domain> [aggregate]` (`Projector::DocsProjector`, the
444
444
  # identical projection `Surface::AggregateDoor#docs` calls one door
445
445
  # over): every command's arguments, the states it may be issued
@@ -454,19 +454,19 @@ module Hecks
454
454
  refused(e)
455
455
  end
456
456
 
457
- # ZOOM LEVEL THREE — is the wiring sound at all: every bind names a
457
+ # **Zoom level three** — is the wiring sound at all: every bind names a
458
458
  # declared aggregate, every adapter satisfies the port it claims, the
459
459
  # default adapter is usable. `Registry#verify!` (`runtime/registry/
460
460
  # verification.rb`) is the one place this repo already answers that
461
461
  # question, and `Runtime::Loader.boot` already calls it as the last
462
- # step of every boot — so THIS is the one method here that boots for
462
+ # step of every boot — so this is the one method here that boots for
463
463
  # itself rather than taking a `runtime:` already in hand, because a
464
464
  # runtime that successfully reached this line already answered the
465
- # question. Given a domain PATH, not a booted runtime, deliberately:
465
+ # question. Given a domain path, not a booted runtime, deliberately:
466
466
  # asking "is this valid" about a domain that failed to boot at all
467
467
  # has to be askable without a runtime to hand it.
468
468
  #
469
- # `deep: true` GOES PAST WIRING INTO LOGIC — `Bluebook::ModelCheck`,
469
+ # `deep: true` goes past wiring into logic — `Bluebook::ModelCheck`,
470
470
  # the lightweight-formal-methods leg (dead lifecycle transitions, a
471
471
  # saga state no handler chain reaches, a dispatch to nowhere). Opt-in
472
472
  # and separate from the base check on purpose: a wiring defect is
@@ -474,7 +474,7 @@ module Hecks
474
474
  # part of it can never fire" — two different questions, and the
475
475
  # first is far cheaper to ask on every boot.
476
476
  #
477
- # ANY BOOT FAILURE ANSWERS THE QUESTION, not only `WiringError` — a
477
+ # Any boot failure answers the question, not only `WiringError` — a
478
478
  # domain path with no `.hecksagon`, a malformed bluebook, is just as
479
479
  # much "not valid" as a real wiring mismatch, and this tool exists
480
480
  # precisely so none of those ever cross a projection of this bus as
@@ -496,7 +496,7 @@ module Hecks
496
496
 
497
497
  # ── beyond the zoom levels ──────────────────────────────────────────
498
498
 
499
- # THE FULL WRITE HISTORY, not just the current head — `bin/history`'s
499
+ # The full write history, not just the current head — `bin/history`'s
500
500
  # own logic, unchanged: an append-only-backed aggregate's `entries`,
501
501
  # every operation that ever touched it. An aggregate bound to a
502
502
  # non-append-only adapter (Memory, Postgres proper) answers an empty
@@ -519,11 +519,11 @@ module Hecks
519
519
  repository.entries.map { |entry| { operation: entry.operation, id: entry.id, state: Facade::JsonDoor.materialize(entry.state) } }
520
520
  end
521
521
 
522
- # `.behaviors` FILES, RUN AND REPORTED — hand-curated examples of how
522
+ # `.behaviors` files, run and reported — hand-curated examples of how
523
523
  # to use a domain, in domain vocabulary (`docs/guides/behaviors.md`),
524
524
  # the survey's own "honest-refusal", generated-example-suite items.
525
525
  # `Hecks::Behaviors` boots each test fresh through `Hecks.boot_files`
526
- # itself — `target:` names a `.behaviors` file OR a directory to
526
+ # itself — `target:` names a `.behaviors` file or a directory to
527
527
  # sweep, never a `runtime:`, the one other method here besides
528
528
  # `validate` that takes a path instead.
529
529
  def behaviors(target:)
@@ -547,7 +547,7 @@ module Hecks
547
547
  runs: Array(result.runs).map { |run| { description: run.description, status: run.status, message: run.message } } }
548
548
  end
549
549
 
550
- # A LIVE TAIL WITHOUT A LIVE PROCESS — `bin/hecks_mcp_door` (its
550
+ # A live tail without a live process — `bin/hecks_mcp_door` (its
551
551
  # transport of MCP-over-stdio) answers one request at a time, no push
552
552
  # channel to a client that only ever asks. This is the honest version
553
553
  # of the survey's `storehouse follow` for that shape: not a
@@ -571,17 +571,17 @@ module Hecks
571
571
  File.readlines(path).map { |line| JSON.parse(line, symbolize_names: true) }
572
572
  end
573
573
 
574
- # WHAT ACTUALLY HAPPENED, with payloads — distinct from `state`
575
- # (what's stored NOW) and `history` (append-only operation
576
- # SNAPSHOTS, no payload). NOT a domain-wide event-sourcing replay:
574
+ # What actually happened, with payloads — distinct from `state`
575
+ # (what's stored now) and `history` (append-only operation
576
+ # snapshots, no payload). Not a domain-wide event-sourcing replay:
577
577
  # "one boot per call" means `runtime.events` is always empty except
578
578
  # during the very call that populated it, discarded the moment that
579
579
  # call returns — there is no cross-call in-memory log to read here.
580
- # So this reads the SAME durable audit log `follow` already tails
580
+ # So this reads the same durable audit log `follow` already tails
581
581
  # (`record!` now stamps a successful dispatch's own announced
582
582
  # events onto its log line), reshaped: `follow` answers "what was
583
- # CALLED, in order, across every tool"; this answers "what HAPPENED
584
- # to one aggregate/record" — events THIS BUS witnessed, which is
583
+ # called, in order, across every tool"; this answers "what happened
584
+ # to one aggregate/record" — events this bus witnessed, which is
585
585
  # every real dispatch ever routed through it, but no more than that.
586
586
  # `aggregate:` narrows to one aggregate; `id:` (requires
587
587
  # `aggregate:` — an id alone is not unique across aggregates)
@@ -616,19 +616,19 @@ module Hecks
616
616
 
617
617
  # ── shared shape ────────────────────────────────────────────────────
618
618
 
619
- # `Runtime::WiringError` BELONGS HERE TOO, alongside the true domain
620
- # refusals — not because it IS one (it's a structural defect, not a
619
+ # `Runtime::WiringError` belongs here too, alongside the true domain
620
+ # refusals — not because it is one (it's a structural defect, not a
621
621
  # rule the caller broke), but because "this domain isn't wired to
622
622
  # answer what you're asking" (a `role:`+`actor_id:` caller reaching
623
623
  # an authorization port nothing implements, a dry run against a
624
624
  # port verb) is exactly the shape this bus promises never crashes
625
625
  # through it. `dry_run_outcome` already treats it this way locally;
626
- # this makes every OTHER caller of `refusal_classes` do the same.
626
+ # this makes every other caller of `refusal_classes` do the same.
627
627
  def refusal_classes = [Runtime::NotFound, Runtime::TypeMismatch, Runtime::WiringError, *Runtime::DOMAIN_REFUSALS]
628
628
 
629
629
  def ok(**fields) = { ok: true }.merge(fields)
630
630
 
631
- # AN HONEST REFUSAL, NOT A CRASH — the survey's own item #9: "an
631
+ # **An honest refusal, not a crash** — the survey's own item #9: "an
632
632
  # explicit, structured refusal a caller can act on" rather than a
633
633
  # stack trace an agent has to parse to find the one line that
634
634
  # mattered. The domain's own refusal text travels verbatim
data/lib/hecks/version.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  module Hecks
2
- # ISOLATED, ON PURPOSE — the gemspec reads this file directly rather
2
+ # **Isolated, on purpose** — the gemspec reads this file directly rather
3
3
  # than `require_relative "lib/hecks"` (the whole framework),
4
- # specifically so evaluating the gemspec never triggers ANY of this
4
+ # specifically so evaluating the gemspec never triggers any of this
5
5
  # gem's own dependencies (prism among them) before Bundler has even
6
6
  # resolved what to install. A real, live chicken-and-egg bug caught
7
7
  # deploying to a Ruby version that doesn't bundle prism for free
@@ -12,5 +12,5 @@ module Hecks
12
12
  # it there, or even in the consuming Gemfile, never closed the gap,
13
13
  # because gemspec evaluation happens before anything Bundler
14
14
  # resolves is actually loadable yet.
15
- VERSION = "1.3.0".freeze
15
+ VERSION = "1.4.0".freeze
16
16
  end