hecks 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (373) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hecks/adapters/driven/claude_code.rb +72 -7
  3. data/lib/hecks/adapters/driven/d1.rb +187 -23
  4. data/lib/hecks/adapters/driven/folder.rb +83 -10
  5. data/lib/hecks/adapters/driven/google_authentication.rb +33 -12
  6. data/lib/hecks/adapters/driven/governance_authorization.rb +87 -18
  7. data/lib/hecks/adapters/driven/heki/journal.rb +13 -4
  8. data/lib/hecks/adapters/driven/heki/saga_store.rb +56 -10
  9. data/lib/hecks/adapters/driven/heki/snapshot.rb +1 -1
  10. data/lib/hecks/adapters/driven/heki.rb +106 -9
  11. data/lib/hecks/adapters/driven/identity_registry.rb +12 -2
  12. data/lib/hecks/adapters/driven/in_memory_ordering.rb +25 -3
  13. data/lib/hecks/adapters/driven/in_process_key_vault.adapter +3 -0
  14. data/lib/hecks/adapters/driven/in_process_key_vault.rb +53 -0
  15. data/lib/hecks/adapters/driven/lambda/client.rb +67 -14
  16. data/lib/hecks/adapters/driven/lambda.rb +82 -35
  17. data/lib/hecks/adapters/driven/local_storage.rb +83 -10
  18. data/lib/hecks/adapters/driven/memory.rb +205 -9
  19. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +21 -1
  20. data/lib/hecks/adapters/driven/postgres/codec.rb +27 -11
  21. data/lib/hecks/adapters/driven/postgres/outbox.rb +40 -2
  22. data/lib/hecks/adapters/driven/postgres/reconnect.rb +23 -7
  23. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +14 -14
  24. data/lib/hecks/adapters/driven/postgres.rb +175 -28
  25. data/lib/hecks/adapters/driven/prism.rb +50 -11
  26. data/lib/hecks/adapters/driven/secure_random_identity.rb +3 -0
  27. data/lib/hecks/adapters/driven/sql_query_builder.rb +34 -22
  28. data/lib/hecks/adapters/driven/sqlite/codec.rb +38 -10
  29. data/lib/hecks/adapters/driven/sqlite/projection.rb +60 -32
  30. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +12 -12
  31. data/lib/hecks/adapters/driven/sqlite.rb +181 -21
  32. data/lib/hecks/adapters/driven/system_clock.rb +3 -0
  33. data/lib/hecks/adapters/driven/tenant_provisioner.adapter +3 -0
  34. data/lib/hecks/adapters/driven/tenant_provisioner.rb +66 -0
  35. data/lib/hecks/adapters/driven.rb +6 -4
  36. data/lib/hecks/adapters/driving/github_webhook.rb +31 -18
  37. data/lib/hecks/behaviors/dsl.rb +60 -2
  38. data/lib/hecks/behaviors/expectations.rb +190 -29
  39. data/lib/hecks/behaviors/ir.rb +12 -1
  40. data/lib/hecks/behaviors/rspec.rb +9 -1
  41. data/lib/hecks/behaviors/runner.rb +21 -2
  42. data/lib/hecks/behaviors.rb +9 -1
  43. data/lib/hecks/bluebook/aggregate.rb +43 -13
  44. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +17 -10
  45. data/lib/hecks/bluebook/assembly/build.rb +26 -5
  46. data/lib/hecks/bluebook/assembly/contract.rb +98 -23
  47. data/lib/hecks/bluebook/assembly/contracts.rb +59 -52
  48. data/lib/hecks/bluebook/assembly/marks.rb +159 -30
  49. data/lib/hecks/bluebook/assembly/specializer.rb +38 -21
  50. data/lib/hecks/bluebook/assembly.rb +32 -14
  51. data/lib/hecks/bluebook/attribute.rb +26 -12
  52. data/lib/hecks/bluebook/behaviour/aggregate.rb +43 -11
  53. data/lib/hecks/bluebook/behaviour/attribute.rb +18 -5
  54. data/lib/hecks/bluebook/behaviour/chapter.rb +76 -5
  55. data/lib/hecks/bluebook/behaviour/command.rb +55 -25
  56. data/lib/hecks/bluebook/behaviour/domain_port.rb +27 -7
  57. data/lib/hecks/bluebook/behaviour/entity.rb +20 -8
  58. data/lib/hecks/bluebook/behaviour/hexagon.rb +30 -4
  59. data/lib/hecks/bluebook/behaviour/lifecycle.rb +27 -6
  60. data/lib/hecks/bluebook/behaviour/policy.rb +42 -17
  61. data/lib/hecks/bluebook/behaviour/process_manager.rb +39 -8
  62. data/lib/hecks/bluebook/behaviour/query.rb +6 -1
  63. data/lib/hecks/bluebook/behaviour/read_model.rb +29 -8
  64. data/lib/hecks/bluebook/behaviour/traits.rb +48 -12
  65. data/lib/hecks/bluebook/behaviour/value_object.rb +21 -9
  66. data/lib/hecks/bluebook/capabilities.rb +27 -0
  67. data/lib/hecks/bluebook/chapter.rb +51 -9
  68. data/lib/hecks/bluebook/command.rb +62 -17
  69. data/lib/hecks/bluebook/domain_port.rb +34 -9
  70. data/lib/hecks/bluebook/dsl/adapter_builder.rb +24 -0
  71. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +49 -49
  72. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +282 -123
  73. data/lib/hecks/bluebook/dsl/attribute_collector.rb +112 -75
  74. data/lib/hecks/bluebook/dsl/binding_proxy.rb +81 -2
  75. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +486 -117
  76. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +179 -47
  77. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
  78. data/lib/hecks/bluebook/dsl/command_builder.rb +284 -122
  79. data/lib/hecks/bluebook/dsl/const_shim.rb +46 -15
  80. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +90 -25
  81. data/lib/hecks/bluebook/dsl/entity_builder.rb +191 -61
  82. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
  83. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +130 -30
  84. data/lib/hecks/bluebook/dsl/identity_declaration.rb +38 -21
  85. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
  86. data/lib/hecks/bluebook/dsl/policy_builder.rb +86 -36
  87. data/lib/hecks/bluebook/dsl/port_builder.rb +38 -7
  88. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +56 -22
  89. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +111 -47
  90. data/lib/hecks/bluebook/dsl/query_builder.rb +37 -8
  91. data/lib/hecks/bluebook/dsl/read_model_builder.rb +127 -52
  92. data/lib/hecks/bluebook/dsl/rule_reference.rb +97 -43
  93. data/lib/hecks/bluebook/dsl/translation_builder.rb +150 -44
  94. data/lib/hecks/bluebook/dsl/value_object_builder.rb +68 -20
  95. data/lib/hecks/bluebook/dsl/word_gate.rb +59 -53
  96. data/lib/hecks/bluebook/dsl/world_builder.rb +51 -8
  97. data/lib/hecks/bluebook/entity.rb +40 -11
  98. data/lib/hecks/bluebook/expression/ast_json.rb +128 -36
  99. data/lib/hecks/bluebook/expression/ast_reader.rb +32 -3
  100. data/lib/hecks/bluebook/expression/canonical_form.rb +55 -16
  101. data/lib/hecks/bluebook/expression/evaluator.rb +221 -43
  102. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +54 -18
  103. data/lib/hecks/bluebook/expression/resolver.rb +369 -128
  104. data/lib/hecks/bluebook/hexagon.rb +35 -1
  105. data/lib/hecks/bluebook/lifecycle.rb +12 -1
  106. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +2 -1
  107. data/lib/hecks/bluebook/meta_validator/judge.rb +126 -108
  108. data/lib/hecks/bluebook/meta_validator/plan.rb +81 -46
  109. data/lib/hecks/bluebook/meta_validator/port_judge.rb +3 -2
  110. data/lib/hecks/bluebook/meta_validator/readings.rb +200 -50
  111. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +68 -41
  112. data/lib/hecks/bluebook/meta_validator/shapes.rb +166 -21
  113. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +286 -46
  114. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +11 -10
  115. data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -5
  116. data/lib/hecks/bluebook/meta_validator.rb +235 -139
  117. data/lib/hecks/bluebook/model_check.rb +434 -104
  118. data/lib/hecks/bluebook/pattern_subset.rb +32 -10
  119. data/lib/hecks/bluebook/policy.rb +15 -13
  120. data/lib/hecks/bluebook/process_manager.rb +27 -14
  121. data/lib/hecks/bluebook/project_discovery.rb +5 -0
  122. data/lib/hecks/bluebook/project_loader.rb +40 -0
  123. data/lib/hecks/bluebook/project_register.rb +50 -6
  124. data/lib/hecks/bluebook/query.rb +31 -4
  125. data/lib/hecks/bluebook/read_model.rb +35 -15
  126. data/lib/hecks/bluebook/reference.rb +26 -13
  127. data/lib/hecks/bluebook/smoke_test.rb +46 -23
  128. data/lib/hecks/bluebook/synthesizer.rb +46 -12
  129. data/lib/hecks/bluebook/translation.rb +34 -5
  130. data/lib/hecks/bluebook/value_object.rb +29 -11
  131. data/lib/hecks/bluebook.rb +5 -6
  132. data/lib/hecks/codemod.rb +138 -50
  133. data/lib/hecks/construct.rb +21 -7
  134. data/lib/hecks/corpus.rb +438 -0
  135. data/lib/hecks/deploy/bluebook/deploy.hecksagon +19 -0
  136. data/lib/hecks/doc/reference.rb +200 -31
  137. data/lib/hecks/embryonaut_bluebook.rb +38 -15
  138. data/lib/hecks/facade/cli_door.rb +69 -10
  139. data/lib/hecks/facade/cli_runner.rb +105 -24
  140. data/lib/hecks/facade/command_request.rb +23 -0
  141. data/lib/hecks/facade/handle.rb +155 -35
  142. data/lib/hecks/facade/json_door.rb +106 -25
  143. data/lib/hecks/facade/surface/aggregate_door.rb +50 -27
  144. data/lib/hecks/facade/surface/chapter.rb +26 -17
  145. data/lib/hecks/facade/surface.rb +16 -3
  146. data/lib/hecks/facade.rb +15 -4
  147. data/lib/hecks/forms/app.rb +46 -30
  148. data/lib/hecks/forms/command_form_renderer.rb +70 -9
  149. data/lib/hecks/forms/field_renderer.rb +142 -6
  150. data/lib/hecks/forms/field_shape.rb +183 -19
  151. data/lib/hecks/forms/html.rb +51 -7
  152. data/lib/hecks/forms/index_renderer.rb +14 -2
  153. data/lib/hecks/forms/page.rb +14 -0
  154. data/lib/hecks/forms/params.rb +120 -23
  155. data/lib/hecks/forms/port_argument.rb +14 -2
  156. data/lib/hecks/forms/query_form_renderer.rb +65 -2
  157. data/lib/hecks/forms/record_renderer.rb +60 -2
  158. data/lib/hecks/forms/record_table.rb +28 -1
  159. data/lib/hecks/forms/reference_options.rb +24 -0
  160. data/lib/hecks/forms/value_object_shape.rb +13 -3
  161. data/lib/hecks/forms.rb +24 -4
  162. data/lib/hecks/fqn.rb +59 -1
  163. data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
  164. data/lib/hecks/framework/bluebook/privacy.bluebook +155 -0
  165. data/lib/hecks/framework/oidc.json +15 -0
  166. data/lib/hecks/framework.rb +79 -25
  167. data/lib/hecks/freezer.rb +27 -11
  168. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +241 -106
  169. data/lib/hecks/fuzzing/combination_miner.rb +178 -0
  170. data/lib/hecks/fuzzing/concurrent_dispatch.rb +241 -45
  171. data/lib/hecks/fuzzing/coverage_campaign.rb +161 -0
  172. data/lib/hecks/fuzzing/differential.rb +192 -0
  173. data/lib/hecks/fuzzing/domain_generator.rb +871 -0
  174. data/lib/hecks/fuzzing/era_boundary.rb +68 -19
  175. data/lib/hecks/fuzzing/form_census.rb +121 -20
  176. data/lib/hecks/fuzzing/generated_domain_check.rb +171 -0
  177. data/lib/hecks/fuzzing/invalid_value_generator.rb +45 -6
  178. data/lib/hecks/fuzzing/isolated_boot.rb +137 -80
  179. data/lib/hecks/fuzzing/nondeterministic.rb +79 -0
  180. data/lib/hecks/fuzzing/persistence_parity.rb +111 -21
  181. data/lib/hecks/fuzzing/properties/corrections.rb +34 -9
  182. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +300 -51
  183. data/lib/hecks/fuzzing/properties/guards.rb +86 -42
  184. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +80 -32
  185. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +40 -35
  186. data/lib/hecks/fuzzing/properties/outbox.rb +70 -32
  187. data/lib/hecks/fuzzing/properties/querying.rb +82 -28
  188. data/lib/hecks/fuzzing/properties.rb +84 -51
  189. data/lib/hecks/fuzzing/qa_settings.rb +164 -0
  190. data/lib/hecks/fuzzing/replay.rb +241 -133
  191. data/lib/hecks/fuzzing/rotation_priority.rb +48 -28
  192. data/lib/hecks/fuzzing/rust_gap_manifest.rb +139 -0
  193. data/lib/hecks/fuzzing/self_consistency.rb +315 -137
  194. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +46 -40
  195. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +18 -11
  196. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +13 -12
  197. data/lib/hecks/fuzzing/sequence_generator/picker.rb +21 -12
  198. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +59 -32
  199. data/lib/hecks/fuzzing/sequence_generator.rb +158 -23
  200. data/lib/hecks/fuzzing/shrinker.rb +309 -0
  201. data/lib/hecks/fuzzing/structural_skips.rb +37 -130
  202. data/lib/hecks/fuzzing/sweep_depth.rb +12 -4
  203. data/lib/hecks/fuzzing/target_capabilities.rb +107 -29
  204. data/lib/hecks/fuzzing/value_generator.rb +110 -22
  205. data/lib/hecks/fuzzing.rb +5 -0
  206. data/lib/hecks/grammar/evolve.rb +188 -12
  207. data/lib/hecks/grammar.rb +53 -7
  208. data/lib/hecks/ir.rb +51 -20
  209. data/lib/hecks/language/bluebook/bluebook.bluebook +41 -0
  210. data/lib/hecks/language/bluebook/policy.bluebook +11 -1
  211. data/lib/hecks/language/bluebook/vocabulary.bluebook +348 -13
  212. data/lib/hecks/language/hecksagon/hecksagon.bluebook +11 -0
  213. data/lib/hecks/language/oidc.json +5 -0
  214. data/lib/hecks/literal.rb +41 -9
  215. data/lib/hecks/naming.rb +112 -31
  216. data/lib/hecks/ports/access_control.rb +53 -2
  217. data/lib/hecks/ports/agent/answers.rb +83 -6
  218. data/lib/hecks/ports/agent.rb +119 -35
  219. data/lib/hecks/ports/authentication.rb +44 -4
  220. data/lib/hecks/ports/authorization.rb +50 -11
  221. data/lib/hecks/ports/clock.rb +42 -23
  222. data/lib/hecks/ports/extraction.rb +16 -0
  223. data/lib/hecks/ports/identity_assignment.rb +23 -2
  224. data/lib/hecks/ports/identity_generation.rb +17 -3
  225. data/lib/hecks/ports/identity_resolution.rb +17 -1
  226. data/lib/hecks/ports/key_vault.port +6 -0
  227. data/lib/hecks/ports/key_vault.rb +58 -0
  228. data/lib/hecks/ports/loading.rb +4 -0
  229. data/lib/hecks/ports/persistence/append_only.rb +172 -8
  230. data/lib/hecks/ports/persistence/binding_policy.rb +34 -0
  231. data/lib/hecks/ports/persistence/codec_boundary.rb +178 -0
  232. data/lib/hecks/ports/persistence/execution.rb +4 -0
  233. data/lib/hecks/ports/persistence/null_saga_store.rb +12 -1
  234. data/lib/hecks/ports/persistence/plugin.rb +42 -4
  235. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +218 -25
  236. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +77 -9
  237. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +81 -24
  238. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +29 -18
  239. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +144 -60
  240. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +103 -8
  241. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +98 -23
  242. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +282 -109
  243. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +63 -25
  244. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +118 -66
  245. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +51 -28
  246. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +34 -5
  247. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +25 -12
  248. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +129 -34
  249. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +51 -6
  250. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +33 -9
  251. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +16 -0
  252. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +95 -6
  253. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +25 -2
  254. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +314 -90
  255. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +68 -10
  256. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +9 -3
  257. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +9 -2
  258. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +48 -8
  259. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +16 -1
  260. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +36 -5
  261. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +23 -3
  262. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +58 -19
  263. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +96 -5
  264. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +15 -0
  265. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +9 -1
  266. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -2
  267. data/lib/hecks/ports/persistence/plugins/era.rb +12 -2
  268. data/lib/hecks/ports/persistence/remote_runtime.rb +9 -2
  269. data/lib/hecks/ports/persistence/repository_factory.rb +23 -3
  270. data/lib/hecks/ports/persistence/state_codec.rb +319 -0
  271. data/lib/hecks/ports/persistence.rb +36 -1
  272. data/lib/hecks/ports/projection.rb +61 -7
  273. data/lib/hecks/ports/query/in_memory.rb +41 -8
  274. data/lib/hecks/ports/query/ordering.rb +21 -6
  275. data/lib/hecks/ports/query.rb +35 -0
  276. data/lib/hecks/ports.rb +1 -0
  277. data/lib/hecks/projections/bootstrap_table.rb +147 -0
  278. data/lib/hecks/projections/diagrams.rb +315 -79
  279. data/lib/hecks/projections/glossary/html.rb +91 -3
  280. data/lib/hecks/projections/glossary/markdown.rb +50 -3
  281. data/lib/hecks/projections/glossary/mermaid.rb +49 -1
  282. data/lib/hecks/projections/glossary/sections.rb +1 -1
  283. data/lib/hecks/projections/glossary/sentences.rb +110 -5
  284. data/lib/hecks/projections/glossary.rb +173 -31
  285. data/lib/hecks/projections/ir.rb +1 -1
  286. data/lib/hecks/projections/model/deviations.rb +62 -17
  287. data/lib/hecks/projections/model.rb +75 -21
  288. data/lib/hecks/projections/oidc.rb +45 -8
  289. data/lib/hecks/projections/parser_table.rb +53 -12
  290. data/lib/hecks/projections/reference.rb +15 -3
  291. data/lib/hecks/projections/rust_vocabulary.rb +646 -0
  292. data/lib/hecks/projections/shape.rb +10 -2
  293. data/lib/hecks/projections/statements.rb +69 -22
  294. data/lib/hecks/projections/vocabulary.rb +26 -9
  295. data/lib/hecks/projections.rb +5 -3
  296. data/lib/hecks/projector/cli_projector.rb +245 -37
  297. data/lib/hecks/projector/docs_projector.rb +154 -28
  298. data/lib/hecks/projector/exporter.rb +104 -29
  299. data/lib/hecks/projector/ir_projector.rb +7 -1
  300. data/lib/hecks/projector/narrate_projector.rb +144 -23
  301. data/lib/hecks/projector/target.rb +42 -18
  302. data/lib/hecks/projector.rb +86 -18
  303. data/lib/hecks/query_ir.rb +94 -47
  304. data/lib/hecks/query_specification/common/comparators.rb +19 -3
  305. data/lib/hecks/query_specification/common/comparison.rb +109 -27
  306. data/lib/hecks/query_specification/common/dsl.rb +65 -9
  307. data/lib/hecks/query_specification/common/null_policy.rb +61 -15
  308. data/lib/hecks/query_specification/common/null_semantics.rb +4 -0
  309. data/lib/hecks/query_specification/common/options.rb +25 -0
  310. data/lib/hecks/query_specification/field_path.rb +69 -15
  311. data/lib/hecks/query_specification/hop_path.rb +57 -20
  312. data/lib/hecks/query_specification/read_model/specification.rb +4 -0
  313. data/lib/hecks/rendering.rb +9 -3
  314. data/lib/hecks/router/namespace_installer.rb +16 -3
  315. data/lib/hecks/router.rb +56 -1
  316. data/lib/hecks/runtime/aggregate_lock.rb +20 -11
  317. data/lib/hecks/runtime/boot_gates.rb +21 -3
  318. data/lib/hecks/runtime/caller.rb +40 -8
  319. data/lib/hecks/runtime/capability_graph.rb +13 -2
  320. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +40 -41
  321. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +45 -67
  322. data/lib/hecks/runtime/command_interpreter.rb +195 -113
  323. data/lib/hecks/runtime/command_rules/admissibility.rb +231 -80
  324. data/lib/hecks/runtime/command_rules/arithmetic.rb +154 -96
  325. data/lib/hecks/runtime/command_rules/authorization.rb +38 -17
  326. data/lib/hecks/runtime/command_rules/emission.rb +18 -1
  327. data/lib/hecks/runtime/command_rules/references.rb +145 -55
  328. data/lib/hecks/runtime/command_rules.rb +3 -0
  329. data/lib/hecks/runtime/dependency_planning.rb +56 -11
  330. data/lib/hecks/runtime/dispatcher.rb +263 -92
  331. data/lib/hecks/runtime/entity_element.rb +304 -67
  332. data/lib/hecks/runtime/entity_interpreter.rb +149 -88
  333. data/lib/hecks/runtime/errors.rb +37 -23
  334. data/lib/hecks/runtime/event.rb +15 -10
  335. data/lib/hecks/runtime/identity.rb +91 -23
  336. data/lib/hecks/runtime/instance.rb +103 -18
  337. data/lib/hecks/runtime/interpreting.rb +24 -16
  338. data/lib/hecks/runtime/invocation.rb +358 -0
  339. data/lib/hecks/runtime/loader.rb +105 -19
  340. data/lib/hecks/runtime/outbox.rb +164 -26
  341. data/lib/hecks/runtime/policy_interpreter.rb +73 -60
  342. data/lib/hecks/runtime/port_operation_interpreter.rb +42 -19
  343. data/lib/hecks/runtime/query_interpreter.rb +93 -74
  344. data/lib/hecks/runtime/reaction_invocation.rb +73 -28
  345. data/lib/hecks/runtime/read_model_interpreter.rb +60 -44
  346. data/lib/hecks/runtime/rebuild_sweep.rb +32 -4
  347. data/lib/hecks/runtime/reference_hop.rb +48 -6
  348. data/lib/hecks/runtime/refusal_wording.rb +142 -115
  349. data/lib/hecks/runtime/registry/saga_persistence.rb +32 -21
  350. data/lib/hecks/runtime/registry/verification.rb +153 -28
  351. data/lib/hecks/runtime/registry.rb +202 -32
  352. data/lib/hecks/runtime/remote_dispatcher.rb +125 -24
  353. data/lib/hecks/runtime/routing.rb +36 -154
  354. data/lib/hecks/runtime/saga_interpreter/correlation.rb +25 -27
  355. data/lib/hecks/runtime/saga_interpreter.rb +90 -76
  356. data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
  357. data/lib/hecks/runtime/tenant_check.rb +33 -13
  358. data/lib/hecks/runtime/tenant_scope.rb +23 -5
  359. data/lib/hecks/runtime/value/admission.rb +75 -30
  360. data/lib/hecks/runtime/value/coercion.rb +555 -142
  361. data/lib/hecks/runtime/value/entity_list_coercion.rb +132 -60
  362. data/lib/hecks/runtime/value.rb +71 -21
  363. data/lib/hecks/runtime.rb +39 -7
  364. data/lib/hecks/storehouse.rb +368 -72
  365. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +130 -0
  366. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +32 -0
  367. data/lib/hecks/version.rb +3 -3
  368. data/lib/hecks/vocabulary.rb +205 -4
  369. data/lib/hecks.rb +91 -11
  370. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +19 -11
  371. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +24 -12
  372. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +38 -14
  373. metadata +28 -2
