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
@@ -5,12 +5,12 @@ require_relative "../../runtime/value"
5
5
  module Hecks
6
6
  module Fuzzing
7
7
  class SequenceGenerator
8
- # THE ARGUMENT SHAPES THE RUBY/RUST DIVERGENCES WERE ACTUALLY FOUND
9
- # THROUGH, injected on purpose, on every domain.
8
+ # The argument shapes the Ruby/Rust divergences were actually found
9
+ # through, injected on purpose, on every domain.
10
10
  #
11
11
  # Ten bugs in one QA session (BUG#7–#16, `QualityControl`'s own
12
12
  # ledger) clustered in a handful of mechanisms — and several were
13
- # only ever reachable because ONE domain happened to declare the
13
+ # only ever reachable because one domain happened to declare the
14
14
  # argument that tripped them: `Roster::Roster.Mark`'s own `to:`
15
15
  # collided with the dispatcher's routing `to:` (BUG#7), a present-
16
16
  # but-null `to:` was misread as a routing envelope (BUG#16), a blank
@@ -20,28 +20,28 @@ module Hecks
20
20
  # in the rotation could ever have found any of those, because
21
21
  # nothing generated ever produced the shape. This module produces
22
22
  # them — for a configurable fraction of command steps, chosen and
23
- # parameterised from the SAME seeded RNG the rest of the generator
23
+ # parameterised from the same seeded RNG the rest of the generator
24
24
  # already draws from, so `generate(domain, seed:, steps:,
25
25
  # adversarial:)` stays exactly as reproducible per seed as it was.
26
26
  #
27
- # OPT-IN, BY CONSTRUCTION. `adversarial: 0.0` (the default) returns
27
+ # **Opt-in, by construction**. `adversarial: 0.0` (the default) returns
28
28
  # before drawing a single random number, so every pinned seed in
29
29
  # spec/fuzzing, spec/rust_conformance_fuzz_spec.rb, bin/fuzz and
30
30
  # bin/generate produces byte-for-byte what it produced before this
31
31
  # module existed. `bin/qa_sweep` turns it on (reading
32
32
  # `QualityControlDials::ADVERSARIAL_FRACTION`) — that script is the
33
- # one place a mutated step's own divergence is a FINDING rather than
33
+ # one place a mutated step's own divergence is a finding rather than
34
34
  # a red CI gate.
35
35
  #
36
- # ONE MUTATION PER STEP, applied in `StepBuilder#build_command_step`
37
- # after the arguments and identity are built and BEFORE the step's
36
+ # One mutation per step, applied in `StepBuilder#build_command_step`
37
+ # after the arguments and identity are built and before the step's
38
38
  # own inline dispatch — so the generator's own `known_ids` tracking
39
- # sees what really happened, and so the returned step's `args` ARE
39
+ # sees what really happened, and so the returned step's `args` are
40
40
  # the mutated bytes both `Fuzzing::Replay` (Ruby) and the compiled
41
41
  # conformance binary (Rust) later receive. The step carries an
42
42
  # `"adversarial"` key naming what was done (`mutation`, the bug
43
43
  # class it exercises, and the exact sub-shape), which `bin/qa_sweep`
44
- # prints on a FOUND SOMETHING report so the agent logging the Bug
44
+ # prints on a found something report so the agent logging the Bug
45
45
  # can name the mechanism, not guess it. Both replay paths ignore
46
46
  # the key: `Replay.call` reads only verb/query/dry_run/args/role,
47
47
  # and `kernel/cli.rs` reads step keys by name.
@@ -72,19 +72,19 @@ module Hecks
72
72
  VALUE_OBJECT_SHAPES = %w[null empty_object].freeze
73
73
 
74
74
  # BUG#7/#8/#14's class — an unknown key, a type-mismatched declared
75
- # key and an absent required key in the SAME step, so both engines
75
+ # key and an absent required key in the same step, so both engines
76
76
  # have to pick which refusal wins; and each pair, so the ordering
77
77
  # of any two is observable on a command too small for all three.
78
78
  #
