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,623 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""coverage_config.py — shared coverage-config module for multi-project
|
|
3
|
+
coverage discovery (issue #1759).
|
|
4
|
+
|
|
5
|
+
`coverage-baseline` and `coverage-delta` previously relied on a single,
|
|
6
|
+
hand-maintained coverage command per manifest. For a multi-project .NET
|
|
7
|
+
solution or JS/TS workspace, that meant a hand-picked, never-revisited
|
|
8
|
+
inclusion/exclusion list — the incident's root cause. This module owns the
|
|
9
|
+
mechanics both skills share: bootstrapping/loading `coverage-config.json`,
|
|
10
|
+
drift-checking it against fresh discovery, weighted-merging per-project
|
|
11
|
+
coverage reports, and flagging a changed measurement basis after a
|
|
12
|
+
bootstrap.
|
|
13
|
+
|
|
14
|
+
**Identity contract.** An accounting unit's "path" is always the exact,
|
|
15
|
+
unmodified string the corresponding `discover_*` function returns for that
|
|
16
|
+
entry — whatever each stack calls the unit natively (a .NET *project*, a JS/TS
|
|
17
|
+
*package*, a Maven *module*, a Gradle *subproject*). No cross-stack
|
|
18
|
+
normalization, case-folding, or
|
|
19
|
+
separator translation is ever applied when matching a discovered path
|
|
20
|
+
against `included`/`excluded` entries — a POSIX-style path and an arbitrary
|
|
21
|
+
opaque label are both treated as opaque strings and compared for exact
|
|
22
|
+
equality only.
|
|
23
|
+
|
|
24
|
+
Stdlib-only (ADR 0014/0015).
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
import dataclasses
|
|
30
|
+
import json
|
|
31
|
+
import os
|
|
32
|
+
import re
|
|
33
|
+
import tempfile
|
|
34
|
+
from datetime import datetime, timezone
|
|
35
|
+
from enum import Enum
|
|
36
|
+
from pathlib import Path
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class TestClassification(Enum):
|
|
40
|
+
"""How a discovered project/package was classified by a `discover_*`
|
|
41
|
+
function. `AMBIGUOUS` covers markers this repo's discovery deliberately
|
|
42
|
+
declines to resolve with certainty (e.g. a conditioned MSBuild property)
|
|
43
|
+
— it is never treated as a negative ("not a test project") result."""
|
|
44
|
+
|
|
45
|
+
TEST = "test"
|
|
46
|
+
NOT_TEST = "not_test"
|
|
47
|
+
AMBIGUOUS = "ambiguous"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def needs_accounting(classification: TestClassification) -> bool:
|
|
51
|
+
"""True when a discovered project must appear in `included` or
|
|
52
|
+
`excluded` (with a reason) — true for `TEST` and `AMBIGUOUS`, false only
|
|
53
|
+
for `NOT_TEST`."""
|
|
54
|
+
return classification in (TestClassification.TEST, TestClassification.AMBIGUOUS)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# Shared discovery-result signals — one canonical contract, imported by every
|
|
58
|
+
# `coverage_discovery_<stack>.py` module rather than each independently
|
|
59
|
+
# converging on a matching shape.
|
|
60
|
+
DISCOVERY_NOT_APPLICABLE = {"signal": "not_applicable"}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def discovery_error(message: str) -> dict:
|
|
64
|
+
"""Return the shared discovery-error signal shape, naming `message`."""
|
|
65
|
+
return {"signal": "error", "message": message}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def is_within(resolved: Path, root: Path) -> bool:
|
|
69
|
+
"""True when `resolved` (already resolved — this performs no resolution
|
|
70
|
+
of its own) equals `root` or is nested inside it. The shared containment
|
|
71
|
+
predicate every `discover_<stack>` module applies before reading or
|
|
72
|
+
classifying a resolved path — previously factored only in
|
|
73
|
+
`coverage_discovery_java.py` and open-coded independently in
|
|
74
|
+
`coverage_discovery_js.py` and `coverage_discovery_dotnet.py` (issues
|
|
75
|
+
#1839/#1848/#1851).
|
|
76
|
+
|
|
77
|
+
Canonical argument order — read this before adding a new call site:
|
|
78
|
+
`is_within(item, container)`. The FIRST argument is the path whose
|
|
79
|
+
containment is being checked; the SECOND is the directory it must be
|
|
80
|
+
inside. Equivalently, `is_within(item, container)` reads as "is `item`
|
|
81
|
+
within `container`?" — same order as `coverage_discovery_dotnet.py`'s and
|
|
82
|
+
`stryker_shard_setup.py`'s calls. #1903 found an independent
|
|
83
|
+
reimplementation in `run_integration_eval.py` with this order inverted
|
|
84
|
+
(container first); that inversion is exactly what a reader moving
|
|
85
|
+
between files cannot safely assume is consistent, hence this note.
|
|
86
|
+
"""
|
|
87
|
+
return resolved == root or root in resolved.parents
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# Namespace prefix stripped by `local_name` below. Anchored to a leading
|
|
91
|
+
# `{...}` only, rather than searching for the LAST `}` in the tag string —
|
|
92
|
+
# the anchored form is what stays correct for a local name that itself
|
|
93
|
+
# legitimately contains `}` (never happens in practice, but the anchoring
|
|
94
|
+
# costs nothing and is strictly safer).
|
|
95
|
+
_XML_NS_RE = re.compile(r"^\{[^}]*\}")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def local_name(tag) -> str:
|
|
99
|
+
"""The namespace-stripped local name of an ElementTree tag (`{ns}Tag` ->
|
|
100
|
+
`Tag`). Tolerates a non-`str` tag (returns `""`) — ElementTree gives
|
|
101
|
+
comment/processing-instruction nodes a non-string, callable `tag`, which
|
|
102
|
+
a `tag.split("}", 1)`-style implementation raises on. The shared,
|
|
103
|
+
canonical implementation: `coverage_discovery_java.py` originally had
|
|
104
|
+
this exact regex-anchored, non-str-safe version; `coverage_discovery_dotnet.py`
|
|
105
|
+
had a second, less-safe `tag.split("}", 1)[-1]` implementation (issues
|
|
106
|
+
#1839/#1848/#1851)."""
|
|
107
|
+
return _XML_NS_RE.sub("", tag if isinstance(tag, str) else "")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# A build file consumed by discovery never legitimately carries a DOCTYPE or
|
|
111
|
+
# ENTITY declaration — presence of either is a zero-false-positive signal of
|
|
112
|
+
# an entity-expansion attempt, not a legitimate build file.
|
|
113
|
+
UNSAFE_XML_MARKERS = ("<!DOCTYPE", "<!ENTITY")
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def find_unsafe_xml_marker(data: bytes):
|
|
117
|
+
"""Return the first disallowed marker (`UNSAFE_XML_MARKERS`) found in
|
|
118
|
+
`data`'s raw bytes, or in `data` decoded as UTF-16LE/UTF-16BE — the other
|
|
119
|
+
encodings expat auto-detects and natively parses besides UTF-8.
|
|
120
|
+
ASCII/Latin-1 are byte-identical to UTF-8 for these ASCII-only tokens, so
|
|
121
|
+
no separate check is needed for those; decoding the raw bytes as Latin-1
|
|
122
|
+
(a total, never-raising 1:1 byte<->codepoint mapping) covers the
|
|
123
|
+
UTF-8/ASCII/Latin-1 case in one pass. Returns `None` when no marker is
|
|
124
|
+
found in any of the three views."""
|
|
125
|
+
raw_text = data.decode("latin-1")
|
|
126
|
+
utf16le_text = data.decode("utf-16le", errors="ignore")
|
|
127
|
+
utf16be_text = data.decode("utf-16be", errors="ignore")
|
|
128
|
+
for marker in UNSAFE_XML_MARKERS:
|
|
129
|
+
if marker in raw_text or marker in utf16le_text or marker in utf16be_text:
|
|
130
|
+
return marker
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def read_and_screen_xml(path: Path):
|
|
135
|
+
"""Read `path` ONCE as bytes and screen the FULL content — never only a
|
|
136
|
+
fixed-size prefix — for a disallowed `<!DOCTYPE`/`<!ENTITY` declaration
|
|
137
|
+
(XML entity-expansion hardening). `xml.etree.ElementTree` is explicitly
|
|
138
|
+
not secure against maliciously constructed data, and discovery parses
|
|
139
|
+
repository-supplied build files, so every stack's XML read routes through
|
|
140
|
+
this one screen.
|
|
141
|
+
|
|
142
|
+
Returns `(data, None)` when the file looks safe to parse — the caller
|
|
143
|
+
parses these SAME already-read bytes via `ET.fromstring`, never re-opening
|
|
144
|
+
`path`, which closes the check-then-use double-open gap a
|
|
145
|
+
`read`-then-`ET.parse(path)` pair leaves open. Returns
|
|
146
|
+
`(None, discovery_error(...))` otherwise (unreadable file, or a
|
|
147
|
+
disallowed marker found).
|
|
148
|
+
|
|
149
|
+
Shared by every discovery stack that parses repository-supplied XML — a
|
|
150
|
+
second copy of this screen is a review finding, not a style choice: one
|
|
151
|
+
stack once shipped without a screen at all, which is exactly the drift a
|
|
152
|
+
single shared implementation prevents. Callers are deliberately not
|
|
153
|
+
enumerated here; a shared helper should not name its consumers."""
|
|
154
|
+
try:
|
|
155
|
+
data = path.read_bytes()
|
|
156
|
+
except OSError as exc:
|
|
157
|
+
return None, discovery_error(f"Could not read '{path}': {exc}")
|
|
158
|
+
marker = find_unsafe_xml_marker(data)
|
|
159
|
+
if marker is not None:
|
|
160
|
+
return None, discovery_error(
|
|
161
|
+
f"Refusing to parse '{path}': contains a disallowed "
|
|
162
|
+
f"'{marker}' declaration (XML entity-expansion hardening)."
|
|
163
|
+
)
|
|
164
|
+
return data, None
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
# `coverage-config.json`'s exclusion schema (`excluded: [{"path", "reason"}]`)
|
|
168
|
+
# is intentionally NOT the same schema as the sibling `mutation_exclude_policy.py`
|
|
169
|
+
# module's `mutation-exclude-policy.json` (`{schema_version, always: [...],
|
|
170
|
+
# propose_and_ask: [...]}`, unit key `file` rather than `path`) — issue #1853.
|
|
171
|
+
# The two are different bounded contexts (coverage-accounting ledger vs.
|
|
172
|
+
# mutation-exclude proposals) with no shared consumer today; do not assume
|
|
173
|
+
# they should be interchangeable or unify them.
|
|
174
|
+
def load_or_bootstrap(
|
|
175
|
+
config_path: Path, discovered_projects: list, now_iso: str
|
|
176
|
+
) -> tuple:
|
|
177
|
+
"""Load `config_path`, or bootstrap it from `discovered_projects` if
|
|
178
|
+
absent (including malformed/corrupt — treated as absent, matching
|
|
179
|
+
`coverage-baseline`'s existing-baseline guard precedent).
|
|
180
|
+
|
|
181
|
+
`discovered_projects` uses the SAME shape `drift_check` expects: a list
|
|
182
|
+
of `{"path": ..., "classification": TestClassification}` entries. Only
|
|
183
|
+
entries where `needs_accounting(classification)` is true are written to
|
|
184
|
+
`included` — a `NOT_TEST` entry is correctly excluded, and the raw
|
|
185
|
+
`TestClassification` enum value never reaches `atomic_write_json` (it is
|
|
186
|
+
not JSON-serializable).
|
|
187
|
+
|
|
188
|
+
Returns `(config, notice)`. `notice` is `None` when an existing config
|
|
189
|
+
was read verbatim (no write happens on this path — `baseline-coverage.json`
|
|
190
|
+
is never touched by this function either way). On bootstrap, `notice` is
|
|
191
|
+
the exact operator-facing string naming how many projects were included.
|
|
192
|
+
|
|
193
|
+
`now_iso` must use the identical ISO-8601 UTC format (with a literal `Z`
|
|
194
|
+
suffix) as `baseline-coverage.json`'s `captured_at` values — this is what
|
|
195
|
+
makes `measurement_basis_notice`'s later datetime comparison valid.
|
|
196
|
+
|
|
197
|
+
**Concurrency.** Two invocations racing against the same absent
|
|
198
|
+
`config_path` are mutually excluded by a create-exclusive `.lock`
|
|
199
|
+
sibling file (`os.O_CREAT | os.O_EXCL`) rather than the plain existence
|
|
200
|
+
check above, which is subject to a TOCTOU gap between the check and the
|
|
201
|
+
write. The loser of the lock never writes — it re-reads `config_path`
|
|
202
|
+
(the winner should have just written it) and returns that content
|
|
203
|
+
instead of overwriting it. The lock file is removed in a `finally` by
|
|
204
|
+
whichever call created it.
|
|
205
|
+
"""
|
|
206
|
+
existing = _read_existing_config(config_path)
|
|
207
|
+
if existing is not None:
|
|
208
|
+
return existing, None
|
|
209
|
+
|
|
210
|
+
included = [
|
|
211
|
+
entry["path"]
|
|
212
|
+
for entry in discovered_projects
|
|
213
|
+
if needs_accounting(entry["classification"])
|
|
214
|
+
]
|
|
215
|
+
config = {
|
|
216
|
+
"schema_version": 1,
|
|
217
|
+
"included": included,
|
|
218
|
+
"excluded": [],
|
|
219
|
+
"bootstrapped_at": now_iso,
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
223
|
+
lock_path = Path(f"{config_path}.lock")
|
|
224
|
+
try:
|
|
225
|
+
lock_fd = os.open(str(lock_path), os.O_CREAT | os.O_EXCL | os.O_WRONLY)
|
|
226
|
+
except FileExistsError:
|
|
227
|
+
# Another call is concurrently bootstrapping the same config path —
|
|
228
|
+
# it should have just written config_path; return that content
|
|
229
|
+
# rather than racing it to overwrite. If it hasn't written yet (a
|
|
230
|
+
# genuinely stuck concurrent bootstrapper), fail loudly rather than
|
|
231
|
+
# silently proceeding to overwrite.
|
|
232
|
+
winner = _read_existing_config(config_path)
|
|
233
|
+
if winner is not None:
|
|
234
|
+
return winner, None
|
|
235
|
+
raise RuntimeError(
|
|
236
|
+
f"'{lock_path}' is held by a concurrent load_or_bootstrap call, "
|
|
237
|
+
f"but '{config_path}' has not been written yet; refusing to "
|
|
238
|
+
f"overwrite it. Re-run once the concurrent call completes."
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
try:
|
|
242
|
+
os.close(lock_fd)
|
|
243
|
+
# Double-checked locking: another call may have won the race between
|
|
244
|
+
# our existence check above and acquiring this lock, written
|
|
245
|
+
# config_path, and already released its own lock before we got here.
|
|
246
|
+
# Re-check now, inside the lock, so we never overwrite that write.
|
|
247
|
+
winner = _read_existing_config(config_path)
|
|
248
|
+
if winner is not None:
|
|
249
|
+
return winner, None
|
|
250
|
+
atomic_write_json(config_path, config)
|
|
251
|
+
finally:
|
|
252
|
+
lock_path.unlink(missing_ok=True)
|
|
253
|
+
|
|
254
|
+
notice = (
|
|
255
|
+
f"coverage-config.json did not exist; created it from fresh discovery "
|
|
256
|
+
f"with {len(included)} project(s) included and zero "
|
|
257
|
+
f"exclusions. Review and add exclusions (with a reason) if any "
|
|
258
|
+
f"project should not count toward coverage."
|
|
259
|
+
)
|
|
260
|
+
return config, notice
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _read_existing_config(config_path: Path) -> dict | None:
|
|
264
|
+
"""Return the parsed config, or `None` if absent/malformed."""
|
|
265
|
+
if not config_path.is_file():
|
|
266
|
+
return None
|
|
267
|
+
try:
|
|
268
|
+
return json.loads(config_path.read_text(encoding="utf-8"))
|
|
269
|
+
except (OSError, json.JSONDecodeError):
|
|
270
|
+
return None
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def _normalize_excluded(excluded_entries: list) -> list:
|
|
274
|
+
"""Normalize `excluded` entries to `{"path": ..., "reason": ...}` dicts,
|
|
275
|
+
tolerating a bare path string (normalized to `{"path": s, "reason":
|
|
276
|
+
""}`) — the shared normalization `drift_check` and
|
|
277
|
+
`format_active_exclusions` both need, extracted so they share one
|
|
278
|
+
implementation rather than two byte-identical copies."""
|
|
279
|
+
return [
|
|
280
|
+
{"path": entry, "reason": ""} if isinstance(entry, str) else entry
|
|
281
|
+
for entry in excluded_entries
|
|
282
|
+
]
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def _validate_config_shape(config: dict) -> None:
|
|
286
|
+
"""Validate `config`'s `included`/`excluded` shape, raising `ValueError`
|
|
287
|
+
naming the malformed field or entry. Extracted from `drift_check` so
|
|
288
|
+
shape validation and drift computation are each independently readable
|
|
289
|
+
— the same extraction precedent as `_format_hard_failure_message`/
|
|
290
|
+
`_format_stale_warning` below (issue #1858). Raises before any drift
|
|
291
|
+
computation runs; callers never see a partially-computed result on a
|
|
292
|
+
malformed config."""
|
|
293
|
+
included_list = config.get("included", [])
|
|
294
|
+
excluded_entries = config.get("excluded", [])
|
|
295
|
+
|
|
296
|
+
if "included" in config and not isinstance(config["included"], list):
|
|
297
|
+
raise ValueError(
|
|
298
|
+
"coverage-config.json \"included\" must be a list of path "
|
|
299
|
+
f"strings; got {type(config['included']).__name__} instead."
|
|
300
|
+
)
|
|
301
|
+
if "excluded" in config and not isinstance(config["excluded"], list):
|
|
302
|
+
raise ValueError(
|
|
303
|
+
"coverage-config.json \"excluded\" must be a list of "
|
|
304
|
+
"{\"path\": ..., \"reason\": ...} entries; got "
|
|
305
|
+
f"{type(config['excluded']).__name__} instead."
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
for entry in included_list:
|
|
309
|
+
if not isinstance(entry, str):
|
|
310
|
+
raise ValueError( # noqa: TRY004 — ValueError is the documented, tested contract for malformed config (see docstring; test_coverage_config.py asserts ValueError)
|
|
311
|
+
"coverage-config.json \"included\" entries must all be "
|
|
312
|
+
f"path strings; got {entry!r} instead."
|
|
313
|
+
)
|
|
314
|
+
for entry in excluded_entries:
|
|
315
|
+
if not isinstance(entry, str) and (
|
|
316
|
+
not isinstance(entry, dict) or "path" not in entry
|
|
317
|
+
):
|
|
318
|
+
raise ValueError(
|
|
319
|
+
"coverage-config.json \"excluded\" entries must all be a "
|
|
320
|
+
"path string or a {\"path\": ..., \"reason\": ...} entry "
|
|
321
|
+
f"carrying a \"path\" key; got {entry!r} instead."
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def drift_check(config: dict, discovered_projects: list) -> dict:
|
|
326
|
+
"""Compare `config`'s `included`/`excluded` entries against fresh
|
|
327
|
+
`discovered_projects` (a list of `{"path": ..., "classification":
|
|
328
|
+
TestClassification}` entries).
|
|
329
|
+
|
|
330
|
+
Returns a structured result:
|
|
331
|
+
|
|
332
|
+
- `unaccounted`: discovered paths needing accounting
|
|
333
|
+
(`needs_accounting(classification)` true) that are in neither
|
|
334
|
+
`included` nor `excluded`.
|
|
335
|
+
- `unaccounted_ambiguous`: the subset of `unaccounted` classified
|
|
336
|
+
`AMBIGUOUS`.
|
|
337
|
+
- `conflicts`: paths present in both `included` and `excluded`.
|
|
338
|
+
- `stale_exclusions`: `excluded` entries whose path no longer appears in
|
|
339
|
+
`discovered_projects` at all (regardless of classification).
|
|
340
|
+
- `hard_failure`: true iff `unaccounted` or `conflicts` is non-empty.
|
|
341
|
+
- `hard_failure_message`: the exact operator-facing failure text, or
|
|
342
|
+
`None` when `hard_failure` is false.
|
|
343
|
+
- `stale_warning_message`: the exact non-blocking warning text (one
|
|
344
|
+
line per stale entry), or `None` when `stale_exclusions` is empty.
|
|
345
|
+
|
|
346
|
+
Malformed-but-parseable configs degrade gracefully rather than raising,
|
|
347
|
+
matching `_read_existing_config`'s own "malformed → treated as absent"
|
|
348
|
+
tolerance: a bare string in `excluded` (instead of `{"path": ...,
|
|
349
|
+
"reason": ...}`) is normalized to `{"path": s, "reason": ""}`, and an
|
|
350
|
+
excluded entry missing `"reason"` renders as `<no reason recorded>`
|
|
351
|
+
rather than raising `KeyError`. A schema violation this module cannot
|
|
352
|
+
silently coerce without failing in the wrong direction — `"included"` or
|
|
353
|
+
`"excluded"` present but not a list at all — raises `ValueError` naming
|
|
354
|
+
the malformed field, rather than degrading `path in included_list` to a
|
|
355
|
+
substring test.
|
|
356
|
+
"""
|
|
357
|
+
_validate_config_shape(config)
|
|
358
|
+
|
|
359
|
+
included_list = config.get("included", [])
|
|
360
|
+
excluded_entries = config.get("excluded", [])
|
|
361
|
+
|
|
362
|
+
normalized_excluded = _normalize_excluded(excluded_entries)
|
|
363
|
+
excluded_by_path = {entry["path"]: entry for entry in normalized_excluded}
|
|
364
|
+
excluded_paths = set(excluded_by_path)
|
|
365
|
+
included_paths = set(included_list)
|
|
366
|
+
|
|
367
|
+
conflicts = [path for path in included_list if path in excluded_paths]
|
|
368
|
+
|
|
369
|
+
unaccounted: list = []
|
|
370
|
+
unaccounted_ambiguous: list = []
|
|
371
|
+
for entry in discovered_projects:
|
|
372
|
+
path = entry["path"]
|
|
373
|
+
classification = entry["classification"]
|
|
374
|
+
if not needs_accounting(classification):
|
|
375
|
+
continue
|
|
376
|
+
if path in included_paths or path in excluded_paths:
|
|
377
|
+
continue
|
|
378
|
+
unaccounted.append(path)
|
|
379
|
+
if classification is TestClassification.AMBIGUOUS:
|
|
380
|
+
unaccounted_ambiguous.append(path)
|
|
381
|
+
|
|
382
|
+
discovered_paths = {entry["path"] for entry in discovered_projects}
|
|
383
|
+
stale_exclusions = [
|
|
384
|
+
excluded_by_path[path]
|
|
385
|
+
for path in excluded_by_path
|
|
386
|
+
if path not in discovered_paths
|
|
387
|
+
]
|
|
388
|
+
|
|
389
|
+
hard_failure = bool(unaccounted) or bool(conflicts)
|
|
390
|
+
hard_failure_message = (
|
|
391
|
+
_format_hard_failure_message(unaccounted, unaccounted_ambiguous, conflicts)
|
|
392
|
+
if hard_failure
|
|
393
|
+
else None
|
|
394
|
+
)
|
|
395
|
+
stale_warning_message = (
|
|
396
|
+
_format_stale_warning(stale_exclusions) if stale_exclusions else None
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
return {
|
|
400
|
+
"unaccounted": unaccounted,
|
|
401
|
+
"unaccounted_ambiguous": unaccounted_ambiguous,
|
|
402
|
+
"conflicts": conflicts,
|
|
403
|
+
"stale_exclusions": stale_exclusions,
|
|
404
|
+
"hard_failure": hard_failure,
|
|
405
|
+
"hard_failure_message": hard_failure_message,
|
|
406
|
+
"stale_warning_message": stale_warning_message,
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def _format_hard_failure_message(
|
|
411
|
+
unaccounted: list, unaccounted_ambiguous: list, conflicts: list
|
|
412
|
+
) -> str:
|
|
413
|
+
"""Build the exact operator-facing failure text for `drift_check`'s
|
|
414
|
+
`unaccounted`/`conflicts` findings. Pure string formatting, extracted
|
|
415
|
+
from `drift_check` so the data analysis above stays readable."""
|
|
416
|
+
sentences = []
|
|
417
|
+
if unaccounted:
|
|
418
|
+
sentences.append(
|
|
419
|
+
f"Coverage capture stopped: {len(unaccounted)} discovered "
|
|
420
|
+
f"project(s) are not accounted for in coverage-config.json: "
|
|
421
|
+
f"{', '.join(unaccounted)}. Add each to \"included\", or to "
|
|
422
|
+
f"\"excluded\" with a \"reason\", then re-run."
|
|
423
|
+
)
|
|
424
|
+
if unaccounted_ambiguous:
|
|
425
|
+
sentences.append(
|
|
426
|
+
f"{len(unaccounted_ambiguous)} of these could not be classified "
|
|
427
|
+
f"with certainty from their test markers: "
|
|
428
|
+
f"{', '.join(unaccounted_ambiguous)}."
|
|
429
|
+
)
|
|
430
|
+
if conflicts:
|
|
431
|
+
conflict_sentence = (
|
|
432
|
+
f"{len(conflicts)} project(s) are listed in both "
|
|
433
|
+
f"\"included\" and \"excluded\": {', '.join(conflicts)}. "
|
|
434
|
+
f"Remove each from one list, then re-run."
|
|
435
|
+
)
|
|
436
|
+
if sentences:
|
|
437
|
+
sentences.append(conflict_sentence)
|
|
438
|
+
else:
|
|
439
|
+
sentences.append(f"Coverage capture stopped: {conflict_sentence}")
|
|
440
|
+
return " ".join(sentences)
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def _format_stale_warning(stale_exclusions: list) -> str:
|
|
444
|
+
"""Build the exact non-blocking warning text for `drift_check`'s
|
|
445
|
+
`stale_exclusions` — one line per entry, in `excluded`'s order. A
|
|
446
|
+
missing `"reason"` (malformed-but-parseable config) renders as
|
|
447
|
+
`<no reason recorded>` rather than raising `KeyError`."""
|
|
448
|
+
return "\n".join(
|
|
449
|
+
f"Warning: excluded entry '{entry['path']}' (reason: "
|
|
450
|
+
f"'{entry.get('reason', '<no reason recorded>')}') no longer "
|
|
451
|
+
f"matches any discovered project — it may have been removed, "
|
|
452
|
+
f"renamed, or the config is out of date. This does not block the "
|
|
453
|
+
f"run."
|
|
454
|
+
for entry in stale_exclusions
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def format_active_exclusions(config: dict) -> str | None:
|
|
459
|
+
"""Render `config`'s currently-active `excluded` entries as one line per
|
|
460
|
+
entry: `"Excluded: '<path>' (reason: '<reason>')"`, joined with
|
|
461
|
+
newlines. Returns `None` when `excluded` is empty.
|
|
462
|
+
|
|
463
|
+
This is informational, always-shown context — printed on EVERY run
|
|
464
|
+
regardless of staleness — distinct from `drift_check`'s
|
|
465
|
+
`stale_warning_message`, which flags a DIFFERENT problem: an excluded
|
|
466
|
+
path no longer being discovered at all. An exclusion can be active
|
|
467
|
+
(still discovered, reason unchanged) and still worth surfacing so an
|
|
468
|
+
operator sees what is currently excluded and why, on every run — not
|
|
469
|
+
only when something about it goes stale.
|
|
470
|
+
|
|
471
|
+
Same bare-string/missing-`"reason"` tolerance as `_format_stale_warning`."""
|
|
472
|
+
excluded_entries = config.get("excluded", [])
|
|
473
|
+
if not excluded_entries:
|
|
474
|
+
return None
|
|
475
|
+
normalized = _normalize_excluded(excluded_entries)
|
|
476
|
+
return "\n".join(
|
|
477
|
+
f"Excluded: '{entry['path']}' (reason: "
|
|
478
|
+
f"'{entry.get('reason', '<no reason recorded>')}')"
|
|
479
|
+
for entry in normalized
|
|
480
|
+
)
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
_WEIGHTED_MERGE_REQUIRED_FIELDS = (
|
|
484
|
+
"covered_statements",
|
|
485
|
+
"total_statements",
|
|
486
|
+
"covered_branches",
|
|
487
|
+
"total_branches",
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
def weighted_merge(project_reports: list) -> dict:
|
|
492
|
+
"""Merge per-project coverage reports, weighted by statement/branch
|
|
493
|
+
count — never a per-project average.
|
|
494
|
+
|
|
495
|
+
Each entry in `project_reports` carries `{covered_statements,
|
|
496
|
+
total_statements, covered_branches, total_branches}` — a plain dict, or
|
|
497
|
+
a `coverage_report_parse.CoverageRecord` (e.g. `aggregate()`'s output)
|
|
498
|
+
consumed directly, with no translation layer, since both already use
|
|
499
|
+
this same field-name vocabulary. Returns `{line_pct, branch_pct}`,
|
|
500
|
+
summed across all reports before dividing. Either percentage is `None`
|
|
501
|
+
(JSON `null`) when its total is 0 across all reports, matching
|
|
502
|
+
`coverage-baseline`'s existing Go-coverage `null` convention. A field
|
|
503
|
+
valued `None` in a per-project report (this repo's own Go-coverage
|
|
504
|
+
convention for a tool with no native branch coverage) coerces to 0
|
|
505
|
+
rather than raising `TypeError`, degrading to the same zero-total →
|
|
506
|
+
`None` result.
|
|
507
|
+
|
|
508
|
+
A count field **entirely absent** from a report dict is a different,
|
|
509
|
+
louder case: it means the report is incomplete (a per-project report
|
|
510
|
+
carrying only percentages with no raw counts), never the legitimate
|
|
511
|
+
zero-total-for-this-metric convention above. This is validated in an
|
|
512
|
+
upfront pass, before any summing, and returns the shared
|
|
513
|
+
`discovery_error(...)` signal naming the missing field(s) rather than
|
|
514
|
+
silently proceeding to a null/0 baseline — "key entirely absent" and
|
|
515
|
+
"key present with value `None`" must never be treated identically.
|
|
516
|
+
"""
|
|
517
|
+
project_reports = [
|
|
518
|
+
dataclasses.asdict(report) if dataclasses.is_dataclass(report) else report
|
|
519
|
+
for report in project_reports
|
|
520
|
+
]
|
|
521
|
+
for report in project_reports:
|
|
522
|
+
missing_fields = [
|
|
523
|
+
field for field in _WEIGHTED_MERGE_REQUIRED_FIELDS if field not in report
|
|
524
|
+
]
|
|
525
|
+
if missing_fields:
|
|
526
|
+
return discovery_error(
|
|
527
|
+
f"Coverage report is missing required count field(s): {missing_fields}"
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
total_statements = sum(
|
|
531
|
+
(report.get("total_statements") or 0) for report in project_reports
|
|
532
|
+
)
|
|
533
|
+
covered_statements = sum(
|
|
534
|
+
(report.get("covered_statements") or 0) for report in project_reports
|
|
535
|
+
)
|
|
536
|
+
total_branches = sum(
|
|
537
|
+
(report.get("total_branches") or 0) for report in project_reports
|
|
538
|
+
)
|
|
539
|
+
covered_branches = sum(
|
|
540
|
+
(report.get("covered_branches") or 0) for report in project_reports
|
|
541
|
+
)
|
|
542
|
+
|
|
543
|
+
line_pct = (
|
|
544
|
+
covered_statements / total_statements * 100 if total_statements else None
|
|
545
|
+
)
|
|
546
|
+
branch_pct = covered_branches / total_branches * 100 if total_branches else None
|
|
547
|
+
|
|
548
|
+
return {"line_pct": line_pct, "branch_pct": branch_pct}
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
def _parse_iso8601(value: str) -> datetime | None:
|
|
552
|
+
"""Parse an ISO-8601 timestamp, substituting a trailing `Z` with
|
|
553
|
+
`+00:00` first (`datetime.fromisoformat` doesn't accept a bare `Z`).
|
|
554
|
+
Returns `None` on any unparseable input rather than raising — callers
|
|
555
|
+
treat that as "cannot compare, no notice".
|
|
556
|
+
|
|
557
|
+
A parsed value with no timezone (naive — no explicit `Z`/offset in the
|
|
558
|
+
input) is assumed UTC. `bootstrapped_at` is always `Z`-suffixed (per
|
|
559
|
+
`load_or_bootstrap`), but `captured_at` may come from a pre-existing
|
|
560
|
+
`baseline-coverage.json` written before this feature, with no timezone
|
|
561
|
+
guarantee at all — comparing a naive and an aware `datetime` raises
|
|
562
|
+
`TypeError`, so every value returned here is aware, treating an absent
|
|
563
|
+
offset as the safe, conservative UTC assumption this codebase already
|
|
564
|
+
uses elsewhere."""
|
|
565
|
+
try:
|
|
566
|
+
parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
|
|
567
|
+
except (TypeError, ValueError, AttributeError):
|
|
568
|
+
return None
|
|
569
|
+
if parsed.tzinfo is None:
|
|
570
|
+
parsed = parsed.replace(tzinfo=timezone.utc)
|
|
571
|
+
return parsed
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
def measurement_basis_notice(bootstrapped_at, captured_at: str):
|
|
575
|
+
"""Return a non-blocking notice when `captured_at` predates
|
|
576
|
+
`bootstrapped_at` — the comparison baseline was captured before
|
|
577
|
+
multi-project discovery existed, so a delta against it may reflect a
|
|
578
|
+
widened measurement scope rather than a real coverage change.
|
|
579
|
+
|
|
580
|
+
Returns `None` when `bootstrapped_at` is `None` (config was never
|
|
581
|
+
bootstrapped, or predates this feature), when either value fails to
|
|
582
|
+
parse as ISO-8601, or when `captured_at >= bootstrapped_at` (compared as
|
|
583
|
+
parsed `datetime` objects, not raw strings — this correctly orders
|
|
584
|
+
equivalent-but-differently-spelled timestamps, e.g. a `Z` suffix vs an
|
|
585
|
+
explicit `+00:00` offset for the same instant). A same-instant capture
|
|
586
|
+
is not "predates" and also returns `None`.
|
|
587
|
+
"""
|
|
588
|
+
if bootstrapped_at is None:
|
|
589
|
+
return None
|
|
590
|
+
bootstrapped_dt = _parse_iso8601(bootstrapped_at)
|
|
591
|
+
captured_dt = _parse_iso8601(captured_at)
|
|
592
|
+
if bootstrapped_dt is None or captured_dt is None:
|
|
593
|
+
return None
|
|
594
|
+
if captured_dt >= bootstrapped_dt:
|
|
595
|
+
return None
|
|
596
|
+
return (
|
|
597
|
+
f"Note: this baseline predates multi-project discovery (bootstrapped "
|
|
598
|
+
f"{bootstrapped_at}). This delta may reflect a widened measurement "
|
|
599
|
+
f"scope, not only a real coverage change. Consider re-running "
|
|
600
|
+
f"/coverage-baseline for a directly comparable baseline."
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
def atomic_write_json(path: Path, obj: dict) -> None:
|
|
605
|
+
"""Write `obj` as JSON to `path` atomically (temp-file-then-rename),
|
|
606
|
+
matching `baseline-coverage.json`'s existing write convention. Shared
|
|
607
|
+
across this module's own callers and Slice 4/5's — reuse this instead of
|
|
608
|
+
re-implementing temp-file-then-rename.
|
|
609
|
+
|
|
610
|
+
On any failure (e.g. `obj` is not JSON-serializable), the temp file is
|
|
611
|
+
removed before the exception propagates — the caller never leaks a
|
|
612
|
+
`.tmp` file it has no handle to."""
|
|
613
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
614
|
+
fd, tmp_name = tempfile.mkstemp(
|
|
615
|
+
prefix=f".{path.name}-", suffix=".tmp", dir=str(path.parent)
|
|
616
|
+
)
|
|
617
|
+
try:
|
|
618
|
+
with os.fdopen(fd, "w", encoding="utf-8") as handle:
|
|
619
|
+
json.dump(obj, handle, indent=2)
|
|
620
|
+
os.replace(tmp_name, path)
|
|
621
|
+
except BaseException:
|
|
622
|
+
Path(tmp_name).unlink(missing_ok=True)
|
|
623
|
+
raise
|