@@ -5,43 +5,49 @@ 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
+ #
11
+ # ## Why these shapes
10
12
  #
11
13
  # Ten bugs in one QA session (BUG#7–#16, `QualityControl`'s own
12
14
  # ledger) clustered in a handful of mechanisms — and several were
13
- # only ever reachable because ONE domain happened to declare the
15
+ # only ever reachable because one domain happened to declare the
14
16
  # argument that tripped them: `Roster::Roster.Mark`'s own `to:`
15
17
  # collided with the dispatcher's routing `to:` (BUG#7), a present-
16
18
  # but-null `to:` was misread as a routing envelope (BUG#16), a blank
17
19
  # creating identity minted a phantom aggregate in Rust (BUG#15), a
18
20
  # single-field closed-set argument offered as bare `null` refused
19
- # with different KINDS on the two engines (BUG#14). No other domain
21
+ # with different kinds on the two engines (BUG#14). No other domain
20
22
  # in the rotation could ever have found any of those, because
21
23
  # nothing generated ever produced the shape. This module produces
22
24
  # them — for a configurable fraction of command steps, chosen and
23
- # parameterised from the SAME seeded RNG the rest of the generator
25
+ # parameterised from the same seeded RNG the rest of the generator
24
26
  # already draws from, so `generate(domain, seed:, steps:,