79
- # THE SECOND ROW REACHES FURTHER DOWN `DISPATCH_ORDER`
79
+ # The second row reaches further down `DISPATCH_ORDER`
80
80
  # (lib/hecks/vocabulary.rb): `nonexistent` addresses an id nothing
81
81
  # holds (a `hydrate`-stage NotFound), `lifecycle` aims a
82
82
  # transition-guarded command at a real record (an
83
- # `admissible_transition`-stage refusal, IF the record's state
83
+ # `admissible_transition`-stage refusal, if the record's state
84
84
  # refuses it), `role` binds a caller whose role the command does
85
85
  # not name (the `refuse_role_mismatch` stage no generated step
86
86
  # had ever reached). Paired with an argument-stage fault each,
87
- # so the ordering of an EARLY stage against a LATE one is
87
+ # so the ordering of an early stage against a late one is
88
88
  # observable — BUG#13 (ledger_ordering's own NOTES.md) was
89
89
  # exactly an argument-invariant-vs-entity-existence ordering
90
90
  # split, and nothing generated had ever asked the question on
@@ -94,8 +94,8 @@ module Hecks
94
94
  nonexistent+mismatch nonexistent+unknown lifecycle+mismatch role+absent role+nonexistent
95
95
  ].freeze
96
96
 
97
- # ITEM 2 OF THE DETECTION PLAN (ANGLE-5) — A DRAWN CALLER ON A
98
- # ROLE-GATED COMMAND. `refuse_role_mismatch` is a `DISPATCH_ORDER`
97
+ # Item 2 of the detection plan (angle-5) — a drawn caller on a
98
+ # role-gated command. `refuse_role_mismatch` is a `DISPATCH_ORDER`
99
99
  # step both engines implement (`command_rules/authorization.rb`,
100
100
  # `rust/src/kernel/repository.rs check_role`) and both replay doors
101
101
  # already read (`Fuzzing::Replay` binds `Hecks.as_caller` from a
@@ -109,24 +109,24 @@ module Hecks
109
109
  # absent_on_gated no caller at all on a gated command — the
110
110
  # unchecked default, recorded so the step reads
111
111
  # as a deliberate control, not an omission
112
- # actor_known the role PLUS an actor this same sequence
113
- # already granted it to (`Governance::
114
- # RoleAssignment.Assign` succeeded earlier) —
112
+ # actor_known the role plus an actor this same sequence
113
+ # already granted it to (the authorization
114
+ # provider's declared `grant:` verb succeeded
115
+ # earlier — `catalog[:grant_verbs]`) —
115
116
  # the real `holds_role?` lookup, both sides
116
117
  # actor_unknown the role plus an actor nothing granted —
117
118
  # `holds_role?` must refuse on both
118
- # A SEPARATE LAYER FROM `KINDS`, with its own probability
119
+ # a separate layer from `KINDS`, with its own probability
119
120
  # (`role_draw:` — `QualityControlDials::ROLE_DRAW_PROBABILITY`):
120
121
  # a caller composes with any argument mutation above rather than
121
122
  # competing with it for the one-mutation-per-step slot, and draws
122
123
  # nothing from the RNG when off, so every pinned seed is
123
124
  # byte-identical to before it existed.
124
125
  CALLER_SHAPES = %w[matching mismatched absent_on_gated actor_known actor_unknown].freeze
125
- GRANT_VERB = "Governance::RoleAssignment.Assign".freeze
126
126
  UNKNOWN_ROLE = "Nobody the domain names".freeze
127
127
 
128
128
  # BUG#11 — an entity command two or more hops deep. Not a mutation
129
- # of arguments but a PREFERENCE (picker.rb weights these up when
129
+ # of arguments but a preference (picker.rb weights these up when
130
130
  # adversarial) plus an addressing coin: flat one-head-per-hop args
131
131
  # (what every other generated entity step uses) or the routed
132
132
  # `to: { aggregate:, entities: [...] }` envelope BUG#11's own fix
