@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.
Files changed (264) hide show
  1. package/out/mcp-server.js +1149 -661
  2. package/package.json +3 -2
  3. package/resources/claude-customizations/.claude/agents/atomic-executor.md +28 -6
  4. package/resources/claude-customizations/.claude/agents/atomic-planner.md +20 -2
  5. package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +17 -4
  6. package/resources/claude-customizations/.claude/agents/epic-planner.md +5 -4
  7. package/resources/claude-customizations/.claude/agents/feature-review.md +6 -4
  8. package/resources/claude-customizations/.claude/agents/orchestrator.md +9 -5
  9. package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +35 -13
  10. package/resources/claude-customizations/.claude/agents/parallel-planner.md +9 -6
  11. package/resources/claude-customizations/.claude/agents/pr-author.md +15 -2
  12. package/resources/claude-customizations/.claude/agents/prd-feature.md +1 -1
  13. package/resources/claude-customizations/.claude/agents/task-researcher.md +8 -0
  14. package/resources/claude-customizations/.claude/hooks/check-powershell-test-purity.ps1 +7 -1
  15. package/resources/claude-customizations/.claude/hooks/check-python-test-purity.ps1 +7 -1
  16. package/resources/claude-customizations/.claude/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
  17. package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +111 -46
  18. package/resources/claude-customizations/.claude/hooks/enforce-completion-helpers.ps1 +105 -0
  19. package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +8 -1
  20. package/resources/claude-customizations/.claude/hooks/enforce-epic-invocation-origin.ps1 +7 -1
  21. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-command.ps1 +190 -0
  22. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-resolution.ps1 +116 -59
  23. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +78 -77
  24. package/resources/claude-customizations/.claude/hooks/enforce-epic-wave-barrier.ps1 +74 -75
  25. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate-resolution.ps1 +127 -35
  26. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +61 -57
  27. package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +7 -1
  28. package/resources/claude-customizations/.claude/hooks/enforce-feature-folder-order.ps1 +129 -39
  29. package/resources/claude-customizations/.claude/hooks/enforce-mermaid-validation.ps1 +7 -1
  30. package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +9 -3
  31. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +39 -51
  32. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
  33. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
  34. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
  35. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +17 -12
  36. package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +9 -3
  37. package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier.ps1 +65 -50
  38. package/resources/claude-customizations/.claude/hooks/enforce-parallel-drift-gate.ps1 +49 -42
  39. package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +54 -72
  40. package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +8 -2
  41. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-command-allowlist.ps1 +279 -0
  42. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +129 -75
  43. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.artifact-root.ps1 +233 -0
  44. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.docs-only.ps1 +224 -0
  45. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +69 -24
  46. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner-helpers.ps1 +29 -18
  47. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +10 -4
  48. package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +14 -5
  49. package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +8 -2
  50. package/resources/claude-customizations/.claude/hooks/feature-folder-resolution.ps1 +373 -0
  51. package/resources/claude-customizations/.claude/hooks/feature-review-coverage-thresholds.ps1 +85 -0
  52. package/resources/claude-customizations/.claude/hooks/hook-command-heredoc.ps1 +119 -0
  53. package/resources/claude-customizations/.claude/hooks/hook-command-invocation-operands.ps1 +198 -0
  54. package/resources/claude-customizations/.claude/hooks/hook-command-invocation.ps1 +254 -255
  55. package/resources/claude-customizations/.claude/hooks/hook-command-payload-powershell.ps1 +201 -0
  56. package/resources/claude-customizations/.claude/hooks/hook-command-payload.ps1 +484 -0
  57. package/resources/claude-customizations/.claude/hooks/hook-command-scanner.ps1 +27 -110
  58. package/resources/claude-customizations/.claude/hooks/hook-dependency-guard.ps1 +114 -0
  59. package/resources/claude-customizations/.claude/hooks/hook-docs-only-diff.ps1 +497 -0
  60. package/resources/claude-customizations/.claude/hooks/premortem-section.ps1 +149 -0
  61. package/resources/claude-customizations/.claude/hooks/subagentstop-envelope.ps1 +192 -0
  62. package/resources/claude-customizations/.claude/hooks/validate-bash.ps1 +9 -3
  63. package/resources/claude-customizations/.claude/hooks/validate-discovery-artifact-gate.ps1 +40 -42
  64. package/resources/claude-customizations/.claude/hooks/validate-executor-output.ps1 +267 -113
  65. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-metrics.ps1 +180 -0
  66. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-rules.ps1 +195 -0
  67. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage.ps1 +141 -395
  68. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-resolution.ps1 +311 -0
  69. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-routing.ps1 +170 -0
  70. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +115 -190
  71. package/resources/claude-customizations/.claude/hooks/validate-planner-output.ps1 +77 -33
  72. package/resources/claude-customizations/.claude/hooks/validate-pr-author-output.ps1 +39 -44
  73. package/resources/claude-customizations/.claude/hooks/validate-prd-feature-output.ps1 +43 -14
  74. package/resources/claude-customizations/.claude/hooks/validate-required-artifact-output.ps1 +66 -36
  75. package/resources/claude-customizations/.claude/hooks/validate-task-researcher-output.ps1 +52 -36
  76. package/resources/claude-customizations/.claude/lib/bash/mutate-parallel-run.sh +191 -0
  77. package/resources/claude-customizations/.claude/lib/bash/parallel-cohorts.sh +13 -3
  78. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-cli.sh +152 -0
  79. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-ops.sh +193 -0
  80. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation.sh +322 -0
  81. package/resources/claude-customizations/.claude/lib/bash/remove-parallel-item.sh +146 -0
  82. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +23 -33
  83. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusTokenShape.psm1 +86 -6
  84. package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-CiGateParser.ps1 +91 -19
  85. package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-MergeConfirmationParser.ps1 +203 -0
  86. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateEpicWaveBarrier.psm1 +346 -0
  87. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1 +24 -3
  88. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +79 -2
  89. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1 +3 -3
  90. package/resources/claude-customizations/.claude/rules/architecture-boundaries.md +5 -5
  91. package/resources/claude-customizations/.claude/rules/csharp.md +2 -2
  92. package/resources/claude-customizations/.claude/rules/general-code-change.md +1 -1
  93. package/resources/claude-customizations/.claude/rules/general-unit-test.md +3 -2
  94. package/resources/claude-customizations/.claude/rules/orchestrator-state.md +37 -4
  95. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +50 -5
  96. package/resources/claude-customizations/.claude/rules/plan-acceptance-gates.md +85 -10
  97. package/resources/claude-customizations/.claude/rules/quality-tiers.md +11 -7
  98. package/resources/claude-customizations/.claude/rules/shell.md +10 -8
  99. package/resources/claude-customizations/.claude/rules/typescript.md +1 -1
  100. package/resources/claude-customizations/.claude/settings.json +3 -4
  101. package/resources/claude-customizations/.claude/skills/acceptance-criteria-tracking/SKILL.md +1 -1
  102. package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +52 -16
  103. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +7 -3
  104. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_detached_lib.sh +1 -1
  105. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_enumerate_lib.sh +2 -0
  106. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_scan_helper.sh +2 -0
  107. package/resources/claude-customizations/.claude/skills/csharp-change-budget-router/SKILL.md +2 -2
  108. package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +45 -9
  109. package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +5 -3
  110. package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
  111. package/resources/claude-customizations/.claude/skills/execution-environment/SKILL.md +134 -0
  112. package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +1 -1
  113. package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +4 -3
  114. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +36 -15
  115. package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +52 -14
  116. package/resources/claude-customizations/.claude/skills/parallel-close/SKILL.md +22 -9
  117. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +108 -21
  118. package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +63 -7
  119. package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +35 -12
  120. package/resources/claude-customizations/.claude/skills/powershell-change-budget-router/SKILL.md +1 -1
  121. package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +3 -1
  122. package/resources/claude-customizations/.claude/skills/python-change-budget-router/SKILL.md +1 -1
  123. package/resources/claude-customizations/.claude/skills/quota-throttling/SKILL.md +1 -1
  124. package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +16 -8
  125. package/resources/claude-customizations/.claude/skills/research-issue/SKILL.md +6 -0
  126. package/resources/claude-customizations/pack-manifests/core.json +27 -0
  127. package/resources/codex-and-agents-customizations/.agents/skills/README.md +1 -0
  128. package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +1 -1
  129. package/resources/codex-and-agents-customizations/.agents/skills/architecture-boundaries/SKILL.md +5 -5
  130. package/resources/codex-and-agents-customizations/.agents/skills/atomic-executor/SKILL.md +19 -7
  131. package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +43 -4
  132. package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +18 -6
  133. package/resources/codex-and-agents-customizations/.agents/skills/csharp/SKILL.md +2 -2
  134. package/resources/codex-and-agents-customizations/.agents/skills/csharp-change-budget-router/SKILL.md +2 -2
  135. package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +2 -2
  136. package/resources/codex-and-agents-customizations/.agents/skills/epic-orchestrate/SKILL.md +16 -1
  137. package/resources/codex-and-agents-customizations/.agents/skills/epic-plan/SKILL.md +17 -1
  138. package/resources/codex-and-agents-customizations/.agents/skills/epic-run/SKILL.md +2 -1
  139. package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
  140. package/resources/codex-and-agents-customizations/.agents/skills/execution-environment/SKILL.md +134 -0
  141. package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +13 -5
  142. package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +1 -1
  143. package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +3 -2
  144. package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +27 -8
  145. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +1 -1
  146. package/resources/codex-and-agents-customizations/.agents/skills/powershell-change-budget-router/SKILL.md +1 -1
  147. package/resources/codex-and-agents-customizations/.agents/skills/python-change-budget-router/SKILL.md +1 -1
  148. package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +11 -7
  149. package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +7 -3
  150. package/resources/codex-and-agents-customizations/.agents/skills/research-issue/SKILL.md +6 -0
  151. package/resources/codex-and-agents-customizations/.agents/skills/translate-claude-to-codex/SKILL.md +1 -1
  152. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp/SKILL.md +2 -2
  153. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +2 -2
  154. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c1.toml +21 -6
  155. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +21 -6
  156. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +21 -6
  157. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +21 -6
  158. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +21 -6
  159. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +21 -6
  160. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +15 -2
  161. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +15 -2
  162. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +15 -2
  163. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +15 -2
  164. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +15 -2
  165. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +15 -2
  166. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planning.toml +1 -1
  167. package/resources/codex-and-agents-customizations/.codex/agents/csharp-atomic-planning.toml +1 -1
  168. package/resources/codex-and-agents-customizations/.codex/agents/epic-orchestrator.toml +4 -1
  169. package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +6 -6
  170. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c1.toml +7 -4
  171. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +7 -4
  172. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +7 -4
  173. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +7 -4
  174. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +7 -4
  175. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +7 -4
  176. package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-planning.toml +1 -1
  177. package/resources/codex-and-agents-customizations/.codex/agents/python-atomic-planning.toml +1 -1
  178. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c1.toml +6 -0
  179. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c2.toml +6 -0
  180. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c3-elevated.toml +6 -0
  181. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c3.toml +6 -0
  182. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c4.toml +6 -0
  183. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher.toml +6 -0
  184. package/resources/codex-and-agents-customizations/.codex/codex-web-setup.sh +318 -273
  185. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  186. package/resources/codex-and-agents-customizations/.codex/hooks/check-powershell-test-purity.ps1 +7 -1
  187. package/resources/codex-and-agents-customizations/.codex/hooks/check-python-test-purity.ps1 +7 -1
  188. package/resources/codex-and-agents-customizations/.codex/hooks/codex-epic-child-launch-attestation.ps1 +1 -1
  189. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
  190. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-codex-model-routing.ps1 +8 -2
  191. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +97 -44
  192. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-helpers.ps1 +105 -0
  193. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-child-worktree-binding.ps1 +7 -1
  194. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate-command.ps1 +190 -0
  195. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +54 -43
  196. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +6 -0
  197. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-root-invocation.ps1 +7 -1
  198. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-wave-barrier.ps1 +7 -1
  199. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-worktree-removal-gate.ps1 +54 -41
  200. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-evidence-locations.ps1 +7 -1
  201. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +35 -14
  202. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
  203. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
  204. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
  205. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +24 -11
  206. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +8 -2
  207. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +13 -4
  208. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +8 -2
  209. package/resources/codex-and-agents-customizations/.codex/hooks/feature-folder-resolution.ps1 +373 -0
  210. package/resources/codex-and-agents-customizations/.codex/hooks/feature-review-coverage-thresholds.ps1 +85 -0
  211. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-heredoc.ps1 +119 -0
  212. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation-operands.ps1 +198 -0
  213. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation.ps1 +254 -255
  214. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload-powershell.ps1 +201 -0
  215. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload.ps1 +484 -0
  216. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-scanner.ps1 +27 -110
  217. package/resources/codex-and-agents-customizations/.codex/hooks/hook-dependency-guard.ps1 +114 -0
  218. package/resources/codex-and-agents-customizations/.codex/hooks/hook-docs-only-diff.ps1 +497 -0
  219. package/resources/codex-and-agents-customizations/.codex/hooks/validate-bash.ps1 +8 -2
  220. package/resources/codex-and-agents-customizations/.codex/hooks/validate-codex-subagent-routing.ps1 +7 -1
  221. package/resources/codex-and-agents-customizations/.codex/hooks/validate-feature-review-coverage.ps1 +24 -8
  222. package/resources/codex-and-agents-customizations/.codex/prompts/feature-review-remediate.md +6 -4
  223. package/resources/codex-and-agents-customizations/pack-manifests/core.json +11 -0
  224. package/resources/customizations/.github/agents/atomic_executor.agent.md +21 -11
  225. package/resources/customizations/.github/agents/atomic_planning.agent.md +8 -3
  226. package/resources/customizations/.github/agents/csharp-atomic-executor.agent.md +13 -9
  227. package/resources/customizations/.github/agents/csharp-atomic-planning.agent.md +2 -2
  228. package/resources/customizations/.github/agents/csharp-orchestrator.agent.md +2 -2
  229. package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +5 -5
  230. package/resources/customizations/.github/agents/orchestrator.agent.md +8 -5
  231. package/resources/customizations/.github/agents/powershell-atomic-executor.agent.md +13 -9
  232. package/resources/customizations/.github/agents/powershell-atomic-planning.agent.md +2 -2
  233. package/resources/customizations/.github/agents/powershell-orchestrator.agent.md +2 -2
  234. package/resources/customizations/.github/agents/powershell-typed-engineer.agent.md +3 -3
  235. package/resources/customizations/.github/agents/python-atomic-executor.agent.md +13 -9
  236. package/resources/customizations/.github/agents/python-atomic-planning.agent.md +2 -2
  237. package/resources/customizations/.github/agents/python-orchestrator.agent.md +2 -2
  238. package/resources/customizations/.github/agents/task-researcher.agent.md +6 -0
  239. package/resources/customizations/.github/instructions/csharp-code-change.instructions.md +4 -4
  240. package/resources/customizations/.github/instructions/csharp-unit-test.instructions.md +2 -2
  241. package/resources/customizations/.github/prompts/generate-atomic-plan.prompt.md +16 -10
  242. package/resources/customizations/.github/prompts/orchestrate-csharp-work.prompt.md +2 -2
  243. package/resources/customizations/.github/prompts/orchestrate-powershell-work.prompt.md +1 -1
  244. package/resources/customizations/.github/prompts/orchestrate-python-work.prompt.md +1 -1
  245. package/resources/customizations/.github/prompts/orchestrate-work.prompt.md +2 -2
  246. package/resources/customizations/.github/skills/acceptance-criteria-tracking/SKILL.md +1 -1
  247. package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +43 -4
  248. package/resources/customizations/.github/skills/csharp-change-budget-router/SKILL.md +1 -1
  249. package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
  250. package/resources/customizations/.github/skills/execution-environment/SKILL.md +134 -0
  251. package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +1 -1
  252. package/resources/customizations/.github/skills/powershell-change-budget-router/SKILL.md +1 -1
  253. package/resources/customizations/.github/skills/remediation-handoff-atomic-planner/SKILL.md +7 -3
  254. package/resources/feature-templates/bug/plan.yyyy-MM-ddTHH-mm.md +10 -0
  255. package/resources/feature-templates/feature/plan.yyyy-MM-ddTHH-mm.md +11 -0
  256. package/resources/feature-templates/refactor/plan.yyyy-MM-ddTHH-mm.md +11 -0
  257. package/resources/powershell/PoshQC/PoshQC.Coverage.psm1 +314 -0
  258. package/resources/powershell/PoshQC/PoshQC.CoverageMerge.psm1 +163 -0
  259. package/resources/powershell/PoshQC/PoshQC.Shard.psm1 +296 -0
  260. package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +48 -32
  261. package/resources/powershell/PoshQC/PoshQC.psd1 +7 -1
  262. package/resources/powershell/PoshQC/PoshQC.psm1 +18 -5
  263. package/resources/powershell/PoshQC/README.md +21 -3
  264. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +5 -319
