hecks 1.2.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 (358) 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 +60 -2
  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/postgres_era.adapter +5 -0
  23. data/lib/hecks/adapters/driven/prism.rb +4 -4
  24. data/lib/hecks/adapters/driven/sql_query_builder.rb +34 -22
  25. data/lib/hecks/adapters/driven/sqlite/codec.rb +38 -10
  26. data/lib/hecks/adapters/driven/sqlite/projection.rb +60 -32
  27. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +12 -12
  28. data/lib/hecks/adapters/driven/sqlite.rb +181 -21
  29. data/lib/hecks/adapters/driven.rb +4 -4
  30. data/lib/hecks/adapters/driving/github_webhook.rb +145 -0
  31. data/lib/hecks/behaviors/dsl.rb +2 -2
  32. data/lib/hecks/behaviors/expectations.rb +51 -23
  33. data/lib/hecks/behaviors/ir.rb +1 -1
  34. data/lib/hecks/behaviors/rspec.rb +1 -1
  35. data/lib/hecks/behaviors/runner.rb +2 -2
  36. data/lib/hecks/behaviors.rb +1 -1
  37. data/lib/hecks/bluebook/aggregate.rb +13 -13
  38. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +10 -10
  39. data/lib/hecks/bluebook/assembly/build.rb +1 -1
  40. data/lib/hecks/bluebook/assembly/contract.rb +39 -16
  41. data/lib/hecks/bluebook/assembly/contracts.rb +40 -38
  42. data/lib/hecks/bluebook/assembly/marks.rb +18 -18
  43. data/lib/hecks/bluebook/assembly/specializer.rb +19 -21
  44. data/lib/hecks/bluebook/assembly.rb +9 -9
  45. data/lib/hecks/bluebook/attribute.rb +9 -9
  46. data/lib/hecks/bluebook/behaviour/aggregate.rb +11 -11
  47. data/lib/hecks/bluebook/behaviour/attribute.rb +5 -5
  48. data/lib/hecks/bluebook/behaviour/chapter.rb +23 -5
  49. data/lib/hecks/bluebook/behaviour/command.rb +23 -23
  50. data/lib/hecks/bluebook/behaviour/domain_port.rb +27 -3
  51. data/lib/hecks/bluebook/behaviour/entity.rb +8 -8
  52. data/lib/hecks/bluebook/behaviour/hexagon.rb +4 -4
  53. data/lib/hecks/bluebook/behaviour/lifecycle.rb +5 -5
  54. data/lib/hecks/bluebook/behaviour/policy.rb +12 -12
  55. data/lib/hecks/bluebook/behaviour/process_manager.rb +7 -7
  56. data/lib/hecks/bluebook/behaviour/query.rb +1 -1
  57. data/lib/hecks/bluebook/behaviour/read_model.rb +8 -8
  58. data/lib/hecks/bluebook/behaviour/traits.rb +12 -12
  59. data/lib/hecks/bluebook/behaviour/value_object.rb +6 -6
  60. data/lib/hecks/bluebook/capabilities.rb +27 -0
  61. data/lib/hecks/bluebook/chapter.rb +28 -9
  62. data/lib/hecks/bluebook/command.rb +12 -12
  63. data/lib/hecks/bluebook/domain_port.rb +9 -9
  64. data/lib/hecks/bluebook/dsl/adapter_builder.rb +24 -0
  65. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +49 -49
  66. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +96 -96
  67. data/lib/hecks/bluebook/dsl/attribute_collector.rb +41 -41
  68. data/lib/hecks/bluebook/dsl/binding_proxy.rb +22 -2
  69. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +111 -74
  70. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +48 -30
  71. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
  72. data/lib/hecks/bluebook/dsl/command_builder.rb +103 -103
  73. data/lib/hecks/bluebook/dsl/const_shim.rb +46 -15
  74. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +90 -25
  75. data/lib/hecks/bluebook/dsl/entity_builder.rb +56 -56
  76. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
  77. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +89 -30
  78. data/lib/hecks/bluebook/dsl/identity_declaration.rb +17 -17
  79. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
  80. data/lib/hecks/bluebook/dsl/policy_builder.rb +30 -21
  81. data/lib/hecks/bluebook/dsl/port_builder.rb +38 -7
  82. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +56 -22
  83. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +35 -35
  84. data/lib/hecks/bluebook/dsl/query_builder.rb +5 -5
  85. data/lib/hecks/bluebook/dsl/read_model_builder.rb +34 -34
  86. data/lib/hecks/bluebook/dsl/rule_reference.rb +41 -39
  87. data/lib/hecks/bluebook/dsl/translation_builder.rb +9 -9
  88. data/lib/hecks/bluebook/dsl/value_object_builder.rb +16 -16
  89. data/lib/hecks/bluebook/dsl/word_gate.rb +59 -53
  90. data/lib/hecks/bluebook/dsl/world_builder.rb +51 -8
  91. data/lib/hecks/bluebook/entity.rb +11 -11
  92. data/lib/hecks/bluebook/expression/ast_json.rb +20 -20
  93. data/lib/hecks/bluebook/expression/ast_reader.rb +3 -3
  94. data/lib/hecks/bluebook/expression/canonical_form.rb +9 -9
  95. data/lib/hecks/bluebook/expression/evaluator.rb +18 -18
  96. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +18 -18
  97. data/lib/hecks/bluebook/expression/resolver.rb +60 -62
  98. data/lib/hecks/bluebook/hexagon.rb +1 -1
  99. data/lib/hecks/bluebook/lifecycle.rb +1 -1
  100. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -1
  101. data/lib/hecks/bluebook/meta_validator/judge.rb +122 -86
  102. data/lib/hecks/bluebook/meta_validator/plan.rb +39 -39
  103. data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -2
  104. data/lib/hecks/bluebook/meta_validator/readings.rb +43 -43
  105. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +44 -37
  106. data/lib/hecks/bluebook/meta_validator/shapes.rb +25 -21
  107. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +145 -31
  108. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +6 -6
  109. data/lib/hecks/bluebook/meta_validator/world_judge.rb +5 -5
  110. data/lib/hecks/bluebook/meta_validator.rb +70 -70
  111. data/lib/hecks/bluebook/model_check.rb +301 -84
  112. data/lib/hecks/bluebook/pattern_subset.rb +9 -9
  113. data/lib/hecks/bluebook/policy.rb +15 -13
  114. data/lib/hecks/bluebook/process_manager.rb +14 -14
  115. data/lib/hecks/bluebook/project_register.rb +6 -6
  116. data/lib/hecks/bluebook/query.rb +4 -4
  117. data/lib/hecks/bluebook/read_model.rb +14 -14
  118. data/lib/hecks/bluebook/reference.rb +8 -8
  119. data/lib/hecks/bluebook/smoke_test.rb +19 -19
  120. data/lib/hecks/bluebook/synthesizer.rb +12 -12
  121. data/lib/hecks/bluebook/translation.rb +4 -4
  122. data/lib/hecks/bluebook/value_object.rb +6 -6
  123. data/lib/hecks/bluebook.rb +2 -2
  124. data/lib/hecks/codemod/legacy_dispatch_args.rb +299 -0
  125. data/lib/hecks/codemod/legacy_dispatch_recorder.rb +186 -0
  126. data/lib/hecks/codemod.rb +36 -35
  127. data/lib/hecks/construct.rb +6 -6
  128. data/lib/hecks/corpus.rb +317 -0
  129. data/lib/hecks/deprecation.rb +95 -0
  130. data/lib/hecks/doc/reference.rb +19 -19
  131. data/lib/hecks/embryonaut_bluebook.rb +11 -11
  132. data/lib/hecks/facade/cli_door.rb +69 -10
  133. data/lib/hecks/facade/cli_runner.rb +105 -24
  134. data/lib/hecks/facade/command_request.rb +23 -0
  135. data/lib/hecks/facade/handle.rb +79 -32
  136. data/lib/hecks/facade/json_door.rb +106 -25
  137. data/lib/hecks/facade/surface/aggregate_door.rb +42 -27
  138. data/lib/hecks/facade/surface/chapter.rb +26 -17
  139. data/lib/hecks/facade/surface.rb +16 -3
  140. data/lib/hecks/facade.rb +15 -4
  141. data/lib/hecks/forms/app.rb +46 -30
  142. data/lib/hecks/forms/command_form_renderer.rb +70 -9
  143. data/lib/hecks/forms/field_renderer.rb +142 -6
  144. data/lib/hecks/forms/field_shape.rb +183 -20
  145. data/lib/hecks/forms/html.rb +51 -7
  146. data/lib/hecks/forms/index_renderer.rb +14 -2
  147. data/lib/hecks/forms/params.rb +120 -23
  148. data/lib/hecks/forms/port_argument.rb +2 -2
  149. data/lib/hecks/forms/query_form_renderer.rb +2 -2
  150. data/lib/hecks/forms/record_renderer.rb +2 -2
  151. data/lib/hecks/forms/record_table.rb +1 -1
  152. data/lib/hecks/forms/value_object_shape.rb +3 -3
  153. data/lib/hecks/forms.rb +24 -4
  154. data/lib/hecks/fqn.rb +1 -1
  155. data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
  156. data/lib/hecks/framework.rb +48 -17
  157. data/lib/hecks/freezer.rb +11 -11
  158. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +90 -90
  159. data/lib/hecks/fuzzing/combination_miner.rb +119 -0
  160. data/lib/hecks/fuzzing/concurrent_dispatch.rb +361 -0
  161. data/lib/hecks/fuzzing/coverage_campaign.rb +118 -0
  162. data/lib/hecks/fuzzing/differential.rb +158 -0
  163. data/lib/hecks/fuzzing/domain_generator.rb +694 -0
  164. data/lib/hecks/fuzzing/era_boundary.rb +124 -0
  165. data/lib/hecks/fuzzing/form_census.rb +199 -0
  166. data/lib/hecks/fuzzing/generated_domain_check.rb +95 -0
  167. data/lib/hecks/fuzzing/invalid_value_generator.rb +6 -6
  168. data/lib/hecks/fuzzing/isolated_boot.rb +226 -38
  169. data/lib/hecks/fuzzing/nondeterministic.rb +67 -0
  170. data/lib/hecks/fuzzing/persistence_parity.rb +161 -0
  171. data/lib/hecks/fuzzing/properties/corrections.rb +100 -0
  172. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +321 -41
  173. data/lib/hecks/fuzzing/properties/guards.rb +129 -26
  174. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +32 -32
  175. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +31 -11
  176. data/lib/hecks/fuzzing/properties/outbox.rb +142 -0
  177. data/lib/hecks/fuzzing/properties/querying.rb +17 -17
  178. data/lib/hecks/fuzzing/properties.rb +84 -39
  179. data/lib/hecks/fuzzing/qa_settings.rb +152 -0
  180. data/lib/hecks/fuzzing/replay.rb +224 -114
  181. data/lib/hecks/fuzzing/rotation_priority.rb +94 -0
  182. data/lib/hecks/fuzzing/rust_gap_manifest.rb +113 -0
  183. data/lib/hecks/fuzzing/self_consistency.rb +676 -0
  184. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +526 -0
  185. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +101 -30
  186. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +55 -8
  187. data/lib/hecks/fuzzing/sequence_generator/picker.rb +31 -11
  188. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +123 -32
  189. data/lib/hecks/fuzzing/sequence_generator.rb +152 -31
  190. data/lib/hecks/fuzzing/shrinker.rb +197 -0
  191. data/lib/hecks/fuzzing/structural_skips.rb +39 -0
  192. data/lib/hecks/fuzzing/sweep_depth.rb +53 -0
  193. data/lib/hecks/fuzzing/target_capabilities.rb +180 -0
  194. data/lib/hecks/fuzzing/value_generator.rb +66 -14
  195. data/lib/hecks/fuzzing.rb +11 -0
  196. data/lib/hecks/grammar/evolve.rb +10 -10
  197. data/lib/hecks/grammar.rb +7 -7
  198. data/lib/hecks/ir.rb +13 -13
  199. data/lib/hecks/language/bluebook/bluebook.bluebook +41 -0
  200. data/lib/hecks/language/bluebook/policy.bluebook +11 -1
  201. data/lib/hecks/language/bluebook/vocabulary.bluebook +365 -15
  202. data/lib/hecks/language/oidc.json +5 -0
  203. data/lib/hecks/literal.rb +9 -9
  204. data/lib/hecks/naming.rb +89 -21
  205. data/lib/hecks/ports/access_control.rb +58 -2
  206. data/lib/hecks/ports/agent/answers.rb +83 -6
  207. data/lib/hecks/ports/agent.rb +119 -35
  208. data/lib/hecks/ports/authentication.rb +44 -4
  209. data/lib/hecks/ports/authorization.rb +53 -11
  210. data/lib/hecks/ports/clock.rb +42 -23
  211. data/lib/hecks/ports/extraction.rb +16 -0
  212. data/lib/hecks/ports/identity_assignment.rb +24 -2
  213. data/lib/hecks/ports/identity_generation.rb +17 -3
  214. data/lib/hecks/ports/identity_resolution.rb +18 -1
  215. data/lib/hecks/ports/loading.rb +4 -0
  216. data/lib/hecks/ports/persistence/append_only.rb +172 -8
  217. data/lib/hecks/ports/persistence/binding_policy.rb +34 -0
  218. data/lib/hecks/ports/persistence/codec_boundary.rb +178 -0
  219. data/lib/hecks/ports/persistence/execution.rb +4 -0
  220. data/lib/hecks/ports/persistence/null_saga_store.rb +12 -1
  221. data/lib/hecks/ports/persistence/plugin.rb +42 -4
  222. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +218 -25
  223. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +77 -9
  224. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +81 -24
  225. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +29 -18
  226. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +144 -60
  227. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +103 -8
  228. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +98 -23
  229. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +282 -109
  230. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +63 -25
  231. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +160 -58
  232. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +51 -28
  233. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +28 -5
  234. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +25 -12
  235. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +129 -34
  236. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +51 -6
  237. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +44 -6
  238. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +16 -0
  239. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +57 -4
  240. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +25 -2
  241. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +338 -83
  242. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +68 -10
  243. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +9 -3
  244. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +9 -2
  245. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +42 -8
  246. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +16 -1
  247. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +36 -5
  248. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +23 -3
  249. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +18 -19
  250. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +5 -5
  251. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +1 -1
  252. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -2
  253. data/lib/hecks/ports/persistence/plugins/era.rb +12 -2
  254. data/lib/hecks/ports/persistence/remote_runtime.rb +9 -2
  255. data/lib/hecks/ports/persistence/repository_factory.rb +29 -6
  256. data/lib/hecks/ports/persistence/state_codec.rb +319 -0
  257. data/lib/hecks/ports/persistence.rb +36 -1
  258. data/lib/hecks/ports/projection.rb +61 -7
  259. data/lib/hecks/ports/query/in_memory.rb +3 -3
  260. data/lib/hecks/ports/query/ordering.rb +6 -6
  261. data/lib/hecks/ports/query.rb +35 -0
  262. data/lib/hecks/projections/bootstrap_table.rb +112 -0
  263. data/lib/hecks/projections/diagrams.rb +75 -75
  264. data/lib/hecks/projections/glossary/html.rb +250 -0
  265. data/lib/hecks/projections/glossary/markdown.rb +105 -0
  266. data/lib/hecks/projections/glossary/mermaid.rb +110 -0
  267. data/lib/hecks/projections/glossary/page.css +271 -0
  268. data/lib/hecks/projections/glossary/page.js +72 -0
  269. data/lib/hecks/projections/glossary/sections.rb +17 -0
  270. data/lib/hecks/projections/glossary/sentences.rb +205 -0
  271. data/lib/hecks/projections/glossary.rb +214 -286
  272. data/lib/hecks/projections/ir.rb +1 -1
  273. data/lib/hecks/projections/model/deviations.rb +18 -17
  274. data/lib/hecks/projections/model.rb +25 -21
  275. data/lib/hecks/projections/oidc.rb +7 -7
  276. data/lib/hecks/projections/parser_table.rb +5 -5
  277. data/lib/hecks/projections/reference.rb +3 -3
  278. data/lib/hecks/projections/rust_vocabulary.rb +443 -0
  279. data/lib/hecks/projections/shape.rb +2 -2
  280. data/lib/hecks/projections/statements.rb +11 -11
  281. data/lib/hecks/projections/vocabulary.rb +9 -9
  282. data/lib/hecks/projections.rb +5 -3
  283. data/lib/hecks/projector/cli_projector.rb +29 -29
  284. data/lib/hecks/projector/docs_projector.rb +13 -13
  285. data/lib/hecks/projector/exporter.rb +42 -21
  286. data/lib/hecks/projector/ir_projector.rb +1 -1
  287. data/lib/hecks/projector/narrate_projector.rb +15 -22
  288. data/lib/hecks/projector/target.rb +13 -13
  289. data/lib/hecks/projector.rb +15 -15
  290. data/lib/hecks/query_ir.rb +47 -47
  291. data/lib/hecks/query_specification/common/comparators.rb +19 -3
  292. data/lib/hecks/query_specification/common/comparison.rb +132 -24
  293. data/lib/hecks/query_specification/common/dsl.rb +65 -9
  294. data/lib/hecks/query_specification/common/null_policy.rb +57 -13
  295. data/lib/hecks/query_specification/common/null_semantics.rb +4 -0
  296. data/lib/hecks/query_specification/common/options.rb +25 -0
  297. data/lib/hecks/query_specification/field_path.rb +69 -15
  298. data/lib/hecks/query_specification/hop_path.rb +57 -20
  299. data/lib/hecks/query_specification/read_model/specification.rb +4 -0
  300. data/lib/hecks/rendering.rb +3 -3
  301. data/lib/hecks/router/namespace_installer.rb +3 -3
  302. data/lib/hecks/router.rb +1 -1
  303. data/lib/hecks/runtime/aggregate_lock.rb +11 -11
  304. data/lib/hecks/runtime/boot_gates.rb +3 -3
  305. data/lib/hecks/runtime/caller.rb +8 -8
  306. data/lib/hecks/runtime/capability_graph.rb +2 -2
  307. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +22 -25
  308. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +64 -67
  309. data/lib/hecks/runtime/command_interpreter.rb +159 -102
  310. data/lib/hecks/runtime/command_rules/admissibility.rb +69 -69
  311. data/lib/hecks/runtime/command_rules/arithmetic.rb +137 -91
  312. data/lib/hecks/runtime/command_rules/authorization.rb +38 -17
  313. data/lib/hecks/runtime/command_rules/emission.rb +18 -1
  314. data/lib/hecks/runtime/command_rules/references.rb +94 -19
  315. data/lib/hecks/runtime/command_rules.rb +3 -0
  316. data/lib/hecks/runtime/dependency_planning.rb +11 -11
  317. data/lib/hecks/runtime/dispatcher.rb +283 -90
  318. data/lib/hecks/runtime/entity_element.rb +282 -46
  319. data/lib/hecks/runtime/entity_interpreter.rb +160 -69
  320. data/lib/hecks/runtime/errors.rb +19 -19
  321. data/lib/hecks/runtime/event.rb +6 -6
  322. data/lib/hecks/runtime/identity.rb +22 -22
  323. data/lib/hecks/runtime/instance.rb +39 -14
  324. data/lib/hecks/runtime/interpreting.rb +12 -12
  325. data/lib/hecks/runtime/invocation.rb +276 -0
  326. data/lib/hecks/runtime/loader.rb +14 -14
  327. data/lib/hecks/runtime/outbox.rb +23 -23
  328. data/lib/hecks/runtime/policy_interpreter.rb +54 -54
  329. data/lib/hecks/runtime/port_operation_interpreter.rb +22 -19
  330. data/lib/hecks/runtime/query_interpreter.rb +111 -56
  331. data/lib/hecks/runtime/reaction_invocation.rb +76 -9
  332. data/lib/hecks/runtime/read_model_interpreter.rb +40 -40
  333. data/lib/hecks/runtime/rebuild_sweep.rb +4 -4
  334. data/lib/hecks/runtime/reference_hop.rb +6 -6
  335. data/lib/hecks/runtime/refusal_wording.rb +92 -112
  336. data/lib/hecks/runtime/registry/saga_persistence.rb +21 -21
  337. data/lib/hecks/runtime/registry/verification.rb +36 -26
  338. data/lib/hecks/runtime/registry.rb +56 -27
  339. data/lib/hecks/runtime/remote_dispatcher.rb +38 -23
  340. data/lib/hecks/runtime/routing.rb +10 -88
  341. data/lib/hecks/runtime/saga_interpreter/correlation.rb +17 -17
  342. data/lib/hecks/runtime/saga_interpreter.rb +93 -56
  343. data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
  344. data/lib/hecks/runtime/tenant_check.rb +9 -9
  345. data/lib/hecks/runtime/tenant_scope.rb +5 -5
  346. data/lib/hecks/runtime/value/admission.rb +75 -30
  347. data/lib/hecks/runtime/value/coercion.rb +379 -226
  348. data/lib/hecks/runtime/value/entity_list_coercion.rb +248 -0
  349. data/lib/hecks/runtime/value.rb +28 -23
  350. data/lib/hecks/runtime.rb +7 -7
  351. data/lib/hecks/storehouse.rb +64 -64
  352. data/lib/hecks/version.rb +3 -3
  353. data/lib/hecks/vocabulary.rb +207 -5
  354. data/lib/hecks.rb +13 -11
  355. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +11 -11
  356. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +14 -12
  357. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +11 -11
  358. metadata +43 -2