@@ -136,7 +136,7 @@ module Hecks
136
136
  DEEP_ENTITY_WEIGHT = 4
137
137
 
138
138
  # BUG#13's mutation is only ever applicable on a step whose
139
- # parent ALREADY holds an element this same sequence appended —
139
+ # parent already holds an element this same sequence appended —
140
140
  # a rare moment (an append has to have succeeded first, and most
141
141
  # mutated appends are refused), so when it is on offer it is
142
142
  # weighted up the same way the picker weights an unexercised
@@ -147,10 +147,10 @@ module Hecks
147
147
 
148
148
  def adversarial? = @adversarial.positive?
149
149
 
150
- # `[]` — and NO RNG DRAW — when adversarial mode is off. Otherwise
150
+ # `[]` — and no RNG draw — when adversarial mode is off. Otherwise
151
151
  # the deep-entity addressing note (every deep step, when
152
152
  # adversarial), then with probability `@adversarial` exactly one
153
- # mutation among those applicable to THIS step's own command.
153
+ # mutation among those applicable to this step's own command.
154
154
  def adversarial_mutations!(args, entry, catalog)
155
155
  return [] unless adversarial?
156
156
 
@@ -176,7 +176,7 @@ module Hecks
176
176
 
177
177
  heads = [entry[:aggregate], *entry[:chain]].map { |construct| (construct.identified_by || :id).to_s }
178
178
  scalars = heads.map { |head| ValueGenerator.scalar_of(args[head]) }
179
- # The heads leave the flat args — this is the CLEAN routed
179
+ # The heads leave the flat args — this is the clean routed
180
180
  # caller BUG#11's own spec pins (`to: {...}, note: {...}`) —
181
181
  # unless the command itself declares an attribute of that name
182
182
  # (chess's `Piece.Move` declares `id` as a fact too), which
@@ -236,7 +236,7 @@ module Hecks
236
236
 
237
237
  def blank_identity_applicable?(args, entry, catalog) = blank_identity_targets(args, entry, catalog).any?
238
238
 
239
- # The identity heads THIS step supplies itself: a creating
239
+ # The identity heads this step supplies itself: a creating
240
240
  # aggregate command's own (every part of a composite), and an
241
241
  # append's caller-supplied entity identity arguments (an entity is
242
242
  # "created" by its append, and BUG#15's blank-identity question
@@ -344,8 +344,8 @@ module Hecks
344
344
  precedence_shapes_for(args, entry).any?
345
345
  end
346
346
 
347
- # EVERY SHAPE WHOSE EVERY PART THIS STEP CAN CARRY. `nonexistent`
348
- # and `lifecycle` need a command that ACTS on a record (a creating
347
+ # Every shape whose every part this step can carry. `nonexistent`
348
+ # and `lifecycle` need a command that acts on a record (a creating
349
349
  # step has no addressed id to point elsewhere, and no state to be
350
350
  # in) and flat addressing (`deep_entity_addressing!`'s routed `to:`
351
351
  # envelope owns the ids then); `lifecycle` additionally needs a
@@ -401,14 +401,14 @@ module Hecks
401
401
  applied << "unknown"
402
402
  end
403
403
  apply_late_stage_parts!(wanted, args, entry, detail, applied, catalog)
404
- # Reported as what was ACTUALLY done — a command with a single
404
+ # Reported as what was actually done — a command with a single
405
405
  # attribute cannot carry both a dropped and a corrupted one.
406
406
  detail.merge("shape" => applied.sort.join("+"))
407
407
  end
408
408
 
409
- # The three parts that reach PAST the argument gate — see
409
+ # The three parts that reach past the argument gate — see
410
410
  # `PRECEDENCE_SHAPES`' own second row. `nonexistent` re-addresses
411
- # the step's LAST hop (the entity element for an entity command,
411
+ # the step's last hop (the entity element for an entity command,
412
412
  # the aggregate itself otherwise) to an id nothing holds;
413
413
  # `lifecycle` mutates nothing (the record's own state is what
414
414
  # refuses, or doesn't) and is recorded so the pairing is visible;
