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,330 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""coverage_delta_steering.py — turn per-Story coverage deltas into a live
|
|
3
|
+
Phase-5 steering signal (issue #1790).
|
|
4
|
+
|
|
5
|
+
Why this exists. `/coverage-delta` already records one snapshot per Story into
|
|
6
|
+
`coverage-history.json`, but nothing read that history *during* the phase: a
|
|
7
|
+
Pass-1 run kept closing Stories and reporting mutation-kill progress while
|
|
8
|
+
line coverage stayed flat at 74.2%, and nobody saw it until the final report —
|
|
9
|
+
by which point the phase's whole budget had gone into the wrong layer. This
|
|
10
|
+
script is the mid-phase check: after each Story's delta lands, it measures the
|
|
11
|
+
movement of the trailing Stories and fails (exit 3) once several consecutive
|
|
12
|
+
Stories have moved line coverage by less than the minimum expected per-Story
|
|
13
|
+
delta, so `/test-improve` can prompt for a Phase-1 targeting re-check before
|
|
14
|
+
spending the next Story.
|
|
15
|
+
|
|
16
|
+
Movement, per Story snapshot, is the change in `line_pct` since the previous
|
|
17
|
+
Story snapshot — or, for the first Story, since the run's
|
|
18
|
+
`baseline_line_pct`. A **negative** movement counts as flat: a Story that made
|
|
19
|
+
coverage worse is certainly not progress. A snapshot with no comparable
|
|
20
|
+
predecessor (no previous Story and no recorded baseline) has movement `None`
|
|
21
|
+
and **breaks** the streak rather than extending it — absence of measurement is
|
|
22
|
+
not evidence of flatness.
|
|
23
|
+
|
|
24
|
+
Branch movement is reported alongside but does **not** drive the verdict:
|
|
25
|
+
branch coverage is not natively available on every stack (Go), so keying the
|
|
26
|
+
gate on it would make the check silently mode-dependent.
|
|
27
|
+
|
|
28
|
+
Statuses (and exit codes):
|
|
29
|
+
|
|
30
|
+
- `flat_streak` (3) — `--consecutive` or more trailing Stories each moved less
|
|
31
|
+
than `--min-line-delta`. The caller prompts; this script does not decide
|
|
32
|
+
policy.
|
|
33
|
+
- `flat_streak_forming` (0) — the trailing Story moved less than the minimum
|
|
34
|
+
but the streak is still shorter than `--consecutive`. A distinct status
|
|
35
|
+
rather than `ok`, because "not yet a streak" is not "coverage is moving".
|
|
36
|
+
- `insufficient_history` (0) — fewer measurable Stories than `--consecutive`,
|
|
37
|
+
or a trailing Story whose movement could not be measured at all, so no
|
|
38
|
+
streak verdict is possible yet.
|
|
39
|
+
- `ok` (0) — the trailing Story moved coverage by at least the minimum.
|
|
40
|
+
- exit `2` — the history file is missing, unreadable, not JSON, not a JSON
|
|
41
|
+
array, or carries a non-snapshot element; also an out-of-range
|
|
42
|
+
`--consecutive`/`--min-line-delta`. Never reported as an all-clear.
|
|
43
|
+
|
|
44
|
+
Stdlib-only (ADR 0014/0015), Python 3.10+ floor (ADR 0031).
|
|
45
|
+
|
|
46
|
+
Usage:
|
|
47
|
+
python3 coverage_delta_steering.py \\
|
|
48
|
+
--history .dev-team-reports/test-improve/<slug>/data/coverage-history.json
|
|
49
|
+
python3 coverage_delta_steering.py --history <path> \\
|
|
50
|
+
--min-line-delta 0.25 --consecutive 3 --json
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
from __future__ import annotations
|
|
54
|
+
|
|
55
|
+
import argparse
|
|
56
|
+
import json
|
|
57
|
+
import sys
|
|
58
|
+
from pathlib import Path
|
|
59
|
+
|
|
60
|
+
DEFAULT_MIN_LINE_DELTA = 0.1
|
|
61
|
+
DEFAULT_CONSECUTIVE = 3
|
|
62
|
+
|
|
63
|
+
STATUS_FLAT = "flat_streak"
|
|
64
|
+
STATUS_FORMING = "flat_streak_forming"
|
|
65
|
+
STATUS_OK = "ok"
|
|
66
|
+
STATUS_INSUFFICIENT = "insufficient_history"
|
|
67
|
+
|
|
68
|
+
# Movements are rounded only to kill binary-float artifacts (72.4 - 72.0 =
|
|
69
|
+
# 0.40000000000000568), NOT to two decimals: rounding to 2dp before the
|
|
70
|
+
# threshold comparison would let a true 0.096-point move clear a 0.1 minimum,
|
|
71
|
+
# making a display concern decide the gate.
|
|
72
|
+
_MOVEMENT_PRECISION = 10
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class HistoryError(ValueError):
|
|
76
|
+
"""The coverage history could not be read or is not a snapshot array."""
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def load_history(path: Path) -> list[dict]:
|
|
80
|
+
"""Read `coverage-history.json`, raising HistoryError on anything that is
|
|
81
|
+
not a readable JSON array of snapshot objects."""
|
|
82
|
+
if not path.is_file():
|
|
83
|
+
raise HistoryError(f"coverage history not found: {path}")
|
|
84
|
+
try:
|
|
85
|
+
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
86
|
+
except (OSError, json.JSONDecodeError) as exc:
|
|
87
|
+
raise HistoryError(f"{path}: unreadable coverage history ({exc})") from exc
|
|
88
|
+
if not isinstance(payload, list):
|
|
89
|
+
raise HistoryError(
|
|
90
|
+
f"{path}: coverage history must be a JSON array of snapshots, "
|
|
91
|
+
f"got {type(payload).__name__}"
|
|
92
|
+
)
|
|
93
|
+
# A non-dict element means the file is corrupt or half-rewritten. Dropping
|
|
94
|
+
# it silently would leave a corrupt history indistinguishable from a phase
|
|
95
|
+
# that simply hasn't closed enough Stories yet — a clean verdict from
|
|
96
|
+
# unusable evidence, which is the one thing this gate must never produce.
|
|
97
|
+
for index, entry in enumerate(payload):
|
|
98
|
+
if not isinstance(entry, dict):
|
|
99
|
+
raise HistoryError(
|
|
100
|
+
f"{path}: coverage history entry at index {index} is a "
|
|
101
|
+
f"{type(entry).__name__}, not a snapshot object"
|
|
102
|
+
)
|
|
103
|
+
return list(payload)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def story_snapshots(history: list[dict]) -> list[dict]:
|
|
107
|
+
"""Keep only Story-attributed snapshots — the per-Story Phase-5 deltas.
|
|
108
|
+
Phase-less snapshots (`/quality-targets-converge`'s whole-suite call
|
|
109
|
+
passes no `--story`) are not Stories and must not enter the streak."""
|
|
110
|
+
return [entry for entry in history if entry.get("story")]
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _movement(current: object, previous: object) -> float | None:
|
|
114
|
+
if isinstance(current, bool) or isinstance(previous, bool):
|
|
115
|
+
return None
|
|
116
|
+
if not isinstance(current, (int, float)) or not isinstance(previous, (int, float)):
|
|
117
|
+
return None
|
|
118
|
+
return round(float(current) - float(previous), _MOVEMENT_PRECISION)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _story_rows(snapshots: list[dict]) -> list[dict]:
|
|
122
|
+
rows = []
|
|
123
|
+
for index, snapshot in enumerate(snapshots):
|
|
124
|
+
if index == 0:
|
|
125
|
+
prev_line = snapshot.get("baseline_line_pct")
|
|
126
|
+
prev_branch = snapshot.get("baseline_branch_pct")
|
|
127
|
+
else:
|
|
128
|
+
prev_line = snapshots[index - 1].get("line_pct")
|
|
129
|
+
prev_branch = snapshots[index - 1].get("branch_pct")
|
|
130
|
+
rows.append(
|
|
131
|
+
{
|
|
132
|
+
"story": snapshot.get("story"),
|
|
133
|
+
"captured_at": snapshot.get("captured_at"),
|
|
134
|
+
"line_pct": snapshot.get("line_pct"),
|
|
135
|
+
"branch_pct": snapshot.get("branch_pct"),
|
|
136
|
+
"line_movement": _movement(snapshot.get("line_pct"), prev_line),
|
|
137
|
+
"branch_movement": _movement(snapshot.get("branch_pct"), prev_branch),
|
|
138
|
+
}
|
|
139
|
+
)
|
|
140
|
+
return rows
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _trailing_flat_streak(rows: list[dict], min_line_delta: float) -> list[dict]:
|
|
144
|
+
"""The trailing run of Stories whose measured line movement is below the
|
|
145
|
+
minimum. Stops at the first Story that moved enough OR whose movement is
|
|
146
|
+
unmeasurable."""
|
|
147
|
+
streak: list[dict] = []
|
|
148
|
+
for row in reversed(rows):
|
|
149
|
+
movement = row["line_movement"]
|
|
150
|
+
if movement is None or movement >= min_line_delta:
|
|
151
|
+
break
|
|
152
|
+
streak.append(row)
|
|
153
|
+
streak.reverse()
|
|
154
|
+
return streak
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def evaluate(
|
|
158
|
+
snapshots: list[dict],
|
|
159
|
+
min_line_delta: float = DEFAULT_MIN_LINE_DELTA,
|
|
160
|
+
consecutive: int = DEFAULT_CONSECUTIVE,
|
|
161
|
+
) -> dict:
|
|
162
|
+
"""Evaluate Story-attributed `snapshots` for a flat-coverage streak."""
|
|
163
|
+
rows = _story_rows(snapshots)
|
|
164
|
+
measured = [r for r in rows if r["line_movement"] is not None]
|
|
165
|
+
streak = _trailing_flat_streak(rows, min_line_delta)
|
|
166
|
+
|
|
167
|
+
if len(streak) >= consecutive:
|
|
168
|
+
status = STATUS_FLAT
|
|
169
|
+
message = (
|
|
170
|
+
f"{len(streak)} consecutive Stories moved line coverage by less than "
|
|
171
|
+
f"{min_line_delta} points ("
|
|
172
|
+
+ ", ".join(f"{r['story']}: {r['line_movement']:+}" for r in streak)
|
|
173
|
+
+ "). Re-check Phase-1 targeting against "
|
|
174
|
+
"coverage-gap-ranking.json before spending the next Story — "
|
|
175
|
+
"mutation-kill progress on an already-covered layer cannot move "
|
|
176
|
+
"these numbers."
|
|
177
|
+
)
|
|
178
|
+
elif len(measured) < consecutive:
|
|
179
|
+
status = STATUS_INSUFFICIENT
|
|
180
|
+
message = (
|
|
181
|
+
f"{len(measured)} Story delta(s) measured; {consecutive} are needed "
|
|
182
|
+
"before a flat-coverage streak can be judged."
|
|
183
|
+
)
|
|
184
|
+
elif rows[-1]["line_movement"] is None:
|
|
185
|
+
# The trailing Story has no comparable predecessor (a null/absent
|
|
186
|
+
# line_pct in the appended snapshot), so its movement could not be
|
|
187
|
+
# measured. Neither `ok` nor `flat_streak` is honest here.
|
|
188
|
+
status = STATUS_INSUFFICIENT
|
|
189
|
+
message = (
|
|
190
|
+
f"The latest Story ({rows[-1]['story']}) could not be measured — "
|
|
191
|
+
"its snapshot carries no comparable line_pct, so no coverage "
|
|
192
|
+
"movement verdict is possible for it."
|
|
193
|
+
)
|
|
194
|
+
elif streak:
|
|
195
|
+
# Below the streak threshold but still not moving: reporting this as
|
|
196
|
+
# `ok` would claim "targeting is producing coverage movement" about a
|
|
197
|
+
# Story that moved less than the minimum — the false all-clear this
|
|
198
|
+
# gate exists to prevent. Exit code stays 0; only the wording and
|
|
199
|
+
# status carry the warning.
|
|
200
|
+
status = STATUS_FORMING
|
|
201
|
+
message = (
|
|
202
|
+
f"The latest {len(streak)} Story/Stories moved line coverage by less "
|
|
203
|
+
f"than {min_line_delta} points ({len(streak)} of {consecutive} "
|
|
204
|
+
"needed for a flat-coverage streak). Watch the next Story's delta "
|
|
205
|
+
"before spending more of the phase on this layer."
|
|
206
|
+
)
|
|
207
|
+
else:
|
|
208
|
+
status = STATUS_OK
|
|
209
|
+
message = (
|
|
210
|
+
f"Latest Story moved line coverage by "
|
|
211
|
+
f"{rows[-1]['line_movement']:+} points — targeting is producing "
|
|
212
|
+
"coverage movement."
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
average = (
|
|
216
|
+
round(sum(r["line_movement"] for r in measured) / len(measured), 2)
|
|
217
|
+
if measured
|
|
218
|
+
else None
|
|
219
|
+
)
|
|
220
|
+
return {
|
|
221
|
+
"status": status,
|
|
222
|
+
"stories_evaluated": len(rows),
|
|
223
|
+
"stories_measured": len(measured),
|
|
224
|
+
"min_line_delta": min_line_delta,
|
|
225
|
+
"consecutive_threshold": consecutive,
|
|
226
|
+
"streak": len(streak),
|
|
227
|
+
"flat_stories": streak,
|
|
228
|
+
"running_average_line_movement": average,
|
|
229
|
+
"stories": rows,
|
|
230
|
+
"message": message,
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def render_text(result: dict) -> str:
|
|
235
|
+
lines = [
|
|
236
|
+
f"Coverage-delta steering: {result['status']}",
|
|
237
|
+
result["message"],
|
|
238
|
+
"",
|
|
239
|
+
f"{'story':<24} {'line %':>8} {'Δ line':>8} {'Δ branch':>9}",
|
|
240
|
+
]
|
|
241
|
+
for row in result["stories"]:
|
|
242
|
+
line_move = row["line_movement"]
|
|
243
|
+
branch_move = row["branch_movement"]
|
|
244
|
+
lines.append(
|
|
245
|
+
f"{row['story']!s:<24} "
|
|
246
|
+
f"{row['line_pct'] if row['line_pct'] is not None else '-':>8} "
|
|
247
|
+
f"{line_move if line_move is not None else '-':>8} "
|
|
248
|
+
f"{branch_move if branch_move is not None else '-':>9}"
|
|
249
|
+
)
|
|
250
|
+
lines.append("")
|
|
251
|
+
lines.append(
|
|
252
|
+
f"running average line movement: {result['running_average_line_movement']}"
|
|
253
|
+
)
|
|
254
|
+
return "\n".join(lines)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def main(argv: list[str] | None = None) -> int:
|
|
258
|
+
parser = argparse.ArgumentParser(
|
|
259
|
+
prog="coverage_delta_steering.py",
|
|
260
|
+
description=(
|
|
261
|
+
"Flag several consecutive Stories whose coverage delta is "
|
|
262
|
+
"near zero, so Phase-1 targeting can be re-checked mid-phase."
|
|
263
|
+
),
|
|
264
|
+
)
|
|
265
|
+
parser.add_argument(
|
|
266
|
+
"--history",
|
|
267
|
+
type=Path,
|
|
268
|
+
required=True,
|
|
269
|
+
help="Path to coverage-history.json.",
|
|
270
|
+
)
|
|
271
|
+
parser.add_argument(
|
|
272
|
+
"--min-line-delta",
|
|
273
|
+
type=float,
|
|
274
|
+
default=DEFAULT_MIN_LINE_DELTA,
|
|
275
|
+
help=(
|
|
276
|
+
"Minimum expected per-Story line-coverage movement in percentage "
|
|
277
|
+
f"points (default: {DEFAULT_MIN_LINE_DELTA})."
|
|
278
|
+
),
|
|
279
|
+
)
|
|
280
|
+
parser.add_argument(
|
|
281
|
+
"--consecutive",
|
|
282
|
+
type=int,
|
|
283
|
+
default=DEFAULT_CONSECUTIVE,
|
|
284
|
+
help=(
|
|
285
|
+
"How many consecutive below-minimum Stories constitute a flat "
|
|
286
|
+
f"streak (default: {DEFAULT_CONSECUTIVE})."
|
|
287
|
+
),
|
|
288
|
+
)
|
|
289
|
+
parser.add_argument("--json", action="store_true", help="machine-readable output")
|
|
290
|
+
args = parser.parse_args(argv)
|
|
291
|
+
|
|
292
|
+
# An out-of-range threshold must fail loudly rather than produce a nonsense
|
|
293
|
+
# verdict: `--consecutive 0` makes `len(streak) >= consecutive` trivially
|
|
294
|
+
# true for any history (including an empty one), and a negative
|
|
295
|
+
# `--min-line-delta` makes every movement — regressions included — count
|
|
296
|
+
# as progress.
|
|
297
|
+
if args.consecutive < 1:
|
|
298
|
+
parser.error("--consecutive must be >= 1")
|
|
299
|
+
if args.min_line_delta < 0:
|
|
300
|
+
parser.error("--min-line-delta must be >= 0")
|
|
301
|
+
|
|
302
|
+
try:
|
|
303
|
+
history = load_history(args.history)
|
|
304
|
+
except HistoryError as exc:
|
|
305
|
+
if args.json:
|
|
306
|
+
print(json.dumps({"error": str(exc)}, indent=2))
|
|
307
|
+
else:
|
|
308
|
+
print(f"ERROR: {exc}", file=sys.stderr)
|
|
309
|
+
return 2
|
|
310
|
+
|
|
311
|
+
result = evaluate(
|
|
312
|
+
story_snapshots(history),
|
|
313
|
+
min_line_delta=args.min_line_delta,
|
|
314
|
+
consecutive=args.consecutive,
|
|
315
|
+
)
|
|
316
|
+
print(json.dumps(result, indent=2) if args.json else render_text(result))
|
|
317
|
+
return 3 if result["status"] == STATUS_FLAT else 0
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
if __name__ == "__main__": # pragma: no cover
|
|
321
|
+
# render_text()'s "Δ line"/"Δ branch" headers crash with
|
|
322
|
+
# UnicodeEncodeError on a non-UTF-8 console (e.g. Windows' default
|
|
323
|
+
# cp1252) before any output reaches the caller. Forcing UTF-8 here only
|
|
324
|
+
# affects real script invocation (this guard never runs for an in-process
|
|
325
|
+
# `import coverage_delta_steering; coverage_delta_steering.main(...)`
|
|
326
|
+
# caller, e.g. a test using capsys), matching every other script's
|
|
327
|
+
# console-encoding independence.
|
|
328
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
329
|
+
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
330
|
+
sys.exit(main())
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""coverage_discovery_dotnet.py — discover and classify every project in a
|
|
3
|
+
.NET solution for multi-project coverage discovery (issue #1759, Slice 2).
|
|
4
|
+
|
|
5
|
+
Enumerates every project via `dotnet sln <solution> list` and classifies
|
|
6
|
+
each `.csproj` using `coverage_config.TestClassification`, checking in
|
|
7
|
+
order:
|
|
8
|
+
|
|
9
|
+
1. An inline `Microsoft.NET.Test.Sdk` `PackageReference` in the `.csproj`
|
|
10
|
+
itself (matched on `Include="Microsoft.NET.Test.Sdk"`, case-insensitively
|
|
11
|
+
per NuGet package ID convention, regardless of attribute order or
|
|
12
|
+
whether a `Version` attribute is present — this also covers Central
|
|
13
|
+
Package Management's version-less inline form) -> `TEST`.
|
|
14
|
+
2. If absent inline, walk every `Directory.Build.props` from the project's
|
|
15
|
+
directory up to the solution root (inclusive). An **unconditioned** match
|
|
16
|
+
(no `Condition` attribute on the `PackageReference` itself or its
|
|
17
|
+
enclosing `ItemGroup`) -> `TEST`. A match that exists only inside a
|
|
18
|
+
`Condition` -> `AMBIGUOUS` (this script deliberately never evaluates
|
|
19
|
+
MSBuild conditions, matching this repo's stdlib-only,
|
|
20
|
+
no-runner-execution convention already established by
|
|
21
|
+
`coverage_readiness.py`).
|
|
22
|
+
3. No match anywhere -> `NOT_TEST`.
|
|
23
|
+
|
|
24
|
+
**Known, documented limitation.** A `GlobalPackageReference` declared in
|
|
25
|
+
`Directory.Packages.props` (MSBuild Central Package Management's other,
|
|
26
|
+
zero-inline-reference mechanism, distinct from the `Directory.Build.props`
|
|
27
|
+
ancestor walk above) is not walked. A project relying solely on it
|
|
28
|
+
classifies `NOT_TEST` — see the plan's Risks section.
|
|
29
|
+
|
|
30
|
+
**Security hardening.** Every resolved project path is verified to stay
|
|
31
|
+
within the resolved repository root before it is read or classified — a
|
|
32
|
+
`.sln` entry containing `..` or an absolute path can never cause this
|
|
33
|
+
module to read or XML-parse a file outside the repository. Separator
|
|
34
|
+
normalization (see `_parse_sln_list_output`, issue #1828) happens before
|
|
35
|
+
that check, so a backslash-separated `..\\outside\\Evil.csproj` is refused
|
|
36
|
+
like its forward-slash form instead of slipping past as one opaque
|
|
37
|
+
filename. Every `.csproj`
|
|
38
|
+
and `Directory.Build.props` file is also screened for `<!DOCTYPE`/
|
|
39
|
+
`<!ENTITY` declarations before being parsed (entity-expansion hardening) —
|
|
40
|
+
neither file legitimately carries either.
|
|
41
|
+
|
|
42
|
+
The `dotnet` subprocess boundary (`shutil.which("dotnet")` and
|
|
43
|
+
`subprocess.run([...])`) is mocked/stubbed in this module's unit tests — no
|
|
44
|
+
real .NET SDK is required to run them, keeping this module inside the
|
|
45
|
+
required `ci-local.sh`/pre-push pytest gate.
|
|
46
|
+
|
|
47
|
+
Stdlib-only (ADR 0014/0015).
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
from __future__ import annotations
|
|
51
|
+
|
|
52
|
+
import os
|
|
53
|
+
import shutil
|
|
54
|
+
import subprocess
|
|
55
|
+
import sys
|
|
56
|
+
import xml.etree.ElementTree as ET
|
|
57
|
+
from pathlib import Path
|
|
58
|
+
|
|
59
|
+
_HERE = Path(__file__).resolve().parent
|
|
60
|
+
sys.path.insert(0, str(_HERE))
|
|
61
|
+
|
|
62
|
+
import coverage_config
|
|
63
|
+
from coverage_config import TestClassification
|
|
64
|
+
|
|
65
|
+
_TEST_SDK_PACKAGE = "Microsoft.NET.Test.Sdk"
|
|
66
|
+
|
|
67
|
+
# `dotnet sln ... list` per-run wall-clock timeout, matching this repo's
|
|
68
|
+
# convention in sibling scripts (e.g. codebase_recon.py).
|
|
69
|
+
_SLN_LIST_TIMEOUT_SECONDS = 120
|
|
70
|
+
|
|
71
|
+
# The disallowed-XML-marker tuple is shared: coverage_config.UNSAFE_XML_MARKERS.
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def discover_dotnet_projects(repo_root):
|
|
75
|
+
"""Discover and classify every project in `repo_root`'s `.sln`.
|
|
76
|
+
|
|
77
|
+
Returns a list of `{"path": <repo-relative project path as returned by
|
|
78
|
+
"dotnet sln list">, "classification": TestClassification}` entries on
|
|
79
|
+
success, `coverage_config.DISCOVERY_NOT_APPLICABLE` when no `.sln` is
|
|
80
|
+
present, or `coverage_config.discovery_error(...)` for any
|
|
81
|
+
tooling/parsing failure.
|
|
82
|
+
"""
|
|
83
|
+
root = Path(repo_root).resolve()
|
|
84
|
+
|
|
85
|
+
sln_files = sorted(root.glob("*.sln"))
|
|
86
|
+
if not sln_files:
|
|
87
|
+
return coverage_config.DISCOVERY_NOT_APPLICABLE
|
|
88
|
+
if len(sln_files) > 1:
|
|
89
|
+
names = ", ".join(f.name for f in sln_files)
|
|
90
|
+
return coverage_config.discovery_error(
|
|
91
|
+
f"Multiple .sln files found at repository root: {names}. "
|
|
92
|
+
"Multi-solution repositories are not supported for discovery; "
|
|
93
|
+
"leave exactly one .sln at the repository root."
|
|
94
|
+
)
|
|
95
|
+
sln_path = sln_files[0]
|
|
96
|
+
|
|
97
|
+
dotnet_path = shutil.which("dotnet")
|
|
98
|
+
if dotnet_path is None:
|
|
99
|
+
return coverage_config.discovery_error(
|
|
100
|
+
"The `dotnet` CLI is not on PATH; install the .NET SDK to "
|
|
101
|
+
"enable solution discovery."
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
result = subprocess.run( # noqa: PLW1510 — returncode is checked explicitly below to return a discovery_error rather than raise
|
|
106
|
+
[dotnet_path, "sln", str(sln_path.resolve()), "list"],
|
|
107
|
+
cwd=str(root),
|
|
108
|
+
capture_output=True,
|
|
109
|
+
text=True,
|
|
110
|
+
env={**os.environ, "DOTNET_CLI_UI_LANGUAGE": "en"},
|
|
111
|
+
timeout=_SLN_LIST_TIMEOUT_SECONDS,
|
|
112
|
+
)
|
|
113
|
+
except subprocess.TimeoutExpired:
|
|
114
|
+
return coverage_config.discovery_error(
|
|
115
|
+
f"`dotnet sln {sln_path.name} list` timed out after "
|
|
116
|
+
f"{_SLN_LIST_TIMEOUT_SECONDS}s."
|
|
117
|
+
)
|
|
118
|
+
except OSError as exc:
|
|
119
|
+
return coverage_config.discovery_error(
|
|
120
|
+
f"Failed to run `dotnet sln {sln_path.name} list`: {exc}"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
if result.returncode != 0:
|
|
124
|
+
return coverage_config.discovery_error(
|
|
125
|
+
f"`dotnet sln {sln_path.name} list` failed: {result.stderr.strip()}"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
project_rel_paths = _parse_sln_list_output(result.stdout)
|
|
129
|
+
|
|
130
|
+
projects = []
|
|
131
|
+
for rel_path in project_rel_paths:
|
|
132
|
+
absolute_problem = _absolute_entry_problem(rel_path)
|
|
133
|
+
if absolute_problem is not None:
|
|
134
|
+
return coverage_config.discovery_error(
|
|
135
|
+
f"Solution '{sln_path.name}' references project "
|
|
136
|
+
f"'{rel_path}', which {absolute_problem}. Only "
|
|
137
|
+
"solution-relative project paths are supported; refusing to "
|
|
138
|
+
"read or classify it."
|
|
139
|
+
)
|
|
140
|
+
csproj_path = (root / rel_path).resolve()
|
|
141
|
+
if not coverage_config.is_within(csproj_path, root):
|
|
142
|
+
return coverage_config.discovery_error(
|
|
143
|
+
f"Solution '{sln_path.name}' references project "
|
|
144
|
+
f"'{rel_path}', which resolves outside the repository "
|
|
145
|
+
f"root ('{root}'); refusing to read or classify it."
|
|
146
|
+
)
|
|
147
|
+
if not csproj_path.is_file():
|
|
148
|
+
return coverage_config.discovery_error(
|
|
149
|
+
f"Solution '{sln_path.name}' references project "
|
|
150
|
+
f"'{rel_path}' but the file does not exist on disk."
|
|
151
|
+
)
|
|
152
|
+
classification = _classify_project(csproj_path, root)
|
|
153
|
+
if isinstance(classification, dict):
|
|
154
|
+
return classification # a discovery_error propagated up
|
|
155
|
+
projects.append({"path": rel_path, "classification": classification})
|
|
156
|
+
|
|
157
|
+
return projects
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _absolute_entry_problem(rel_path: str) -> str | None:
|
|
161
|
+
"""Return a reason when a (already separator-normalized) `.sln` entry is an
|
|
162
|
+
absolute path rather than a solution-relative one, else `None`.
|
|
163
|
+
|
|
164
|
+
Checked explicitly rather than left to `PurePosixPath.is_absolute()`,
|
|
165
|
+
which is platform-divergent for exactly the shapes a Windows-authored
|
|
166
|
+
solution produces: a drive-letter path like `C:/Projects/App.csproj` is
|
|
167
|
+
NOT absolute on POSIX, so it would silently become a nonsense subpath of
|
|
168
|
+
the repo root and fail with a misleading "does not exist on disk" instead
|
|
169
|
+
of naming the real problem. A UNC path normalizes to a leading `//`, which
|
|
170
|
+
IS absolute on POSIX — so without this check the same input produces two
|
|
171
|
+
different diagnostics depending on the runner's OS."""
|
|
172
|
+
if len(rel_path) >= 2 and rel_path[1] == ":" and rel_path[0].isalpha():
|
|
173
|
+
return "is an absolute Windows path (drive letter)"
|
|
174
|
+
if rel_path.startswith("//"):
|
|
175
|
+
return "is an absolute UNC network path"
|
|
176
|
+
if rel_path.startswith("/"):
|
|
177
|
+
return "is an absolute path"
|
|
178
|
+
return None
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _parse_sln_list_output(stdout: str) -> list:
|
|
182
|
+
"""Parse `dotnet sln list`'s stdout into a list of repo-relative project
|
|
183
|
+
paths in POSIX form, skipping the `Project(s)` header and its underline.
|
|
184
|
+
|
|
185
|
+
**Separator normalization (issue #1828).** `dotnet sln list` echoes the
|
|
186
|
+
separators the `.sln` was authored with, and a Windows-authored solution
|
|
187
|
+
emits backslashes. On POSIX `pathlib` treats `src\\App\\App.csproj` as a
|
|
188
|
+
single opaque filename — `PurePath(...).name` returns the whole string —
|
|
189
|
+
so the `.csproj` is never found and every project in the solution fails
|
|
190
|
+
the "does not exist on disk" check even though the file is present. Since
|
|
191
|
+
most enterprise .NET solutions are Windows-authored and their CI is
|
|
192
|
+
commonly Linux, that made .NET discovery non-functional for them.
|
|
193
|
+
|
|
194
|
+
Backslashes are therefore normalized to `/` here, once, at parse time.
|
|
195
|
+
This is deliberately unconditional rather than POSIX-only: it keeps the
|
|
196
|
+
recorded path identity — the string every caller compares against
|
|
197
|
+
`included`/`excluded` entries — byte-identical across platforms, so a
|
|
198
|
+
`coverage-config.json` written on Windows still reconciles on Linux.
|
|
199
|
+
Normalizing before the caller's containment check also keeps the `..`
|
|
200
|
+
traversal guard effective on backslash-separated escapes, which
|
|
201
|
+
previously slipped past it as opaque filenames.
|
|
202
|
+
|
|
203
|
+
Accepted limitation: a genuine POSIX filename containing a literal
|
|
204
|
+
backslash would be rewritten. MSBuild solution files always use `\\` as a
|
|
205
|
+
separator, so such a path does not occur in `.sln` output."""
|
|
206
|
+
projects = []
|
|
207
|
+
for raw_line in stdout.splitlines():
|
|
208
|
+
line = raw_line.strip()
|
|
209
|
+
if not line:
|
|
210
|
+
continue
|
|
211
|
+
if line == "Project(s)":
|
|
212
|
+
continue
|
|
213
|
+
if set(line) == {"-"}:
|
|
214
|
+
continue
|
|
215
|
+
projects.append(line.replace("\\", "/"))
|
|
216
|
+
return projects
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
# The entity-expansion screen this module introduced now lives in
|
|
220
|
+
# `coverage_config` so every discovery stack that parses repository-supplied
|
|
221
|
+
# XML shares ONE implementation — the Java stack (#1765) originally shipped
|
|
222
|
+
# without a screen at all, which is exactly the drift a shared helper
|
|
223
|
+
# prevents. Call sites below use `coverage_config.read_and_screen_xml`
|
|
224
|
+
# directly: private re-export aliases would give one shared symbol two names
|
|
225
|
+
# depending on which file you are reading.
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _classify_project(csproj_path: Path, solution_root: Path):
|
|
229
|
+
"""Classify a single project's `.csproj`, walking ancestor
|
|
230
|
+
`Directory.Build.props` files (never climbing above `solution_root`)
|
|
231
|
+
when no inline marker is found. Each `Directory.Build.props` found is
|
|
232
|
+
resolved and containment-checked against `solution_root` before being
|
|
233
|
+
read or parsed — mirroring the JS module's symlinked-manifest handling
|
|
234
|
+
(`coverage_discovery_js._classify_package`) — so a symlinked
|
|
235
|
+
`Directory.Build.props` pointing outside the solution root is refused
|
|
236
|
+
rather than read. Returns a `TestClassification` value, or
|
|
237
|
+
`coverage_config.discovery_error(...)` if the `.csproj` (or an ancestor
|
|
238
|
+
`Directory.Build.props`) is not well-formed XML, is unreadable, escapes
|
|
239
|
+
`solution_root`, or fails the entity-expansion safety check."""
|
|
240
|
+
data, unsafe = coverage_config.read_and_screen_xml(csproj_path)
|
|
241
|
+
if unsafe is not None:
|
|
242
|
+
return unsafe
|
|
243
|
+
try:
|
|
244
|
+
csproj_root = ET.fromstring(data)
|
|
245
|
+
except ET.ParseError as exc:
|
|
246
|
+
return coverage_config.discovery_error(
|
|
247
|
+
f"Could not parse '{csproj_path}' as XML: {exc}"
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
if _test_sdk_reference_kind(csproj_root) is not None:
|
|
251
|
+
return TestClassification.TEST
|
|
252
|
+
|
|
253
|
+
found_conditioned = False
|
|
254
|
+
current_dir = csproj_path.parent
|
|
255
|
+
while coverage_config.is_within(current_dir, solution_root):
|
|
256
|
+
props_path = current_dir / "Directory.Build.props"
|
|
257
|
+
if props_path.is_file():
|
|
258
|
+
try:
|
|
259
|
+
resolved_props_path = props_path.resolve()
|
|
260
|
+
except OSError as exc:
|
|
261
|
+
return coverage_config.discovery_error(
|
|
262
|
+
f"Could not resolve '{props_path}': {exc}"
|
|
263
|
+
)
|
|
264
|
+
if not coverage_config.is_within(resolved_props_path, solution_root):
|
|
265
|
+
return coverage_config.discovery_error(
|
|
266
|
+
f"'{props_path}' resolves to '{resolved_props_path}', "
|
|
267
|
+
f"which is outside the solution root "
|
|
268
|
+
f"('{solution_root}'); refusing to read or parse it."
|
|
269
|
+
)
|
|
270
|
+
props_data, unsafe = coverage_config.read_and_screen_xml(resolved_props_path)
|
|
271
|
+
if unsafe is not None:
|
|
272
|
+
return unsafe
|
|
273
|
+
try:
|
|
274
|
+
props_root = ET.fromstring(props_data)
|
|
275
|
+
except ET.ParseError as exc:
|
|
276
|
+
return coverage_config.discovery_error(
|
|
277
|
+
f"Could not parse '{props_path}' as XML: {exc}"
|
|
278
|
+
)
|
|
279
|
+
kind = _test_sdk_reference_kind(props_root)
|
|
280
|
+
if kind == "unconditioned":
|
|
281
|
+
return TestClassification.TEST
|
|
282
|
+
if kind == "conditioned":
|
|
283
|
+
found_conditioned = True
|
|
284
|
+
if current_dir == solution_root:
|
|
285
|
+
break
|
|
286
|
+
current_dir = current_dir.parent
|
|
287
|
+
|
|
288
|
+
if found_conditioned:
|
|
289
|
+
return TestClassification.AMBIGUOUS
|
|
290
|
+
return TestClassification.NOT_TEST
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def _test_sdk_reference_kind(root) -> str | None:
|
|
294
|
+
"""Return `"unconditioned"`, `"conditioned"`, or `None` for whether
|
|
295
|
+
`root` contains a `Microsoft.NET.Test.Sdk` `PackageReference` (matched
|
|
296
|
+
case-insensitively, per NuGet package ID convention), and whether it (or
|
|
297
|
+
its enclosing `ItemGroup`) carries an MSBuild `Condition` attribute.
|
|
298
|
+
This script deliberately never evaluates the condition's truth value —
|
|
299
|
+
presence alone drives the distinction."""
|
|
300
|
+
found_conditioned = False
|
|
301
|
+
for item_group in root.iter():
|
|
302
|
+
if coverage_config.local_name(item_group.tag) != "ItemGroup":
|
|
303
|
+
continue
|
|
304
|
+
group_conditioned = "Condition" in item_group.attrib
|
|
305
|
+
for child in item_group:
|
|
306
|
+
if coverage_config.local_name(child.tag) != "PackageReference":
|
|
307
|
+
continue
|
|
308
|
+
include = (child.get("Include") or "").strip().casefold()
|
|
309
|
+
if include != _TEST_SDK_PACKAGE.casefold():
|
|
310
|
+
continue
|
|
311
|
+
if group_conditioned or "Condition" in child.attrib:
|
|
312
|
+
found_conditioned = True
|
|
313
|
+
else:
|
|
314
|
+
return "unconditioned"
|
|
315
|
+
return "conditioned" if found_conditioned else None
|