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,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "qa_tool"
|
|
4
|
+
|
|
5
|
+
module Hecks
|
|
6
|
+
module Adapters
|
|
7
|
+
# The `SweepTools` port's adapter: answers `Sweep`'s queries by running the QA commands that
|
|
8
|
+
# work a whole pass: a tick, a sweep, the Heki migration, the ledger's role and a concurrency
|
|
9
|
+
# racer. A run that finds something ends with status 2, which is an answer; anything else the
|
|
10
|
+
# command calls an error is refused with its report.
|
|
11
|
+
class SweepTools < QaTool
|
|
12
|
+
# @return [Hash] `text:` one tick's report
|
|
13
|
+
def tick
|
|
14
|
+
run_command("qa_tick", answers: [0, 2])
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# @param target [Hash, String, nil] the target to sweep; the rotation's pick when absent
|
|
18
|
+
# @param arguments [Hash, String, nil] the sweep's own flags, such as `--all --seeds 5`
|
|
19
|
+
# @return [Hash] `text:` the sweep's report
|
|
20
|
+
def run(target: nil, arguments: nil)
|
|
21
|
+
run_command("qa_sweep", *plain(target), *words(arguments), answers: [0, 2])
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @param domain [Hash, String] the domain directory whose bindings are the destination
|
|
25
|
+
# @param data [Hash, String] the Heki data directory read from
|
|
26
|
+
# @param arguments [Hash, String, nil] aggregate names, and `--force` to apply
|
|
27
|
+
# @return [Hash] `text:` what was, or would be, migrated
|
|
28
|
+
def migrate_ledger_from_heki(domain:, data:, arguments: nil)
|
|
29
|
+
run_command("qa_postgres_migrate", plain(domain), plain(data), *words(arguments))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @param database [Hash, String] the database the role is made to own
|
|
33
|
+
# @param role [Hash, String, nil] the role's name; `hecks_qa` when absent
|
|
34
|
+
# @return [Hash] `text:` what was done, and what already held
|
|
35
|
+
def create_ledger_role(database:, role: nil)
|
|
36
|
+
run_command("qa_postgres_role", plain(database), *(role ? ["--role", plain(role)] : []))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @param domain [Hash, String] the domain directory
|
|
40
|
+
# @param database [Hash, String] the scratch database
|
|
41
|
+
# @param schema [Hash, String] the schema this racer dispatches into
|
|
42
|
+
# @param verb [Hash, String] the command to dispatch
|
|
43
|
+
# @param step_arguments [Hash, String] the command's arguments, as JSON
|
|
44
|
+
# @return [Hash] `text:` `succeeded`, `refused` or `crashed:<class>: <message>`
|
|
45
|
+
def race(domain:, database:, schema:, verb:, step_arguments:)
|
|
46
|
+
run_command("qa_concurrency_racer", plain(domain), plain(database), plain(schema), plain(verb),
|
|
47
|
+
plain(step_arguments))
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Binds the `TargetTools` port to `TargetTools`, which answers `Target`'s queries: the seed, generated domains, the miner, the novelty gate and the scan.
|
|
2
|
+
# Required by name, not relative, so a copy of this directory shares the one implementation loaded.
|
|
3
|
+
require "hecks/quality_control/adapters/target_tools"
|
|
4
|
+
|
|
5
|
+
Hecks.adapter "TargetTools" do
|
|
6
|
+
port "TargetTools"
|
|
7
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "qa_tool"
|
|
4
|
+
|
|
5
|
+
module Hecks
|
|
6
|
+
module Adapters
|
|
7
|
+
# The `TargetTools` port's adapter: answers `Target`'s queries by running the QA commands that
|
|
8
|
+
# enrol, generate and judge chapters: the seed, the generated-domain check, the combination
|
|
9
|
+
# miner, the novelty gate and the external-domain scan. A finding or a judgment is an answer;
|
|
10
|
+
# anything else the command calls an error is refused with its report.
|
|
11
|
+
class TargetTools < QaTool
|
|
12
|
+
# @return [Hash] `text:` each corpus domain's outcome: identified, or already on file
|
|
13
|
+
def seed
|
|
14
|
+
run_command("qa_seed_targets")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# @param arguments [Hash, String, nil] the command's own flags, such as `--domains 3 --rust`
|
|
18
|
+
# @return [Hash] `text:` the report of the generated domains' check
|
|
19
|
+
def check_generated_domains(arguments: nil)
|
|
20
|
+
run_command("qa_generated_domains", *words(arguments), answers: [0, 2])
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @param arguments [Hash, String, nil] the command's own flags, such as `--brief`
|
|
24
|
+
# @return [Hash] `text:` the miner's report
|
|
25
|
+
def mine_combinations(arguments: nil)
|
|
26
|
+
run_command("qa_mine_combinations", *words(arguments), answers: [0, 1])
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @param domain [Hash, String] the candidate stress domain's path
|
|
30
|
+
# @param arguments [Hash, String, nil] `--against` and the paths to compare with
|
|
31
|
+
# @return [Hash] `text:` the new form pairs it earns its place with, or that it earns none
|
|
32
|
+
def judge_novelty(domain:, arguments: nil)
|
|
33
|
+
run_command("qa_domain_novelty", plain(domain), *words(arguments), answers: [0, 1])
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @param arguments [Hash, String, nil] the command's own flags, such as `--max-depth 4`
|
|
37
|
+
# @return [Hash] `text:` the sibling domains that depend on hecks and are not enrolled
|
|
38
|
+
def discover_external_domains(arguments: nil)
|
|
39
|
+
run_command("qa_discover_external_domains", *words(arguments))
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module QualityControlCli
|
|
5
|
+
# How one QA command starts another as its own OS process, from `lib/` rather than through a
|
|
6
|
+
# launcher script: a sweep spawns racers and generated-domain checks, and a launcher query runs
|
|
7
|
+
# any of them. A fresh process is the point: its own boot, its own Postgres connections, and a
|
|
8
|
+
# crash that ends only itself.
|
|
9
|
+
module Child
|
|
10
|
+
# Each command's class, and how it is called: `:argv_root` is `call(ARGV, root:)`, `:argv` is
|
|
11
|
+
# `call(ARGV)` and `:root` is `call(root:)`. A command lives in
|
|
12
|
+
# `hecks/quality_control/cli/<command>`.
|
|
13
|
+
COMMANDS = {
|
|
14
|
+
"qa_tick" => ["QaTick", :argv_root],
|
|
15
|
+
"qa_sweep" => ["QaSweep", :argv_root],
|
|
16
|
+
"qa_pr_check" => ["QaPrCheck", :argv_root],
|
|
17
|
+
"qa_generated_domains" => ["QaGeneratedDomains", :argv_root],
|
|
18
|
+
"qa_mine_combinations" => ["QaMineCombinations", :argv_root],
|
|
19
|
+
"qa_domain_novelty" => ["QaDomainNovelty", :argv_root],
|
|
20
|
+
"qa_discover_external_domains" => ["QaDiscoverExternalDomains", :argv_root],
|
|
21
|
+
"qa_concurrency_racer" => ["QaConcurrencyRacer", :argv_root],
|
|
22
|
+
"qa_postgres_migrate" => ["QaPostgresMigrate", :argv],
|
|
23
|
+
"qa_postgres_role" => ["QaPostgresRole", :argv],
|
|
24
|
+
"qa_seed_angles" => ["QaSeedAngles", :root],
|
|
25
|
+
"qa_seed_targets" => ["QaSeedTargets", :root]
|
|
26
|
+
}.freeze
|
|
27
|
+
|
|
28
|
+
module_function
|
|
29
|
+
|
|
30
|
+
# @param root [String] the checkout whose `lib/` holds the command and where it runs
|
|
31
|
+
# @param command [String] a key of `COMMANDS`
|
|
32
|
+
# @param args [Array<String>] the command's own arguments
|
|
33
|
+
# @return [Array<String>] the argv that runs it under Bundler, ready for `Process.spawn`
|
|
34
|
+
# or `Open3` with `chdir: root`
|
|
35
|
+
# @raise [KeyError] when the command is not one of `COMMANDS`
|
|
36
|
+
def argv(root, command, *args)
|
|
37
|
+
klass, style = COMMANDS.fetch(command)
|
|
38
|
+
call = { argv_root: "ARGV, root: #{root.inspect}", argv: "ARGV", root: "root: #{root.inspect}" }.fetch(style)
|
|
39
|
+
code = "require #{"hecks/quality_control/cli/#{command}".inspect}; " \
|
|
40
|
+
"exit(Hecks::QualityControlCli::#{klass}.call(#{call}))"
|
|
41
|
+
["bundle", "exec", "ruby", "-I", File.join(root, "lib"), "-e", code, "--", *args]
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require_relative "../../../hecks"
|
|
5
|
+
require_relative "../../ports/persistence/plugins/era"
|
|
6
|
+
require_relative "../../fuzzing/concurrent_dispatch"
|
|
7
|
+
|
|
8
|
+
module Hecks
|
|
9
|
+
module QualityControlCli
|
|
10
|
+
# The command behind `hecks quality_control race`: one racer of
|
|
11
|
+
# `Hecks::Fuzzing::ConcurrentDispatch`, run as its own OS process.
|
|
12
|
+
#
|
|
13
|
+
# It is spawned, not forked: the caller (`hecks quality_control ask run`) holds live
|
|
14
|
+
# `PostgresEra` connections, and fork duplicates their file descriptors and SSL state,
|
|
15
|
+
# corrupting them when a child exits.
|
|
16
|
+
#
|
|
17
|
+
# hecks quality_control race <domain-path> <database> <schema> <verb> <args-json>
|
|
18
|
+
#
|
|
19
|
+
# It prints one line, `succeeded`, `refused` or `crashed:<class>: <message>`, and always exits
|
|
20
|
+
# 0.
|
|
21
|
+
class QaConcurrencyRacer
|
|
22
|
+
USAGE = "usage: hecks quality_control race <domain-path> <database> <schema> <verb> <args-json>"
|
|
23
|
+
|
|
24
|
+
# Dispatches the one step and prints how it went.
|
|
25
|
+
#
|
|
26
|
+
# @param argv [Array<String>] the domain path, database, schema, verb and args JSON
|
|
27
|
+
# @param root [String] unused; a racer needs no checkout of its own
|
|
28
|
+
# @param out [IO] where the outcome goes
|
|
29
|
+
# @return [Integer] 0 always
|
|
30
|
+
# @raise [SystemExit] with the usage line when an argument is missing
|
|
31
|
+
def self.call(argv, root: nil, out: $stdout)
|
|
32
|
+
domain_path, database, schema, verb, args_json = argv
|
|
33
|
+
abort USAGE unless domain_path && database && schema && verb && args_json
|
|
34
|
+
|
|
35
|
+
step = { "verb" => verb, "args" => JSON.parse(args_json) }
|
|
36
|
+
outcome = Hecks::Fuzzing::ConcurrentDispatch.boot_preserving_schema(domain_path, database: database,
|
|
37
|
+
schema: schema) do |copy|
|
|
38
|
+
Hecks::Fuzzing::ConcurrentDispatch.dispatch_one(Hecks.boot(copy), step)
|
|
39
|
+
end
|
|
40
|
+
out.puts outcome
|
|
41
|
+
0
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "open3"
|
|
5
|
+
|
|
6
|
+
module Hecks
|
|
7
|
+
module QualityControlCli
|
|
8
|
+
# The command behind `hecks quality_control discover_external_domains`: reports sibling-repo
|
|
9
|
+
# bluebook domains under `--projects-dir` that already depend on the hecks gem but are not
|
|
10
|
+
# enrolled yet. It never enrolls anything itself.
|
|
11
|
+
#
|
|
12
|
+
# hecks quality_control discover_external_domains
|
|
13
|
+
# hecks quality_control discover_external_domains --projects-dir ~/Projects
|
|
14
|
+
# hecks quality_control discover_external_domains --max-depth 4
|
|
15
|
+
# hecks quality_control discover_external_domains --known-path <path> ... # bypass the
|
|
16
|
+
# ledger read
|
|
17
|
+
class QaDiscoverExternalDomains
|
|
18
|
+
USAGE = "usage: hecks quality_control discover_external_domains [--projects-dir <path>] " \
|
|
19
|
+
"[--max-depth N] [--known-path <path> ...]"
|
|
20
|
+
|
|
21
|
+
DEFAULT_PROJECTS_DIR = File.expand_path("~/Projects").freeze
|
|
22
|
+
DEFAULT_MAX_DEPTH = 3
|
|
23
|
+
|
|
24
|
+
# Skipped everywhere in a sibling repo: VCS internals, vendored dependency trees, and this
|
|
25
|
+
# repo's own worktree scratch space.
|
|
26
|
+
SKIP_DIR_BASENAMES = %w[.git .hg .svn vendor node_modules .bundle tmp log coverage .yardoc .ruby-lsp
|
|
27
|
+
.parked bower_components .claude dist build].freeze
|
|
28
|
+
|
|
29
|
+
# Word-boundary matched so "hecksagain" (a real, separate gem) and "hecks_site" never
|
|
30
|
+
# false-positive as a hecks dependency.
|
|
31
|
+
GEMFILE_PATTERN = /gem\s*\(?\s*["']hecks["']/
|
|
32
|
+
GEMFILE_LOCK_PATTERN = /^\s+hecks\s+\(/
|
|
33
|
+
|
|
34
|
+
# Raised for a wrong command line or an unreadable ledger; it ends the run with status 1.
|
|
35
|
+
class Refused < StandardError
|
|
36
|
+
# @return [Boolean] whether the usage line follows the message
|
|
37
|
+
attr_reader :usage
|
|
38
|
+
|
|
39
|
+
# @param message [String] why the run stopped
|
|
40
|
+
# @param usage [Boolean] whether the usage line follows the message
|
|
41
|
+
def initialize(message, usage: true)
|
|
42
|
+
super(message)
|
|
43
|
+
@usage = usage
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Reports the candidates.
|
|
48
|
+
#
|
|
49
|
+
# @param argv [Array<String>] `--projects-dir`, `--max-depth` and `--known-path`
|
|
50
|
+
# @param root [String] the repository root
|
|
51
|
+
# @return [Integer] 0 once reported, 1 for a usage error or an unreadable ledger
|
|
52
|
+
def self.call(argv, root:)
|
|
53
|
+
new(root: root).call(argv)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# @param root [String] the repository root
|
|
57
|
+
def initialize(root:)
|
|
58
|
+
@root = root
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @param argv [Array<String>] `--projects-dir`, `--max-depth` and `--known-path`
|
|
62
|
+
# @return [Integer] the exit status
|
|
63
|
+
def call(argv)
|
|
64
|
+
options = parse(argv.dup)
|
|
65
|
+
return 0 if options == :help
|
|
66
|
+
|
|
67
|
+
candidates, skipped, siblings = discover(options)
|
|
68
|
+
report(options, candidates, skipped, siblings)
|
|
69
|
+
0
|
|
70
|
+
rescue Refused => e
|
|
71
|
+
warn e.message
|
|
72
|
+
warn USAGE if e.usage
|
|
73
|
+
1
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def parse(argv)
|
|
79
|
+
options = { projects_dir: DEFAULT_PROJECTS_DIR, max_depth: DEFAULT_MAX_DEPTH, known_paths: nil }
|
|
80
|
+
until argv.empty?
|
|
81
|
+
case (arg = argv.shift)
|
|
82
|
+
when "-h", "--help"
|
|
83
|
+
puts USAGE
|
|
84
|
+
return :help
|
|
85
|
+
when "--projects-dir"
|
|
86
|
+
refuse!("--projects-dir needs a path") if argv.empty?
|
|
87
|
+
options[:projects_dir] = File.expand_path(argv.shift)
|
|
88
|
+
when "--max-depth"
|
|
89
|
+
refuse!("--max-depth needs a number") if argv.empty?
|
|
90
|
+
options[:max_depth] = Integer(argv.shift)
|
|
91
|
+
when "--known-path"
|
|
92
|
+
refuse!("--known-path needs a path") if argv.empty?
|
|
93
|
+
(options[:known_paths] ||= []) << File.expand_path(argv.shift)
|
|
94
|
+
else
|
|
95
|
+
refuse!("unexpected argument #{arg.inspect}")
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
refuse!("--max-depth must be >= 0") if options[:max_depth].negative?
|
|
99
|
+
refuse!("#{options[:projects_dir]} is not a directory") unless File.directory?(options[:projects_dir])
|
|
100
|
+
options
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def refuse!(message)
|
|
104
|
+
raise Refused, message
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def depends_on_hecks?(dir)
|
|
108
|
+
gemfile = File.join(dir, "Gemfile")
|
|
109
|
+
lockfile = File.join(dir, "Gemfile.lock")
|
|
110
|
+
|
|
111
|
+
(File.file?(gemfile) && File.read(gemfile).match?(GEMFILE_PATTERN)) ||
|
|
112
|
+
(File.file?(lockfile) && File.read(lockfile).match?(GEMFILE_LOCK_PATTERN))
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# A monorepo's real hecks-dependent Ruby app can live below the sibling repo's own top level
|
|
116
|
+
# (a nested `Gemfile` there, not one at `repo_root`). Checked against the candidate's own
|
|
117
|
+
# directory first, then each ancestor up to and including `repo_root`, so the common case, the
|
|
118
|
+
# dependency declared at `repo_root` itself, still answers exactly as it always has.
|
|
119
|
+
def depends_on_hecks_anywhere_above?(entity_dir, repo_root)
|
|
120
|
+
root = File.expand_path(repo_root)
|
|
121
|
+
dir = File.expand_path(entity_dir)
|
|
122
|
+
loop do
|
|
123
|
+
return true if depends_on_hecks?(dir)
|
|
124
|
+
return false if dir == root
|
|
125
|
+
|
|
126
|
+
parent = File.dirname(dir)
|
|
127
|
+
return false if parent == dir # filesystem root reached without finding repo_root
|
|
128
|
+
|
|
129
|
+
dir = parent
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# `repo_root` itself is depth 0, so a project that is one domain at its own top can be a
|
|
134
|
+
# candidate too.
|
|
135
|
+
def bluebook_shaped_dirs(repo_root, max_depth)
|
|
136
|
+
found = []
|
|
137
|
+
walk = lambda do |dir, depth|
|
|
138
|
+
entity_name = File.basename(dir)
|
|
139
|
+
found << [dir, entity_name] if File.file?(File.join(dir, "bluebook", "#{entity_name}.bluebook"))
|
|
140
|
+
return if depth >= max_depth
|
|
141
|
+
|
|
142
|
+
children_of(dir).each do |child|
|
|
143
|
+
next if SKIP_DIR_BASENAMES.include?(child)
|
|
144
|
+
|
|
145
|
+
child_path = File.join(dir, child)
|
|
146
|
+
next unless File.directory?(child_path) && !File.symlink?(child_path)
|
|
147
|
+
next if child == "bluebook" # the bluebook/ directory itself is never an entity dir
|
|
148
|
+
|
|
149
|
+
walk.call(child_path, depth + 1)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
walk.call(repo_root, 0)
|
|
153
|
+
found
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def children_of(dir)
|
|
157
|
+
Dir.children(dir).sort
|
|
158
|
+
rescue Errno::EACCES, Errno::ENOENT
|
|
159
|
+
[]
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Shells out rather than booting the ledger in-process, so this is safe to run alongside a
|
|
163
|
+
# live qa_tick or qa_sweep.
|
|
164
|
+
def ledger_target_paths
|
|
165
|
+
out, err, status = Open3.capture3("bundle", "exec", "ruby", File.join(@root, "exe/hecks"),
|
|
166
|
+
"run", "qa/bluebook", "ask", "target.all", chdir: @root)
|
|
167
|
+
unless status.success?
|
|
168
|
+
raise Refused, "hecks quality_control discover_external_domains: could not read the ledger's targets " \
|
|
169
|
+
"(hecks run qa/bluebook ask target.all exited #{status.exitstatus}):\n#{err}\n" \
|
|
170
|
+
"pass --known-path <path> ... to run without the ledger", usage: false
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
JSON.parse(out).filter_map { |row| row.dig("path", "value") }
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def sibling_repos(projects_dir)
|
|
177
|
+
hecks_root = begin
|
|
178
|
+
File.realpath(@root)
|
|
179
|
+
rescue Errno::ENOENT
|
|
180
|
+
@root
|
|
181
|
+
end
|
|
182
|
+
Dir.children(projects_dir).sort.filter_map do |child|
|
|
183
|
+
next if child == "hecks" # this repo, already fully covered by existing seeding
|
|
184
|
+
|
|
185
|
+
path = File.join(projects_dir, child)
|
|
186
|
+
next unless File.directory?(path) && !File.symlink?(path)
|
|
187
|
+
|
|
188
|
+
begin
|
|
189
|
+
next if File.realpath(path) == hecks_root
|
|
190
|
+
rescue Errno::ENOENT
|
|
191
|
+
next
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
path
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# @return [Array(Array<Hash>, Array<String>, Array<String>)] the candidates, the siblings
|
|
199
|
+
# with no hecks dependency, and every sibling scanned
|
|
200
|
+
def discover(options)
|
|
201
|
+
known_raw = options[:known_paths] || ledger_target_paths
|
|
202
|
+
# Ledger paths may be stored root-relative or absolute; normalize both to compare.
|
|
203
|
+
known = known_raw.to_set { |path| path.start_with?("/") ? File.expand_path(path) : File.expand_path(path, @root) }
|
|
204
|
+
siblings = sibling_repos(options[:projects_dir])
|
|
205
|
+
candidates = []
|
|
206
|
+
skipped = []
|
|
207
|
+
siblings.each do |repo_path|
|
|
208
|
+
found = false
|
|
209
|
+
bluebook_shaped_dirs(repo_path, options[:max_depth]).each do |entity_dir, entity_name|
|
|
210
|
+
next unless depends_on_hecks_anywhere_above?(entity_dir, repo_path)
|
|
211
|
+
|
|
212
|
+
found = true
|
|
213
|
+
absolute = File.expand_path(entity_dir)
|
|
214
|
+
next if known.include?(absolute)
|
|
215
|
+
|
|
216
|
+
repo_name = File.basename(repo_path)
|
|
217
|
+
candidates << { repo: repo_name, reference: "#{repo_name}/#{entity_name}", path: absolute }
|
|
218
|
+
end
|
|
219
|
+
skipped << repo_path unless found
|
|
220
|
+
end
|
|
221
|
+
[candidates.sort_by { |c| [c[:repo], c[:reference]] }, skipped, siblings]
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def report(options, candidates, skipped, siblings)
|
|
225
|
+
puts "scanned #{siblings.size} sibling(s) under #{options[:projects_dir]} " \
|
|
226
|
+
"(max depth #{options[:max_depth]}), #{siblings.size - skipped.size} depend on the hecks gem"
|
|
227
|
+
puts "no hecks dependency, skipped: #{skipped.map { |p| File.basename(p) }.join(', ')}" unless skipped.empty?
|
|
228
|
+
puts
|
|
229
|
+
if candidates.empty?
|
|
230
|
+
puts "no new candidates — every hecks-dependent sibling's bluebook-shaped domain is either " \
|
|
231
|
+
"already a Target or none was found in the shape <name>/bluebook/<name>.bluebook within " \
|
|
232
|
+
"--max-depth #{options[:max_depth]}."
|
|
233
|
+
return
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
puts "#{candidates.size} candidate(s) — report only, nothing identified. Review each, then run " \
|
|
237
|
+
"the command yourself to enroll it:"
|
|
238
|
+
puts
|
|
239
|
+
candidates.each do |c|
|
|
240
|
+
puts " #{c[:reference]}"
|
|
241
|
+
puts " path: #{c[:path]}"
|
|
242
|
+
puts " enroll: hecks run qa/bluebook identify reference=#{c[:reference]} path=#{c[:path]}"
|
|
243
|
+
puts
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "open3"
|
|
5
|
+
require_relative "../../../hecks"
|
|
6
|
+
require_relative "../../fuzzing/form_census"
|
|
7
|
+
|
|
8
|
+
module Hecks
|
|
9
|
+
module QualityControlCli
|
|
10
|
+
# The command behind `hecks quality_control judge_novelty`: does a new stress domain earn its
|
|
11
|
+
# place? It is the gate before a domain becomes a `Target`.
|
|
12
|
+
#
|
|
13
|
+
# hecks quality_control judge_novelty <domain-path> [--against <path> ...]
|
|
14
|
+
#
|
|
15
|
+
# A domain is new when it puts two forms together on one aggregate that no existing target does
|
|
16
|
+
# (`Hecks::Fuzzing::FormCensus`). It is compared against the ledger's `Target.path` rows unless
|
|
17
|
+
# `--against` names others. "Nothing new" can also mean the domain's form is not yet named in
|
|
18
|
+
# `FormCensus::FORMS`.
|
|
19
|
+
#
|
|
20
|
+
# Exit codes: 0 new pair found; 1 no new pair; 2 usage or wrong path shape.
|
|
21
|
+
class QaDomainNovelty
|
|
22
|
+
EXIT_NOVEL = 0
|
|
23
|
+
EXIT_NOTHING = 1
|
|
24
|
+
EXIT_USAGE = 2
|
|
25
|
+
|
|
26
|
+
USAGE = "usage: hecks quality_control judge_novelty <domain-path> [--against <path> ...]"
|
|
27
|
+
|
|
28
|
+
# Judges the candidate.
|
|
29
|
+
#
|
|
30
|
+
# @param argv [Array<String>] the candidate's path, then optionally `--against` and paths
|
|
31
|
+
# @param root [String] the repository root
|
|
32
|
+
# @return [Integer] 0 when the candidate earns its place, 1 when it does not, 2 for a usage
|
|
33
|
+
# error
|
|
34
|
+
def self.call(argv, root:)
|
|
35
|
+
new(root: root).call(argv)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @param root [String] the repository root
|
|
39
|
+
def initialize(root:)
|
|
40
|
+
@root = root
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @param argv [Array<String>] the candidate's path, then optionally `--against` and paths
|
|
44
|
+
# @return [Integer] the exit status
|
|
45
|
+
def call(argv)
|
|
46
|
+
candidate, against = parse(argv)
|
|
47
|
+
refuse_unless_stress_domain_shaped!(normalize(candidate))
|
|
48
|
+
existing_source = against.empty? ? "the ledger's Target.path rows" : "--against"
|
|
49
|
+
existing = (against.empty? ? ledger_target_paths : against).map { |path| normalize(path) }.uniq
|
|
50
|
+
own = existing.select { |path| path == normalize(candidate) }
|
|
51
|
+
existing -= own
|
|
52
|
+
puts "note: #{candidate} is already a target — left out of the comparison" unless own.empty?
|
|
53
|
+
|
|
54
|
+
missing, present = existing.partition { |path| Hecks::Fuzzing::FormCensus.bluebook_files(path).nil? }
|
|
55
|
+
missing.each { |path| warn "skipping #{path}: no bluebook/*.bluebook on disk" }
|
|
56
|
+
judge(candidate, present, existing_source)
|
|
57
|
+
rescue UsageError => e
|
|
58
|
+
warn e.message
|
|
59
|
+
warn USAGE
|
|
60
|
+
EXIT_USAGE
|
|
61
|
+
rescue Stopped
|
|
62
|
+
EXIT_USAGE
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
# Raised for a wrong command line or path shape; it ends the run with `EXIT_USAGE`.
|
|
68
|
+
class UsageError < StandardError; end
|
|
69
|
+
|
|
70
|
+
# Raised once the reason has been printed; it ends the run with `EXIT_USAGE`.
|
|
71
|
+
class Stopped < StandardError; end
|
|
72
|
+
|
|
73
|
+
def parse(argv)
|
|
74
|
+
candidate = nil
|
|
75
|
+
against = []
|
|
76
|
+
mode = :candidate
|
|
77
|
+
argv.each do |arg|
|
|
78
|
+
if arg == "--against"
|
|
79
|
+
mode = :against
|
|
80
|
+
elsif mode == :against
|
|
81
|
+
against << arg
|
|
82
|
+
elsif candidate.nil?
|
|
83
|
+
candidate = arg
|
|
84
|
+
else
|
|
85
|
+
raise UsageError, "unexpected argument #{arg.inspect}"
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
raise UsageError, "no candidate domain given" if candidate.nil?
|
|
89
|
+
raise UsageError, "--against needs at least one path" if mode == :against && against.empty?
|
|
90
|
+
|
|
91
|
+
[candidate, against]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# `hecks project_rust` reads exactly `<name>/bluebook/<name>.bluebook`, so refuse anything
|
|
95
|
+
# else early.
|
|
96
|
+
def refuse_unless_stress_domain_shaped!(path)
|
|
97
|
+
expected = File.join(path, "bluebook", "#{File.basename(path)}.bluebook")
|
|
98
|
+
return if File.file?(expected)
|
|
99
|
+
|
|
100
|
+
raise UsageError, "#{path} is not shaped like a stress domain: expected #{expected} to exist " \
|
|
101
|
+
"(`<name>/bluebook/<name>.bluebook` — a flat file breaks `hecks project_rust`; " \
|
|
102
|
+
"see qa/stress_domains/ledger_ordering/NOTES.md)"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# The ledger's target paths via one read-only `ask`; never boots the ledger in this process.
|
|
106
|
+
def ledger_target_paths
|
|
107
|
+
out, err, status = Open3.capture3("bundle", "exec", "ruby", File.join(@root, "exe/hecks"),
|
|
108
|
+
"run", "qa/bluebook", "ask", "target.all", chdir: @root)
|
|
109
|
+
unless status.success?
|
|
110
|
+
warn "hecks quality_control judge_novelty: could not read the ledger's targets (hecks run qa/bluebook ask " \
|
|
111
|
+
"target.all exited #{status.exitstatus}):\n#{err}"
|
|
112
|
+
warn "pass --against <path> ... to run without the ledger"
|
|
113
|
+
raise Stopped
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
JSON.parse(out).map { |row| row.dig("path", "value") }.compact
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def normalize(path) = File.expand_path(path, @root)
|
|
120
|
+
|
|
121
|
+
def judge(candidate, present, existing_source)
|
|
122
|
+
census = Hecks::Fuzzing::FormCensus.census(normalize(candidate))
|
|
123
|
+
candidate_pairs = Hecks::Fuzzing::FormCensus.covered_pairs(census)
|
|
124
|
+
existing_pairs = present.each_with_object(Hash.new { |h, k| h[k] = [] }) do |path, covered|
|
|
125
|
+
Hecks::Fuzzing::FormCensus.covered_pairs(Hecks::Fuzzing::FormCensus.census(path)).each do |pair, names|
|
|
126
|
+
covered[pair].concat(names.map { |name| "#{name} (#{path.delete_prefix("#{@root}/")})" })
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
new_pairs = candidate_pairs.reject { |pair, _| existing_pairs.key?(pair) }
|
|
130
|
+
|
|
131
|
+
puts "candidate: #{candidate} — #{census.size} aggregate(s), #{candidate_pairs.size} form pair(s) met"
|
|
132
|
+
puts "against: #{present.size} domain(s) from #{existing_source}, " \
|
|
133
|
+
"#{existing_pairs.size} form pair(s) met between them"
|
|
134
|
+
puts
|
|
135
|
+
return nothing_new(candidate) if new_pairs.empty?
|
|
136
|
+
|
|
137
|
+
width = new_pairs.keys.map(&:size).max
|
|
138
|
+
puts "new pair(s) — met on one aggregate here, on none of the existing targets:"
|
|
139
|
+
new_pairs.sort.each { |pair, names| puts " #{pair.ljust(width)} #{names.uniq.join(', ')}" }
|
|
140
|
+
puts
|
|
141
|
+
puts "#{new_pairs.size} new pair(s) — #{candidate} earns its place."
|
|
142
|
+
EXIT_NOVEL
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def nothing_new(candidate)
|
|
146
|
+
puts "no new pair — every pair of forms #{candidate} puts together on one aggregate is already met " \
|
|
147
|
+
"by an existing target. Either the domain is not new, or the form it exists for is not yet " \
|
|
148
|
+
"named in Hecks::Fuzzing::FormCensus::FORMS (lib/hecks/fuzzing/form_census.rb) — name it " \
|
|
149
|
+
"there first, citing the gap it is one step from, and run this again."
|
|
150
|
+
puts
|
|
151
|
+
puts "READ THAT AS A QUESTION ABOUT THE CENSUS, NOT A VERDICT ON THE DOMAIN. This gate measures " \
|
|
152
|
+
"ONE AGGREGATE's own declared forms, so a domain whose point is a chapter-level construct — " \
|
|
153
|
+
"a policy, an `across` target, a process manager, a read model's own group_by/median, an " \
|
|
154
|
+
"outbox, a dry run — is invisible to it by construction, not by omission. `corrects` and " \
|
|
155
|
+
"`role_gated` were exactly that: declared all over the corpus, unnamed here, so this line " \
|
|
156
|
+
"told three stress domains built around retroactive correction that they were redundant " \
|
|
157
|
+
"(their own NOTES.md each say so). If that is your domain's case, say which construct it " \
|
|
158
|
+
"exists for in its NOTES.md and keep it."
|
|
159
|
+
EXIT_NOTHING
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|