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,742 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""coverage_discovery_java.py — discover and classify every module in a Java
|
|
3
|
+
multi-module build for multi-project coverage discovery (issue #1765).
|
|
4
|
+
|
|
5
|
+
Generalizes the shape landed for .NET solutions (`coverage_discovery_dotnet.py`)
|
|
6
|
+
and JS/TS workspaces (`coverage_discovery_js.py`) in #1759/#1822 to the two
|
|
7
|
+
JVM multi-module build graphs:
|
|
8
|
+
|
|
9
|
+
- **Maven** — the root `pom.xml`'s `<modules><module>` list, resolved
|
|
10
|
+
**recursively** through aggregator modules (a module that itself declares
|
|
11
|
+
`<modules>` contributes its own children), with cycle protection so a
|
|
12
|
+
self- or mutually-referential graph terminates.
|
|
13
|
+
- **Gradle** — `settings.gradle` / `settings.gradle.kts` `include(...)`
|
|
14
|
+
declarations, translating Gradle's colon-separated project paths (`:a:b`)
|
|
15
|
+
to directory paths (`a/b`). Both the parenthesized (Kotlin DSL) and bare
|
|
16
|
+
(Groovy) call forms are recognized, including a multi-line argument list —
|
|
17
|
+
`include ':app',\n ':lib'` is a single declaration and yields BOTH
|
|
18
|
+
modules. `includeBuild` (composite builds) and `includeFlat` (sibling-
|
|
19
|
+
directory projects) are separate build graphs and are deliberately out of
|
|
20
|
+
scope; neither is mistaken for `include`.
|
|
21
|
+
|
|
22
|
+
**Out of scope, named explicitly.** A Gradle subproject whose directory is
|
|
23
|
+
remapped in `settings.gradle` (`project(":api").projectDir = file("custom")`)
|
|
24
|
+
is not resolved to its remapped location — this module resolves the default
|
|
25
|
+
`:a:b` → `a/b` layout only. Because a declared module whose directory is
|
|
26
|
+
absent is a hard `discovery_error` (never a silent `NOT_TEST`), such a
|
|
27
|
+
remapping surfaces as a loud, actionable error naming the missing path rather
|
|
28
|
+
than silently dropping the subproject from coverage accounting.
|
|
29
|
+
|
|
30
|
+
**Stack precedence (no cross-merge).** When both a root `pom.xml` declaring
|
|
31
|
+
`<modules>` and a `settings.gradle*` declaring `include(...)` are present,
|
|
32
|
+
Maven wins and Gradle is not consulted — the same "first present config wins"
|
|
33
|
+
rule `coverage_discovery_js.py` applies across `package.json#workspaces` →
|
|
34
|
+
`pnpm-workspace.yaml` → `lerna.json`.
|
|
35
|
+
|
|
36
|
+
**No JVM, no build-tool CLI.** Unlike .NET discovery (which shells out to
|
|
37
|
+
`dotnet sln list`), this module is a pure static parse, matching the JS/TS
|
|
38
|
+
module's filesystem-only approach. Invoking `mvn`/`gradlew` would require a
|
|
39
|
+
JVM and network access for plugin resolution and cost tens of seconds on
|
|
40
|
+
every measurement — unacceptable for a per-run re-derivation.
|
|
41
|
+
|
|
42
|
+
**Identity contract.** A module's "path" is its directory, expressed relative
|
|
43
|
+
to `repo_root` in POSIX form (matching both existing discovery modules) — the
|
|
44
|
+
exact, unmodified string every caller compares against `included`/`excluded`
|
|
45
|
+
entries, per `coverage_config`'s identity contract. The POSIX-relative
|
|
46
|
+
rendering is applied once at resolution time, never per comparison.
|
|
47
|
+
|
|
48
|
+
**Classification.** `TEST` when the module has BOTH a test-source directory
|
|
49
|
+
(`src/test/java` or `src/test/kotlin`) AND a JUnit/TestNG dependency declared
|
|
50
|
+
in its own build file. `AMBIGUOUS` — never silently `NOT_TEST` — when a
|
|
51
|
+
test-source directory is present but the test-framework dependency is not
|
|
52
|
+
declared in the module's own build file: it may be inherited from a parent
|
|
53
|
+
POM's `<dependencyManagement>`/`<dependencies>` or a Gradle `subprojects {}`
|
|
54
|
+
block or convention plugin. This module deliberately never evaluates that
|
|
55
|
+
inheritance (mirroring the .NET module's refusal to evaluate MSBuild
|
|
56
|
+
`Condition` attributes); it reports the ambiguity and lets
|
|
57
|
+
`coverage_config.drift_check` force an operator decision. `NOT_TEST` only
|
|
58
|
+
when there is no test-source directory at all.
|
|
59
|
+
|
|
60
|
+
**Security hardening.** Every resolved module directory, every build file
|
|
61
|
+
read, and the test-source-directory probe are verified to stay within the
|
|
62
|
+
resolved repository root before they are read or classified — a
|
|
63
|
+
`<module>../../etc</module>`, an `include(':..:..')`, a symlinked build file,
|
|
64
|
+
or a symlinked `src/test/java` can never cause this module to read or stat
|
|
65
|
+
outside the repository. An out-of-root entry is reported as an error, never
|
|
66
|
+
silently dropped.
|
|
67
|
+
|
|
68
|
+
Every XML read routes through `coverage_config.read_and_screen_xml`, the
|
|
69
|
+
shared `<!DOCTYPE`/`<!ENTITY` entity-expansion screen the .NET stack
|
|
70
|
+
introduced (`xml.etree.ElementTree` is explicitly not secure against
|
|
71
|
+
maliciously constructed data, and a `pom.xml` never legitimately carries
|
|
72
|
+
either declaration). It reads the bytes once and the caller parses THOSE
|
|
73
|
+
bytes via `ET.fromstring`, so no path is ever checked and then re-opened.
|
|
74
|
+
Gradle build files are read the same way — one read per contained path, never
|
|
75
|
+
a resolve-then-reopen pair.
|
|
76
|
+
|
|
77
|
+
A parsed Gradle project spec is validated against a conservative character
|
|
78
|
+
allowlist before any path is built from it: a raw NUL (reachable because
|
|
79
|
+
settings files are decoded with `errors="replace"`, which does not replace
|
|
80
|
+
U+0000) would otherwise make `Path.resolve()` raise `ValueError` — not
|
|
81
|
+
`OSError` — and escape this module's "every failure is a structured
|
|
82
|
+
`discovery_error`" contract as an unhandled traceback.
|
|
83
|
+
|
|
84
|
+
Stdlib-only (ADR 0014/0015/0031).
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
from __future__ import annotations
|
|
88
|
+
|
|
89
|
+
import re
|
|
90
|
+
import sys
|
|
91
|
+
import xml.etree.ElementTree as ET
|
|
92
|
+
from pathlib import Path
|
|
93
|
+
|
|
94
|
+
_HERE = Path(__file__).resolve().parent
|
|
95
|
+
sys.path.insert(0, str(_HERE))
|
|
96
|
+
|
|
97
|
+
import coverage_config
|
|
98
|
+
from coverage_config import TestClassification
|
|
99
|
+
|
|
100
|
+
# Test-source directories that mark a module as carrying tests at all.
|
|
101
|
+
_TEST_SOURCE_DIRS = ("src/test/java", "src/test/kotlin")
|
|
102
|
+
|
|
103
|
+
# Test-framework tokens searched for in a module's own build file. Matched
|
|
104
|
+
# case-insensitively against the raw build-file text for Gradle, and against
|
|
105
|
+
# declared dependency coordinates for Maven.
|
|
106
|
+
_TEST_FRAMEWORK_TOKENS = ("junit", "testng")
|
|
107
|
+
|
|
108
|
+
_MAVEN_POM_FILE = "pom.xml"
|
|
109
|
+
_GRADLE_SETTINGS_FILES = ("settings.gradle", "settings.gradle.kts")
|
|
110
|
+
_GRADLE_BUILD_FILES = ("build.gradle", "build.gradle.kts")
|
|
111
|
+
|
|
112
|
+
# A Gradle line counts as a TEST-framework dependency declaration only when a
|
|
113
|
+
# test *configuration* is being invoked with an argument. `test`-prefixed
|
|
114
|
+
# covers testImplementation / testCompileOnly / testRuntimeOnly / testApi /
|
|
115
|
+
# testAnnotationProcessor; `androidTest`-prefixed covers the Android variants;
|
|
116
|
+
# `integrationTest…` and other custom test source sets match the same infix.
|
|
117
|
+
#
|
|
118
|
+
# The token may follow start-of-line, `{`, or `;` so the one-line
|
|
119
|
+
# `dependencies { testImplementation "…" }` form is recognized, and the
|
|
120
|
+
# character after it must begin an ARGUMENT — `(`, a quote, or an
|
|
121
|
+
# identifier/`$` — never `{`. That last exclusion is load-bearing: `test {
|
|
122
|
+
# useJUnitPlatform() }` configures the Test *task*, and `testng { suites … }`
|
|
123
|
+
# the TestNG plugin extension; neither declares a dependency, so treating
|
|
124
|
+
# either as one would resolve a genuinely inherited-only module to a confident
|
|
125
|
+
# `TEST` and spend the `AMBIGUOUS` signal `drift_check` needs.
|
|
126
|
+
# The `(?![A-Za-z])` after the identifier is load-bearing: without it the regex
|
|
127
|
+
# backtracks into a PREFIX of a longer name, so `testng { suites '…' }` matches
|
|
128
|
+
# as `test` followed by the argument-looking `n` of `ng`. The lookahead forces
|
|
129
|
+
# the identifier to be complete before the argument check runs.
|
|
130
|
+
_GRADLE_TEST_CONFIG_RE = re.compile(
|
|
131
|
+
r"(?:^|[{;])\s*[A-Za-z]*[Tt]est[A-Za-z]*(?![A-Za-z])\s*(?:\(|['\"]|[A-Za-z_$])",
|
|
132
|
+
re.ASCII,
|
|
133
|
+
)
|
|
134
|
+
# A declaration split across lines (`testImplementation(\n "…"\n)`) is joined
|
|
135
|
+
# before scanning, so the configuration token and its argument are seen
|
|
136
|
+
# together. A line ending in `(` or `,` continues into the next.
|
|
137
|
+
_GRADLE_CONTINUATION_END = ("(", ",")
|
|
138
|
+
# `platform(...)`/`enforcedPlatform(...)` pins a version without declaring a
|
|
139
|
+
# dependency — Gradle's analogue of Maven's <dependencyManagement>.
|
|
140
|
+
_GRADLE_BOM_RE = re.compile(r"\b(?:enforced)?platform\s*\(", re.ASCII)
|
|
141
|
+
|
|
142
|
+
# `include`, Groovy or Kotlin DSL — but never `includeBuild`, `includeFlat`, or
|
|
143
|
+
# any other `include`-prefixed identifier. The lookahead rejects an identifier
|
|
144
|
+
# continuation rather than one specific suffix, so a new Gradle `includeXxx`
|
|
145
|
+
# API can never silently start matching as `include`.
|
|
146
|
+
_GRADLE_INCLUDE_RE = re.compile(r"\binclude(?![A-Za-z0-9_])")
|
|
147
|
+
_GRADLE_QUOTED_RE = re.compile(r"""['"]([^'"]*)['"]""")
|
|
148
|
+
# Characters that may separate the quoted project paths of one `include` call.
|
|
149
|
+
# Anything else (`;`, a new statement, an identifier) ends the argument list.
|
|
150
|
+
_GRADLE_ARG_SEPARATORS = frozenset(" \t\r\n,()")
|
|
151
|
+
# Conservative allowlist for a Gradle project spec, applied BEFORE any path is
|
|
152
|
+
# built from it. A raw NUL is the motivating case: settings files are decoded
|
|
153
|
+
# with errors="replace", which does not replace U+0000, and `Path.resolve()`
|
|
154
|
+
# raises ValueError (not OSError) on an embedded null — escaping this module's
|
|
155
|
+
# structured-error contract as an unhandled traceback.
|
|
156
|
+
_GRADLE_SPEC_ALLOWED_RE = re.compile(r"^[A-Za-z0-9._:\-/ ]+$")
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def discover_java_modules(repo_root):
|
|
160
|
+
"""Discover and classify every module in `repo_root`'s Java multi-module
|
|
161
|
+
build.
|
|
162
|
+
|
|
163
|
+
Returns a list of `{"path": <repo-relative POSIX module directory>,
|
|
164
|
+
"classification": TestClassification}` entries on success (sorted by
|
|
165
|
+
path), `coverage_config.DISCOVERY_NOT_APPLICABLE` when no multi-module
|
|
166
|
+
signal is present, or `coverage_config.discovery_error(...)` for a
|
|
167
|
+
parsing or path-containment failure.
|
|
168
|
+
"""
|
|
169
|
+
root = Path(repo_root).resolve()
|
|
170
|
+
|
|
171
|
+
rel_paths = _maven_module_paths(root)
|
|
172
|
+
if rel_paths is None:
|
|
173
|
+
rel_paths = _gradle_module_paths(root)
|
|
174
|
+
if rel_paths is None:
|
|
175
|
+
return coverage_config.DISCOVERY_NOT_APPLICABLE
|
|
176
|
+
if isinstance(rel_paths, dict):
|
|
177
|
+
return rel_paths # a discovery_error propagated up
|
|
178
|
+
|
|
179
|
+
modules = []
|
|
180
|
+
for rel_path in rel_paths:
|
|
181
|
+
classification = _classify_module(root / rel_path, root)
|
|
182
|
+
if isinstance(classification, dict):
|
|
183
|
+
return classification # a discovery_error propagated up
|
|
184
|
+
modules.append({"path": rel_path, "classification": classification})
|
|
185
|
+
return modules
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
# ---------------------------------------------------------------------------
|
|
189
|
+
# Maven — recursive <modules> resolution
|
|
190
|
+
# ---------------------------------------------------------------------------
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _maven_module_paths(root: Path):
|
|
194
|
+
"""Resolve the root `pom.xml`'s `<modules>` list, recursively through
|
|
195
|
+
aggregator modules.
|
|
196
|
+
|
|
197
|
+
Returns a sorted list of repo-relative POSIX module directories,
|
|
198
|
+
`None` when this stack does not apply (no root `pom.xml`, or one that
|
|
199
|
+
declares no `<modules>` element at all — a single-module project), or
|
|
200
|
+
`coverage_config.discovery_error(...)` for a parse, containment, or
|
|
201
|
+
empty-declaration failure."""
|
|
202
|
+
root_pom = root / _MAVEN_POM_FILE
|
|
203
|
+
if not root_pom.is_file():
|
|
204
|
+
return None
|
|
205
|
+
|
|
206
|
+
declared = _parse_pom_modules(root_pom, root)
|
|
207
|
+
if isinstance(declared, dict):
|
|
208
|
+
return declared # a discovery_error propagated up
|
|
209
|
+
if declared is None:
|
|
210
|
+
return None # no <modules> element — single-module project
|
|
211
|
+
|
|
212
|
+
if not declared:
|
|
213
|
+
return coverage_config.discovery_error(
|
|
214
|
+
f"'{root_pom}' declares a <modules> element with no <module> "
|
|
215
|
+
"entries. A declared-but-empty module list cannot be "
|
|
216
|
+
"distinguished from a discovery bug, and silently treating it as "
|
|
217
|
+
"an empty project list is the exact silent-exclusion failure "
|
|
218
|
+
"mode multi-project discovery exists to prevent. Remove the "
|
|
219
|
+
"empty <modules> element, or list the real modules."
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
resolved: set[str] = set()
|
|
223
|
+
# Each queue item is a (module-spec, declaring-pom-directory) pair, so a
|
|
224
|
+
# nested aggregator's <module> entries resolve relative to that
|
|
225
|
+
# aggregator's own directory, the way Maven itself resolves them.
|
|
226
|
+
queue = [(spec, root) for spec in declared]
|
|
227
|
+
while queue:
|
|
228
|
+
spec, base = queue.pop()
|
|
229
|
+
module_dir = _contained_dir(base / spec, root)
|
|
230
|
+
if isinstance(module_dir, dict):
|
|
231
|
+
return module_dir # a discovery_error propagated up
|
|
232
|
+
if not module_dir.is_dir():
|
|
233
|
+
return coverage_config.discovery_error(
|
|
234
|
+
f"'{base / 'pom.xml'}' declares <module>{spec}</module> but "
|
|
235
|
+
f"'{module_dir}' is not a directory on disk. A declared "
|
|
236
|
+
"module that does not exist is a broken build graph, not a "
|
|
237
|
+
"non-test module — classifying it NOT_TEST would drop it from "
|
|
238
|
+
"coverage accounting silently."
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
rel = module_dir.relative_to(root).as_posix()
|
|
242
|
+
if rel in resolved:
|
|
243
|
+
continue # cycle, or the same module declared twice
|
|
244
|
+
resolved.add(rel)
|
|
245
|
+
|
|
246
|
+
child_pom = module_dir / _MAVEN_POM_FILE
|
|
247
|
+
if not child_pom.is_file():
|
|
248
|
+
# Same domain fact as the missing-directory branch above, so it
|
|
249
|
+
# gets the same treatment: `mvn` itself fails on a <module> with
|
|
250
|
+
# no POM, making this a broken build graph rather than a non-test
|
|
251
|
+
# module. Classifying it would land it on AMBIGUOUS/NOT_TEST and
|
|
252
|
+
# (for NOT_TEST) drop it from accounting silently.
|
|
253
|
+
#
|
|
254
|
+
# Deliberately Maven-only: a Gradle subproject directory with no
|
|
255
|
+
# build file is legitimate (a container project), so the Gradle
|
|
256
|
+
# branch keeps `_classify_module`'s lenient path.
|
|
257
|
+
return coverage_config.discovery_error(
|
|
258
|
+
f"'{base / _MAVEN_POM_FILE}' declares <module>{spec}</module> "
|
|
259
|
+
f"but '{child_pom}' does not exist. A declared Maven module "
|
|
260
|
+
"with no POM is a broken build graph — `mvn` itself fails on "
|
|
261
|
+
"it — not a non-test module."
|
|
262
|
+
)
|
|
263
|
+
child_modules = _parse_pom_modules(child_pom, root)
|
|
264
|
+
if isinstance(child_modules, dict):
|
|
265
|
+
return child_modules # a discovery_error propagated up
|
|
266
|
+
if child_modules == []:
|
|
267
|
+
# Same rule as the root POM below: a declared-but-empty <modules>
|
|
268
|
+
# is indistinguishable from a discovery bug, so it must never be
|
|
269
|
+
# accepted silently — one level down included.
|
|
270
|
+
return coverage_config.discovery_error(
|
|
271
|
+
f"'{child_pom}' declares a <modules> element with no <module> "
|
|
272
|
+
"entries. A declared-but-empty module list cannot be "
|
|
273
|
+
"distinguished from a discovery bug; remove the empty "
|
|
274
|
+
"<modules> element, or list the real modules."
|
|
275
|
+
)
|
|
276
|
+
for child_spec in child_modules or ():
|
|
277
|
+
queue.append((child_spec, module_dir))
|
|
278
|
+
|
|
279
|
+
return sorted(resolved)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def _parse_pom_modules(pom_path: Path, root: Path):
|
|
283
|
+
"""Return the list of `<module>` text values declared in `pom_path`'s
|
|
284
|
+
`<modules>` element, `None` when the POM declares no `<modules>` element
|
|
285
|
+
at all, or `coverage_config.discovery_error(...)` when the file cannot be
|
|
286
|
+
read, is not well-formed XML, or resolves outside `root`."""
|
|
287
|
+
root_el = _parse_contained_xml(pom_path, root)
|
|
288
|
+
if isinstance(root_el, dict):
|
|
289
|
+
return root_el # a discovery_error propagated up
|
|
290
|
+
|
|
291
|
+
for child in root_el:
|
|
292
|
+
if coverage_config.local_name(child.tag) != "modules":
|
|
293
|
+
continue
|
|
294
|
+
specs = []
|
|
295
|
+
for module_el in child:
|
|
296
|
+
if coverage_config.local_name(module_el.tag) != "module":
|
|
297
|
+
continue
|
|
298
|
+
text = (module_el.text or "").strip()
|
|
299
|
+
if text:
|
|
300
|
+
specs.append(text)
|
|
301
|
+
return specs
|
|
302
|
+
return None
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
# ---------------------------------------------------------------------------
|
|
306
|
+
# Gradle — settings.gradle(.kts) include(...) resolution
|
|
307
|
+
# ---------------------------------------------------------------------------
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def _gradle_module_paths(root: Path):
|
|
311
|
+
"""Resolve `include(...)` declarations from `settings.gradle` or
|
|
312
|
+
`settings.gradle.kts`.
|
|
313
|
+
|
|
314
|
+
Returns a sorted list of repo-relative POSIX module directories,
|
|
315
|
+
`None` when neither settings file is present (this stack does not
|
|
316
|
+
apply), or `coverage_config.discovery_error(...)` when a settings file is
|
|
317
|
+
present but declares no `include(...)` this parser recognizes, or a
|
|
318
|
+
resolved project escapes `root`."""
|
|
319
|
+
settings_path = None
|
|
320
|
+
for name in _GRADLE_SETTINGS_FILES:
|
|
321
|
+
candidate = root / name
|
|
322
|
+
if candidate.is_file():
|
|
323
|
+
settings_path = candidate
|
|
324
|
+
break
|
|
325
|
+
if settings_path is None:
|
|
326
|
+
return None
|
|
327
|
+
|
|
328
|
+
text = _read_contained_text(settings_path, root)
|
|
329
|
+
if isinstance(text, dict):
|
|
330
|
+
return text # a discovery_error propagated up
|
|
331
|
+
|
|
332
|
+
specs = _parse_gradle_includes(text)
|
|
333
|
+
if not specs:
|
|
334
|
+
return coverage_config.discovery_error(
|
|
335
|
+
f"'{settings_path}' is present but declares no include(...) this "
|
|
336
|
+
"parser recognizes (supported: Groovy and Kotlin DSL "
|
|
337
|
+
"include ':a', include(\":a\", \":b\") forms; includeBuild "
|
|
338
|
+
"composite builds are out of scope). Treating it as "
|
|
339
|
+
"single-project would silently exclude every subproject from "
|
|
340
|
+
"coverage — the exact failure mode multi-project discovery "
|
|
341
|
+
"exists to prevent."
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
resolved: set[str] = set()
|
|
345
|
+
for spec in specs:
|
|
346
|
+
cleaned = spec.strip().strip(":")
|
|
347
|
+
rel_spec = cleaned.replace(":", "/")
|
|
348
|
+
if not rel_spec:
|
|
349
|
+
continue
|
|
350
|
+
if not _GRADLE_SPEC_ALLOWED_RE.match(rel_spec):
|
|
351
|
+
return coverage_config.discovery_error(
|
|
352
|
+
f"'{settings_path}' includes a project spec containing "
|
|
353
|
+
"characters this module refuses to build a path from "
|
|
354
|
+
f"({cleaned!r}); allowed characters are letters, digits, and "
|
|
355
|
+
"'. _ - / :'. Refusing to resolve it."
|
|
356
|
+
)
|
|
357
|
+
module_dir = _contained_dir(root / rel_spec, root)
|
|
358
|
+
if isinstance(module_dir, dict):
|
|
359
|
+
return module_dir # a discovery_error propagated up
|
|
360
|
+
if not module_dir.is_dir():
|
|
361
|
+
return coverage_config.discovery_error(
|
|
362
|
+
f"'{settings_path}' includes ':{spec.strip().strip(':')}' but "
|
|
363
|
+
f"'{module_dir}' is not a directory on disk. A declared "
|
|
364
|
+
"subproject that does not exist is a broken build graph, not a "
|
|
365
|
+
"non-test module — classifying it NOT_TEST would drop it from "
|
|
366
|
+
"coverage accounting silently. (A subproject whose directory is "
|
|
367
|
+
"remapped via project(...).projectDir is not resolved by this "
|
|
368
|
+
"module; see the module docstring.)"
|
|
369
|
+
)
|
|
370
|
+
resolved.add(module_dir.relative_to(root).as_posix())
|
|
371
|
+
if not resolved:
|
|
372
|
+
return coverage_config.discovery_error(
|
|
373
|
+
f"'{settings_path}' declares include(...) entries but none "
|
|
374
|
+
"resolved to a subproject path."
|
|
375
|
+
)
|
|
376
|
+
return sorted(resolved)
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def _parse_gradle_includes(text: str) -> list:
|
|
380
|
+
"""Every quoted project path declared by an `include` call in `text`.
|
|
381
|
+
|
|
382
|
+
Scans the **whole comment-stripped document**, not line by line: a Groovy
|
|
383
|
+
multi-line argument list (`include ':app',\\n ':lib'`) is one
|
|
384
|
+
declaration and must yield both modules. Parsing per line would see
|
|
385
|
+
`':lib'` on a line with no `include` token and silently drop it — a
|
|
386
|
+
partial discovery is the same silent-subproject-exclusion failure mode
|
|
387
|
+
this module exists to prevent, and unlike a zero-spec parse it would slip
|
|
388
|
+
past the "no recognized include" guard because `specs` is non-empty.
|
|
389
|
+
|
|
390
|
+
`includeBuild`/`includeFlat` are excluded by the regex's identifier
|
|
391
|
+
lookahead, and each call's argument list is bounded by
|
|
392
|
+
`_collect_quoted_args`, so a second statement on the same line
|
|
393
|
+
(`include ':core'; includeBuild '../other'`) cannot bleed into the
|
|
394
|
+
first call's specs."""
|
|
395
|
+
stripped = _strip_gradle_comments(text)
|
|
396
|
+
specs = []
|
|
397
|
+
for match in _GRADLE_INCLUDE_RE.finditer(stripped):
|
|
398
|
+
specs.extend(_collect_quoted_args(stripped, match.end()))
|
|
399
|
+
return specs
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def _collect_quoted_args(text: str, start: int) -> list:
|
|
403
|
+
"""The quoted string arguments of one `include` call beginning at `start`.
|
|
404
|
+
|
|
405
|
+
Consumes quoted strings separated only by `_GRADLE_ARG_SEPARATORS`
|
|
406
|
+
(whitespace, commas, parentheses), so a multi-line list keeps collecting
|
|
407
|
+
across newlines. Any other character — `;`, a new `include`, an
|
|
408
|
+
identifier, an assignment — ends this call's argument list."""
|
|
409
|
+
args = []
|
|
410
|
+
i = start
|
|
411
|
+
length = len(text)
|
|
412
|
+
while i < length:
|
|
413
|
+
ch = text[i]
|
|
414
|
+
if ch in _GRADLE_ARG_SEPARATORS:
|
|
415
|
+
i += 1
|
|
416
|
+
continue
|
|
417
|
+
if ch in "'\"":
|
|
418
|
+
match = _GRADLE_QUOTED_RE.match(text, i)
|
|
419
|
+
if match is None:
|
|
420
|
+
break # an unterminated quote ends the argument list
|
|
421
|
+
args.append(match.group(1))
|
|
422
|
+
i = match.end()
|
|
423
|
+
continue
|
|
424
|
+
break
|
|
425
|
+
return args
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def _strip_gradle_comments(text: str) -> str:
|
|
429
|
+
"""`text` with `//` line comments and `/* ... */` block comments replaced
|
|
430
|
+
by whitespace, ignoring comment markers that appear inside a quoted
|
|
431
|
+
string. Block comments are handled because commenting out a subproject
|
|
432
|
+
with `/* include ':legacy' */` is an ordinary Gradle idiom, and treating
|
|
433
|
+
it as a live declaration would invent a phantom module.
|
|
434
|
+
|
|
435
|
+
Comment spans are replaced with spaces rather than removed so that
|
|
436
|
+
positions stay aligned and a block comment can never join two separate
|
|
437
|
+
statements into one."""
|
|
438
|
+
out = []
|
|
439
|
+
i = 0
|
|
440
|
+
length = len(text)
|
|
441
|
+
quote = None
|
|
442
|
+
while i < length:
|
|
443
|
+
ch = text[i]
|
|
444
|
+
if quote:
|
|
445
|
+
out.append(ch)
|
|
446
|
+
if ch == "\\" and i + 1 < length:
|
|
447
|
+
out.append(text[i + 1]) # an escape can't close the string
|
|
448
|
+
i += 2
|
|
449
|
+
continue
|
|
450
|
+
if ch == quote:
|
|
451
|
+
quote = None
|
|
452
|
+
i += 1
|
|
453
|
+
continue
|
|
454
|
+
if ch in "'\"":
|
|
455
|
+
quote = ch
|
|
456
|
+
out.append(ch)
|
|
457
|
+
i += 1
|
|
458
|
+
continue
|
|
459
|
+
if ch == "/" and text[i + 1 : i + 2] == "/":
|
|
460
|
+
while i < length and text[i] != "\n":
|
|
461
|
+
out.append(" ")
|
|
462
|
+
i += 1
|
|
463
|
+
continue
|
|
464
|
+
if ch == "/" and text[i + 1 : i + 2] == "*":
|
|
465
|
+
end = text.find("*/", i + 2)
|
|
466
|
+
stop = length if end == -1 else end + 2
|
|
467
|
+
for j in range(i, stop):
|
|
468
|
+
out.append("\n" if text[j] == "\n" else " ")
|
|
469
|
+
i = stop
|
|
470
|
+
continue
|
|
471
|
+
out.append(ch)
|
|
472
|
+
i += 1
|
|
473
|
+
return "".join(out)
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
# ---------------------------------------------------------------------------
|
|
477
|
+
# Containment helpers
|
|
478
|
+
# ---------------------------------------------------------------------------
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
def _contained_dir(candidate: Path, root: Path):
|
|
482
|
+
"""`candidate` resolved, once, after confirming it stays inside `root`;
|
|
483
|
+
otherwise `coverage_config.discovery_error(...)`.
|
|
484
|
+
|
|
485
|
+
Containment only — this helper deliberately does not require the path to
|
|
486
|
+
exist, because a non-existent path still has to be containment-checked
|
|
487
|
+
before it is named in any message. Existence is the CALLER's check: both
|
|
488
|
+
`_maven_module_paths` and `_gradle_module_paths` reject a declared module
|
|
489
|
+
whose directory is absent as its own `discovery_error`, matching
|
|
490
|
+
`coverage_discovery_dotnet.py`'s missing-project error rather than letting
|
|
491
|
+
it classify `NOT_TEST` and vanish from coverage accounting."""
|
|
492
|
+
try:
|
|
493
|
+
resolved = candidate.resolve()
|
|
494
|
+
except (OSError, ValueError) as exc:
|
|
495
|
+
# ValueError, not just OSError — see `_contained_file` for why.
|
|
496
|
+
return coverage_config.discovery_error(
|
|
497
|
+
f"Could not resolve module path '{candidate}': {exc}"
|
|
498
|
+
)
|
|
499
|
+
if not coverage_config.is_within(resolved, root):
|
|
500
|
+
return coverage_config.discovery_error(
|
|
501
|
+
f"Module path '{candidate}' resolves to '{resolved}', which is "
|
|
502
|
+
f"outside the repository root ('{root}'); refusing to include it."
|
|
503
|
+
)
|
|
504
|
+
return resolved
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
def _contained_file(candidate: Path, root: Path):
|
|
508
|
+
"""`candidate` resolved (following symlinks) after confirming the resolved
|
|
509
|
+
target stays inside `root`; otherwise `coverage_config.discovery_error(...)`.
|
|
510
|
+
Separate from `_contained_dir` because a symlinked build file inside an
|
|
511
|
+
already-contained directory can still point outside the repository."""
|
|
512
|
+
try:
|
|
513
|
+
resolved = candidate.resolve()
|
|
514
|
+
except (OSError, ValueError) as exc:
|
|
515
|
+
# ValueError, not just OSError: `resolve()` raises it for a path with
|
|
516
|
+
# an embedded NUL, which is reachable from a settings file decoded
|
|
517
|
+
# with errors="replace" (U+0000 is not replaced).
|
|
518
|
+
return coverage_config.discovery_error(
|
|
519
|
+
f"Could not resolve build file '{candidate}': {exc}"
|
|
520
|
+
)
|
|
521
|
+
if not coverage_config.is_within(resolved, root):
|
|
522
|
+
return coverage_config.discovery_error(
|
|
523
|
+
f"Build file '{candidate}' resolves to '{resolved}', which is "
|
|
524
|
+
f"outside the repository root ('{root}'); refusing to read it."
|
|
525
|
+
)
|
|
526
|
+
return resolved
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
def _parse_contained_xml(path: Path, root: Path):
|
|
530
|
+
"""The parsed root element of the XML at `path`, or
|
|
531
|
+
`coverage_config.discovery_error(...)`.
|
|
532
|
+
|
|
533
|
+
Containment-checks `path`, then reads it ONCE through
|
|
534
|
+
`coverage_config.read_and_screen_xml` — the shared `<!DOCTYPE`/`<!ENTITY`
|
|
535
|
+
entity-expansion screen — and parses THOSE bytes. Never re-opens the
|
|
536
|
+
checked path, so there is no resolve-then-reopen window in which a
|
|
537
|
+
symlink swap could redirect the read."""
|
|
538
|
+
contained = _contained_file(path, root)
|
|
539
|
+
if isinstance(contained, dict):
|
|
540
|
+
return contained # a discovery_error propagated up
|
|
541
|
+
data, error = coverage_config.read_and_screen_xml(contained)
|
|
542
|
+
if error is not None:
|
|
543
|
+
return error
|
|
544
|
+
try:
|
|
545
|
+
return ET.fromstring(data)
|
|
546
|
+
except ET.ParseError as exc:
|
|
547
|
+
return coverage_config.discovery_error(
|
|
548
|
+
f"Could not parse '{path}' as XML: {exc}"
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
def _read_contained_text(path: Path, root: Path):
|
|
553
|
+
"""The text of the file at `path`, or
|
|
554
|
+
`coverage_config.discovery_error(...)`.
|
|
555
|
+
|
|
556
|
+
Containment-checks `path` and reads it exactly once. Decoded with
|
|
557
|
+
`errors="replace"` because a Gradle settings/build file's encoding is not
|
|
558
|
+
declared anywhere — an undecodable byte must not abort discovery. Callers
|
|
559
|
+
that build a filesystem path from the decoded text must validate it first
|
|
560
|
+
(see `_GRADLE_SPEC_ALLOWED_RE`): `errors="replace"` does not replace
|
|
561
|
+
U+0000."""
|
|
562
|
+
contained = _contained_file(path, root)
|
|
563
|
+
if isinstance(contained, dict):
|
|
564
|
+
return contained # a discovery_error propagated up
|
|
565
|
+
try:
|
|
566
|
+
return contained.read_text(encoding="utf-8", errors="replace")
|
|
567
|
+
except OSError as exc:
|
|
568
|
+
return coverage_config.discovery_error(f"Could not read '{path}': {exc}")
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
# ---------------------------------------------------------------------------
|
|
572
|
+
# Module classification
|
|
573
|
+
# ---------------------------------------------------------------------------
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
def _classify_module(module_dir: Path, root: Path):
|
|
577
|
+
"""`TEST` when `module_dir` has both a test-source directory and a
|
|
578
|
+
JUnit/TestNG dependency in its own build file; `AMBIGUOUS` when it has a
|
|
579
|
+
test-source directory but no such dependency declared locally (it may be
|
|
580
|
+
inherited — this module never evaluates inheritance); `NOT_TEST` when it
|
|
581
|
+
has no test-source directory at all.
|
|
582
|
+
|
|
583
|
+
Returns `coverage_config.discovery_error(...)` instead — never a
|
|
584
|
+
classification — when a build file exists but cannot be read or parsed, or
|
|
585
|
+
when it (or a test-source directory) resolves outside `root`."""
|
|
586
|
+
has_test_sources = False
|
|
587
|
+
for rel in _TEST_SOURCE_DIRS:
|
|
588
|
+
# Route the probe through the containment check rather than a bare
|
|
589
|
+
# `is_dir()`: `is_dir()` follows symlinks, so `mod/src/test/java -> /etc`
|
|
590
|
+
# would otherwise stat outside the repository root and let an
|
|
591
|
+
# out-of-root directory's existence decide this module's
|
|
592
|
+
# classification.
|
|
593
|
+
candidate = _contained_dir(module_dir / rel, root)
|
|
594
|
+
if isinstance(candidate, dict):
|
|
595
|
+
return candidate # a discovery_error propagated up
|
|
596
|
+
if candidate.is_dir():
|
|
597
|
+
has_test_sources = True
|
|
598
|
+
break
|
|
599
|
+
if not has_test_sources:
|
|
600
|
+
return TestClassification.NOT_TEST
|
|
601
|
+
|
|
602
|
+
declares_framework = _declares_test_framework(module_dir, root)
|
|
603
|
+
if isinstance(declares_framework, dict):
|
|
604
|
+
return declares_framework # a discovery_error propagated up
|
|
605
|
+
return (
|
|
606
|
+
TestClassification.TEST
|
|
607
|
+
if declares_framework
|
|
608
|
+
else TestClassification.AMBIGUOUS
|
|
609
|
+
)
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
def _declares_test_framework(module_dir: Path, root: Path):
|
|
613
|
+
"""True when `module_dir`'s own build file declares a JUnit or TestNG
|
|
614
|
+
dependency; False when no build file declares one (including when the
|
|
615
|
+
module has no build file at all — an inherited-only declaration is
|
|
616
|
+
indistinguishable from none here, which is exactly why the caller maps
|
|
617
|
+
False to `AMBIGUOUS` rather than `NOT_TEST`). Returns
|
|
618
|
+
`coverage_config.discovery_error(...)` when a build file exists but cannot
|
|
619
|
+
be read or parsed.
|
|
620
|
+
|
|
621
|
+
Both branches are scoped the SAME way, deliberately: to a real dependency
|
|
622
|
+
declaration in the module's own build file. An earlier version scoped the
|
|
623
|
+
POM branch carefully and then let the Gradle branch substring-scan the
|
|
624
|
+
whole file — which resolved to a confident `TEST` for exactly the shapes
|
|
625
|
+
the POM branch documents as wrong (a `platform('org.junit:junit-bom')`
|
|
626
|
+
version pin, a `plugins { id 'org.junit...' }` id, a project name
|
|
627
|
+
containing `junit`), silently spending the `AMBIGUOUS` signal
|
|
628
|
+
`drift_check` needs."""
|
|
629
|
+
pom_path = module_dir / _MAVEN_POM_FILE
|
|
630
|
+
if pom_path.is_file():
|
|
631
|
+
found = _pom_declares_test_framework(pom_path, root)
|
|
632
|
+
if isinstance(found, dict) or found:
|
|
633
|
+
return found
|
|
634
|
+
|
|
635
|
+
for name in _GRADLE_BUILD_FILES:
|
|
636
|
+
build_path = module_dir / name
|
|
637
|
+
if not build_path.is_file():
|
|
638
|
+
continue
|
|
639
|
+
text = _read_contained_text(build_path, root)
|
|
640
|
+
if isinstance(text, dict):
|
|
641
|
+
return text # a discovery_error propagated up
|
|
642
|
+
if _gradle_text_declares_test_framework(text):
|
|
643
|
+
return True
|
|
644
|
+
|
|
645
|
+
return False
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
def _gradle_text_declares_test_framework(text: str) -> bool:
|
|
649
|
+
"""True when comment-stripped Gradle build-script `text` declares a
|
|
650
|
+
JUnit/TestNG dependency on a TEST configuration.
|
|
651
|
+
|
|
652
|
+
Scoped to match the POM branch's precision rather than substring-scanning
|
|
653
|
+
the file:
|
|
654
|
+
|
|
655
|
+
- only lines whose dependency configuration is a test one
|
|
656
|
+
(`testImplementation`, `testCompileOnly`, `testRuntimeOnly`,
|
|
657
|
+
`testApi`, `androidTestImplementation`, …) count. A `plugins { id
|
|
658
|
+
'org.junit.platform...' }` line, an `archivesBaseName` containing
|
|
659
|
+
`junit`, or a non-test `implementation` are not test dependencies.
|
|
660
|
+
- a `platform(...)`/`enforcedPlatform(...)` BOM line is excluded: like
|
|
661
|
+
Maven's `<dependencyManagement>`, it pins a version without declaring
|
|
662
|
+
a dependency, so it must leave the module `AMBIGUOUS` rather than
|
|
663
|
+
resolving it to `TEST`."""
|
|
664
|
+
for line in _join_gradle_continuations(_strip_gradle_comments(text)):
|
|
665
|
+
lowered = line.lower()
|
|
666
|
+
if not any(token in lowered for token in _TEST_FRAMEWORK_TOKENS):
|
|
667
|
+
continue
|
|
668
|
+
if _GRADLE_BOM_RE.search(lowered):
|
|
669
|
+
continue # a version pin, not a dependency declaration
|
|
670
|
+
if _GRADLE_TEST_CONFIG_RE.search(line):
|
|
671
|
+
return True
|
|
672
|
+
return False
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
def _join_gradle_continuations(text: str) -> list:
|
|
676
|
+
"""`text`'s lines, with a line that ends in `(` or `,` joined to the one
|
|
677
|
+
after it.
|
|
678
|
+
|
|
679
|
+
Without this, a Kotlin-DSL declaration split across lines —
|
|
680
|
+
`testImplementation(\\n "org.junit.jupiter:junit-jupiter"\\n)` — hides the
|
|
681
|
+
configuration token from its own argument: the first line carries no
|
|
682
|
+
framework token and the second does not begin with the configuration, so
|
|
683
|
+
the module would classify `AMBIGUOUS` despite declaring the dependency
|
|
684
|
+
locally."""
|
|
685
|
+
joined = []
|
|
686
|
+
pending = ""
|
|
687
|
+
for raw_line in text.splitlines():
|
|
688
|
+
line = raw_line.strip()
|
|
689
|
+
if not line:
|
|
690
|
+
continue
|
|
691
|
+
pending = f"{pending} {line}".strip() if pending else line
|
|
692
|
+
if pending.endswith(_GRADLE_CONTINUATION_END):
|
|
693
|
+
continue
|
|
694
|
+
joined.append(pending)
|
|
695
|
+
pending = ""
|
|
696
|
+
if pending:
|
|
697
|
+
joined.append(pending)
|
|
698
|
+
return joined
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
def _pom_declares_test_framework(pom_path: Path, root: Path):
|
|
702
|
+
"""True when `pom_path`'s own top-level `<dependencies>` declare a JUnit or
|
|
703
|
+
TestNG artifact; False otherwise; `coverage_config.discovery_error(...)`
|
|
704
|
+
when the POM cannot be read or is not well-formed XML.
|
|
705
|
+
|
|
706
|
+
Scoped deliberately to `<project>`'s DIRECT `<dependencies>` child, and
|
|
707
|
+
within it to each `<dependency>`'s own `<groupId>`/`<artifactId>`. A
|
|
708
|
+
whole-document scan would classify as `TEST`: a module merely *named*
|
|
709
|
+
`junit-extensions` (its own `<artifactId>`), a `<dependencyManagement>`
|
|
710
|
+
or `junit-bom` version declaration, a `junit-platform-maven-plugin` under
|
|
711
|
+
`<build><plugins>`, and a `<profiles>`-scoped dependency. The first three
|
|
712
|
+
are not dependencies of this module at all; the last is *conditional*, and
|
|
713
|
+
a conditional declaration is exactly what this module reports as
|
|
714
|
+
`AMBIGUOUS` (mirroring the .NET module's refusal to evaluate MSBuild
|
|
715
|
+
`Condition` attributes) rather than resolving to a confident `TEST`.
|
|
716
|
+
Returning False for those shapes is what preserves the `AMBIGUOUS` signal
|
|
717
|
+
`drift_check` uses to force an operator decision."""
|
|
718
|
+
root_el = _parse_contained_xml(pom_path, root)
|
|
719
|
+
if isinstance(root_el, dict):
|
|
720
|
+
return root_el # a discovery_error propagated up
|
|
721
|
+
|
|
722
|
+
for child in root_el:
|
|
723
|
+
if coverage_config.local_name(child.tag) != "dependencies":
|
|
724
|
+
continue
|
|
725
|
+
for dependency in child:
|
|
726
|
+
if coverage_config.local_name(dependency.tag) != "dependency":
|
|
727
|
+
continue
|
|
728
|
+
if _dependency_names_test_framework(dependency):
|
|
729
|
+
return True
|
|
730
|
+
return False
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
def _dependency_names_test_framework(dependency) -> bool:
|
|
734
|
+
"""True when a single `<dependency>` element's own `<groupId>`/
|
|
735
|
+
`<artifactId>` names a JUnit or TestNG artifact."""
|
|
736
|
+
for field in dependency:
|
|
737
|
+
if coverage_config.local_name(field.tag) not in ("groupId", "artifactId"):
|
|
738
|
+
continue
|
|
739
|
+
value = (field.text or "").lower()
|
|
740
|
+
if any(token in value for token in _TEST_FRAMEWORK_TOKENS):
|
|
741
|
+
return True
|
|
742
|
+
return False
|