hecks 2.8.0 → 3.0.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.
- checksums.yaml +4 -4
- data/exe/hecks +50 -7
- data/lib/hecks/adapters/driven/claude_code.rb +4 -104
- data/lib/hecks/adapters/driven/d1.rb +44 -237
- data/lib/hecks/adapters/driven/folder.rb +17 -76
- data/lib/hecks/adapters/driven/google_authentication.rb +15 -49
- data/lib/hecks/adapters/driven/governance_authorization.rb +23 -70
- data/lib/hecks/adapters/driven/heki/journal.rb +8 -41
- data/lib/hecks/adapters/driven/heki/saga_store.rb +12 -54
- data/lib/hecks/adapters/driven/heki/snapshot.rb +4 -12
- data/lib/hecks/adapters/driven/heki.rb +13 -42
- data/lib/hecks/adapters/driven/identity_registry.rb +3 -14
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +9 -26
- data/lib/hecks/adapters/driven/in_process_key_vault.rb +2 -14
- data/lib/hecks/adapters/driven/lambda/client.rb +10 -51
- data/lib/hecks/adapters/driven/lambda.rb +10 -84
- data/lib/hecks/adapters/driven/local_storage.rb +5 -40
- data/lib/hecks/adapters/driven/memory.rb +32 -100
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +5 -21
- data/lib/hecks/adapters/driven/postgres/codec.rb +6 -28
- data/lib/hecks/adapters/driven/postgres/outbox.rb +3 -12
- data/lib/hecks/adapters/driven/postgres/reconnect.rb +10 -32
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +34 -108
- data/lib/hecks/adapters/driven/postgres/shared_connection.rb +180 -0
- data/lib/hecks/adapters/driven/postgres.rb +149 -136
- data/lib/hecks/adapters/driven/postgres_era.adapter +4 -15
- data/lib/hecks/adapters/driven/prism.rb +48 -38
- data/lib/hecks/adapters/driven/sql_query_builder.rb +21 -121
- data/lib/hecks/adapters/driven/sqlite/codec.rb +8 -40
- data/lib/hecks/adapters/driven/sqlite/projection.rb +26 -82
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +40 -84
- data/lib/hecks/adapters/driven/sqlite.rb +128 -65
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +76 -62
- data/lib/hecks/adapters/driven.rb +5 -36
- data/lib/hecks/adapters/driving/github_webhook.rb +8 -76
- data/lib/hecks/behaviors/dsl.rb +7 -25
- data/lib/hecks/behaviors/expectations.rb +32 -142
- data/lib/hecks/behaviors/ir.rb +7 -20
- data/lib/hecks/behaviors/rspec.rb +6 -17
- data/lib/hecks/behaviors/runner.rb +10 -34
- data/lib/hecks/behaviors.rb +4 -8
- data/lib/hecks/bench/cli.rb +62 -0
- data/lib/hecks/bench/environment.rb +71 -0
- data/lib/hecks/bench/postgres_probe.rb +30 -0
- data/lib/hecks/bench/report.rb +76 -0
- data/lib/hecks/bench/ruby_runner.rb +93 -0
- data/lib/hecks/bench/run.rb +55 -0
- data/lib/hecks/bench/rust_runner.rb +101 -0
- data/lib/hecks/bench/stats.rb +46 -0
- data/lib/hecks/bench/suite.rb +98 -0
- data/lib/hecks/bench/workload.rb +89 -0
- data/lib/hecks/bench.rb +17 -0
- data/lib/hecks/bluebook/aggregate.rb +9 -75
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +9 -38
- data/lib/hecks/bluebook/assembly/build.rb +4 -15
- data/lib/hecks/bluebook/assembly/contract.rb +15 -136
- data/lib/hecks/bluebook/assembly/contracts.rb +56 -174
- data/lib/hecks/bluebook/assembly/marks.rb +31 -223
- data/lib/hecks/bluebook/assembly/specializer.rb +6 -52
- data/lib/hecks/bluebook/assembly.rb +6 -45
- data/lib/hecks/bluebook/attribute.rb +12 -61
- data/lib/hecks/bluebook/behaviour/aggregate.rb +39 -39
- data/lib/hecks/bluebook/behaviour/attribute.rb +3 -14
- data/lib/hecks/bluebook/behaviour/chapter.rb +14 -42
- data/lib/hecks/bluebook/behaviour/command.rb +14 -66
- data/lib/hecks/bluebook/behaviour/domain_port.rb +11 -20
- data/lib/hecks/bluebook/behaviour/entity.rb +7 -27
- data/lib/hecks/bluebook/behaviour/hexagon.rb +23 -41
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +15 -40
- data/lib/hecks/bluebook/behaviour/policy.rb +23 -52
- data/lib/hecks/bluebook/behaviour/process_manager.rb +30 -57
- data/lib/hecks/bluebook/behaviour/read_model.rb +25 -31
- data/lib/hecks/bluebook/behaviour/traits.rb +27 -64
- data/lib/hecks/bluebook/behaviour/value_object.rb +5 -24
- data/lib/hecks/bluebook/capabilities.rb +17 -49
- data/lib/hecks/bluebook/chapter.rb +16 -50
- data/lib/hecks/bluebook/command.rb +23 -81
- data/lib/hecks/bluebook/domain_port.rb +49 -67
- data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +50 -229
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +50 -377
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +33 -204
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +16 -69
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +96 -708
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +63 -169
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +3 -1
- data/lib/hecks/bluebook/dsl/command_builder.rb +74 -546
- data/lib/hecks/bluebook/dsl/const_shim.rb +8 -41
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +42 -89
- data/lib/hecks/bluebook/dsl/entity_builder.rb +44 -330
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +20 -269
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +148 -197
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +13 -88
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +6 -20
- data/lib/hecks/bluebook/dsl/policy_builder.rb +16 -88
- data/lib/hecks/bluebook/dsl/port_builder.rb +2 -6
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +22 -96
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +31 -184
- data/lib/hecks/bluebook/dsl/query_builder.rb +38 -43
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +75 -201
- data/lib/hecks/bluebook/dsl/rule_reference.rb +22 -163
- data/lib/hecks/bluebook/dsl/translation_builder.rb +28 -114
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +10 -124
- data/lib/hecks/bluebook/dsl/word_gate.rb +16 -162
- data/lib/hecks/bluebook/dsl/world_builder.rb +46 -111
- data/lib/hecks/bluebook/entity.rb +13 -71
- data/lib/hecks/bluebook/expression/ast_json.rb +24 -190
- data/lib/hecks/bluebook/expression/ast_reader.rb +12 -54
- data/lib/hecks/bluebook/expression/canonical_form.rb +11 -88
- data/lib/hecks/bluebook/expression/evaluator.rb +47 -295
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +30 -147
- data/lib/hecks/bluebook/expression/resolver.rb +39 -661
- data/lib/hecks/bluebook/hexagon.rb +18 -33
- data/lib/hecks/bluebook/lifecycle.rb +5 -16
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -7
- data/lib/hecks/bluebook/meta_validator/judge.rb +111 -361
- data/lib/hecks/bluebook/meta_validator/plan.rb +33 -162
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -7
- data/lib/hecks/bluebook/meta_validator/readings.rb +56 -342
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +25 -176
- data/lib/hecks/bluebook/meta_validator/shapes.rb +53 -267
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +47 -306
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +7 -43
- data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +246 -0
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -18
- data/lib/hecks/bluebook/meta_validator.rb +87 -352
- data/lib/hecks/bluebook/model_check/client_profile.rb +6 -21
- data/lib/hecks/bluebook/model_check.rb +122 -596
- data/lib/hecks/bluebook/pattern_subset.rb +49 -69
- data/lib/hecks/bluebook/process_manager.rb +13 -83
- data/lib/hecks/bluebook/project_loader.rb +3 -39
- data/lib/hecks/bluebook/project_register.rb +12 -60
- data/lib/hecks/bluebook/query.rb +27 -51
- data/lib/hecks/bluebook/read_model.rb +57 -81
- data/lib/hecks/bluebook/reference.rb +7 -41
- data/lib/hecks/bluebook/smoke_test.rb +9 -99
- data/lib/hecks/bluebook/synthesizer.rb +13 -62
- data/lib/hecks/bluebook/translation.rb +26 -62
- data/lib/hecks/bluebook/value_object.rb +11 -36
- data/lib/hecks/bluebook.rb +5 -22
- data/lib/hecks/cache_dir.rb +4 -23
- data/lib/hecks/canonical_json.rb +30 -0
- data/lib/hecks/chapters.rb +102 -0
- data/lib/hecks/cli/behaviors.rb +61 -0
- data/lib/hecks/cli/canonicalise.rb +25 -0
- data/lib/hecks/cli/check_engine_agreement.rb +32 -0
- data/lib/hecks/cli/check_era.rb +88 -0
- data/lib/hecks/cli/console.rb +66 -0
- data/lib/hecks/cli/deploy_template_diff.rb +40 -0
- data/lib/hecks/cli/doc_coverage.rb +32 -0
- data/lib/hecks/cli/document.rb +5 -23
- data/lib/hecks/cli/expression_projection.rb +41 -0
- data/lib/hecks/cli/follow.rb +88 -0
- data/lib/hecks/cli/history.rb +44 -0
- data/lib/hecks/cli/ir.rb +4 -6
- data/lib/hecks/cli/mcp.rb +5 -6
- data/lib/hecks/cli/model_check.rb +24 -88
- data/lib/hecks/cli/pattern_cases.rb +47 -0
- data/lib/hecks/cli/present.rb +39 -0
- data/lib/hecks/cli/project_cli.rb +223 -64
- data/lib/hecks/cli/project_diagrams.rb +4 -10
- data/lib/hecks/cli/project_glossary.rb +44 -0
- data/lib/hecks/cli/reference.rb +46 -0
- data/lib/hecks/cli/refresh_projections.rb +44 -0
- data/lib/hecks/cli/refresh_rspec_runtime_baseline.rb +150 -0
- data/lib/hecks/cli/release.rb +70 -0
- data/lib/hecks/cli/release_gem.rb +59 -0
- data/lib/hecks/cli/rspec_io_parallel_files.rb +137 -0
- data/lib/hecks/cli/rspec_shard_files.rb +66 -0
- data/lib/hecks/cli/run.rb +15 -61
- data/lib/hecks/cli/seed_semantics_corpus.rb +71 -0
- data/lib/hecks/cli/serve_query_ir_mcp.rb +30 -0
- data/lib/hecks/cli/shape.rb +70 -0
- data/lib/hecks/cli/smoke_http.rb +230 -0
- data/lib/hecks/cli/smoke_test.rb +7 -19
- data/lib/hecks/cli/spec_example.rb +27 -0
- data/lib/hecks/cli/statements.rb +32 -0
- data/lib/hecks/cli/stores.rb +4 -6
- data/lib/hecks/cli/stress_concurrency_specs.rb +177 -0
- data/lib/hecks/cli.rb +216 -73
- data/lib/hecks/codemod.rb +324 -0
- data/lib/hecks/construct.rb +8 -34
- data/lib/hecks/corpus.rb +423 -0
- data/lib/hecks/deploy/bluebook/deploy.bluebook +617 -158
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +41 -11
- data/lib/hecks/deploy/oidc.json +82 -2
- data/lib/hecks/doc/reference.rb +351 -0
- data/lib/hecks/doors/cli_door.rb +196 -0
- data/lib/hecks/doors/cli_runner.rb +297 -0
- data/lib/hecks/{facade → doors}/command_request.rb +14 -34
- data/lib/hecks/doors/handle.rb +206 -0
- data/lib/hecks/doors/json_door.rb +132 -0
- data/lib/hecks/doors/launcher_options.rb +129 -0
- data/lib/hecks/{cli → doors}/mcp_door.rb +25 -120
- data/lib/hecks/doors/mcp_door_scope.rb +97 -0
- data/lib/hecks/doors/ruby_door/aggregate_door.rb +162 -0
- data/lib/hecks/doors/ruby_door/chapter.rb +72 -0
- data/lib/hecks/doors/ruby_door.rb +77 -0
- data/lib/hecks/doors.rb +61 -0
- data/lib/hecks/embryonaut_bluebook/lock.rb +3 -35
- data/lib/hecks/embryonaut_bluebook/shape.rb +1 -17
- data/lib/hecks/embryonaut_bluebook/vendor.rb +8 -53
- data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +4 -19
- data/lib/hecks/embryonaut_bluebook.rb +5 -72
- data/lib/hecks/engine_agreement.rb +156 -0
- data/lib/hecks/facade.rb +23 -51
- data/lib/hecks/forms/app.rb +21 -76
- data/lib/hecks/forms/banking_presentation.rb +50 -0
- data/lib/hecks/forms/command_form_renderer.rb +10 -29
- data/lib/hecks/forms/field_renderer.rb +17 -156
- data/lib/hecks/forms/field_shape.rb +31 -222
- data/lib/hecks/forms/html.rb +12 -47
- data/lib/hecks/forms/index_renderer.rb +1 -4
- data/lib/hecks/forms/page.rb +3 -9
- data/lib/hecks/forms/params.rb +14 -60
- data/lib/hecks/forms/port_argument.rb +2 -19
- data/lib/hecks/forms/query_form_renderer.rb +4 -79
- data/lib/hecks/forms/record_renderer.rb +8 -75
- data/lib/hecks/forms/record_table.rb +7 -50
- data/lib/hecks/forms/reference_options.rb +3 -30
- data/lib/hecks/forms/value_object_shape.rb +8 -35
- data/lib/hecks/forms.rb +5 -33
- data/lib/hecks/fqn.rb +5 -12
- data/lib/hecks/framework/bluebook/compliance.bluebook +16 -65
- data/lib/hecks/framework/bluebook/console_settings.bluebook +47 -134
- data/lib/hecks/framework/bluebook/framework.hecksagon +5 -14
- data/lib/hecks/framework/bluebook/governance.bluebook +13 -34
- data/lib/hecks/framework/bluebook/identity.bluebook +11 -26
- data/lib/hecks/framework/bluebook/privacy.bluebook +8 -33
- data/lib/hecks/framework.rb +11 -80
- data/lib/hecks/freezer.rb +5 -39
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +393 -0
- data/lib/hecks/fuzzing/combination_miner.rb +86 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +214 -0
- data/lib/hecks/fuzzing/coverage_campaign.rb +143 -0
- data/lib/hecks/fuzzing/differential.rb +156 -0
- data/lib/hecks/fuzzing/domain_generator.rb +615 -0
- data/lib/hecks/fuzzing/era_boundary.rb +71 -0
- data/lib/hecks/fuzzing/form_census.rb +136 -0
- data/lib/hecks/fuzzing/generated_domain_check.rb +93 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +64 -0
- data/lib/hecks/fuzzing/isolated_boot.rb +301 -0
- data/lib/hecks/fuzzing/nondeterministic.rb +51 -0
- data/lib/hecks/fuzzing/outside_world.rb +45 -0
- data/lib/hecks/fuzzing/persistence_parity.rb +102 -0
- data/lib/hecks/fuzzing/properties/corrections.rb +88 -0
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +314 -0
- data/lib/hecks/fuzzing/properties/guards.rb +190 -0
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +300 -0
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +93 -0
- data/lib/hecks/fuzzing/properties/outbox.rb +89 -0
- data/lib/hecks/fuzzing/properties/querying.rb +175 -0
- data/lib/hecks/fuzzing/properties.rb +162 -0
- data/lib/hecks/fuzzing/qa_settings.rb +123 -0
- data/lib/hecks/fuzzing/replay.rb +505 -0
- data/lib/hecks/fuzzing/rotation_priority.rb +48 -0
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +108 -0
- data/lib/hecks/fuzzing/self_consistency.rb +339 -0
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +385 -0
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +132 -0
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +95 -0
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +77 -0
- data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +109 -0
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +237 -0
- data/lib/hecks/fuzzing/sequence_generator.rb +189 -0
- data/lib/hecks/fuzzing/shrinker.rb +166 -0
- data/lib/hecks/fuzzing/structural_skips.rb +33 -0
- data/lib/hecks/fuzzing/sweep_depth.rb +43 -0
- data/lib/hecks/fuzzing/target_capabilities.rb +163 -0
- data/lib/hecks/fuzzing/value_generator.rb +216 -0
- data/lib/hecks/fuzzing.rb +23 -0
- data/lib/hecks/grammar/evolve.rb +336 -0
- data/lib/hecks/grammar/expression.bluebook +15 -40
- data/lib/hecks/grammar/translation.bluebook +3 -6
- data/lib/hecks/grammar.rb +16 -41
- data/lib/hecks/hecks/adapters/codebase/codemods.rb +45 -0
- data/lib/hecks/hecks/adapters/codebase/conformance.rb +71 -0
- data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +202 -0
- data/lib/hecks/hecks/adapters/codebase/gem_registry.rb +80 -0
- data/lib/hecks/hecks/adapters/codebase/kernel_tables.rb +54 -0
- data/lib/hecks/hecks/adapters/codebase/language.rb +240 -0
- data/lib/hecks/hecks/adapters/codebase/npm_registry.rb +102 -0
- data/lib/hecks/hecks/adapters/codebase/publishing.rb +142 -0
- data/lib/hecks/hecks/adapters/codebase/regeneration.rb +67 -0
- data/lib/hecks/hecks/adapters/codebase/release_facts.rb +90 -0
- data/lib/hecks/hecks/adapters/codebase/ruby_child.rb +83 -0
- data/lib/hecks/hecks/adapters/codebase/secret_vault.rb +36 -0
- data/lib/hecks/hecks/adapters/codebase/source_tree.adapter +7 -0
- data/lib/hecks/hecks/adapters/codebase/source_tree.rb +189 -0
- data/lib/hecks/hecks/adapters/codebase/sqlite_fixture.rb +71 -0
- data/lib/hecks/hecks/adapters/codebase/style.rb +171 -0
- data/lib/hecks/hecks/adapters/codebase/test_runner.rb +55 -0
- data/lib/hecks/hecks/adapters/codebase/test_suite.rb +165 -0
- data/lib/hecks/hecks/adapters/codebase/tree.rb +111 -0
- data/lib/hecks/hecks/adapters/console_capture.rb +79 -0
- data/lib/hecks/hecks/adapters/deploy_toolchain.adapter +7 -0
- data/lib/hecks/hecks/adapters/deploy_toolchain.rb +108 -0
- data/lib/hecks/hecks/adapters/git.adapter +7 -0
- data/lib/hecks/hecks/adapters/git.rb +102 -0
- data/lib/hecks/hecks/adapters/host_http.adapter +7 -0
- data/lib/hecks/hecks/adapters/host_http.rb +65 -0
- data/lib/hecks/hecks/adapters/in_process_boot.adapter +7 -0
- data/lib/hecks/hecks/adapters/in_process_boot.rb +197 -0
- data/lib/hecks/hecks/adapters/in_process_operations.rb +202 -0
- data/lib/hecks/hecks/adapters/journal_store/changes.rb +143 -0
- data/lib/hecks/hecks/adapters/journal_store/compaction.rb +145 -0
- data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +132 -0
- data/lib/hecks/hecks/adapters/journal_store/examination.rb +135 -0
- data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +163 -0
- data/lib/hecks/hecks/adapters/journal_store/readings.rb +187 -0
- data/lib/hecks/hecks/adapters/journal_store.adapter +7 -0
- data/lib/hecks/hecks/adapters/journal_store.rb +64 -0
- data/lib/hecks/hecks/adapters/local_files.adapter +7 -0
- data/lib/hecks/hecks/adapters/local_files.rb +46 -0
- data/lib/hecks/hecks/adapters/pg_admin.adapter +7 -0
- data/lib/hecks/hecks/adapters/pg_admin.rb +245 -0
- data/lib/hecks/hecks/adapters/process_pool.adapter +7 -0
- data/lib/hecks/hecks/adapters/process_pool.rb +119 -0
- data/lib/hecks/hecks/adapters/rust_toolchain.adapter +7 -0
- data/lib/hecks/hecks/adapters/rust_toolchain.rb +179 -0
- data/lib/hecks/hecks/adapters/rust_workspace.rb +150 -0
- data/lib/hecks/hecks/adapters/shell.adapter +7 -0
- data/lib/hecks/hecks/adapters/shell.rb +49 -0
- data/lib/hecks/hecks/adapters/terminal.adapter +7 -0
- data/lib/hecks/hecks/adapters/terminal.rb +74 -0
- data/lib/hecks/hecks/codebase.behaviors +172 -0
- data/lib/hecks/hecks/codebase.bluebook +2453 -0
- data/lib/hecks/hecks/context_map.hecksagon +13 -0
- data/lib/hecks/hecks/custodian.behaviors +179 -0
- data/lib/hecks/hecks/custodian.bluebook +1847 -0
- data/lib/hecks/hecks/deploy.behaviors +74 -0
- data/lib/hecks/hecks/deploy.world +12 -0
- data/lib/hecks/hecks/environments/memory.world +10 -0
- data/lib/hecks/hecks/governance.world +7 -0
- data/lib/hecks/hecks/hecks.behaviors +62 -0
- data/lib/hecks/hecks/hecks.bluebook +124 -0
- data/lib/hecks/hecks/hecks.hecksagon +390 -0
- data/lib/hecks/hecks/hecks.world +42 -0
- data/lib/hecks/ir.rb +14 -83
- data/lib/hecks/kernel_capabilities.rb +194 -0
- data/lib/hecks/language/adapter.bluebook +2 -7
- data/lib/hecks/language/bluebook/aggregate.bluebook +56 -221
- data/lib/hecks/language/bluebook/attaches/paging.bluebook +6 -15
- data/lib/hecks/language/bluebook/bluebook.bluebook +26 -83
- data/lib/hecks/language/bluebook/bluebook.hecksagon +3 -15
- data/lib/hecks/language/bluebook/command.bluebook +27 -144
- data/lib/hecks/language/bluebook/entity.bluebook +24 -97
- data/lib/hecks/language/bluebook/policy.bluebook +16 -67
- data/lib/hecks/language/bluebook/process_manager.bluebook +28 -151
- data/lib/hecks/language/bluebook/projection.bluebook +126 -60
- data/lib/hecks/language/bluebook/query.bluebook +19 -43
- data/lib/hecks/language/bluebook/shape.bluebook +23 -73
- data/lib/hecks/language/bluebook/syntax.bluebook +51 -262
- data/lib/hecks/language/bluebook/vocabulary.bluebook +83 -198
- data/lib/hecks/language/hecksagon/adapter_binding.bluebook +3 -10
- data/lib/hecks/language/hecksagon/domain_port.bluebook +14 -19
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +26 -25
- data/lib/hecks/language/hecksagon/port_operation.bluebook +3 -14
- data/lib/hecks/language/oidc.json +35 -0
- data/lib/hecks/language/port.bluebook +4 -17
- data/lib/hecks/language/translation/translation.bluebook +3 -9
- data/lib/hecks/language/translation/translation_aggregate.bluebook +5 -24
- data/lib/hecks/language/world/wiring.bluebook +1 -3
- data/lib/hecks/language/world/world.bluebook +5 -7
- data/lib/hecks/literal.rb +8 -50
- data/lib/hecks/mcp_stdio_guard.rb +4 -41
- data/lib/hecks/naming.rb +29 -137
- data/lib/hecks/persistence_legacy_fixture/regenerate.rb +161 -0
- data/lib/hecks/persistence_legacy_fixture.rb +113 -0
- data/lib/hecks/ports/access_control.rb +7 -23
- data/lib/hecks/ports/agent/answers.rb +5 -23
- data/lib/hecks/ports/agent.rb +47 -172
- data/lib/hecks/ports/authentication.rb +12 -39
- data/lib/hecks/ports/authorization.rb +16 -43
- data/lib/hecks/ports/clock.rb +5 -45
- data/lib/hecks/ports/extraction.rb +7 -12
- data/lib/hecks/ports/identity_assignment.rb +8 -29
- data/lib/hecks/ports/identity_generation.rb +6 -29
- data/lib/hecks/ports/identity_resolution.rb +2 -10
- data/lib/hecks/ports/key_vault.rb +9 -45
- data/lib/hecks/ports/persistence/append_only.rb +61 -64
- data/lib/hecks/ports/persistence/binding_policy.rb +4 -14
- data/lib/hecks/ports/persistence/codec_boundary.rb +20 -80
- data/lib/hecks/ports/persistence/null_saga_store.rb +4 -20
- data/lib/hecks/ports/persistence/plugin.rb +13 -42
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +44 -144
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +15 -115
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +20 -85
- data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +6 -25
- data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +18 -24
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +55 -181
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/events.rb +77 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +12 -122
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +28 -128
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +73 -474
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +31 -124
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +45 -219
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +13 -108
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +19 -76
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +9 -49
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +6 -123
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +23 -55
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +14 -72
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +7 -16
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +43 -127
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +13 -45
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +131 -555
- data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +13 -63
- data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +227 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +2 -17
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +3 -14
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +13 -58
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -17
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +9 -26
- data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +50 -47
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +18 -124
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +34 -123
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +5 -12
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +3 -9
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -14
- data/lib/hecks/ports/persistence/plugins/era/translation.rb +1 -0
- data/lib/hecks/ports/persistence/plugins/era.rb +4 -23
- data/lib/hecks/ports/persistence/postgres_dump/connection.rb +7 -23
- data/lib/hecks/ports/persistence/postgres_dump.rb +4 -35
- data/lib/hecks/ports/persistence/remote_runtime.rb +2 -24
- data/lib/hecks/ports/persistence/repository_factory.rb +8 -27
- data/lib/hecks/ports/persistence/state_codec.rb +47 -108
- data/lib/hecks/ports/persistence.rb +7 -14
- data/lib/hecks/ports/projection.rb +42 -84
- data/lib/hecks/ports/query/in_memory.rb +10 -58
- data/lib/hecks/ports/query/ordering.rb +11 -32
- data/lib/hecks/ports/query.rb +14 -37
- data/lib/hecks/ports.rb +2 -8
- data/lib/hecks/projection_files.rb +285 -0
- data/lib/hecks/projections/bootstrap_table.rb +12 -44
- data/lib/hecks/projections/deploy/fargate/assembly.rb +2 -15
- data/lib/hecks/projections/deploy/fargate/cdn.rb +1 -48
- data/lib/hecks/projections/deploy/fargate/cdn_default.rb +2 -11
- data/lib/hecks/projections/deploy/fargate/check.rb +5 -14
- data/lib/hecks/projections/deploy/fargate/containers.rb +3 -78
- data/lib/hecks/projections/deploy/fargate/extras.rb +3 -44
- data/lib/hecks/projections/deploy/fargate/monitoring.rb +0 -44
- data/lib/hecks/projections/deploy/fargate/settings.rb +2 -54
- data/lib/hecks/projections/deploy/fargate/yaml.rb +13 -58
- data/lib/hecks/projections/deploy/fargate.rb +40 -207
- data/lib/hecks/projections/deploy/lambda.rb +191 -708
- data/lib/hecks/projections/deploy/preview/access_yaml.rb +1 -33
- data/lib/hecks/projections/deploy/preview/containers.rb +6 -111
- data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
- data/lib/hecks/projections/deploy/preview/routing_yaml.rb +4 -56
- data/lib/hecks/projections/deploy/preview/script.rb +2 -14
- data/lib/hecks/projections/deploy/preview/settings.rb +3 -33
- data/lib/hecks/projections/deploy/preview/task_yaml.rb +2 -38
- data/lib/hecks/projections/deploy/preview/template.rb +3 -59
- data/lib/hecks/projections/deploy/preview.rb +2 -67
- data/lib/hecks/projections/deploy/scripts/deploy-service.sh.tmpl +1 -1
- data/lib/hecks/projections/deploy/scripts/expected-era.header +1 -1
- data/lib/hecks/projections/deploy/scripts/hosting.mk.tmpl +3 -3
- data/lib/hecks/projections/deploy/scripts/settings.rb +3 -25
- data/lib/hecks/projections/deploy/scripts/smoke-after-deploy.sh.tmpl +1 -1
- data/lib/hecks/projections/deploy/scripts.rb +4 -57
- data/lib/hecks/projections/deploy/shared.rb +48 -147
- data/lib/hecks/projections/deploy/smoke.rb +4 -61
- data/lib/hecks/projections/deploy/template_diff/comparer.rb +3 -25
- data/lib/hecks/projections/deploy/template_diff/loader.rb +2 -11
- data/lib/hecks/projections/deploy/template_diff.rb +5 -57
- data/lib/hecks/projections/diagrams.rb +22 -584
- data/lib/hecks/projections/glossary/html.rb +14 -130
- data/lib/hecks/projections/glossary/markdown.rb +8 -69
- data/lib/hecks/projections/glossary/mermaid.rb +9 -66
- data/lib/hecks/projections/glossary/sensitivity.rb +5 -34
- data/lib/hecks/projections/glossary/sentences.rb +14 -159
- data/lib/hecks/projections/glossary.rb +30 -222
- data/lib/hecks/projections/ir.rb +3 -10
- data/lib/hecks/projections/model/deviations.rb +16 -43
- data/lib/hecks/projections/model.rb +12 -51
- data/lib/hecks/projections/oidc.rb +12 -56
- data/lib/hecks/projections/parser_table.rb +13 -32
- data/lib/hecks/projections/reference.rb +4 -18
- data/lib/hecks/projections/rust_vocabulary.rb +13 -251
- data/lib/hecks/projections/shape.rb +3 -29
- data/lib/hecks/projections/statements.rb +7 -86
- data/lib/hecks/projections/vocabulary.rb +6 -22
- data/lib/hecks/projections.rb +2 -16
- data/lib/hecks/projector/cli_projector.rb +154 -400
- data/lib/hecks/projector/docs_projector.rb +10 -201
- data/lib/hecks/projector/exporter.rb +42 -134
- data/lib/hecks/projector/ir_projector.rb +2 -9
- data/lib/hecks/projector/narrate_projector.rb +15 -189
- data/lib/hecks/projector/target.rb +13 -76
- data/lib/hecks/projector.rb +30 -121
- data/lib/hecks/quality_control/adapters/agent.adapter +7 -0
- data/lib/hecks/quality_control/adapters/agent.rb +58 -0
- data/lib/hecks/quality_control/adapters/angle_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/angle_tools.rb +15 -0
- data/lib/hecks/quality_control/adapters/clearance_recorder.rb +36 -0
- data/lib/hecks/quality_control/adapters/clearance_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/clearance_tools.rb +17 -0
- data/lib/hecks/quality_control/adapters/git_pr.adapter +7 -0
- data/lib/hecks/quality_control/adapters/git_pr.rb +226 -0
- data/lib/hecks/quality_control/adapters/github_checks.adapter +9 -0
- data/lib/hecks/quality_control/adapters/github_checks.rb +83 -0
- data/lib/hecks/quality_control/adapters/github_ci_webhook.rb +56 -0
- data/lib/hecks/quality_control/adapters/github_issues.adapter +7 -0
- data/lib/hecks/quality_control/adapters/github_issues.rb +49 -0
- data/lib/hecks/quality_control/adapters/qa_tool.rb +59 -0
- data/lib/hecks/quality_control/adapters/sweep_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/sweep_tools.rb +51 -0
- data/lib/hecks/quality_control/adapters/target_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/target_tools.rb +43 -0
- data/lib/hecks/quality_control/cli/child.rb +45 -0
- data/lib/hecks/quality_control/cli/qa_concurrency_racer.rb +45 -0
- data/lib/hecks/quality_control/cli/qa_discover_external_domains.rb +248 -0
- data/lib/hecks/quality_control/cli/qa_domain_novelty.rb +163 -0
- data/lib/hecks/quality_control/cli/qa_generated_domains.rb +450 -0
- data/lib/hecks/quality_control/cli/qa_log_bug.rb +195 -0
- data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +220 -0
- data/lib/hecks/quality_control/cli/qa_open_pr.rb +305 -0
- data/lib/hecks/quality_control/cli/qa_postgres_migrate.rb +154 -0
- data/lib/hecks/quality_control/cli/qa_postgres_role.rb +159 -0
- data/lib/hecks/quality_control/cli/qa_pr_check.rb +232 -0
- data/lib/hecks/quality_control/cli/qa_seed_angles.rb +142 -0
- data/lib/hecks/quality_control/cli/qa_seed_targets.rb +69 -0
- data/lib/hecks/quality_control/cli/qa_sweep/all_mode.rb +280 -0
- data/lib/hecks/quality_control/cli/qa_sweep/checks.rb +329 -0
- data/lib/hecks/quality_control/cli/qa_sweep/dials.rb +92 -0
- data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +117 -0
- data/lib/hecks/quality_control/cli/qa_sweep/parsing.rb +175 -0
- data/lib/hecks/quality_control/cli/qa_sweep/release_mode.rb +60 -0
- data/lib/hecks/quality_control/cli/qa_sweep.rb +611 -0
- data/lib/hecks/quality_control/cli/qa_tick.rb +274 -0
- data/lib/hecks/quality_control/quality_control.bluebook +2224 -0
- data/lib/hecks/quality_control/quality_control.hecksagon +13 -0
- data/lib/hecks/quality_control/quality_control.ports.hecksagon +54 -0
- data/lib/hecks/query_ir.rb +346 -0
- data/lib/hecks/query_ir_mcp.rb +198 -0
- data/lib/hecks/query_specification/common/comparators.rb +5 -26
- data/lib/hecks/query_specification/common/comparison.rb +35 -143
- data/lib/hecks/query_specification/common/dsl.rb +8 -24
- data/lib/hecks/query_specification/common/null_policy.rb +20 -70
- data/lib/hecks/query_specification/common/options.rb +2 -6
- data/lib/hecks/query_specification/common/where_clause.rb +2 -10
- data/lib/hecks/query_specification/field_path.rb +37 -77
- data/lib/hecks/query_specification/hop_path.rb +12 -76
- data/lib/hecks/release/gem_pin.rb +2 -26
- data/lib/hecks/release/runner/ci_publisher.rb +5 -8
- data/lib/hecks/release/runner/clean_tree.rb +42 -0
- data/lib/hecks/release/runner/commands.rb +2 -4
- data/lib/hecks/release/runner/console.rb +6 -0
- data/lib/hecks/release/runner/gem_publisher.rb +15 -9
- data/lib/hecks/release/runner/npm_publisher.rb +9 -53
- data/lib/hecks/release/runner/preflight.rb +29 -20
- data/lib/hecks/release/runner/published.rb +8 -26
- data/lib/hecks/release/runner/tagger.rb +31 -23
- data/lib/hecks/release/runner.rb +60 -38
- data/lib/hecks/rendering.rb +4 -26
- data/lib/hecks/router/namespace_installer.rb +15 -39
- data/lib/hecks/router.rb +4 -8
- data/lib/hecks/runtime/adapter_lookup.rb +124 -0
- data/lib/hecks/runtime/aggregate_lock.rb +8 -33
- data/lib/hecks/runtime/boot_gates.rb +3 -18
- data/lib/hecks/runtime/caller.rb +19 -59
- data/lib/hecks/runtime/capability_graph.rb +5 -21
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +16 -78
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +27 -160
- data/lib/hecks/runtime/command_interpreter.rb +124 -306
- data/lib/hecks/runtime/command_rules/admissibility.rb +95 -366
- data/lib/hecks/runtime/command_rules/arithmetic.rb +66 -196
- data/lib/hecks/runtime/command_rules/authorization.rb +13 -45
- data/lib/hecks/runtime/command_rules/emission.rb +8 -21
- data/lib/hecks/runtime/command_rules/references.rb +23 -202
- data/lib/hecks/runtime/dependency_planning.rb +50 -104
- data/lib/hecks/runtime/dispatcher.rb +134 -243
- data/lib/hecks/runtime/entity_element.rb +40 -462
- data/lib/hecks/runtime/entity_interpreter.rb +64 -219
- data/lib/hecks/runtime/errors.rb +32 -115
- data/lib/hecks/runtime/event.rb +3 -25
- data/lib/hecks/runtime/identity.rb +18 -154
- data/lib/hecks/runtime/instance.rb +18 -150
- data/lib/hecks/runtime/interpreting.rb +14 -70
- data/lib/hecks/runtime/invocation.rb +28 -164
- data/lib/hecks/runtime/loader.rb +109 -194
- data/lib/hecks/runtime/outbox.rb +39 -262
- data/lib/hecks/runtime/policy_interpreter.rb +43 -202
- data/lib/hecks/runtime/port_operation_interpreter.rb +28 -97
- data/lib/hecks/runtime/query_interpreter.rb +122 -187
- data/lib/hecks/runtime/reaction_invocation.rb +36 -138
- data/lib/hecks/runtime/reaction_outcome.rb +89 -0
- data/lib/hecks/runtime/read_model_interpreter.rb +117 -208
- data/lib/hecks/runtime/rebuild_sweep.rb +8 -54
- data/lib/hecks/runtime/reference_hop.rb +8 -81
- data/lib/hecks/runtime/refusal_wording.rb +28 -87
- data/lib/hecks/runtime/registry/saga_persistence.rb +10 -102
- data/lib/hecks/runtime/registry/verification.rb +213 -227
- data/lib/hecks/runtime/registry/world_defaults.rb +4 -53
- data/lib/hecks/runtime/registry.rb +156 -247
- data/lib/hecks/runtime/remote_dispatcher.rb +68 -154
- data/lib/hecks/runtime/routing.rb +4 -15
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +14 -61
- data/lib/hecks/runtime/saga_interpreter.rb +79 -263
- data/lib/hecks/runtime/saga_pending_dispatch.rb +2 -43
- data/lib/hecks/runtime/tenant_check.rb +6 -68
- data/lib/hecks/runtime/tenant_scope.rb +3 -28
- data/lib/hecks/runtime/value/admission.rb +16 -67
- data/lib/hecks/runtime/value/coercion.rb +162 -760
- data/lib/hecks/runtime/value/entity_list_coercion.rb +26 -219
- data/lib/hecks/runtime/value/field_checks.rb +172 -0
- data/lib/hecks/runtime/value.rb +15 -84
- data/lib/hecks/runtime.rb +35 -69
- data/lib/hecks/rust_build/conformance.rb +158 -0
- data/lib/hecks/rust_build/conformance_fuzz.rb +71 -0
- data/lib/hecks/rust_build/coverage.rb +218 -0
- data/lib/hecks/rust_build/native_build.rb +124 -0
- data/lib/hecks/rust_build/project_rust/root_mod.rb +120 -0
- data/lib/hecks/rust_build/project_rust.rb +332 -0
- data/lib/hecks/rust_build/wasm.rb +105 -0
- data/lib/hecks/rust_build/wasm_browser.rb +113 -0
- data/lib/hecks/rust_build.rb +144 -0
- data/lib/hecks/storehouse.rb +155 -582
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +10 -34
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +6 -23
- data/lib/hecks/three_zero/forms.yml +127 -0
- data/lib/hecks/three_zero.rb +12 -0
- data/lib/hecks/tools/argument_gate_matrix.rb +316 -0
- data/lib/hecks/tools/comment_style.rb +1070 -0
- data/lib/hecks/tools/corpus_report.rb +87 -0
- data/lib/hecks/tools/deploy_recipe.rb +255 -0
- data/lib/hecks/tools/deploy_recipe_lint.rb +282 -0
- data/lib/hecks/tools/drop_implicit_append_fields.rb +95 -0
- data/lib/hecks/tools/era_projection_backfill.rb +94 -0
- data/lib/hecks/tools/era_reattest.rb +146 -0
- data/lib/hecks/tools/evolve_run.rb +189 -0
- data/lib/hecks/tools/fuzz_sweep.rb +373 -0
- data/lib/hecks/tools/heki_compaction.rb +100 -0
- data/lib/hecks/tools/hoist_local_givens.rb +246 -0
- data/lib/hecks/tools/journal_compaction.rb +89 -0
- data/lib/hecks/tools/oidc_manifests.rb +70 -0
- data/lib/hecks/tools/query_ir_run.rb +81 -0
- data/lib/hecks/tools/regeneration_run.rb +166 -0
- data/lib/hecks/tools/rust_comment_style.rb +642 -0
- data/lib/hecks/tools/sequence_script.rb +62 -0
- data/lib/hecks/tools/tail_merge.rb +117 -0
- data/lib/hecks/tools/tenant_provisioning.rb +142 -0
- data/lib/hecks/tools/translation_audit.rb +208 -0
- data/lib/hecks/tools/translation_scaffold.rb +131 -0
- data/lib/hecks/tools.rb +86 -0
- data/lib/hecks/vendoring/git_environment.rb +2 -8
- data/lib/hecks/vendoring/git_source.rb +4 -10
- data/lib/hecks/vendoring.rb +7 -46
- data/lib/hecks/version.rb +3 -14
- data/lib/hecks.rb +52 -90
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -46
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +11 -80
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +10 -99
- data/qa/settings.yml +66 -0
- data/rust/.cargo/config.toml +6 -0
- data/rust/.gitignore +2 -0
- data/rust/Cargo.lock +54 -0
- data/rust/Cargo.toml +61 -0
- data/rust/build/.cargo/config.toml +3 -0
- data/rust/build/.gitignore +1 -0
- data/rust/build/Cargo.lock +7 -0
- data/rust/build/Cargo.toml +19 -0
- data/rust/build/src/build_artifact.rs +47 -0
- data/rust/build/src/cargo_sync.rs +248 -0
- data/rust/build/src/json.rs +421 -0
- data/rust/build/src/lineage_pass.rs +285 -0
- data/rust/build/src/main.rs +51 -0
- data/rust/build/src/optional_pass.rs +150 -0
- data/rust/build/src/pipeline.rs +264 -0
- data/rust/build/src/reserved_names.rs +76 -0
- data/rust/build/src/resolve.rs +124 -0
- data/rust/build/src/root.rs +25 -0
- data/rust/build/src/sidecars.rs +73 -0
- data/rust/build/src/subprocess.rs +51 -0
- data/rust/build/src/tmp.rs +31 -0
- data/rust/codegen/.cargo/config.toml +3 -0
- data/rust/codegen/.gitignore +1 -0
- data/rust/codegen/Cargo.lock +7 -0
- data/rust/codegen/Cargo.toml +20 -0
- data/rust/codegen/src/attr.rs +32 -0
- data/rust/codegen/src/bridging.rs +481 -0
- data/rust/codegen/src/commands.rs +1226 -0
- data/rust/codegen/src/constraints.rs +109 -0
- data/rust/codegen/src/dependency_planning.rs +209 -0
- data/rust/codegen/src/domain_generator.rs +1288 -0
- data/rust/codegen/src/exemplar.rs +230 -0
- data/rust/codegen/src/expr_emitter.rs +87 -0
- data/rust/codegen/src/fielded.rs +192 -0
- data/rust/codegen/src/hecks_naming.rs +33 -0
- data/rust/codegen/src/json.rs +335 -0
- data/rust/codegen/src/json_codec.rs +697 -0
- data/rust/codegen/src/literal.rs +207 -0
- data/rust/codegen/src/main.rs +290 -0
- data/rust/codegen/src/manifest.rs +227 -0
- data/rust/codegen/src/mutations.rs +784 -0
- data/rust/codegen/src/naming.rs +286 -0
- data/rust/codegen/src/ports.rs +208 -0
- data/rust/codegen/src/prelude.rs +116 -0
- data/rust/codegen/src/queries.rs +786 -0
- data/rust/codegen/src/reactions.rs +532 -0
- data/rust/codegen/src/read_models.rs +697 -0
- data/rust/codegen/src/reference_specs.rs +40 -0
- data/rust/codegen/src/registry.rs +966 -0
- data/rust/codegen/src/reserved_names.rs +76 -0
- data/rust/codegen/src/shared.rs +93 -0
- data/rust/codegen/src/skip_reason.rs +25 -0
- data/rust/codegen/src/types.rs +362 -0
- data/rust/host/.cargo/config.toml +3 -0
- data/rust/host/.gitignore +1 -0
- data/rust/host/Cargo.lock +4297 -0
- data/rust/host/Cargo.toml +61 -0
- data/rust/host/HECKS_RELEASE +1 -0
- data/rust/host/rds-ca-bundle.pem +2736 -0
- data/rust/host/src/api.rs +1596 -0
- data/rust/host/src/approval.rs +652 -0
- data/rust/host/src/auth.rs +1287 -0
- data/rust/host/src/bin/expr_harness.rs +46 -0
- data/rust/host/src/bin/lineage_harness.rs +119 -0
- data/rust/host/src/bin/mint_harness.rs +135 -0
- data/rust/host/src/checkout.rs +265 -0
- data/rust/host/src/dispatch.rs +1443 -0
- data/rust/host/src/expr_json.rs +1117 -0
- data/rust/host/src/field_hints.rs +52 -0
- data/rust/host/src/ir.rs +733 -0
- data/rust/host/src/journal.rs +874 -0
- data/rust/host/src/lambda_client.rs +378 -0
- data/rust/host/src/log.rs +147 -0
- data/rust/host/src/main.rs +474 -0
- data/rust/host/src/mint.rs +1865 -0
- data/rust/host/src/payments/keystore.rs +330 -0
- data/rust/host/src/payments/tests.rs +1400 -0
- data/rust/host/src/payments.rs +767 -0
- data/rust/host/src/presentation.rs +698 -0
- data/rust/host/src/presentation_write.rs +1431 -0
- data/rust/host/src/query_step.rs +149 -0
- data/rust/host/src/rate_limit.rs +904 -0
- data/rust/host/src/reference_transform.rs +241 -0
- data/rust/host/src/reference_validate.rs +338 -0
- data/rust/host/src/resend.rs +296 -0
- data/rust/host/src/secrets.rs +106 -0
- data/rust/host/src/server.rs +663 -0
- data/rust/host/src/storage_shape.rs +175 -0
- data/rust/host/src/test_pg.rs +40 -0
- data/rust/host/src/ui_schema.rs +1351 -0
- data/rust/host/src/wasm_runner.rs +191 -0
- data/rust/host/src/web/newsletter/cooldown.rs +126 -0
- data/rust/host/src/web/newsletter/tests.rs +104 -0
- data/rust/host/src/web/newsletter.rs +310 -0
- data/rust/host/src/web/newsletter_send.rs +357 -0
- data/rust/host/src/web/registrations.rs +1505 -0
- data/rust/host/src/web.rs +2937 -0
- data/rust/lsp/.gitignore +1 -0
- data/rust/lsp/Cargo.lock +7 -0
- data/rust/lsp/Cargo.toml +16 -0
- data/rust/lsp/README.md +133 -0
- data/rust/lsp/src/diagnostics.rs +165 -0
- data/rust/lsp/src/json.rs +385 -0
- data/rust/lsp/src/main.rs +399 -0
- data/rust/lsp/src/outline.rs +177 -0
- data/rust/lsp/src/rpc.rs +67 -0
- data/rust/parser/.cargo/config.toml +5 -0
- data/rust/parser/.gitignore +1 -0
- data/rust/parser/Cargo.lock +7 -0
- data/rust/parser/Cargo.toml +21 -0
- data/rust/parser/src/build/closed_sets.rs +27 -0
- data/rust/parser/src/build/identity.rs +276 -0
- data/rust/parser/src/build/mod.rs +12 -0
- data/rust/parser/src/build/naming.rs +141 -0
- data/rust/parser/src/build/pattern_subset.rs +161 -0
- data/rust/parser/src/build/query_derive.rs +72 -0
- data/rust/parser/src/build/query_inference.rs +269 -0
- data/rust/parser/src/build/query_options.rs +100 -0
- data/rust/parser/src/build/read_model.rs +94 -0
- data/rust/parser/src/build/references.rs +32 -0
- data/rust/parser/src/canonical.rs +114 -0
- data/rust/parser/src/diag.rs +55 -0
- data/rust/parser/src/emit.rs +953 -0
- data/rust/parser/src/expr/ast_json.rs +147 -0
- data/rust/parser/src/expr/evaluator.rs +81 -0
- data/rust/parser/src/expr/mod.rs +84 -0
- data/rust/parser/src/expr/resolver.rs +497 -0
- data/rust/parser/src/ir.rs +387 -0
- data/rust/parser/src/keywords.rs +585 -0
- data/rust/parser/src/lex.rs +662 -0
- data/rust/parser/src/main.rs +252 -0
- data/rust/parser/src/parse/aggregate.rs +612 -0
- data/rust/parser/src/parse/chapter.rs +517 -0
- data/rust/parser/src/parse/command.rs +637 -0
- data/rust/parser/src/parse/domain_port.rs +127 -0
- data/rust/parser/src/parse/entity.rs +527 -0
- data/rust/parser/src/parse/file.rs +67 -0
- data/rust/parser/src/parse/hecksagon.rs +231 -0
- data/rust/parser/src/parse/lifecycle.rs +162 -0
- data/rust/parser/src/parse/mod.rs +1262 -0
- data/rust/parser/src/parse/policy.rs +87 -0
- data/rust/parser/src/parse/process_manager.rs +305 -0
- data/rust/parser/src/parse/query.rs +100 -0
- data/rust/parser/src/parse/read_model.rs +214 -0
- data/rust/parser/src/parse/value_object.rs +250 -0
- data/rust/parser/src/ruby_value.rs +422 -0
- data/rust/project/bridging.rb +257 -0
- data/rust/project/commands.rb +877 -0
- data/rust/project/constraints.rb +86 -0
- data/rust/project/dependency_planning.rb +178 -0
- data/rust/project/domain_generator.rb +980 -0
- data/rust/project/exemplar.rb +148 -0
- data/rust/project/expr_emitter.rb +72 -0
- data/rust/project/fielded.rb +141 -0
- data/rust/project/json_codec.rb +641 -0
- data/rust/project/mutations.rb +721 -0
- data/rust/project/naming.rb +205 -0
- data/rust/project/ports.rb +65 -0
- data/rust/project/queries.rb +493 -0
- data/rust/project/reactions.rb +317 -0
- data/rust/project/read_models.rb +690 -0
- data/rust/project/reference_specs.rb +27 -0
- data/rust/project/registry.rb +396 -0
- data/rust/project/skip_reason.rb +29 -0
- data/rust/project/types.rb +225 -0
- data/rust/project/write_if_changed.rb +83 -0
- data/rust/project.rb +30 -0
- data/rust/project_rust_pipeline.rb +383 -0
- data/rust/src/exemplar/commands.rs +279 -0
- data/rust/src/exemplar/constraints.rs +18 -0
- data/rust/src/exemplar/fielded.rs +280 -0
- data/rust/src/exemplar/json.rs +277 -0
- data/rust/src/exemplar/mod.rs +17 -0
- data/rust/src/exemplar/mutations.rs +80 -0
- data/rust/src/exemplar/queries.rs +33 -0
- data/rust/src/exemplar/reactions.rs +88 -0
- data/rust/src/exemplar/read_models.rs +48 -0
- data/rust/src/exemplar/registry.rs +166 -0
- data/rust/src/exemplar/types.rs +44 -0
- data/rust/src/kernel/attribute_shapes/composite.rs +88 -0
- data/rust/src/kernel/attribute_shapes/list.rs +18 -0
- data/rust/src/kernel/attribute_shapes/mod.rs +44 -0
- data/rust/src/kernel/attribute_shapes/optional.rs +9 -0
- data/rust/src/kernel/attribute_shapes/scalar.rs +39 -0
- data/rust/src/kernel/cli.rs +585 -0
- data/rust/src/kernel/dispatch.rs +1090 -0
- data/rust/src/kernel/expr.rs +458 -0
- data/rust/src/kernel/expression_operators/arithmetic.rs +128 -0
- data/rust/src/kernel/expression_operators/comparison.rs +48 -0
- data/rust/src/kernel/expression_operators/enumeration.rs +232 -0
- data/rust/src/kernel/expression_operators/logical.rs +13 -0
- data/rust/src/kernel/expression_operators/membership.rs +18 -0
- data/rust/src/kernel/expression_operators/mod.rs +68 -0
- data/rust/src/kernel/expression_operators/pattern_match.rs +73 -0
- data/rust/src/kernel/expression_operators/positional.rs +126 -0
- data/rust/src/kernel/expression_operators/presence.rs +101 -0
- data/rust/src/kernel/expression_operators/sign_test.rs +16 -0
- data/rust/src/kernel/expression_operators/sized.rs +35 -0
- data/rust/src/kernel/expression_operators/text.rs +79 -0
- data/rust/src/kernel/expression_operators/to_string.rs +18 -0
- data/rust/src/kernel/json.rs +779 -0
- data/rust/src/kernel/mod.rs +133 -0
- data/rust/src/kernel/named_query.rs +219 -0
- data/rust/src/kernel/orchestrate.rs +1810 -0
- data/rust/src/kernel/pattern.rs +307 -0
- data/rust/src/kernel/query_comparators.rs +206 -0
- data/rust/src/kernel/query_ordering.rs +103 -0
- data/rust/src/kernel/read_model.rs +914 -0
- data/rust/src/kernel/reference_lookup.rs +149 -0
- data/rust/src/kernel/repository.rs +468 -0
- data/rust/src/kernel/routing.rs +612 -0
- data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +118 -0
- data/rust/src/kernel/vocab/entity_dispatch_order.rs +114 -0
- data/rust/src/kernel/vocab/field_hint.rs +70 -0
- data/rust/src/kernel/vocab/mod.rs +20 -0
- data/rust/src/kernel/vocab/query_comparator.rs +65 -0
- data/rust/src/kernel/vocab/refusal_template.rs +1592 -0
- data/rust/src/lib.rs +8 -0
- data/rust/src/main.rs +16 -0
- data/rust/web/.gitignore +1 -0
- data/rust/web/Cargo.lock +119 -0
- data/rust/web/Cargo.toml +18 -0
- data/rust/web/src/lib.rs +8 -0
- metadata +478 -12
- data/lib/hecks/facade/cli_door.rb +0 -178
- data/lib/hecks/facade/cli_runner.rb +0 -271
- data/lib/hecks/facade/handle.rb +0 -293
- data/lib/hecks/facade/json_door.rb +0 -247
- data/lib/hecks/facade/surface/aggregate_door.rb +0 -222
- data/lib/hecks/facade/surface/chapter.rb +0 -116
- data/lib/hecks/facade/surface.rb +0 -61
- data/lib/hecks/mcp_door_scope.rb +0 -176
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "tempfile"
|
|
4
|
+
require_relative "../child"
|
|
5
|
+
|
|
6
|
+
module Hecks
|
|
7
|
+
module QualityControlCli
|
|
8
|
+
class QaSweep
|
|
9
|
+
# `--all`: one child sweep per waiting target, in parallel, then the later waves and one
|
|
10
|
+
# consolidated report.
|
|
11
|
+
#
|
|
12
|
+
# A child is a fresh process, never a fork: a forked child would share this process's live
|
|
13
|
+
# Postgres socket.
|
|
14
|
+
module AllMode
|
|
15
|
+
# Every child prints one `resolved depth:` line; the child's log is the single source of
|
|
16
|
+
# truth that the report prints verbatim, so scraping it here cannot disagree.
|
|
17
|
+
RESOLVED_DEPTH_LINE = /^resolved depth: seeds=(\d+) steps=(\d+) \(clean_streak=(\d+)\)$/
|
|
18
|
+
|
|
19
|
+
# Parsed from a child's `resolved modes:` line; wave-2 candidates come from the parent's own
|
|
20
|
+
# inference, not from this.
|
|
21
|
+
RESOLVED_MODES_LINE = /^resolved modes: (\S*) \(capabilities=([^;)]*)(?:; deferred=([^)]*))?\)$/
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
# Sweeps every waiting target and reports.
|
|
26
|
+
#
|
|
27
|
+
# @param modes [Array<Symbol>] the enabled modes each child is told
|
|
28
|
+
# @param parity_wave [Boolean] whether a `persistence_parity` wave follows a clean wave 1
|
|
29
|
+
# @param deferred_wave_modes [Array<Symbol>] the modes that get a wave of their own
|
|
30
|
+
# @return [Integer] 2 when any child found something, else 1 when any errored, else 0
|
|
31
|
+
def run_all_mode(modes:, parity_wave:, deferred_wave_modes:)
|
|
32
|
+
now = Time.now.to_i
|
|
33
|
+
waiting = query("Target.Rotation").map { |row| row[:reference][:value] }
|
|
34
|
+
stale = stale_holds(now)
|
|
35
|
+
targets = waiting + stale.map { |row| row[:reference][:value] }
|
|
36
|
+
if targets.empty?
|
|
37
|
+
puts "rotation is empty — nothing waiting to sweep, and no stale hold to reclaim. Clean no-op."
|
|
38
|
+
return EXIT_OK
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
announce_all(targets, stale, modes, now)
|
|
42
|
+
started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
43
|
+
results = run_pool(targets) do |reference|
|
|
44
|
+
spawn_sweep_child(reference, @seeds_override, @steps_override, @self_consistency, modes: modes)
|
|
45
|
+
end
|
|
46
|
+
results = results.map { |r| r.merge(wave: 1) }
|
|
47
|
+
results += run_parity_wave(results) if parity_wave
|
|
48
|
+
# Each enabled deferred mode gets its own wave over wave 1's clean targets.
|
|
49
|
+
deferred_wave_modes.each do |mode|
|
|
50
|
+
results += run_deferred_wave(results.select { |r| r[:wave] == 1 }, mode)
|
|
51
|
+
end
|
|
52
|
+
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at
|
|
53
|
+
|
|
54
|
+
clean = results.select { |r| r[:exit_status] == EXIT_OK }
|
|
55
|
+
found = results.select { |r| r[:exit_status] == EXIT_FOUND_SOMETHING }
|
|
56
|
+
errored = results.reject { |r| [EXIT_OK, EXIT_FOUND_SOMETHING].include?(r[:exit_status]) }
|
|
57
|
+
print_all_mode_report(results, elapsed, clean, found, errored)
|
|
58
|
+
all_mode_exit_code(found, errored)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def announce_all(targets, stale, modes, now)
|
|
62
|
+
puts "sweeping #{targets.size} target(s), at most #{@max_parallel} at once: #{targets.join(', ')} " \
|
|
63
|
+
"(adversarial fraction #{@adversarial}, self-consistency #{@self_consistency}, " \
|
|
64
|
+
"role draw #{@role_draw}, dry-run fraction #{@dry_run}, enabled modes #{modes.join(',')})"
|
|
65
|
+
stale.each do |row|
|
|
66
|
+
puts "reclaimed stale hold: #{row[:reference][:value]} (held by #{row[:held_by][:value]}, " \
|
|
67
|
+
"#{now - row[:claimed_at][:value]}s ago)"
|
|
68
|
+
end
|
|
69
|
+
puts
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# `Target.Rotation` lists only waiting targets, so stale holds are found here:
|
|
73
|
+
# `Target.Claim`'s given (claimed_at + window <= now) restated in Ruby, since a `where`
|
|
74
|
+
# cannot add two fields.
|
|
75
|
+
def stale_holds(now)
|
|
76
|
+
query("Target.Held").select { |row| row[:claimed_at][:value] + row[:window][:value] <= now }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Spawns `hecks quality_control ask run target=<target>` as a fresh process, not a fork.
|
|
80
|
+
# Seeds/steps forward only when given, so each child derives its own depth from its own
|
|
81
|
+
# streak.
|
|
82
|
+
def spawn_sweep_child(target_reference, seeds_override, steps_override, self_consistency,
|
|
83
|
+
modes:, parity: false)
|
|
84
|
+
# Unlinked at once: the open fd stays readable, nothing is left on disk, and no fixed path
|
|
85
|
+
# collides.
|
|
86
|
+
log = Tempfile.new(["qa_sweep-#{filesystem_safe_component(target_reference)}-", ".log"])
|
|
87
|
+
log.unlink
|
|
88
|
+
|
|
89
|
+
args = [target_reference]
|
|
90
|
+
args += ["--seeds", seeds_override.to_s] if seeds_override
|
|
91
|
+
args += ["--steps", steps_override.to_s] if steps_override
|
|
92
|
+
args += ["--adversarial", @adversarial.to_s, "--self-consistency", self_consistency.to_s]
|
|
93
|
+
args += ["--role-draw", @role_draw.to_s, "--dry-run", @dry_run.to_s]
|
|
94
|
+
args += parity ? ["--persistence-parity"] : ["--modes", modes.join(",")]
|
|
95
|
+
|
|
96
|
+
pid = Process.spawn(*Child.argv(@root, "qa_sweep", *args), out: log, err: log, chdir: @root)
|
|
97
|
+
{ target: target_reference, pid: pid, log: log }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def collect_sweep_child(child, status)
|
|
101
|
+
child[:log].rewind
|
|
102
|
+
output = child[:log].read
|
|
103
|
+
child[:log].close
|
|
104
|
+
|
|
105
|
+
{ target: child[:target], exit_status: status.exitstatus, termsig: status.termsig, output: output }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Keeps at most `@max_parallel` children alive; results come back in queue order so reports
|
|
109
|
+
# are stable.
|
|
110
|
+
def run_pool(targets)
|
|
111
|
+
pending = targets.dup
|
|
112
|
+
running = {}
|
|
113
|
+
results = {}
|
|
114
|
+
|
|
115
|
+
until pending.empty? && running.empty?
|
|
116
|
+
while running.size < @max_parallel && (next_target = pending.shift)
|
|
117
|
+
child = yield(next_target)
|
|
118
|
+
running[child[:pid]] = child
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
pid, status = Process.wait2(-1)
|
|
122
|
+
child = running.delete(pid)
|
|
123
|
+
next unless child
|
|
124
|
+
|
|
125
|
+
results[child[:target]] = collect_sweep_child(child, status)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
targets.map { |target| results.fetch(target) }
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Wave 2: `--persistence-parity` children for wave-1 targets that came back clean and infer
|
|
132
|
+
# `postgres_era`. Skipped by `--no-parity` or when `--modes` already named the mode.
|
|
133
|
+
def run_parity_wave(wave1)
|
|
134
|
+
candidates = wave_candidates(wave1, :persistence_parity)
|
|
135
|
+
if candidates.empty?
|
|
136
|
+
puts "parity wave: no clean target binds PostgresEra — nothing to compare Memory against. Skipped."
|
|
137
|
+
return []
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
puts
|
|
141
|
+
puts "parity wave: Memory vs real PostgresEra for #{candidates.size} target(s), " \
|
|
142
|
+
"at most #{@max_parallel} at once: #{candidates.join(', ')}"
|
|
143
|
+
results = run_pool(candidates) do |reference|
|
|
144
|
+
spawn_sweep_child(reference, @seeds_override, nil, false, modes: [], parity: true)
|
|
145
|
+
end
|
|
146
|
+
# Labelled in the target column so every report section tells a wave-2 row from wave 1's.
|
|
147
|
+
results.map { |r| r.merge(wave: 2, target: "#{r[:target]} [parity wave]") }
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Later wave for deferred modes (era_boundary, concurrency): `--modes <mode>` children over
|
|
151
|
+
# the clean wave-1 targets whose capabilities make the mode eligible.
|
|
152
|
+
def run_deferred_wave(wave1, mode)
|
|
153
|
+
candidates = wave_candidates(wave1, mode)
|
|
154
|
+
if candidates.empty?
|
|
155
|
+
puts "#{mode} wave: no clean target qualifies. Skipped."
|
|
156
|
+
return []
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
puts
|
|
160
|
+
puts "#{mode} wave: #{candidates.size} target(s), at most #{@max_parallel} at once: #{candidates.join(', ')}"
|
|
161
|
+
results = run_pool(candidates) do |reference|
|
|
162
|
+
spawn_sweep_child(reference, @seeds_override, nil, false, modes: [mode])
|
|
163
|
+
end
|
|
164
|
+
results.map { |r| r.merge(wave: 2, target: "#{r[:target]} [#{mode} wave]") }
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# @return [Array<String>] the clean wave-1 targets whose capabilities make `mode` eligible
|
|
168
|
+
def wave_candidates(wave1, mode)
|
|
169
|
+
clean_refs = wave1.select { |r| r[:exit_status] == EXIT_OK }.map { |r| r[:target] }
|
|
170
|
+
rows = query("Target.All").select { |row| clean_refs.include?(row[:reference][:value]) }
|
|
171
|
+
eligible = rows.select do |row|
|
|
172
|
+
capabilities = Hecks::Fuzzing::TargetCapabilities.infer(resolve_target_path(row[:path][:value]),
|
|
173
|
+
rust_dir: @rust_dir)
|
|
174
|
+
Hecks::Fuzzing::TargetCapabilities.eligible?(mode, capabilities)
|
|
175
|
+
end
|
|
176
|
+
eligible.map { |row| row[:reference][:value] }
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def resolved_depth_for(output)
|
|
180
|
+
match = output.match(RESOLVED_DEPTH_LINE)
|
|
181
|
+
return "depth unknown (target errored before it could claim)" unless match
|
|
182
|
+
|
|
183
|
+
"#{match[1]} seeds x #{match[2]} steps (clean streak #{match[3]})"
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def resolved_modes_for(output)
|
|
187
|
+
match = output.match(RESOLVED_MODES_LINE)
|
|
188
|
+
return "modes unknown (target errored before it could resolve them)" unless match
|
|
189
|
+
|
|
190
|
+
line = "#{match[1].empty? ? 'none' : match[1]} (capabilities: #{match[2].empty? ? 'none' : match[2]}"
|
|
191
|
+
line += "; deferred: #{match[3]}" if match[3] && !match[3].empty?
|
|
192
|
+
"#{line})"
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def print_resolved_modes(results)
|
|
196
|
+
return if results.empty?
|
|
197
|
+
|
|
198
|
+
puts
|
|
199
|
+
puts "resolved modes (by target):"
|
|
200
|
+
results.each { |r| puts " #{r[:target]}: #{resolved_modes_for(r[:output])}" }
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def print_resolved_depths(results)
|
|
204
|
+
return if results.empty?
|
|
205
|
+
|
|
206
|
+
puts
|
|
207
|
+
puts "resolved depth (by target):"
|
|
208
|
+
results.each { |r| puts " #{r[:target]}: #{resolved_depth_for(r[:output])}" }
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def print_operational_errors(errored)
|
|
212
|
+
return if errored.empty?
|
|
213
|
+
|
|
214
|
+
puts
|
|
215
|
+
puts "OPERATIONAL ERRORS (#{errored.size}) — read each message, fix the actual problem, don't retry blind:"
|
|
216
|
+
errored.each do |r|
|
|
217
|
+
label = r[:exit_status] ? "exit #{r[:exit_status]}" : "killed by signal #{r[:termsig]}"
|
|
218
|
+
puts
|
|
219
|
+
puts "-- #{r[:target]} (#{label}) --"
|
|
220
|
+
puts r[:output].strip
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def print_found_something(found)
|
|
225
|
+
return if found.empty?
|
|
226
|
+
|
|
227
|
+
puts
|
|
228
|
+
puts "FOUND SOMETHING (#{found.size}) — full report per target below; act on every one:"
|
|
229
|
+
found.each do |r|
|
|
230
|
+
puts
|
|
231
|
+
puts "#" * 72
|
|
232
|
+
puts "# #{r[:target]}"
|
|
233
|
+
puts "#" * 72
|
|
234
|
+
puts r[:output]
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def print_all_mode_report(results, elapsed, clean, found, errored)
|
|
239
|
+
puts "=" * 72
|
|
240
|
+
puts "CONSOLIDATED REPORT — #{results.size} target(s) swept, #{elapsed.round(1)}s wall-clock " \
|
|
241
|
+
"(genuinely parallel — not the sum of every target's own sweep time)"
|
|
242
|
+
puts "=" * 72
|
|
243
|
+
puts
|
|
244
|
+
puts "clean (#{clean.size}): #{clean.empty? ? 'none' : clean.map { |r| r[:target] }.join(', ')}"
|
|
245
|
+
|
|
246
|
+
print_resolved_depths(results)
|
|
247
|
+
print_resolved_modes(results)
|
|
248
|
+
print_operational_errors(errored)
|
|
249
|
+
print_found_something(found)
|
|
250
|
+
print_trace_output(results)
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# A clean child's trace would otherwise be discarded; shown only under `QA_SWEEP_TRACE=1`.
|
|
254
|
+
def print_trace_output(results)
|
|
255
|
+
return unless @trace_enabled
|
|
256
|
+
return if results.empty?
|
|
257
|
+
|
|
258
|
+
puts
|
|
259
|
+
puts "QA_SWEEP_TRACE output (by target, in sweep order):"
|
|
260
|
+
results.each do |r|
|
|
261
|
+
trace_lines = r[:output].lines.grep(/^\[qa_sweep_trace /)
|
|
262
|
+
next if trace_lines.empty?
|
|
263
|
+
|
|
264
|
+
puts " #{r[:target]}:"
|
|
265
|
+
trace_lines.each { |line| puts " #{line.chomp}" }
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# A finding outranks operational errors: 2 if any child found something, else 1 if any
|
|
270
|
+
# errored.
|
|
271
|
+
def all_mode_exit_code(found, errored)
|
|
272
|
+
return EXIT_FOUND_SOMETHING unless found.empty?
|
|
273
|
+
return EXIT_ERROR unless errored.empty?
|
|
274
|
+
|
|
275
|
+
EXIT_OK
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
end
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module QualityControlCli
|
|
5
|
+
class QaSweep
|
|
6
|
+
# The comparisons one sweep makes per seed, and the checks it logs for each. A sweep logs one
|
|
7
|
+
# `Check` per mode per seed; the `[mode]` subject prefix tells the ledger which axis held or
|
|
8
|
+
# surprised, and each expectation is written before looking.
|
|
9
|
+
module Checks
|
|
10
|
+
MODE_EXPECTATIONS = {
|
|
11
|
+
differential: "Ruby and the compiled Rust conformance binary agree on instances, events, refusals, " \
|
|
12
|
+
"reactions, sagas, queries and dry runs across every generated step",
|
|
13
|
+
properties_in_differential: "no declared property is violated over the Ruby side of the same " \
|
|
14
|
+
"replayed history",
|
|
15
|
+
self_consistency: "each engine agrees with itself on rehydration/idempotency/value-object round trip",
|
|
16
|
+
ruby_only: "no declared property is violated and the interpreter does not crash across every " \
|
|
17
|
+
"generated step",
|
|
18
|
+
persistence_parity: "Memory and a real, disposable PostgresEra-backed boot agree on instances, " \
|
|
19
|
+
"events, refusals, reactions, sagas and queries across every generated step",
|
|
20
|
+
adapter_parity_sqlite: "Memory and a real, on-disk SQLite-backed boot agree on instances, events, " \
|
|
21
|
+
"refusals, reactions, sagas and queries across every generated step",
|
|
22
|
+
concurrency: "two real, forked, cross-process dispatches of the same generated command against " \
|
|
23
|
+
"the same PostgresEra-backed schema settle on the same set of outcomes the identical " \
|
|
24
|
+
"pair settles on when dispatched sequentially with no contention",
|
|
25
|
+
era_boundary: "no ancestor PostgresEra era this target's own real lineage holds still carries " \
|
|
26
|
+
"post-cut writes nobody has merged forward (Lineage#diverged_count, every ancestor, " \
|
|
27
|
+
"is zero)"
|
|
28
|
+
}.freeze
|
|
29
|
+
|
|
30
|
+
# Modes whose divergences are a pure function of the step list; a race, the per-sweep checks
|
|
31
|
+
# and generator crashes are not.
|
|
32
|
+
SHRINKABLE_MODES = %i[differential properties_in_differential self_consistency ruby_only
|
|
33
|
+
adapter_parity_sqlite persistence_parity].freeze
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
# Flattens Replay's `:self_consistency` results into the same `{field:, detail:}` shape as
|
|
38
|
+
# every finding.
|
|
39
|
+
def self_consistency_divergences(history) = Hecks::Fuzzing::Differential.self_consistency_divergences(history)
|
|
40
|
+
|
|
41
|
+
def property_divergences(history) = Hecks::Fuzzing::Differential.property_divergences(history)
|
|
42
|
+
|
|
43
|
+
# The Ruby-vs-Rust diff from `spec/rust_conformance_fuzz_spec.rb`, one divergence list per
|
|
44
|
+
# active mode so `run_one_seed` logs a `Check` per mode.
|
|
45
|
+
def diff_ruby_vs_rust(differ, domain_path, steps, binary, modes:)
|
|
46
|
+
Hecks::Fuzzing::Differential.diff(
|
|
47
|
+
differ, domain_path, steps, binary,
|
|
48
|
+
modes: modes, adapter_parity_sqlite: -> { adapter_parity_sqlite_divergences(domain_path, steps) }
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Single-runtime fallback (`hecks fuzz`'s `outcome`) for domains with no compiled Rust
|
|
53
|
+
# binary: a violated property, or an exception escaping `Replay.call`.
|
|
54
|
+
def ruby_only_outcome(domain_path, steps, modes:)
|
|
55
|
+
self_consistency = modes.include?(:self_consistency)
|
|
56
|
+
history = Hecks::Fuzzing::Replay.call(domain_path, steps, self_consistency: self_consistency)
|
|
57
|
+
outcomes = { ruby_only: property_divergences(history) }
|
|
58
|
+
outcomes[:self_consistency] = self_consistency_divergences(history) if self_consistency
|
|
59
|
+
if modes.include?(:adapter_parity_sqlite)
|
|
60
|
+
outcomes[:adapter_parity_sqlite] = adapter_parity_sqlite_divergences(domain_path, steps)
|
|
61
|
+
end
|
|
62
|
+
outcomes
|
|
63
|
+
rescue StandardError => e
|
|
64
|
+
# A crash is one mode's finding, not the absence of the others: each other enabled mode
|
|
65
|
+
# gets an explicit not_checked entry so the ledger still holds one Check per resolved
|
|
66
|
+
# mode.
|
|
67
|
+
crashed = { ruby_only: [{ field: "crash", detail: "#{e.class}: #{e.message}" }] }
|
|
68
|
+
%i[self_consistency adapter_parity_sqlite].each do |mode|
|
|
69
|
+
next unless modes.include?(mode)
|
|
70
|
+
|
|
71
|
+
crashed[mode] = [{ field: "not_checked",
|
|
72
|
+
detail: "the Ruby replay crashed before #{mode} ran — #{e.class}: #{e.message}" }]
|
|
73
|
+
end
|
|
74
|
+
crashed
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def log_check!(sweep, subject:, expectation:)
|
|
78
|
+
sweep.check!(subject: { value: subject }, expectation: { value: expectation })
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def check_for(mode, feature, seed, shape, divergences)
|
|
82
|
+
{ mode: mode,
|
|
83
|
+
subject: "[#{mode}] #{feature} fuzz seed #{seed} (#{shape})",
|
|
84
|
+
expectation: MODE_EXPECTATIONS.fetch(mode),
|
|
85
|
+
divergences: divergences,
|
|
86
|
+
clean: divergences.empty?,
|
|
87
|
+
observation: divergences.empty? ? "held" : "diverged on: #{divergences.map { |d| d[:field] }.join(', ')}" }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Extra `generate` arguments a guided seed needs in its `reproduce:` line; empty when
|
|
91
|
+
# unguided.
|
|
92
|
+
def plan_arguments(plan)
|
|
93
|
+
return "" unless plan
|
|
94
|
+
|
|
95
|
+
parts = []
|
|
96
|
+
parts << "prefix: #{plan.prefix.inspect}" if plan.prefix
|
|
97
|
+
parts << "favor: #{plan.favor.inspect}" unless plan.favor.empty?
|
|
98
|
+
parts.empty? ? "" : ", #{parts.join(', ')}"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Once-per-sweep report of verbs skipped as not generated: held with the full list,
|
|
102
|
+
# surprised if any skipped verb's construct is outside the documented boundary.
|
|
103
|
+
def structural_skip_check(differ, binary, boundary)
|
|
104
|
+
skipped = differ.structural_skips.to_a.sort
|
|
105
|
+
gaps = Hecks::Fuzzing::RustGapManifest.for_binary(binary)
|
|
106
|
+
attributed = Hecks::Fuzzing::StructuralSkips.attribute(gaps, skipped)
|
|
107
|
+
outside = Hecks::Fuzzing::StructuralSkips.outside_boundary(attributed, boundary)
|
|
108
|
+
listing = attributed.map { |e| "#{e[:verb]} [#{e[:constructs].join(',')}]" }.join("; ")
|
|
109
|
+
divergences = outside.map do |e|
|
|
110
|
+
declares = e[:constructs].empty? ? "nothing" : e[:constructs].join(",")
|
|
111
|
+
{ field: "structural_skip", verb: e[:verb], constructs: e[:constructs],
|
|
112
|
+
detail: "#{e[:verb]} was skipped as not generated (construct: #{declares}), outside " \
|
|
113
|
+
"STRUCTURAL_REFUSAL_BOUNDARY — a codegen regression or a new gap, not a documented boundary" }
|
|
114
|
+
end
|
|
115
|
+
observation = if divergences.empty?
|
|
116
|
+
"all #{skipped.size} inside the documented boundary"
|
|
117
|
+
else
|
|
118
|
+
"#{divergences.size} outside the boundary"
|
|
119
|
+
end
|
|
120
|
+
{ mode: :structural_skip_report,
|
|
121
|
+
subject: "[structural_skip_report] structurally skipped #{skipped.size} verb(s): [#{listing}]",
|
|
122
|
+
expectation: "every named query/read model skipped because manifest.json declares it not " \
|
|
123
|
+
"generated names a construct inside QualityControlDials::STRUCTURAL_REFUSAL_BOUNDARY",
|
|
124
|
+
divergences: divergences, clean: divergences.empty?, observation: observation }
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# `id:` addresses the Sweep; `sequence:` is the Check's own identity. The CLI's
|
|
128
|
+
# to.aggregate=/to.entity= split is a CLI-layer translation, not what in-process dispatch
|
|
129
|
+
# takes.
|
|
130
|
+
def mark_held!(sweep, sequence, observation)
|
|
131
|
+
@runtime.dispatch_flat("QualityControl::Sweep.Check.Held",
|
|
132
|
+
{ id: sweep.id, sequence: { value: sequence }, observation: { value: observation } })
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# `target:` is what the SuspendOnSurprise policy reads; the target is suspended inside this
|
|
136
|
+
# dispatch.
|
|
137
|
+
def mark_surprised!(sweep, sequence, observation, target_reference)
|
|
138
|
+
@runtime.dispatch_flat("QualityControl::Sweep.Check.Surprised",
|
|
139
|
+
{ id: sweep.id, sequence: { value: sequence }, observation: { value: observation },
|
|
140
|
+
target: { value: target_reference } })
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# One `PersistenceParity.diff` per seed against this sweep's disposable schema. A
|
|
144
|
+
# `PG::Error` or `WiringError` escaping it is a crash finding, never swallowed.
|
|
145
|
+
def persistence_parity_outcome(domain_path, steps, database, schema)
|
|
146
|
+
pair = @adapter_parity_pairs.fetch(:persistence_parity)
|
|
147
|
+
divergences = Hecks::Fuzzing::PersistenceParity.diff(domain_path, steps, left: pair.fetch(:left),
|
|
148
|
+
right: pair.fetch(:right),
|
|
149
|
+
database: database, schema: schema)
|
|
150
|
+
[divergences.empty? ? :clean : :divergence, divergences]
|
|
151
|
+
rescue StandardError => e
|
|
152
|
+
[:crash, [{ field: "process", detail: "#{e.class}: #{e.message}" }]]
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Memory vs SQLite through the same `PersistenceParity.diff`, run inside the primary seat's
|
|
156
|
+
# loop; an operational failure surfaces as a `process` finding.
|
|
157
|
+
def adapter_parity_sqlite_divergences(domain_path, steps)
|
|
158
|
+
pair = @adapter_parity_pairs.fetch(:adapter_parity_sqlite)
|
|
159
|
+
Hecks::Fuzzing::PersistenceParity.diff(domain_path, steps, left: pair.fetch(:left), right: pair.fetch(:right))
|
|
160
|
+
rescue StandardError => e
|
|
161
|
+
[{ field: "process", detail: "#{e.class}: #{e.message}" }]
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# `ConcurrentDispatch.check` already returns the flat divergence shape and rescues to a
|
|
165
|
+
# `process` finding; `[]` means the sequence had no command step to race.
|
|
166
|
+
def concurrency_outcome(domain_path, steps, database, race_schema, reference_schema)
|
|
167
|
+
Hecks::Fuzzing::ConcurrentDispatch.check(domain_path, steps, database: database,
|
|
168
|
+
race_schema: race_schema,
|
|
169
|
+
reference_schema: reference_schema)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Seedless, once per sweep: audits the target's own persisted lineage. Nothing to audit is a
|
|
173
|
+
# note, not a finding.
|
|
174
|
+
def era_boundary_check(domain_path)
|
|
175
|
+
result = Hecks::Fuzzing::EraBoundary.diverged_ancestor_writes(domain_path)
|
|
176
|
+
return unchecked_era_boundary(domain_path, result) unless result[:checked]
|
|
177
|
+
|
|
178
|
+
divergences =
|
|
179
|
+
if result[:diverged_total].positive?
|
|
180
|
+
[{ field: "era_boundary", breakdown: result[:breakdown],
|
|
181
|
+
detail: "#{result[:diverged_total]} post-cut write(s) across #{result[:era_count]} era(s) " \
|
|
182
|
+
"that nothing has ever merged forward — hecks merge_tail <this target's own domain " \
|
|
183
|
+
"path> is the fix; see this script's own header on `era_boundary` for the class of " \
|
|
184
|
+
"bug this is" }]
|
|
185
|
+
else
|
|
186
|
+
[]
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
{ mode: :era_boundary, subject: "[era_boundary] #{File.basename(domain_path)} — ancestor-era audit " \
|
|
190
|
+
"(#{result[:era_count]} era(s) on record)",
|
|
191
|
+
expectation: MODE_EXPECTATIONS.fetch(:era_boundary), divergences: divergences,
|
|
192
|
+
clean: divergences.empty?,
|
|
193
|
+
observation: divergences.empty? ? "no ancestor era holds an unmerged write" : "#{result[:diverged_total]} diverged" }
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Nothing to audit and could not audit stay apart: `not_applicable` logs no `Check`, while
|
|
197
|
+
# an error is a finding, so an unreachable database never counts as a held `Check` toward
|
|
198
|
+
# the streak.
|
|
199
|
+
def unchecked_era_boundary(domain_path, result)
|
|
200
|
+
if result[:kind] == :not_applicable
|
|
201
|
+
return { mode: :era_boundary, skip: true, observation: "not applicable — #{result[:reason]}" }
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
{ mode: :era_boundary, subject: "[era_boundary] #{File.basename(domain_path)} — ancestor-era audit",
|
|
205
|
+
expectation: MODE_EXPECTATIONS.fetch(:era_boundary),
|
|
206
|
+
divergences: [{ field: "era_boundary_unchecked", detail: result[:reason] }], clean: false,
|
|
207
|
+
observation: "could not audit — #{result[:reason]}" }
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Runs one seed under every active mode: `{ steps:, trace:, checks: }`, one check per mode;
|
|
211
|
+
# logging is the loop's job. A generator crash is a finding too: a step broke the
|
|
212
|
+
# interpreter before replay.
|
|
213
|
+
def run_one_seed(seat, seed)
|
|
214
|
+
shape = "#{@steps_per_sequence} steps, adversarial #{@adversarial}, role_draw #{@role_draw}, " \
|
|
215
|
+
"dry_run #{@dry_run}"
|
|
216
|
+
plan = @campaign&.plan(seed)
|
|
217
|
+
shape += ", spliced" if plan&.spliced?
|
|
218
|
+
begin
|
|
219
|
+
trace = Hecks::Fuzzing::SequenceGenerator.trace(
|
|
220
|
+
@domain_path, seed: seed, steps: @steps_per_sequence, adversarial: @adversarial,
|
|
221
|
+
role_draw: @role_draw, dry_run: @dry_run, **(plan ? plan.generator_options : {})
|
|
222
|
+
)
|
|
223
|
+
steps = trace.steps
|
|
224
|
+
rescue StandardError => e
|
|
225
|
+
return { plan: plan, steps: [], checks: [generator_crash_check(seed, shape, e)] }
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
outcomes = seed_outcomes(seat, steps)
|
|
229
|
+
{ plan: plan, steps: steps, trace: trace,
|
|
230
|
+
checks: outcomes.map { |mode_key, divergences| check_for(mode_key, @feature, seed, shape, divergences) } }
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def generator_crash_check(seed, shape, error)
|
|
234
|
+
{ mode: :generator, subject: "[generator] #{@feature} fuzz seed #{seed} (#{shape}) — sequence generation",
|
|
235
|
+
expectation: "SequenceGenerator builds every step without anything but a domain refusal escaping " \
|
|
236
|
+
"its own inline dispatch",
|
|
237
|
+
divergences: [{ field: "generator_crash",
|
|
238
|
+
detail: "#{error.class}: #{error.message}\n#{error.backtrace.first(6).join("\n")}" }],
|
|
239
|
+
clean: false, observation: "generator crashed: #{error.class}: #{error.message}" }
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def seed_outcomes(seat, steps)
|
|
243
|
+
case seat
|
|
244
|
+
when :differential then diff_ruby_vs_rust(@differ, @domain_path, steps, @binary, modes: @active_modes)
|
|
245
|
+
when :persistence_parity
|
|
246
|
+
{ persistence_parity: persistence_parity_outcome(@domain_path, steps, @parity_database,
|
|
247
|
+
@parity_schema).last }
|
|
248
|
+
when :concurrency
|
|
249
|
+
{ concurrency: concurrency_outcome(@domain_path, steps, @concurrency_database, @race_schema,
|
|
250
|
+
@reference_schema) }
|
|
251
|
+
else ruby_only_outcome(@domain_path, steps, modes: @active_modes)
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Re-runs only the comparison that surprised; `seat` matters only for `self_consistency`,
|
|
256
|
+
# which reads the Rust binary's rehydration door under a differential seat.
|
|
257
|
+
def candidate_divergences(check_mode, seat, steps)
|
|
258
|
+
case check_mode
|
|
259
|
+
when :differential
|
|
260
|
+
diff_ruby_vs_rust(@differ, @domain_path, steps, @binary, modes: [:differential])[:differential]
|
|
261
|
+
when :properties_in_differential, :ruby_only
|
|
262
|
+
ruby_only_outcome(@domain_path, steps, modes: [])[:ruby_only]
|
|
263
|
+
when :self_consistency then self_consistency_candidate(seat, steps)
|
|
264
|
+
when :adapter_parity_sqlite then adapter_parity_sqlite_divergences(@domain_path, steps)
|
|
265
|
+
when :persistence_parity
|
|
266
|
+
persistence_parity_outcome(@domain_path, steps, @parity_database, @parity_schema).last
|
|
267
|
+
end || []
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def self_consistency_candidate(seat, steps)
|
|
271
|
+
if seat == :differential
|
|
272
|
+
diff_ruby_vs_rust(@differ, @domain_path, steps, @binary, modes: [:self_consistency])[:self_consistency]
|
|
273
|
+
else
|
|
274
|
+
ruby_only_outcome(@domain_path, steps, modes: [:self_consistency])[:self_consistency]
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# Keeps a candidate only while its `Shrinker.signature` still contains the original's; a
|
|
279
|
+
# raising candidate is a different finding, so it is rejected.
|
|
280
|
+
def shrink_check(check, seat, steps)
|
|
281
|
+
original = Hecks::Fuzzing::Shrinker.signature(check[:divergences])
|
|
282
|
+
result = Hecks::Fuzzing::Shrinker.call(steps, budget: @shrink_budget) do |candidate|
|
|
283
|
+
divergences = candidate_divergences(check[:mode], seat, candidate)
|
|
284
|
+
Hecks::Fuzzing::Shrinker.reproduces?(original, divergences)
|
|
285
|
+
rescue StandardError
|
|
286
|
+
false
|
|
287
|
+
end
|
|
288
|
+
{ mode: check[:mode], steps: result.steps, attempts: result.attempts, exhausted: result.exhausted,
|
|
289
|
+
original_size: steps.size }
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# Writes the shrunk steps in `hecks fuzz`'s `{name, note, steps}` shape so `hecks run` and
|
|
293
|
+
# `hecks check_conformance` replay it unchanged.
|
|
294
|
+
def write_shrunk!(shrunk, seed)
|
|
295
|
+
dir = File.join(@root, "tmp/qa-shrunk")
|
|
296
|
+
FileUtils.mkdir_p(dir)
|
|
297
|
+
path = File.join(dir, "#{filesystem_safe_component(@sweep_reference)}-#{shrunk[:mode]}.json")
|
|
298
|
+
File.write(path, JSON.pretty_generate(name: "#{@feature}-#{shrunk[:mode]}-shrunk",
|
|
299
|
+
note: "hecks quality_control ask run #{@sweep_reference} seed #{seed}, shrunk " \
|
|
300
|
+
"from #{shrunk[:original_size]} steps",
|
|
301
|
+
steps: shrunk[:steps]))
|
|
302
|
+
path
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# Coverage-guided generation's corpus, one JSON file per (target, mode): `--all`'s waves can
|
|
306
|
+
# run a target's primary sweep and its persistence-parity/concurrency variants as separate
|
|
307
|
+
# `hecks quality_control ask run` processes within the same tick, and each explores a
|
|
308
|
+
# conceptually independent axis of the same domain, so each keeps its own file rather than
|
|
309
|
+
# sharing one that nothing here locks.
|
|
310
|
+
def coverage_corpus_path(target_reference, mode)
|
|
311
|
+
File.join(@coverage_corpus_dir, "#{filesystem_safe_component(target_reference)}-#{mode}.json")
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# Nil when nothing was ever saved for this (target, mode), or the file cannot be parsed;
|
|
315
|
+
# either way the campaign starts from an empty corpus.
|
|
316
|
+
def load_coverage_state(path)
|
|
317
|
+
JSON.parse(File.read(path))
|
|
318
|
+
rescue Errno::ENOENT, JSON::ParserError
|
|
319
|
+
nil
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def save_coverage_state!(path, campaign)
|
|
323
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
324
|
+
File.write(path, JSON.pretty_generate(campaign.to_h))
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
end
|