pi-dev-team 0.2.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.
- package/LICENSE +21 -0
- package/PORTING.md +134 -0
- package/README.md +207 -0
- package/UPSTREAM.json +64 -0
- package/agents/Explore.md +15 -0
- package/agents/a11y-review.md +118 -0
- package/agents/adr-author.md +70 -0
- package/agents/ai-provenance-review.md +120 -0
- package/agents/angular-reactivity-review.md +95 -0
- package/agents/arch-review.md +135 -0
- package/agents/architect.md +78 -0
- package/agents/autoship-batch-proposer.md +69 -0
- package/agents/claude-setup-review.md +136 -0
- package/agents/codebase-recon.md +184 -0
- package/agents/component-architecture-review.md +119 -0
- package/agents/concurrency-review.md +109 -0
- package/agents/correctness-review.md +290 -0
- package/agents/data-flow-tracer.md +120 -0
- package/agents/doc-review.md +165 -0
- package/agents/domain-review.md +136 -0
- package/agents/general-purpose.md +10 -0
- package/agents/gherkin-quality-critic.md +113 -0
- package/agents/js-fp-review.md +114 -0
- package/agents/mutation-kill.md +684 -0
- package/agents/naming-review.md +142 -0
- package/agents/orchestrator.md +339 -0
- package/agents/performance-review.md +105 -0
- package/agents/plan-review-acceptance.md +115 -0
- package/agents/plan-review-design.md +90 -0
- package/agents/plan-review-parallelization.md +84 -0
- package/agents/plan-review-strategic.md +96 -0
- package/agents/plan-review-ux.md +110 -0
- package/agents/platform-engineer.md +64 -0
- package/agents/product-manager.md +68 -0
- package/agents/progress-guardian.md +79 -0
- package/agents/qa-engineer.md +289 -0
- package/agents/quality-reviewer.md +132 -0
- package/agents/react-reactivity-review.md +102 -0
- package/agents/refactor-opportunity-review.md +128 -0
- package/agents/security-engineer.md +60 -0
- package/agents/security-review.md +218 -0
- package/agents/session-analysis.md +95 -0
- package/agents/software-engineer.md +105 -0
- package/agents/spec-compliance-review.md +100 -0
- package/agents/spec-reviewer.md +114 -0
- package/agents/structure-review.md +146 -0
- package/agents/tech-writer.md +84 -0
- package/agents/test-review.md +246 -0
- package/agents/test-smell-review.md +188 -0
- package/agents/token-efficiency-review.md +139 -0
- package/agents/ui-ux-designer.md +54 -0
- package/agents/vue-reactivity-review.md +95 -0
- package/bin/__pycache__/claudecpython-314.pyc +0 -0
- package/bin/claude +258 -0
- package/docs/upstream/.pages +1 -0
- package/docs/upstream/CHANGELOG.md +2586 -0
- package/docs/upstream/README.md +155 -0
- package/docs/upstream/agent-architecture.md +214 -0
- package/docs/upstream/agent_info.md +187 -0
- package/docs/upstream/artifact-migration.md +124 -0
- package/docs/upstream/code-intelligence-nudge.md +149 -0
- package/docs/upstream/code-review-process.md +294 -0
- package/docs/upstream/concurrent-use.md +73 -0
- package/docs/upstream/context-management.md +111 -0
- package/docs/upstream/developer-notes.md +280 -0
- package/docs/upstream/diagrams/architecture-overview.svg +101 -0
- package/docs/upstream/diagrams/review-dispatch.svg +139 -0
- package/docs/upstream/diagrams/team-agents.svg +128 -0
- package/docs/upstream/diagrams/test-improve-flow.svg +166 -0
- package/docs/upstream/diagrams/workflow-linear.svg +66 -0
- package/docs/upstream/diagrams/workflow-three-phase.svg +200 -0
- package/docs/upstream/eval-maintenance.md +95 -0
- package/docs/upstream/eval-running-guide.md +147 -0
- package/docs/upstream/eval-system.md +291 -0
- package/docs/upstream/session-review-oss-complements.md +75 -0
- package/docs/upstream/session-review.md +212 -0
- package/docs/upstream/skills.md +188 -0
- package/docs/upstream/team-structure.md +21 -0
- package/docs/upstream/telemetry-ci-access.md +129 -0
- package/docs/upstream/telemetry-repo-security.md +120 -0
- package/docs/upstream/test-evaluation.md +277 -0
- package/docs/upstream/test-improve.md +154 -0
- package/docs/upstream/triage-workflow.md +282 -0
- package/docs/upstream/workflows.md +289 -0
- package/extensions/dev-team/index.ts +539 -0
- package/extensions/dev-team/lib/agents.ts +272 -0
- package/extensions/dev-team/lib/ai-credits.ts +92 -0
- package/extensions/dev-team/lib/autocompact.ts +81 -0
- package/extensions/dev-team/lib/child-run.ts +102 -0
- package/extensions/dev-team/lib/config.ts +236 -0
- package/extensions/dev-team/lib/gh-command.ts +103 -0
- package/extensions/dev-team/lib/github-style.ts +307 -0
- package/extensions/dev-team/lib/hooks.ts +350 -0
- package/extensions/dev-team/lib/metrics.ts +115 -0
- package/extensions/dev-team/lib/safe-read.ts +49 -0
- package/extensions/dev-team/lib/session-files.ts +57 -0
- package/extensions/dev-team/lib/session-spend.ts +123 -0
- package/extensions/dev-team/lib/shell-scan.ts +205 -0
- package/extensions/dev-team/lib/skills.ts +213 -0
- package/extensions/dev-team/lib/subagent-render.ts +245 -0
- package/extensions/dev-team/lib/subagent-types.ts +164 -0
- package/extensions/dev-team/lib/subagent.ts +596 -0
- package/extensions/dev-team/lib/terminal-text.ts +54 -0
- package/extensions/dev-team/lib/tools-misc.ts +152 -0
- package/extensions/dev-team/lib/transcript.ts +110 -0
- package/extensions/dev-team/lib/trust.ts +52 -0
- package/extensions/dev-team/lib/usage-breakdown.ts +176 -0
- package/extensions/dev-team/lib/usage-chart.ts +153 -0
- package/extensions/dev-team/lib/usage-command.ts +107 -0
- package/extensions/dev-team/lib/usage-history.ts +203 -0
- package/extensions/dev-team/lib/usage-render.ts +225 -0
- package/extensions/dev-team/lib/usage-split-bar.ts +127 -0
- package/extensions/dev-team/lib/usage-state.ts +116 -0
- package/extensions/dev-team/lib/usage-text.ts +159 -0
- package/extensions/dev-team/lib/usage-view.ts +109 -0
- package/hooks/__pycache__/refactor_test_freeze_guard.cpython-314.pyc +0 -0
- package/hooks/agent_dispatch_ledger.py +190 -0
- package/hooks/autocompact_setup_nudge.py +99 -0
- package/hooks/bash_retry_guard.py +228 -0
- package/hooks/boundary_events_write_guard.py +352 -0
- package/hooks/code_intelligence_nudge.py +293 -0
- package/hooks/code_intelligence_turn_mark.py +317 -0
- package/hooks/codegraph_bootstrap.py +139 -0
- package/hooks/contract_version_guard.py +362 -0
- package/hooks/cost_meter.py +106 -0
- package/hooks/destructive-commands.json +62 -0
- package/hooks/destructive_guard.py +477 -0
- package/hooks/eval_compliance_check.py +440 -0
- package/hooks/guards.json +17 -0
- package/hooks/hooks.json +323 -0
- package/hooks/internal_double_gate.py +296 -0
- package/hooks/js_fp_review.py +212 -0
- package/hooks/knowledge_index.py +119 -0
- package/hooks/lib/__pycache__/artifact_paths.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/atomic_state.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/autocompact_config.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/boundary_events.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/doc_classification.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/gh_pr_create_detect.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/git_safe_diff.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/instrument_log.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/metrics_query.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/plugin_version.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/pre_commit_doc_classifier.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/review_agent_registry.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/review_gate_corroboration.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/review_gate_hash.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/review_verdicts.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/stdin_json.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/stryker_invocation.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/telemetry_consent.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/test_file_classify.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/token_efficiency_limits.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/verify_guard_state.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/xunit_v3_operator_gate.cpython-314.pyc +0 -0
- package/hooks/lib/agent_skill_hints.py +74 -0
- package/hooks/lib/artifact_paths.py +263 -0
- package/hooks/lib/atomic_state.py +557 -0
- package/hooks/lib/autocompact_config.py +103 -0
- package/hooks/lib/autoship_log.py +106 -0
- package/hooks/lib/banned_scripts_policy.py +51 -0
- package/hooks/lib/boundary_events.py +436 -0
- package/hooks/lib/build_knowledge_index.py +504 -0
- package/hooks/lib/build_skills_index.py +361 -0
- package/hooks/lib/build_state.py +116 -0
- package/hooks/lib/classify_ship_outcome.py +126 -0
- package/hooks/lib/config_changelog_schema.py +115 -0
- package/hooks/lib/cost_meter.py +955 -0
- package/hooks/lib/doc_classification.py +116 -0
- package/hooks/lib/gh_pr_create_detect.py +136 -0
- package/hooks/lib/git_safe_diff.py +123 -0
- package/hooks/lib/instrument_log.py +66 -0
- package/hooks/lib/iteration_journal_gate.py +197 -0
- package/hooks/lib/knowledge_index_paths.py +88 -0
- package/hooks/lib/mcp_json_repowise.py +177 -0
- package/hooks/lib/metrics_query.py +202 -0
- package/hooks/lib/minimal_yaml.py +434 -0
- package/hooks/lib/plugin_version.py +142 -0
- package/hooks/lib/pre_commit_detect.py +537 -0
- package/hooks/lib/pre_commit_doc_classifier.py +126 -0
- package/hooks/lib/pricing.py +118 -0
- package/hooks/lib/report_pdf.py +371 -0
- package/hooks/lib/review_agent_registry.py +142 -0
- package/hooks/lib/review_dispatch_ledger.py +101 -0
- package/hooks/lib/review_gate_corroboration.py +521 -0
- package/hooks/lib/review_gate_hash.py +252 -0
- package/hooks/lib/review_gate_normalized_hash.py +1115 -0
- package/hooks/lib/review_verdicts.py +301 -0
- package/hooks/lib/run_report.py +160 -0
- package/hooks/lib/skill_categories.yaml +125 -0
- package/hooks/lib/stdin_json.py +57 -0
- package/hooks/lib/stryker_invocation.py +102 -0
- package/hooks/lib/telemetry_consent.py +41 -0
- package/hooks/lib/telemetry_report.py +108 -0
- package/hooks/lib/test_file_classify.py +160 -0
- package/hooks/lib/token_efficiency_limits.py +51 -0
- package/hooks/lib/turn_identity.py +77 -0
- package/hooks/lib/verify_guard_state.py +110 -0
- package/hooks/lib/workflow_state.py +206 -0
- package/hooks/lib/xunit_v3_operator_gate.py +596 -0
- package/hooks/mcp_json_repowise_nudge.py +74 -0
- package/hooks/mutation_adapters/__init__.py +7 -0
- package/hooks/mutation_adapters/__pycache__/__init__.cpython-314.pyc +0 -0
- package/hooks/mutation_adapters/__pycache__/lib.cpython-314.pyc +0 -0
- package/hooks/mutation_adapters/__pycache__/mutmut.cpython-314.pyc +0 -0
- package/hooks/mutation_adapters/__pycache__/pitest.cpython-314.pyc +0 -0
- package/hooks/mutation_adapters/__pycache__/stryker.cpython-314.pyc +0 -0
- package/hooks/mutation_adapters/__pycache__/stryker_net.cpython-314.pyc +0 -0
- package/hooks/mutation_adapters/lib.py +478 -0
- package/hooks/mutation_adapters/mutmut.py +188 -0
- package/hooks/mutation_adapters/pitest.py +266 -0
- package/hooks/mutation_adapters/stryker.py +157 -0
- package/hooks/mutation_adapters/stryker_net.py +264 -0
- package/hooks/mutation_gate.py +193 -0
- package/hooks/mutation_testing_smoke_gate.py +371 -0
- package/hooks/pending_review_notify.py +121 -0
- package/hooks/phase_marker.py +138 -0
- package/hooks/post_compact_state_reinject.py +180 -0
- package/hooks/post_format.py +115 -0
- package/hooks/pre_commit_knowledge_index.py +128 -0
- package/hooks/pre_commit_review.py +66 -0
- package/hooks/pre_pr_review.py +694 -0
- package/hooks/pre_tool_guard.py +405 -0
- package/hooks/py.sh +73 -0
- package/hooks/refactor-bash-write-patterns.json +29 -0
- package/hooks/refactor_test_bash_guard.py +253 -0
- package/hooks/refactor_test_freeze_guard.py +139 -0
- package/hooks/refactor_test_revert_guard.py +186 -0
- package/hooks/repo_review_nudge.py +287 -0
- package/hooks/review_verdict_recorder.py +464 -0
- package/hooks/scan_bash_command_for_banned_scripts.py +428 -0
- package/hooks/scan_worktree_for_banned_scripts.py +238 -0
- package/hooks/session_learning_trigger.py +248 -0
- package/hooks/skills_index.py +126 -0
- package/hooks/stryker_xunit_shim_guard.py +571 -0
- package/hooks/subagent_completion_guard.py +309 -0
- package/hooks/subagent_skill_context.py +139 -0
- package/hooks/task_completion_metrics.py +216 -0
- package/hooks/tdd_guard.py +229 -0
- package/hooks/telemetry.py +341 -0
- package/hooks/token_efficiency_review.py +194 -0
- package/hooks/verify_guard.py +183 -0
- package/hooks/verify_guard_edit_marker.py +73 -0
- package/hooks/version_check.py +173 -0
- package/knowledge/accepted-risks-schema.md +98 -0
- package/knowledge/adr-decision-criteria.md +64 -0
- package/knowledge/adversarial-review-protocol.md +139 -0
- package/knowledge/agent-registry.md +228 -0
- package/knowledge/agent-review-methodology.md +80 -0
- package/knowledge/ai-friendly-repo-guidelines.md +67 -0
- package/knowledge/architecture-assessment.md +96 -0
- package/knowledge/artifact-lifecycle.md +57 -0
- package/knowledge/cd-maturity-model.md +82 -0
- package/knowledge/cd-test-architecture.md +190 -0
- package/knowledge/ci-cd-file-scope.md +24 -0
- package/knowledge/codegraph-vs-graphify.md +192 -0
- package/knowledge/component-test-patterns.md +139 -0
- package/knowledge/database-change-management.md +80 -0
- package/knowledge/database-test-patterns.md +79 -0
- package/knowledge/decision-defaults.md +88 -0
- package/knowledge/dependency-breaking-techniques.md +116 -0
- package/knowledge/deployment-pipeline.md +86 -0
- package/knowledge/design-smells.md +122 -0
- package/knowledge/directory-enumeration.md +38 -0
- package/knowledge/domain-modeling.md +123 -0
- package/knowledge/evidence-bundle.md +90 -0
- package/knowledge/exploratory-testing-field-guide.md +122 -0
- package/knowledge/failure-routing.md +28 -0
- package/knowledge/fixture-construction.md +56 -0
- package/knowledge/frontend-component-architecture.md +139 -0
- package/knowledge/gherkin-quality-review-dispatch.md +135 -0
- package/knowledge/index.json +6766 -0
- package/knowledge/internal-collaborator-doubling.md +101 -0
- package/knowledge/legacy-test-strategy.md +71 -0
- package/knowledge/long-run-waiting.md +66 -0
- package/knowledge/microservice-testing.md +71 -0
- package/knowledge/model-pricing.json +23 -0
- package/knowledge/mutation-score-formulas.md +60 -0
- package/knowledge/object-calisthenics.md +147 -0
- package/knowledge/oracle-provenance.md +94 -0
- package/knowledge/orchestrator-script-implementation.md +185 -0
- package/knowledge/owasp-detection.md +148 -0
- package/knowledge/plan-review-rubric.md +56 -0
- package/knowledge/proxy-connectivity.md +62 -0
- package/knowledge/reactive-effect-patterns.md +73 -0
- package/knowledge/recon-inventory-excludes.txt +32 -0
- package/knowledge/references/bdd-value-guide.md +61 -0
- package/knowledge/references/csharp-http-client-testing.md +264 -0
- package/knowledge/release-strategies.md +74 -0
- package/knowledge/report-output-location.md +117 -0
- package/knowledge/report-pdf-integration.md +63 -0
- package/knowledge/report-print.css +129 -0
- package/knowledge/report-template.md +114 -0
- package/knowledge/report-to-pdf.md +69 -0
- package/knowledge/request-processing-flow.md +63 -0
- package/knowledge/result-verification.md +52 -0
- package/knowledge/review-agent-output-contract.md +121 -0
- package/knowledge/review-lens-classification.md +113 -0
- package/knowledge/review-rubric.md +62 -0
- package/knowledge/review-template.md +104 -0
- package/knowledge/rule-fixtures/A02.insecure-random-js/negative.js +1 -0
- package/knowledge/rule-fixtures/A02.insecure-random-js/positive.js +1 -0
- package/knowledge/rule-fixtures/A02.weak-hashing-md5/negative.py +1 -0
- package/knowledge/rule-fixtures/A02.weak-hashing-md5/positive.py +1 -0
- package/knowledge/rule-fixtures/A03.command-injection/negative.js +1 -0
- package/knowledge/rule-fixtures/A03.command-injection/positive.js +1 -0
- package/knowledge/rule-fixtures/A03.sql-injection/negative.js +1 -0
- package/knowledge/rule-fixtures/A03.sql-injection/positive.js +1 -0
- package/knowledge/rule-fixtures/A03.xss-innerhtml/negative.js +1 -0
- package/knowledge/rule-fixtures/A03.xss-innerhtml/positive.js +1 -0
- package/knowledge/rule-fixtures/A05.cors-wildcard/negative.js +1 -0
- package/knowledge/rule-fixtures/A05.cors-wildcard/positive.js +1 -0
- package/knowledge/rule-fixtures/A05.default-credentials/negative.js +1 -0
- package/knowledge/rule-fixtures/A05.default-credentials/positive.js +1 -0
- package/knowledge/rule-fixtures/A07.jwt-alg-none/negative.js +1 -0
- package/knowledge/rule-fixtures/A07.jwt-alg-none/positive.js +1 -0
- package/knowledge/rule-fixtures/A08.binary-formatter/negative.cs +1 -0
- package/knowledge/rule-fixtures/A08.binary-formatter/positive.cs +1 -0
- package/knowledge/rule-fixtures/A08.js-eval/negative.js +1 -0
- package/knowledge/rule-fixtures/A08.js-eval/positive.js +1 -0
- package/knowledge/rule-fixtures/A08.object-input-stream/negative.java +1 -0
- package/knowledge/rule-fixtures/A08.object-input-stream/positive.java +1 -0
- package/knowledge/schemas/disposition-register-v1.json +65 -0
- package/knowledge/schemas/recon-envelope-v1.json +198 -0
- package/knowledge/schemas/unified-finding-v1.json +72 -0
- package/knowledge/security-primitives-contract.md +301 -0
- package/knowledge/security-review-rule-map.yaml +107 -0
- package/knowledge/skills-registry.md +72 -0
- package/knowledge/task-size-classifier.md +103 -0
- package/knowledge/telemetry-schema.md +881 -0
- package/knowledge/test-automation-maturity.md +56 -0
- package/knowledge/test-automation-principles.md +71 -0
- package/knowledge/test-cadence-tradeoffs.md +68 -0
- package/knowledge/test-doubles.md +105 -0
- package/knowledge/test-file-indicators.md +22 -0
- package/knowledge/test-layer-gates.md +35 -0
- package/knowledge/test-matrix-examples/django-batch.md +24 -0
- package/knowledge/test-matrix-examples/dotnet-grpc-fronting-api.md +90 -0
- package/knowledge/test-matrix-examples/dotnet-http-consumer.md +131 -0
- package/knowledge/test-matrix-examples/react-node-spa.md +24 -0
- package/knowledge/test-matrix-examples/spring-boot-service.md +25 -0
- package/knowledge/test-matrix-examples/ssr-htmx.md +24 -0
- package/knowledge/test-organization.md +70 -0
- package/knowledge/test-pyramid.md +84 -0
- package/knowledge/test-refactoring.md +67 -0
- package/knowledge/test-review-division-of-labor.md +85 -0
- package/knowledge/test-smells.md +80 -0
- package/knowledge/test-stack-profiles/bdd-frameworks.md +235 -0
- package/knowledge/test-stack-profiles/django.md +13 -0
- package/knowledge/test-stack-profiles/dotnet.md +18 -0
- package/knowledge/test-stack-profiles/go.md +16 -0
- package/knowledge/test-stack-profiles/node.md +16 -0
- package/knowledge/test-stack-profiles/react.md +12 -0
- package/knowledge/test-stack-profiles/spring-boot.md +16 -0
- package/knowledge/test-stack-profiles/ssr-htmx.md +14 -0
- package/knowledge/test-stack-profiles/vue.md +12 -0
- package/knowledge/test-strategy.md +70 -0
- package/knowledge/testability-patterns.md +240 -0
- package/knowledge/testing-quadrants.md +44 -0
- package/knowledge/testing-techniques/approval.md +15 -0
- package/knowledge/testing-techniques/chaos.md +17 -0
- package/knowledge/testing-techniques/fuzz.md +15 -0
- package/knowledge/testing-techniques/property-based.md +15 -0
- package/knowledge/testing-techniques/schema-validation.md +15 -0
- package/knowledge/testing-techniques/screenshot.md +15 -0
- package/knowledge/three-phase-workflow.md +198 -0
- package/knowledge/value-patterns.md +55 -0
- package/knowledge/verification-mode.md +116 -0
- package/knowledge/virtual-service-libraries.md +75 -0
- package/knowledge/wave-consolidation-guidance.md +21 -0
- package/overrides/agents/Explore.md +15 -0
- package/overrides/agents/general-purpose.md +10 -0
- package/overrides/notes/autoship.md +6 -0
- package/overrides/notes/issues-from-assessment.md +3 -0
- package/overrides/notes/issues-from-plan.md +3 -0
- package/overrides/notes/mutation-night-watch.md +3 -0
- package/overrides/notes/mutation-testing.md +3 -0
- package/overrides/notes/pr.md +7 -0
- package/overrides/notes/project-init.md +6 -0
- package/overrides/notes/setup.md +13 -0
- package/overrides/notes/specs.md +3 -0
- package/overrides/skills/headless-run/SKILL.md +45 -0
- package/overrides/skills/upgrade/SKILL.md +30 -0
- package/overrides/skills/version/SKILL.md +25 -0
- package/package.json +36 -0
- package/scripts/authoring_digest.py +93 -0
- package/scripts/autoship_discover.py +121 -0
- package/scripts/autoship_group.py +409 -0
- package/scripts/autoship_proposals.py +494 -0
- package/scripts/autoship_queue.py +291 -0
- package/scripts/autoship_reclaim.py +495 -0
- package/scripts/build_jobs.py +108 -0
- package/scripts/build_rollback_point.py +240 -0
- package/scripts/build_slice_scope.py +157 -0
- package/scripts/build_wave.py +109 -0
- package/scripts/build_wave_reconcile.py +252 -0
- package/scripts/build_worktree_baseref.py +113 -0
- package/scripts/check_agent_scope.py +117 -0
- package/scripts/check_agent_tool_mapping.py +213 -0
- package/scripts/check_review_agent_mcp_tools.py +317 -0
- package/scripts/check_security_assessment_mcp_tools.py +165 -0
- package/scripts/checkpoint_abort.py +502 -0
- package/scripts/claude_setup_review.py +438 -0
- package/scripts/codebase_recon.py +556 -0
- package/scripts/coverage_config.py +623 -0
- package/scripts/coverage_delta_steering.py +330 -0
- package/scripts/coverage_discovery_dotnet.py +315 -0
- package/scripts/coverage_discovery_java.py +742 -0
- package/scripts/coverage_discovery_js.py +546 -0
- package/scripts/coverage_gap_ranking.py +556 -0
- package/scripts/coverage_readiness.py +455 -0
- package/scripts/coverage_report_parse.py +521 -0
- package/scripts/detect_bdd_convention.py +252 -0
- package/scripts/eval_ablation.py +376 -0
- package/scripts/gherkin_analysis_coverage_gate.py +306 -0
- package/scripts/gherkin_cross_feature_duplicate_titles_gate.py +173 -0
- package/scripts/gherkin_effectiveness_rollup.py +238 -0
- package/scripts/gherkin_failure_path_gate.py +206 -0
- package/scripts/gherkin_feature_merge.py +720 -0
- package/scripts/gherkin_stub_gate.py +163 -0
- package/scripts/gherkin_stub_merge.py +479 -0
- package/scripts/git_origin_host.py +88 -0
- package/scripts/install-java-static-analysis.py +110 -0
- package/scripts/issue_deps.py +74 -0
- package/scripts/lib/_bdd_markers.py +28 -0
- package/scripts/lib/_gherkin_text.py +93 -0
- package/scripts/lib/_vendored_tree.py +70 -0
- package/scripts/lib/autoship_state.py +397 -0
- package/scripts/lib/claude_md_guard.py +226 -0
- package/scripts/lib/deterministic_recon.py +446 -0
- package/scripts/lib/mcp_tool_grants.py +211 -0
- package/scripts/lib/plan_parse.py +386 -0
- package/scripts/lib/review_result.py +84 -0
- package/scripts/lib/review_roster.py +86 -0
- package/scripts/lib/session_log/__init__.py +34 -0
- package/scripts/lib/session_log/__pycache__/__init__.cpython-314.pyc +0 -0
- package/scripts/lib/session_log/__pycache__/records.cpython-314.pyc +0 -0
- package/scripts/lib/session_log/classify.py +231 -0
- package/scripts/lib/session_log/corrections.py +194 -0
- package/scripts/lib/session_log/discovery.py +108 -0
- package/scripts/lib/session_log/records.py +218 -0
- package/scripts/lib/session_log/redact.py +76 -0
- package/scripts/lib/session_log/signals.py +373 -0
- package/scripts/lib/session_report_downstream.py +614 -0
- package/scripts/lib/session_report_maintainer.py +1273 -0
- package/scripts/lib/session_report_shared.py +262 -0
- package/scripts/lib/settings_hook_guard.py +157 -0
- package/scripts/lib/slug.py +33 -0
- package/scripts/lib/stub_extractors/__init__.py +82 -0
- package/scripts/lib/stub_extractors/_common.py +328 -0
- package/scripts/lib/stub_extractors/csharp.py +19 -0
- package/scripts/lib/stub_extractors/go.py +173 -0
- package/scripts/lib/stub_extractors/java.py +18 -0
- package/scripts/lib/stub_extractors/jsts.py +126 -0
- package/scripts/mutation_stack_sections.py +149 -0
- package/scripts/mutation_yield_steering.py +345 -0
- package/scripts/orchestrator.py +895 -0
- package/scripts/plan_gherkin_export.py +227 -0
- package/scripts/plan_waves.py +208 -0
- package/scripts/pr_close_keyword_lint.py +108 -0
- package/scripts/progress_guardian.py +888 -0
- package/scripts/recon_inventory.py +273 -0
- package/scripts/review_findings_log.py +93 -0
- package/scripts/run_invariants.py +124 -0
- package/scripts/select_lenses.py +640 -0
- package/scripts/session_report.py +486 -0
- package/scripts/set_autocompact_env.py +221 -0
- package/scripts/ship_resume_guard.py +135 -0
- package/scripts/ship_review_gate.py +63 -0
- package/scripts/specs_convention_marker.py +103 -0
- package/scripts/test_improve_resume.py +277 -0
- package/scripts/test_review_mechanics.py +958 -0
- package/scripts/token_efficiency_review.py +322 -0
- package/scripts/verdict_scope.py +285 -0
- package/scripts/verify_gherkin_quality_critic_isolation.py +296 -0
- package/scripts/verify_tier.py +157 -0
- package/skills/adr-tools/SKILL.md +118 -0
- package/skills/agent-readiness/SKILL.md +105 -0
- package/skills/agent-readiness/ai_friendly_analyzers.py +326 -0
- package/skills/agent-readiness/scanner.py +441 -0
- package/skills/agent-readiness/scorecard.yaml +88 -0
- package/skills/api-design/SKILL.md +115 -0
- package/skills/apply-fixes/SKILL.md +171 -0
- package/skills/apply-test-doubles/SKILL.md +321 -0
- package/skills/artifact-lifecycle/SKILL.md +127 -0
- package/skills/autoship/SKILL.md +1124 -0
- package/skills/benchmark/SKILL.md +105 -0
- package/skills/branch-workflow/SKILL.md +89 -0
- package/skills/browse/SKILL.md +184 -0
- package/skills/browser-testing/SKILL.md +62 -0
- package/skills/browser-testing/references/playwright-patterns.md +216 -0
- package/skills/build/SKILL.md +422 -0
- package/skills/build/references/static-self-heal.md +245 -0
- package/skills/careful/SKILL.md +72 -0
- package/skills/cd-test-architecture/SKILL.md +371 -0
- package/skills/ci-debugging/SKILL.md +105 -0
- package/skills/co-evolution-audit/SKILL.md +269 -0
- package/skills/code-review/SKILL.md +1015 -0
- package/skills/code-review/examples/aggregated-sample.json +56 -0
- package/skills/code-review/examples/sample-report.md +41 -0
- package/skills/code-review/output-format.md +478 -0
- package/skills/code-review/scripts/activation.py +86 -0
- package/skills/code-review/scripts/change_impact.py +357 -0
- package/skills/code-review/scripts/change_shape.py +372 -0
- package/skills/code-review/scripts/change_size.py +212 -0
- package/skills/code-review/scripts/changed_file_list.py +141 -0
- package/skills/code-review/scripts/closing_pass.py +187 -0
- package/skills/code-review/scripts/consolidate.py +277 -0
- package/skills/code-review/scripts/contract_failure_report.py +185 -0
- package/skills/code-review/scripts/dispatch_reconcile.py +66 -0
- package/skills/code-review/scripts/dispatch_waves.py +164 -0
- package/skills/code-review/scripts/finding_signature.py +446 -0
- package/skills/code-review/scripts/ledger.py +283 -0
- package/skills/code-review/scripts/partition.py +169 -0
- package/skills/code-review/scripts/render_tiered_findings.py +274 -0
- package/skills/code-review/scripts/repo_invariants.py +1066 -0
- package/skills/code-review/scripts/review_context_pack.py +306 -0
- package/skills/code-review/scripts/review_round_log.py +345 -0
- package/skills/code-review/scripts/review_value_coverage.py +297 -0
- package/skills/code-review/scripts/validate_review_output.py +467 -0
- package/skills/code-review/sliced-mode.md +205 -0
- package/skills/competitive-analysis/SKILL.md +191 -0
- package/skills/context-loading-protocol/SKILL.md +157 -0
- package/skills/continue/SKILL.md +90 -0
- package/skills/cost-report/SKILL.md +178 -0
- package/skills/coverage-baseline/SKILL.md +335 -0
- package/skills/coverage-baseline/references/multi-project-discovery.md +202 -0
- package/skills/coverage-delta/SKILL.md +181 -0
- package/skills/coverage-delta/references/mutation-gate.md +70 -0
- package/skills/design-doc/SKILL.md +95 -0
- package/skills/design-interrogation/SKILL.md +89 -0
- package/skills/design-it-twice/SKILL.md +91 -0
- package/skills/docker-image-audit/SKILL.md +108 -0
- package/skills/docker-image-audit/references/install-guide.md +64 -0
- package/skills/docker-image-audit/references/report-template.md +73 -0
- package/skills/docker-image-create/SKILL.md +185 -0
- package/skills/domain-analysis/SKILL.md +183 -0
- package/skills/domain-driven-design/SKILL.md +194 -0
- package/skills/exploratory-testing/SKILL.md +108 -0
- package/skills/explore/SKILL.md +51 -0
- package/skills/farley-score/SKILL.md +165 -0
- package/skills/feature-file-validation/SKILL.md +78 -0
- package/skills/feature-file-validation/references/validation-rules.md +115 -0
- package/skills/feedback-learning/SKILL.md +414 -0
- package/skills/fix/SKILL.md +450 -0
- package/skills/freeze/SKILL.md +68 -0
- package/skills/frontend-architecture/SKILL.md +113 -0
- package/skills/gherkin-derive/SKILL.md +630 -0
- package/skills/gherkin-public/SKILL.md +266 -0
- package/skills/governance-compliance/SKILL.md +150 -0
- package/skills/guard/SKILL.md +75 -0
- package/skills/handoff/SKILL.md +139 -0
- package/skills/handoff/references/summary-templates.md +242 -0
- package/skills/harness-audit/SKILL.md +751 -0
- package/skills/harness-audit/scripts/lesson_validate.py +386 -0
- package/skills/harness-audit/scripts/redundancy_criterion.py +188 -0
- package/skills/headless-run/SKILL.md +45 -0
- package/skills/headless-run/scripts/isolated_dispatch.py +381 -0
- package/skills/help/SKILL.md +72 -0
- package/skills/hexagonal-architecture/SKILL.md +85 -0
- package/skills/human-oversight-protocol/SKILL.md +224 -0
- package/skills/issues-from-assessment/SKILL.md +223 -0
- package/skills/issues-from-plan/SKILL.md +133 -0
- package/skills/legacy-code/SKILL.md +132 -0
- package/skills/mermaid-diagramming/SKILL.md +120 -0
- package/skills/mutation-night-watch/SKILL.md +154 -0
- package/skills/mutation-night-watch/references/scheduling.md +135 -0
- package/skills/mutation-testing/SKILL.md +396 -0
- package/skills/mutation-testing/references/languages/csharp-stryker-net.md +676 -0
- package/skills/mutation-testing/references/languages/go-go-mutesting.md +95 -0
- package/skills/mutation-testing/references/languages/java-pitest.md +77 -0
- package/skills/mutation-testing/references/languages/javascript-stryker.md +188 -0
- package/skills/mutation-testing/references/languages/python-mutmut.md +97 -0
- package/skills/mutation-testing/references/time-estimation.md +34 -0
- package/skills/mutation-testing/references/tool-detection.md +15 -0
- package/skills/mutation-testing/references/workflow-callers.md +23 -0
- package/skills/mutation-testing/scripts/__pycache__/xunit_v3_feature_detector.cpython-314.pyc +0 -0
- package/skills/mutation-testing/scripts/csharp_stryker_net_slice_runner.py +635 -0
- package/skills/mutation-testing/scripts/csharp_stryker_net_status_loop.py +525 -0
- package/skills/mutation-testing/scripts/csharp_stryker_net_wrapper.py +681 -0
- package/skills/mutation-testing/scripts/mutation_baseline_reuse.py +292 -0
- package/skills/mutation-testing/scripts/mutation_exclude_policy.py +268 -0
- package/skills/mutation-testing/scripts/mutation_feasibility_gate.py +463 -0
- package/skills/mutation-testing/scripts/mutation_kill_headless.py +331 -0
- package/skills/mutation-testing/scripts/mutation_kill_insert.py +199 -0
- package/skills/mutation-testing/scripts/mutation_kill_insert_python.py +150 -0
- package/skills/mutation-testing/scripts/mutation_kill_loop.py +869 -0
- package/skills/mutation-testing/scripts/mutation_kill_loop_python.py +949 -0
- package/skills/mutation-testing/scripts/mutation_kill_retry.py +592 -0
- package/skills/mutation-testing/scripts/mutation_kill_shared.py +620 -0
- package/skills/mutation-testing/scripts/mutation_nightwatch.py +462 -0
- package/skills/mutation-testing/scripts/mutation_nightwatch_stacks.py +425 -0
- package/skills/mutation-testing/scripts/mutation_report.py +743 -0
- package/skills/mutation-testing/scripts/mutation_report_cli.py +175 -0
- package/skills/mutation-testing/scripts/mutation_safety_gate.py +69 -0
- package/skills/mutation-testing/scripts/stryker_shard_pipeline.py +847 -0
- package/skills/mutation-testing/scripts/stryker_shard_setup.py +440 -0
- package/skills/mutation-testing/scripts/stryker_timeout_retry.py +142 -0
- package/skills/mutation-testing/scripts/xunit_v3_feature_detector.py +341 -0
- package/skills/performance-benchmark/SKILL.md +174 -0
- package/skills/performance-benchmark/examples/report-format.md +43 -0
- package/skills/performance-benchmark/references/benchmark-script.md +169 -0
- package/skills/performance-metrics/SKILL.md +265 -0
- package/skills/plan/SKILL.md +199 -0
- package/skills/plan/references/gherkin-persistence.md +43 -0
- package/skills/plan/references/plan-template.md +182 -0
- package/skills/pr/SKILL.md +289 -0
- package/skills/pr/scripts/gate_retry_state.py +368 -0
- package/skills/project-init/README.md +141 -0
- package/skills/project-init/SKILL.md +1197 -0
- package/skills/project-init/evals/evals.json +200 -0
- package/skills/project-init/references/capability-tools.md +55 -0
- package/skills/project-init/references/configs.md +221 -0
- package/skills/property-based-testing/SKILL.md +121 -0
- package/skills/property-based-testing/fixtures/invariant_fixture.py +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/README.md +42 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/LICENSE +21 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/README.md +263 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/cjs/fast-check.d.ts +5165 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/cjs/fast-check.js +12147 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/cjs/package.json +3 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/cjs/types57/fast-check.d.ts +5165 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/fast-check.d.ts +5165 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/fast-check.js +12011 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/rolldown-runtime-D7D4PA-g.js +13 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/types57/fast-check.d.ts +5165 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/package.json +94 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/LICENSE +21 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/README.md +168 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/RandomGenerator-DcXj09Ch.d.ts +14 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformBigInt.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformBigInt.js +38 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformFloat32.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformFloat32.js +18 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformFloat64.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformFloat64.js +22 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformInt.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformInt.js +134 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/RandomGenerator-DcXj09Ch.d.ts +14 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformBigInt.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformBigInt.js +37 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformFloat32.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformFloat32.js +17 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformFloat64.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformFloat64.js +21 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformInt.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformInt.js +133 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/congruential32.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/congruential32.js +44 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/mersenne.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/mersenne.js +90 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/xoroshiro128plus.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/xoroshiro128plus.js +80 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/xorshift128plus.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/xorshift128plus.js +78 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/package.json +3 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/types/JumpableRandomGenerator.d.ts +16 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/types/JumpableRandomGenerator.js +0 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/types/RandomGenerator.d.ts +2 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/types/RandomGenerator.js +0 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/generateN.d.ts +6 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/generateN.js +8 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/purify.d.ts +12 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/purify.js +9 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/skipN.d.ts +6 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/skipN.js +6 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/congruential32.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/congruential32.js +46 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/mersenne.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/mersenne.js +92 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/xoroshiro128plus.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/xoroshiro128plus.js +82 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/xorshift128plus.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/xorshift128plus.js +80 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/types/JumpableRandomGenerator.d.ts +16 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/types/JumpableRandomGenerator.js +0 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/types/RandomGenerator.d.ts +2 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/types/RandomGenerator.js +0 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/generateN.d.ts +6 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/generateN.js +9 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/purify.d.ts +12 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/purify.js +10 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/skipN.d.ts +6 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/skipN.js +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/package.json +133 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/package-lock.json +1179 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/package.json +14 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/roundtrip.js +29 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/roundtrip.properties.test.js +16 -0
- package/skills/property-based-testing/fixtures/no_property_fixture.py +10 -0
- package/skills/property-based-testing/fixtures/roundtrip_fixture.py +16 -0
- package/skills/property-based-testing/references/languages/javascript.md +54 -0
- package/skills/property-based-testing/scripts/detect_and_dispatch.py +80 -0
- package/skills/property-based-testing/scripts/hypothesis_scaffold.py +276 -0
- package/skills/proxy-resilience/SKILL.md +84 -0
- package/skills/quality-gate-pipeline/SKILL.md +184 -0
- package/skills/quality-targets-converge/SKILL.md +254 -0
- package/skills/repo-review/SKILL.md +159 -0
- package/skills/report-pdf/SKILL.md +66 -0
- package/skills/review/SKILL.md +47 -0
- package/skills/review-agent/SKILL.md +152 -0
- package/skills/review-summary/SKILL.md +73 -0
- package/skills/run-report/SKILL.md +70 -0
- package/skills/semantic-duplication-scan/SKILL.md +337 -0
- package/skills/semantic-scan/SKILL.md +53 -0
- package/skills/semgrep-analyze/SKILL.md +139 -0
- package/skills/setup/SKILL.md +1122 -0
- package/skills/ship/SKILL.md +240 -0
- package/skills/source-verification/SKILL.md +210 -0
- package/skills/source-verification/scripts/claim_extractor.py +155 -0
- package/skills/specs/.size-baseline.json +4 -0
- package/skills/specs/SKILL.md +243 -0
- package/skills/specs/references/completeness-checklist.md +83 -0
- package/skills/specs/references/extraction.md +58 -0
- package/skills/specs/references/glossary.md +59 -0
- package/skills/specs/references/persistence.md +115 -0
- package/skills/specs/references/predictability-check.md +77 -0
- package/skills/static-analysis-integration/SKILL.md +235 -0
- package/skills/static-analysis-integration/adapters/_envelope.py +26 -0
- package/skills/static-analysis-integration/adapters/jscpd-adapter.py +66 -0
- package/skills/static-analysis-integration/adapters/lizard-adapter.py +81 -0
- package/skills/static-analysis-integration/adapters/mypy-adapter.py +50 -0
- package/skills/static-analysis-integration/adapters/mypy-src-layout.py +93 -0
- package/skills/static-analysis-integration/adapters/security-review-adapter.py +212 -0
- package/skills/static-analysis-integration/maintenance.md +23 -0
- package/skills/static-analysis-integration/references/language-setup.md +228 -0
- package/skills/static-analysis-integration/references/sarif-parser.md +124 -0
- package/skills/static-analysis-integration/references/security-review-adapter.md +118 -0
- package/skills/static-analysis-integration/references/tool-configs.md +617 -0
- package/skills/static-analysis-integration/rulesets/pmd-quickstart.xml +24 -0
- package/skills/stryker-xunit-v2-shim/SKILL.md +274 -0
- package/skills/stryker-xunit-v2-shim/references/shim-howto.md +256 -0
- package/skills/stryker-xunit-v2-shim/scripts/generate_shim.py +143 -0
- package/skills/systematic-debugging/SKILL.md +130 -0
- package/skills/telemetry/SKILL.md +75 -0
- package/skills/test-audit-disable/SKILL.md +129 -0
- package/skills/test-design/SKILL.md +177 -0
- package/skills/test-design/scripts/__pycache__/internal_double_detector.cpython-314.pyc +0 -0
- package/skills/test-design/scripts/internal_double_detector.py +631 -0
- package/skills/test-design-advisor/SKILL.md +166 -0
- package/skills/test-driven-development/SKILL.md +169 -0
- package/skills/test-health/SKILL.md +262 -0
- package/skills/test-improve/SKILL.md +239 -0
- package/skills/test-improve/references/phase-0-approach-contract.md +228 -0
- package/skills/test-improve/references/phase-1-analyze.md +131 -0
- package/skills/test-improve/references/phase-2-baseline.md +121 -0
- package/skills/test-improve/references/phase-3-derive-gherkin.md +53 -0
- package/skills/test-improve/references/phase-4-plan-fixes.md +34 -0
- package/skills/test-improve/references/phase-5-improve.md +215 -0
- package/skills/test-improve/references/phase-6-refactor-decision.md +45 -0
- package/skills/test-improve/references/phase-7-refactor.md +44 -0
- package/skills/test-improve/references/phase-8-validate.md +66 -0
- package/skills/test-improve/references/phase-9-close-out-prompt.md +11 -0
- package/skills/test-improve/references/phase-9-report.md +62 -0
- package/skills/test-improve/references/review-loop.md +92 -0
- package/skills/test-improve/templates/executive-summary.md +123 -0
- package/skills/threat-modeling/SKILL.md +108 -0
- package/skills/triage/SKILL.md +211 -0
- package/skills/ubiquitous-language/SKILL.md +192 -0
- package/skills/ubiquitous-language/scripts/collect_domain_signals.py +300 -0
- package/skills/unfreeze/SKILL.md +37 -0
- package/skills/upgrade/SKILL.md +31 -0
- package/skills/upgrade/scripts/check_version_drift.py +113 -0
- package/skills/upgrade/scripts/enable_autoupdate.py +149 -0
- package/skills/version/SKILL.md +25 -0
- package/sync/__pycache__/sync_upstream.cpython-314.pyc +0 -0
- package/sync/sync_upstream.py +293 -0
- package/templates/ACCEPTED-RISKS.md.tmpl +46 -0
- package/templates/agents/agent-template.md +151 -0
- package/templates/agents/angular-testing.md +66 -0
- package/templates/agents/csharp-quality.md +63 -0
- package/templates/agents/esm-enforcer.md +52 -0
- package/templates/agents/front-end-testing.md +65 -0
- package/templates/agents/go-quality.md +65 -0
- package/templates/agents/python-quality.md +62 -0
- package/templates/agents/react-testing.md +61 -0
- package/templates/agents/ts-enforcer.md +60 -0
- package/templates/agents/twelve-factor-audit.md +49 -0
- package/tools/entropy-check.py +250 -0
- package/tools/model-hash-verify.py +213 -0
|
@@ -0,0 +1,847 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""stryker_shard_pipeline.py — sharded Stryker pipeline with timeout-abort.
|
|
3
|
+
|
|
4
|
+
Migrated from the ACI ``nextgen-test-upgrade-process`` ``stryker-pipeline.py``
|
|
5
|
+
and de-hardcoded: nothing repo-specific lives here. Shards are discovered from
|
|
6
|
+
``stryker-config.shard-*.json`` files (emitted by ``stryker_shard_setup.py``);
|
|
7
|
+
each shard runs Stryker through the shipped ``csharp_stryker_net_wrapper`` and
|
|
8
|
+
the per-shard survivor-fix loop is ``mutation_kill_loop`` forced into
|
|
9
|
+
``--headless`` mode (the pipeline is unattended — there is no live agent turn).
|
|
10
|
+
|
|
11
|
+
Compounding worktrees: shards are processed **sequentially**, each in its own
|
|
12
|
+
git worktree created from the current ``HEAD``. Because the survivor-fix loop
|
|
13
|
+
commits its fixes onto ``HEAD`` before the next shard's worktree is created,
|
|
14
|
+
the second shard's worktree already contains the first shard's committed fixes.
|
|
15
|
+
|
|
16
|
+
Timeout-abort: Stryker output is streamed through the wrapper's line-callback
|
|
17
|
+
(added in #1136 Slice 5.1). A ``"N mutants got status Timeout"`` line kills the
|
|
18
|
+
Stryker process and marks the shard failed — timeouts are non-deterministic
|
|
19
|
+
noise that would otherwise inflate the score, so we refuse to score them.
|
|
20
|
+
|
|
21
|
+
Generic, stdlib-only, cross-platform (macOS, Linux, Windows). The only
|
|
22
|
+
non-stdlib imports are the three sibling plugin modules in this same
|
|
23
|
+
``scripts/`` directory.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import argparse
|
|
29
|
+
import json
|
|
30
|
+
import os
|
|
31
|
+
import re
|
|
32
|
+
import subprocess
|
|
33
|
+
import sys
|
|
34
|
+
import threading
|
|
35
|
+
import time
|
|
36
|
+
from collections.abc import Callable, Sequence
|
|
37
|
+
from datetime import datetime, timezone
|
|
38
|
+
from pathlib import Path
|
|
39
|
+
from typing import NamedTuple
|
|
40
|
+
|
|
41
|
+
# typing, not collections.abc: the module-level type aliases below are real
|
|
42
|
+
# runtime expressions, so `from __future__ import annotations` cannot defer
|
|
43
|
+
# them — they must be subscriptable at import time. collections.abc generics
|
|
44
|
+
# have been since 3.9, which the 3.10 floor (ADR 0031) clears.
|
|
45
|
+
import csharp_stryker_net_wrapper as wrapper
|
|
46
|
+
import mutation_report
|
|
47
|
+
from mutation_kill_retry import EXIT_GENERATION_EXHAUSTED
|
|
48
|
+
|
|
49
|
+
# ── Constants ────────────────────────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
PYTHON = sys.executable
|
|
52
|
+
LOOP_SCRIPT = str(Path(__file__).resolve().parent / "mutation_kill_loop.py")
|
|
53
|
+
|
|
54
|
+
SHARD_GLOB = "stryker-config.shard-*.json"
|
|
55
|
+
SHARD_PREFIX = "stryker-config.shard-"
|
|
56
|
+
SHARD_SUFFIX = ".json"
|
|
57
|
+
|
|
58
|
+
# Stryker prints e.g. "5 mutants got status Timeout" once at least one mutant
|
|
59
|
+
# times out. A leading non-zero count means real timeouts (never "0 mutants").
|
|
60
|
+
# The status word comes from mutation_report's status vocabulary (AC4) — no raw
|
|
61
|
+
# literal is retyped here.
|
|
62
|
+
TIMEOUT_PATTERN = re.compile(
|
|
63
|
+
rf"[1-9]\d*\s+mutants got status {mutation_report.STATUS_TIMEOUT}"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Conventional C# test-file names paired with a source file's stem.
|
|
67
|
+
_TEST_FILE_SUFFIXES = ("Tests.cs", "Test.cs", "Specs.cs", "Spec.cs")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class ShardSetupMissing(Exception):
|
|
71
|
+
"""No ``stryker-config.shard-*.json`` files were discovered — the operator
|
|
72
|
+
must run shard setup first. Carries an actionable message."""
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class FixOutcome(NamedTuple):
|
|
76
|
+
"""``launch_survivor_fix``'s return value (#1951).
|
|
77
|
+
|
|
78
|
+
Replaces the previous ``bool`` return plus a mutable ``exhausted``
|
|
79
|
+
out-parameter — extending that pattern with two more per-run outcomes
|
|
80
|
+
(unresolved test files, no report) would have meant threading a fourth
|
|
81
|
+
and fifth mutable accumulator through three function signatures, which
|
|
82
|
+
is the exact command-query smell #1951 flagged. A single typed result
|
|
83
|
+
mirrors :class:`ShardRunResult`'s existing precedent instead.
|
|
84
|
+
|
|
85
|
+
``ok`` is False the moment a launched fix exits any non-zero, non-5 code
|
|
86
|
+
(see ``launch_survivor_fix``'s docstring for the full exit-code
|
|
87
|
+
decoding). ``exhausted``/``unresolved`` carry ``"shard/source"`` entries.
|
|
88
|
+
``no_report`` is True when the shard had no Stryker report to act on at
|
|
89
|
+
all — no survivor files were even considered.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
ok: bool
|
|
93
|
+
exhausted: list[str]
|
|
94
|
+
unresolved: list[str]
|
|
95
|
+
no_report: bool
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class ShardOutcome(NamedTuple):
|
|
99
|
+
"""``process_shard``'s return value (#1951).
|
|
100
|
+
|
|
101
|
+
``status`` keeps the three prior string values (``"ok"`` / ``"failed"`` /
|
|
102
|
+
``"skipped"``). The other three fields are ``launch_survivor_fix``'s
|
|
103
|
+
:class:`FixOutcome` propagated up one level — empty/False for a shard
|
|
104
|
+
that never reached the survivor-fix stage (Stryker itself failed, the
|
|
105
|
+
shard was skipped, or ``--skip-agent`` was set).
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
status: str
|
|
109
|
+
exhausted: list[str]
|
|
110
|
+
unresolved: list[str]
|
|
111
|
+
no_report: bool
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class ShardRunResult(NamedTuple):
|
|
115
|
+
"""``run_all``'s return value.
|
|
116
|
+
|
|
117
|
+
A NamedTuple (not a bare tuple) so a future call site can't silently
|
|
118
|
+
transpose fields — access by name, not position. #1951 widened this from
|
|
119
|
+
2 fields (``failed``, ``exhausted``) to 5, so the previous "still
|
|
120
|
+
2-tuple-unpackable" guarantee no longer holds: ``failed, exhausted =
|
|
121
|
+
run_all(...)`` now raises ``ValueError`` (too many values to unpack).
|
|
122
|
+
That guarantee was a bonus of a 2-field type, not a design goal in its
|
|
123
|
+
own right — every internal caller passes ``run_all``'s result straight
|
|
124
|
+
through by field access, so the trade (3 more silently-swallowed
|
|
125
|
+
run-level outcomes made visible) is worth the break.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
failed: list[str]
|
|
129
|
+
exhausted: list[str]
|
|
130
|
+
unresolved: list[str]
|
|
131
|
+
no_report: list[str]
|
|
132
|
+
skipped: list[str]
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
# ── Small helpers ──────────────────────────────────────────────────────────────
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _safe(v: str) -> str:
|
|
139
|
+
"""Whitespace-collapse a caller-supplied value before it lands in a log
|
|
140
|
+
line — same precedent as ``mutation_kill_loop.py``'s and
|
|
141
|
+
``mutation_kill_loop_python.py``'s ``_commit_message`` (#1607):
|
|
142
|
+
``source``/``shard`` are survivor-report file paths, not
|
|
143
|
+
operator-supplied, but could theoretically contain a newline that would
|
|
144
|
+
otherwise forge extra log lines."""
|
|
145
|
+
return " ".join(str(v).split())
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def ts() -> str:
|
|
149
|
+
"""Wall-clock ``HH:MM:SS`` stamp for per-shard progress lines."""
|
|
150
|
+
# Local wall-clock time is the point (a human-readable progress line),
|
|
151
|
+
# not a comparable timestamp — go through UTC-aware then convert back
|
|
152
|
+
# to the system local zone so the value stays tz-aware for DTZ005
|
|
153
|
+
# without changing the displayed (local) hour/minute/second.
|
|
154
|
+
return datetime.now(timezone.utc).astimezone().strftime("%H:%M:%S")
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def shard_config_path(repo_root: Path, shard: str) -> Path:
|
|
158
|
+
return Path(repo_root) / f"{SHARD_PREFIX}{shard}{SHARD_SUFFIX}"
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def report_path(out_dir: Path) -> Path:
|
|
162
|
+
return Path(out_dir) / "reports" / "mutation-report.json"
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def discover_shards(repo_root: Path) -> list[str]:
|
|
166
|
+
"""Return shard slugs from ``stryker-config.shard-*.json``, sorted.
|
|
167
|
+
|
|
168
|
+
Raises :class:`ShardSetupMissing` — naming how to generate the configs —
|
|
169
|
+
when none exist, rather than silently processing nothing.
|
|
170
|
+
"""
|
|
171
|
+
configs = sorted(Path(repo_root).glob(SHARD_GLOB))
|
|
172
|
+
if not configs:
|
|
173
|
+
raise ShardSetupMissing(
|
|
174
|
+
f"No {SHARD_GLOB} files found in {repo_root}. Run shard setup first: "
|
|
175
|
+
"python3 stryker_shard_setup.py (generates one shard config per "
|
|
176
|
+
"source project)."
|
|
177
|
+
)
|
|
178
|
+
return [c.name[len(SHARD_PREFIX) : -len(SHARD_SUFFIX)] for c in configs]
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
# ── Timeout-abort callback ─────────────────────────────────────────────────────
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def make_timeout_callback(
|
|
185
|
+
timed_out: threading.Event, *, log: Callable[[str], None] = print
|
|
186
|
+
) -> Callable[[str], bool]:
|
|
187
|
+
"""Return a line-callback for ``wrapper.run_stryker`` that sets
|
|
188
|
+
``timed_out`` and requests an abort (returns True) on the first Stryker
|
|
189
|
+
timeout line. Every other line is echoed for live progress."""
|
|
190
|
+
|
|
191
|
+
def callback(line: str) -> bool:
|
|
192
|
+
sys.stdout.write(line)
|
|
193
|
+
if TIMEOUT_PATTERN.search(line):
|
|
194
|
+
timed_out.set()
|
|
195
|
+
return True
|
|
196
|
+
return False
|
|
197
|
+
|
|
198
|
+
return callback
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
# ── git worktree management ─────────────────────────────────────────────────────
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _git(cmd: Sequence[str], repo_root: Path, check: bool) -> subprocess.CompletedProcess:
|
|
205
|
+
"""Run a git command in ``repo_root``.
|
|
206
|
+
|
|
207
|
+
``check`` is deliberately POSITIONAL, not keyword-only. This is the default
|
|
208
|
+
``GitRunner``, and that alias — ``Callable[[Sequence[str], Path, bool],
|
|
209
|
+
object]`` — is the contract every call site and every test fake already
|
|
210
|
+
implements positionally. Declaring it ``*, check`` here made the production
|
|
211
|
+
default the only implementation that did not satisfy its own alias, so
|
|
212
|
+
every real invocation (any path that does not inject a fake, which is all
|
|
213
|
+
of them: ``main()`` calls ``run_all(...)`` with no ``git_run``) raised
|
|
214
|
+
``TypeError: _git() takes 2 positional arguments but 3 were given`` (#1953).
|
|
215
|
+
|
|
216
|
+
The bug was invisible to the suite precisely because every test injects a
|
|
217
|
+
fake, so the default was never exercised — which is why the regression
|
|
218
|
+
test below calls the real default rather than a fake.
|
|
219
|
+
"""
|
|
220
|
+
return subprocess.run(
|
|
221
|
+
list(cmd), cwd=str(repo_root), capture_output=True, check=check
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
GitRunner = Callable[[Sequence[str], Path, bool], object]
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def worktree_add(
|
|
229
|
+
repo_root: Path, path: Path, *, ref: str = "HEAD", git_run: GitRunner | None = None
|
|
230
|
+
) -> None:
|
|
231
|
+
"""Create a git worktree at ``path`` from ``ref`` (default ``HEAD``).
|
|
232
|
+
|
|
233
|
+
A pre-existing worktree at that path is force-removed first so reruns are
|
|
234
|
+
idempotent. Creating from ``HEAD`` is what makes shards compounding: the
|
|
235
|
+
fixes previous shards committed onto ``HEAD`` are already present.
|
|
236
|
+
"""
|
|
237
|
+
git_run = git_run or _git
|
|
238
|
+
if Path(path).exists():
|
|
239
|
+
git_run(["git", "worktree", "remove", "--force", str(path)], repo_root, False)
|
|
240
|
+
git_run(["git", "worktree", "add", "--quiet", str(path), ref], repo_root, True)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def worktree_remove(
|
|
244
|
+
repo_root: Path, path: Path, *, git_run: GitRunner | None = None
|
|
245
|
+
) -> None:
|
|
246
|
+
git_run = git_run or _git
|
|
247
|
+
git_run(["git", "worktree", "remove", "--force", str(path)], repo_root, False)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
# ── Stryker runner (timeout-abort via the wrapper callback) ────────────────────
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
StrykerRunner = Callable[..., int]
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def run_shard_stryker(
|
|
257
|
+
shard: str,
|
|
258
|
+
*,
|
|
259
|
+
repo_root: Path,
|
|
260
|
+
worktree: Path,
|
|
261
|
+
out_dir: Path,
|
|
262
|
+
stryker_bin: str,
|
|
263
|
+
log: Callable[[str], None] = print,
|
|
264
|
+
run_stryker: StrykerRunner | None = None,
|
|
265
|
+
) -> bool:
|
|
266
|
+
"""Run Stryker for one shard in its worktree. Returns True on success.
|
|
267
|
+
|
|
268
|
+
Streams output through ``wrapper.run_stryker``'s line-callback so a timeout
|
|
269
|
+
line kills the process and marks the shard failed — no re-implemented
|
|
270
|
+
streamer (AC4). A non-zero Stryker exit also fails the shard.
|
|
271
|
+
"""
|
|
272
|
+
run_stryker = run_stryker or wrapper.run_stryker
|
|
273
|
+
config = shard_config_path(repo_root, shard)
|
|
274
|
+
if not config.exists():
|
|
275
|
+
log(f"ERROR: {config} not found")
|
|
276
|
+
return False
|
|
277
|
+
|
|
278
|
+
Path(out_dir).mkdir(parents=True, exist_ok=True)
|
|
279
|
+
logfile = Path(out_dir).parent / f"{shard}.log"
|
|
280
|
+
|
|
281
|
+
timed_out = threading.Event()
|
|
282
|
+
rc = run_stryker(
|
|
283
|
+
stryker_bin=stryker_bin,
|
|
284
|
+
stryker_args=["--config-file", str(config), "--output", str(out_dir)],
|
|
285
|
+
logfile=logfile,
|
|
286
|
+
line_callback=make_timeout_callback(timed_out, log=log),
|
|
287
|
+
cwd=worktree,
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
if timed_out.is_set():
|
|
291
|
+
log(
|
|
292
|
+
f"[{ts()}] Shard ABORTED (timeout): {shard} — increase "
|
|
293
|
+
"additional-timeout or reduce concurrency, then retry with "
|
|
294
|
+
"stryker_timeout_retry.py"
|
|
295
|
+
)
|
|
296
|
+
return False
|
|
297
|
+
if rc != 0:
|
|
298
|
+
log(f"[{ts()}] Shard FAILED: {shard} (exit {rc})")
|
|
299
|
+
return False
|
|
300
|
+
return True
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
# ── Survivor-fix launch (forced --headless) ─────────────────────────────────────
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def survivor_source_files(report: Path) -> list[str]:
|
|
307
|
+
"""Return the source-file keys in the report that have Survived mutants.
|
|
308
|
+
|
|
309
|
+
Report parsing and the ``Survived`` status vocabulary live in
|
|
310
|
+
``mutation_report`` (AC4) — this never re-walks the report itself.
|
|
311
|
+
"""
|
|
312
|
+
return mutation_report.files_with_survivors(report)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def shard_test_projects(config_path: Path) -> list[str]:
|
|
316
|
+
raw = json.loads(Path(config_path).read_text(encoding="utf-8"))
|
|
317
|
+
inner = raw.get("stryker-config", raw)
|
|
318
|
+
return list(inner.get("test-projects", []))
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
TestFileResolver = Callable[[str, Sequence[str], Path], Path | None]
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def default_resolve_test_file(
|
|
325
|
+
source: str, test_projects: Sequence[str], repo_root: Path
|
|
326
|
+
) -> Path | None:
|
|
327
|
+
"""Best-effort convention search for a source file's test file.
|
|
328
|
+
|
|
329
|
+
Looks under each configured test-project directory for
|
|
330
|
+
``<Stem>Tests.cs`` / ``<Stem>Test.cs`` / ``<Stem>Specs.cs``. Returns the
|
|
331
|
+
first hit, or None when no conventional pairing is found (the caller skips
|
|
332
|
+
that file rather than guessing — never a silent mis-target)."""
|
|
333
|
+
stem = Path(source).stem
|
|
334
|
+
for tp in test_projects:
|
|
335
|
+
tp_dir = (Path(repo_root) / tp).parent
|
|
336
|
+
if not tp_dir.exists():
|
|
337
|
+
continue
|
|
338
|
+
for suffix in _TEST_FILE_SUFFIXES:
|
|
339
|
+
for hit in sorted(tp_dir.rglob(f"{stem}{suffix}")):
|
|
340
|
+
return hit
|
|
341
|
+
return None
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def build_loop_command(
|
|
345
|
+
*,
|
|
346
|
+
config: Path,
|
|
347
|
+
source_file: str,
|
|
348
|
+
source_path: str,
|
|
349
|
+
test_file: Path,
|
|
350
|
+
report: Path,
|
|
351
|
+
model: str | None,
|
|
352
|
+
max_rounds: int,
|
|
353
|
+
stryker_bin: str = "dotnet",
|
|
354
|
+
) -> list[str]:
|
|
355
|
+
"""Build the ``mutation_kill_loop`` invocation. ``--headless`` is forced —
|
|
356
|
+
the pipeline is unattended and can never depend on a live agent turn.
|
|
357
|
+
|
|
358
|
+
``stryker_bin`` is forwarded explicitly so an override on the pipeline's
|
|
359
|
+
own ``--stryker-bin`` (e.g. targeting a global install) reaches this
|
|
360
|
+
per-file survivor-fix subprocess too, rather than that subprocess
|
|
361
|
+
silently falling back to its own independent default.
|
|
362
|
+
"""
|
|
363
|
+
cmd = [
|
|
364
|
+
PYTHON,
|
|
365
|
+
LOOP_SCRIPT,
|
|
366
|
+
"--headless",
|
|
367
|
+
"--config",
|
|
368
|
+
str(config),
|
|
369
|
+
"--file",
|
|
370
|
+
source_file,
|
|
371
|
+
"--source-path",
|
|
372
|
+
str(source_path),
|
|
373
|
+
"--test-file",
|
|
374
|
+
str(test_file),
|
|
375
|
+
"--report",
|
|
376
|
+
str(report),
|
|
377
|
+
"--max-rounds",
|
|
378
|
+
str(max_rounds),
|
|
379
|
+
"--stryker-bin",
|
|
380
|
+
stryker_bin,
|
|
381
|
+
]
|
|
382
|
+
if model:
|
|
383
|
+
cmd += ["--model", model]
|
|
384
|
+
return cmd
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def launch_survivor_fix(
|
|
388
|
+
shard: str,
|
|
389
|
+
*,
|
|
390
|
+
repo_root: Path,
|
|
391
|
+
out_dir: Path,
|
|
392
|
+
config_path: Path,
|
|
393
|
+
model: str | None,
|
|
394
|
+
max_rounds: int,
|
|
395
|
+
stryker_bin: str = "dotnet",
|
|
396
|
+
run: Callable[..., subprocess.CompletedProcess] = subprocess.run,
|
|
397
|
+
resolve_test_file: TestFileResolver | None = None,
|
|
398
|
+
log: Callable[[str], None] = print,
|
|
399
|
+
) -> FixOutcome:
|
|
400
|
+
"""Launch the forced-headless survivor-fix loop for a shard's survivors.
|
|
401
|
+
|
|
402
|
+
Invokes ``mutation_kill_loop`` (always with ``--headless``) once per source
|
|
403
|
+
file that still has survivors, seeding round 1 from the shard's report.
|
|
404
|
+
Runs from ``repo_root`` so fixes commit onto ``HEAD`` — that is what makes
|
|
405
|
+
the *next* shard's worktree compounding.
|
|
406
|
+
|
|
407
|
+
Returns a :class:`FixOutcome`. ``ok`` is True when every launched fix
|
|
408
|
+
exited zero or the dedicated GenerationExhausted exit code 5 (or none
|
|
409
|
+
were launched); False the moment one exits any other non-zero code. The
|
|
410
|
+
implementation does not distinguish the fatal-revert exit code 4
|
|
411
|
+
(``mutation_kill_headless``/``mutation_kill_loop_python`` #1598) from any
|
|
412
|
+
other non-zero, non-5 code — every such exit is treated identically as a
|
|
413
|
+
failed revert, which means the working tree may be in an
|
|
414
|
+
unknown/possibly-mutated state, so processing stops for the remaining
|
|
415
|
+
files in this shard rather than silently continuing onto a tree that may
|
|
416
|
+
already be broken. Exit code 4 is ``RevertFailed`` specifically — a
|
|
417
|
+
failed revert. Exit code 5 covers two distinct outcomes that both map to
|
|
418
|
+
the same code: true GenerationExhausted (a fully spent
|
|
419
|
+
retry-then-downgrade budget) and any other clean RuntimeError (e.g. a
|
|
420
|
+
generation timeout, or a mutmut/Stryker infrastructure failure — not
|
|
421
|
+
exhaustion). Both are carved out from the fatal case: this file is
|
|
422
|
+
logged as unfixed and the loop continues to the next file in the shard
|
|
423
|
+
(the tree was not mutated), matching ``mutation-kill.md``'s documented
|
|
424
|
+
exhaustion contract that ```--all` continues``` (#1908 review) — but the
|
|
425
|
+
pipeline's overall exit code becomes ``EXIT_GENERATION_EXHAUSTED`` (5) at
|
|
426
|
+
the end if any file across any shard hit either outcome, unless a hard
|
|
427
|
+
failure elsewhere makes it 1 (``main()``'s 3-branch priority: failed >
|
|
428
|
+
exhausted/unresolved/no_report > clean).
|
|
429
|
+
|
|
430
|
+
Two more outcomes, previously silently swallowed as a bare ``True`` (#1951):
|
|
431
|
+
a survivor whose test file never resolves (``FixOutcome.unresolved``, one
|
|
432
|
+
entry per such survivor — logged ``Agent SKIP: no test file resolved`` but
|
|
433
|
+
the loop continues, since other survivors in the shard may still resolve)
|
|
434
|
+
and a shard with no Stryker report at all (``FixOutcome.no_report``) —
|
|
435
|
+
both now propagate to the run-level result instead of reading identically
|
|
436
|
+
to a fully clean run with zero fix attempts made.
|
|
437
|
+
"""
|
|
438
|
+
resolve_test_file = resolve_test_file or default_resolve_test_file
|
|
439
|
+
report = report_path(out_dir)
|
|
440
|
+
if not report.exists():
|
|
441
|
+
log(f"[{ts()}] Agent SKIP: no report for {_safe(shard)}")
|
|
442
|
+
return FixOutcome(ok=True, exhausted=[], unresolved=[], no_report=True)
|
|
443
|
+
files = survivor_source_files(report)
|
|
444
|
+
if not files:
|
|
445
|
+
log(f"[{ts()}] Agent SKIP: no survivors in {_safe(shard)}")
|
|
446
|
+
return FixOutcome(ok=True, exhausted=[], unresolved=[], no_report=False)
|
|
447
|
+
|
|
448
|
+
exhausted: list[str] = []
|
|
449
|
+
unresolved: list[str] = []
|
|
450
|
+
test_projects = shard_test_projects(config_path)
|
|
451
|
+
for source in files:
|
|
452
|
+
test_file = resolve_test_file(source, test_projects, repo_root)
|
|
453
|
+
if test_file is None:
|
|
454
|
+
log(f"[{ts()}] Agent SKIP: no test file resolved for {_safe(source)}")
|
|
455
|
+
unresolved.append(f"{_safe(shard)}/{_safe(source)}")
|
|
456
|
+
continue
|
|
457
|
+
cmd = build_loop_command(
|
|
458
|
+
config=config_path,
|
|
459
|
+
source_file=Path(source).name,
|
|
460
|
+
source_path=source,
|
|
461
|
+
test_file=test_file,
|
|
462
|
+
report=report,
|
|
463
|
+
model=model,
|
|
464
|
+
max_rounds=max_rounds,
|
|
465
|
+
stryker_bin=stryker_bin,
|
|
466
|
+
)
|
|
467
|
+
log(f"[{ts()}] Agent START (headless): {_safe(shard)} — {_safe(source)}")
|
|
468
|
+
result = run(cmd, cwd=str(repo_root))
|
|
469
|
+
rc = result.returncode
|
|
470
|
+
if rc == EXIT_GENERATION_EXHAUSTED:
|
|
471
|
+
log(
|
|
472
|
+
f"[{ts()}] Agent UNFIXED (headless, clean): {_safe(shard)} — "
|
|
473
|
+
f"{_safe(source)} (exit {rc}) — retry budget spent OR a "
|
|
474
|
+
"clean generation/infrastructure failure (#1956: exit 5 "
|
|
475
|
+
"covers both since #1939, so this line names the OUTCOME, "
|
|
476
|
+
"not a specific cause); logging this file as unfixed and "
|
|
477
|
+
"continuing to the next file in this shard (the tree was "
|
|
478
|
+
"not mutated) — the pipeline's overall exit code becomes "
|
|
479
|
+
"EXIT_GENERATION_EXHAUSTED (5) at the end unless a hard "
|
|
480
|
+
"failure elsewhere makes it 1"
|
|
481
|
+
)
|
|
482
|
+
exhausted.append(f"{_safe(shard)}/{_safe(source)}")
|
|
483
|
+
continue
|
|
484
|
+
if rc != 0:
|
|
485
|
+
log(
|
|
486
|
+
f"[{ts()}] Agent FAILED (headless): {_safe(shard)} — "
|
|
487
|
+
f"{_safe(source)} (exit {rc}) — stopping further files in "
|
|
488
|
+
"this shard; the working tree may be left in an "
|
|
489
|
+
"unknown/possibly-mutated state (this treats every "
|
|
490
|
+
"non-4-non-5 exit the same as a failed revert — see #1930)"
|
|
491
|
+
)
|
|
492
|
+
return FixOutcome(
|
|
493
|
+
ok=False, exhausted=exhausted, unresolved=unresolved, no_report=False
|
|
494
|
+
)
|
|
495
|
+
return FixOutcome(ok=True, exhausted=exhausted, unresolved=unresolved, no_report=False)
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
# ── Resume (skip-existing wins over max-age) ────────────────────────────────────
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
def report_age_seconds(out_dir: Path) -> float | None:
|
|
502
|
+
rp = report_path(out_dir)
|
|
503
|
+
if not rp.exists():
|
|
504
|
+
return None
|
|
505
|
+
return time.time() - rp.stat().st_mtime
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
def should_skip(
|
|
509
|
+
out_dir: Path,
|
|
510
|
+
*,
|
|
511
|
+
skip_existing: bool,
|
|
512
|
+
max_age_hours: float,
|
|
513
|
+
shard: str,
|
|
514
|
+
log: Callable[[str], None] = print,
|
|
515
|
+
) -> bool:
|
|
516
|
+
"""Resume gate. ``--skip-existing`` is evaluated **first**, so it wins over
|
|
517
|
+
``--max-age-hours``: any existing report is skipped regardless of its age.
|
|
518
|
+
``--max-age-hours`` only applies when ``--skip-existing`` is not set.
|
|
519
|
+
"""
|
|
520
|
+
age = report_age_seconds(out_dir)
|
|
521
|
+
if age is None:
|
|
522
|
+
return False
|
|
523
|
+
if skip_existing:
|
|
524
|
+
log(f"[{ts()}] SKIPPED ({shard}): --skip-existing wins over --max-age-hours")
|
|
525
|
+
return True
|
|
526
|
+
if max_age_hours > 0 and age < max_age_hours * 3600:
|
|
527
|
+
log(
|
|
528
|
+
f"[{ts()}] SKIPPED ({shard}): report {int(age / 60)}m old "
|
|
529
|
+
f"< --max-age-hours {max_age_hours}"
|
|
530
|
+
)
|
|
531
|
+
return True
|
|
532
|
+
return False
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
# ── Summary (honest score) ──────────────────────────────────────────────────────
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
def print_summary(
|
|
539
|
+
shards: Sequence[str],
|
|
540
|
+
shard_out_base: Path,
|
|
541
|
+
*,
|
|
542
|
+
log: Callable[[str], None] = print,
|
|
543
|
+
exhausted: Sequence[str] = (),
|
|
544
|
+
unresolved: Sequence[str] = (),
|
|
545
|
+
no_report: Sequence[str] = (),
|
|
546
|
+
skipped: Sequence[str] = (),
|
|
547
|
+
) -> None:
|
|
548
|
+
"""Print the per-shard honest score with Timeout and NoCoverage broken out
|
|
549
|
+
separately — never the timeout-inflated reported score (AC3).
|
|
550
|
+
|
|
551
|
+
Four run-level outcome lines are appended when non-empty — each a case
|
|
552
|
+
that previously read identically to a fully clean run (#1951):
|
|
553
|
+
|
|
554
|
+
- ``EXHAUSTED files (N)`` — ``shard/source`` entries that hit
|
|
555
|
+
``EXIT_GENERATION_EXHAUSTED`` (5): either true GenerationExhausted (a
|
|
556
|
+
fully spent retry-then-downgrade budget) or any other clean
|
|
557
|
+
``RuntimeError`` (e.g. a generation timeout, or a mutmut/Stryker
|
|
558
|
+
infrastructure failure — not exhaustion); see ``launch_survivor_fix``.
|
|
559
|
+
- ``UNRESOLVED test file (N)`` — ``shard/source`` entries where
|
|
560
|
+
``resolve_test_file`` never found a conventional pairing.
|
|
561
|
+
- ``NO REPORT shards (N)`` — shards Stryker produced no report for.
|
|
562
|
+
- ``SKIPPED shards (N)`` — shards resumed past via ``--skip-existing``/
|
|
563
|
+
``--max-age-hours``; informational only, never affects the exit code.
|
|
564
|
+
"""
|
|
565
|
+
log("=== Pipeline summary ===")
|
|
566
|
+
for shard in shards:
|
|
567
|
+
rp = report_path(Path(shard_out_base) / shard)
|
|
568
|
+
if not rp.exists():
|
|
569
|
+
log(f" {shard}: no report")
|
|
570
|
+
continue
|
|
571
|
+
s = mutation_report.score_report(rp)
|
|
572
|
+
log(
|
|
573
|
+
f" {shard}: honest={s.honest_score:.1f}% "
|
|
574
|
+
f"killed={s.killed} survived={s.survived} "
|
|
575
|
+
f"timeout={s.timeout} nocoverage={s.no_coverage}"
|
|
576
|
+
)
|
|
577
|
+
if skipped:
|
|
578
|
+
log(f"\nSKIPPED shards ({len(skipped)}): " + ", ".join(skipped))
|
|
579
|
+
if no_report:
|
|
580
|
+
log(f"\nNO REPORT shards ({len(no_report)}): " + ", ".join(no_report))
|
|
581
|
+
if unresolved:
|
|
582
|
+
log(f"\nUNRESOLVED test file ({len(unresolved)}): " + ", ".join(unresolved))
|
|
583
|
+
if exhausted:
|
|
584
|
+
log(f"\nEXHAUSTED files ({len(exhausted)}): " + ", ".join(exhausted))
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
# ── Orchestration ───────────────────────────────────────────────────────────────
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
def process_shard(
|
|
591
|
+
shard: str,
|
|
592
|
+
*,
|
|
593
|
+
repo_root: Path,
|
|
594
|
+
worktree_base: Path,
|
|
595
|
+
shard_out_base: Path,
|
|
596
|
+
stryker_bin: str,
|
|
597
|
+
model: str | None,
|
|
598
|
+
max_rounds: int,
|
|
599
|
+
skip_agent: bool,
|
|
600
|
+
skip_existing: bool,
|
|
601
|
+
max_age_hours: float,
|
|
602
|
+
log: Callable[[str], None] = print,
|
|
603
|
+
run_stryker: StrykerRunner | None = None,
|
|
604
|
+
run: Callable[..., object] = subprocess.run,
|
|
605
|
+
resolve_test_file: TestFileResolver | None = None,
|
|
606
|
+
git_run: GitRunner | None = None,
|
|
607
|
+
events: list[tuple] | None = None,
|
|
608
|
+
) -> ShardOutcome:
|
|
609
|
+
"""Process one shard end to end. Returns a :class:`ShardOutcome` whose
|
|
610
|
+
``status`` is ``"ok"`` / ``"failed"`` / ``"skipped"``, with
|
|
611
|
+
``launch_survivor_fix``'s :class:`FixOutcome` fields propagated up
|
|
612
|
+
unchanged for shards that reach the survivor-fix stage. ``events`` (when
|
|
613
|
+
supplied) records the worktree-creation and fix-launch ordering so tests
|
|
614
|
+
can assert compounding sequencing."""
|
|
615
|
+
out_dir = Path(shard_out_base) / shard
|
|
616
|
+
if should_skip(
|
|
617
|
+
out_dir,
|
|
618
|
+
skip_existing=skip_existing,
|
|
619
|
+
max_age_hours=max_age_hours,
|
|
620
|
+
shard=shard,
|
|
621
|
+
log=log,
|
|
622
|
+
):
|
|
623
|
+
return ShardOutcome("skipped", [], [], False)
|
|
624
|
+
|
|
625
|
+
wt = Path(worktree_base) / f"shard-{shard}"
|
|
626
|
+
worktree_add(repo_root, wt, git_run=git_run)
|
|
627
|
+
if events is not None:
|
|
628
|
+
events.append(("worktree_add", shard))
|
|
629
|
+
log(f"[{ts()}] Shard START: {shard}")
|
|
630
|
+
try:
|
|
631
|
+
ok = run_shard_stryker(
|
|
632
|
+
shard,
|
|
633
|
+
repo_root=repo_root,
|
|
634
|
+
worktree=wt,
|
|
635
|
+
out_dir=out_dir,
|
|
636
|
+
stryker_bin=stryker_bin,
|
|
637
|
+
log=log,
|
|
638
|
+
run_stryker=run_stryker,
|
|
639
|
+
)
|
|
640
|
+
finally:
|
|
641
|
+
worktree_remove(repo_root, wt, git_run=git_run)
|
|
642
|
+
|
|
643
|
+
if not ok:
|
|
644
|
+
return ShardOutcome("failed", [], [], False)
|
|
645
|
+
log(f"[{ts()}] Shard DONE: {shard}")
|
|
646
|
+
|
|
647
|
+
if not skip_agent:
|
|
648
|
+
fix = launch_survivor_fix(
|
|
649
|
+
shard,
|
|
650
|
+
repo_root=repo_root,
|
|
651
|
+
out_dir=out_dir,
|
|
652
|
+
config_path=shard_config_path(repo_root, shard),
|
|
653
|
+
model=model,
|
|
654
|
+
max_rounds=max_rounds,
|
|
655
|
+
stryker_bin=stryker_bin,
|
|
656
|
+
run=run,
|
|
657
|
+
resolve_test_file=resolve_test_file,
|
|
658
|
+
log=log,
|
|
659
|
+
)
|
|
660
|
+
if events is not None:
|
|
661
|
+
events.append(("fix", shard))
|
|
662
|
+
status = "ok" if fix.ok else "failed"
|
|
663
|
+
return ShardOutcome(status, fix.exhausted, fix.unresolved, fix.no_report)
|
|
664
|
+
return ShardOutcome("ok", [], [], False)
|
|
665
|
+
|
|
666
|
+
|
|
667
|
+
def run_all(
|
|
668
|
+
shards: Sequence[str],
|
|
669
|
+
*,
|
|
670
|
+
repo_root: Path,
|
|
671
|
+
worktree_base: Path,
|
|
672
|
+
shard_out_base: Path,
|
|
673
|
+
stryker_bin: str,
|
|
674
|
+
model: str | None,
|
|
675
|
+
max_rounds: int,
|
|
676
|
+
skip_agent: bool,
|
|
677
|
+
skip_existing: bool,
|
|
678
|
+
max_age_hours: float,
|
|
679
|
+
log: Callable[[str], None] = print,
|
|
680
|
+
run_stryker: StrykerRunner | None = None,
|
|
681
|
+
run: Callable[..., object] = subprocess.run,
|
|
682
|
+
resolve_test_file: TestFileResolver | None = None,
|
|
683
|
+
git_run: GitRunner | None = None,
|
|
684
|
+
events: list[tuple] | None = None,
|
|
685
|
+
) -> ShardRunResult:
|
|
686
|
+
"""Process every shard **sequentially** (compounding depends on ordering).
|
|
687
|
+
Returns a :class:`ShardRunResult` collecting, across all shards: failed
|
|
688
|
+
shards, exhausted/unresolved source files, no-report shards, and
|
|
689
|
+
skipped shards (#1951)."""
|
|
690
|
+
failed: list[str] = []
|
|
691
|
+
exhausted: list[str] = []
|
|
692
|
+
unresolved: list[str] = []
|
|
693
|
+
no_report: list[str] = []
|
|
694
|
+
skipped: list[str] = []
|
|
695
|
+
for shard in shards:
|
|
696
|
+
outcome = process_shard(
|
|
697
|
+
shard,
|
|
698
|
+
repo_root=repo_root,
|
|
699
|
+
worktree_base=worktree_base,
|
|
700
|
+
shard_out_base=shard_out_base,
|
|
701
|
+
stryker_bin=stryker_bin,
|
|
702
|
+
model=model,
|
|
703
|
+
max_rounds=max_rounds,
|
|
704
|
+
skip_agent=skip_agent,
|
|
705
|
+
skip_existing=skip_existing,
|
|
706
|
+
max_age_hours=max_age_hours,
|
|
707
|
+
log=log,
|
|
708
|
+
run_stryker=run_stryker,
|
|
709
|
+
run=run,
|
|
710
|
+
resolve_test_file=resolve_test_file,
|
|
711
|
+
git_run=git_run,
|
|
712
|
+
events=events,
|
|
713
|
+
)
|
|
714
|
+
if outcome.status == "failed":
|
|
715
|
+
failed.append(shard)
|
|
716
|
+
elif outcome.status == "skipped":
|
|
717
|
+
skipped.append(shard)
|
|
718
|
+
exhausted.extend(outcome.exhausted)
|
|
719
|
+
unresolved.extend(outcome.unresolved)
|
|
720
|
+
if outcome.no_report:
|
|
721
|
+
no_report.append(shard)
|
|
722
|
+
return ShardRunResult(failed, exhausted, unresolved, no_report, skipped)
|
|
723
|
+
|
|
724
|
+
|
|
725
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
726
|
+
parser = argparse.ArgumentParser(
|
|
727
|
+
prog="stryker_shard_pipeline.py",
|
|
728
|
+
description="Sharded Stryker pipeline with compounding worktrees and timeout-abort",
|
|
729
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
730
|
+
)
|
|
731
|
+
parser.add_argument(
|
|
732
|
+
"shards", nargs="*", default=[], help="Shards to run (default: all discovered)"
|
|
733
|
+
)
|
|
734
|
+
parser.add_argument("--repo-root", help="Repo root (default: cwd)")
|
|
735
|
+
parser.add_argument(
|
|
736
|
+
"--stryker-bin",
|
|
737
|
+
default=os.environ.get("STRYKER_BIN", "dotnet"),
|
|
738
|
+
help="Stryker executable name, or 'dotnet' to invoke a local-tool-"
|
|
739
|
+
"manifest install via 'dotnet stryker' (default: %(default)s)",
|
|
740
|
+
)
|
|
741
|
+
parser.add_argument(
|
|
742
|
+
"--model",
|
|
743
|
+
help="Generation model for the forced-headless survivor-fix loop.",
|
|
744
|
+
)
|
|
745
|
+
parser.add_argument(
|
|
746
|
+
"--max-rounds", type=int, default=3, help="Survivor-fix rounds per file (default: 3)"
|
|
747
|
+
)
|
|
748
|
+
parser.add_argument(
|
|
749
|
+
"--skip-agent", action="store_true", help="Run shards only; skip survivor-fix"
|
|
750
|
+
)
|
|
751
|
+
parser.add_argument(
|
|
752
|
+
"--skip-existing",
|
|
753
|
+
action="store_true",
|
|
754
|
+
help="Skip shards with an existing report, any age (wins over --max-age-hours)",
|
|
755
|
+
)
|
|
756
|
+
parser.add_argument(
|
|
757
|
+
"--max-age-hours",
|
|
758
|
+
type=float,
|
|
759
|
+
default=0,
|
|
760
|
+
help="Skip shards whose report is younger than N hours (ignored under --skip-existing)",
|
|
761
|
+
)
|
|
762
|
+
return parser
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
766
|
+
"""CLI entry point. Exit code follows a 3-branch priority, failure first:
|
|
767
|
+
|
|
768
|
+
- ``1`` — one or more shards failed (``run_all``'s ``failed`` list is
|
|
769
|
+
non-empty). Takes priority over exhaustion. A shard is marked failed
|
|
770
|
+
when ``launch_survivor_fix`` returns a :class:`FixOutcome` with
|
|
771
|
+
``ok=False`` — which happens on any non-zero, non-5 per-file exit code
|
|
772
|
+
(most commonly ``EXIT_REVERT_FAILED``, ``4``); this branch is where
|
|
773
|
+
that per-file signal ends up, since ``main()`` never returns ``4``
|
|
774
|
+
itself.
|
|
775
|
+
- ``EXIT_GENERATION_EXHAUSTED`` (``5``) — no shard failed, but at least
|
|
776
|
+
one of three "a survivor was not addressed this run" outcomes fired
|
|
777
|
+
(#1951): ``exhausted`` is non-empty (a true ``GenerationExhausted`` — a
|
|
778
|
+
fully spent retry-then-downgrade budget — or any other clean
|
|
779
|
+
``RuntimeError`` such as a generation timeout), ``unresolved`` is
|
|
780
|
+
non-empty (a survivor whose test file never resolved), or
|
|
781
|
+
``no_report`` is non-empty (a shard Stryker produced no report for).
|
|
782
|
+
All three previously read identically to a fully clean run; only
|
|
783
|
+
``exhausted`` got run-level visibility before this fix.
|
|
784
|
+
- ``0`` — fully clean: no failures, no exhaustions, no unresolved test
|
|
785
|
+
files, no missing reports. (``skipped`` shards — resumed past via
|
|
786
|
+
``--skip-existing``/``--max-age-hours`` — are reported in the summary
|
|
787
|
+
but never affect the exit code; see ``print_summary``.)
|
|
788
|
+
"""
|
|
789
|
+
args = build_parser().parse_args(list(sys.argv[1:] if argv is None else argv))
|
|
790
|
+
repo_root = Path(args.repo_root).resolve() if args.repo_root else Path.cwd()
|
|
791
|
+
|
|
792
|
+
try:
|
|
793
|
+
all_shards = discover_shards(repo_root)
|
|
794
|
+
except ShardSetupMissing as exc:
|
|
795
|
+
sys.stderr.write(f"error: {exc}\n")
|
|
796
|
+
return 1
|
|
797
|
+
shards = list(args.shards) if args.shards else all_shards
|
|
798
|
+
|
|
799
|
+
worktree_base = repo_root / ".stryker-worktrees"
|
|
800
|
+
shard_out_base = repo_root / "StrykerOutput" / "shards"
|
|
801
|
+
worktree_base.mkdir(parents=True, exist_ok=True)
|
|
802
|
+
shard_out_base.mkdir(parents=True, exist_ok=True)
|
|
803
|
+
|
|
804
|
+
# DOTNET_ROOT resolution is delegated to the wrapper (AC4) and exported so
|
|
805
|
+
# every shard's Stryker subprocess inherits it.
|
|
806
|
+
dotnet_root, err = wrapper.resolve_dotnet_root(
|
|
807
|
+
preset=os.environ.get("DOTNET_ROOT"),
|
|
808
|
+
candidates=wrapper.default_probe_candidates(),
|
|
809
|
+
)
|
|
810
|
+
if err is not None:
|
|
811
|
+
sys.stderr.write(err)
|
|
812
|
+
return wrapper.EXIT_NO_DOTNET_SDK
|
|
813
|
+
assert dotnet_root is not None
|
|
814
|
+
os.environ["DOTNET_ROOT"] = dotnet_root
|
|
815
|
+
|
|
816
|
+
print(f"Pipeline: {' '.join(shards)}")
|
|
817
|
+
result = run_all(
|
|
818
|
+
shards,
|
|
819
|
+
repo_root=repo_root,
|
|
820
|
+
worktree_base=worktree_base,
|
|
821
|
+
shard_out_base=shard_out_base,
|
|
822
|
+
stryker_bin=args.stryker_bin,
|
|
823
|
+
model=args.model,
|
|
824
|
+
max_rounds=args.max_rounds,
|
|
825
|
+
skip_agent=args.skip_agent,
|
|
826
|
+
skip_existing=args.skip_existing,
|
|
827
|
+
max_age_hours=args.max_age_hours,
|
|
828
|
+
)
|
|
829
|
+
|
|
830
|
+
print_summary(
|
|
831
|
+
shards,
|
|
832
|
+
shard_out_base,
|
|
833
|
+
exhausted=result.exhausted,
|
|
834
|
+
unresolved=result.unresolved,
|
|
835
|
+
no_report=result.no_report,
|
|
836
|
+
skipped=result.skipped,
|
|
837
|
+
)
|
|
838
|
+
if result.failed:
|
|
839
|
+
print(f"\nFAILED shards: {' '.join(result.failed)}")
|
|
840
|
+
return 1
|
|
841
|
+
if result.exhausted or result.unresolved or result.no_report:
|
|
842
|
+
return EXIT_GENERATION_EXHAUSTED
|
|
843
|
+
return 0
|
|
844
|
+
|
|
845
|
+
|
|
846
|
+
if __name__ == "__main__":
|
|
847
|
+
sys.exit(main())
|