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,159 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pg"
|
|
4
|
+
|
|
5
|
+
module Hecks
|
|
6
|
+
module QualityControlCli
|
|
7
|
+
# The command behind `hecks quality_control create_ledger_role`: creates the ordinary Postgres
|
|
8
|
+
# role the QA ledger connects as and makes it own the database. It is idempotent, and refuses a
|
|
9
|
+
# superuser or `BYPASSRLS` role, which the era write-fence cannot bind.
|
|
10
|
+
#
|
|
11
|
+
# hecks quality_control create_ledger_role <database> [--role <role>] # role defaults to
|
|
12
|
+
# hecks_qa
|
|
13
|
+
class QaPostgresRole
|
|
14
|
+
USAGE = "usage: hecks quality_control create_ledger_role <database> [--role <role>]"
|
|
15
|
+
|
|
16
|
+
# `relkind` of each relation to the word `ALTER` takes for it. Partitions are their own
|
|
17
|
+
# rows; `ALTER TABLE ... OWNER` on the parent does not recurse.
|
|
18
|
+
KINDS = { "r" => "TABLE", "p" => "TABLE", "S" => "SEQUENCE", "v" => "VIEW", "m" => "MATERIALIZED VIEW" }.freeze
|
|
19
|
+
|
|
20
|
+
# Creates the role and hands it the database.
|
|
21
|
+
#
|
|
22
|
+
# @param argv [Array<String>] the database, and optionally `--role NAME`
|
|
23
|
+
# @param out [IO] where the report goes
|
|
24
|
+
# @param err [IO] where a usage error goes
|
|
25
|
+
# @return [Integer] 0 once the database is the role's, 1 for a usage error
|
|
26
|
+
# @raise [SystemExit] when the role is a superuser or `BYPASSRLS`, or the database is missing
|
|
27
|
+
def self.call(argv, out: $stdout, err: $stderr)
|
|
28
|
+
new(out: out, err: err).call(argv)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @param out [IO] where the report goes
|
|
32
|
+
# @param err [IO] where a usage error goes
|
|
33
|
+
def initialize(out: $stdout, err: $stderr)
|
|
34
|
+
@out = out
|
|
35
|
+
@err = err
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @param argv [Array<String>] the database, and optionally `--role NAME`
|
|
39
|
+
# @return [Integer] the exit status
|
|
40
|
+
# @raise [SystemExit] when the role is a superuser or `BYPASSRLS`, or the database is missing
|
|
41
|
+
def call(argv)
|
|
42
|
+
argv = argv.dup
|
|
43
|
+
role = "hecks_qa"
|
|
44
|
+
if (index = argv.index("--role"))
|
|
45
|
+
role = argv[index + 1] or return usage("--role needs a name")
|
|
46
|
+
argv.slice!(index, 2)
|
|
47
|
+
end
|
|
48
|
+
database = argv.shift
|
|
49
|
+
return usage("no database named") if database.to_s.empty?
|
|
50
|
+
return usage("unexpected argument #{argv.first.inspect}") unless argv.empty?
|
|
51
|
+
|
|
52
|
+
done = []
|
|
53
|
+
skipped = []
|
|
54
|
+
admin = PG.connect(dbname: "postgres")
|
|
55
|
+
create_role(admin, role, done, skipped)
|
|
56
|
+
take_database(admin, database, role, done, skipped)
|
|
57
|
+
admin.close
|
|
58
|
+
take_contents(database, role, done)
|
|
59
|
+
report(database, role, done, skipped)
|
|
60
|
+
0
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def usage(message)
|
|
66
|
+
@err.puts "hecks quality_control create_ledger_role: #{message}"
|
|
67
|
+
@err.puts USAGE
|
|
68
|
+
1
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def create_role(admin, role, done, skipped)
|
|
72
|
+
attrs = admin.exec_params("SELECT rolsuper, rolbypassrls FROM pg_roles WHERE rolname = $1", [role])
|
|
73
|
+
if attrs.ntuples.zero?
|
|
74
|
+
# CREATE ROLE has no IF NOT EXISTS. Concurrent creates can lose on the catalog index
|
|
75
|
+
# (unique_violation) rather than duplicate_object, so the block rescues both.
|
|
76
|
+
admin.exec(<<~SQL)
|
|
77
|
+
DO $$ BEGIN
|
|
78
|
+
CREATE ROLE #{admin.quote_ident(role)} LOGIN NOSUPERUSER NOBYPASSRLS NOCREATEDB NOCREATEROLE;
|
|
79
|
+
EXCEPTION WHEN duplicate_object OR unique_violation THEN NULL;
|
|
80
|
+
END $$
|
|
81
|
+
SQL
|
|
82
|
+
done << "created role #{role} (LOGIN, no SUPERUSER, no BYPASSRLS)"
|
|
83
|
+
elsif attrs[0]["rolsuper"] == "t" || attrs[0]["rolbypassrls"] == "t"
|
|
84
|
+
admin.close
|
|
85
|
+
abort "hecks quality_control create_ledger_role: role #{role} already exists as " \
|
|
86
|
+
"#{attrs[0]['rolsuper'] == 't' ? 'a superuser' : 'a BYPASSRLS role'} — the era write-fence " \
|
|
87
|
+
"cannot bite it, which is the exact state this script exists to end. Pick another role, or " \
|
|
88
|
+
"ALTER ROLE #{role} NOSUPERUSER NOBYPASSRLS first."
|
|
89
|
+
else
|
|
90
|
+
skipped << "role #{role} exists, ordinary"
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def take_database(admin, database, role, done, skipped)
|
|
95
|
+
owner = admin.exec_params(
|
|
96
|
+
"SELECT pg_get_userbyid(datdba) AS owner FROM pg_database WHERE datname = $1", [database]
|
|
97
|
+
)
|
|
98
|
+
if owner.ntuples.zero?
|
|
99
|
+
admin.close
|
|
100
|
+
abort "hecks quality_control create_ledger_role: no database #{database} — createdb it first (PostgresEra provisions " \
|
|
101
|
+
"every table it needs on first connect, never the database itself)"
|
|
102
|
+
end
|
|
103
|
+
if owner[0]["owner"] == role
|
|
104
|
+
skipped << "database #{database} already owned by #{role}"
|
|
105
|
+
else
|
|
106
|
+
admin.exec("ALTER DATABASE #{admin.quote_ident(database)} OWNER TO #{admin.quote_ident(role)}")
|
|
107
|
+
done << "database #{database}: owner #{owner[0]['owner']} -> #{role}"
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def take_contents(database, role, done)
|
|
112
|
+
db = PG.connect(dbname: database)
|
|
113
|
+
quoted_role = db.quote_ident(role)
|
|
114
|
+
schema_owner = db.exec("SELECT pg_get_userbyid(nspowner) AS owner FROM pg_namespace WHERE nspname = 'public'")
|
|
115
|
+
if schema_owner.ntuples.positive? && schema_owner[0]["owner"] != role
|
|
116
|
+
db.exec("ALTER SCHEMA public OWNER TO #{quoted_role}")
|
|
117
|
+
done << "schema public: owner #{schema_owner[0]['owner']} -> #{role}"
|
|
118
|
+
end
|
|
119
|
+
take_relations(db, role, quoted_role, done)
|
|
120
|
+
take_functions(db, role, quoted_role, done)
|
|
121
|
+
db.close
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# relkind: r table, p partitioned table, S sequence, v view, m matview.
|
|
125
|
+
def take_relations(db, role, quoted_role, done)
|
|
126
|
+
relations = db.exec_params(<<~SQL, [role])
|
|
127
|
+
SELECT c.relname, c.relkind, pg_get_userbyid(c.relowner) AS owner
|
|
128
|
+
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
129
|
+
WHERE n.nspname = 'public' AND c.relkind IN ('r', 'p', 'S', 'v', 'm') AND pg_get_userbyid(c.relowner) <> $1
|
|
130
|
+
ORDER BY c.relkind, c.relname
|
|
131
|
+
SQL
|
|
132
|
+
relations.each do |row|
|
|
133
|
+
db.exec("ALTER #{KINDS.fetch(row['relkind'])} #{db.quote_ident(row['relname'])} OWNER TO #{quoted_role}")
|
|
134
|
+
end
|
|
135
|
+
done << "#{relations.ntuples} relation(s) in public -> #{role}" if relations.ntuples.positive?
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def take_functions(db, role, quoted_role, done)
|
|
139
|
+
functions = db.exec_params(<<~SQL, [role])
|
|
140
|
+
SELECT p.proname, pg_get_function_identity_arguments(p.oid) AS args
|
|
141
|
+
FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace
|
|
142
|
+
WHERE n.nspname = 'public' AND pg_get_userbyid(p.proowner) <> $1
|
|
143
|
+
ORDER BY p.proname
|
|
144
|
+
SQL
|
|
145
|
+
functions.each do |row|
|
|
146
|
+
db.exec("ALTER FUNCTION #{db.quote_ident(row['proname'])}(#{row['args']}) OWNER TO #{quoted_role}")
|
|
147
|
+
end
|
|
148
|
+
done << "#{functions.ntuples} function(s) in public -> #{role}" if functions.ntuples.positive?
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def report(database, role, done, skipped)
|
|
152
|
+
@out.puts "hecks quality_control create_ledger_role: #{database} is #{role}'s"
|
|
153
|
+
done.each { |line| @out.puts " did: #{line}" }
|
|
154
|
+
skipped.each { |line| @out.puts " already: #{line}" }
|
|
155
|
+
@out.puts " bind it: database \"postgres://#{role}@localhost/#{database}\"" if done.any?
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../../hecks"
|
|
4
|
+
# The ledger's `persisted_by "PostgresEra"` binding needs the era plugin, which core does not load.
|
|
5
|
+
require_relative "../../ports/persistence/plugins/era"
|
|
6
|
+
require_relative "../adapters/git_pr"
|
|
7
|
+
|
|
8
|
+
module Hecks
|
|
9
|
+
module QualityControlCli
|
|
10
|
+
# The command behind `hecks quality_control check_pull_requests`: checks CI for every PR the
|
|
11
|
+
# QualityControl ledger tracks as open (`Patch` and `Improvement`). It starts a `Clearance` per
|
|
12
|
+
# unsettled head commit and lets the CI port's policies record the verdict.
|
|
13
|
+
#
|
|
14
|
+
# Exit codes: 0 nothing to act on; 1 operational error (`gh`, bad sha, ledger boot);
|
|
15
|
+
# 2 a newly red PR, with the details on stdout. A commit's `Clearance` is minted once and never
|
|
16
|
+
# re-asked; pending checks are skipped, since the CI port has no way to say "ask again later".
|
|
17
|
+
class QaPrCheck
|
|
18
|
+
EXIT_OK = 0
|
|
19
|
+
EXIT_ERROR = 1
|
|
20
|
+
EXIT_NEWLY_RED = 2
|
|
21
|
+
|
|
22
|
+
USAGE = "usage: hecks quality_control check_pull_requests"
|
|
23
|
+
|
|
24
|
+
SHA_PATTERN = /\A[0-9a-fA-F]{7,40}\z/
|
|
25
|
+
|
|
26
|
+
# Two worklists, checked the same way. The citation is what a row references: a `Bug` for a
|
|
27
|
+
# `Patch`, an optional `Angle` for an `Improvement`.
|
|
28
|
+
WORKLISTS = [
|
|
29
|
+
{ aggregate: "Patch", citation_field: :bug, citation_label: "bug" },
|
|
30
|
+
{ aggregate: "Improvement", citation_field: :angle, citation_label: "angle" }
|
|
31
|
+
].freeze
|
|
32
|
+
|
|
33
|
+
# Checks every tracked PR.
|
|
34
|
+
#
|
|
35
|
+
# @param argv [Array<String>] no arguments; `--help` prints the usage
|
|
36
|
+
# @param root [String] the repository root, where the ledger's boot directory is found
|
|
37
|
+
# @param env [Hash{String => String}] `QA_SWEEP_DOMAIN_DIR` points at another ledger
|
|
38
|
+
# @return [Integer] 0 clean, 1 on an operational error, 2 for a newly red PR
|
|
39
|
+
# @raise [SystemExit] when given arguments, or the ledger does not boot
|
|
40
|
+
def self.call(argv, root:, env: ENV)
|
|
41
|
+
new(root: root, env: env).call(argv)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @param root [String] the repository root
|
|
45
|
+
# @param env [Hash{String => String}] `QA_SWEEP_DOMAIN_DIR` points at another ledger
|
|
46
|
+
def initialize(root:, env: ENV)
|
|
47
|
+
@domain_dir = env.fetch("QA_SWEEP_DOMAIN_DIR", File.join(root, "qa/bluebook"))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @param argv [Array<String>] no arguments; `--help` prints the usage
|
|
51
|
+
# @return [Integer] the exit status
|
|
52
|
+
# @raise [SystemExit] when given arguments, or the ledger does not boot
|
|
53
|
+
def call(argv)
|
|
54
|
+
unless argv.empty?
|
|
55
|
+
if %w[-h --help].include?(argv.first)
|
|
56
|
+
puts USAGE
|
|
57
|
+
return EXIT_OK
|
|
58
|
+
end
|
|
59
|
+
abort "#{USAGE}\nthis script takes no arguments — it checks every PR QualityControl::Patch or " \
|
|
60
|
+
"QualityControl::Improvement tracks as open, every time"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Every `gh` call goes through the `GitPr` adapter. With `--json`, gh exits 0 whatever the
|
|
64
|
+
# checks say, so a failure means only that the call did not succeed.
|
|
65
|
+
@git_pr = Hecks::Adapters::GitPr.new(repo_dir: Dir.pwd)
|
|
66
|
+
@runtime = boot_ledger
|
|
67
|
+
worklists = WORKLISTS.to_h { |w| [w, query("#{w[:aggregate]}.Open")] }
|
|
68
|
+
if worklists.values.all?(&:empty?)
|
|
69
|
+
puts "no PRs tracked as open by QualityControl::Patch or QualityControl::Improvement — nothing to check"
|
|
70
|
+
return EXIT_OK
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
check_all(worklists)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def boot_ledger
|
|
79
|
+
Hecks.boot(@domain_dir)
|
|
80
|
+
rescue StandardError => e
|
|
81
|
+
abort "the QualityControl ledger did not boot — fix the ledger itself before checking PRs against it " \
|
|
82
|
+
"(#{e.class}: #{e.message})"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def check_all(worklists)
|
|
86
|
+
newly_red = []
|
|
87
|
+
operational_errors = []
|
|
88
|
+
worklists.each do |worklist, rows|
|
|
89
|
+
rows.each { |row| check_one_pr(row, newly_red, operational_errors, **worklist) }
|
|
90
|
+
end
|
|
91
|
+
report_errors(operational_errors)
|
|
92
|
+
return report_newly_red(newly_red) if newly_red.any?
|
|
93
|
+
return EXIT_ERROR if operational_errors.any?
|
|
94
|
+
|
|
95
|
+
puts
|
|
96
|
+
puts "clean — every PR QualityControl::Patch or QualityControl::Improvement tracks as open is " \
|
|
97
|
+
"either already cleared, still running, or was retired this run."
|
|
98
|
+
EXIT_OK
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def query(name, **args) = @runtime.query("QualityControl::#{name}", **args)
|
|
102
|
+
|
|
103
|
+
# Reads nested row fields by `[]`, because `Hecks::Runtime::Value` does not answer `Hash#dig`.
|
|
104
|
+
def field(row, *path)
|
|
105
|
+
path.reduce(row) { |held, key| held.nil? ? nil : held[key] }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def clearance_for_commit(sha)
|
|
109
|
+
query("Clearance.All").find { |row| field(row, :commit, :value) == sha }
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Retires a Patch/Improvement whose PR GitHub reports merged or closed; nothing else tells
|
|
113
|
+
# the ledger. Returns true if it retired one.
|
|
114
|
+
def retire_if_settled?(view, number, label, aggregate)
|
|
115
|
+
case view[:state]
|
|
116
|
+
when "MERGED"
|
|
117
|
+
@runtime.dispatch_flat("QualityControl::#{aggregate}.Merge", { id: number })
|
|
118
|
+
puts "#{label}: merged — retired from the worklist"
|
|
119
|
+
true
|
|
120
|
+
when "CLOSED"
|
|
121
|
+
@runtime.dispatch_flat("QualityControl::#{aggregate}.Close", { id: number })
|
|
122
|
+
puts "#{label}: closed without merging — retired from the worklist"
|
|
123
|
+
true
|
|
124
|
+
else
|
|
125
|
+
false
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Runs a read against `gh`; a failure is appended to `operational_errors` and answers nil.
|
|
130
|
+
def reading_gh(label, operational_errors)
|
|
131
|
+
yield
|
|
132
|
+
rescue Hecks::Adapters::GitPr::CommandFailed => e
|
|
133
|
+
operational_errors << "#{label}: #{e.message}"
|
|
134
|
+
nil
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# True when every check has finished, so a verdict is worth asking for. Pending stays out
|
|
138
|
+
# here because the CI port cannot express "ask again later".
|
|
139
|
+
def checks_settled?(number, label, citation, sha, operational_errors)
|
|
140
|
+
checks = reading_gh(label, operational_errors) { @git_pr.checks(number) }
|
|
141
|
+
return false unless checks
|
|
142
|
+
|
|
143
|
+
if checks.empty?
|
|
144
|
+
puts "#{label}, #{citation}, #{sha[0, 7]}: gh reports no checks at all yet — skipping this run"
|
|
145
|
+
return false
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
if checks.any? { |c| c[:bucket] == "pending" }
|
|
149
|
+
puts "#{label}, #{citation}, #{sha[0, 7]}: checks still running — skipping this run"
|
|
150
|
+
return false
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
true
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Checks one tracked PR, appending to `newly_red` or `operational_errors` in place. The live
|
|
157
|
+
# head sha is read from gh each run, since a push mints a new commit to clear.
|
|
158
|
+
def check_one_pr(row, newly_red, operational_errors, aggregate:, citation_field:, citation_label:)
|
|
159
|
+
number = field(row, :number, :value)
|
|
160
|
+
cited = field(row, citation_field)
|
|
161
|
+
citation = cited ? "#{citation_label} #{cited}" : "no #{citation_label} cited"
|
|
162
|
+
label = "#{aggregate} PR ##{number} (#{field(row, :branch, :value)})"
|
|
163
|
+
|
|
164
|
+
view = reading_gh(label, operational_errors) { @git_pr.pull_request(number) }
|
|
165
|
+
return unless view
|
|
166
|
+
return if retire_if_settled?(view, number, label, aggregate)
|
|
167
|
+
|
|
168
|
+
sha = view[:headRefOid].to_s
|
|
169
|
+
unless sha.match?(SHA_PATTERN)
|
|
170
|
+
operational_errors << "#{label}: head commit #{sha.inspect} does not look like a sha — skipping"
|
|
171
|
+
return
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
if (existing = clearance_for_commit(sha))
|
|
175
|
+
puts "#{label}, #{citation}, #{sha[0, 7]}: already cleared (#{existing[:status]}) — " \
|
|
176
|
+
"settled once, not re-checked"
|
|
177
|
+
return
|
|
178
|
+
end
|
|
179
|
+
return unless checks_settled?(number, label, citation, sha, operational_errors)
|
|
180
|
+
|
|
181
|
+
ask_the_ledger(sha, label, citation, number, newly_red, aggregate: aggregate)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Starts a Clearance and asks the CI port for a verdict. ClearOnPass/RefuseOnFail settle the
|
|
185
|
+
# record synchronously, so `find` sees it straight away; a red one is appended to `newly_red`.
|
|
186
|
+
def ask_the_ledger(sha, label, citation, number, newly_red, aggregate:)
|
|
187
|
+
::QualityControl::Clearance.start!(commit: { value: sha })
|
|
188
|
+
@runtime.dispatch_flat("QualityControl::Clearance.CI.Run", { commit: sha })
|
|
189
|
+
|
|
190
|
+
cleared = ::QualityControl::Clearance.find(sha)
|
|
191
|
+
summary = cleared.summary.to_h[:value]
|
|
192
|
+
|
|
193
|
+
if cleared.status == "green"
|
|
194
|
+
puts "#{label}, #{citation}, #{sha[0, 7]}: CLEARED — #{summary}"
|
|
195
|
+
return
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
puts "#{label}, #{citation}, #{sha[0, 7]}: RED — #{summary}"
|
|
199
|
+
newly_red << { aggregate: aggregate, pr: number, citation: citation, commit: sha, refusal: summary }
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def report_errors(operational_errors)
|
|
203
|
+
return if operational_errors.empty?
|
|
204
|
+
|
|
205
|
+
puts
|
|
206
|
+
puts "operational error(s):"
|
|
207
|
+
operational_errors.each { |message| puts " #{message}" }
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def report_newly_red(newly_red)
|
|
211
|
+
puts
|
|
212
|
+
puts "=" * 72
|
|
213
|
+
puts "FOUND #{newly_red.length} NEWLY-RED PR(S) — BugCiWatch/ImprovementCiWatch already dispatched " \
|
|
214
|
+
"the matching Regress for each, by aggregate."
|
|
215
|
+
puts "An agent picks up the investigation from here: the same judgment"
|
|
216
|
+
puts "SKILL.md's own \"on a surprising check\" section describes for a sweep's"
|
|
217
|
+
puts "own find — self-contained fix with a regression test and a draft PR,"
|
|
218
|
+
puts "or leave the record for something bigger. Never re-verify from this script."
|
|
219
|
+
puts "=" * 72
|
|
220
|
+
newly_red.each do |finding|
|
|
221
|
+
puts
|
|
222
|
+
puts "aggregate: #{finding[:aggregate]}"
|
|
223
|
+
puts "citation: #{finding[:citation]}"
|
|
224
|
+
puts "pr: ##{finding[:pr]}"
|
|
225
|
+
puts "commit: #{finding[:commit]}"
|
|
226
|
+
puts "refusal: #{finding[:refusal]}"
|
|
227
|
+
end
|
|
228
|
+
EXIT_NEWLY_RED
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../../hecks"
|
|
4
|
+
# The era/lineage plugin has to be loaded before `Hecks.boot` for a real (non-Memory) adapter
|
|
5
|
+
# (ADR 0033).
|
|
6
|
+
require_relative "../../ports/persistence/plugins/era"
|
|
7
|
+
|
|
8
|
+
module Hecks
|
|
9
|
+
module QualityControlCli
|
|
10
|
+
# The command behind `hecks quality_control angle.seed`: seeds the QualityControl ledger's
|
|
11
|
+
# `Angle` backlog with the practice's starting leads. It is idempotent: only references not
|
|
12
|
+
# already on file are proposed.
|
|
13
|
+
class QaSeedAngles
|
|
14
|
+
# A `resolution:` present means the lead was already chased down: it is proposed,
|
|
15
|
+
# investigated and built in one pass. Its absence means the lever is still open.
|
|
16
|
+
SEED = [
|
|
17
|
+
{
|
|
18
|
+
reference: "ANGLE-1",
|
|
19
|
+
premise: "None of BUG#1-5 touched saga/process_manager machinery at all, despite this " \
|
|
20
|
+
"session having just built a real one (BugCiWatch) — a stress domain specifically " \
|
|
21
|
+
"exercising process-manager correlation, multi-step handler chains, and compensation " \
|
|
22
|
+
"legs was a natural, and the highest-priority, next target.",
|
|
23
|
+
citation: "BugCiWatch (lib/hecks/quality_control/quality_control.bluebook, \"noticing when a fix " \
|
|
24
|
+
"stops holding\"); BUG#1-5, none of which touched saga machinery",
|
|
25
|
+
proposer: "Claude QA",
|
|
26
|
+
resolution: "BUG#6 — SagaInterpreter#qualified cannot tell a same-domain nested-entity " \
|
|
27
|
+
"command reference (dispatch Manifest::Slot::Fill) apart from a genuine " \
|
|
28
|
+
"cross-domain qualifier, so the leg is silently read as targeting a nonexistent " \
|
|
29
|
+
"domain and refuses every time; found by qa/stress_domains/waybill on its first " \
|
|
30
|
+
"real run, before fuzzing was even needed. PR #530 (merged)."
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
reference: "ANGLE-2",
|
|
34
|
+
premise: "nested_pieces (qa/stress_domains/nested_pieces) proves two-level ENTITY nesting " \
|
|
35
|
+
"fuzzes and dispatches correctly, but nothing in the corpus exercises deep " \
|
|
36
|
+
"reference_to HOPS — a chain of reference-typed lookups across aggregates — or a " \
|
|
37
|
+
"governance/authorization-shaped construct combined with existence-checking. Entity " \
|
|
38
|
+
"nesting and reference hopping are different DSL mechanisms entirely (entity " \
|
|
39
|
+
"addressing vs CommandRules::References#resolve_references / " \
|
|
40
|
+
"resolve_state_references), and ADR 0037's own Finding 5 already shows reference " \
|
|
41
|
+
"resolution is exactly the kind of place Ruby and Rust structurally diverge — a " \
|
|
42
|
+
"stress domain built around multi-hop references, or a scope/authorization check " \
|
|
43
|
+
"combined with existence-checking, is a genuinely different construct combination " \
|
|
44
|
+
"from anything the six bugs found this session actually touched.",
|
|
45
|
+
citation: "qa/stress_domains/nested_pieces (entity nesting, not reference hops); " \
|
|
46
|
+
"docs/decisions/0037-generated-sequence-fuzz-bridge-found-real-gaps-two-fixed-" \
|
|
47
|
+
"three-catalogued.md, Finding 5 (resolve_state_references never ported to Rust)",
|
|
48
|
+
proposer: "Claude QA"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
reference: "ANGLE-3",
|
|
52
|
+
premise: "BUG#1/#3/#4 were specifically ENGINE-DIVERGENCE bugs — Ruby and Rust disagreeing " \
|
|
53
|
+
"on the same dispatch — a class Ruby-only property/exception fuzzing structurally " \
|
|
54
|
+
"cannot see, because there is no second engine to disagree with. " \
|
|
55
|
+
"spec/rust_conformance_fuzz_spec.rb's own DOMAINS list wires up only pizzas and " \
|
|
56
|
+
"banking for CI-gated differential fuzzing. rust/Cargo.toml already declares " \
|
|
57
|
+
"compiled-binary features for compliance and roster (plus embryonaut and meta) that " \
|
|
58
|
+
"sit unused for this purpose; examples/chess and examples/directory have no Cargo " \
|
|
59
|
+
"feature at all — `hecks project_rust` has never regenerated a binary for either. " \
|
|
60
|
+
"Widening either list would let differential fuzzing catch a whole bug class " \
|
|
61
|
+
"Ruby-only fuzzing cannot, on domains the practice already owns.",
|
|
62
|
+
citation: "BUG#1/#3/#4 (engine-divergence bugs); spec/rust_conformance_fuzz_spec.rb DOMAINS " \
|
|
63
|
+
"(pizzas/banking only); rust/Cargo.toml [features] (compliance/roster/embryonaut/" \
|
|
64
|
+
"meta have binaries and are not wired in; chess/directory have no feature at all)",
|
|
65
|
+
proposer: "Claude QA"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
reference: "ANGLE-4",
|
|
69
|
+
premise: "lib/hecks/bluebook/model_check.rb's ALLOWED_FINDINGS, " \
|
|
70
|
+
"spec/fuzzing/meta_domain_coverage_spec.rb's META_DOMAIN_GUARANTEED_BY_CONSTRUCTION " \
|
|
71
|
+
"and META_DOMAIN_KNOWN_GAPS, and ADR 0037's own catalogued Findings 3-5 are all " \
|
|
72
|
+
"places where something once labeled 'known, not a bug' could be re-examined — a " \
|
|
73
|
+
"gap accepted a year ago is not guaranteed to still be the right call once the " \
|
|
74
|
+
"surrounding code has moved. This session's own waybill finding (BUG#6) confirmed a " \
|
|
75
|
+
"SECOND instance of exactly the family ALLOWED_FINDINGS[\"quality_control\"] already " \
|
|
76
|
+
"named once (Naming.command_ref's bare-constant rewrite conflating a genuine " \
|
|
77
|
+
"cross-domain qualifier with mere nesting) — proof the allowlists are themselves a " \
|
|
78
|
+
"live source of leads, not just a static ledger of settled questions.",
|
|
79
|
+
citation: "lib/hecks/bluebook/model_check.rb ALLOWED_FINDINGS; " \
|
|
80
|
+
"spec/fuzzing/meta_domain_coverage_spec.rb META_DOMAIN_GUARANTEED_BY_CONSTRUCTION / " \
|
|
81
|
+
"META_DOMAIN_KNOWN_GAPS; docs/decisions/0037-generated-sequence-fuzz-bridge-found-" \
|
|
82
|
+
"real-gaps-two-fixed-three-catalogued.md Findings 3-5",
|
|
83
|
+
proposer: "Claude QA"
|
|
84
|
+
}
|
|
85
|
+
].freeze
|
|
86
|
+
|
|
87
|
+
# Proposes the leads the ledger lacks.
|
|
88
|
+
#
|
|
89
|
+
# @param root [String] the repository root, where `qa/bluebook` is the ledger
|
|
90
|
+
# @param out [IO] where each lead's outcome goes
|
|
91
|
+
# @return [Integer] 0 once seeded
|
|
92
|
+
# @raise [SystemExit] when the ledger does not boot
|
|
93
|
+
def self.call(root:, out: $stdout)
|
|
94
|
+
new(root: root, out: out).call
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# @param root [String] the repository root
|
|
98
|
+
# @param out [IO] where each lead's outcome goes
|
|
99
|
+
def initialize(root:, out: $stdout)
|
|
100
|
+
@domain_dir = File.join(root, "qa/bluebook")
|
|
101
|
+
@out = out
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# @return [Integer] 0 once seeded
|
|
105
|
+
# @raise [SystemExit] when the ledger does not boot
|
|
106
|
+
def call
|
|
107
|
+
runtime = begin
|
|
108
|
+
Hecks.boot(@domain_dir)
|
|
109
|
+
rescue StandardError => e
|
|
110
|
+
abort "the QualityControl ledger did not boot — fix the ledger itself before seeding it " \
|
|
111
|
+
"(#{e.class}: #{e.message})"
|
|
112
|
+
end
|
|
113
|
+
existing = runtime.query("QualityControl::Angle.All").map { |row| row[:reference][:value] }
|
|
114
|
+
SEED.each do |angle|
|
|
115
|
+
if existing.include?(angle[:reference])
|
|
116
|
+
@out.puts "already on file: #{angle[:reference]}"
|
|
117
|
+
else
|
|
118
|
+
propose(angle)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
0
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
private
|
|
125
|
+
|
|
126
|
+
def propose(angle)
|
|
127
|
+
record = ::QualityControl::Angle.propose!(
|
|
128
|
+
reference: { value: angle[:reference] }, premise: { value: angle[:premise] },
|
|
129
|
+
citation: { value: angle[:citation] }, proposer: { value: angle[:proposer] },
|
|
130
|
+
now: { value: Time.now.to_i }
|
|
131
|
+
)
|
|
132
|
+
if angle[:resolution]
|
|
133
|
+
record = record.investigate!
|
|
134
|
+
record.build!(resolution: { value: angle[:resolution] })
|
|
135
|
+
@out.puts "proposed, investigated, and built: #{angle[:reference]}"
|
|
136
|
+
else
|
|
137
|
+
@out.puts "proposed: #{angle[:reference]}"
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../../hecks"
|
|
4
|
+
# Load the era plugin before `Hecks.boot` (see hecks run).
|
|
5
|
+
require_relative "../../ports/persistence/plugins/era"
|
|
6
|
+
require_relative "../../fuzzing"
|
|
7
|
+
require_relative "../../corpus"
|
|
8
|
+
|
|
9
|
+
module Hecks
|
|
10
|
+
module QualityControlCli
|
|
11
|
+
# The command behind `hecks quality_control target.seed`: identifies every corpus domain as a
|
|
12
|
+
# rotation `Target`, printing each one's inferred capabilities. It is idempotent: references
|
|
13
|
+
# already on file are skipped whatever their status (re-`Identify` refuses).
|
|
14
|
+
#
|
|
15
|
+
# It skips `examples/embryonaut` (an external product) and `lib/hecks/framework` (no standalone
|
|
16
|
+
# Rust binary) on purpose.
|
|
17
|
+
class QaSeedTargets
|
|
18
|
+
# Identifies the targets the ledger lacks.
|
|
19
|
+
#
|
|
20
|
+
# @param root [String] the repository root
|
|
21
|
+
# @param env [Hash{String => String}] `QA_SWEEP_DOMAIN_DIR` points at another ledger
|
|
22
|
+
# @param out [IO] where each target's outcome goes
|
|
23
|
+
# @return [Integer] 0 once seeded
|
|
24
|
+
# @raise [SystemExit] when the ledger does not boot or a corpus path is missing
|
|
25
|
+
def self.call(root:, env: ENV, out: $stdout)
|
|
26
|
+
new(root: root, env: env, out: out).call
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @param root [String] the repository root
|
|
30
|
+
# @param env [Hash{String => String}] `QA_SWEEP_DOMAIN_DIR` points at another ledger
|
|
31
|
+
# @param out [IO] where each target's outcome goes
|
|
32
|
+
def initialize(root:, env: ENV, out: $stdout)
|
|
33
|
+
@root = root
|
|
34
|
+
@domain_dir = env.fetch("QA_SWEEP_DOMAIN_DIR", File.join(root, "qa/bluebook"))
|
|
35
|
+
@out = out
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @return [Integer] 0 once seeded
|
|
39
|
+
# @raise [SystemExit] when the ledger does not boot or a corpus path is missing
|
|
40
|
+
def call
|
|
41
|
+
# reference => path relative to the repository root, discovered from the corpus.
|
|
42
|
+
seed = Hecks::Corpus.rotation_targets(root: @root)
|
|
43
|
+
runtime = begin
|
|
44
|
+
Hecks.boot(@domain_dir)
|
|
45
|
+
rescue StandardError => e
|
|
46
|
+
abort "the QualityControl ledger did not boot — fix the ledger itself before seeding it " \
|
|
47
|
+
"(#{e.class}: #{e.message})"
|
|
48
|
+
end
|
|
49
|
+
existing = runtime.query("QualityControl::Target.All").map { |row| row[:reference][:value] }
|
|
50
|
+
seed.each { |reference, path| identify(reference, path, existing) }
|
|
51
|
+
0
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def identify(reference, path, existing)
|
|
57
|
+
capabilities = Hecks::Fuzzing::TargetCapabilities.infer(File.join(@root, path)).join(",")
|
|
58
|
+
if existing.include?(reference)
|
|
59
|
+
@out.puts "already on file: #{reference} (#{path}; capabilities #{capabilities})"
|
|
60
|
+
return
|
|
61
|
+
end
|
|
62
|
+
abort "#{reference} names a path that does not exist on disk: #{path}" unless File.directory?(File.join(@root, path))
|
|
63
|
+
|
|
64
|
+
::QualityControl::Target.identify!(reference: { value: reference }, path: { value: path })
|
|
65
|
+
@out.puts "identified: #{reference} (#{path}; capabilities #{capabilities})"
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|