@@ -433,7 +433,7 @@ module Hecks
433
433
  applied << "role"
434
434
  end
435
435
 
436
- # ── ANGLE-5: a drawn caller on a role-gated command ──────────────
436
+ # ── Angle-5: a drawn caller on a role-gated command ──────────────
437
437
 
438
438
  def role_draw? = @role_draw.positive?
439
439
 
@@ -491,7 +491,7 @@ module Hecks
491
491
  end
492
492
  end
493
493
 
494
- # A GRANT AIMED AT A ROLE SOME COMMAND ACTUALLY DECLARES. Left to
494
+ # A grant aimed at a role some command actually declares. Left to
495
495
  # `ValueGenerator`, `Assign`'s `role_name` is random text
496
496
  # ("hotel"), which no command is gated on — so a granted actor
497
497
  # could never satisfy `holds_role?` for anything, and the
@@ -500,7 +500,7 @@ module Hecks
500
500
  # domain's own declared roles instead; with it off, nothing here
501
501
  # runs (no RNG draw, no change to the args).
502
502
  def steer_grant!(args, entry, catalog)
503
- return unless role_draw? && entry[:verb] == GRANT_VERB && catalog[:roles].any?
503
+ return unless role_draw? && catalog[:grant_verbs].include?(entry[:verb]) && catalog[:roles].any?
504
504
  return unless args.key?("role_name")
505
505
 
506
506
  args["role_name"] = { "value" => catalog[:roles].sample(random: @random) }
@@ -29,8 +29,8 @@ module Hecks
29
29
  catalog_entities(domain_name, aggregate, entity_commands, entity_queries)
30
30
  end
31
31
 
32
- # THE BARE DOMAIN FORM — "Domain.report_name", no "::" — the
33
- # SAME shape `Dispatcher#query` itself branches on to route a
32
+ # **The bare domain form** — "Domain.report_name", no "::" — the
33
+ # same shape `Dispatcher#query` itself branches on to route a
34
34
  # read-model ask apart from an aggregate query. A report was
35
35
  # never in this catalog at all before: `aggregation_matches_
36
36
  # recompute` (count/median) has only ever been exercised by
@@ -52,24 +52,31 @@ module Hecks
52
52
  # from (adversary.rb `other_role`).
53
53
  roles: (creating + instance + entity_commands).filter_map { |e| e[:command].role }
54
54
  .map(&:to_s).reject(&:empty?).uniq.sort,
55
+ # The grant verb every loaded authorization provider declares
56
+ # (`provides "authorization", grant: ...`) — what the caller
57
+ # draw steers at a declared role and records as a real grant.
58
+ # Read off the declaration, never the literal Governance name.
59
+ grant_verbs: runtime.registry.authorization_providers
60
+ .filter_map { |chapter| chapter.provided_verb(Bluebook::Capabilities::AUTHORIZATION, :grant) }
61
+ .sort,
55
62
  # Which aggregates this corpus can actually make one of — the ones
56
63
  # `satisfiable?` is entitled to wait for.
57
64
  creatable: creating.to_set { |entry| entry[:aggregate].hecks_name } }
58
65
  end
59
66
 
60
- # EVERY ENTITY, AT EVERY DEPTH — `Card` nested inside `Board`
67
+ # Every entity, at every depth — `Card` nested inside `Board`
61
68
  # inside `Workspace` (qa/stress_domains/nested_pieces) walks in
62
69
  # as `chain: [Board, Card]`, the exact hop list
63
- # `EntityInterpreter#walk_entity_chain` resolves the dotted verb
70
+ # `EntityInterpreter::Resolution.of` resolves the dotted verb
64
71
  # back into. Before this walk existed the catalog only ever read
65
72
  # `aggregate.entities` one level down, so a two-hop entity
66
73
  # command (BUG#11's whole class) could never be generated at all
67
74
  # — the one shape the differential harness most needed to reach
68
75
  # was structurally absent from every sequence it ever produced.
