hecks 2.9.0 → 3.0.1
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 +13 -4
- 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 +15 -8
- 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 +15 -5
- 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,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "json"
|
|
5
|
+
require "open3"
|
|
6
|
+
require "yaml"
|
|
7
|
+
|
|
8
|
+
module Hecks
|
|
9
|
+
module CLI
|
|
10
|
+
# The command behind `hecks refresh_runtime_baseline`: regenerates the committed runtime
|
|
11
|
+
# baselines the sharded CI jobs group from. It prefers one CI run's timings; a local run is
|
|
12
|
+
# the fallback.
|
|
13
|
+
#
|
|
14
|
+
# hecks refresh_runtime_baseline --from-run <run-id> # preferred: one CI run's timings
|
|
15
|
+
# hecks refresh_runtime_baseline [workers] # fallback: this machine only
|
|
16
|
+
#
|
|
17
|
+
# `--from-run` reads the runtime-log artifacts of a successful run and writes a family only
|
|
18
|
+
# when every leg uploaded one; a partial set would skew the split. Runner seconds differ from a
|
|
19
|
+
# laptop's, so a local baseline misses subprocess-heavy specs. The local mode refuses a red run.
|
|
20
|
+
module RefreshRspecRuntimeBaseline
|
|
21
|
+
REPO = "heckslabs/hecks"
|
|
22
|
+
USAGE = "usage: hecks refresh_runtime_baseline --from-run <run-id> | [workers]"
|
|
23
|
+
|
|
24
|
+
# Only `spec/..._spec.rb:<seconds>` lines; RSpec's "Run options" line must not be copied
|
|
25
|
+
# into a committed file. Seconds can arrive in exponent form (`9.69e-05`).
|
|
26
|
+
RUNTIME_LINE = %r{\A(?<file>spec/\S+_spec\.rb):(?<seconds>\d+(?:\.\d+)?(?:e-?\d+)?)\z}
|
|
27
|
+
|
|
28
|
+
module_function
|
|
29
|
+
|
|
30
|
+
# Refreshes the baselines from a CI run or a local run.
|
|
31
|
+
#
|
|
32
|
+
# @param argv [Array<String>] `--from-run RUN_ID`, or optionally the local worker count
|
|
33
|
+
# @param root [String] the checkout whose `.github/` baselines are rewritten
|
|
34
|
+
# @param out [IO] where what was written goes
|
|
35
|
+
# @return [Integer] the exit status, 0 once written
|
|
36
|
+
# @raise [SystemExit] when the run is unreadable or unsuccessful, the local suite is red, or
|
|
37
|
+
# no artifact family is complete
|
|
38
|
+
def call(argv, root:, out: $stdout)
|
|
39
|
+
if argv.first == "--from-run"
|
|
40
|
+
from_run(root, argv.fetch(1) { abort USAGE }, out)
|
|
41
|
+
else
|
|
42
|
+
from_local(root, argv.fetch(0, "6"), out)
|
|
43
|
+
end
|
|
44
|
+
0
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Reads the leg counts from the workflow matrix so a resize cannot leave a stale count here.
|
|
48
|
+
#
|
|
49
|
+
# @param root [String] the checkout
|
|
50
|
+
# @return [Hash{String => Hash}] each artifact family's leg count and baseline file
|
|
51
|
+
def families(root)
|
|
52
|
+
{
|
|
53
|
+
"rspec-runtime-group" => { legs: matrix_legs(root, "ci-rspec.yml", "rspec_shard"),
|
|
54
|
+
baseline: ".github/rspec_runtime_baseline.log" },
|
|
55
|
+
"postgres-io-runtime-group" => { legs: matrix_legs(root, "ci-postgres-io-parallel.yml",
|
|
56
|
+
"rspec_postgres_io_parallel_shard"),
|
|
57
|
+
baseline: ".github/postgres_io_runtime_baseline.log" }
|
|
58
|
+
}
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @param root [String] the checkout
|
|
62
|
+
# @param workflow [String] the workflow file's name
|
|
63
|
+
# @param job [String] the job whose matrix is counted
|
|
64
|
+
# @return [Integer] how many groups the job's matrix has
|
|
65
|
+
def matrix_legs(root, workflow, job)
|
|
66
|
+
YAML.load_file(File.join(root, ".github/workflows", workflow)).dig("jobs", job, "strategy", "matrix", "group").size
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# @param paths [Array<String>] runtime logs
|
|
70
|
+
# @return [Array<String>] one sorted `file:seconds` line per spec file
|
|
71
|
+
def baseline_lines(paths)
|
|
72
|
+
seconds_by_file = paths.flat_map { |path| File.readlines(path) }.filter_map do |line|
|
|
73
|
+
match = RUNTIME_LINE.match(line.strip) or next
|
|
74
|
+
[match[:file], Float(match[:seconds])]
|
|
75
|
+
end.to_h
|
|
76
|
+
seconds_by_file.map { |file, seconds| format("%<file>s:%<seconds>.2f", file: file, seconds: seconds) }.sort
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# @param root [String] the checkout
|
|
80
|
+
# @param relative [String] the baseline's path under the checkout
|
|
81
|
+
# @param lines [Array<String>] the baseline's lines
|
|
82
|
+
# @param out [IO] where the note goes
|
|
83
|
+
# @return [void]
|
|
84
|
+
def write_baseline(root, relative, lines, out)
|
|
85
|
+
File.write(File.join(root, relative), "#{lines.join("\n")}\n")
|
|
86
|
+
out.puts "wrote #{relative}: #{lines.size} spec files"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# @param run_id [String] the CI run's id
|
|
90
|
+
# @return [void]
|
|
91
|
+
# @raise [SystemExit] when `gh` cannot read the run or it did not succeed
|
|
92
|
+
def refuse_unless_successful!(run_id)
|
|
93
|
+
out, status = Open3.capture2("gh", "run", "view", run_id, "-R", REPO, "--json", "conclusion")
|
|
94
|
+
abort "hecks refresh_runtime_baseline: could not read run #{run_id}" unless status.success?
|
|
95
|
+
|
|
96
|
+
conclusion = JSON.parse(out)["conclusion"]
|
|
97
|
+
return if conclusion == "success"
|
|
98
|
+
|
|
99
|
+
abort "hecks refresh_runtime_baseline: run #{run_id} concluded #{conclusion.inspect} — " \
|
|
100
|
+
"refusing to write a baseline from anything but a successful run"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# @param root [String] the checkout
|
|
104
|
+
# @param run_id [String] the CI run's id
|
|
105
|
+
# @param out [IO] where what was written goes
|
|
106
|
+
# @return [void]
|
|
107
|
+
# @raise [SystemExit] when no family of artifacts is complete
|
|
108
|
+
def from_run(root, run_id, out)
|
|
109
|
+
refuse_unless_successful!(run_id)
|
|
110
|
+
dir = File.join(root, "tmp/ci-runtime-#{run_id}")
|
|
111
|
+
FileUtils.rm_rf(dir)
|
|
112
|
+
|
|
113
|
+
written = families(root).count do |prefix, family|
|
|
114
|
+
system("gh", "run", "download", run_id, "-R", REPO, "-p", "#{prefix}-*", "-D", dir,
|
|
115
|
+
out: File::NULL, err: File::NULL)
|
|
116
|
+
logs = Dir[File.join(dir, "#{prefix}-*", "*.log")]
|
|
117
|
+
legs = logs.map { |path| File.basename(File.dirname(path)) }.uniq.size
|
|
118
|
+
if legs == family[:legs]
|
|
119
|
+
write_baseline(root, family[:baseline], baseline_lines(logs), out)
|
|
120
|
+
true
|
|
121
|
+
else
|
|
122
|
+
warn "skipping #{family[:baseline]}: run #{run_id} has #{legs} of #{family[:legs]} #{prefix}-* artifacts"
|
|
123
|
+
false
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
return unless written.zero?
|
|
127
|
+
|
|
128
|
+
abort "hecks refresh_runtime_baseline: run #{run_id} had no complete artifact family — nothing written"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# @param root [String] the checkout
|
|
132
|
+
# @param workers [String] how many `parallel_rspec` workers to time with
|
|
133
|
+
# @param out [IO] where what was written goes
|
|
134
|
+
# @return [void]
|
|
135
|
+
# @raise [SystemExit] when the local suite is red
|
|
136
|
+
def from_local(root, workers, out)
|
|
137
|
+
raw = "tmp/rspec_runtime_baseline.raw.log"
|
|
138
|
+
FileUtils.mkdir_p(File.join(root, "tmp"))
|
|
139
|
+
FileUtils.rm_f(File.join(root, raw))
|
|
140
|
+
|
|
141
|
+
formatters = "--tag ~io --tag ~fuzzing --format progress --format ParallelTests::RSpec::RuntimeLogger --out #{raw}"
|
|
142
|
+
green = system("bundle", "exec", "parallel_rspec", "spec", "-n", workers, "-o", formatters, chdir: root)
|
|
143
|
+
abort "hecks refresh_runtime_baseline: the spec run failed — not writing a baseline from a red suite" unless green
|
|
144
|
+
|
|
145
|
+
write_baseline(root, families(root).fetch("rspec-runtime-group")[:baseline],
|
|
146
|
+
baseline_lines([File.join(root, raw)]), out)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "optparse"
|
|
4
|
+
require_relative "../release/runner"
|
|
5
|
+
|
|
6
|
+
module Hecks
|
|
7
|
+
module CLI
|
|
8
|
+
# The command behind `hecks publish`: tags the merged release commit, publishes the gem, and
|
|
9
|
+
# gets `@hecks/client` published to npm. See "Releasing" in CONTRIBUTING.md for the release PR.
|
|
10
|
+
#
|
|
11
|
+
# hecks publish --dry-run # every check and build, nothing tagged or published
|
|
12
|
+
# hecks publish # tag, gem, then wait for CI to publish the client
|
|
13
|
+
module Release
|
|
14
|
+
# The flags a run starts with, before any is named.
|
|
15
|
+
DEFAULT_FLAGS = { dry_run: false, gem_only: false, npm_only: false, npm_local: false,
|
|
16
|
+
no_wait: false, yes: false }.freeze
|
|
17
|
+
|
|
18
|
+
module_function
|
|
19
|
+
|
|
20
|
+
# Parses the flags and runs the release.
|
|
21
|
+
#
|
|
22
|
+
# @param argv [Array<String>] the command line's flags
|
|
23
|
+
# @param root [String] the repository root the release runs in
|
|
24
|
+
# @return [Integer] the release's exit status, or 2 for a flag it does not know
|
|
25
|
+
def call(argv, root:)
|
|
26
|
+
argv = argv.dup
|
|
27
|
+
flags = DEFAULT_FLAGS.dup
|
|
28
|
+
parser = parser_for(flags)
|
|
29
|
+
begin
|
|
30
|
+
parser.parse!(argv)
|
|
31
|
+
raise OptionParser::InvalidArgument, argv.join(" ") unless argv.empty?
|
|
32
|
+
|
|
33
|
+
return 0 if flags.delete(:help)
|
|
34
|
+
|
|
35
|
+
options = Hecks::Release::Runner::Options.new(**flags)
|
|
36
|
+
rescue OptionParser::ParseError, ArgumentError => e
|
|
37
|
+
warn "hecks publish: #{e.message}"
|
|
38
|
+
warn parser
|
|
39
|
+
return 2
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
Hecks::Release::Runner.new(root: root, options: options).call
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @param flags [Hash{Symbol => Boolean}] filled in as the parser reads each flag
|
|
46
|
+
# @return [OptionParser] the parser for `hecks publish`'s flags
|
|
47
|
+
def parser_for(flags)
|
|
48
|
+
OptionParser.new do |opts|
|
|
49
|
+
opts.banner = "Usage: hecks publish [--dry-run] [--gem-only | --npm-only] [--npm-local | --no-wait] [--yes]"
|
|
50
|
+
opts.separator("Tags the merged release commit, publishes the gem, and gets @hecks/client published.")
|
|
51
|
+
opts.separator("By default CI publishes the client from the tag and this waits for it.")
|
|
52
|
+
opts.on("--dry-run", "run every check and build; tag, push and publish nothing") { flags[:dry_run] = true }
|
|
53
|
+
opts.on("--gem-only", "publish the gem only") { flags[:gem_only] = true }
|
|
54
|
+
opts.on("--npm-only", "skip the gem: wait for CI's publish, or with --npm-local publish from here") do
|
|
55
|
+
flags[:npm_only] = true
|
|
56
|
+
end
|
|
57
|
+
opts.on("--npm-local", "publish @hecks/client from this machine (first publish, or CI is down)") do
|
|
58
|
+
flags[:npm_local] = true
|
|
59
|
+
end
|
|
60
|
+
opts.on("--no-wait", "do not wait for CI's publish to reach npm") { flags[:no_wait] = true }
|
|
61
|
+
opts.on("--yes", "answer the confirmations yes") { flags[:yes] = true }
|
|
62
|
+
opts.on("--help", "print this usage") do
|
|
63
|
+
puts opts
|
|
64
|
+
flags[:help] = true
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require_relative "../release/runner/commands"
|
|
5
|
+
require_relative "../hecks/adapters/codebase/gem_registry"
|
|
6
|
+
require_relative "../version"
|
|
7
|
+
|
|
8
|
+
module Hecks
|
|
9
|
+
module CLI
|
|
10
|
+
# The command behind `hecks publish_gem` and the release runner's gem step: builds the hecks
|
|
11
|
+
# gem and pushes it to rubygems.org, using a push-scoped API key from 1Password instead of
|
|
12
|
+
# `~/.gem/credentials`. It pushes the version it is given, `Hecks::VERSION` by default.
|
|
13
|
+
module ReleaseGem
|
|
14
|
+
module_function
|
|
15
|
+
|
|
16
|
+
# Builds and pushes the gem, refusing unless `op` is installed and the JS client is at the
|
|
17
|
+
# same version (`spec/hecks_client_version_spec.rb` pins that rule too).
|
|
18
|
+
#
|
|
19
|
+
# @param root [String] the repository root the gem is built from
|
|
20
|
+
# @param commands [#run!, #capture, nil] runs the child commands; a real one when nil
|
|
21
|
+
# @param out [IO] where progress goes
|
|
22
|
+
# @param err [IO] where a refusal goes
|
|
23
|
+
# @param version [String] the version to build and push
|
|
24
|
+
# @return [Integer] 0 once pushed, 1 when refused
|
|
25
|
+
def call(root:, commands: nil, out: $stdout, err: $stderr, version: Hecks::VERSION)
|
|
26
|
+
Dir.chdir(root) do
|
|
27
|
+
commands ||= Hecks::Release::Runner::Commands.new
|
|
28
|
+
registry = Hecks::Adapters::Codebase::GemRegistry.new(root: root, commands: commands)
|
|
29
|
+
refusal = refusal_for(root, commands, version)
|
|
30
|
+
if refusal
|
|
31
|
+
err.puts refusal
|
|
32
|
+
return 1
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
out.puts "Building and pushing hecks-#{version}.gem to rubygems.org " \
|
|
36
|
+
"(1Password will prompt for Touch ID)..."
|
|
37
|
+
registry.push!(version)
|
|
38
|
+
out.puts "Released hecks #{version}."
|
|
39
|
+
0
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @param root [String] the repository root
|
|
44
|
+
# @param commands [#run!, #capture] runs the child commands
|
|
45
|
+
# @param version [String] the version being released
|
|
46
|
+
# @return [String, nil] why the release cannot start, or nil when it can
|
|
47
|
+
def refusal_for(root, commands, version = Hecks::VERSION)
|
|
48
|
+
unless Hecks::Adapters::Codebase::SecretVault.new(commands: commands).installed?
|
|
49
|
+
return "1Password CLI (op) not found. Install: brew install 1password-cli"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
client_version = JSON.parse(File.read(File.join(root, "packages/hecks-client/package.json"))).fetch("version")
|
|
53
|
+
return nil if client_version == version
|
|
54
|
+
|
|
55
|
+
"packages/hecks-client is at #{client_version} but Hecks::VERSION is #{version}; bump the package first."
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "open3"
|
|
5
|
+
|
|
6
|
+
module Hecks
|
|
7
|
+
module CLI
|
|
8
|
+
# The command behind `hecks list_io_parallel_specs`: prints, one per line, the spec files with
|
|
9
|
+
# at least one example matching an RSpec tag filter. ci.yml hands the list to `parallel_rspec`
|
|
10
|
+
# instead of the whole `spec` directory.
|
|
11
|
+
#
|
|
12
|
+
# Given every spec file, parallel_tests ignores the runtime log unless it covers about 67% of
|
|
13
|
+
# them, so a tag-filtered job silently groups by file size. A dry run, not grep, picks the
|
|
14
|
+
# files so inherited tags, `~fuzzing` and shared examples count.
|
|
15
|
+
#
|
|
16
|
+
# The list is committed (`.github/postgres_io_spec_files.txt`). `--write FILE` refreshes it;
|
|
17
|
+
# `--check FILE` (CI's postgres_io_file_list job) fails on a stale list.
|
|
18
|
+
#
|
|
19
|
+
# hecks list_io_parallel_specs '<exclude-pattern regex>' -- --tag io --tag ~fuzzing
|
|
20
|
+
module RspecIoParallelFiles
|
|
21
|
+
module_function
|
|
22
|
+
|
|
23
|
+
# Lists the matching files, writes them, or checks a committed list.
|
|
24
|
+
#
|
|
25
|
+
# @param argv [Array<String>] `--check FILE` or `--write FILE` optionally, the exclude
|
|
26
|
+
# pattern, `--`, then the RSpec tag arguments
|
|
27
|
+
# @param root [String] the checkout whose `spec/` is searched; the dry run runs there
|
|
28
|
+
# @param out [IO] where the list goes when neither flag is given
|
|
29
|
+
# @param err [IO] where the tally and any staleness go
|
|
30
|
+
# @return [Integer] the exit status, 0 once done
|
|
31
|
+
# @raise [SystemExit] on a bad command line, an empty candidate set, a failed dry run, or a
|
|
32
|
+
# stale committed list
|
|
33
|
+
def call(argv, root: Dir.pwd, out: $stdout, err: $stderr)
|
|
34
|
+
argv = argv.dup
|
|
35
|
+
mode, list_path = argv.shift(2) if %w[--check --write].include?(argv.first)
|
|
36
|
+
abort "hecks list_io_parallel_specs: #{mode} needs a file path" if mode && list_path.nil?
|
|
37
|
+
|
|
38
|
+
exclude_arg, tag_args = parse(argv)
|
|
39
|
+
candidates = Dir.glob("spec/**/*_spec.rb", base: root).grep_v(Regexp.new(exclude_arg)).sort
|
|
40
|
+
refuse_empty_candidates(candidates)
|
|
41
|
+
files, example_count = matching_files(candidates, tag_args, root, err)
|
|
42
|
+
err.puts "hecks list_io_parallel_specs: #{files.size} of #{candidates.size} candidate files carry a " \
|
|
43
|
+
"matching example (#{example_count} examples total) under `#{tag_args.join(' ')}`"
|
|
44
|
+
|
|
45
|
+
case mode
|
|
46
|
+
when "--write" then File.write(File.expand_path(list_path, root), "#{files.join("\n")}\n")
|
|
47
|
+
when "--check" then check(files, File.expand_path(list_path, root), list_path, exclude_arg, tag_args, err)
|
|
48
|
+
else out.puts files
|
|
49
|
+
end
|
|
50
|
+
0
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @param argv [Array<String>] the exclude pattern, `--`, and the tag arguments
|
|
54
|
+
# @return [Array(String, Array<String>)] the pattern and the tag arguments
|
|
55
|
+
# @raise [SystemExit] with the usage line when either is missing
|
|
56
|
+
def parse(argv)
|
|
57
|
+
exclude_arg, *rest = argv
|
|
58
|
+
separator = rest.index("--")
|
|
59
|
+
usage = "usage: hecks list_io_parallel_specs '<exclude-pattern regex>' -- <rspec tag args...>"
|
|
60
|
+
abort usage unless exclude_arg && separator
|
|
61
|
+
tag_args = rest[(separator + 1)..]
|
|
62
|
+
abort usage if tag_args.nil? || tag_args.empty?
|
|
63
|
+
|
|
64
|
+
[exclude_arg, tag_args]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @param candidates [Array<String>] the spec files left after the exclude pattern
|
|
68
|
+
# @return [void]
|
|
69
|
+
# @raise [SystemExit] when there are none, which means a broken pattern or an empty checkout
|
|
70
|
+
def refuse_empty_candidates(candidates)
|
|
71
|
+
return unless candidates.empty?
|
|
72
|
+
|
|
73
|
+
abort "hecks list_io_parallel_specs: found ZERO candidate spec files after applying the exclude " \
|
|
74
|
+
"pattern — that's almost certainly a broken pattern or an empty checkout, not a real empty " \
|
|
75
|
+
"test suite. Refusing to silently hand parallel_rspec nothing to run."
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Asks RSpec for a dry run of the candidates under the tag filter.
|
|
79
|
+
#
|
|
80
|
+
# @param candidates [Array<String>] the spec files to ask about
|
|
81
|
+
# @param tag_args [Array<String>] the RSpec tag arguments
|
|
82
|
+
# @param root [String] the directory the dry run runs in
|
|
83
|
+
# @param err [IO] where a failed run's stderr goes
|
|
84
|
+
# @return [Array(Array<String>, Integer)] the files with a matching example, and how many
|
|
85
|
+
# examples matched
|
|
86
|
+
# @raise [SystemExit] when the dry run fails, prints no JSON, or matches nothing
|
|
87
|
+
def matching_files(candidates, tag_args, root, err)
|
|
88
|
+
command = ["bundle", "exec", "rspec", "--dry-run", *tag_args, "--format", "json", *candidates]
|
|
89
|
+
stdout, stderr, status = Open3.capture3(*command, chdir: root)
|
|
90
|
+
unless status.success?
|
|
91
|
+
err.puts stderr
|
|
92
|
+
abort "hecks list_io_parallel_specs: `#{command.join(' ')}` exited #{status.exitstatus} — " \
|
|
93
|
+
"see stderr above. Refusing to guess a file list from a failed dry run."
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
start = stdout.index('{"version"')
|
|
97
|
+
abort "hecks list_io_parallel_specs: couldn't find RSpec's JSON output in the dry-run's stdout:\n#{stdout}" unless start
|
|
98
|
+
|
|
99
|
+
examples = JSON.parse(stdout[start..])["examples"] || []
|
|
100
|
+
files = examples.map { |e| e.fetch("file_path").sub(%r{\A\./}, "") }.uniq.sort
|
|
101
|
+
return [files, examples.size] unless files.empty?
|
|
102
|
+
|
|
103
|
+
abort "hecks list_io_parallel_specs: #{candidates.size} candidate files, but the dry run matched ZERO " \
|
|
104
|
+
"examples under `#{tag_args.join(' ')}` — that's almost certainly a broken tag filter, not a " \
|
|
105
|
+
"real empty set. Refusing to silently hand parallel_rspec nothing to run."
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# @param files [Array<String>] the files that carry a matching example
|
|
109
|
+
# @param path [String] the committed list's absolute path
|
|
110
|
+
# @param shown [String] the path as the person typed it
|
|
111
|
+
# @param exclude_arg [String] the exclude pattern, for the refresh command
|
|
112
|
+
# @param tag_args [Array<String>] the tag arguments, for the refresh command
|
|
113
|
+
# @param err [IO] where the difference goes
|
|
114
|
+
# @return [void]
|
|
115
|
+
# @raise [SystemExit] when the committed list differs
|
|
116
|
+
def check(files, path, shown, exclude_arg, tag_args, err)
|
|
117
|
+
committed = File.exist?(path) ? File.readlines(path, chomp: true).reject(&:empty?) : []
|
|
118
|
+
missing = files - committed
|
|
119
|
+
stale = committed - files
|
|
120
|
+
return if missing.empty? && stale.empty?
|
|
121
|
+
|
|
122
|
+
err.puts "#{shown} is out of date."
|
|
123
|
+
err.puts named("carries a matching example but is not listed (would never run in the shards)", missing) if missing.any?
|
|
124
|
+
err.puts named("listed but carries no matching example any more", stale) if stale.any?
|
|
125
|
+
abort "Refresh it and commit the result:\n " \
|
|
126
|
+
"hecks list_io_parallel_specs --write #{shown} '#{exclude_arg}' -- #{tag_args.join(' ')}"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# @param heading [String] what the paths have in common
|
|
130
|
+
# @param paths [Array<String>] the paths
|
|
131
|
+
# @return [String] the heading with the paths under it
|
|
132
|
+
def named(heading, paths)
|
|
133
|
+
" #{heading}:\n #{paths.join("\n ")}"
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "parallel_tests/test/runner"
|
|
4
|
+
|
|
5
|
+
module Hecks
|
|
6
|
+
module CLI
|
|
7
|
+
# The command behind `hecks shard_specs`: prints the spec files assigned to one group
|
|
8
|
+
# (1-indexed) of a runtime-balanced N-way split. `rspec_shard` in ci-rspec.yml passes the
|
|
9
|
+
# committed runtime baseline so every leg agrees.
|
|
10
|
+
#
|
|
11
|
+
# It does not use `parallel_rspec --only-group`, which forces one process per leg; this suite
|
|
12
|
+
# is CPU-bound, so each leg would use one of four vCPUs. The group's list comes from
|
|
13
|
+
# `tests_in_groups`, leaving `parallel_rspec` its own workers.
|
|
14
|
+
module RspecShardFiles
|
|
15
|
+
USAGE = "usage: hecks shard_specs <group 1-indexed> <num_groups> [runtime_log_path]"
|
|
16
|
+
|
|
17
|
+
module_function
|
|
18
|
+
|
|
19
|
+
# Prints the files of one group.
|
|
20
|
+
#
|
|
21
|
+
# @param argv [Array<String>] the group, the number of groups, and optionally a runtime log
|
|
22
|
+
# @param root [String] the checkout whose `spec/` is split
|
|
23
|
+
# @param out [IO] where the files go, one per line
|
|
24
|
+
# @param err [IO] where the group's size goes
|
|
25
|
+
# @return [Integer] the exit status, 0 once printed
|
|
26
|
+
# @raise [SystemExit] on missing or out-of-range arguments, or when there are no spec files
|
|
27
|
+
def call(argv, root: Dir.pwd, out: $stdout, err: $stderr)
|
|
28
|
+
group_arg, num_groups_arg, runtime_log = argv
|
|
29
|
+
abort USAGE unless group_arg && num_groups_arg
|
|
30
|
+
|
|
31
|
+
group = Integer(group_arg)
|
|
32
|
+
num_groups = Integer(num_groups_arg)
|
|
33
|
+
abort "group must be between 1 and #{num_groups}, got #{group}" unless (1..num_groups).cover?(group)
|
|
34
|
+
|
|
35
|
+
files = Dir.glob("spec/**/*_spec.rb", base: root)
|
|
36
|
+
if files.empty?
|
|
37
|
+
abort "hecks shard_specs: found ZERO spec files under spec/ — " \
|
|
38
|
+
"refusing to hand parallel_rspec nothing to run"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
groups = Dir.chdir(root) { groups_of(files, num_groups, runtime_log && File.expand_path(runtime_log, root)) }
|
|
42
|
+
selected = groups[group - 1] || []
|
|
43
|
+
err.puts "hecks shard_specs: group #{group}/#{num_groups} has #{selected.size} of #{files.size} files"
|
|
44
|
+
out.puts selected
|
|
45
|
+
0
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Splits the files by recorded runtime, falling back to file size on a cold cache.
|
|
49
|
+
#
|
|
50
|
+
# `allowed_missing_percent` covers gaps inside a log, not a missing file (`Runner.runtimes`
|
|
51
|
+
# raises `Errno::ENOENT`).
|
|
52
|
+
#
|
|
53
|
+
# @param files [Array<String>] the spec files, relative to the checkout
|
|
54
|
+
# @param num_groups [Integer] how many groups to split into
|
|
55
|
+
# @param runtime_log [String, nil] the runtime log's path, used when the file exists
|
|
56
|
+
# @return [Array<Array<String>>] the files of each group
|
|
57
|
+
def groups_of(files, num_groups, runtime_log)
|
|
58
|
+
options = { group_by: :runtime, allowed_missing_percent: 100 }
|
|
59
|
+
options[:runtime_log] = runtime_log if runtime_log && File.exist?(runtime_log)
|
|
60
|
+
ParallelTests::Test::Runner.tests_in_groups(files, num_groups, **options)
|
|
61
|
+
rescue Errno::ENOENT
|
|
62
|
+
ParallelTests::Test::Runner.tests_in_groups(files, num_groups, group_by: :filesize)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
data/lib/hecks/cli/run.rb
CHANGED
|
@@ -6,7 +6,7 @@ require_relative "../ports/persistence/plugins/era"
|
|
|
6
6
|
|
|
7
7
|
module Hecks
|
|
8
8
|
module CLI
|
|
9
|
-
# The command behind `
|
|
9
|
+
# The command behind `hecks run`: dispatches one verb from the
|
|
10
10
|
# command line, or executes a JSON step list and reports instances, events,
|
|
11
11
|
# refusals, reactions, sagas and query rows.
|
|
12
12
|
#
|
|
@@ -55,8 +55,8 @@ module Hecks
|
|
|
55
55
|
# near miss deserves the runner's "did you mean" rather than the step-list
|
|
56
56
|
# form's "no such script".
|
|
57
57
|
def cli_form(domain, argv, program)
|
|
58
|
-
runtime = Hecks.boot(domain,
|
|
59
|
-
text, status =
|
|
58
|
+
runtime = Hecks.boot(domain, install_doors: false)
|
|
59
|
+
text, status = Doors::CliRunner.call(runtime: runtime, argv: argv, program: program)
|
|
60
60
|
status.zero? ? puts(text) : abort(text)
|
|
61
61
|
exit 0
|
|
62
62
|
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require_relative "../../hecks"
|
|
5
|
+
require_relative "../fuzzing"
|
|
6
|
+
|
|
7
|
+
module Hecks
|
|
8
|
+
module CLI
|
|
9
|
+
# The command behind `hecks seed_semantics_corpus`: seeds (or deliberately re-seeds) the
|
|
10
|
+
# `expect` block of semantics-corpus fixtures from the Ruby runtime. Review the result
|
|
11
|
+
# against `docs/semantics/bluebook-semantics.md` before committing.
|
|
12
|
+
#
|
|
13
|
+
# hecks seed_semantics_corpus # fill fixtures missing an expect
|
|
14
|
+
# SEED=refusal_kind_lifecycle hecks seed_semantics_corpus # re-seed one, deliberately
|
|
15
|
+
#
|
|
16
|
+
# The seeded expectation is the definition, not a recording: both runtimes are compared to it.
|
|
17
|
+
# `occurred_at` is stripped from events (C7.3/C9.1); refusals keep verb, error and kind (C8.2).
|
|
18
|
+
module SeedSemanticsCorpus
|
|
19
|
+
module_function
|
|
20
|
+
|
|
21
|
+
# Seeds the fixtures that need it and reports which.
|
|
22
|
+
#
|
|
23
|
+
# @param root [String] the checkout whose `spec/corpus/semantics/` is seeded
|
|
24
|
+
# @param env [Hash{String => String}] `SEED` names the one fixture to re-seed
|
|
25
|
+
# @param out [IO] where the report goes
|
|
26
|
+
# @return [Integer] the exit status, always 0
|
|
27
|
+
def call(root:, env: ENV, out: $stdout)
|
|
28
|
+
only = env.fetch("SEED", nil)
|
|
29
|
+
seeded = Dir.glob(File.join(root, "spec/corpus/semantics", "*.json")).filter_map do |path|
|
|
30
|
+
fixture = JSON.parse(File.read(path))
|
|
31
|
+
name = File.basename(path, ".json")
|
|
32
|
+
next if only ? only != name : fixture.key?("expect")
|
|
33
|
+
|
|
34
|
+
fixture["expect"] = expectation_for(root, fixture)
|
|
35
|
+
File.write(path, "#{JSON.pretty_generate(fixture)}\n")
|
|
36
|
+
name
|
|
37
|
+
end
|
|
38
|
+
report(out, seeded)
|
|
39
|
+
0
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Replays the fixture's script against the Ruby runtime. Each refusal's `"kind"` is reduced
|
|
43
|
+
# to its class name; `occurred_at` is stripped from events.
|
|
44
|
+
#
|
|
45
|
+
# @param root [String] the checkout the fixture's domain path is relative to
|
|
46
|
+
# @param fixture [Hash] the parsed fixture: `domain` and `steps`
|
|
47
|
+
# @return [Hash{String => Array}] its `"expect"` block: refusals, instances, events
|
|
48
|
+
def expectation_for(root, fixture)
|
|
49
|
+
result = Hecks::Fuzzing::Replay.call(File.join(root, fixture["domain"]), fixture["steps"])
|
|
50
|
+
{
|
|
51
|
+
"refusals" => JSON.parse(JSON.generate(result[:refusals])).each { |r| r["kind"] = r["kind"]&.split("::")&.last },
|
|
52
|
+
"instances" => JSON.parse(JSON.generate(result[:instances])),
|
|
53
|
+
"events" => JSON.parse(JSON.generate(result[:events])).each { |e| e.delete("occurred_at") }
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# @param out [IO] where the report goes
|
|
58
|
+
# @param seeded [Array<String>] the fixtures written
|
|
59
|
+
# @return [void]
|
|
60
|
+
def report(out, seeded)
|
|
61
|
+
if seeded.empty?
|
|
62
|
+
out.puts "nothing to seed — every fixture carries its expect (pass SEED=<name> to re-seed one deliberately)"
|
|
63
|
+
else
|
|
64
|
+
out.puts "seeded: #{seeded.join(', ')}"
|
|
65
|
+
out.puts "review each expect against docs/semantics/bluebook-semantics.md before committing — " \
|
|
66
|
+
"it is the definition now"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../mcp_stdio_guard"
|
|
4
|
+
|
|
5
|
+
module Hecks
|
|
6
|
+
module CLI
|
|
7
|
+
# The command behind `hecks serve_query_ir_mcp` and `hecks serve_query_ir_mcp`: an MCP server
|
|
8
|
+
# exposing `Hecks::QueryIR`'s queries as tools, over newline-delimited JSON-RPC on stdio.
|
|
9
|
+
#
|
|
10
|
+
# Stdio only and unauthenticated (ADR 0062); the protocol lives in `Hecks::QueryIrMcp` and the
|
|
11
|
+
# query logic in `Hecks::QueryIR`. The stdio guard runs before the runtime loads, so a refused
|
|
12
|
+
# transport never boots anything.
|
|
13
|
+
module ServeQueryIrMcp
|
|
14
|
+
module_function
|
|
15
|
+
|
|
16
|
+
# Enforces stdio, then serves until stdin closes.
|
|
17
|
+
#
|
|
18
|
+
# @param argv [Array<String>] the arguments the server was started with
|
|
19
|
+
# @return [void]
|
|
20
|
+
# @raise [SystemExit] when the guard refuses the transport
|
|
21
|
+
def call(argv)
|
|
22
|
+
Hecks::McpStdioGuard.enforce_stdio!(server: "hecks-query-ir")
|
|
23
|
+
|
|
24
|
+
require_relative "../../hecks"
|
|
25
|
+
require_relative "../query_ir_mcp"
|
|
26
|
+
Hecks::QueryIrMcp.start(argv: argv)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|