@danmoisan/drm-copilot-mcp 1.1.18 → 1.1.19
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 +989 -662
- package/package.json +3 -2
- package/resources/claude-customizations/.claude/agents/atomic-executor.md +11 -0
- package/resources/claude-customizations/.claude/agents/atomic-planner.md +10 -0
- package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +12 -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 +5 -4
- package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +31 -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/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-resolution.ps1 +116 -59
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +55 -23
- 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 +493 -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 +66 -38
- 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 +64 -32
- 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 +31 -34
- 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/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 +47 -8
- 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 +5 -1
- 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/epic-orchestrate/SKILL.md +33 -6
- package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
- 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 +14 -6
- 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 +79 -16
- 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/pr-author/SKILL.md +3 -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 +2 -2
- package/resources/claude-customizations/pack-manifests/core.json +23 -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 +11 -0
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +4 -0
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +10 -0
- package/resources/codex-and-agents-customizations/.agents/skills/csharp/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 +8 -0
- 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/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 +15 -6
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/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/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 +11 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +11 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +11 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +11 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +11 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +11 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +10 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +10 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +10 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +10 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +10 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +10 -0
- package/resources/codex-and-agents-customizations/.codex/agents/epic-orchestrator.toml +2 -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 +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +2 -2
- 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.ps1 +37 -5
- 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 +493 -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/pack-manifests/core.json +9 -0
- package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +2 -2
- 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/skills/acceptance-criteria-tracking/SKILL.md +1 -1
- package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +4 -0
- package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
- package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +1 -1
- package/resources/powershell/PoshQC/PoshQC.Coverage.psm1 +314 -0
- package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +25 -28
- package/resources/powershell/PoshQC/PoshQC.psm1 +1 -0
- package/resources/powershell/PoshQC/README.md +8 -2
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +2 -319
|
@@ -170,8 +170,8 @@ If any gate fails, revert/fix immediately before proceeding.
|
|
|
170
170
|
Run the repo-standard C# toolchain in this order:
|
|
171
171
|
|
|
172
172
|
1) **Format**: `csharpier .`
|
|
173
|
-
2) **Analyze/Lint build**: `msbuild
|
|
174
|
-
3) **Type-safe nullable build**: `msbuild
|
|
173
|
+
2) **Analyze/Lint build**: `msbuild <solution>.sln /t:Build /p:Configuration=Debug /p:Platform='Any CPU' /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`
|
|
174
|
+
3) **Type-safe nullable build**: `msbuild <solution>.sln /t:Build /p:Configuration=Debug /p:Platform='Any CPU' /p:Nullable=enable /p:TreatWarningsAsErrors=true`
|
|
175
175
|
4) **Test with coverage**: `vstest.console.exe <test-assembly-paths> /EnableCodeCoverage`
|
|
176
176
|
|
|
177
177
|
If tools cannot run in the environment, STOP implementation and provide plan + proposed diffs marked **unverified**.
|
|
@@ -38,8 +38,8 @@ These are the required tools for C# code in this repo:
|
|
|
38
38
|
- Enforce analyzer diagnostics in build using `EnableNETAnalyzers` and `EnforceCodeStyleInBuild`.
|
|
39
39
|
- Prefer fixing diagnostics over suppressing them.
|
|
40
40
|
- Approved commands (Windows; choose the variant for your shell):
|
|
41
|
-
- **CMD / Developer Command Prompt:** `msbuild
|
|
42
|
-
- **PowerShell:** `msbuild
|
|
41
|
+
- **CMD / Developer Command Prompt:** `msbuild <solution>.sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`
|
|
42
|
+
- **PowerShell:** `msbuild <solution>.sln /t:Build /p:Configuration=Debug /p:Platform='Any CPU' /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`
|
|
43
43
|
|
|
44
44
|
3. **Type Checking — C# compiler + nullable analysis**
|
|
45
45
|
|
|
@@ -47,8 +47,8 @@ These are the required tools for C# code in this repo:
|
|
|
47
47
|
- Enable nullable reference types and fail builds on warnings for touched code paths.
|
|
48
48
|
- Avoid introducing nullable warnings; fix the root null-state issue instead.
|
|
49
49
|
- Approved commands (Windows; choose the variant for your shell):
|
|
50
|
-
- **CMD / Developer Command Prompt:** `msbuild
|
|
51
|
-
- **PowerShell:** `msbuild
|
|
50
|
+
- **CMD / Developer Command Prompt:** `msbuild <solution>.sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /p:Nullable=enable /p:TreatWarningsAsErrors=true`
|
|
51
|
+
- **PowerShell:** `msbuild <solution>.sln /t:Build /p:Configuration=Debug /p:Platform='Any CPU' /p:Nullable=enable /p:TreatWarningsAsErrors=true`
|
|
52
52
|
|
|
53
53
|
> **Testing tools and behavior are defined in the unit test policies.** Do not define test behavior here; instead, obey `general-unit-test.instructions.md` and `csharp-unit-test.instructions.md`.
|
|
54
54
|
|
|
@@ -43,8 +43,8 @@ If there is any conflict between these documents, halt and notify the user.
|
|
|
43
43
|
|
|
44
44
|
- For C# work, use these concrete commands for the general policy toolchain loop:
|
|
45
45
|
1. `csharpier .`
|
|
46
|
-
2. `msbuild
|
|
47
|
-
3. `msbuild
|
|
46
|
+
2. `msbuild <solution>.sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`
|
|
47
|
+
3. `msbuild <solution>.sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /p:Nullable=enable /p:TreatWarningsAsErrors=true`
|
|
48
48
|
4. `vstest.console.exe <test-assembly-paths> /EnableCodeCoverage`
|
|
49
49
|
|
|
50
50
|
- The loop behavior (restart rules, must-pass requirements, and audit expectations) is defined by `general-code-change.instructions.md` and is intentionally not repeated here.
|
|
@@ -86,7 +86,7 @@ During feature review (feature-audit phase):
|
|
|
86
86
|
|
|
87
87
|
### When Orchestrators Enforce AC Tracking
|
|
88
88
|
|
|
89
|
-
Orchestrators do not directly check off AC items.
|
|
89
|
+
Orchestrators do not directly check off AC items. The one exception is a CI-dependent criterion, which the item's own orchestrator run checks off at S9 as described in `### CI-Dependent Criteria` above. For every other criterion, orchestrators instead:
|
|
90
90
|
|
|
91
91
|
1. Ensure delegated executors and reviewers reference this skill.
|
|
92
92
|
2. After executor or reviewer completion, verify that AC source files reflect delivered work (spot-check; do not re-execute verification).
|
|
@@ -102,6 +102,10 @@ For short-path/minimal-audit plans, Phase 0 evidence is incomplete unless both a
|
|
|
102
102
|
|
|
103
103
|
`Output Summary:` is mandatory for each command-step artifact and must concisely summarize the essential result signal (for example: pass/fail status, key counts, coverage headline, or primary diagnostic).
|
|
104
104
|
|
|
105
|
+
## Evidence Filename Timestamps in Plans
|
|
106
|
+
|
|
107
|
+
Plans name evidence artifacts `<name>.<timestamp>.md` and never fix a plan-time stamp in an evidence filename. An acceptance condition identifies a consumed evidence artifact by the derivation in `evidence-and-timestamp-conventions` (`### Evidence Filename Timestamp`): the lexicographically greatest file matching `<name>.*.md`, not a pre-stamped literal path. This section governs artifact paths only: asserted search tokens remain concrete.
|
|
108
|
+
|
|
105
109
|
## Coverage Evidence Contract (Mandatory when policy requires coverage)
|
|
106
110
|
|
|
107
111
|
For any language in scope where repository policy requires coverage validation:
|
|
@@ -19,7 +19,16 @@ Use this skill when:
|
|
|
19
19
|
Use `yyyy-MM-ddTHH-mm` for all audit, remediation, and evidence artifacts.
|
|
20
20
|
Example: `2026-02-06T14-30`.
|
|
21
21
|
|
|
22
|
-
The `Timestamp` value is
|
|
22
|
+
The `Timestamp` value is UTC read from the host system clock when the recorded command runs (for example `Get-Date -AsUTC -Format yyyy-MM-ddTHH-mm` in PowerShell 7, `(Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH-mm')` in Windows PowerShell 5.1, or `date -u +%Y-%m-%dT%H-%M` in a POSIX shell); the agent never composes or estimates it. The value is written without a zone suffix.
|
|
23
|
+
|
|
24
|
+
### Evidence Filename Timestamp
|
|
25
|
+
|
|
26
|
+
The stamp in the filename of every artifact under `<FEATURE>/evidence/<kind>/` is the UTC time at which that file is written, read from the host system clock immediately before the write, in the same `yyyy-MM-ddTHH-mm` shape with no zone suffix. It is never a plan-time stamp, a plan-cycle identifier, or any other value fixed before the write.
|
|
27
|
+
|
|
28
|
+
- Plans name such an artifact `<name>.<timestamp>.md`, and the executor substitutes the write time. A later loop pass writes a new file with a new stamp.
|
|
29
|
+
- A task that consumes an evidence artifact selects the lexicographically greatest file matching `<name>.*.md`. The fixed-width UTC shape sorts in chronological order, so that file is the most recently written one.
|
|
30
|
+
- Plan files (`plan.<timestamp>.md`, stamped by the feature-folder tooling in America/New_York) and the review artifact set (`policy-audit`, `code-review`, `feature-audit`, `remediation-inputs`, which share one stamp) are outside this rule.
|
|
31
|
+
- Existing evidence files whose names carry a plan-time stamp are not renamed.
|
|
23
32
|
|
|
24
33
|
## Canonical Evidence Locations
|
|
25
34
|
|
|
@@ -123,7 +123,7 @@ Always apply:
|
|
|
123
123
|
- toolchain checks fail
|
|
124
124
|
- the code review contains blockers
|
|
125
125
|
- required acceptance criteria are FAIL or PARTIAL
|
|
126
|
-
- coverage
|
|
126
|
+
- coverage below the governing thresholds (the root `CLAUDE.md` thresholds when stated, otherwise the defaults: line 85%, branch 75% for branch-capable languages; repo-wide per language, modified files including any regression on changed lines, or new files)
|
|
127
127
|
- Create `remediation-inputs.<timestamp>.md` first.
|
|
128
128
|
- Create the target remediation plan file from the canonical plan template.
|
|
129
129
|
- Hand off plan creation through `remediation-handoff-atomic-planner`.
|
|
@@ -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
|
+
}
|
|
@@ -147,6 +147,9 @@ Custom path for Koverage-compatible coverage XML output.
|
|
|
147
147
|
Skip creation of Koverage-friendly coverage copy.
|
|
148
148
|
.PARAMETER ScanFolders
|
|
149
149
|
Optional workspace-relative or workspace-contained folders to scan instead of the entire root.
|
|
150
|
+
.PARAMETER ResolveCoveragePopulation
|
|
151
|
+
Injectable seam that derives the code-coverage file population from the workspace (issue #527).
|
|
152
|
+
Defaults to Resolve-PoshQCCoveragePopulation.
|
|
150
153
|
#>
|
|
151
154
|
function Invoke-PoshQCTest {
|
|
152
155
|
[CmdletBinding()]
|
|
@@ -282,6 +285,10 @@ function Invoke-PoshQCTest {
|
|
|
282
285
|
[scriptblock] $CopyCoverage = {
|
|
283
286
|
param([string] $CoveragePath, [string] $RepoRoot, [string] $KoveragePath)
|
|
284
287
|
Convert-PoshQCCoverageToRelative -InputPath $CoveragePath -OutputPath $KoveragePath -RepoRoot $RepoRoot
|
|
288
|
+
},
|
|
289
|
+
[scriptblock] $ResolveCoveragePopulation = {
|
|
290
|
+
param([string] $RootPath, $Settings, [string] $SettingsFile, [string[]] $ScanFolderRoots, [string[]] $Excluded, [scriptblock] $PathExists, [scriptblock] $Log)
|
|
291
|
+
Resolve-PoshQCCoveragePopulation -Root $RootPath -Settings $Settings -SettingsFile $SettingsFile -ScanFolderRoots $ScanFolderRoots -ExcludeDirs $Excluded -SettingsPathExists $PathExists -Logger $Log
|
|
285
292
|
}
|
|
286
293
|
)
|
|
287
294
|
|
|
@@ -290,6 +297,10 @@ function Invoke-PoshQCTest {
|
|
|
290
297
|
if (-not $Root) {
|
|
291
298
|
$Root = $PWD.ProviderPath
|
|
292
299
|
}
|
|
300
|
+
# Resolve a relative -Root once so run, coverage, and output paths are absolute (issue #527).
|
|
301
|
+
if (-not [IO.Path]::IsPathRooted($Root)) {
|
|
302
|
+
$Root = [IO.Path]::GetFullPath([IO.Path]::Combine($PWD.ProviderPath, $Root))
|
|
303
|
+
}
|
|
293
304
|
|
|
294
305
|
& $EnsureModule 'Pester' "Pester is not installed. Run Install-PoshQCTool (alias Install-PoshQCTools) first."
|
|
295
306
|
|
|
@@ -336,34 +347,20 @@ function Invoke-PoshQCTest {
|
|
|
336
347
|
}
|
|
337
348
|
|
|
338
349
|
if ($coverageEnabled -and $config.CodeCoverage) {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
#
|
|
349
|
-
#
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
$
|
|
353
|
-
foreach ($prunedPath in @($resolvedCoveragePaths | Where-Object { $survivingCoveragePaths -notcontains $_ })) {
|
|
354
|
-
# Log every prune individually so coverage removal is never silent.
|
|
355
|
-
& $Logger "Pruned nonexistent code coverage path: $prunedPath"
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
if ($survivingCoveragePaths.Count -gt 0) {
|
|
359
|
-
$config.CodeCoverage.Path = $survivingCoveragePaths
|
|
360
|
-
} else {
|
|
361
|
-
# An enabled-but-empty path set makes Pester instrument every Run.Path directory,
|
|
362
|
-
# so disable coverage for this invocation instead and continue with the test run.
|
|
363
|
-
$config.CodeCoverage.Enabled = $false
|
|
364
|
-
$coverageEnabled = $false
|
|
365
|
-
& $Logger "Code coverage disabled for this invocation: no configured coverage path exists under root '$Root'."
|
|
366
|
-
}
|
|
350
|
+
# The coverage population is derived from the workspace, not from a list shipped with the
|
|
351
|
+
# module, so the measured set does not depend on which module copy was loaded (issue #527).
|
|
352
|
+
# Nonexistent caller-supplied entries are pruned and logged by the resolver (issue #409).
|
|
353
|
+
$population = & $ResolveCoveragePopulation $Root $settings $SettingsPath $effectiveScanFolders $ExcludeDirs $TestPathExists $Logger
|
|
354
|
+
$populationPaths = @($population.Paths | Where-Object { $_ })
|
|
355
|
+
& $Logger "Code coverage population: source=$($population.Source); files=$($populationPaths.Count)"
|
|
356
|
+
if ($populationPaths.Count -gt 0) {
|
|
357
|
+
$config.CodeCoverage.Path = $populationPaths
|
|
358
|
+
} else {
|
|
359
|
+
# An enabled-but-empty path set makes Pester instrument every Run.Path directory,
|
|
360
|
+
# so disable coverage for this invocation instead and continue with the test run.
|
|
361
|
+
$config.CodeCoverage.Enabled = $false
|
|
362
|
+
$coverageEnabled = $false
|
|
363
|
+
& $Logger "Code coverage disabled for this invocation: no configured coverage path exists under root '$Root'."
|
|
367
364
|
}
|
|
368
365
|
|
|
369
366
|
if ($config.CodeCoverage.OutputPath.Value) {
|
|
@@ -65,8 +65,14 @@ PoshQC is a lightweight PowerShell quality gate that wraps Invoke-Formatter, PSS
|
|
|
65
65
|
- PSScriptAnalyzer settings: `./settings/pssa.settings.psd1`
|
|
66
66
|
- Enforces compatible syntax for 5.1 and 7.6, 4-space indentation, ShouldProcess for state-changing functions, and safety guards (no Invoke-Expression, no global vars, etc.).
|
|
67
67
|
- Pester settings: `./settings/pester.runsettings.psd1`
|
|
68
|
-
- Runs tests under `scripts` and `tests/powershell`, outputs JUnit XML and CoverageGutters coverage, and enables coverage
|
|
68
|
+
- Runs tests under `scripts` and `tests/powershell`, outputs JUnit XML and CoverageGutters coverage, and enables coverage. The shipped runsettings carry no `CodeCoverage.Path` list; `Invoke-PoshQCTest` derives the coverage population from the workspace (see Coverage population below).
|
|
69
69
|
- When `-ScanFolders` is supplied to `Invoke-PoshQCSuite` or the lower-level commands, the suite narrows discovery to those workspace-relative or workspace-contained folders.
|
|
70
|
+
- Coverage population (issue #527): `Invoke-PoshQCTest` measures the same file set for the same workspace whichever module copy is loaded.
|
|
71
|
+
- Workspace configuration: `config/poshqc-coverage.json` with the schema `{"version": 1, "roots": [...]}`. Each root is a workspace-relative folder. The file is validated fail-fast, and every error names `config/poshqc-coverage.json`: empty content, invalid JSON, a `version` other than 1, a missing or non-array `roots`, and a blank, absolute, or `..`-segment entry are rejected. An empty `roots` array yields an empty population.
|
|
72
|
+
- Precedence, reported as the source name: (1) `settings` - a caller-supplied settings file (`-SettingsPath` other than the module's own `settings/pester.runsettings.psd1`) with a non-empty `CodeCoverage.Path` is honored, and each entry that does not exist is pruned and logged; a `CodeCoverage.Path` list in the module's own settings file is ignored and the ignore is logged. (2) `config` - `config/poshqc-coverage.json` exists. (3) `fallback` - the effective test scan folders: explicit `-ScanFolders`, else `config/poshqc-scan.json`, else the settings `Run.Path`.
|
|
73
|
+
- Built-in exclusions: `.ps1` and `.psm1` files only; files named `*.Tests.ps1` are excluded, as are files whose first root-relative segment is `tests` and files under any root-relative directory named in `DefaultExcludedDirs` (for example `node_modules`, `artifacts`, `.venv`). A nonexistent root is skipped with a warning.
|
|
74
|
+
- An empty population disables coverage for that invocation, logs `Code coverage disabled for this invocation`, and still runs the tests.
|
|
75
|
+
- Each run logs `Code coverage population: source=<source>; files=<count>` before Pester starts.
|
|
70
76
|
|
|
71
77
|
## Typical workflow
|
|
72
78
|
|
|
@@ -77,4 +83,4 @@ PoshQC is a lightweight PowerShell quality gate that wraps Invoke-Formatter, PSS
|
|
|
77
83
|
## Notes for standalone use
|
|
78
84
|
|
|
79
85
|
- Place `PoshQC.psm1`, `PoshQC.psd1`, and the `settings/` folder together; import the module from that directory.
|
|
80
|
-
- Adjust `settings/pester.runsettings.psd1` and `settings/pssa.settings.psd1` to match your repo paths and policies.
|
|
86
|
+
- Adjust `settings/pester.runsettings.psd1` and `settings/pssa.settings.psd1` to match your repo paths and policies. Declare coverage roots in `config/poshqc-coverage.json` at the workspace root rather than in the runsettings.
|