69
- # `entity:` stays the LAST hop (what every existing reader means
76
+ # `entity:` stays the last hop (what every existing reader means
70
77
  # by "the entity"); `chain:` is the whole path.
71
78
  #
72
- # Entity QUERIES stay one hop deep, exactly as before — a nested
79
+ # Entity queries stay one hop deep, exactly as before — a nested
73
80
  # entity's query has no established wire spelling this generator
74
81
  # can vouch for, and nothing in the corpus declares one.
75
82
  def catalog_entities(domain_name, aggregate, entity_commands, entity_queries)
@@ -90,7 +97,7 @@ module Hecks
90
97
  end
91
98
 
92
99
  # Depth-first, parents before children, in declaration order — so
93
- # the depth-1 entries land in `entity_commands` in EXACTLY the
100
+ # the depth-1 entries land in `entity_commands` in exactly the
94
101
  # order they always did (a pinned seed's picker pool is the same
95
102
  # pool it was), and a nested entity's own entries follow its
96
103
  # parent's.
@@ -108,16 +115,16 @@ module Hecks
108
115
  # (CommandInterpreter#entity_element) when the append's own field
109
116
  # mapping doesn't already assign it — the common case, predicted here.
110
117
  # A domain whose append explicitly assigns identity through a mapped
111
- # argument is covered too, without guessing: whatever value THIS
112
- # generator supplied for that argument at dispatch time IS the
118
+ # argument is covered too, without guessing: whatever value this
119
+ # generator supplied for that argument at dispatch time is the
113
120
  # identity, and gets recorded directly (see `record_outcome`).
114
121
  #
115
122
  # `owner_chain:` — `[]` for an aggregate-level append (`Board.AddList`,
116
- # `Folder.AddSlip`), the entity path for an ENTITY-level one
123
+ # `Folder.AddSlip`), the entity path for an entity-level one
117
124
  # (`Board.AddCard` appending into `Board.cards`, owner_chain
118
125
  # `[Board]`) — so `record_outcome` can key the appended element
119
126
  # under the exact parent-plus-hops it landed beneath.
120
- # `identity_arguments:` — EVERY identity head the mapping sources
127
+ # `identity_arguments:` — every identity head the mapping sources
121
128
  # from a command argument (a composite entity identity has several),
122
129
  # what the adversarial duplicate-identity mutation replays;
123
130
  # `identity_argument:` stays the single-head reading the existing
@@ -15,7 +15,7 @@ module Hecks
15
15
  parent_scalar = identity_scalar_of(aggregate, args)
16
16
 
17
17
  @known_ids[aggregate.hecks_name] << parent_scalar if entry[:entity].nil? && entry[:command].creates?
18
- record_grant(args) if entry[:verb] == Adversary::GRANT_VERB
18
+ record_grant(args) if catalog[:grant_verbs].include?(entry[:verb])
19
19
 
20
20
  populator = populator_for_entry(catalog, entry)
21
21
  return unless populator
@@ -35,24 +35,25 @@ module Hecks
35
35
  end
36
36
  @entity_known_ids[key] << new_id
37
37
 
38
- # THE CALLER-SUPPLIED IDENTITY TUPLE, kept whole — every mapped
38
+ # The caller-supplied identity tuple, kept whole — every mapped
39
39
  # identity argument and the exact value this step offered for it
40
40
  # — so the adversarial duplicate-identity mutation (BUG#13's own
41
41
  # shape, `Folder.AddSlip` twice under the same `reference`) can
42
- # offer it AGAIN later against the same parent, composite
43
- # identities included (BUG#13's fix explicitly did NOT cover
42
+ # offer it again later against the same parent, composite
43
+ # identities included (BUG#13's fix explicitly did not cover
44
44
  # those; this is how a sequence gets to ask).
45
45
  return if populator[:identity_arguments].empty?
46
46
 
47
47
  @appended_identities[key] << populator[:identity_arguments].to_h { |name| [name.to_s, args[name.to_s]] }
48
48
  end
49
49
 
