@danmoisan/drm-copilot-mcp 1.1.18 → 1.1.20
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/out/mcp-server.js +1149 -661
- package/package.json +3 -2
- package/resources/claude-customizations/.claude/agents/atomic-executor.md +28 -6
- package/resources/claude-customizations/.claude/agents/atomic-planner.md +20 -2
- package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +17 -4
- package/resources/claude-customizations/.claude/agents/epic-planner.md +5 -4
- package/resources/claude-customizations/.claude/agents/feature-review.md +6 -4
- package/resources/claude-customizations/.claude/agents/orchestrator.md +9 -5
- package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +35 -13
- package/resources/claude-customizations/.claude/agents/parallel-planner.md +9 -6
- package/resources/claude-customizations/.claude/agents/pr-author.md +15 -2
- package/resources/claude-customizations/.claude/agents/prd-feature.md +1 -1
- package/resources/claude-customizations/.claude/agents/task-researcher.md +8 -0
- package/resources/claude-customizations/.claude/hooks/check-powershell-test-purity.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/check-python-test-purity.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +111 -46
- package/resources/claude-customizations/.claude/hooks/enforce-completion-helpers.ps1 +105 -0
- package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +8 -1
- package/resources/claude-customizations/.claude/hooks/enforce-epic-invocation-origin.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-command.ps1 +190 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-resolution.ps1 +116 -59
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +78 -77
- package/resources/claude-customizations/.claude/hooks/enforce-epic-wave-barrier.ps1 +74 -75
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate-resolution.ps1 +127 -35
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +61 -57
- package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-feature-folder-order.ps1 +129 -39
- package/resources/claude-customizations/.claude/hooks/enforce-mermaid-validation.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +9 -3
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +39 -51
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +17 -12
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +9 -3
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier.ps1 +65 -50
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-drift-gate.ps1 +49 -42
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +54 -72
- package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +8 -2
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-command-allowlist.ps1 +279 -0
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +129 -75
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.artifact-root.ps1 +233 -0
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.docs-only.ps1 +224 -0
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +69 -24
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner-helpers.ps1 +29 -18
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +10 -4
- package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +14 -5
- package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +8 -2
- package/resources/claude-customizations/.claude/hooks/feature-folder-resolution.ps1 +373 -0
- package/resources/claude-customizations/.claude/hooks/feature-review-coverage-thresholds.ps1 +85 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-heredoc.ps1 +119 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-invocation-operands.ps1 +198 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-invocation.ps1 +254 -255
- package/resources/claude-customizations/.claude/hooks/hook-command-payload-powershell.ps1 +201 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-payload.ps1 +484 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-scanner.ps1 +27 -110
- package/resources/claude-customizations/.claude/hooks/hook-dependency-guard.ps1 +114 -0
- package/resources/claude-customizations/.claude/hooks/hook-docs-only-diff.ps1 +497 -0
- package/resources/claude-customizations/.claude/hooks/premortem-section.ps1 +149 -0
- package/resources/claude-customizations/.claude/hooks/subagentstop-envelope.ps1 +192 -0
- package/resources/claude-customizations/.claude/hooks/validate-bash.ps1 +9 -3
- package/resources/claude-customizations/.claude/hooks/validate-discovery-artifact-gate.ps1 +40 -42
- package/resources/claude-customizations/.claude/hooks/validate-executor-output.ps1 +267 -113
- package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-metrics.ps1 +180 -0
- package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-rules.ps1 +195 -0
- package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage.ps1 +141 -395
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-resolution.ps1 +311 -0
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-routing.ps1 +170 -0
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +115 -190
- package/resources/claude-customizations/.claude/hooks/validate-planner-output.ps1 +77 -33
- package/resources/claude-customizations/.claude/hooks/validate-pr-author-output.ps1 +39 -44
- package/resources/claude-customizations/.claude/hooks/validate-prd-feature-output.ps1 +43 -14
- package/resources/claude-customizations/.claude/hooks/validate-required-artifact-output.ps1 +66 -36
- package/resources/claude-customizations/.claude/hooks/validate-task-researcher-output.ps1 +52 -36
- package/resources/claude-customizations/.claude/lib/bash/mutate-parallel-run.sh +191 -0
- package/resources/claude-customizations/.claude/lib/bash/parallel-cohorts.sh +13 -3
- package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-cli.sh +152 -0
- package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-ops.sh +193 -0
- package/resources/claude-customizations/.claude/lib/bash/parallel-mutation.sh +322 -0
- package/resources/claude-customizations/.claude/lib/bash/remove-parallel-item.sh +146 -0
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +23 -33
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusTokenShape.psm1 +86 -6
- package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-CiGateParser.ps1 +91 -19
- package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-MergeConfirmationParser.ps1 +203 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateEpicWaveBarrier.psm1 +346 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1 +24 -3
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +79 -2
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1 +3 -3
- package/resources/claude-customizations/.claude/rules/architecture-boundaries.md +5 -5
- package/resources/claude-customizations/.claude/rules/csharp.md +2 -2
- package/resources/claude-customizations/.claude/rules/general-code-change.md +1 -1
- package/resources/claude-customizations/.claude/rules/general-unit-test.md +3 -2
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +37 -4
- package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +50 -5
- package/resources/claude-customizations/.claude/rules/plan-acceptance-gates.md +85 -10
- package/resources/claude-customizations/.claude/rules/quality-tiers.md +11 -7
- package/resources/claude-customizations/.claude/rules/shell.md +10 -8
- package/resources/claude-customizations/.claude/rules/typescript.md +1 -1
- package/resources/claude-customizations/.claude/settings.json +3 -4
- package/resources/claude-customizations/.claude/skills/acceptance-criteria-tracking/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +52 -16
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +7 -3
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_detached_lib.sh +1 -1
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_enumerate_lib.sh +2 -0
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_scan_helper.sh +2 -0
- package/resources/claude-customizations/.claude/skills/csharp-change-budget-router/SKILL.md +2 -2
- package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +45 -9
- package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +5 -3
- package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
- package/resources/claude-customizations/.claude/skills/execution-environment/SKILL.md +134 -0
- package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +4 -3
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +36 -15
- package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +52 -14
- package/resources/claude-customizations/.claude/skills/parallel-close/SKILL.md +22 -9
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +108 -21
- package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +63 -7
- package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +35 -12
- package/resources/claude-customizations/.claude/skills/powershell-change-budget-router/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +3 -1
- package/resources/claude-customizations/.claude/skills/python-change-budget-router/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/quota-throttling/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +16 -8
- package/resources/claude-customizations/.claude/skills/research-issue/SKILL.md +6 -0
- package/resources/claude-customizations/pack-manifests/core.json +27 -0
- package/resources/codex-and-agents-customizations/.agents/skills/README.md +1 -0
- package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/architecture-boundaries/SKILL.md +5 -5
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-executor/SKILL.md +19 -7
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +43 -4
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +18 -6
- package/resources/codex-and-agents-customizations/.agents/skills/csharp/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.agents/skills/csharp-change-budget-router/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.agents/skills/epic-orchestrate/SKILL.md +16 -1
- package/resources/codex-and-agents-customizations/.agents/skills/epic-plan/SKILL.md +17 -1
- package/resources/codex-and-agents-customizations/.agents/skills/epic-run/SKILL.md +2 -1
- package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
- package/resources/codex-and-agents-customizations/.agents/skills/execution-environment/SKILL.md +134 -0
- package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +13 -5
- package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +3 -2
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +27 -8
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/powershell-change-budget-router/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/python-change-budget-router/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +11 -7
- package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +7 -3
- package/resources/codex-and-agents-customizations/.agents/skills/research-issue/SKILL.md +6 -0
- package/resources/codex-and-agents-customizations/.agents/skills/translate-claude-to-codex/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c1.toml +21 -6
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +21 -6
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +21 -6
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +21 -6
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +21 -6
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +21 -6
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +15 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +15 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +15 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +15 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +15 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +15 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planning.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-atomic-planning.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/epic-orchestrator.toml +4 -1
- package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +6 -6
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c1.toml +7 -4
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +7 -4
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +7 -4
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +7 -4
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +7 -4
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +7 -4
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-planning.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-atomic-planning.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c1.toml +6 -0
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c2.toml +6 -0
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c3-elevated.toml +6 -0
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c3.toml +6 -0
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c4.toml +6 -0
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher.toml +6 -0
- package/resources/codex-and-agents-customizations/.codex/codex-web-setup.sh +318 -273
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/check-powershell-test-purity.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/check-python-test-purity.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/codex-epic-child-launch-attestation.ps1 +1 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-codex-model-routing.ps1 +8 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +97 -44
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-helpers.ps1 +105 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-child-worktree-binding.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate-command.ps1 +190 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +54 -43
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +6 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-root-invocation.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-wave-barrier.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-worktree-removal-gate.ps1 +54 -41
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-evidence-locations.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +35 -14
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +24 -11
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +8 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +13 -4
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +8 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/feature-folder-resolution.ps1 +373 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/feature-review-coverage-thresholds.ps1 +85 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-heredoc.ps1 +119 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation-operands.ps1 +198 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation.ps1 +254 -255
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload-powershell.ps1 +201 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload.ps1 +484 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-scanner.ps1 +27 -110
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-dependency-guard.ps1 +114 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-docs-only-diff.ps1 +497 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-bash.ps1 +8 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-codex-subagent-routing.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-feature-review-coverage.ps1 +24 -8
- package/resources/codex-and-agents-customizations/.codex/prompts/feature-review-remediate.md +6 -4
- package/resources/codex-and-agents-customizations/pack-manifests/core.json +11 -0
- package/resources/customizations/.github/agents/atomic_executor.agent.md +21 -11
- package/resources/customizations/.github/agents/atomic_planning.agent.md +8 -3
- package/resources/customizations/.github/agents/csharp-atomic-executor.agent.md +13 -9
- package/resources/customizations/.github/agents/csharp-atomic-planning.agent.md +2 -2
- package/resources/customizations/.github/agents/csharp-orchestrator.agent.md +2 -2
- package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +5 -5
- package/resources/customizations/.github/agents/orchestrator.agent.md +8 -5
- package/resources/customizations/.github/agents/powershell-atomic-executor.agent.md +13 -9
- package/resources/customizations/.github/agents/powershell-atomic-planning.agent.md +2 -2
- package/resources/customizations/.github/agents/powershell-orchestrator.agent.md +2 -2
- package/resources/customizations/.github/agents/powershell-typed-engineer.agent.md +3 -3
- package/resources/customizations/.github/agents/python-atomic-executor.agent.md +13 -9
- package/resources/customizations/.github/agents/python-atomic-planning.agent.md +2 -2
- package/resources/customizations/.github/agents/python-orchestrator.agent.md +2 -2
- package/resources/customizations/.github/agents/task-researcher.agent.md +6 -0
- package/resources/customizations/.github/instructions/csharp-code-change.instructions.md +4 -4
- package/resources/customizations/.github/instructions/csharp-unit-test.instructions.md +2 -2
- package/resources/customizations/.github/prompts/generate-atomic-plan.prompt.md +16 -10
- package/resources/customizations/.github/prompts/orchestrate-csharp-work.prompt.md +2 -2
- package/resources/customizations/.github/prompts/orchestrate-powershell-work.prompt.md +1 -1
- package/resources/customizations/.github/prompts/orchestrate-python-work.prompt.md +1 -1
- package/resources/customizations/.github/prompts/orchestrate-work.prompt.md +2 -2
- package/resources/customizations/.github/skills/acceptance-criteria-tracking/SKILL.md +1 -1
- package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +43 -4
- package/resources/customizations/.github/skills/csharp-change-budget-router/SKILL.md +1 -1
- package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
- package/resources/customizations/.github/skills/execution-environment/SKILL.md +134 -0
- package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +1 -1
- package/resources/customizations/.github/skills/powershell-change-budget-router/SKILL.md +1 -1
- package/resources/customizations/.github/skills/remediation-handoff-atomic-planner/SKILL.md +7 -3
- package/resources/feature-templates/bug/plan.yyyy-MM-ddTHH-mm.md +10 -0
- package/resources/feature-templates/feature/plan.yyyy-MM-ddTHH-mm.md +11 -0
- package/resources/feature-templates/refactor/plan.yyyy-MM-ddTHH-mm.md +11 -0
- package/resources/powershell/PoshQC/PoshQC.Coverage.psm1 +314 -0
- package/resources/powershell/PoshQC/PoshQC.CoverageMerge.psm1 +163 -0
- package/resources/powershell/PoshQC/PoshQC.Shard.psm1 +296 -0
- package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +48 -32
- package/resources/powershell/PoshQC/PoshQC.psd1 +7 -1
- package/resources/powershell/PoshQC/PoshQC.psm1 +18 -5
- package/resources/powershell/PoshQC/README.md +21 -3
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +5 -319
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Returns the non-blank string entries of one list in a Pester settings table.
|
|
4
|
+
.DESCRIPTION
|
|
5
|
+
Reads Settings[Section][Key] and returns its non-blank string entries in their original order.
|
|
6
|
+
Returns nothing when Settings or the section is not a dictionary or when the key is absent.
|
|
7
|
+
Used for 'CodeCoverage.Path' and 'Run.Path' (issue #527).
|
|
8
|
+
.PARAMETER Settings
|
|
9
|
+
Settings table, normally the hashtable loaded from the Pester runsettings file.
|
|
10
|
+
.PARAMETER Section
|
|
11
|
+
Top-level section name, for example 'CodeCoverage' or 'Run'.
|
|
12
|
+
.PARAMETER Key
|
|
13
|
+
Key inside the section, for example 'Path'.
|
|
14
|
+
#>
|
|
15
|
+
function Get-PoshQCSettingsList {
|
|
16
|
+
[CmdletBinding()]
|
|
17
|
+
[OutputType([string])]
|
|
18
|
+
param(
|
|
19
|
+
$Settings,
|
|
20
|
+
[string] $Section,
|
|
21
|
+
[string] $Key
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
if ($Settings -isnot [System.Collections.IDictionary]) {
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
$sectionValue = $Settings[$Section]
|
|
29
|
+
if ($sectionValue -isnot [System.Collections.IDictionary]) {
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (-not $sectionValue.Contains($Key)) {
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
foreach ($entry in @($sectionValue[$Key])) {
|
|
38
|
+
if ($entry -is [string] -and -not [string]::IsNullOrWhiteSpace($entry)) {
|
|
39
|
+
[string] $entry
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
<#
|
|
45
|
+
.SYNOPSIS
|
|
46
|
+
Reads and validates the workspace coverage configuration (issue #527).
|
|
47
|
+
.DESCRIPTION
|
|
48
|
+
Resolves the workspace-relative configuration file (default 'config/poshqc-coverage.json')
|
|
49
|
+
against Root. An absent file reports Present = $false. A present file must be a JSON object
|
|
50
|
+
with 'version' equal to 1 and a 'roots' array of workspace-relative paths; every validation
|
|
51
|
+
failure throws a message that names the configuration file. An empty 'roots' array is valid
|
|
52
|
+
and yields an empty coverage population.
|
|
53
|
+
.PARAMETER Root
|
|
54
|
+
Workspace root the configuration path resolves against.
|
|
55
|
+
.PARAMETER ConfigRelativePath
|
|
56
|
+
Workspace-relative path to the configuration file.
|
|
57
|
+
.PARAMETER TestPathExists
|
|
58
|
+
Injectable file-existence seam. Defaults to Test-Path -PathType Leaf.
|
|
59
|
+
.PARAMETER ReadContent
|
|
60
|
+
Injectable read seam returning the raw file text. Defaults to Get-Content -Raw.
|
|
61
|
+
#>
|
|
62
|
+
function Get-PoshQCCoverageConfigRoot {
|
|
63
|
+
[CmdletBinding()]
|
|
64
|
+
[OutputType([pscustomobject])]
|
|
65
|
+
param(
|
|
66
|
+
[string] $Root,
|
|
67
|
+
[string] $ConfigRelativePath = 'config/poshqc-coverage.json',
|
|
68
|
+
[scriptblock] $TestPathExists = { param([string] $Path) Test-Path -LiteralPath $Path -PathType Leaf },
|
|
69
|
+
[scriptblock] $ReadContent = { param([string] $Path) Get-Content -LiteralPath $Path -Raw }
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
$ErrorActionPreference = 'Stop'
|
|
73
|
+
|
|
74
|
+
$configPath = if ([IO.Path]::IsPathRooted($ConfigRelativePath)) {
|
|
75
|
+
$ConfigRelativePath
|
|
76
|
+
} else {
|
|
77
|
+
Join-Path -Path $Root -ChildPath $ConfigRelativePath
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (-not (& $TestPathExists $configPath)) {
|
|
81
|
+
return [pscustomobject]@{ Present = $false; Roots = [string[]] @() }
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
$raw = [string] (& $ReadContent $configPath)
|
|
85
|
+
if ([string]::IsNullOrWhiteSpace($raw)) {
|
|
86
|
+
throw "Coverage configuration '$ConfigRelativePath' is empty."
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
$parsed = $raw | ConvertFrom-Json -ErrorAction Stop
|
|
91
|
+
} catch {
|
|
92
|
+
throw "Invalid JSON in coverage configuration '$ConfigRelativePath': $($_.Exception.Message)"
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if ($parsed -isnot [System.Management.Automation.PSCustomObject]) {
|
|
96
|
+
throw "Coverage configuration '$ConfigRelativePath' must contain a JSON object."
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# Read properties through PSObject so a missing property yields $null under StrictMode.
|
|
100
|
+
$versionProperty = $parsed.PSObject.Properties['version']
|
|
101
|
+
$versionIsInteger = $null -ne $versionProperty -and ($versionProperty.Value -is [long] -or $versionProperty.Value -is [int])
|
|
102
|
+
if (-not $versionIsInteger -or $versionProperty.Value -ne 1) {
|
|
103
|
+
throw "Coverage configuration '$ConfigRelativePath' must declare 'version' equal to 1."
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
$rootsProperty = $parsed.PSObject.Properties['roots']
|
|
107
|
+
if ($null -eq $rootsProperty) {
|
|
108
|
+
throw "Coverage configuration '$ConfigRelativePath' must declare a 'roots' array."
|
|
109
|
+
}
|
|
110
|
+
if ($rootsProperty.Value -isnot [System.Array]) {
|
|
111
|
+
throw "Coverage configuration '$ConfigRelativePath' must declare 'roots' as an array."
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
$validated = [System.Collections.Generic.List[string]]::new()
|
|
115
|
+
foreach ($entry in $rootsProperty.Value) {
|
|
116
|
+
if ($entry -isnot [string] -or [string]::IsNullOrWhiteSpace($entry)) {
|
|
117
|
+
throw "Coverage configuration '$ConfigRelativePath' contains a blank or non-string 'roots' entry."
|
|
118
|
+
}
|
|
119
|
+
if ([IO.Path]::IsPathRooted($entry)) {
|
|
120
|
+
throw "Coverage configuration '$ConfigRelativePath' entry '$entry' must be a workspace-relative path, not an absolute path."
|
|
121
|
+
}
|
|
122
|
+
if (@($entry -split '[\\/]+') -contains '..') {
|
|
123
|
+
throw "Coverage configuration '$ConfigRelativePath' entry '$entry' must not contain '..' segments."
|
|
124
|
+
}
|
|
125
|
+
$validated.Add($entry)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return [pscustomobject]@{ Present = $true; Roots = [string[]] $validated.ToArray() }
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
<#
|
|
132
|
+
.SYNOPSIS
|
|
133
|
+
Enumerates the PowerShell production files under a set of coverage roots (issue #527).
|
|
134
|
+
.DESCRIPTION
|
|
135
|
+
Joins each non-rooted root to Root, skips a nonexistent root with one warning, and keeps
|
|
136
|
+
files with a .ps1 or .psm1 extension (case-insensitive) whose name does not match
|
|
137
|
+
'*.Tests.ps1', whose first root-relative segment is not 'tests', and whose root-relative
|
|
138
|
+
directory segments are not in ExcludeDirs. Root-relative paths use forward slashes. The result
|
|
139
|
+
is sorted ordinally on RelativePath and de-duplicated case-insensitively, keeping the
|
|
140
|
+
ordinally smallest variant.
|
|
141
|
+
.PARAMETER Root
|
|
142
|
+
Workspace root that relative roots and relative paths are computed against.
|
|
143
|
+
.PARAMETER Roots
|
|
144
|
+
Coverage roots, workspace-relative or absolute.
|
|
145
|
+
.PARAMETER ExcludeDirs
|
|
146
|
+
Directory names excluded from the population.
|
|
147
|
+
.PARAMETER TestPathExists
|
|
148
|
+
Injectable directory-existence seam. Defaults to Test-Path -PathType Container.
|
|
149
|
+
.PARAMETER EnumerateFiles
|
|
150
|
+
Injectable recursive file enumeration seam. Defaults to Get-ChildItem -Recurse -File -Force.
|
|
151
|
+
.PARAMETER WarningLogger
|
|
152
|
+
Injectable warning seam for skipped roots. Defaults to Write-Warning.
|
|
153
|
+
#>
|
|
154
|
+
function Get-PoshQCCoverageFileSet {
|
|
155
|
+
[CmdletBinding()]
|
|
156
|
+
[OutputType([pscustomobject])]
|
|
157
|
+
param(
|
|
158
|
+
[string] $Root,
|
|
159
|
+
[string[]] $Roots,
|
|
160
|
+
[string[]] $ExcludeDirs = $script:DefaultExcludedDirs,
|
|
161
|
+
[scriptblock] $TestPathExists = { param([string] $Path) Test-Path -LiteralPath $Path -PathType Container },
|
|
162
|
+
[scriptblock] $EnumerateFiles = { param([string] $Path) Get-ChildItem -LiteralPath $Path -Recurse -File -Force },
|
|
163
|
+
[scriptblock] $WarningLogger = { param([string] $Message) Write-Warning $Message }
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
$ErrorActionPreference = 'Stop'
|
|
167
|
+
|
|
168
|
+
$rootPrefix = ([string] $Root -replace '\\', '/').TrimEnd('/')
|
|
169
|
+
$candidates = [System.Collections.Generic.Dictionary[string, object]]::new([StringComparer]::Ordinal)
|
|
170
|
+
foreach ($coverageRoot in @($Roots | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })) {
|
|
171
|
+
$rootPath = if ([IO.Path]::IsPathRooted($coverageRoot)) { $coverageRoot } else { Join-Path -Path $Root -ChildPath $coverageRoot }
|
|
172
|
+
if (-not (& $TestPathExists $rootPath)) {
|
|
173
|
+
& $WarningLogger "Coverage root '$coverageRoot' does not exist under '$Root'; skipping."
|
|
174
|
+
continue
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
foreach ($file in @(& $EnumerateFiles $rootPath)) {
|
|
178
|
+
if ($null -eq $file) { continue }
|
|
179
|
+
$extension = [string] $file.Extension
|
|
180
|
+
if (-not ($extension -ieq '.ps1' -or $extension -ieq '.psm1')) { continue }
|
|
181
|
+
if ([string] $file.Name -like '*.Tests.ps1') { continue }
|
|
182
|
+
|
|
183
|
+
$fullPath = [string] $file.FullName
|
|
184
|
+
$normalizedPath = $fullPath -replace '\\', '/'
|
|
185
|
+
$relativePath = $normalizedPath
|
|
186
|
+
if ($rootPrefix -and $normalizedPath.StartsWith("$rootPrefix/", [StringComparison]::OrdinalIgnoreCase)) {
|
|
187
|
+
$relativePath = $normalizedPath.Substring($rootPrefix.Length + 1)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
$segments = @($relativePath -split '/' | Where-Object { $_ -ne '' })
|
|
191
|
+
if ($segments.Count -gt 0 -and $segments[0] -ieq 'tests') { continue }
|
|
192
|
+
$directorySegments = if ($segments.Count -gt 1) { @($segments[0..($segments.Count - 2)]) } else { @() }
|
|
193
|
+
$excludedSegments = @($directorySegments | Where-Object { $ExcludeDirs -contains $_ })
|
|
194
|
+
if ($excludedSegments.Count -gt 0) { continue }
|
|
195
|
+
|
|
196
|
+
if (-not $candidates.ContainsKey($relativePath)) {
|
|
197
|
+
$candidates[$relativePath] = [pscustomobject]@{ RelativePath = $relativePath; FullPath = $fullPath }
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
[string[]] $keys = @($candidates.Keys)
|
|
203
|
+
[Array]::Sort($keys, [StringComparer]::Ordinal)
|
|
204
|
+
$seen = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase)
|
|
205
|
+
foreach ($key in $keys) {
|
|
206
|
+
if ($seen.Add($key)) {
|
|
207
|
+
$candidates[$key]
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
<#
|
|
213
|
+
.SYNOPSIS
|
|
214
|
+
Resolves the code-coverage population for one Invoke-PoshQCTest run (issue #527).
|
|
215
|
+
.DESCRIPTION
|
|
216
|
+
Applies the coverage precedence and returns the source name and the absolute file paths:
|
|
217
|
+
(1) 'settings': a caller-supplied settings file (one whose full path differs from the
|
|
218
|
+
module-default settings file) with a non-empty CodeCoverage.Path; relative entries are joined
|
|
219
|
+
to Root, rooted entries are kept as written, and each nonexistent entry is pruned and logged.
|
|
220
|
+
A non-empty CodeCoverage.Path in the module-default settings file is ignored and logged.
|
|
221
|
+
(2) 'config': the workspace coverage configuration reports Present.
|
|
222
|
+
(3) 'fallback': the effective scan-folder roots when non-empty, else the settings Run.Path.
|
|
223
|
+
.PARAMETER Root
|
|
224
|
+
Absolute workspace root.
|
|
225
|
+
.PARAMETER Settings
|
|
226
|
+
Settings table loaded from the Pester runsettings file.
|
|
227
|
+
.PARAMETER SettingsFile
|
|
228
|
+
Path of the settings file the table was loaded from.
|
|
229
|
+
.PARAMETER ScanFolderRoots
|
|
230
|
+
Effective test scan folders of the run (explicit -ScanFolders or the scan configuration).
|
|
231
|
+
.PARAMETER ExcludeDirs
|
|
232
|
+
Directory names excluded from the population.
|
|
233
|
+
.PARAMETER DefaultSettingsFile
|
|
234
|
+
The module-default settings file; a list in it is not honored.
|
|
235
|
+
.PARAMETER SettingsPathExists
|
|
236
|
+
Injectable existence seam for caller-supplied settings entries. Defaults to Test-Path.
|
|
237
|
+
.PARAMETER Logger
|
|
238
|
+
Injectable information seam. Defaults to Write-Information.
|
|
239
|
+
.PARAMETER WarningLogger
|
|
240
|
+
Injectable warning seam passed to the file-set seam. Defaults to Write-Warning.
|
|
241
|
+
.PARAMETER ReadConfig
|
|
242
|
+
Injectable configuration seam. Defaults to Get-PoshQCCoverageConfigRoot.
|
|
243
|
+
.PARAMETER GetFileSet
|
|
244
|
+
Injectable file-set seam. Defaults to Get-PoshQCCoverageFileSet.
|
|
245
|
+
#>
|
|
246
|
+
function Resolve-PoshQCCoveragePopulation {
|
|
247
|
+
[CmdletBinding()]
|
|
248
|
+
[OutputType([pscustomobject])]
|
|
249
|
+
param(
|
|
250
|
+
[string] $Root,
|
|
251
|
+
$Settings,
|
|
252
|
+
[string] $SettingsFile,
|
|
253
|
+
[string[]] $ScanFolderRoots,
|
|
254
|
+
[string[]] $ExcludeDirs = $script:DefaultExcludedDirs,
|
|
255
|
+
[string] $DefaultSettingsFile = $script:PesterSettings,
|
|
256
|
+
[scriptblock] $SettingsPathExists = { param([string] $Path) Test-Path $Path },
|
|
257
|
+
[scriptblock] $Logger = { param([string] $Message) Write-Information $Message -InformationAction Continue },
|
|
258
|
+
[scriptblock] $WarningLogger = { param([string] $Message) Write-Warning $Message },
|
|
259
|
+
[scriptblock] $ReadConfig = { param([string] $RootPath) Get-PoshQCCoverageConfigRoot -Root $RootPath },
|
|
260
|
+
[scriptblock] $GetFileSet = {
|
|
261
|
+
param([string] $RootPath, [string[]] $Roots, [string[]] $Excluded, [scriptblock] $Warn)
|
|
262
|
+
Get-PoshQCCoverageFileSet -Root $RootPath -Roots $Roots -ExcludeDirs $Excluded -WarningLogger $Warn
|
|
263
|
+
}
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
$ErrorActionPreference = 'Stop'
|
|
267
|
+
|
|
268
|
+
$settingsPaths = @(Get-PoshQCSettingsList -Settings $Settings -Section 'CodeCoverage' -Key 'Path')
|
|
269
|
+
if ($settingsPaths.Count -gt 0) {
|
|
270
|
+
# A settings file is caller-supplied custom when its full path differs from the module default.
|
|
271
|
+
$isCustomSettingsFile = $false
|
|
272
|
+
if (-not [string]::IsNullOrWhiteSpace($SettingsFile)) {
|
|
273
|
+
$settingsFullPath = [IO.Path]::GetFullPath([IO.Path]::Combine($PWD.ProviderPath, $SettingsFile))
|
|
274
|
+
$defaultFullPath = ''
|
|
275
|
+
if (-not [string]::IsNullOrWhiteSpace($DefaultSettingsFile)) {
|
|
276
|
+
$defaultFullPath = [IO.Path]::GetFullPath([IO.Path]::Combine($PWD.ProviderPath, $DefaultSettingsFile))
|
|
277
|
+
}
|
|
278
|
+
$isCustomSettingsFile = -not [string]::Equals($settingsFullPath, $defaultFullPath, [StringComparison]::OrdinalIgnoreCase)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if ($isCustomSettingsFile) {
|
|
282
|
+
$survivingPaths = [System.Collections.Generic.List[string]]::new()
|
|
283
|
+
foreach ($entry in $settingsPaths) {
|
|
284
|
+
# Rooted entries are kept and tested as written; they are never re-joined to Root.
|
|
285
|
+
$candidate = if ([IO.Path]::IsPathRooted($entry)) { $entry } else { Join-Path -Path $Root -ChildPath $entry }
|
|
286
|
+
if (& $SettingsPathExists $candidate) {
|
|
287
|
+
$survivingPaths.Add($candidate)
|
|
288
|
+
} else {
|
|
289
|
+
& $Logger "Pruned nonexistent code coverage path: $candidate"
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return [pscustomobject]@{ Source = 'settings'; Paths = [string[]] $survivingPaths.ToArray() }
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
& $Logger 'Ignored CodeCoverage.Path from the module-default settings file; the coverage population is derived from the workspace.'
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
$configRoot = & $ReadConfig $Root
|
|
300
|
+
if ($null -ne $configRoot -and $configRoot.Present) {
|
|
301
|
+
$source = 'config'
|
|
302
|
+
$coverageRoots = @($configRoot.Roots)
|
|
303
|
+
} else {
|
|
304
|
+
$source = 'fallback'
|
|
305
|
+
$coverageRoots = @($ScanFolderRoots | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
|
|
306
|
+
if ($coverageRoots.Count -eq 0) {
|
|
307
|
+
$coverageRoots = @(Get-PoshQCSettingsList -Settings $Settings -Section 'Run' -Key 'Path')
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
$files = @(& $GetFileSet $Root ([string[]] $coverageRoots) $ExcludeDirs $WarningLogger)
|
|
312
|
+
$paths = [string[]] @($files | Where-Object { $null -ne $_ } | ForEach-Object { $_.FullPath })
|
|
313
|
+
return [pscustomobject]@{ Source = $source; Paths = $paths }
|
|
314
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Loads CoverageGutters (JaCoCo-structured) XML text into an XmlDocument (issue #953).
|
|
4
|
+
.DESCRIPTION
|
|
5
|
+
Disables external resolution, so the DOCTYPE reference to report.dtd is never fetched, and keeps
|
|
6
|
+
whitespace so the merged report serializes with the layout of the first input.
|
|
7
|
+
.PARAMETER CoverageXml
|
|
8
|
+
The XML text of one coverage report.
|
|
9
|
+
#>
|
|
10
|
+
function ConvertFrom-PoshQCCoverageXml {
|
|
11
|
+
[CmdletBinding()]
|
|
12
|
+
[OutputType([System.Xml.XmlDocument])]
|
|
13
|
+
param(
|
|
14
|
+
[Parameter(Mandatory)][string] $CoverageXml
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
$document = New-Object System.Xml.XmlDocument
|
|
18
|
+
$document.XmlResolver = $null
|
|
19
|
+
$document.PreserveWhitespace = $true
|
|
20
|
+
$document.LoadXml($CoverageXml)
|
|
21
|
+
$document
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
<#
|
|
25
|
+
.SYNOPSIS
|
|
26
|
+
Merges the coverage reports written by the shards of one test run (issue #953).
|
|
27
|
+
.DESCRIPTION
|
|
28
|
+
Takes XML text only (no file I/O). Every report must list the same set of
|
|
29
|
+
package/sourcefile/line-number triples, otherwise the merge throws. For each triple the merged
|
|
30
|
+
line carries ci = max(ci) and mi = (mi + ci of the first report) - max(ci). The LINE and
|
|
31
|
+
INSTRUCTION counters of every sourcefile, every package, and the report are then recomputed
|
|
32
|
+
bottom-up: LINE covered counts lines with ci greater than 0 and LINE missed counts lines with ci
|
|
33
|
+
equal to 0; INSTRUCTION covered is the sum of ci and INSTRUCTION missed is the sum of mi.
|
|
34
|
+
Class and method elements and every counter other than LINE and INSTRUCTION are removed, because they
|
|
35
|
+
cannot be recomputed from merged line hits (CR-8). No coverage threshold is enforced. Returns the merged XML text.
|
|
36
|
+
.PARAMETER CoverageXml
|
|
37
|
+
The XML text of each shard's coverage report; at least one is required.
|
|
38
|
+
#>
|
|
39
|
+
function Merge-PoshQCCoverageReport {
|
|
40
|
+
[CmdletBinding()]
|
|
41
|
+
[OutputType([string])]
|
|
42
|
+
param(
|
|
43
|
+
[Parameter(Mandatory)][AllowEmptyCollection()][string[]] $CoverageXml
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
if ($CoverageXml.Count -lt 1) {
|
|
47
|
+
throw 'Merge-PoshQCCoverageReport requires at least one coverage report.'
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
$indexLines = {
|
|
51
|
+
param([System.Xml.XmlDocument] $Document, [int] $Position)
|
|
52
|
+
$index = New-Object 'System.Collections.Generic.Dictionary[string,System.Xml.XmlElement]' ([System.StringComparer]::Ordinal)
|
|
53
|
+
foreach ($package in $Document.SelectNodes('/report/package')) {
|
|
54
|
+
foreach ($sourceFile in $package.SelectNodes('sourcefile')) {
|
|
55
|
+
foreach ($line in $sourceFile.SelectNodes('line')) {
|
|
56
|
+
$key = '{0}|{1}|{2}' -f $package.GetAttribute('name'), $sourceFile.GetAttribute('name'), $line.GetAttribute('nr')
|
|
57
|
+
if ($index.ContainsKey($key)) {
|
|
58
|
+
throw "Coverage report $Position lists line '$key' more than once."
|
|
59
|
+
}
|
|
60
|
+
$index.Add($key, $line)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
, $index
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
$merged = ConvertFrom-PoshQCCoverageXml -CoverageXml $CoverageXml[0]
|
|
68
|
+
$mergedLines = & $indexLines $merged 1
|
|
69
|
+
for ($position = 1; $position -lt $CoverageXml.Count; $position++) {
|
|
70
|
+
$reportNumber = $position + 1
|
|
71
|
+
$otherLines = & $indexLines (ConvertFrom-PoshQCCoverageXml -CoverageXml $CoverageXml[$position]) $reportNumber
|
|
72
|
+
if ($otherLines.Count -ne $mergedLines.Count) {
|
|
73
|
+
throw "Coverage report $reportNumber lists $($otherLines.Count) lines; report 1 lists $($mergedLines.Count)."
|
|
74
|
+
}
|
|
75
|
+
foreach ($entry in $otherLines.GetEnumerator()) {
|
|
76
|
+
$line = $null
|
|
77
|
+
if (-not $mergedLines.TryGetValue($entry.Key, [ref] $line)) {
|
|
78
|
+
throw "Coverage report $reportNumber lists line '$($entry.Key)', which report 1 does not list."
|
|
79
|
+
}
|
|
80
|
+
$otherCovered = [long] $entry.Value.GetAttribute('ci')
|
|
81
|
+
$currentCovered = [long] $line.GetAttribute('ci')
|
|
82
|
+
if ($otherCovered -gt $currentCovered) {
|
|
83
|
+
$commandTotal = [long] $line.GetAttribute('mi') + $currentCovered
|
|
84
|
+
$line.SetAttribute('ci', [string] $otherCovered)
|
|
85
|
+
$line.SetAttribute('mi', [string] ($commandTotal - $otherCovered))
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# Issue #953 (CR-8): class and method elements, and counters other than LINE and INSTRUCTION, cannot be
|
|
91
|
+
# recomputed from merged line hits and would keep the figures of report 1, so the merged report omits them.
|
|
92
|
+
foreach ($staleNode in @($merged.SelectNodes("//class | //counter[@type!='LINE' and @type!='INSTRUCTION']"))) {
|
|
93
|
+
[void] $staleNode.ParentNode.RemoveChild($staleNode)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
$setCounter = {
|
|
97
|
+
param([System.Xml.XmlElement] $Node, [string] $Type, [long] $Covered, [long] $Missed)
|
|
98
|
+
$counter = $Node.SelectSingleNode("counter[@type='$Type']")
|
|
99
|
+
if ($counter) {
|
|
100
|
+
$counter.SetAttribute('missed', [string] $Missed)
|
|
101
|
+
$counter.SetAttribute('covered', [string] $Covered)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
$setTotals = {
|
|
105
|
+
param([System.Xml.XmlElement] $Node, [long[]] $Totals)
|
|
106
|
+
& $setCounter $Node 'INSTRUCTION' $Totals[2] $Totals[3]
|
|
107
|
+
& $setCounter $Node 'LINE' $Totals[0] $Totals[1]
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
# Totals layout: 0 = LINE covered, 1 = LINE missed, 2 = INSTRUCTION covered, 3 = INSTRUCTION missed.
|
|
111
|
+
$reportTotals = New-Object 'long[]' 4
|
|
112
|
+
foreach ($package in $merged.DocumentElement.SelectNodes('package')) {
|
|
113
|
+
$packageTotals = New-Object 'long[]' 4
|
|
114
|
+
foreach ($sourceFile in $package.SelectNodes('sourcefile')) {
|
|
115
|
+
$fileTotals = New-Object 'long[]' 4
|
|
116
|
+
foreach ($line in $sourceFile.SelectNodes('line')) {
|
|
117
|
+
$lineCovered = [long] $line.GetAttribute('ci')
|
|
118
|
+
if ($lineCovered -gt 0) { $fileTotals[0]++ } else { $fileTotals[1]++ }
|
|
119
|
+
$fileTotals[2] += $lineCovered
|
|
120
|
+
$fileTotals[3] += [long] $line.GetAttribute('mi')
|
|
121
|
+
}
|
|
122
|
+
& $setTotals $sourceFile $fileTotals
|
|
123
|
+
for ($slot = 0; $slot -lt 4; $slot++) { $packageTotals[$slot] += $fileTotals[$slot] }
|
|
124
|
+
}
|
|
125
|
+
& $setTotals $package $packageTotals
|
|
126
|
+
for ($slot = 0; $slot -lt 4; $slot++) { $reportTotals[$slot] += $packageTotals[$slot] }
|
|
127
|
+
}
|
|
128
|
+
& $setTotals $merged.DocumentElement $reportTotals
|
|
129
|
+
|
|
130
|
+
$merged.OuterXml
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
<#
|
|
134
|
+
.SYNOPSIS
|
|
135
|
+
Summarizes the report-level LINE counter of a coverage report (issue #953).
|
|
136
|
+
.DESCRIPTION
|
|
137
|
+
Returns covered, missed, total, and percent (rounded to two decimals; 0 when total is 0) from the
|
|
138
|
+
report-level LINE counter. Takes XML text only and enforces no threshold.
|
|
139
|
+
.PARAMETER CoverageXml
|
|
140
|
+
The XML text of one coverage report.
|
|
141
|
+
#>
|
|
142
|
+
function Get-PoshQCCoverageLineSummary {
|
|
143
|
+
[CmdletBinding()]
|
|
144
|
+
param(
|
|
145
|
+
[Parameter(Mandatory)][string] $CoverageXml
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
$document = ConvertFrom-PoshQCCoverageXml -CoverageXml $CoverageXml
|
|
149
|
+
$counter = $document.SelectSingleNode("/report/counter[@type='LINE']")
|
|
150
|
+
if (-not $counter) {
|
|
151
|
+
throw 'The coverage report has no report-level LINE counter.'
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
$covered = [long] $counter.GetAttribute('covered')
|
|
155
|
+
$missed = [long] $counter.GetAttribute('missed')
|
|
156
|
+
$total = $covered + $missed
|
|
157
|
+
[pscustomobject]@{
|
|
158
|
+
covered = $covered
|
|
159
|
+
missed = $missed
|
|
160
|
+
total = $total
|
|
161
|
+
percent = if ($total -gt 0) { [math]::Round(100.0 * $covered / $total, 2) } else { 0 }
|
|
162
|
+
}
|
|
163
|
+
}
|