25
27
  # adversarial:)` stays exactly as reproducible per seed as it was.
26
28
  #
27
- # OPT-IN, BY CONSTRUCTION. `adversarial: 0.0` (the default) returns
28
- # before drawing a single random number, so every pinned seed in
29
- # spec/fuzzing, spec/rust_conformance_fuzz_spec.rb, bin/fuzz and
30
- # bin/generate produces byte-for-byte what it produced before this
31
- # module existed. `bin/qa_sweep` turns it on (reading
29
+ # ## Opt-in, by construction
30
+ #
31
+ # `adversarial: 0.0` (the default) returns before drawing a single
32
+ # random number, so every pinned seed in spec/fuzzing,
33
+ # spec/rust_conformance_fuzz_spec.rb, bin/fuzz and bin/generate
34
+ # produces byte-for-byte what it produced before this module
35
+ # existed. `bin/qa_sweep` turns it on (reading
32
36
  # `QualityControlDials::ADVERSARIAL_FRACTION`) — that script is the
33
- # one place a mutated step's own divergence is a FINDING rather than
37
+ # one place a mutated step's own divergence is a finding rather than
34
38
  # a red CI gate.
35
39
  #
36
- # ONE MUTATION PER STEP, applied in `StepBuilder#build_command_step`
37
- # after the arguments and identity are built and BEFORE the step's
40
+ # ## When it applies
41
+ #
42
+ # One mutation per step, applied in `StepBuilder#build_command_step`
43
+ # after the arguments and identity are built and before the step's
38
44
  # own inline dispatch — so the generator's own `known_ids` tracking