50
- # A GRANT THIS SEQUENCE MADE FOR REAL`Governance::RoleAssignment.
51
- # Assign` succeeded with these exact args, so `actor_id` now holds
50
+ # A grant this sequence made for realthe authorization provider's
51
+ # declared `grant:` verb (`Governance::RoleAssignment.Assign` in
52
+ # every boot today) succeeded with these exact args, so `actor_id` now holds
52
53
  # `role_name` in this boot's own store, on both replay sides. The
53
54
  # `actor_known` caller shape (adversary.rb) replays that identity
54
55
  # against a command gated on the same role: the one way a
55
- # generated step reaches `holds_role?`'s AUTHORIZED branch rather
56
+ # generated step reaches `holds_role?`'s authorized branch rather
56
57
  # than only its refusing one.
57
58
  def record_grant(args)
58
59
  role = ValueGenerator.scalar_of(args["role_name"]).to_s
@@ -60,7 +61,7 @@ module Hecks
60
61
  @granted[role] << actor unless role.empty? || actor.empty?
61
62
  end
62
63
 
63
- # THE POOL AN APPENDED ELEMENT LANDS IN — the aggregate's own
64
+ # The pool an appended element lands in — the aggregate's own
64
65
  # identity, then one scalar per owning hop (an aggregate-level
65
66
  # append has none; `Board.AddCard` has `Board`'s own `number`,
66
67
  # read straight back off the args this step addressed it by).
@@ -81,18 +82,18 @@ module Hecks
81
82
  "#{aggregate_name}.#{chain_names.join('.')}##{scalars.join('/')}"
82
83
  end
83
84
 
84
- # THE SCALAR THIS STEP'S OWN AGGREGATE IDENTITY RESOLVES TO, from the
85
+ # The scalar this step's own aggregate identity resolves to, from the
85
86
  # args a creating (or entity-populating) command actually
86
- # dispatched — a SINGLE declared head (or the untyped default
87
+ # dispatched — a single declared head (or the untyped default
87
88
  # `:id`) reads straight off its own top-level arg the way this
88
89
  # always has (`ValueGenerator.scalar_of`, opening the identity value
89
- # object). A genuinely COMPOSITE identity (`composite_identity?`,
90
+ # object). A genuinely composite identity (`composite_identity?`,
90
91
  # shared with StepBuilder#add_identity! — see its own comment) has
91
92
  # no such top-level arg at all: `add_identity!` deliberately leaves
92
93
  # a composite creating command's own individually-declared fields
93
94
  # alone rather than forcing a synthetic `id` neither the bluebook
94
95
  # nor the command ever declared, so this joins those fields itself,
95
- # in the SAME declaration order `Runtime::Identity.of` joins them
96
+ # in the same declaration order `Runtime::Identity.of` joins them
96
97
  # at dispatch (`Naming.identity`, `Naming::IDENTITY_JOIN`) — which
97
98
  # is exactly what makes the result usable later as the bare `id:`
98
99
  # a composite Revoke/act-again command expects (`Identity.from`'s
@@ -1,8 +1,8 @@
1
1
  module Hecks
2
2
  module Fuzzing
3
3
  class SequenceGenerator
4
- # Which step to try next: eligibility (what is POSSIBLE from the
5
- # state so far) and weighting (what is LIKELY to reach somewhere
4
+ # Which step to try next: eligibility (what is possible from the
5
+ # state so far) and weighting (what is likely to reach somewhere
6
6
  # new).
7
7
  module Picker
8
8
  private
@@ -23,7 +23,7 @@ module Hecks
23
23
 
24
24
  # BUG#11's own preference (adversary.rb) — an entity command two
25
25
  # or more hops deep is weighted up the same way an unexercised
26
- # verb is, ONLY in adversarial mode: the eligibility rules above
26
+ # verb is, only in adversarial mode: the eligibility rules above
27
27
  # still decide what is possible, and a default-mode pool is
28
28
  # exactly the pool it always was.
29
29
  def deep_entity_bias(rest)
@@ -31,11 +31,11 @@ module Hecks
31
31
  deep * Adversary::DEEP_ENTITY_WEIGHT