@@ -0,0 +1,296 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Selects the test files assigned to one shard (issue #953).
4
+ .DESCRIPTION
5
+ Assigns the files in longest-processing-time order (issue #953, CR-10): heaviest file first, equal weights
6
+ in ordinal path order, each file to the shard with the least assigned weight, then the fewest files, then
7
+ the lowest index, so the shard loads differ by at most the weight of one file. When Weight is omitted every
8
+ file weighs 1, which assigns the files round-robin in ordinal path order. Returns this shard's files in
9
+ input order. Throws when ShardCount is below 1, ShardIndex is outside 1..ShardCount, Weight and TestFile
10
+ differ in length, or a weight is negative (CR-18).
11
+ .PARAMETER TestFile
12
+ Ordered list of test file paths to distribute.
13
+ .PARAMETER ShardIndex
14
+ One-based index of the shard to select.
15
+ .PARAMETER ShardCount
16
+ Total number of shards.
17
+ .PARAMETER Weight
18
+ Optional non-negative weight of each test file (for example its size in bytes), parallel to TestFile.
19
+ #>
20
+ function Get-PoshQCShardSlice {
21
+ [CmdletBinding()]
22
+ [OutputType([string])]
23
+ param(
24
+ [Parameter(Mandatory)][AllowEmptyCollection()][string[]] $TestFile,
25
+ [Parameter(Mandatory)][int] $ShardIndex,
26
+ [Parameter(Mandatory)][int] $ShardCount,
27
+ [long[]] $Weight
28
+ )
29
+
30
+ if ($ShardCount -lt 1) {
31
+ throw "ShardCount must be at least 1; received $ShardCount."
32
+ }
33
+ if ($ShardIndex -lt 1 -or $ShardIndex -gt $ShardCount) {
34
+ throw "ShardIndex must be between 1 and $ShardCount; received $ShardIndex."
35
+ }
36
+ if ($null -eq $Weight) {
37
+ $Weight = [long[]] (@(1) * $TestFile.Count)
38
+ }
39
+ if ($Weight.Count -ne $TestFile.Count) {
40
+ throw "Weight lists $($Weight.Count) values; TestFile lists $($TestFile.Count) files."
41
+ }
42
+ for ($position = 0; $position -lt $Weight.Count; $position++) {
43
+ if ($Weight[$position] -lt 0) {
44
+ throw "Weight must not be negative; '$($TestFile[$position])' has weight $($Weight[$position])."
45
+ }
46
+ }
47
+
48
+ $order = [int[]] @(for ($position = 0; $position -lt $TestFile.Count; $position++) { $position })
49
+ $heaviestFirst = [System.Comparison[int]] {
50
+ param([int] $Left, [int] $Right)
51
+ $byWeight = $Weight[$Right].CompareTo($Weight[$Left])
52
+ if ($byWeight -ne 0) { return $byWeight }
53
+ [string]::CompareOrdinal($TestFile[$Left], $TestFile[$Right])
54
+ }
55
+ [System.Array]::Sort($order, $heaviestFirst)
56
+
57
+ $load = New-Object 'long[]' $ShardCount
58
+ $fileCount = New-Object 'int[]' $ShardCount
59
+ $selected = New-Object 'System.Collections.Generic.HashSet[int]'
60
+ foreach ($position in $order) {
61
+ $target = 0
62
+ for ($shard = 1; $shard -lt $ShardCount; $shard++) {
63
+ if ($load[$shard] -lt $load[$target] -or ($load[$shard] -eq $load[$target] -and $fileCount[$shard] -lt $fileCount[$target])) {
64
+ $target = $shard
65
+ }
66
+ }
67
+ $load[$target] += $Weight[$position]
68
+ $fileCount[$target]++
69
+ if ($target -eq $ShardIndex - 1) {
70
+ [void] $selected.Add($position)
71
+ }
72
+ }
73
+
74
+ for ($position = 0; $position -lt $TestFile.Count; $position++) {
75
+ if ($selected.Contains($position)) {
76
+ $TestFile[$position]
77
+ }
78
+ }
79
+ }
80
+
81
+ <#
82
+ .SYNOPSIS
83
+ Builds the shard manifest for one shard (issue #953).
84
+ .DESCRIPTION
85
+ Returns an in-memory object with shardIndex, shardCount, all (root-relative forward-slash paths
86
+ of every input file), selected (root-relative paths of this shard's slice), and selectedFullPath
87
+ (the slice as given). Relative paths are computed by case-insensitive removal of the root prefix;
88
+ a path outside the root keeps its forward-slash form.
89
+ .PARAMETER RootPath
90
+ Workspace root that the relative paths are computed against.
91
+ .PARAMETER TestFile
92
+ Ordered list of test file paths to distribute.
93
+ .PARAMETER ShardIndex
94
+ One-based index of the shard.
95
+ .PARAMETER ShardCount
96
+ Total number of shards.
97
+ .PARAMETER Weight
98
+ Optional weight of each test file, passed to Get-PoshQCShardSlice.
99
+ #>
100
+ function New-PoshQCShardManifest {
101
+ [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Pure in-memory factory that changes no system state.')]
102
+ [CmdletBinding()]
103
+ param(
104
+ [Parameter(Mandatory)][string] $RootPath,
105
+ [Parameter(Mandatory)][AllowEmptyCollection()][string[]] $TestFile,
106
+ [Parameter(Mandatory)][int] $ShardIndex,
107
+ [Parameter(Mandatory)][int] $ShardCount,
108
+ [long[]] $Weight
109
+ )
110
+
111
+ $sliceArguments = @{ TestFile = $TestFile; ShardIndex = $ShardIndex; ShardCount = $ShardCount }
112
+ if ($PSBoundParameters.ContainsKey('Weight')) {
113
+ $sliceArguments['Weight'] = $Weight
114
+ }
115
+ $selected = @(Get-PoshQCShardSlice @sliceArguments)
116
+ $prefix = ($RootPath -replace '\\', '/').TrimEnd('/') + '/'
117
+ $toRelative = {
118
+ param([string] $Path)
119
+ $normalized = $Path -replace '\\', '/'
120
+ if ($normalized.StartsWith($prefix, [System.StringComparison]::OrdinalIgnoreCase)) {
121
+ $normalized.Substring($prefix.Length)
122
+ } else {
123
+ $normalized
124
+ }
125
+ }
126
+
127
+ [pscustomobject]@{
128
+ shardIndex = $ShardIndex
129
+ shardCount = $ShardCount
130
+ all = @($TestFile | ForEach-Object { & $toRelative $_ })
131
+ selected = @($selected | ForEach-Object { & $toRelative $_ })
132
+ selectedFullPath = $selected
133
+ }
134
+ }
135
+
136
+ <#
137
+ .SYNOPSIS
138
+ Verifies that a set of shard manifests covers every test file exactly once (issue #953).
139
+ .DESCRIPTION
140
+ Throws unless the shard indices are exactly 1..N once each, every manifest declares shardCount N,
141
+ every 'all' list is identical, the 'selected' lists are pairwise disjoint, and their union sorted
142
+ ordinally equals 'all' sorted ordinally. Returns $true otherwise.
143
+ .PARAMETER ShardManifest
144
+ The manifests produced by New-PoshQCShardManifest, one per shard (for example loaded from JSON).
145
+ #>
146
+ function Test-PoshQCShardCompleteness {
147
+ [CmdletBinding()]
148
+ [OutputType([bool])]
149
+ param(
150
+ [Parameter(Mandatory)][object[]] $ShardManifest
151
+ )
152
+
153
+ $count = $ShardManifest.Count
154
+ $indices = @($ShardManifest | ForEach-Object { [int] $_.shardIndex } | Sort-Object)
155
+ if (($indices -join ',') -ne ((1..$count) -join ',')) {
156
+ throw "Shard indices '$($indices -join ',')' are not exactly 1..$count."
157
+ }
158
+
159
+ $reference = @($ShardManifest[0].all | ForEach-Object { [string] $_ })
160
+ $seen = New-Object 'System.Collections.Generic.HashSet[string]' ([System.StringComparer]::Ordinal)
161
+ foreach ($manifest in $ShardManifest) {
162
+ if ([int] $manifest.shardCount -ne $count) {
163
+ throw "Shard $($manifest.shardIndex) declares shardCount $($manifest.shardCount); $count manifests were supplied."
164
+ }
165
+ $all = @($manifest.all | ForEach-Object { [string] $_ })
166
+ if (($all -join "`n") -cne ($reference -join "`n")) {
167
+ throw "Shard $($manifest.shardIndex) reports an 'all' list that differs from shard $($ShardManifest[0].shardIndex)."
168
+ }
169
+ foreach ($path in @($manifest.selected | ForEach-Object { [string] $_ })) {
170
+ if (-not $seen.Add($path)) {
171
+ throw "Test file '$path' is selected by more than one shard."
172
+ }
173
+ }
174
+ }
175
+
176
+ [string[]] $union = @($seen)
177
+ [string[]] $expected = $reference
178
+ [System.Array]::Sort($union, [System.StringComparer]::Ordinal)
179
+ [System.Array]::Sort($expected, [System.StringComparer]::Ordinal)
180
+ if (($union -join "`n") -cne ($expected -join "`n")) {
181
+ $missing = @($expected | Where-Object { -not $seen.Contains($_) })
182
+ throw "The shard selections do not cover every test file; missing or extra entries (missing: $($missing -join ', '))."
183
+ }
184
+
185
+ $true
186
+ }
187
+
188
+ <#
189
+ .SYNOPSIS
190
+ Verifies that the shard test counts add up to the discovery count (issue #953).
191
+ .DESCRIPTION
192
+ Throws unless the sum of ShardTestCount equals DiscoveryCount. Returns $true otherwise.
193
+ .PARAMETER ShardTestCount
194
+ The test count reported by each shard (for example the JUnit testsuites/@tests value).
195
+ .PARAMETER DiscoveryCount
196
+ The test count reported by a discovery-only Pester pass over every test file.
197
+ #>
198
+ function Test-PoshQCShardTestCount {
199
+ [CmdletBinding()]
200
+ [OutputType([bool])]
201
+ param(
202
+ [Parameter(Mandatory)][AllowEmptyCollection()][int[]] $ShardTestCount,
203
+ [Parameter(Mandatory)][int] $DiscoveryCount
204
+ )
205
+
206
+ $sum = 0
207
+ foreach ($testCount in $ShardTestCount) {
208
+ $sum += $testCount
209
+ }
210
+ if ($sum -ne $DiscoveryCount) {
211
+ throw "Shard test counts sum to $sum; the discovery pass found $DiscoveryCount tests."
212
+ }
213
+
214
+ $true
215
+ }
216
+
217
+ <#
218
+ .SYNOPSIS
219
+ Selects this shard's test files for Invoke-PoshQCTest (issue #953).
220
+ .DESCRIPTION
221
+ Weights each enumerated test file with Get-PoshQCShardTestFileWeight (a FileInfo item, as the default
222
+ EnumerateTests seam returns, weighs the UTF-8 size of its text with CRLF counted as LF; CR-19), builds
223
+ the manifest with New-PoshQCShardManifest, and throws when the shard selects no test file (CR-13), so Pester
224
+ never runs with an empty Run.Path. When ShardFileListPath is set, WriteShardFileList receives the path and
225
+ the manifest JSON without selectedFullPath. Returns the full paths of the selected files.
226
+ .PARAMETER RootPath
227
+ Workspace root that the manifest paths are relative to.
228
+ .PARAMETER TestFile
229
+ Enumerated test files: FileInfo items, objects with FullName (and optionally Length), or path strings.
230
+ .PARAMETER ShardIndex
231
+ One-based index of the shard.
232
+ .PARAMETER ShardCount
233
+ Total number of shards.
234
+ .PARAMETER ShardFileListPath
235
+ Optional path that receives the shard manifest JSON.
236
+ .PARAMETER WriteShardFileList
237
+ Seam that writes the manifest JSON; receives the path and the JSON text.
238
+ #>
239
+ function Select-PoshQCShardTestFile {
240
+ [CmdletBinding()]
241
+ [OutputType([string[]])]
242
+ param(
243
+ [Parameter(Mandatory)][string] $RootPath,
244
+ [Parameter(Mandatory)][AllowEmptyCollection()][object[]] $TestFile,
245
+ [Parameter(Mandatory)][int] $ShardIndex,
246
+ [Parameter(Mandatory)][int] $ShardCount,
247
+ [string] $ShardFileListPath,
248
+ [Parameter(Mandatory)][scriptblock] $WriteShardFileList
249
+ )
250
+
251
+ $paths = [string[]] @($TestFile | ForEach-Object { if ($_ -is [string]) { $_ } else { $_.FullName } })
252
+ $weights = [long[]] @(Get-PoshQCShardTestFileWeight -TestFile $TestFile)
253
+ $manifest = New-PoshQCShardManifest -RootPath $RootPath -TestFile $paths -ShardIndex $ShardIndex -ShardCount $ShardCount -Weight $weights
254
+ if (@($manifest.selected).Count -eq 0) {
255
+ throw "Shard $ShardIndex of $ShardCount selects no test file; $($paths.Count) test files cannot fill $ShardCount shards. Lower the shard count."
256
+ }
257
+ if ($ShardFileListPath) {
258
+ & $WriteShardFileList $ShardFileListPath ($manifest | Select-Object -Property shardIndex, shardCount, all, selected | ConvertTo-Json -Depth 4)
259
+ }
260
+ [string[]] $manifest.selectedFullPath
261
+ }
262
+
263
+ <#
264
+ .SYNOPSIS
265
+ Returns the shard weight of each enumerated test file (issue #953, CR-19).
266
+ .DESCRIPTION
267
+ A FileInfo item weighs the UTF-8 byte count of its text after every CRLF pair is replaced by LF, so a checkout
268
+ with CRLF line endings computes the same weights, and therefore the same shard assignment, as a checkout with
269
+ LF line endings. Another object weighs its Length property when it has one; a plain path string, or an object
270
+ without a Length property, weighs 1. Returns one weight per item, in input order.
271
+ .PARAMETER TestFile
272
+ Enumerated test files: FileInfo items, objects with FullName (and optionally Length), or path strings.
273
+ .PARAMETER ReadTestFileText
274
+ Seam that returns the text of a test file from its full path. Defaults to [System.IO.File]::ReadAllText.
275
+ #>
276
+ function Get-PoshQCShardTestFileWeight {
277
+ [CmdletBinding()]
278
+ [OutputType([long[]])]
279
+ param(
280
+ [Parameter(Mandatory)][AllowEmptyCollection()][object[]] $TestFile,
281
+ [scriptblock] $ReadTestFileText = { param([string] $Path) [System.IO.File]::ReadAllText($Path) }
282
+ )
283
+
284
+ $weights = New-Object 'System.Collections.Generic.List[long]'
285
+ foreach ($item in $TestFile) {
286
+ if ($item -is [System.IO.FileInfo]) {
287
+ $text = [string] (& $ReadTestFileText $item.FullName)
288
+ $weights.Add([System.Text.Encoding]::UTF8.GetByteCount($text.Replace("`r`n", "`n")))
289
+ } elseif ($item -isnot [string] -and $null -ne $item.PSObject.Properties['Length']) {
290
+ $weights.Add([long] $item.Length)
291
+ } else {
292
+ $weights.Add(1)
293
+ }
294
+ }
295
+ [long[]] $weights.ToArray()
296
+ }
@@ -147,6 +147,17 @@ 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.
153
+ .PARAMETER ShardIndex
154
+ One-based shard index; used only when ShardCount is greater than 0 (issue #953).
155
+ .PARAMETER ShardCount
156
+ Number of shards. When greater than 0, Run.Path is set to this shard's size-balanced slice of the test files (Select-PoshQCShardTestFile).
157
+ .PARAMETER ShardFileListPath
158
+ Optional path that receives the shard manifest (shardIndex, shardCount, all, selected) as JSON.
159
+ .PARAMETER WriteShardFileList
160
+ Injectable seam that writes the shard manifest JSON. Defaults to Set-Content.
150
161
  #>
151
162
  function Invoke-PoshQCTest {
152
163
  [CmdletBinding()]
@@ -157,12 +168,19 @@ function Invoke-PoshQCTest {
157
168
  [string[]] $ExcludeDirs = $script:DefaultExcludedDirs,
158
169
  [string] $KoverageOutputPath,
159
170
  [switch] $DisableKoverageCopy,
171
+ [int] $ShardIndex = 0,
172
+ [int] $ShardCount = 0,
173
+ [string] $ShardFileListPath,
174
+ [scriptblock] $WriteShardFileList = { param([string] $Path, [string] $Json) Set-Content -LiteralPath $Path -Value $Json -Encoding utf8 },
160
175
  [scriptblock] $EnsureModule = {
161
- param([string] $Name, [string] $ErrorMessage)
162
- if (-not (Get-Module -ListAvailable -Name $Name)) { throw $ErrorMessage }
176
+ param([string] $Name, [string] $ErrorMessage, [string] $MinimumVersion, [string] $MaximumVersion)
177
+ # Issue #953 (CR-1): only an installed version inside the supported range counts as present, so a host
178
+ # whose only Pester is 6.x or the inbox 3.4.0 receives the actionable message instead of an import error.
179
+ if (-not (Get-Module -ListAvailable -FullyQualifiedName @{ ModuleName = $Name; ModuleVersion = $MinimumVersion; MaximumVersion = $MaximumVersion })) { throw $ErrorMessage }
163
180
  # Import globally so Pester's Describe/It/Mock resolve in test containers that the
164
181
  # default $InvokePester trampoline now hosts in the global session state (issue #392).
165
- Import-Module $Name -Global -ErrorAction Stop
182
+ # Issue #953 (D8, CR-2): -MinimumVersion and -MaximumVersion keep the import inside the supported range.
183
+ Import-Module $Name -Global -MinimumVersion $MinimumVersion -MaximumVersion $MaximumVersion -ErrorAction Stop
166
184
  },
167
185
  [scriptblock] $TestPathExists = { param([string] $Path) Test-Path $Path },
168
186
  [scriptblock] $LoadSettings = { param([string] $Path) Import-PowerShellDataFile -Path $Path },
@@ -282,6 +300,10 @@ function Invoke-PoshQCTest {
282
300
  [scriptblock] $CopyCoverage = {
283
301
  param([string] $CoveragePath, [string] $RepoRoot, [string] $KoveragePath)
284
302
  Convert-PoshQCCoverageToRelative -InputPath $CoveragePath -OutputPath $KoveragePath -RepoRoot $RepoRoot
303
+ },
304
+ [scriptblock] $ResolveCoveragePopulation = {
305
+ param([string] $RootPath, $Settings, [string] $SettingsFile, [string[]] $ScanFolderRoots, [string[]] $Excluded, [scriptblock] $PathExists, [scriptblock] $Log)
306
+ Resolve-PoshQCCoveragePopulation -Root $RootPath -Settings $Settings -SettingsFile $SettingsFile -ScanFolderRoots $ScanFolderRoots -ExcludeDirs $Excluded -SettingsPathExists $PathExists -Logger $Log
285
307
  }
286
308
  )
287
309
 
@@ -290,8 +312,12 @@ function Invoke-PoshQCTest {
290
312
  if (-not $Root) {
291
313
  $Root = $PWD.ProviderPath
292
314
  }
315
+ # Resolve a relative -Root once so run, coverage, and output paths are absolute (issue #527).
316
+ if (-not [IO.Path]::IsPathRooted($Root)) {
317
+ $Root = [IO.Path]::GetFullPath([IO.Path]::Combine($PWD.ProviderPath, $Root))
318
+ }
293
319
 
294
- & $EnsureModule 'Pester' "Pester is not installed. Run Install-PoshQCTool (alias Install-PoshQCTools) first."
320
+ & $EnsureModule 'Pester' "Pester is not installed in the supported range (5.9.0 to 5.99.99). Run Install-PoshQCTool (alias Install-PoshQCTools) first." '5.9.0' '5.99.99'
295
321
 
296
322
  if (-not (& $TestPathExists $SettingsPath)) {
297
323
  throw "Settings not found: $SettingsPath"
@@ -336,34 +362,20 @@ function Invoke-PoshQCTest {
336
362
  }
337
363
 
338
364
  if ($coverageEnabled -and $config.CodeCoverage) {
339
- if ($config.CodeCoverage.Path.Value) {
340
- $resolvedCoveragePaths = @(
341
- $config.CodeCoverage.Path.Value |
342
- ForEach-Object {
343
- if ([IO.Path]::IsPathRooted($_)) { $_ } else { Join-Path $Root $_ }
344
- }
345
- )
346
-
347
- # Prune coverage paths that do not exist under the resolved root before Pester sees
348
- # them. Pester's Resolve-CoverageInfo discards the whole set and raises a terminating
349
- # error on the first unresolvable entry, which aborts the run at RunStart in any
350
- # workspace that does not contain this repository's coverage layout (issue #409).
351
- # Rooted entries are tested as-is; they are never re-joined to $Root.
352
- $survivingCoveragePaths = @($resolvedCoveragePaths | Where-Object { & $TestPathExists $_ })
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
- }
365
+ # The coverage population is derived from the workspace, not from a list shipped with the
366
+ # module, so the measured set does not depend on which module copy was loaded (issue #527).
367
+ # Nonexistent caller-supplied entries are pruned and logged by the resolver (issue #409).
368
+ $population = & $ResolveCoveragePopulation $Root $settings $SettingsPath $effectiveScanFolders $ExcludeDirs $TestPathExists $Logger
369
+ $populationPaths = @($population.Paths | Where-Object { $_ })
370
+ & $Logger "Code coverage population: source=$($population.Source); files=$($populationPaths.Count)"
371
+ if ($populationPaths.Count -gt 0) {
372
+ $config.CodeCoverage.Path = $populationPaths
373
+ } else {
374
+ # An enabled-but-empty path set makes Pester instrument every Run.Path directory,
375
+ # so disable coverage for this invocation instead and continue with the test run.
376
+ $config.CodeCoverage.Enabled = $false
377
+ $coverageEnabled = $false
378
+ & $Logger "Code coverage disabled for this invocation: no configured coverage path exists under root '$Root'."
367
379
  }
368
380
 
369
381
  if ($config.CodeCoverage.OutputPath.Value) {
@@ -396,6 +408,10 @@ function Invoke-PoshQCTest {
396
408
  & $Logger "No Pester test files found under configured paths for root $Root"
397
409
  return
398
410
  }
411
+ if ($ShardCount -gt 0) {
412
+ # Issue #953 (FR-09, CR-10, CR-13): run only this shard's size-balanced slice of the enumerated test files.
413
+ $config.Run.Path = [string[]] @(Select-PoshQCShardTestFile -RootPath $Root -TestFile @($testFiles) -ShardIndex $ShardIndex -ShardCount $ShardCount -ShardFileListPath $ShardFileListPath -WriteShardFileList $WriteShardFileList)
414
+ }
399
415
 
400
416
  $pesterResult = & $InvokePester $config
401
417
 
@@ -16,7 +16,13 @@
16
16
  'Invoke-PoshQCAnalyze',
17
17
  'Invoke-PoshQCAnalyzeAutofix',
18
18
  'Invoke-PoshQCSuite',
19
- 'Invoke-PoshQCTest'
19
+ 'Invoke-PoshQCTest',
20
+ 'Get-PoshQCShardSlice',
21
+ 'Merge-PoshQCCoverageReport',
22
+ 'Get-PoshQCCoverageLineSummary',
23
+ 'New-PoshQCShardManifest',
24
+ 'Test-PoshQCShardCompleteness',
25
+ 'Test-PoshQCShardTestCount'
20
26
  )
21
27
  CmdletsToExport = @()
22
28
  VariablesToExport = @()
@@ -54,11 +54,16 @@ function Install-PoshQCTool {
54
54
 
55
55
  $requiredModules = @(
56
56
  @{ Name = 'PSScriptAnalyzer'; Version = '1.22.0' },
57
- @{ Name = 'Pester'; Version = '5.6.1' }
57
+ # Issue #953 (D8): UpperBound is exclusive; an installed Pester 6.x does not satisfy the requirement.
58
+ @{ Name = 'Pester'; Version = '5.9.0'; UpperBound = '6.0.0' }
58
59
  )
59
60
 
61
+ # Issue #953 (CR-3): one version-range predicate serves the present check and the post-install check.
62
+ # Where-Object runs it in this function's scope, so $module is the requirement of the current iteration.
63
+ $isSupportedVersion = { $_.Version -ge [version]$module.Version -and (-not $module.ContainsKey('UpperBound') -or $_.Version -lt [version]$module.UpperBound) }
64
+
60
65
  foreach ($module in $requiredModules) {
61
- $installed = & $FindModule $module.Name | Where-Object { $_.Version -ge [version]$module.Version } | Select-Object -First 1
66
+ $installed = & $FindModule $module.Name | Where-Object -FilterScript $isSupportedVersion | Select-Object -First 1
62
67
  if ($installed) {
63
68
  & $Logger "$($module.Name) $($installed.Version) already present."
64
69
  continue
@@ -71,8 +76,7 @@ function Install-PoshQCTool {
71
76
  throw "Failed to install $($module.Name) $($module.Version): $($_.Exception.Message)"
72
77
  }
73
78
 
74
- $post = & $FindModule $module.Name | Where-Object { $_.Version -ge [version]$module.Version } | Select-Object -First 1
75
- if (-not $post) {
79
+ if (-not (& $FindModule $module.Name | Where-Object -FilterScript $isSupportedVersion | Select-Object -First 1)) {
76
80
  throw "Failed to install $($module.Name) $($module.Version)."
77
81
  }
78
82
  & $Logger "$($module.Name) $($module.Version) installed."
@@ -113,7 +117,10 @@ if (-not $subModuleCache) {
113
117
  foreach ($subModuleName in @(
114
118
  'PoshQC.FileDiscovery.psm1',
115
119
  'PoshQC.ScanConfig.psm1',
120
+ 'PoshQC.Coverage.psm1',
116
121
  'PoshQC.Analyzer.psm1',
122
+ 'PoshQC.Shard.psm1',
123
+ 'PoshQC.CoverageMerge.psm1',
117
124
  'PoshQC.Testing.psm1'
118
125
  )) {
119
126
  $subModulePath = Join-Path $script:ModuleRoot $subModuleName
@@ -142,6 +149,12 @@ Export-ModuleMember -Function @(
142
149
  'Invoke-PoshQCSuite',
143
150
  'Invoke-PoshQCTest',
144
151
  'Convert-PoshQCCoverageToRelative',
145
- 'Get-PoshQCScanConfigFolder'
152
+ 'Get-PoshQCScanConfigFolder',
153
+ 'Get-PoshQCShardSlice',
154
+ 'Merge-PoshQCCoverageReport',
155
+ 'Get-PoshQCCoverageLineSummary',
156
+ 'New-PoshQCShardManifest',
157
+ 'Test-PoshQCShardCompleteness',
158
+ 'Test-PoshQCShardTestCount'
146
159
  ) -Alias @('Install-PoshQCTools')
147
160
 
@@ -13,7 +13,7 @@ PoshQC is a lightweight PowerShell quality gate that wraps Invoke-Formatter, PSS
13
13
  ## Requirements
14
14
 
15
15
  - PowerShell 5.1 or 7.6+
16
- - Modules: PSScriptAnalyzer 1.22.0, Pester 5.6.1 (installed automatically via the helper if missing)
16
+ - Modules: PSScriptAnalyzer 1.22.0, Pester 5.9.0 or later, below 6.0 (installed automatically via the helper if missing)
17
17
 
18
18
  ## Getting started
19
19
 
@@ -59,14 +59,32 @@ PoshQC is a lightweight PowerShell quality gate that wraps Invoke-Formatter, PSS
59
59
  - `Invoke-PoshQCSuite`: run format, analyze, and test in one pass, optionally narrowing the scan scope with `-ScanFolders`.
60
60
  - `Invoke-PoshQCTest`: run Pester using the bundled runsettings, including coverage and optional Koverage copy.
61
61
  - `Convert-PoshQCCoverageToRelative`: utility to strip repo-root prefixes and write a `.koverage.xml` copy (used internally by `Invoke-PoshQCTest`).
62
+ - `Get-PoshQCShardSlice`: select one shard's test files; with `-Weight` (non-negative values, for example text sizes in bytes) the heaviest files are assigned first to the least-loaded shard, and a negative weight is rejected.
63
+ - `New-PoshQCShardManifest`: build the in-memory shard manifest (`shardIndex`, `shardCount`, `all`, `selected`, `selectedFullPath`).
64
+ - `Test-PoshQCShardCompleteness`: throw unless a set of shard manifests selects every test file exactly once.
65
+ - `Test-PoshQCShardTestCount`: throw unless the shard test counts sum to a discovery-pass test count.
66
+ - `Merge-PoshQCCoverageReport`: merge the CoverageGutters XML texts of the shards at LINE granularity (no file I/O, no threshold).
67
+ - `Get-PoshQCCoverageLineSummary`: return covered, missed, total, and percent from a report's LINE counter.
62
68
 
63
69
  ## Configuration
64
70
 
65
71
  - PSScriptAnalyzer settings: `./settings/pssa.settings.psd1`
66
72
  - 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
73
  - Pester settings: `./settings/pester.runsettings.psd1`
68
- - Runs tests under `scripts` and `tests/powershell`, outputs JUnit XML and CoverageGutters coverage, and enables coverage over `scripts/dev-tools/*.ps1`, `scripts/powershell/**/*.psm1`, and `src/**/*.ps1`.
74
+ - 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).
75
+ - Coverage uses Pester's profiler-based mode (`CodeCoverage.UseBreakpoints = $false`, issue #953), which needs Pester 5.4 or later and is faster than breakpoint mode on the same coverage population. The PoshQC suites target PowerShell 7; profiler mode under Windows PowerShell 5.1 is not exercised in CI. To use breakpoint mode, pass a settings file with `UseBreakpoints = $true` through `-SettingsPath`.
69
76
  - When `-ScanFolders` is supplied to `Invoke-PoshQCSuite` or the lower-level commands, the suite narrows discovery to those workspace-relative or workspace-contained folders.
77
+ - Coverage population (issue #527): `Invoke-PoshQCTest` measures the same file set for the same workspace whichever module copy is loaded.
78
+ - 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.
79
+ - 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`.
80
+ - 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.
81
+ - An empty population disables coverage for that invocation, logs `Code coverage disabled for this invocation`, and still runs the tests.
82
+ - Each run logs `Code coverage population: source=<source>; files=<count>` before Pester starts.
83
+ - Sharded runs (issue #953): `Invoke-PoshQCTest -ShardIndex <n> -ShardCount <N>` runs only shard n of N.
84
+ - Test files are weighted by the UTF-8 size in bytes of their text with CRLF line endings counted as LF, and assigned heaviest first to the shard with the least assigned weight (ties: fewer files, then the lower shard index; equal sizes are taken in ordinal path order), so runners whose checkouts differ only in line-ending conversion compute the same assignment.
85
+ - `-ShardFileListPath <path>` writes the shard manifest (`shardIndex`, `shardCount`, `all`, `selected`) as JSON through the `-WriteShardFileList` seam.
86
+ - A shard that selects no test file (more shards than test files) stops with an error before Pester runs.
87
+ - `Merge-PoshQCCoverageReport` merges the shard reports with `ci = max(ci)` per line and recomputes the LINE and INSTRUCTION counters, so the merged LINE figures are exact. It omits `class` and `method` elements and METHOD and CLASS counters, because those cannot be recomputed from line hits.
70
88
 
71
89
  ## Typical workflow
72
90
 
@@ -77,4 +95,4 @@ PoshQC is a lightweight PowerShell quality gate that wraps Invoke-Formatter, PSS
77
95
  ## Notes for standalone use
78
96
 
79
97
  - 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.
98
+ - 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.