@@ -0,0 +1,180 @@
1
+ module Hecks
2
+ module Fuzzing
3
+ # What a sweep target can actually be checked for, read off the
4
+ # filesystem — never off a stored list.
5
+ #
6
+ # `bin/qa_sweep` used to decide its one comparison mode inline: "is
7
+ # there a Cargo feature named after this directory? then
8
+ # `:differential`, else `:ruby_only`", and a separate hand-typed abort
9
+ # for `--persistence-parity` ("does any .hecksagon bind PostgresEra?").
10
+ # Every further mode the practice adds (era boundary, concurrency, a
11
+ # WASM front) would have grown one more inline `if`, each one a
12
+ # policy decision hiding in a script. This module is those decisions
13
+ # as data: `MODE_REQUIREMENTS` says which capabilities each mode
14
+ # needs, `infer` says which capabilities a target's own directory
15
+ # actually has, and `resolve` is the one rule that joins them —
16
+ # `modes_to_run = enabled ∩ eligible`.
17
+ #
18
+ # Inference decides; a stored list only records. `Target.capabilities`
19
+ # (qa/bluebook/quality_control.bluebook, once PR-1's era lands) is
20
+ # written by the runner from exactly this inference at release time
21
+ # so `Target.EligibleFor(mode)` can audit the rotation from the
22
+ # ledger alone — but the runner re-infers every sweep, because a
23
+ # stored list that lags yesterday's Cargo feature is precisely the
24
+ # "quiet divergence" (the chapter's own opening comment) this whole
25
+ # practice exists to hunt. Nothing here ever reads the ledger.
26
+ #
27
+ # Every regex is one the harness already owned, moved here rather than
28
+ # re-derived, and each one's provenance is named beside it so a
29
+ # future edit to the original site is a visible drift, not a silent
30
+ # one: the Cargo feature line (`RustConformanceHelpers#build_rust_for`),
31
+ # the PostgresEra binding (`bin/qa_sweep`'s old `POSTGRES_ERA_BINDING`),
32
+ # the translations glob (`IsolatedBoot#strip_translations!`).
33
+ module TargetCapabilities
34
+ module_function
35
+
36
+ # `RustConformanceHelpers#build_rust_for`'s own test, scoped to the
37
+ # `[features]` table the way `bin/project_rust`'s Cargo sync scopes
38
+ # its own lookup (a `[package] name = "rust"` line must never read
39
+ # as a feature named `rust`).
40
+ FEATURES_TABLE = /^\[features\](?:\n(?!\[).*)*$/
41
+
42
+ # `bin/qa_sweep`'s former `POSTGRES_ERA_BINDING` — both spellings
43
+ # `IsolatedBoot#rewrite_bindings!` has to catch: aggregate-scoped
44
+ # (`Directory::Member.persisted_by("PostgresEra")`) and the bare
45
+ # domain-level default (`persisted_by "PostgresEra"`).
46
+ POSTGRES_ERA_BINDING = /persisted_by\s*\(?\s*"PostgresEra"/
47
+
48
+ # `HecksagonBuilder#uses_framework "X"` — captures which member a
49
+ # hecksagon attaches, whatever its name. Whether that member answers
50
+ # a role check is then read off its own declaration (`provides
51
+ # "authorization"`, via `Framework.providers_of`), the same rule
52
+ # `Registry#authorization_provider_for` applies at runtime — never a
53
+ # match on the literal "Governance".
54
+ FRAMEWORK_ATTACHED = /uses_framework\s*\(?\s*"([^"]+)"/
55
+
56
+ # A command-level `role "..."` — the only construct
57
+ # `refuse_role_mismatch` ever has anything to check a caller against.
58
+ ROLE_GATED = /^\s*role\s+"/
59
+
60
+ # `authorize :vault_access, tenant: :branch_code`
61
+ # (examples/banking/bluebook/safe_deposit_boxes.bluebook) — the one
62
+ # `tenant:` spelling the corpus has.
63
+ TENANT_SCOPED = /\btenant:/
64
+
65
+ PROCESS_MANAGER = /^\s*process_manager\s+"/
66
+
67
+ # Which capabilities each mode needs before it can say anything true
68
+ # about a target. An empty list means "any target at all" — every
69
+ # domain boots under Memory, so Ruby-only properties and the
70
+ # self-consistency pass are always answerable. `ruby_only` is listed
71
+ # requirement-free on purpose and then excluded by `resolve` whenever
72
+ # `differential` resolved too: they are the same seat, and a compiled
73
+ # Rust binary is strictly the better occupant (item 1 of the
74
+ # detection plan folded the Ruby-only property battery into the
75
+ # differential seat, so nothing is lost by the exclusion).
76
+ #
77
+ # The four `false`-by-default modes in `QualityControlDials::MODES`
78
+ # (`adapter_parity_postgres`, `era_boundary`, `concurrency`,
79
+ # `wasm_front`) are named here with their requirements even though
80
+ # nothing runs them yet — so `resolved modes:` can already say, per
81
+ # target, which of them would be eligible the day a human flips the
82
+ # dial, and so flipping it is a one-line data change rather than a
83
+ # code change plus a data change.
84
+ MODE_REQUIREMENTS = {
85
+ differential: %w[rust],
86
+ ruby_only: [],
87
+ self_consistency: [],
88
+ properties_in_differential: %w[rust],
89
+ structural_skip_report: %w[rust],
90
+ adapter_parity_sqlite: %w[sqlite],
91
+ persistence_parity: %w[postgres_era],
92
+ adapter_parity_postgres: %w[postgres_era],
93
+ era_boundary: %w[translations postgres_era],
94
+ concurrency: %w[postgres_era],
95
+ wasm_front: %w[rust]
96
+ }.freeze
97
+
98
+ # Which of those modes `bin/qa_sweep` can actually run today.
99
+ # `MODE_REQUIREMENTS` above says what a mode needs; this says what
100
+ # exists to do it, and the two are not the same. Conflating them is
101
+ # how `qa/settings.yml` came to enable `wasm_front` and
102
+ # `adapter_parity_postgres` with nothing behind either — no seat, no
103
+ # check folded into the seed loop, no `MODE_EXPECTATIONS` entry — while
104
+ # `resolved modes:` still printed them per target, so the sweep
105
+ # advertised coverage it never performed. A mode named in
106
+ # `MODE_REQUIREMENTS` but absent here is one this practice wants and has
107
+ # not built: `bin/qa_sweep` refuses to start when a dial or `--modes`
108
+ # enables it, rather than resolving it into a line nobody can act on.
109
+ # `spec/qa_sweep_runnable_modes_spec.rb` keeps this list honest from
110
+ # both sides by grepping the runner itself.
111
+ RUNNABLE_MODES = %i[differential ruby_only self_consistency properties_in_differential
112
+ structural_skip_report adapter_parity_sqlite persistence_parity
113
+ era_boundary concurrency].freeze
114
+
115
+ # Modes that name a separate, expensive pass of their own rather than
116
+ # an extra check folded into the ordinary per-seed loop —
117
+ # `bin/qa_sweep` runs these only when asked by name (`--modes
118
+ # persistence_parity`, or its older alias `--persistence-parity`) or
119
+ # as `--all`'s own second wave, never silently inside a plain
120
+ # single-target sweep (the seed cap `PERSISTENCE_PARITY_SEED_CAP`
121
+ # exists because that pass pays for real Postgres I/O per dispatch).
122
+ DEFERRED_MODES = %i[persistence_parity adapter_parity_postgres era_boundary concurrency].freeze
123
+
124
+ # Sorted, plain strings — comma-joined by the runner into the
125
+ # `Target.Release(capabilities:)` value object and printed verbatim
126
+ # on the `resolved modes:` line, so the same spelling is what a
127
+ # human reads, what `--all` parses back, and what the ledger stores.
128
+ def infer(domain_path, rust_dir: File.expand_path("../../../rust", __dir__))
129
+ capabilities = %w[sqlite]
130
+ capabilities << "rust" if rust_feature?(domain_path, rust_dir)
131
+ capabilities << "postgres_era" if any_file?(domain_path, "*.hecksagon", POSTGRES_ERA_BINDING)
132
+ capabilities << "translations" if Dir.glob(File.join(domain_path, "**", "translations", "*.bluebook")).any?
133
+ capabilities << "governance" if authorization_attached?(domain_path)
134
+ capabilities << "role_gated" if any_file?(domain_path, "*.bluebook", ROLE_GATED)
135
+ capabilities << "tenant" if any_file?(domain_path, "*.bluebook", TENANT_SCOPED)
136
+ capabilities << "sagas" if any_file?(domain_path, "*.bluebook", PROCESS_MANAGER)
137
+ capabilities.sort
138
+ end
139
+
140
+ def eligible?(mode, capabilities)
141
+ required = MODE_REQUIREMENTS.fetch(mode.to_sym) { raise ArgumentError, "unknown sweep mode #{mode.inspect}" }
142
+ (required - capabilities).empty?
143
+ end
144
+
145
+ # The one rule. `enabled` is whatever the dial (or `--modes`) turned
146
+ # on, in the dial's own declaration order — that order is preserved
147
+ # so the printed line reads the same way the dial does. Then the
148
+ # single exclusion named on `MODE_REQUIREMENTS`.
149
+ def resolve(enabled, capabilities)
150
+ resolved = enabled.map(&:to_sym).select { |mode| eligible?(mode, capabilities) }
151
+ resolved.delete(:ruby_only) if resolved.include?(:differential)
152
+ resolved
153
+ end
154
+
155
+ def rust_feature?(domain_path, rust_dir)
156
+ cargo_toml = File.join(rust_dir, "Cargo.toml")
157
+ return false unless File.file?(cargo_toml)
158
+
159
+ feature = File.basename(domain_path).downcase
160
+ features = File.read(cargo_toml)[FEATURES_TABLE] || ""
161
+ features.match?(/^#{Regexp.escape(feature)}\s*=\s*\[\]/)
162
+ end
163
+
164
+ # The capability label stays "governance" — it is the value
165
+ # `bin/qa_sweep` writes into the QualityControl ledger's
166
+ # `Target.capabilities`, and renaming it is a ledger change, not
167
+ # part of dropping the name check.
168
+ def authorization_attached?(domain_path)
169
+ attached = Dir.glob(File.join(domain_path, "**", "*.hecksagon"))
170
+ .flat_map { |path| File.read(path).scan(FRAMEWORK_ATTACHED).flatten }.uniq
171
+ providers = Framework.providers_of(Bluebook::Capabilities::AUTHORIZATION)
172
+ attached.intersect?(providers)
173
+ end
174
+
175
+ def any_file?(domain_path, glob, pattern)
176
+ Dir.glob(File.join(domain_path, "**", glob)).any? { |path| File.read(path).match?(pattern) }
177
+ end
178
+ end
179
+ end
180
+ end
@@ -4,7 +4,7 @@ module Hecks
4
4
  module Fuzzing
5
5
  # One attribute, one value — in the exact JSON shape the hand-written
6
6
  # corpus already uses (a value-object-typed attribute is a nested hash keyed
7
- # by its own field names ; a reference is the BARE ID of the head it points
7
+ # by its own field names ; a reference is the bare ID of the head it points
8
8
  # at, because that is what a reference is). Everything
9
9
  # returned is a plain, JSON-safe Ruby value : String keys throughout, never
10
10
  # symbols, so a generated step can be dumped straight to JSON.
@@ -15,7 +15,7 @@ module Hecks
15
15
  # codebase," and a fixed corpus of a few dozen examples rarely happens to
16
16
  # exercise the boundary that actually breaks. But mostly not : an attribute
17
17
  # named `email`/`currency` almost always needs a specific shape just to get
18
- # PAST its own invariant (`address.include?("@")`, `currency.size == 3`) —
18
+ # past its own invariant (`address.include?("@")`, `currency.size == 3`) —
19
19
  # banking's very first fuzz run never got past Customer.Register, because
20
20
  # no plain random word ever contains "@". Reaching deep state matters more
21
21
  # than edge-casing every single field every single time, so the "normal"
@@ -31,7 +31,7 @@ module Hecks
31
31
  # (lib/hecks/runtime/value/coercion.rb) has accepted a bare
32
32
  # `"large"` in place of `{"value" => "large"}` for any single-field
33
33
  # value object since 86727afd — but until this generator actually
34
- # PRODUCES that shape, nothing exercises it: neither the adapter-
34
+ # produces that shape, nothing exercises it: neither the adapter-
35
35
  # agreement gate nor the Rust/WASM `from_json` codegen (rust/project/
36
36
  # json_codec.rb) can ever be caught drifting on a shape they're
37
37
  # never handed.
@@ -44,15 +44,60 @@ module Hecks
44
44
  # PRD 05 (numeric-boundary-coverage) — Bignum (`2**100`, past i64's
45
45
  # own ceiling, which Ruby's own `Integer` has no such ceiling for —
46
46
  # `rust/src/kernel/json.rs`'s own `integral_i64` doc comment names
47
- # exactly this: a Rust kernel value CANNOT represent it, so this
47
+ # exactly this: a Rust kernel value cannot represent it, so this
48
48
  # exercises a real cross-runtime capability gap, not a Ruby-only
49
49
  # edge) and its negative twin. Both round-trip through Ruby's own
50
50
  # arithmetic/JSON cleanly (confirmed directly: `(2**100).clamp(...)`
51
51
  # and `JSON.generate(2**100)` both just work — Integer has no
52
- # ceiling here), so nothing in THIS runtime needed a fix for these;
52
+ # ceiling here), so nothing in this runtime needed a fix for these;
53
53
  # they're included so a real generated sequence occasionally
54
54
  # produces the value at all, since nothing had, repo-wide, before.
55
55
  INTEGER_EDGE_CASES = [0, -1, 2_147_483_647, -2_147_483_648, 2**100, -(2**100)].freeze
56
+ # BUG#35 (QualityControl QA ledger, `lease-clock-json-precision`) —
57
+ # a Bignum edge case (`2**100`, above) landing on an Integer-typed
58
+ # clock or count reading fires the already-catalogued `Json::Num`/
59
+ # f64 precision-loss class (`rust/src/kernel/json.rs`'s
60
+ # `parse_number` parses every number through `s.parse::<f64>()`
61
+ # before any target-type conversion runs, and `Json::Num` is a
62
+ # plain `f64` end to end — see that file's own header) on a new
63
+ # site every time a new clock/count-shaped field is authored,
64
+ # without adding any new coverage: both engines already refuse
65
+ # (`TypeMismatch`, out of `i64` range either way) for this shape,
66
+ # `tenant_ledger`'s own NOTES.md already logged the identical class
67
+ # on a stored money attribute, and `spec/support/
68
+ # rust_conformance_helpers.rb`'s own `reduce_to_wire_precision`
69
+ # already documents and normalizes past the wire format's own loss
70
+ # for a query's echoed args. An exact-integer Rust JSON
71
+ # deserialization path was considered and rejected as this
72
+ # generator's own fix instead: `Json::Num(f64)` is pattern-matched
73
+ # throughout `rust/src/kernel` (query_comparators.rs,
74
+ # query_ordering.rs, read_model.rs, named_query.rs, this kernel's
75
+ # own `Fielded` impl, arithmetic overflow checks) and emitted by
76
+ # codegen (`rust/project/reactions.rb`, `rust/codegen/src/
77
+ # reactions.rs`) — adding a second, exact-integer numeric variant
78
+ # would mean auditing and updating every one of those sites, a
79
+ # refactor of the JSON layer itself, not a one-domain fix (exactly
80
+ # the ledger's own stated concern: "risks affecting every other
81
+ # domain's large-integer handling"). So instead, per the ledger's
82
+ # own sanctioned fallback: an Integer-typed field whose name reads
83
+ # as a clock or a count is capped to a narrower, still-real
84
+ # edge-case pool below (still exercises the ordinary i32
85
+ # boundaries, just never a value past f64's own 2**53 exact-
86
+ # integer ceiling) — every other Integer-typed field (a money
87
+ # amount, an identity sequence, anything not clock/count-shaped)
88
+ # still draws from the full `INTEGER_EDGE_CASES` pool above,
89
+ # unchanged.
90
+ SAFE_INTEGER_EDGE_CASES = [0, -1, 2_147_483_647, -2_147_483_648].freeze
91
+ # Matched against the value object's own declared name plus the
92
+ # attribute's own name (`value_for`'s `"#{context} #{attribute.
93
+ # name}"`, e.g. "LeaseInstant value", "RetryCount value") — a
94
+ # clock reading ("instant"/"clock"/"expir(es/y)"/"ttl"/"now"/
95
+ # "timestamp"/"epoch") or a quantity capped by a policy ("count").
96
+ # Deliberately narrow: matches the ledger's own "clock/count"
97
+ # wording exactly, not every plausibly-large-looking name (an
98
+ # "amount"/"cents"/"sequence" field is not clock/count-shaped and
99
+ # keeps the full Bignum edge-case pool).
100
+ CLOCK_OR_COUNT_NAME_PATTERN = /clock|instant|expir|ttl|\bnow\b|timestamp|epoch|count/i
56
101
  # NaN and +/-Infinity — the real find (see `spec/runtime/
57
102
  # numeric_boundary_spec.rb`): `Value::Coercion#check_numeric_fields`
58
103
  # used to let all three sail through untyped-checked (each really
@@ -60,7 +105,7 @@ module Hecks
60
105
  # (raw `ArgumentError`, not a domain refusal) or `JSON.generate`
61
106
  # (`JSON::GeneratorError`, also not a domain refusal) — both fixed
62
107
  # at the source now, so these are safe to generate. -0.0 is
63
- # deliberately included too even though it was ALREADY safe
108
+ # deliberately included too even though it was already safe
64
109
  # (finite, round-trips through JSON as `-0.0` cleanly) — a signed
65
110
  # zero is exactly the kind of boundary a hand-written corpus never
66
111
  # happens to type, and the fuzzer existing to cover it is the
@@ -112,10 +157,10 @@ module Hecks
112
157
  end
113
158
  end
114
159
 
115
- # `fields.size == 1` — the SAME test `Behaviour::ValueObject#
160
+ # `fields.size == 1` — the same test `Behaviour::ValueObject#
116
161
  # sole_attribute` names: a genuinely single-field value object,
117
162
  # not merely "this particular closed-set member happened to pick
118
- # one field." Unwrapping ONLY here, not in `invalid_member`
163
+ # one field." Unwrapping only here, not in `invalid_member`
119
164
  # above — a deliberately-wrong combination stays a Hash so its
120
165
  # own wrongness is what gets exercised, not a second, unrelated
121
166
  # shape question.
@@ -133,10 +178,10 @@ module Hecks
133
178
  end
134
179
  end
135
180
 
136
- # THE ID ITSELF. This minted `{"value" => id}` back when a reference was
181
+ # The ID itself. This minted `{"value" => id}` back when a reference was
137
182
  # stored wrapped ; the payload gate refuses that shape now, so a fuzzer
138
183
  # still emitting it would have every generated reference refused and
139
- # the SILENT guard would report the fuzzer broken rather than
184
+ # the silent guard would report the fuzzer broken rather than
140
185
  # the runtime.
141
186
  def reference_value(attribute, random:, known_ids:)
142
187
  pool = known_ids[attribute.type.target_name.to_s] || []
@@ -148,7 +193,7 @@ module Hecks
148
193
  def primitive(type_name, random:, name: nil)
149
194
  case type_name
150
195
  when "String" then string_value(random, name: name)
151
- when "Integer" then integer_value(random)
196
+ when "Integer" then integer_value(random, name: name)
152
197
  when "Float" then float_value(random)
153
198
  when "TrueClass", "FalseClass" then random.rand(2).zero?
154
199
  else raise ArgumentError, "ValueGenerator does not know primitive type #{type_name.inspect}"
@@ -172,19 +217,26 @@ module Hecks
172
217
  # sequence that can never get past one never reaches the state a deeper
173
218
  # bug would need. Zero and negative are still real, reachable outcomes —
174
219
  # via the edge-case pool, deliberately, not by starving them entirely.
175
- def integer_value(random)
176
- return INTEGER_EDGE_CASES.sample(random: random) if random.rand < EDGE_CASE_PROBABILITY
220
+ def integer_value(random, name: nil)
221
+ if random.rand < EDGE_CASE_PROBABILITY
222
+ pool = clock_or_count_shaped?(name) ? SAFE_INTEGER_EDGE_CASES : INTEGER_EDGE_CASES
223
+ return pool.sample(random: random)
224
+ end
177
225
 
178
226
  random.rand(1..1000)
179
227
  end
180
228
 
229
+ def clock_or_count_shaped?(name)
230
+ name.to_s.match?(CLOCK_OR_COUNT_NAME_PATTERN)
231
+ end
232
+
181
233
  def float_value(random)
182
234
  return FLOAT_EDGE_CASES.sample(random: random) if random.rand < EDGE_CASE_PROBABILITY
183
235
 
184
236
  random.rand(0.01..1000.0).round(2)
185
237
  end
186
238
 
187
- # The bare scalar a generated IDENTITY value stands for, for recording into
239
+ # The bare scalar a generated identity value stands for, for recording into
188
240
  # `known_ids`. An identity is declared as a value object, so this opens one ;
189
241
  # a reference pointing at this record is already that scalar and needs no
190
242
  # opening at all. The two used to be the same reading and are not any more.
data/lib/hecks/fuzzing.rb CHANGED
@@ -8,5 +8,16 @@ end
8
8
  require_relative "fuzzing/value_generator"
9
9
  require_relative "fuzzing/invalid_value_generator"
10
10
  require_relative "fuzzing/sequence_generator"
11
+ require_relative "fuzzing/nondeterministic"
11
12
  require_relative "fuzzing/replay"
13
+ require_relative "fuzzing/shrinker"
12
14
  require_relative "fuzzing/properties"
15
+ require_relative "fuzzing/qa_settings"
16
+ require_relative "fuzzing/rotation_priority"
17
+ require_relative "fuzzing/sweep_depth"
18
+ require_relative "fuzzing/persistence_parity"
19
+ require_relative "fuzzing/form_census"
20
+ require_relative "fuzzing/target_capabilities"
21
+ require_relative "fuzzing/rust_gap_manifest"
22
+ require_relative "fuzzing/structural_skips"
23
+ require_relative "fuzzing/coverage_campaign"
@@ -1,7 +1,7 @@
1
1
  module Hecks
2
2
  module Grammar
3
3
  # The file surgery under bin/evolve: reading and rewriting the
4
- # aggregate-local KeywordSeed/ArgumentSeed rows as TEXT, so a
4
+ # aggregate-local KeywordSeed/ArgumentSeed rows as text, so a
5
5
  # proposed word enters the table exactly as a hand would write it
6
6
  # and an admitted one loses its ceremony (an absent status reads as
7
7
  # admitted — the grown-column convention).
@@ -18,7 +18,7 @@ module Hecks
18
18
  module_function
19
19
 
20
20
  # bin/evolve's own `--name value` flag reader. Only consumes the
21
- # NEXT argv element as the value when that element doesn't itself
21
+ # next argv element as the value when that element doesn't itself
22
22
  # look like a flag — otherwise `--foo --bar` would swallow `--bar`
23
23
  # as `--foo`'s value (and `--bar` would then never be seen at
24
24
  # all), and a value-less `--foo` at the end of argv would bypass
@@ -97,7 +97,7 @@ module Hecks
97
97
  row = %(#{indent}member word: "#{word}", context: "#{context}", body: "#{body}", ) +
98
98
  %(inner: "#{inner}", opens: "#{opens}", fills: "#{fills}", status: "proposed"\n)
99
99
 
100
- # At the END of the one_of — grouping by context is a courtesy of
100
+ # At the end of the one_of — grouping by context is a courtesy of
101
101
  # the hand; a proposed row sits at the bottom until admission,
102
102
  # when whoever admits it may move it home.
103
103
  closing = block.rindex(/^\s*end\s*$/)
@@ -119,7 +119,7 @@ module Hecks
119
119
  next line unless member_row?(line, word, context)
120
120
 
121
121
  stripped = line.sub(/,\s*status: "[^"]*"/, "")
122
- # Admitted is the default and stays UNSPELLED — only a word
122
+ # Admitted is the default and stays unspelled — only a word
123
123
  # entering or leaving the language carries its status.
124
124
  to == "admitted" ? stripped : stripped.sub(/\n\z/, %(, status: "#{to}"\n))
125
125
  end.join
@@ -219,10 +219,10 @@ module Hecks
219
219
  block
220
220
  end
221
221
 
222
- # From `value_object "KeywordSeed"` to ITS OWN closing `end` — `member`
222
+ # From `value_object "KeywordSeed"` to its own closing `end` — `member`
223
223
  # rows sit bare now (S3, ADR 0025 — the `one_of do ... end` wrapper
224
224
  # is gone), so the first bare `end` line after the opener already
225
- # IS the value object's own, the same fact the original one_of-
225
+ # is the value object's own, the same fact the original one_of-
226
226
  # nested version of this method leaned on (nothing else nested
227
227
  # inside it either, before or after).
228
228
  def keyword_blocks(source) = seed_blocks(source, "KeywordSeed")
@@ -230,7 +230,7 @@ module Hecks
230
230
 
231
231
  # ── the Argument rows — a word's own arguments, at last with tooling
232
232
  # of their own rather than the rename-only cascade above. A word may
233
- # carry SEVERAL argument rows (one per position, one per named
233
+ # carry several argument rows (one per position, one per named
234
234
  # kwarg), so identity here is the full (keyword, context, at, named)
235
235
  # tuple, not the two-field key a Keyword row answers to.
236
236
 
@@ -250,7 +250,7 @@ module Hecks
250
250
  end
251
251
  end
252
252
 
253
- # `pairs_shape` — for a `pairs` argument that fills ONE field with a
253
+ # `pairs_shape` — for a `pairs` argument that fills one field with a
254
254
  # whole key/value list rather than naming a field per pair (the
255
255
  # shape `Handler.dispatch`'s own `with:` already carries). Without
256
256
  # it, `spec/syntax_conformance_spec.rb` reads a pairs argument
@@ -315,7 +315,7 @@ module Hecks
315
315
 
316
316
  def argument_identity(row) = [row[:keyword], row[:context], row[:at], row[:named]]
317
317
 
318
- # The rename cascade, ROW-AWARE — only the rows that actually belong
318
+ # The rename cascade, row-aware — only the rows that actually belong
319
319
  # to the renamed word, spelling updated in place, rather than a
320
320
  # blind `gsub` on every `keyword: "word",` substring in the file
321
321
  # (which a coincidentally-matching row elsewhere could have
@@ -337,7 +337,7 @@ module Hecks
337
337
  end
338
338
  end
339
339
 
340
- # From `value_object "ArgumentSeed"` to ITS OWN closing `end` — see
340
+ # From `value_object "ArgumentSeed"` to its own closing `end` — see
341
341
  # `keyword_block`'s own comment for why the first bare `end` after
342
342
  # the opener is already the right one, now that `member` rows sit
343
343
  # bare (S3, ADR 0025).
data/lib/hecks/grammar.rb CHANGED
@@ -7,12 +7,12 @@ require_relative "../hecks"
7
7
 
8
8
  module Hecks
9
9
  # The sublanguage grammar domains (grammar/*.bluebook) and the one boot
10
- # path for reading them as DATA — the expression chapter replayed
10
+ # path for reading them as data — the expression chapter replayed
11
11
  # through its own admission ledger, so anything derived from it (the
12
12
  # operator projections, the conformance specs) reads the set that
13
13
  # actually survived the Admit gates, never a hand-copied list.
14
14
  #
15
- # Booted on CALL, never at require: the Prism adapter normalises every
15
+ # Booted on call, never at require: the Prism adapter normalises every
16
16
  # predicate through CanonicalForm while a bluebook loads, so the
17
17
  # expression machinery cannot boot the chapter that configures it —
18
18
  # this module exists precisely so generators and specs boot it in a
@@ -42,7 +42,7 @@ module Hecks
42
42
  JSON.parse(File.read(LEDGER)).fetch("steps").each do |step|
43
43
  args = symbolize(step.fetch("args"))
44
44
  begin
45
- dispatcher.dispatch(step.fetch("verb"), **args)
45
+ dispatcher.dispatch_flat(step.fetch("verb"), args)
46
46
  rescue *Runtime::DOMAIN_REFUSALS => e
47
47
  raise Runtime::WiringError,
48
48
  "the admission ledger refused at #{step['verb']} #{step['args']} — #{e.message}"
@@ -77,15 +77,15 @@ module Hecks
77
77
  registry.repository("Expression", aggregate).all
78
78
  end
79
79
 
80
- # THE OPERATORS THE LANGUAGE STANDS ON. Every guard and invariant in
80
+ # The operators the language stands on. Every guard and invariant in
81
81
  # the language's own chapters — the meta-domain (Bluebook, World) and
82
82
  # the grammar chapters beside this file — evaluates through the very
83
83
  # operator table the ledger admits. An operator one of those
84
- # predicates uses is SELF-BEARING: retire it and the language can no
84
+ # predicates uses is self-bearing: retire it and the language can no
85
85
  # longer read its own rules — found the hard way, as a projection
86
86
  # missing `!=` that could not boot the chapter to fix itself. This
87
87
  # derives the set, with a usage site per operator, so the generator
88
- # and the conformance spec can refuse the retirement BY NAME instead
88
+ # and the conformance spec can refuse the retirement by name instead
89
89
  # of wedging.
90
90
  def self_bearing_operators
91
91
  sites = Hash.new { |h, k| h[k] = [] }
@@ -146,7 +146,7 @@ module Hecks
146
146
  walk_operators(node, evaluator).uniq
147
147
  end
148
148
 
149
- # A recursive descent over a CLOSED, declared set of AST node types
149
+ # A recursive descent over a closed, declared set of AST node types
150
150
  # (Evaluator's boolean/compare/include nodes, Resolver's arithmetic
151
151
  # nodes, and the generic Struct fallback) — each branch does the
152
152
  # same one thing (name the node's own operator, recurse into its
data/lib/hecks/ir.rb CHANGED
@@ -1,19 +1,19 @@
1
1
  module Hecks
2
- # WHAT A CONSTRUCT EMITS, DECLARED RATHER THAN WRITTEN OUT.
2
+ # What a construct emits, declared rather than written out.
3
3
  #
4
- # `IR` is a thing this framework PRODUCES, not a thing its model IS.
4
+ # `IR` is a thing this framework produces, not a thing its model is.
5
5
  # The language self-hosts, and its own bluebook declares aggregates
6
6
  # named `Bluebook`, `Aggregate`, `Command`, `Entity`, `ValueObject`,
7
7
  # `Policy`, `ReadModel` — there is no `IR` aggregate anywhere in the
8
8
  # grammar. Where the grammar's own vision line does say IR it means the
9
- # emission: "the IR it STORES must equal the IR the DSL builder
10
- # PRODUCES". `IR_VERSION` says the same thing structurally — a version
9
+ # emission: "the IR it stores must equal the IR the DSL builder
10
+ # produces". `IR_VERSION` says the same thing structurally — a version
11
11
  # stamped on `to_h`'s output rather than on the object is a version of
12
- # the EMISSION, which only makes sense if the two are different things.
12
+ # the emission, which only makes sense if the two are different things.
13
13
  #
14
- # So emitting IR is a CAPABILITY a construct has, and this is that
14
+ # So emitting IR is a capability a construct has, and this is that
15
15
  # capability: `include Hecks::IR` and declare the shape once.
16
- # The model it emits FROM is `Hecks::Bluebook` — a chapter class
16
+ # The model it emits from is `Hecks::Bluebook` — a chapter class
17
17
  # nesting everything a chapter declares. It is deliberately not named
18
18
  # after this, its own output.
19
19
  #
@@ -36,18 +36,18 @@ module Hecks
36
36
  # canonical_form: -> { CanonicalForm.table } # anything else
37
37
  # )
38
38
  #
39
- # KEY ORDER IS THE DECLARATION ORDER, and that is load-bearing rather
39
+ # Key order is the declaration order, and that is load-bearing rather
40
40
  # than cosmetic: `spec/golden/ir/*.json` pins the emitted form exactly,
41
41
  # so a reordered declaration is a changed artifact and the golden specs
42
42
  # will say so.
43
43
  module IR
44
- # THE TWO SHAPES A CONSTRUCT COMES IN, and why this module has two
44
+ # The two shapes a construct comes in, and why this module has two
45
45
  # doors instead of hiding the difference.
46
46
  #
47
47
  # `Bluebook`/`Aggregate`/`Policy`/`ReadModel` are ordinary objects —
48
48
  # metadata records, one instance per declaration. `Command`/`Entity`/
49
- # `ValueObject` are anonymous CLASSES (`Class.new(self)`, see
50
- # `Command.declare`), because those three are referenced as TYPES in
49
+ # `ValueObject` are anonymous classes (`Class.new(self)`, see
50
+ # `Command.declare`), because those three are referenced as types in
51
51
  # a bluebook (`attribute :price, Money`) and a type has to be a real
52
52
  # Ruby constant to be named.
53
53
  #
@@ -87,7 +87,7 @@ module Hecks
87
87
  def one(source) = One.new(source)
88
88
 
89
89
  # Walks the superclass chain so a `Class.new(ValueObject)` — which
90
- # is what every declared value object actually IS — inherits the
90
+ # is what every declared value object actually is — inherits the
91
91
  # shape its base declared, rather than each anonymous subclass
92
92
  # having to redeclare it.
93
93
  def ir_spec
@@ -112,7 +112,7 @@ module Hecks
112
112
  private
113
113
 
114
114
  # An instance reads its class's declaration; a class-shaped
115
- # construct IS the declaration holder.
115
+ # construct is the declaration holder.
116
116
  def ir_spec_for(construct)
117
117
  return construct.ir_spec if construct.respond_to?(:ir_spec)
118
118
 
@@ -73,6 +73,26 @@ Hecks.bluebook "Bluebook", version: "1" do
73
73
  # chapters attach to nothing, and ABSENT IS NOT EMPTY, the same
74
74
  # reading `version`/`formerly_known_as` above give.
75
75
  attribute :attaches_to, list_of(AttachesToContext)
76
+ # A CAPABILITY THIS CHAPTER ANSWERS FOR OTHER DOMAINS — declared, so
77
+ # nothing downstream has to recognise the chapter BY NAME. One row
78
+ # per (capability, key, verb): `provides "authorization", grant:
79
+ # "RoleAssignment.Assign"` is the row (authorization, grant,
80
+ # RoleAssignment.Assign). The runtime's role check, boot's
81
+ # ungoverned-role refusal, the authorization adapter and the fuzzer
82
+ # all read the rows rather than the literal "Governance". Most
83
+ # chapters provide nothing, and ABSENT IS NOT EMPTY, the same reading
84
+ # `attaches_to` above gives.
85
+ attribute :provides, list_of(Provision)
86
+
87
+ value_object "Provision" do
88
+ attribute :capability, String
89
+ attribute :key, String
90
+ attribute :verb, String
91
+ end
92
+
93
+ value_object "ProvisionText" do
94
+ attribute :value, String
95
+ end
76
96
 
77
97
  value_object "RuleText" do
78
98
  # deliberately permissive : source_token and replacement are legitimately
@@ -159,6 +179,22 @@ Hecks.bluebook "Bluebook", version: "1" do
159
179
  emits ChapterAttached
160
180
  end
161
181
 
182
+ # ONE ROW OF ONE CAPABILITY — `provides "authorization", assignments:
183
+ # ..., grant: ..., transitions: ...` offers three, one per key, in the
184
+ # order they were written.
185
+ command "Provide" do
186
+ role "Language"
187
+ goal "Declare one verb this chapter answers a capability with"
188
+
189
+ attribute :capability, ProvisionText
190
+ attribute :key, ProvisionText
191
+ attribute :verb, ProvisionText
192
+
193
+ sets :provides, append: { capability: :capability, key: :key, verb: :verb }
194
+
195
+ emits CapabilityProvided
196
+ end
197
+
162
198
  # THE WAY BACK.
163
199
  #
164
200
  # Until these existed the meta-domain was write-only: twelve categories, 34
@@ -214,6 +250,7 @@ Hecks.bluebook "Bluebook", version: "1" do
214
250
  member word: "vision", context: "Bluebook", body: "none", inner: "", opens: "", fills: "vision"
215
251
  member word: "formerly_known_as", context: "Bluebook", body: "none", inner: "", opens: "", fills: "formerly_known_as"
216
252
  member word: "attaches_to", context: "Bluebook", body: "none", inner: "", opens: "", fills: "attaches_to", calls: "attaches_to_impl"
253
+ member word: "provides", context: "Bluebook", body: "none", inner: "", opens: "", fills: "provides", calls: "provides_impl"
217
254
  member word: "core", context: "Bluebook", body: "none", inner: "", opens: "", fills: "classification"
218
255
  member word: "supporting", context: "Bluebook", body: "none", inner: "", opens: "", fills: "classification"
219
256
  member word: "generic", context: "Bluebook", body: "none", inner: "", opens: "", fills: "classification"
@@ -246,6 +283,10 @@ Hecks.bluebook "Bluebook", version: "1" do
246
283
  member keyword: "vision", context: "Bluebook", at: "1", named: "", kind: "text", required: "true", fills: "vision", coerce: "false"
247
284
  member keyword: "formerly_known_as", context: "Bluebook", at: "1", named: "", kind: "text", required: "true", fills: "formerly_known_as"
248
285
  member keyword: "attaches_to", context: "Bluebook", at: "1", named: "", kind: "text", required: "true", fills: "attaches_to", variadic: "true"
286
+ member keyword: "provides", context: "Bluebook", at: "1", named: "", kind: "text", required: "true", fills: "provides"
287
+ member keyword: "provides", context: "Bluebook", at: "", named: "assignments", kind: "text", required: "false", fills: "provides"
288
+ member keyword: "provides", context: "Bluebook", at: "", named: "grant", kind: "text", required: "false", fills: "provides"
289
+ member keyword: "provides", context: "Bluebook", at: "", named: "transitions", kind: "text", required: "false", fills: "provides"
249
290
  member keyword: "aggregate", context: "Bluebook", at: "1", named: "", kind: "text", required: "true", fills: "name"
250
291
  member keyword: "read_model", context: "Bluebook", at: "1", named: "", kind: "text", required: "true", fills: "name"
251
292
  member keyword: "policy", context: "Bluebook", at: "1", named: "", kind: "text", required: "true", fills: "name"