39
- # sees what really happened, and so the returned step's `args` ARE
45
+ # sees what really happened, and so the returned step's `args` are
40
46
  # the mutated bytes both `Fuzzing::Replay` (Ruby) and the compiled
41
47
  # conformance binary (Rust) later receive. The step carries an
42
48
  # `"adversarial"` key naming what was done (`mutation`, the bug
43
49
  # 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
50
+ # prints on a found something report so the agent logging the Bug
45
51
  # can name the mechanism, not guess it. Both replay paths ignore
46
52
  # the key: `Replay.call` reads only verb/query/dry_run/args/role,
47
53
  # and `kernel/cli.rs` reads step keys by name.
@@ -72,19 +78,19 @@ module Hecks
72
78
  VALUE_OBJECT_SHAPES = %w[null empty_object].freeze
73
79
 
74
80
  # 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
81
+ # key and an absent required key in the same step, so both engines
76
82
  # have to pick which refusal wins; and each pair, so the ordering
77
83
  # of any two is observable on a command too small for all three.
78
84
  #
79
- # THE SECOND ROW REACHES FURTHER DOWN `DISPATCH_ORDER`
85
+ # The second row reaches further down `DISPATCH_ORDER`
80
86
  # (lib/hecks/vocabulary.rb): `nonexistent` addresses an id nothing
