hecks 2.9.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 +51 -6
- data/lib/hecks/adapters/driven/heki/journal.rb +1 -1
- data/lib/hecks/adapters/driven/lambda/client.rb +1 -1
- data/lib/hecks/adapters/driven/lambda.rb +1 -1
- data/lib/hecks/adapters/driven/memory.rb +16 -3
- data/lib/hecks/adapters/driven/postgres/reconnect.rb +6 -2
- data/lib/hecks/adapters/driven/postgres/shared_connection.rb +180 -0
- data/lib/hecks/adapters/driven/postgres.rb +4 -2
- data/lib/hecks/adapters/driven/prism.rb +41 -8
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +73 -16
- data/lib/hecks/behaviors/expectations.rb +1 -1
- data/lib/hecks/behaviors.rb +1 -1
- 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/assembly/contracts.rb +2 -0
- data/lib/hecks/bluebook/attribute.rb +1 -1
- data/lib/hecks/bluebook/behaviour/aggregate.rb +30 -1
- data/lib/hecks/bluebook/behaviour/domain_port.rb +7 -0
- data/lib/hecks/bluebook/chapter.rb +6 -2
- data/lib/hecks/bluebook/domain_port.rb +36 -5
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +10 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +8 -0
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -1
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +31 -3
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +122 -3
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +1 -1
- data/lib/hecks/bluebook/dsl/query_builder.rb +27 -1
- data/lib/hecks/bluebook/dsl/rule_reference.rb +1 -1
- data/lib/hecks/bluebook/dsl/world_builder.rb +29 -2
- data/lib/hecks/bluebook/expression/canonical_form.rb +1 -1
- data/lib/hecks/bluebook/expression/resolver.rb +3 -2
- data/lib/hecks/bluebook/hexagon.rb +13 -5
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +1 -0
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +4 -1
- data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +246 -0
- data/lib/hecks/bluebook/meta_validator.rb +9 -4
- data/lib/hecks/bluebook/model_check.rb +23 -0
- data/lib/hecks/bluebook/pattern_subset.rb +38 -0
- data/lib/hecks/bluebook/query.rb +20 -5
- data/lib/hecks/bluebook/smoke_test.rb +1 -1
- 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 +2 -2
- 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 +1 -1
- data/lib/hecks/cli/mcp.rb +3 -3
- data/lib/hecks/cli/model_check.rb +7 -4
- data/lib/hecks/cli/pattern_cases.rb +47 -0
- data/lib/hecks/cli/present.rb +39 -0
- data/lib/hecks/cli/project_cli.rb +224 -32
- data/lib/hecks/cli/project_diagrams.rb +4 -3
- 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 +3 -3
- 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 +5 -3
- data/lib/hecks/cli/spec_example.rb +27 -0
- data/lib/hecks/cli/statements.rb +32 -0
- data/lib/hecks/cli/stores.rb +4 -5
- data/lib/hecks/cli/stress_concurrency_specs.rb +177 -0
- data/lib/hecks/cli.rb +201 -52
- data/lib/hecks/codemod.rb +324 -0
- data/lib/hecks/corpus.rb +423 -0
- data/lib/hecks/deploy/bluebook/deploy.bluebook +591 -23
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +41 -4
- 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 +1 -1
- data/lib/hecks/{facade → doors}/handle.rb +2 -2
- data/lib/hecks/{facade → doors}/json_door.rb +4 -4
- data/lib/hecks/doors/launcher_options.rb +129 -0
- data/lib/hecks/{cli → doors}/mcp_door.rb +7 -7
- data/lib/hecks/doors/mcp_door_scope.rb +97 -0
- data/lib/hecks/{facade/surface → doors/ruby_door}/aggregate_door.rb +8 -3
- data/lib/hecks/{facade/surface → doors/ruby_door}/chapter.rb +3 -3
- data/lib/hecks/{facade/surface.rb → doors/ruby_door.rb} +36 -5
- data/lib/hecks/doors.rb +61 -0
- data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +1 -1
- data/lib/hecks/embryonaut_bluebook.rb +1 -1
- data/lib/hecks/engine_agreement.rb +156 -0
- data/lib/hecks/facade.rb +24 -37
- data/lib/hecks/forms/app.rb +6 -6
- data/lib/hecks/forms/banking_presentation.rb +50 -0
- data/lib/hecks/forms/field_shape.rb +2 -1
- data/lib/hecks/forms/port_argument.rb +1 -1
- 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/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/kernel_capabilities.rb +194 -0
- data/lib/hecks/language/bluebook/bluebook.bluebook +11 -0
- data/lib/hecks/language/bluebook/query.bluebook +7 -0
- data/lib/hecks/language/bluebook/vocabulary.bluebook +4 -4
- data/lib/hecks/language/hecksagon/domain_port.bluebook +10 -0
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +20 -0
- data/lib/hecks/mcp_stdio_guard.rb +2 -2
- data/lib/hecks/persistence_legacy_fixture/regenerate.rb +161 -0
- data/lib/hecks/persistence_legacy_fixture.rb +113 -0
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +8 -5
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +4 -1
- data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +15 -2
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +32 -15
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +6 -3
- 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 +1 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +43 -24
- data/lib/hecks/ports/persistence/plugins/era/translation.rb +1 -0
- data/lib/hecks/ports/persistence/state_codec.rb +24 -5
- data/lib/hecks/projection_files.rb +285 -0
- data/lib/hecks/projections/bootstrap_table.rb +1 -1
- data/lib/hecks/projections/deploy/fargate.rb +5 -5
- data/lib/hecks/projections/deploy/lambda.rb +43 -32
- data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
- data/lib/hecks/projections/deploy/preview/template.rb +1 -1
- 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/smoke-after-deploy.sh.tmpl +1 -1
- data/lib/hecks/projections/deploy/shared.rb +1 -1
- data/lib/hecks/projections/diagrams.rb +3 -3
- data/lib/hecks/projections/model/deviations.rb +1 -1
- data/lib/hecks/projections/parser_table.rb +4 -4
- data/lib/hecks/projections/reference.rb +2 -3
- data/lib/hecks/projections/rust_vocabulary.rb +1 -1
- data/lib/hecks/projector/cli_projector.rb +106 -37
- data/lib/hecks/projector.rb +1 -1
- 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/release/runner/ci_publisher.rb +2 -2
- data/lib/hecks/release/runner/clean_tree.rb +42 -0
- data/lib/hecks/release/runner/console.rb +6 -0
- data/lib/hecks/release/runner/gem_publisher.rb +14 -7
- data/lib/hecks/release/runner/npm_publisher.rb +6 -36
- data/lib/hecks/release/runner/preflight.rb +26 -2
- data/lib/hecks/release/runner/published.rb +8 -26
- data/lib/hecks/release/runner/tagger.rb +30 -15
- data/lib/hecks/release/runner.rb +57 -6
- data/lib/hecks/router/namespace_installer.rb +3 -3
- data/lib/hecks/runtime/adapter_lookup.rb +124 -0
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +4 -0
- data/lib/hecks/runtime/dependency_planning.rb +20 -2
- data/lib/hecks/runtime/dispatcher.rb +53 -3
- data/lib/hecks/runtime/interpreting.rb +3 -0
- data/lib/hecks/runtime/loader.rb +65 -12
- data/lib/hecks/runtime/outbox.rb +1 -1
- data/lib/hecks/runtime/policy_interpreter.rb +5 -2
- data/lib/hecks/runtime/port_operation_interpreter.rb +2 -10
- data/lib/hecks/runtime/query_interpreter.rb +77 -0
- data/lib/hecks/runtime/reaction_outcome.rb +89 -0
- data/lib/hecks/runtime/refusal_wording.rb +23 -1
- data/lib/hecks/runtime/registry/verification.rb +163 -4
- data/lib/hecks/runtime/registry.rb +57 -3
- data/lib/hecks/runtime/remote_dispatcher.rb +31 -2
- data/lib/hecks/runtime/value/coercion.rb +23 -135
- data/lib/hecks/runtime/value/field_checks.rb +172 -0
- data/lib/hecks/runtime/value.rb +4 -1
- data/lib/hecks/runtime.rb +20 -6
- 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 +16 -16
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +2 -2
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +1 -1
- 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/version.rb +1 -1
- data/lib/hecks.rb +32 -15
- 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 +477 -12
- data/lib/hecks/facade/cli_door.rb +0 -96
- data/lib/hecks/facade/cli_runner.rb +0 -127
- data/lib/hecks/mcp_door_scope.rb +0 -95
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Fuzzing
|
|
3
|
+
module Properties
|
|
4
|
+
# Properties that check a query's answer against an independent recomputation.
|
|
5
|
+
module Querying
|
|
6
|
+
# Differential oracle: each replayed ask is answered natively and by the reference
|
|
7
|
+
# interpreter, and the two must agree.
|
|
8
|
+
#
|
|
9
|
+
# Refusal is read by key presence (`:error` / `:reference_error`), not truthiness;
|
|
10
|
+
# one side refusing while the other answers is a divergence. Read-model asks
|
|
11
|
+
# (no "::" in the verb) have no reference twin and are skipped.
|
|
12
|
+
#
|
|
13
|
+
# @param history [Hash] a replayed history as returned by `Replay.call`
|
|
14
|
+
# @return [true, String] true, or a message naming each disagreeing query
|
|
15
|
+
def query_answers_match_reference(history)
|
|
16
|
+
offenders = history.fetch(:queries).filter_map do |asked|
|
|
17
|
+
next unless asked[:query].is_a?(String) && asked[:query].include?("::")
|
|
18
|
+
|
|
19
|
+
native_refused = asked.key?(:error)
|
|
20
|
+
reference_refused = asked.key?(:reference_error)
|
|
21
|
+
|
|
22
|
+
if native_refused != reference_refused
|
|
23
|
+
next "#{asked[:query]} #{asked[:args].inspect} — native " \
|
|
24
|
+
"#{native_refused ? "refused (#{asked[:error]})" : 'answered'}, " \
|
|
25
|
+
"but the reference interpreter #{reference_refused ? "refused (#{asked[:reference_error]})" : 'answered'} — " \
|
|
26
|
+
"a refusal-shaped divergence, not just a differing row set"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
next if native_refused
|
|
30
|
+
next if asked[:rows] == asked[:reference_rows]
|
|
31
|
+
|
|
32
|
+
"#{asked[:query]} #{asked[:args].inspect} answered #{asked[:rows].inspect} " \
|
|
33
|
+
"natively but #{asked[:reference_rows].inspect} through the reference interpreter"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
offenders.empty? || offenders.join("; ")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Recomputes order/offset/limit from `history[:instances]` and compares it with the
|
|
40
|
+
# real answer of every paged query (`order_by` with `offset` or `limit`).
|
|
41
|
+
#
|
|
42
|
+
# Ordering reuses `Ports::Query::Ordering.apply` so "in order" cannot drift from the
|
|
43
|
+
# interpreter; only the offset-then-limit slice is restated. Comparing the interpreter's
|
|
44
|
+
# native and reference paths instead could share one bug.
|
|
45
|
+
#
|
|
46
|
+
# Kept as one method: the steps run once, in order, and share every intermediate.
|
|
47
|
+
# rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
48
|
+
#
|
|
49
|
+
# @param history [Hash] a replayed history as returned by `Replay.call`
|
|
50
|
+
# @return [true, String] true, or a message naming each mismatching query
|
|
51
|
+
def paging_offset_partitions_correctly(history)
|
|
52
|
+
bluebooks = history.fetch(:bluebooks)
|
|
53
|
+
|
|
54
|
+
offenders = history.fetch(:queries).filter_map do |asked|
|
|
55
|
+
next if asked[:error] || !asked[:query].is_a?(String) || !asked[:query].include?("::")
|
|
56
|
+
|
|
57
|
+
declared = query_for_verb(bluebooks, asked[:query])
|
|
58
|
+
next unless declared&.order_by && (declared.offset || declared.limit)
|
|
59
|
+
|
|
60
|
+
domain, aggregate_name, = Naming.split_verb(asked[:query])
|
|
61
|
+
aggregate = bluebooks[domain]&.aggregate(aggregate_name)
|
|
62
|
+
args = normalize_query_args(aggregate, declared, asked[:args] || {})
|
|
63
|
+
rows = query_eligible_rows(asked.fetch(:instances_at), domain, aggregate_name, declared.wheres, args,
|
|
64
|
+
bluebooks: bluebooks)
|
|
65
|
+
ordered = Ports::Query::Ordering.apply(
|
|
66
|
+
rows, declared.order_by, declared.null_semantics, identity: ->(row) { row[:id].to_s }
|
|
67
|
+
) { |row| Ports::Query::InMemory.comparable(QuerySpecification::FieldPath.dig(row, declared.order_by.field)) }
|
|
68
|
+
|
|
69
|
+
skipped = declared.offset ? ordered.drop(resolve_paging_value(declared.offset.value, args).to_i) : ordered
|
|
70
|
+
expected = declared.limit ? skipped.first(resolve_paging_value(declared.limit.value, args).to_i) : skipped
|
|
71
|
+
actual = asked[:rows]
|
|
72
|
+
next if actual == expected
|
|
73
|
+
|
|
74
|
+
"#{asked[:query]} #{args.inspect} answered #{actual.inspect}, but independently recomputing " \
|
|
75
|
+
"order/offset/limit from #{rows.length} eligible row(s) gives #{expected.inspect}"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
offenders.empty? || offenders.join("; ")
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Real dispatch normalizes a query's own declared arguments
|
|
82
|
+
# (`QueryInterpreter#normalize_args`) before ever evaluating a where clause,
|
|
83
|
+
# filling a composite argument's own declared defaults along the way
|
|
84
|
+
# (`Value.for_attribute` -> `Value.build`). The language's own ambiguous-
|
|
85
|
+
# comparison guard (`refuse_ambiguous_comparison!`) rules out comparing most
|
|
86
|
+
# multi-field value objects whole, but a single-attribute one (still eligible,
|
|
87
|
+
# since it is never ambiguous) can still arrive with that one field omitted —
|
|
88
|
+
# recomputing eligibility against the raw fuzzed args instead of this normalized
|
|
89
|
+
# copy would then disagree with real dispatch — the same false-divergence shape
|
|
90
|
+
# `build_guard_check` guards against for the guard check. `Runtime::QueryInterpreter.
|
|
91
|
+
# new(nil)` is safe: `normalize_args` never reads the registry it would otherwise need.
|
|
92
|
+
def normalize_query_args(aggregate, declared, args)
|
|
93
|
+
return args unless aggregate
|
|
94
|
+
|
|
95
|
+
Runtime::QueryInterpreter.new(nil).send(:normalize_args, aggregate, declared, args)
|
|
96
|
+
rescue StandardError
|
|
97
|
+
args
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Resolves the declared Query for a replayed verb.
|
|
101
|
+
#
|
|
102
|
+
# Entity-level queries (dotted query path) return nil: they are not one aggregate's
|
|
103
|
+
# own rows, which #query_eligible_rows assumes.
|
|
104
|
+
#
|
|
105
|
+
# @param bluebooks [Hash{String => Bluebook::Chapter}] every loaded domain
|
|
106
|
+
# @param verb [String] `"Domain::Aggregate.Query"`
|
|
107
|
+
# @return [Bluebook::Query, nil]
|
|
108
|
+
def query_for_verb(bluebooks, verb)
|
|
109
|
+
domain, aggregate_name, query_path = Naming.split_verb(verb)
|
|
110
|
+
return nil unless query_path && !query_path.include?(".")
|
|
111
|
+
|
|
112
|
+
bluebook = bluebooks[domain]
|
|
113
|
+
aggregate = bluebook&.aggregate(aggregate_name)
|
|
114
|
+
aggregate&.query(query_path)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Recomputes which of an aggregate's own stored rows a query's `wheres` admit.
|
|
118
|
+
#
|
|
119
|
+
# Rows get `id:` merged in because Ordering.apply's `identity:` and the real answer
|
|
120
|
+
# both use it. `bluebooks:` is needed only to resolve a `/` hop clause, whose head
|
|
121
|
+
# names a declared reference; without it the clause would be dug as a local path.
|
|
122
|
+
#
|
|
123
|
+
# @param instances [Hash{String => Hash}] `history[:instances]` or an `:instances_at`
|
|
124
|
+
# snapshot
|
|
125
|
+
# @param wheres [Array<QuerySpecification::Common::WhereClause>] clauses every row
|
|
126
|
+
# must satisfy
|
|
127
|
+
# @param args [Hash] the query's call args, for clauses whose value is a Symbol
|
|
128
|
+
# @return [Array<Hash>] each admitted row's state, merged with its `id:`
|
|
129
|
+
def query_eligible_rows(instances, domain, aggregate_name, wheres, args, bluebooks: {})
|
|
130
|
+
aggregate = bluebooks[domain]&.aggregate(aggregate_name)
|
|
131
|
+
prefix = "#{domain}::#{aggregate_name}#"
|
|
132
|
+
instances.filter_map do |key, state|
|
|
133
|
+
next unless key.start_with?(prefix)
|
|
134
|
+
|
|
135
|
+
row = state.merge(id: key.split("#").last)
|
|
136
|
+
next unless wheres.all? do |clause|
|
|
137
|
+
resolved = resolve_hop_clause(instances, domain, aggregate, clause, args, bluebooks)
|
|
138
|
+
held = Ports::Query::InMemory.comparable(QuerySpecification::FieldPath.dig(row, resolved.field))
|
|
139
|
+
Ports::Query::InMemory.holds?(resolved, held, args)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
row
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Resolves one hop of a `/`-chained clause into a local `in` clause over the target's ids.
|
|
147
|
+
#
|
|
148
|
+
# Restates `Runtime::ReferenceHop#fold` over the replay snapshot, so the property never
|
|
149
|
+
# checks the runtime against its own code path. A clause with no resolvable hop head
|
|
150
|
+
# is returned unchanged.
|
|
151
|
+
#
|
|
152
|
+
# @param aggregate [Bluebook::Aggregate, nil] nil skips hop resolution
|
|
153
|
+
# @return [QuerySpecification::Common::WhereClause] `clause`, or the `in` clause
|
|
154
|
+
def resolve_hop_clause(instances, domain, aggregate, clause, args, bluebooks)
|
|
155
|
+
return clause unless aggregate && QuerySpecification::HopPath.hop_head?(clause.field, aggregate.attributes)
|
|
156
|
+
|
|
157
|
+
hop, rest = QuerySpecification::HopPath.next_hop(clause.field, aggregate.attributes)
|
|
158
|
+
target = hop.target
|
|
159
|
+
return clause unless target
|
|
160
|
+
|
|
161
|
+
inner = QuerySpecification::Common::WhereClause.new(field: rest, op: clause.op, value: clause.value)
|
|
162
|
+
ids = query_eligible_rows(instances, domain, target.hecks_name, [inner], args, bluebooks: bluebooks)
|
|
163
|
+
.map { |row| row[:id].to_s }.uniq
|
|
164
|
+
|
|
165
|
+
QuerySpecification::Common::WhereClause.new(field: hop.attribute.name, op: "in", value: ids)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Resolves a declared limit/offset: a literal, or a Symbol naming a call arg.
|
|
169
|
+
def resolve_paging_value(value, args)
|
|
170
|
+
value.is_a?(Symbol) ? args[value] : value
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require_relative "../bluebook/model_check"
|
|
3
|
+
require_relative "../bluebook/meta_validator"
|
|
4
|
+
require_relative "../ports/query/in_memory"
|
|
5
|
+
require_relative "../query_specification/field_path"
|
|
6
|
+
require_relative "../runtime/value"
|
|
7
|
+
require_relative "properties/lifecycle_and_replay"
|
|
8
|
+
require_relative "properties/querying"
|
|
9
|
+
require_relative "properties/guards"
|
|
10
|
+
require_relative "properties/dispatch_and_mutations"
|
|
11
|
+
require_relative "properties/invariants_and_aggregation"
|
|
12
|
+
require_relative "properties/corrections"
|
|
13
|
+
require_relative "properties/outbox"
|
|
14
|
+
|
|
15
|
+
module Hecks
|
|
16
|
+
module Fuzzing
|
|
17
|
+
# Declared properties, checked over a replayed history: each maps a name
|
|
18
|
+
# to `->(history) { true, or a message string naming what broke }`.
|
|
19
|
+
module Properties
|
|
20
|
+
# Grouped by responsibility across properties/*.rb; `extend`ed so each
|
|
21
|
+
# stays reachable as `Properties.foo(history)`, matching `module_function` below.
|
|
22
|
+
extend LifecycleAndReplay
|
|
23
|
+
extend Querying
|
|
24
|
+
extend Guards
|
|
25
|
+
extend DispatchAndMutations
|
|
26
|
+
extend DryRuns
|
|
27
|
+
extend InvariantsAndAggregation
|
|
28
|
+
extend Corrections
|
|
29
|
+
extend Outbox
|
|
30
|
+
|
|
31
|
+
module_function
|
|
32
|
+
|
|
33
|
+
# Which language feature each property is answerable for — exhaustive of
|
|
34
|
+
# what would go unchecked without it, not of everything its body touches.
|
|
35
|
+
FEATURE_COVERAGE = {
|
|
36
|
+
lifecycle_values_are_declared: %w[Aggregate#state_field Aggregate#state_start Aggregate#transitions
|
|
37
|
+
Entity#state_field Entity#state_start Entity#transitions],
|
|
38
|
+
saga_advances_follow_declared_handlers: %w[Handler#from_state Handler#to_state Handler#event_type],
|
|
39
|
+
query_answers_match_reference: %w[Query#wheres Query#order_field Query#order_way Query#limit],
|
|
40
|
+
paging_offset_partitions_correctly: %w[Query#options],
|
|
41
|
+
authorize_scopes_or_refuses: %w[Query#options],
|
|
42
|
+
guard_refusals_are_declared: %w[Command#givens Command#ensures],
|
|
43
|
+
lifecycle_guard_and_given_violations_are_refused: %w[Command#from Aggregate#preconditions Entity#preconditions],
|
|
44
|
+
# Dispatch#command_name/with_spec aren't claimable feature names — the
|
|
45
|
+
# coverage walk only reaches one level of entity nesting and Dispatch
|
|
46
|
+
# sits two deep — but this property still checks the real behavior
|
|
47
|
+
# (dispatch_args) those names would have covered.
|
|
48
|
+
dispatch_binding_fidelity: %w[Handler#dispatches Policy#with_spec],
|
|
49
|
+
mutations_match_recompute: %w[Command#mutations],
|
|
50
|
+
sagas_rehydrate_cleanly: %w[ProcessManager#states ProcessManager#correlates_by
|
|
51
|
+
ProcessManager#starts_on ProcessManager#ends_on],
|
|
52
|
+
fanout_dispatches_once_per_matching_row: %w[Policy#for_each Policy#where],
|
|
53
|
+
aggregation_matches_recompute: %w[ReadModel#count ReadModel#median_field ReadModel#sum_field
|
|
54
|
+
ReadModel#avg_field ReadModel#min_field ReadModel#max_field
|
|
55
|
+
ReadModel#percentile_field ReadModel#percentile_at
|
|
56
|
+
ReadModel#any_field ReadModel#all_field],
|
|
57
|
+
stored_records_satisfy_declared_invariants: %w[Aggregate#invariants Entity#invariants],
|
|
58
|
+
group_by_matches_recompute: %w[ReadModel#group_by],
|
|
59
|
+
# A runtime door, not a grammar construct — `Dispatcher#dry_run?` is not
|
|
60
|
+
# a word a bluebook declares. Listed empty rather than omitted, so every
|
|
61
|
+
# property still names what it is answerable for.
|
|
62
|
+
dry_runs_leave_no_trace: [],
|
|
63
|
+
# Another runtime door, not a grammar construct — same reasoning as
|
|
64
|
+
# dry_runs_leave_no_trace above.
|
|
65
|
+
outbox_rows_match_reactions: [],
|
|
66
|
+
# Reads command.mutations for :corrects ops — the same list
|
|
67
|
+
# mutations_match_recompute reads, for a different question.
|
|
68
|
+
corrections_reference_an_emitted_event: %w[Command#mutations],
|
|
69
|
+
# No feature string exists for what this reads: an argument's own
|
|
70
|
+
# `relationship` (Argument is a value object, outside the meta-domain
|
|
71
|
+
# walk). Its declaration side, `Query#options`, is already claimed by
|
|
72
|
+
# authorize_scopes_or_refuses.
|
|
73
|
+
commands_respect_tenant_scope: []
|
|
74
|
+
}.freeze
|
|
75
|
+
|
|
76
|
+
# Features a replay could never observe violated, because construction
|
|
77
|
+
# makes the violation impossible to produce — not untested, but
|
|
78
|
+
# unfalsifiable. Each entry names the runtime path that guarantees it;
|
|
79
|
+
# add one only after reading and confirming that path (mirrors the
|
|
80
|
+
# discipline spec/fuzzing/meta_domain_coverage_spec.rb's KNOWN_GAPS demands
|
|
81
|
+
# in the other direction).
|
|
82
|
+
GUARANTEED_BY_CONSTRUCTION = {
|
|
83
|
+
"Aggregate#attributes" => "every field's pattern/closed-set/type passes through Value.build's one coercion " \
|
|
84
|
+
"door (value/coercion.rb#check_patterns, value/admission.rb) before it can exist " \
|
|
85
|
+
"— a stored value that violated its own declared shape was never producible to " \
|
|
86
|
+
"begin with",
|
|
87
|
+
"Aggregate#value_objects" => "the shape being coerced above — same door, same guarantee",
|
|
88
|
+
# S17, ADR 0026 — saga_advances_follow_declared_handlers already walks
|
|
89
|
+
# this list to find event_type/from_state/to_state; same door, same guarantee.
|
|
90
|
+
"ProcessManager#handlers" => "saga_advances_follow_declared_handlers already walks this list to find " \
|
|
91
|
+
"event_type/from_state/to_state — same door, same guarantee",
|
|
92
|
+
"Aggregate#identified_by" => "CommandInterpreter's data-driven dispatch order refuses AlreadyExists " \
|
|
93
|
+
"(command_interpreter.rb, command.creates?) for every creating command uniformly, " \
|
|
94
|
+
"before a duplicate id can ever be stored — collision is refused at the door, not " \
|
|
95
|
+
"produced and later caught",
|
|
96
|
+
"Entity#identified_by" => "EntityElement.check_entity_collision (runtime/entity_element.rb, moved there " \
|
|
97
|
+
"BUG#145 so both call sites share it) checks Array(current) against every part " \
|
|
98
|
+
"of the entity's own identity before an append can land — MutationApplier#" \
|
|
99
|
+
"entity_element's aggregate-owned call (Workspace.boards, on both branches " \
|
|
100
|
+
"identity arrives by: caller-supplied, or composite) AND EntityElement#" \
|
|
101
|
+
"appended_to_element's entity-owned, nested-one-hop-further call (Board.cards — " \
|
|
102
|
+
"unconditional, no auto-mint branch exists at that depth) — the same " \
|
|
103
|
+
"AlreadyExists refusal Aggregate#identified_by gets above, one or two levels " \
|
|
104
|
+
"down. Auto-minted (aggregate-owned) entities never reach the check " \
|
|
105
|
+
"(current.size + 1 can't repeat unless something remove:s from the list between " \
|
|
106
|
+
"mints, which no real domain does today — see the comment on #entity_element " \
|
|
107
|
+
"itself)",
|
|
108
|
+
"Command#attributes" => "command arguments are coerced through the SAME Value.build door as any other " \
|
|
109
|
+
"attribute — an accepted dispatch's own args already passed pattern/admits/invariant checks",
|
|
110
|
+
"Command#emits" => "CommandRules::Emission#emit iterates command.emits ITSELF to construct every " \
|
|
111
|
+
"announced Event (command_rules/emission.rb) — there is no other path to emit, so " \
|
|
112
|
+
"a command can never announce a name its own declaration doesn't list",
|
|
113
|
+
"Query#attributes" => "query arguments are coerced through the same Value.build door — same guarantee as " \
|
|
114
|
+
"Command#attributes",
|
|
115
|
+
"Query#returns" => "every row a port answers is built as the returned value object by Value.build " \
|
|
116
|
+
"(QueryInterpreter#shaped) before it enters the domain — an answer of any other " \
|
|
117
|
+
"shape is refused, so none can be accepted",
|
|
118
|
+
"Entity#attributes" => "same coercion door, one level in — an entity's own attributes are Value-typed exactly " \
|
|
119
|
+
"the way an aggregate's are",
|
|
120
|
+
"ValueObject#attributes" => "the shape Value.build enforces IS this declaration — the guarantee and the " \
|
|
121
|
+
"feature are the same fact seen from two sides",
|
|
122
|
+
"ValueObject#invariants" => "run inside the SAME coercion call (coercion.rb, before construction returns) " \
|
|
123
|
+
"that pattern-checks a VO's fields — a VO whose invariant did not hold could not " \
|
|
124
|
+
"finish being built",
|
|
125
|
+
"ValueObject#rows" => "closed-set membership is checked in value/admission.rb, the second half of the same " \
|
|
126
|
+
"one construction door",
|
|
127
|
+
# S17, ADR 0026 — Member is a genuine entity, so this reads Member#pairs,
|
|
128
|
+
# not Member#shape. ValueObject#members is the same fact ValueObject#rows
|
|
129
|
+
# counts, seen from the other side.
|
|
130
|
+
"ValueObject#members" => "the members list IS what ValueObject#rows counts — same door, same guarantee",
|
|
131
|
+
"Member#pairs" => "one level into ValueObject#rows — same door"
|
|
132
|
+
}.freeze
|
|
133
|
+
|
|
134
|
+
# Runs the standard property battery over one replayed history — all but
|
|
135
|
+
# determinism, which replays twice itself and so is checked separately.
|
|
136
|
+
#
|
|
137
|
+
# @param history [Hash] a replayed history, as returned by `Fuzzing::Replay.call`
|
|
138
|
+
# @return [Hash{Symbol => true, String}] each property name mapped to `true`
|
|
139
|
+
# (passed) or a message string naming what broke
|
|
140
|
+
def check(history)
|
|
141
|
+
{ lifecycle_values_are_declared: lifecycle_values_are_declared(history),
|
|
142
|
+
saga_advances_follow_declared_handlers: saga_advances_follow_declared_handlers(history),
|
|
143
|
+
query_answers_match_reference: query_answers_match_reference(history),
|
|
144
|
+
guard_refusals_are_declared: guard_refusals_are_declared(history),
|
|
145
|
+
sagas_rehydrate_cleanly: sagas_rehydrate_cleanly(history),
|
|
146
|
+
fanout_dispatches_once_per_matching_row: fanout_dispatches_once_per_matching_row(history),
|
|
147
|
+
aggregation_matches_recompute: aggregation_matches_recompute(history),
|
|
148
|
+
stored_records_satisfy_declared_invariants: stored_records_satisfy_declared_invariants(history),
|
|
149
|
+
group_by_matches_recompute: group_by_matches_recompute(history),
|
|
150
|
+
paging_offset_partitions_correctly: paging_offset_partitions_correctly(history),
|
|
151
|
+
lifecycle_guard_and_given_violations_are_refused: lifecycle_guard_and_given_violations_are_refused(history),
|
|
152
|
+
authorize_scopes_or_refuses: authorize_scopes_or_refuses(history),
|
|
153
|
+
commands_respect_tenant_scope: commands_respect_tenant_scope(history),
|
|
154
|
+
dispatch_binding_fidelity: dispatch_binding_fidelity(history),
|
|
155
|
+
mutations_match_recompute: mutations_match_recompute(history),
|
|
156
|
+
dry_runs_leave_no_trace: dry_runs_leave_no_trace(history),
|
|
157
|
+
corrections_reference_an_emitted_event: corrections_reference_an_emitted_event(history),
|
|
158
|
+
outbox_rows_match_reactions: outbox_rows_match_reactions(history) }
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
require "yaml"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Fuzzing
|
|
5
|
+
# Loads and validates `qa/settings.yml`, the hecks_qa dials.
|
|
6
|
+
#
|
|
7
|
+
# Fails loud: a missing key, unknown key or wrong-typed value raises at load time.
|
|
8
|
+
# The meaning of each dial is documented on `QualityControlDials` in the bluebook.
|
|
9
|
+
class QaSettings
|
|
10
|
+
# Accessor name to the class (or classes) a valid value must be an instance of.
|
|
11
|
+
# Booleans list both TrueClass and FalseClass; Numeric admits `0` as well as `0.0`.
|
|
12
|
+
EXPECTED_TYPES = {
|
|
13
|
+
cadence_seconds: Integer,
|
|
14
|
+
pr_cap_per_day: Integer,
|
|
15
|
+
widening_tiers: Array,
|
|
16
|
+
sweep_max_parallel: Integer,
|
|
17
|
+
liveness_fallback_seconds: Integer,
|
|
18
|
+
draft_only: [TrueClass, FalseClass],
|
|
19
|
+
auto_merge: [TrueClass, FalseClass],
|
|
20
|
+
branch_prefix: String,
|
|
21
|
+
adversarial_fraction: Numeric,
|
|
22
|
+
guided_generation: [TrueClass, FalseClass],
|
|
23
|
+
corpus_splice_probability: Numeric,
|
|
24
|
+
favor_rare_verbs: Integer,
|
|
25
|
+
self_consistency_checks: [TrueClass, FalseClass],
|
|
26
|
+
shrink_budget: Integer,
|
|
27
|
+
yield_weight_seconds: Integer,
|
|
28
|
+
yield_decay_percent: Integer,
|
|
29
|
+
rotation_stale_floor_seconds: Integer,
|
|
30
|
+
persistence_parity_seed_cap: Integer,
|
|
31
|
+
concurrency_seed_cap: Integer,
|
|
32
|
+
adapter_parity_pairs: Hash,
|
|
33
|
+
modes: Hash,
|
|
34
|
+
role_draw_probability: Numeric,
|
|
35
|
+
dry_run_fraction: Numeric,
|
|
36
|
+
generated_domains_per_tick: Integer,
|
|
37
|
+
generated_domains_rust: [TrueClass, FalseClass],
|
|
38
|
+
generated_domain_seeds: Integer,
|
|
39
|
+
structural_refusal_boundary: Array
|
|
40
|
+
}.freeze
|
|
41
|
+
|
|
42
|
+
attr_reader(*EXPECTED_TYPES.keys)
|
|
43
|
+
|
|
44
|
+
# The one real file, resolved from this file's `__dir__` rather than the bluebook's: the
|
|
45
|
+
# chapter ships in `lib/hecks/quality_control/`, the settings stay in `qa/`, and IsolatedBoot
|
|
46
|
+
# copies only `qa/bluebook`, so a bluebook-relative path would miss it.
|
|
47
|
+
DEFAULT_PATH = File.expand_path("../../../qa/settings.yml", __dir__)
|
|
48
|
+
|
|
49
|
+
class << self
|
|
50
|
+
# Loads and validates `qa/settings.yml` (or `path`), returning a frozen instance.
|
|
51
|
+
#
|
|
52
|
+
# @param path [String] path to the YAML settings file; defaults to `DEFAULT_PATH`
|
|
53
|
+
# @return [Hecks::Fuzzing::QaSettings] the validated, frozen settings
|
|
54
|
+
# @raise [ArgumentError] if `path` does not exist, is not valid YAML, is not a
|
|
55
|
+
# YAML mapping at the top level, is missing a required key, declares an
|
|
56
|
+
# unknown key, or gives a value the wrong type for its dial
|
|
57
|
+
def load(path = DEFAULT_PATH)
|
|
58
|
+
raise ArgumentError, "qa settings file not found: #{path}" unless File.file?(path)
|
|
59
|
+
|
|
60
|
+
raw = begin
|
|
61
|
+
YAML.safe_load_file(path, symbolize_names: true)
|
|
62
|
+
rescue Psych::SyntaxError => e
|
|
63
|
+
raise ArgumentError, "#{path} is not valid YAML: #{e.message}"
|
|
64
|
+
end
|
|
65
|
+
raise ArgumentError, "#{path} must be a YAML mapping at the top level, got #{raw.class}" unless raw.is_a?(Hash)
|
|
66
|
+
|
|
67
|
+
new(raw, path)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# @param raw [Hash] parsed YAML settings keyed by symbol, one entry per dial in
|
|
72
|
+
# `EXPECTED_TYPES`
|
|
73
|
+
# @param path [String] path to the settings file, used only in error messages
|
|
74
|
+
# @raise [ArgumentError] if `raw` is missing a required key, declares an unknown
|
|
75
|
+
# key, or gives a value the wrong type for its dial
|
|
76
|
+
def initialize(raw, path)
|
|
77
|
+
missing = EXPECTED_TYPES.keys - raw.keys
|
|
78
|
+
raise ArgumentError, "#{path} is missing #{missing.sort.join(', ')}" if missing.any?
|
|
79
|
+
|
|
80
|
+
extra = raw.keys - EXPECTED_TYPES.keys
|
|
81
|
+
if extra.any?
|
|
82
|
+
raise ArgumentError,
|
|
83
|
+
"#{path} declares unknown key(s) #{extra.sort.join(', ')} — " \
|
|
84
|
+
"Hecks::Fuzzing::QaSettings::EXPECTED_TYPES doesn't recognise them"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
EXPECTED_TYPES.each do |key, expected|
|
|
88
|
+
value = raw.fetch(key)
|
|
89
|
+
expected_classes = Array(expected)
|
|
90
|
+
unless expected_classes.any? { |klass| value.is_a?(klass) }
|
|
91
|
+
raise ArgumentError,
|
|
92
|
+
"#{path}: #{key} must be a #{expected_classes.map(&:name).join(' or ')}, " \
|
|
93
|
+
"got #{value.class} (#{value.inspect})"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
instance_variable_set(:"@#{key}", value)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
symbolize_adapter_parity_pairs!(path)
|
|
100
|
+
freeze_values!
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
private
|
|
104
|
+
|
|
105
|
+
# YAML cannot spell a Symbol value, and IsolatedBoot case-matches adapters by symbol.
|
|
106
|
+
def symbolize_adapter_parity_pairs!(path)
|
|
107
|
+
@adapter_parity_pairs = @adapter_parity_pairs.to_h do |mode, pair|
|
|
108
|
+
unless pair.is_a?(Hash) && pair.key?(:left) && pair.key?(:right)
|
|
109
|
+
raise ArgumentError,
|
|
110
|
+
"#{path}: adapter_parity_pairs.#{mode} must have both left and right, got #{pair.inspect}"
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
[mode, { left: pair[:left].to_sym, right: pair[:right].to_sym }]
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def freeze_values!
|
|
118
|
+
EXPECTED_TYPES.each_key { |key| instance_variable_get(:"@#{key}").freeze }
|
|
119
|
+
freeze
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|