32
32
  end
33
33
 
34
- # WHILE THERE IS NOTHING TO FIND, MAKING SOMETHING IS THE ONLY USEFUL MOVE.
34
+ # While there is nothing to find, making something is the only useful move.
35
35
  #
36
36
  # A flat weight is right once the domain has records in it, and badly
37
37
  # wrong before: banking declares ten queries, and from an empty store
38
- # exactly ONE creating command is satisfiable — Customer.Register, the
38
+ # exactly one creating command is satisfiable — Customer.Register, the
39
39
  # verb the whole cascade waits on. Two entries against ten left runs
40
40
  # spending their entire budget querying a store nothing had been written
41
41
  # to ; one seed answered ten empty queries in a row and emitted nothing
@@ -54,9 +54,9 @@ module Hecks
54
54
  @known_ids[entry[:aggregate].hecks_name].any? && satisfiable?(catalog, entry)
55
55
  end
56
56
 
57
- # A ROOTLESS report (no `reference_to` at all) has nothing to wait
57
+ # A rootless report (no `reference_to` at all) has nothing to wait
58
58
  # for — it reads whole tables, the same "always eligible" position
59
- # `catalog[:queries]` itself takes. A ROOTED one needs a real
59
+ # `catalog[:queries]` itself takes. A rooted one needs a real
60
60
  # instance of its own `reference_target` to ask about first, same
61
61
  # rule `actionable?` already gives an instance command — asking
62
62
  # `Banking.disputed_payment_count` before any Account exists would
@@ -66,7 +66,7 @@ module Hecks
66
66
  entry[:model].reference_target.nil? || @known_ids[entry[:model].reference_target].any?
67
67
  end
68
68
 
69
- # A COMMAND THAT REFERENCES NOTHING THAT EXISTS CANNOT SUCCEED, so it is
69
+ # A command that references nothing that exists cannot succeed, so it is
70
70
  # not offered until something does.
71
71
  #
72
72
  # `ValueGenerator.reference_value` has no real id to hand over when its
@@ -78,7 +78,7 @@ module Hecks
78
78
  # spent its whole budget being refused.
79
79
  #
80
80
  # This is the rule instance commands already follow — `known_ids.any?` —
81
- # applied to what a command REFERENCES rather than to what it acts on.
81
+ # applied to what a command references rather than to what it acts on.
82
82
  # A target no creating command in this corpus can make (a cross-domain
83
83
  # reference, which `CommandRules#resolve_references` skips anyway) is
84
84
  # exempt, or the whole domain would starve waiting for it.
@@ -92,14 +92,23 @@ module Hecks
92
92
  # Coverage-guided rather than uniformly random : a verb this sequence has
93
93
  # not dispatched yet is weighted up, so a run spends its budget on the
94
94
  # commands it has not reached instead of re-rolling the ones it has. The
95
- # eligibility rules above still decide what is POSSIBLE — this only decides
95
+ # eligibility rules above still decide what is possible — this only decides
96
96
  # what is likely, so a verb gated behind state it does not have yet stays
97
97
  # out of the pool entirely rather than being preferred forever.
98
98
  def steer(pool)
99
99
  fresh = pool.reject { |entry| @exercised.include?(entry[:verb]) }
100
- return pool if fresh.empty?
100
+ steered = fresh.empty? ? pool : pool + (fresh * UNEXERCISED_WEIGHT)
101
+ favor(steered)
102
+ end
103
+
104
+ # `favor:` (CoverageCampaign's rare verbs) — weighted up when
105
+ # eligible, never made eligible. An empty favor list returns the
106
+ # pool untouched, so an unguided seed draws exactly what it did.
107
+ def favor(pool)
108
+ return pool if @favor.empty?
101
109
 
102
- pool + (fresh * UNEXERCISED_WEIGHT)
110
+ favored = pool.uniq.select { |entry| @favor.include?(entry[:verb]) }
111
+ pool + (favored * SequenceGenerator::FAVOR_WEIGHT)
103
112
  end
104
113
  end
105
114
  end