81
87
  # holds (a `hydrate`-stage NotFound), `lifecycle` aims a
82
88
  # transition-guarded command at a real record (an
83
- # `admissible_transition`-stage refusal, IF the record's state
89
+ # `admissible_transition`-stage refusal, if the record's state
84
90
  # refuses it), `role` binds a caller whose role the command does
85
91
  # not name (the `refuse_role_mismatch` stage no generated step
86
92
  # had ever reached). Paired with an argument-stage fault each,
87
- # so the ordering of an EARLY stage against a LATE one is
93
+ # so the ordering of an early stage against a late one is
88
94
  # observable — BUG#13 (ledger_ordering's own NOTES.md) was
89
95
  # exactly an argument-invariant-vs-entity-existence ordering
90
96
  # split, and nothing generated had ever asked the question on
@@ -94,8 +100,8 @@ module Hecks
94
100
  nonexistent+mismatch nonexistent+unknown lifecycle+mismatch role+absent role+nonexistent
95
101
  ].freeze
96
102
 
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`
103
+ # Item 2 of the detection plan (angle-5) — a drawn caller on a
104
+ # role-gated command. `refuse_role_mismatch` is a `DISPATCH_ORDER`
99
105
  # step both engines implement (`command_rules/authorization.rb`,
100
106
  # `rust/src/kernel/repository.rs check_role`) and both replay doors
101
107
  # already read (`Fuzzing::Replay` binds `Hecks.as_caller` from a
@@ -109,24 +115,24 @@ module Hecks
109
115
  # absent_on_gated no caller at all on a gated command — the
110
116
  # unchecked default, recorded so the step reads
111
117
  # 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) —
118
+ # actor_known the role plus an actor this same sequence
119
+ # already granted it to (the authorization
120
+ # provider's declared `grant:` verb succeeded
121
+ # earlier — `catalog[:grant_verbs]`) —
115
122
  # the real `holds_role?` lookup, both sides
116
123
  # actor_unknown the role plus an actor nothing granted —
117
124
  # `holds_role?` must refuse on both
118
- # A SEPARATE LAYER FROM `KINDS`, with its own probability
125
+ # a separate layer from `KINDS`, with its own probability
119
126
  # (`role_draw:` — `QualityControlDials::ROLE_DRAW_PROBABILITY`):
120
127
  # a caller composes with any argument mutation above rather than
121
128
  # competing with it for the one-mutation-per-step slot, and draws
122
129
  # nothing from the RNG when off, so every pinned seed is
123
130
  # byte-identical to before it existed.
124
131
  CALLER_SHAPES = %w[matching mismatched absent_on_gated actor_known actor_unknown].freeze
125
- GRANT_VERB = "Governance::RoleAssignment.Assign".freeze
126
132
  UNKNOWN_ROLE = "Nobody the domain names".freeze
127
133
 
128
134
  # 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
135
+ # of arguments but a preference (picker.rb weights these up when
130
136
  # adversarial) plus an addressing coin: flat one-head-per-hop args
131
137
  # (what every other generated entity step uses) or the routed
132
138
  # `to: { aggregate:, entities: [...] }` envelope BUG#11's own fix
@@ -136,7 +142,7 @@ module Hecks
136
142
  DEEP_ENTITY_WEIGHT = 4
137
143
 
138
144
  # BUG#13's mutation is only ever applicable on a step whose
139
- # parent ALREADY holds an element this same sequence appended —
145
+ # parent already holds an element this same sequence appended —
140
146
  # a rare moment (an append has to have succeeded first, and most
141
147
  # mutated appends are refused), so when it is on offer it is
142
148
  # weighted up the same way the picker weights an unexercised
@@ -147,10 +153,10 @@ module Hecks
147
153
 
148
154
  def adversarial? = @adversarial.positive?
149
155
 
150
- # `[]` — and NO RNG DRAW — when adversarial mode is off. Otherwise
156
+ # `[]` — and no RNG draw — when adversarial mode is off. Otherwise
151
157
  # the deep-entity addressing note (every deep step, when
152
158
  # adversarial), then with probability `@adversarial` exactly one
153
- # mutation among those applicable to THIS step's own command.
159
+ # mutation among those applicable to this step's own command.
154
160
  def adversarial_mutations!(args, entry, catalog)
155
161
  return [] unless adversarial?
156
162
 
@@ -176,7 +182,7 @@ module Hecks
176
182
 
177
183
  heads = [entry[:aggregate], *entry[:chain]].map { |construct| (construct.identified_by || :id).to_s }
178
184
  scalars = heads.map { |head| ValueGenerator.scalar_of(args[head]) }
179
- # The heads leave the flat args — this is the CLEAN routed
185
+ # The heads leave the flat args — this is the clean routed
180
186
  # caller BUG#11's own spec pins (`to: {...}, note: {...}`) —
181
187
  # unless the command itself declares an attribute of that name
182
188
  # (chess's `Piece.Move` declares `id` as a fact too), which
@@ -236,7 +242,7 @@ module Hecks
236
242
 
237
243
  def blank_identity_applicable?(args, entry, catalog) = blank_identity_targets(args, entry, catalog).any?
238
244
 
239
- # The identity heads THIS step supplies itself: a creating
245
+ # The identity heads this step supplies itself: a creating
240
246
  # aggregate command's own (every part of a composite), and an
241
247
  # append's caller-supplied entity identity arguments (an entity is
242
248
  # "created" by its append, and BUG#15's blank-identity question
@@ -344,8 +350,8 @@ module Hecks
344
350
  precedence_shapes_for(args, entry).any?
345
351
  end
346
352
 
347
- # EVERY SHAPE WHOSE EVERY PART THIS STEP CAN CARRY. `nonexistent`
348
- # and `lifecycle` need a command that ACTS on a record (a creating
353
+ # Every shape whose every part this step can carry. `nonexistent`
354
+ # and `lifecycle` need a command that acts on a record (a creating
349
355
  # step has no addressed id to point elsewhere, and no state to be
350
356
  # in) and flat addressing (`deep_entity_addressing!`'s routed `to:`
351
357
  # envelope owns the ids then); `lifecycle` additionally needs a
@@ -401,14 +407,14 @@ module Hecks
401
407
  applied << "unknown"
402
408
  end
403
409
  apply_late_stage_parts!(wanted, args, entry, detail, applied, catalog)
404
- # Reported as what was ACTUALLY done — a command with a single
410
+ # Reported as what was actually done — a command with a single
405
411
  # attribute cannot carry both a dropped and a corrupted one.
406
412
  detail.merge("shape" => applied.sort.join("+"))
407
413
  end
408
414
 
409
- # The three parts that reach PAST the argument gate — see
415
+ # The three parts that reach past the argument gate — see
410
416
  # `PRECEDENCE_SHAPES`' own second row. `nonexistent` re-addresses
411
- # the step's LAST hop (the entity element for an entity command,
417
+ # the step's last hop (the entity element for an entity command,
412
418
  # the aggregate itself otherwise) to an id nothing holds;
413
419
  # `lifecycle` mutates nothing (the record's own state is what
414
420
  # refuses, or doesn't) and is recorded so the pairing is visible;
@@ -433,7 +439,7 @@ module Hecks
433
439
  applied << "role"
434
440
  end
435
441
 
436
- # ── ANGLE-5: a drawn caller on a role-gated command ──────────────
442
+ # ── Angle-5: a drawn caller on a role-gated command ──────────────
437
443
 
438
444
  def role_draw? = @role_draw.positive?
439
445
 
@@ -491,7 +497,7 @@ module Hecks
491
497
  end
492
498
  end
493
499
 
494
- # A GRANT AIMED AT A ROLE SOME COMMAND ACTUALLY DECLARES. Left to
500
+ # A grant aimed at a role some command actually declares. Left to
495
501
  # `ValueGenerator`, `Assign`'s `role_name` is random text
496
502
  # ("hotel"), which no command is gated on — so a granted actor
497
503
  # could never satisfy `holds_role?` for anything, and the
@@ -500,7 +506,7 @@ module Hecks
500
506
  # domain's own declared roles instead; with it off, nothing here
501
507
  # runs (no RNG draw, no change to the args).
502
508
  def steer_grant!(args, entry, catalog)
503
- return unless role_draw? && entry[:verb] == GRANT_VERB && catalog[:roles].any?
509
+ return unless role_draw? && catalog[:grant_verbs].include?(entry[:verb]) && catalog[:roles].any?
504
510
  return unless args.key?("role_name")
505
511
 
506
512
  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 real — the 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