@danmoisan/drm-copilot-mcp 1.1.26 → 1.1.27

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 (159) hide show
  1. package/out/mcp-server.js +170 -52
  2. package/package.json +1 -1
  3. package/resources/claude-customizations/.claude/agents/atomic-executor.md +3 -3
  4. package/resources/claude-customizations/.claude/agents/atomic-planner.md +2 -2
  5. package/resources/claude-customizations/.claude/agents/csharp-typed-engineer.md +7 -7
  6. package/resources/claude-customizations/.claude/agents/feature-review.md +6 -3
  7. package/resources/claude-customizations/.claude/agents/orchestrator.md +11 -7
  8. package/resources/claude-customizations/.claude/agents/powershell-typed-engineer.md +7 -7
  9. package/resources/claude-customizations/.claude/agents/python-typed-engineer.md +6 -6
  10. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-review.ps1 +699 -0
  11. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +16 -1
  12. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-rules.ps1 +50 -16
  13. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorState.psm1 +6 -1
  14. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCompletion.psm1 +4 -2
  15. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCompletionChecks.psm1 +51 -2
  16. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateReceipts.psm1 +5 -2
  17. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRemediationAccounting.psm1 +116 -4
  18. package/resources/claude-customizations/.claude/rules/csharp.md +8 -8
  19. package/resources/claude-customizations/.claude/rules/orchestrator-state.md +28 -2
  20. package/resources/claude-customizations/.claude/rules/powershell.md +2 -2
  21. package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +8 -12
  22. package/resources/claude-customizations/.claude/skills/csharp-qa-gate/SKILL.md +14 -15
  23. package/resources/claude-customizations/.claude/skills/epic-progress-report/SKILL.md +338 -0
  24. package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.Logic.ps1 +248 -0
  25. package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.Metrics.ps1 +232 -0
  26. package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.Render.ps1 +196 -0
  27. package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.State.ps1 +286 -0
  28. package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.ps1 +475 -0
  29. package/resources/claude-customizations/.claude/skills/execution-environment/SKILL.md +18 -13
  30. package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +21 -9
  31. package/resources/claude-customizations/.claude/skills/invoke-csharp-engineer/SKILL.md +5 -5
  32. package/resources/claude-customizations/.claude/skills/invoke-powershell-engineer/SKILL.md +4 -4
  33. package/resources/claude-customizations/.claude/skills/invoke-python-engineer/SKILL.md +4 -4
  34. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +24 -2
  35. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +8 -0
  36. package/resources/claude-customizations/.claude/skills/powershell-qa-gate/SKILL.md +16 -17
  37. package/resources/claude-customizations/.claude/skills/python-qa-gate/SKILL.md +15 -16
  38. package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +1 -1
  39. package/resources/claude-customizations/.claude-variants/csharp-legacy/agents/csharp-typed-engineer.md +6 -6
  40. package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +13 -14
  41. package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/invoke-csharp-engineer/SKILL.md +4 -4
  42. package/resources/claude-customizations/pack-manifests/core.json +7 -0
  43. package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +8 -12
  44. package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +1 -1
  45. package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +13 -14
  46. package/resources/codex-and-agents-customizations/.agents/skills/execution-environment/SKILL.md +18 -13
  47. package/resources/codex-and-agents-customizations/.agents/skills/feature-review/SKILL.md +5 -1
  48. package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +18 -5
  49. package/resources/codex-and-agents-customizations/.agents/skills/invoke-csharp-engineer/SKILL.md +4 -4
  50. package/resources/codex-and-agents-customizations/.agents/skills/invoke-powershell-engineer/SKILL.md +4 -4
  51. package/resources/codex-and-agents-customizations/.agents/skills/invoke-python-engineer/SKILL.md +4 -4
  52. package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +24 -2
  53. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-state/SKILL.md +16 -1
  54. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +3 -3
  55. package/resources/codex-and-agents-customizations/.agents/skills/powershell/SKILL.md +2 -2
  56. package/resources/codex-and-agents-customizations/.agents/skills/powershell-qa-gate/SKILL.md +15 -16
  57. package/resources/codex-and-agents-customizations/.agents/skills/python-qa-gate/SKILL.md +14 -15
  58. package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/SKILL.md +2 -0
  59. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +13 -14
  60. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/invoke-csharp-engineer/SKILL.md +4 -4
  61. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c1.toml +3 -3
  62. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +3 -3
  63. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +3 -3
  64. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +3 -3
  65. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +3 -3
  66. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +3 -3
  67. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +2 -2
  68. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +2 -2
  69. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +2 -2
  70. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +2 -2
  71. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +2 -2
  72. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +2 -2
  73. package/resources/codex-and-agents-customizations/.codex/agents/csharp-atomic-executor.toml +2 -2
  74. package/resources/codex-and-agents-customizations/.codex/agents/csharp-atomic-planning.toml +1 -1
  75. package/resources/codex-and-agents-customizations/.codex/agents/csharp-orchestrator.toml +1 -1
  76. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c1.toml +6 -6
  77. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c2.toml +6 -6
  78. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3-elevated.toml +6 -6
  79. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3.toml +6 -6
  80. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c4.toml +6 -6
  81. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer.toml +6 -6
  82. package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +2 -2
  83. package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-executor.toml +2 -2
  84. package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-planning.toml +1 -1
  85. package/resources/codex-and-agents-customizations/.codex/agents/powershell-di-unit-test-engineer.toml +1 -1
  86. package/resources/codex-and-agents-customizations/.codex/agents/powershell-orchestrator.toml +1 -1
  87. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c1.toml +7 -7
  88. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c2.toml +7 -7
  89. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3-elevated.toml +7 -7
  90. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3.toml +7 -7
  91. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c4.toml +7 -7
  92. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer.toml +7 -7
  93. package/resources/codex-and-agents-customizations/.codex/agents/pytest-unit-test-coding.toml +1 -1
  94. package/resources/codex-and-agents-customizations/.codex/agents/python-atomic-executor.toml +2 -2
  95. package/resources/codex-and-agents-customizations/.codex/agents/python-atomic-planning.toml +1 -1
  96. package/resources/codex-and-agents-customizations/.codex/agents/python-execution-only-typed.toml +1 -1
  97. package/resources/codex-and-agents-customizations/.codex/agents/python-orchestrator.toml +1 -1
  98. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c1.toml +6 -6
  99. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c2.toml +6 -6
  100. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3-elevated.toml +6 -6
  101. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3.toml +6 -6
  102. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c4.toml +6 -6
  103. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer.toml +6 -6
  104. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  105. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate-review.ps1 +699 -0
  106. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +29 -2
  107. package/resources/codex-and-agents-customizations/.codex/hooks/validate-feature-review-coverage.ps1 +51 -4
  108. package/resources/codex-and-agents-customizations/.codex-variants/csharp-legacy/agents/csharp-typed-engineer.toml +6 -6
  109. package/resources/codex-and-agents-customizations/pack-manifests/core.json +1 -0
  110. package/resources/customizations/.github/agents/5.1-Beast-adjusted.agent.md +1 -1
  111. package/resources/customizations/.github/agents/Powershell DI Unit Test Engineer.agent.md +20 -26
  112. package/resources/customizations/.github/agents/atomic_executor.agent.md +4 -4
  113. package/resources/customizations/.github/agents/atomic_planning.agent.md +4 -4
  114. package/resources/customizations/.github/agents/commentary-remediation.agent.md +1 -1
  115. package/resources/customizations/.github/agents/commit-steward.agent.md +1 -1
  116. package/resources/customizations/.github/agents/csharp-atomic-executor.agent.md +14 -14
  117. package/resources/customizations/.github/agents/csharp-atomic-planning.agent.md +2 -2
  118. package/resources/customizations/.github/agents/csharp-orchestrator.agent.md +4 -4
  119. package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +19 -22
  120. package/resources/customizations/.github/agents/epic-review.agent.md +1 -1
  121. package/resources/customizations/.github/agents/gpt-5-beast-mode.agent.md +3 -3
  122. package/resources/customizations/.github/agents/orchestrator.agent.md +8 -8
  123. package/resources/customizations/.github/agents/powershell-atomic-executor.agent.md +12 -12
  124. package/resources/customizations/.github/agents/powershell-atomic-planning.agent.md +4 -4
  125. package/resources/customizations/.github/agents/powershell-orchestrator.agent.md +6 -6
  126. package/resources/customizations/.github/agents/powershell-typed-engineer.agent.md +21 -24
  127. package/resources/customizations/.github/agents/pytest-unit-test-coding.agent.md +13 -17
  128. package/resources/customizations/.github/agents/python-atomic-executor.agent.md +11 -11
  129. package/resources/customizations/.github/agents/python-atomic-planning.agent.md +2 -2
  130. package/resources/customizations/.github/agents/python-execution-only-typed.agent.md +14 -17
  131. package/resources/customizations/.github/agents/python-orchestrator.agent.md +7 -7
  132. package/resources/customizations/.github/agents/python-typed-engineer.agent.md +17 -20
  133. package/resources/customizations/.github/agents/staged-review.agent.md +2 -2
  134. package/resources/customizations/.github/agents/tdd-green.agent.md +1 -1
  135. package/resources/customizations/.github/agents/tdd-red.agent.md +3 -3
  136. package/resources/customizations/.github/agents/tdd-refactor.agent.md +2 -2
  137. package/resources/customizations/.github/agents/typescript-engineer.agent.md +3 -3
  138. package/resources/customizations/.github/codex/execute-hard-lock.prompt.md +3 -3
  139. package/resources/customizations/.github/instructions/general-code-change.instructions.md +1 -1
  140. package/resources/customizations/.github/instructions/powershell-unit-test.instructions.md +1 -1
  141. package/resources/customizations/.github/instructions/python-code-change.instructions.md +5 -5
  142. package/resources/customizations/.github/instructions/python-suppressions.instructions.md +3 -3
  143. package/resources/customizations/.github/instructions/python-unit-test.instructions.md +1 -1
  144. package/resources/customizations/.github/prompts/execute-hard-lock.prompt.md +2 -2
  145. package/resources/customizations/.github/prompts/generate-commit-message-repo.prompt.md +2 -2
  146. package/resources/customizations/.github/prompts/remediate-comments.prompt.md +2 -2
  147. package/resources/customizations/.github/prompts/review-feature.prompt.md +1 -1
  148. package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +8 -12
  149. package/resources/customizations/.github/skills/execution-environment/SKILL.md +18 -13
  150. package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +15 -2
  151. package/resources/feature-templates/bug/plan.yyyy-MM-ddTHH-mm.md +4 -4
  152. package/resources/feature-templates/feature/plan.yyyy-MM-ddTHH-mm.md +3 -3
  153. package/resources/feature-templates/refactor/plan.yyyy-MM-ddTHH-mm.md +2 -2
  154. package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +2 -2
  155. package/resources/powershell/PoshQC/PoshQC.psm1 +3 -3
  156. package/resources/powershell/PoshQC/README.md +8 -8
  157. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +2 -2
  158. package/resources/templates/policy_audit/AGENTS.md +9 -10
  159. package/resources/templates/policy_audit/policy-audit.yyyy-MM-ddTHH-mm.md +19 -20
@@ -0,0 +1,475 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Print the TL;DR block and the hourly progress report of an epic.
4
+
5
+ .DESCRIPTION
6
+ Reads the epic kickoff and the feature plans from the integration branch (or from a
7
+ feature worktree when one holds the plan), the epic orchestrator checkpoint, the pull
8
+ request state of each feature branch, and the weekly quota of the active cswap
9
+ account, then prints the task progress, the per-feature state, the average and
10
+ recent velocity, the sample standard deviation, the estimated time remaining, and the
11
+ weekly quota.
12
+
13
+ The pure logic lives in Get-EpicProgressReport.Logic.ps1, which this script
14
+ dot-sources and which dot-sources the State, Metrics, and Render files. Every I/O call
15
+ goes through a wrapper function in this script. When gh or cswap is not installed, or
16
+ prints no parseable output, the report still prints: the pull-request state is empty
17
+ and the quota is n/a.
18
+
19
+ The state file at -StatePath keeps the start instant, the baseline done count, the
20
+ weekly baseline, and the check-in samples (Unix time and done count). A sample is added
21
+ when the previous sample is at least 90 seconds old; the recent velocity and its
22
+ standard deviation are computed from those samples. The state file is read before the
23
+ first git call. The legacy sample array written by earlier versions is still read.
24
+
25
+ This file is the canonical script of the /epic-progress-report skill. The entry-point
26
+ block at the bottom is skipped when the file is dot-sourced.
27
+
28
+ .PARAMETER EpicFolder
29
+ Mandatory. The epic folder name under docs/features/epics/. It is also the epic name
30
+ printed in the status heading.
31
+
32
+ .PARAMETER StartUtc
33
+ Optional. The instant from which elapsed time and average velocity are measured;
34
+ converted to UTC. A start after the current instant counts as zero elapsed time.
35
+ Resolution: the passed value, else now when -ResetBaseline is given, else the start
36
+ stored in the state file. With none of these the script stops with an error before any
37
+ git call. A passed value is persisted with the next state write.
38
+
39
+ .PARAMETER EpicIssue
40
+ The epic issue number printed in the report. Default: the number on the first
41
+ 'Epic issue: #<n>' line of the kickoff file. The script stops with an error when
42
+ neither this parameter nor the kickoff yields a number.
43
+
44
+ .PARAMETER IntegrationBranch
45
+ The branch that holds the kickoff and the plans. Default:
46
+ origin/epic/<EpicFolder>-integration.
47
+
48
+ .PARAMETER KickoffPath
49
+ The repository-relative path of the kickoff file on the integration branch. Default:
50
+ docs/features/epics/<EpicFolder>/epic-kickoff.md. Only the feature-summary table, whose
51
+ header's first cell is issue_num or Issue, is read; every other table in the file is ignored.
52
+ Kickoff text without that header, a feature row with fewer than five cells, and a feature
53
+ row with a blank plan cell each raise a terminating error.
54
+
55
+ .PARAMETER RepositoryRoot
56
+ The repository root in which git runs. Default: the current location.
57
+
58
+ .PARAMETER CheckpointPath
59
+ The epic orchestrator checkpoint. Default:
60
+ artifacts/orchestration/epic-orchestrator-state.json under the repository root.
61
+
62
+ .PARAMETER StatePath
63
+ The state file that keeps the start, the baselines, and the check-in samples. Default:
64
+ .claude/state/epic-progress-<EpicFolder>.json under the repository root, a gitignored
65
+ location that survives a container stop with the checkout. The directory is created
66
+ when missing.
67
+
68
+ .PARAMETER BaselineDone
69
+ The number of tasks already complete at the start. Resolution: the passed value, else
70
+ the current done count when -ResetBaseline is given, else the stored value, else 0.
71
+
72
+ .PARAMETER BaselineWeeklyPct
73
+ The weekly quota percentage at the start. Resolution: the passed value, else the stored
74
+ value, else 0. -ResetBaseline does not change it.
75
+
76
+ .PARAMETER WaveOverheadHours
77
+ The review and CI hours added per open wave in the wave-parallel estimate.
78
+ Default: 1.25.
79
+
80
+ .PARAMETER SampleCap
81
+ The maximum number of check-in samples kept in the state file. Default: 14.
82
+
83
+ .PARAMETER Mode
84
+ TLDR prints the four-line TL;DR block only; Full prints the TL;DR block followed by
85
+ the full report. Default: Full.
86
+
87
+ .PARAMETER ResetBaseline
88
+ Discards every stored sample, sets the start to now (unless -StartUtc is passed) and the
89
+ baseline done count to the current done count (unless -BaselineDone is passed), adds
90
+ the first sample, and writes the state file. The weekly baseline is kept.
91
+
92
+ .PARAMETER PairSamples
93
+ After the normal sample rule, keeps the newest sample and every second older sample
94
+ and writes the state file, so the intervals match a doubled cadence. A one-shot
95
+ command: each run halves the samples again. Cannot be combined with -ResetBaseline.
96
+
97
+ .EXAMPLE
98
+ pwsh -NoProfile -File .claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.ps1 -EpicFolder example-epic -StartUtc 2026-01-05T09:00:00Z -BaselineDone 40 -Mode TLDR
99
+
100
+ Prints the TL;DR block for the epic in docs/features/epics/example-epic/, measuring
101
+ from 09:00 UTC and counting 40 tasks as complete at the start; both values are stored
102
+ for later runs.
103
+
104
+ .EXAMPLE
105
+ pwsh -NoProfile -File .claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.ps1 -EpicFolder example-epic -EpicIssue 12
106
+
107
+ Prints the full report for epic issue 12 with the start and baseline read from the
108
+ state file.
109
+
110
+ .EXAMPLE
111
+ pwsh -NoProfile -File .claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.ps1 -EpicFolder example-epic -ResetBaseline
112
+
113
+ Restarts the measurement now after a pause: the stored samples are discarded and the
114
+ current done count becomes the baseline.
115
+
116
+ .NOTES
117
+ The tool is read-only toward the repository and GitHub: it runs only git fetch, git
118
+ show, git worktree list, and gh pr list, and the only file it writes is the state file
119
+ at -StatePath (issues #1080 and #1093).
120
+ #>
121
+
122
+ [CmdletBinding()]
123
+ param(
124
+ [Parameter(Mandatory = $true)]
125
+ [ValidateNotNullOrEmpty()]
126
+ [string] $EpicFolder,
127
+
128
+ [datetime] $StartUtc,
129
+
130
+ [string] $EpicIssue = '',
131
+
132
+ [string] $IntegrationBranch = '',
133
+
134
+ [string] $KickoffPath = '',
135
+
136
+ [string] $RepositoryRoot = '',
137
+
138
+ [string] $CheckpointPath = '',
139
+
140
+ [string] $StatePath = '',
141
+
142
+ [int] $BaselineDone = 0,
143
+
144
+ [double] $BaselineWeeklyPct = 0,
145
+
146
+ [double] $WaveOverheadHours = 1.25,
147
+
148
+ [ValidateRange(1, 100000)]
149
+ [int] $SampleCap = 14,
150
+
151
+ [ValidateSet('TLDR', 'Full')]
152
+ [string] $Mode = 'Full',
153
+
154
+ [switch] $ResetBaseline,
155
+
156
+ [switch] $PairSamples
157
+ )
158
+
159
+ . (Join-Path -Path $PSScriptRoot -ChildPath 'Get-EpicProgressReport.Logic.ps1')
160
+
161
+ function Invoke-GitExe {
162
+ <#
163
+ .SYNOPSIS
164
+ Run git with the given arguments and return its standard output lines.
165
+ .PARAMETER GitArgs
166
+ The git arguments.
167
+ #>
168
+ [CmdletBinding()]
169
+ [OutputType([string[]])]
170
+ param([Parameter(Mandatory = $true)] [string[]] $GitArgs)
171
+
172
+ & git @GitArgs 2>$null
173
+ }
174
+
175
+ function Invoke-GhExe {
176
+ <#
177
+ .SYNOPSIS
178
+ Run gh with the given arguments and return its standard output lines.
179
+ .PARAMETER GhArgs
180
+ The gh arguments.
181
+ #>
182
+ [CmdletBinding()]
183
+ [OutputType([string[]])]
184
+ param([Parameter(Mandatory = $true)] [string[]] $GhArgs)
185
+
186
+ & gh @GhArgs 2>$null
187
+ }
188
+
189
+ function Invoke-CswapExe {
190
+ <#
191
+ .SYNOPSIS
192
+ Run cswap with the given arguments and return its standard output lines.
193
+ .PARAMETER CswapArgs
194
+ The cswap arguments.
195
+ #>
196
+ [CmdletBinding()]
197
+ [OutputType([string[]])]
198
+ param([Parameter(Mandatory = $true)] [string[]] $CswapArgs)
199
+
200
+ & cswap @CswapArgs 2>$null
201
+ }
202
+
203
+ function Test-EpicProgressCommand {
204
+ <#
205
+ .SYNOPSIS
206
+ Return whether an application of the given name is installed.
207
+ .PARAMETER Name
208
+ The application name.
209
+ #>
210
+ [CmdletBinding()]
211
+ [OutputType([bool])]
212
+ param([Parameter(Mandatory = $true)] [string] $Name)
213
+
214
+ return [bool] (Get-Command -Name $Name -CommandType Application -ErrorAction SilentlyContinue)
215
+ }
216
+
217
+ function Test-EpicProgressPath {
218
+ <#
219
+ .SYNOPSIS
220
+ Return whether a file exists at the given path; a blank path returns $false.
221
+ .PARAMETER Path
222
+ The file path.
223
+ #>
224
+ [CmdletBinding()]
225
+ [OutputType([bool])]
226
+ param([Parameter(Mandatory = $true)] [AllowEmptyString()] [string] $Path)
227
+
228
+ if ([string]::IsNullOrWhiteSpace($Path)) { return $false }
229
+ return Test-Path -LiteralPath $Path -PathType Leaf
230
+ }
231
+
232
+ function Read-EpicProgressFile {
233
+ <#
234
+ .SYNOPSIS
235
+ Return the raw text of a file.
236
+ .PARAMETER Path
237
+ The file path.
238
+ #>
239
+ [CmdletBinding()]
240
+ [OutputType([string])]
241
+ param([Parameter(Mandatory = $true)] [string] $Path)
242
+
243
+ return Get-Content -LiteralPath $Path -Raw
244
+ }
245
+
246
+ function Write-EpicProgressState {
247
+ <#
248
+ .SYNOPSIS
249
+ Write the state file content as UTF-8; the only file the report writes.
250
+ .DESCRIPTION
251
+ Creates the parent directory (for example .claude/state) when it is missing. Both
252
+ writes run only inside the ShouldProcess block, so -WhatIf writes nothing.
253
+ .PARAMETER Path
254
+ The state file path.
255
+ .PARAMETER Content
256
+ The JSON text to write.
257
+ #>
258
+ [CmdletBinding(SupportsShouldProcess = $true)]
259
+ param(
260
+ [Parameter(Mandatory = $true)] [string] $Path,
261
+ [Parameter(Mandatory = $true)] [string] $Content
262
+ )
263
+
264
+ if ($PSCmdlet.ShouldProcess($Path, 'Write epic progress state')) {
265
+ $directory = Split-Path -Path $Path -Parent
266
+ if ($directory -and -not (Test-Path -LiteralPath $directory -PathType Container)) {
267
+ $null = New-Item -ItemType Directory -Path $directory -Force
268
+ }
269
+ Set-Content -LiteralPath $Path -Value $Content -Encoding utf8
270
+ }
271
+ }
272
+
273
+ function Get-EpicProgressUtcNow {
274
+ <#
275
+ .SYNOPSIS
276
+ Return the current UTC instant.
277
+ #>
278
+ [CmdletBinding()]
279
+ [OutputType([datetime])]
280
+ param()
281
+
282
+ return [datetime]::UtcNow
283
+ }
284
+
285
+ function Get-EpicProgressLastExitCode {
286
+ <#
287
+ .SYNOPSIS
288
+ Return the exit code of the most recent native command ($LASTEXITCODE).
289
+ #>
290
+ [CmdletBinding()]
291
+ [OutputType([int])]
292
+ param()
293
+
294
+ return $LASTEXITCODE
295
+ }
296
+
297
+ function Invoke-EpicProgressReport {
298
+ <#
299
+ .SYNOPSIS
300
+ Gather the report inputs through the I/O seams and return the report lines.
301
+ .DESCRIPTION
302
+ Wiring only. Runs git with exactly 'fetch -q origin', 'worktree list --porcelain',
303
+ and 'show <IntegrationBranch>:<path>'; runs 'gh pr list' only when gh is installed
304
+ and 'cswap list --json' only when cswap is installed; treats an unparseable
305
+ checkpoint or state file as empty; reads the state file and resolves the start before
306
+ the first git call; writes the state file only when the sample set changed or
307
+ ResetBaseline or PairSamples is given. When 'git fetch -q origin' exits with a non-zero code, writes a warning
308
+ naming the code and continues with the last fetched state of the integration branch.
309
+ Returns the TL;DR lines, followed by the Full lines unless Mode is TLDR.
310
+ .PARAMETER EpicFolder
311
+ The epic folder name; also the epic name in the status heading.
312
+ .PARAMETER StartUtc
313
+ The start instant; when not passed, resolved by Resolve-EpicProgressBaseline.
314
+ .PARAMETER EpicIssue
315
+ The epic issue number; blank to read it from the kickoff file.
316
+ .PARAMETER IntegrationBranch
317
+ The integration branch; blank for the default.
318
+ .PARAMETER KickoffPath
319
+ The kickoff path on the integration branch; blank for the default.
320
+ .PARAMETER RepositoryRoot
321
+ The repository root; blank for the current location.
322
+ .PARAMETER CheckpointPath
323
+ The epic orchestrator checkpoint path; blank for the default.
324
+ .PARAMETER StatePath
325
+ The state file path; blank for the default under .claude/state of the repository root.
326
+ .PARAMETER BaselineDone
327
+ The tasks already complete at the start; when not passed, resolved from the reset or the state.
328
+ .PARAMETER BaselineWeeklyPct
329
+ The weekly quota percentage at the start; when not passed, resolved from the state.
330
+ .PARAMETER WaveOverheadHours
331
+ The hours added per open wave.
332
+ .PARAMETER SampleCap
333
+ The maximum number of samples kept.
334
+ .PARAMETER Mode
335
+ TLDR or Full.
336
+ .PARAMETER ResetBaseline
337
+ Discard the stored samples, restart the baseline now, and write the state.
338
+ .PARAMETER PairSamples
339
+ Pair the samples after the sample rule and write the state.
340
+ #>
341
+ [CmdletBinding()]
342
+ [OutputType([string[]])]
343
+ param(
344
+ [Parameter(Mandatory = $true)] [string] $EpicFolder,
345
+ [datetime] $StartUtc = [datetime]::MinValue,
346
+ [string] $EpicIssue = '',
347
+ [string] $IntegrationBranch = '',
348
+ [string] $KickoffPath = '',
349
+ [string] $RepositoryRoot = '',
350
+ [string] $CheckpointPath = '',
351
+ [string] $StatePath = '',
352
+ [int] $BaselineDone = 0,
353
+ [double] $BaselineWeeklyPct = 0,
354
+ [double] $WaveOverheadHours = 1.25,
355
+ [ValidateRange(1, 100000)] [int] $SampleCap = 14,
356
+ [ValidateSet('TLDR', 'Full')] [string] $Mode = 'Full',
357
+ [switch] $ResetBaseline,
358
+ [switch] $PairSamples
359
+ )
360
+
361
+ if ($ResetBaseline -and $PairSamples) {
362
+ throw '-ResetBaseline and -PairSamples cannot be combined: a reset discards the samples that -PairSamples would pair. Run them separately.'
363
+ }
364
+
365
+ $setting = Resolve-EpicProgressSetting -EpicFolder $EpicFolder -EpicIssue $EpicIssue -IntegrationBranch $IntegrationBranch -KickoffPath $KickoffPath -RepositoryRoot $RepositoryRoot -CheckpointPath $CheckpointPath -StatePath $StatePath -CurrentLocation (Get-Location).ProviderPath
366
+ # The state file is read before the first git call.
367
+ $stateText = ''
368
+ if (Test-EpicProgressPath -Path $setting.StatePath) { $stateText = Read-EpicProgressFile -Path $setting.StatePath }
369
+ $storedState = ConvertFrom-EpicProgressStateText -Text $stateText
370
+ $nowUtc = (Get-EpicProgressUtcNow).ToUniversalTime()
371
+ $baseline = Resolve-EpicProgressBaseline -StartUtc $StartUtc -StartUtcPassed:($PSBoundParameters.ContainsKey('StartUtc')) -BaselineDone $BaselineDone -BaselineDonePassed:($PSBoundParameters.ContainsKey('BaselineDone')) -BaselineWeeklyPct $BaselineWeeklyPct -BaselineWeeklyPctPassed:($PSBoundParameters.ContainsKey('BaselineWeeklyPct')) -State $storedState -ResetBaseline:$ResetBaseline -NowUtc $nowUtc
372
+ # A reset discards every stored sample; the sample rule then adds the first sample (now, done).
373
+ $priorSamples = $storedState.Samples
374
+ if ($ResetBaseline) { $priorSamples = [object[]] @() }
375
+ $null = Invoke-GitExe -GitArgs @('fetch', '-q', 'origin')
376
+ $fetchExitCode = Get-EpicProgressLastExitCode
377
+ if ($fetchExitCode -ne 0) {
378
+ Write-Warning "git fetch -q origin failed with exit code $fetchExitCode; the report uses the last fetched state of $($setting.IntegrationBranch)."
379
+ }
380
+ $worktreeLines = @(Invoke-GitExe -GitArgs @('worktree', 'list', '--porcelain'))
381
+ $worktreeOf = ConvertFrom-EpicProgressWorktreeList -PorcelainLine $worktreeLines
382
+ $kickoffText = @(Invoke-GitExe -GitArgs @('show', "$($setting.IntegrationBranch):$($setting.KickoffPath)")) -join "`n"
383
+
384
+ $epicIssueText = $setting.EpicIssue
385
+ if ([string]::IsNullOrWhiteSpace($epicIssueText)) { $epicIssueText = Get-EpicProgressIssueFromKickoff -KickoffText $kickoffText }
386
+ if ([string]::IsNullOrWhiteSpace($epicIssueText)) {
387
+ throw "The epic issue number could not be resolved: pass -EpicIssue or add an 'Epic issue: #<n>' line to $($setting.KickoffPath) on $($setting.IntegrationBranch)."
388
+ }
389
+
390
+ $checkpointFeature = @{}
391
+ if (Test-EpicProgressPath -Path $setting.CheckpointPath) {
392
+ $checkpointText = Read-EpicProgressFile -Path $setting.CheckpointPath
393
+ if (Test-EpicProgressJsonText -Json $checkpointText) {
394
+ $checkpoint = ConvertFrom-Json -InputObject $checkpointText
395
+ $checkpointEntries = @($checkpoint.features)
396
+ foreach ($checkpointEntry in $checkpointEntries) {
397
+ if ($null -ne $checkpointEntry -and $null -ne $checkpointEntry.issue_num) { $checkpointFeature["$($checkpointEntry.issue_num)"] = $checkpointEntry }
398
+ }
399
+ }
400
+ }
401
+
402
+ $ghInstalled = Test-EpicProgressCommand -Name 'gh'
403
+ $features = [System.Collections.Generic.List[object]]::new()
404
+ $rows = @(ConvertFrom-EpicProgressKickoffRow -KickoffText $kickoffText)
405
+ foreach ($row in $rows) {
406
+ $checkpointEntry = $checkpointFeature[$row.Issue]
407
+ $branch = ''
408
+ $mergeStatus = 'not_started'
409
+ if ($null -ne $checkpointEntry) {
410
+ if ($checkpointEntry.branch_name) { $branch = [string] $checkpointEntry.branch_name }
411
+ if ($checkpointEntry.merge_status) { $mergeStatus = [string] $checkpointEntry.merge_status }
412
+ }
413
+ $prState = ''
414
+ if ($branch -and $ghInstalled) {
415
+ $prJson = @(Invoke-GhExe -GhArgs @('pr', 'list', '--head', $branch, '--state', 'all', '--json', 'number,state')) -join "`n"
416
+ $prState = ConvertFrom-EpicProgressPrList -Json $prJson
417
+ }
418
+ $planText = $null
419
+ if ($branch -and $worktreeOf.ContainsKey($branch)) {
420
+ $livePath = Join-Path -Path $worktreeOf[$branch] -ChildPath $row.PlanPath
421
+ if (Test-EpicProgressPath -Path $livePath) { $planText = Read-EpicProgressFile -Path $livePath }
422
+ }
423
+ if ($null -eq $planText) { $planText = @(Invoke-GitExe -GitArgs @('show', "$($setting.IntegrationBranch):$($row.PlanPath)")) -join "`n" }
424
+ $count = Get-EpicProgressTaskCount -PlanText $planText
425
+ $features.Add([pscustomobject]@{
426
+ Issue = $row.Issue; Wave = $row.Wave; Band = $row.Band; Total = $count.Total; Done = $count.Done
427
+ State = (Get-EpicProgressFeatureState -MergeStatus $mergeStatus -PrState $prState -Done $count.Done)
428
+ Phase = (Get-EpicProgressCurrentPhase -PlanText $planText); Pr = $prState
429
+ })
430
+ }
431
+
432
+ $weeklyPct = [double]::NaN
433
+ if (Test-EpicProgressCommand -Name 'cswap') {
434
+ $quotaJson = @(Invoke-CswapExe -CswapArgs @('list', '--json')) -join "`n"
435
+ $weeklyPct = Get-EpicProgressWeeklyQuota -Json $quotaJson
436
+ }
437
+
438
+ $nowUnixSeconds = [DateTimeOffset]::new($nowUtc).ToUnixTimeSeconds()
439
+ $done = 0
440
+ foreach ($record in $features) { $done += $record.Done }
441
+ $baselineDoneValue = $baseline.BaselineDone
442
+ if ($baseline.UseCurrentDone) { $baselineDoneValue = $done }
443
+ $selection = Select-EpicProgressSample -Sample $priorSamples -NowUnixSeconds $nowUnixSeconds -Done $done -SampleCap $SampleCap
444
+ $currentSamples = $selection.Sample
445
+ # Pairing is a one-shot rewrite applied after the sample rule (cadence change).
446
+ if ($PairSamples) { $currentSamples = [object[]] @(Merge-EpicProgressSamplePair -Sample $currentSamples) }
447
+ if ($selection.Changed -or $ResetBaseline -or $PairSamples) {
448
+ $startUnixSeconds = [DateTimeOffset]::new($baseline.StartUtc).ToUnixTimeSeconds()
449
+ $stateJson = ConvertTo-EpicProgressStateJson -StartUnixSeconds $startUnixSeconds -BaselineDone $baselineDoneValue -BaselineWeeklyPct $baseline.BaselineWeeklyPct -Sample $currentSamples
450
+ Write-EpicProgressState -Path $setting.StatePath -Content $stateJson
451
+ }
452
+
453
+ $summary = Get-EpicProgressSummary -Feature $features.ToArray() -BaselineDone $baselineDoneValue -StartUtc $baseline.StartUtc -NowUtc $nowUtc -Sample $currentSamples -WaveOverheadHours $WaveOverheadHours -WeeklyPct $weeklyPct -BaselineWeeklyPct $baseline.BaselineWeeklyPct -EpicIssue $epicIssueText -EpicName $EpicFolder
454
+ $lines = [System.Collections.Generic.List[string]]::new()
455
+ $tldrLines = Get-EpicProgressTldrLine -Summary $summary
456
+ $lines.AddRange([string[]] $tldrLines)
457
+ if ($Mode -ne 'TLDR') {
458
+ $fullLines = Get-EpicProgressFullLine -Summary $summary
459
+ $lines.AddRange([string[]] $fullLines)
460
+ }
461
+ return $lines.ToArray()
462
+ }
463
+
464
+ # Entry point: skipped when the script is dot-sourced for testing. Git runs in the
465
+ # repository root, so the location is pushed there for the run and restored afterwards.
466
+ if ($MyInvocation.InvocationName -ne '.') {
467
+ $entryRoot = $RepositoryRoot
468
+ if ([string]::IsNullOrWhiteSpace($entryRoot)) { $entryRoot = (Get-Location).ProviderPath }
469
+ Push-Location -LiteralPath $entryRoot
470
+ try {
471
+ Invoke-EpicProgressReport @PSBoundParameters
472
+ } finally {
473
+ Pop-Location
474
+ }
475
+ }
@@ -13,9 +13,9 @@ Maintenance rule: this brief is updated in the same change that alters any fact
13
13
 
14
14
  ## Development Toolchain Baseline
15
15
 
16
- The host manifest, both devcontainer Dockerfiles, and development CI use Python 3.13.17, Node.js 26.13.0/npm 11.20.0, PowerShell 7.6.9, and Poetry 2.3.2. Shell validation pins shfmt 3.14.1, ShellCheck 0.11.0, and actionlint 1.7.12. Host bootstrap treats manifest versions as minima and preserves equal/newer installations; the container verifier accepts later Python patches within 3.13 and checks the other selected pins.
16
+ The host manifest, both devcontainer Dockerfiles, and development CI select Python 3.13.18, Node.js 26.13.0/npm 11.20.0, PowerShell 7.6.9, and Poetry 2.5.1. Both images use `mcr.microsoft.com/devcontainers/python:3.3.2-3.13-bookworm`, require Git >=2.56.0, and install GitHub CLI 2.103.3, .NET SDK 10.0.403, uv 0.13.7, Claude Code 2.1.318, claude-swap 0.29.0, and Codex 0.166.0. Shell validation pins shfmt 3.14.1, ShellCheck 0.11.0, actionlint 1.7.12, and kcov v43. PowerShell selects Pester 6.2.0 and PSScriptAnalyzer 1.25.0. The images also retain Graphite 1.7.14, GitHub Copilot CLI 0.0.377, Bats, and bashdb. Host bootstrap treats manifest versions as minima and preserves equal/newer installations; the container verifier accepts later Python patches within 3.13 and later Git versions, and checks the other selected pins.
17
17
 
18
- The devcontainer minor image can lag the Python patch, so both Dockerfiles copy the verified official Python runtime stage and select it and NodeSource ahead of older base-feature shims. Full configuration builds include the selected devcontainer features; a raw Dockerfile image alone does not prove feature-provided commands. Runtime verification uses an isolated Linux virtual environment rather than the host Windows environment.
18
+ The pinned base supplies Python and Git; image-managed CLI installs take precedence on PATH. Full configuration builds include the selected devcontainer features. Local Docker retains the Docker outside-of-Docker feature; floating Git/GitHub CLI features do not replace image pins. Both configurations run the same shared post-create script and mandatory verifier. Local source is a live host bind mount with four `volume-nocopy` Linux dependency volumes for `.venv` and the root, extension, and MCP `node_modules`; setup prepares only their mount roots. A container-only Git environment override disables the host Windows fsmonitor without changing host configuration. The former workspace volume is retained for recovery. Runtime verification uses the Linux environment rather than host Windows dependencies. Codex includes its native optional package, disables the startup update prompt through root-owned system settings, and uses the file credential store; the image does not populate user authentication state. Python bytecode stays under `/tmp/pycache`.
19
19
 
20
20
  Python 3.11/3.12 compatibility jobs, consumer package engines, and independent release-publishing runtime pins remain unchanged. The shared version predicate lives in `scripts/bash/container_versions.sh`; `.devcontainer/verify-container.sh` resolves it relative to its own repository location.
21
21
 
@@ -39,7 +39,8 @@ Two separate mechanisms restrict Bash command text. They are kept apart here bec
39
39
 
40
40
  - PoshQC MCP results carry no output. The summary string is composed from a fixed template before the child process runs (`runPoshQcWorkflow` in `extensions/drm-copilot/src/repo-automation-service.ts`), and `RepoAutomationExecutionResult` has no exit-code or output field. An `ok: true` result proves only that the tool was invoked.
41
41
  - Counts, percentages, and findings therefore come from files or direct runs: pass and fail counts from `artifacts/pester/pester-junit.xml`, line coverage from `artifacts/pester/powershell-coverage.xml`, and analyzer findings from a direct `Invoke-ScriptAnalyzer` run with `scripts/powershell/PoshQC/settings/pssa.settings.psd1`.
42
- - The MCP test tool reads the installed extension's settings. Branch-current Pester runs import `scripts/powershell/PoshQC/PoshQC.psd1` and call `Invoke-PoshQCTest` from a `sh`-launched PowerShell 7. The coverage population is derived by `Invoke-PoshQCTest` from `config/poshqc-coverage.json`; there is no hand-maintained coverage path list.
42
+ - The MCP test tool reads the installed extension's settings. Branch-current Pester runs import `scripts/powershell/PoshQC/PoshQC.psd1` and call `Invoke-PoshQCParallelTest` from a `sh`-launched PowerShell 7. The coverage population is derived by `Invoke-PoshQCTest`, which each shard runs, from `config/poshqc-coverage.json`; there is no hand-maintained coverage path list.
43
+ - `Invoke-PoshQCParallelTest` returns an object whose `ExitCode` is read first, and the launcher exits with that value. Merged JUnit and coverage exist only after a fully green run, and a failed run can leave older merged files in place. Failure detail is under `artifacts/pester/parallel/shard-<i>/`.
43
44
  - `Invoke-Pester` without `-EnableExit` (or `Run.Exit`) exits 0 when tests fail; read the JUnit file rather than the exit code of such a run.
44
45
 
45
46
  ## Batch Budget
@@ -88,7 +89,7 @@ Two separate mechanisms restrict Bash command text. They are kept apart here bec
88
89
  - `_shell-coverage.yml` checks out at depth one, so `origin/main` does not exist in that job; no test may diff against it.
89
90
  - The Linux hook-suite job runs `tests/scripts/claude-hooks` and `tests/scripts/codex-hooks` on a case-sensitive file system: use `Join-Path`, forward-slash relative paths, exact file-name case, and no drive letters or backslash literals.
90
91
  - The `.gitattributes` rule `* text=auto eol=lf` normalizes line endings on checkout, so a CRLF fixture committed as text is not CRLF in CI.
91
- - Shard and single-process Pester runs expose module-scope import visibility and leaked state that per-suite local runs hide.
92
+ - Files in one shard share a process, so a leak surfaces when shard contents change.
92
93
 
93
94
  ## Contract Tests That Pin Mirrored Files
94
95
 
@@ -105,6 +106,7 @@ Run these after editing any mirrored or bundled file; each is under `tests/scrip
105
106
  - `test_push_down_issue_824_follow_ups.py`: pushed rules and skills name no consuming product.
106
107
  - `test_typescript_toolchain_instruction_contracts.py`: no reference to another test framework, and every backticked npm script token in the pinned mirrors exists in the root `package.json`.
107
108
  - `tests/scripts/claude-runtime/claude-architecture-doc.Tests.ps1` and `tests/scripts/claude-runtime/hook-dependency-guard-completeness.Tests.ps1`: agent tool names in the architecture document and the guarded dot-source pattern in hooks.
109
+ - `tests/scripts/claude-runtime/parallel-gate-wording.Tests.ps1`: per-file required and forbidden gate wording in the mirrored skills, agents, and templates, and the sweep of those surfaces for removed test-suite baseline wording.
108
110
 
109
111
  ## Preflight Defect Classes the Validator Cannot See
110
112
 
@@ -135,6 +137,7 @@ These pass every validator rule and are found only by reading the plan against t
135
137
  - Python: measure a module with `--cov=<dotted.module>`, `--cov-branch`, `--cov-report=term-missing`, and `--cov-report=json:<path>`; read branch figures from the JSON report.
136
138
  - TypeScript: a new production file needs its own entry in the `coverageThreshold` map of `extensions/drm-copilot/jest.config.cjs`; Jest runs through `node run-jest.cjs` in `extensions/drm-copilot/`.
137
139
  - PowerShell: per-file line coverage is read from `artifacts/pester/powershell-coverage.xml` written by the self-hosted PoshQC module.
140
+ - Per-file figures are read from the single gate run, and no earlier figure is compared.
138
141
  - File size follows three tiers for production, test, and reusable script files, counted in logical lines (a trailing terminator adds no line): at 500 lines look for a logical split (guidance); at 700 lines split more aggressively, and reviewers report a non-blocking Warning (advisory); above 800 lines a split is enforced regardless of logical separability (hard limit; exactly 800 passes). The hard limit is enforced by `tests/scripts/ci/FileSizePolicy.RepoGate.Tests.ps1` in the existing PowerShell shards against `config/file-size-baseline.json`, where legacy files above 800 may shrink (with the entry tightened) or stay equal but may not grow, and by the PreToolUse hook `.claude/hooks/enforce-file-size-limit.ps1` (`.codex/hooks/enforce-file-size-limit.ps1`). New logic in a file near a tier goes into a new module and a new test file. Markdown is exempt.
139
142
 
140
143
  ## Test Runtime
@@ -144,17 +147,19 @@ These pass every validator rule and are found only by reading the plan against t
144
147
  - Fix repeated production work first. An immutable structure, such as a schema registry that is rebuilt and re-verified on every call, is built once per owning instance. This speeds up the runtime as well as the tests.
145
148
  - Second, build an expensive immutable fixture once per session and hand each test its own copy wherever a test could mutate it, so test independence is unchanged.
146
149
  - Parallel workers reduce wall-clock time only: they are capped by the slowest test and leave the cost in the product.
147
- - pytest: pass `-n auto` (pytest-xdist) on the command line of the CI step `Run tests with Pytest` and of the parallel VS Code tasks, never in `addopts`, because the VS Code Test Explorer keys results to the wrong file when `addopts` carries it. `-n auto` counts physical cores, so a runner can start fewer workers than it has vCPUs; measure it against an explicit count such as `-n 4` before keeping it. pytest-cov combines the worker coverage into one report, and the serial `poetry run pytest` command remains the isolation reference.
148
- - Pester: Pester 5 has no in-process parallel mode, so parallel Pester runs are process-per-shard: `Invoke-PoshQCTest -ShardIndex -ShardCount` on the CI runners and `Invoke-PoshQCParallelTest` locally. Read the per-file `time` of the JUnit `testsuite` elements before changing the shard count or the weights in `config/poshqc-shard-weights.json`.
150
+ - pytest: pass `-n auto` (pytest-xdist) on the command line of the CI step `Run tests with Pytest` and of the parallel VS Code tasks, never in `addopts`, because the VS Code Test Explorer keys results to the wrong file when `addopts` carries it. `-n auto` counts physical cores, so a runner can start fewer workers than it has vCPUs; measure it against an explicit count such as `-n 4` before keeping it. pytest-cov combines the worker coverage into one report.
151
+ - Pester: parallel validation uses separate Pester 6 processes: `Invoke-PoshQCTest -ShardIndex -ShardCount` on the CI runners and `Invoke-PoshQCParallelTest` locally. Read the per-file `time` of the JUnit `testsuite` elements before changing the shard count or the weights in `config/poshqc-shard-weights.json`.
149
152
  - A candidate fix can be measured before the tree is edited by loading a pytest plugin module from `SCRATCH` with `-p <module>` and `PYTHONPATH`.
150
153
 
151
154
  ## Test Isolation Checks
152
155
 
153
156
  - Tests create no temporary files; file and transcript reads go through injected seams with in-memory content.
154
- - Before review, run each isolation check and record it under `<FEATURE>/evidence/regression-testing/`:
155
- - a seeded shuffled Jest run (`node run-jest.cjs --randomize --seed=<n>` in `extensions/drm-copilot/`) with the seed recorded;
156
- - a reversed-order run of the new or changed Python test modules in one process;
157
- - a seeded shuffled file-order run of the touched Pester suites in one process, with at least three seeds and each seed and its file order recorded;
158
- - a fresh single-process Pester run over `tests/scripts`, which is the CI shape;
159
- - in addition to the serial runs, a concurrent-shard Pester run through `Invoke-PoshQCParallelTest` and a `pytest -n auto` run, both with zero failures.
160
- - An order dependence found by any run is fixed on both sides: the suite that leaves state behind (globals, functions, environment variables, mocks, imported modules, script-scope state) and the suite that assumes prior state. Every seed is then re-run.
157
+ - The test gates are parallel:
158
+ - the Pester gate is one `Invoke-PoshQCParallelTest` run;
159
+ - the pytest gate is one `pytest -n auto` run;
160
+ - Jest runs through its normal parallel workers.
161
+ - Each gate reaches zero failures and is re-run only after a fix.
162
+ - A focused run of one test file to debug a specific failure is debugging only; it is never a gate or evidence.
163
+ - Serial tasks remain for debugging one failure and are not gates.
164
+ - A leak found by a gate is fixed on both sides: the suite that leaves state behind (globals, functions, environment variables, mocks, imported modules, script-scope state) and the suite that assumes prior state.
165
+ - The parallel gates govern where a policy file names a toolchain test command.
@@ -42,12 +42,21 @@ Always apply:
42
42
  - `code-review-template` for `code-review.<timestamp>.md`
43
43
  - `feature-audit-template` for `feature-audit.<timestamp>.md`
44
44
  - The MCP-resolved asset is the authoritative template source for review artifacts in this workflow.
45
+ - When an installed template older than this change carries baseline fields, each baseline field is recorded as `N/A - no pre-change run`, and the comparison line reads `Baseline: N/A - no pre-change run -> Post-change: <percent>. Change: N/A.`
45
46
 
46
47
  ### No silent fixes
47
48
 
48
49
  - Do not clean up code during review.
49
50
  - If checks fail, document the failure and include exact remediation guidance.
50
51
 
52
+ ### Process-reduction ledger
53
+
54
+ - This rule applies only when `docs/engineering/process-reductions/` exists, so a repository without that folder is unaffected.
55
+ - A change that reduces evidence, plan tasks, gates, or review process adds its ledger entry in the same commit, in the format of that folder's `README.md`.
56
+ - A branch diff that makes such a reduction with no entry is a Blocking finding.
57
+ - The reviewer confirms from the branch log that each reduction is its own commit, with the `reduction: NNNN` token in its subject.
58
+ - One-off waivers for a single item are not entries.
59
+
51
60
  ### Work-mode acceptance-criteria contract
52
61
 
53
62
  - Read the persisted marker from `issue.md` using one of:
@@ -105,17 +114,17 @@ If the branch diff modifies any path matching `.github/workflows/**`, `scripts/b
105
114
  4. tests
106
115
  5. coverage (mandatory for every language that has changed files)
107
116
  - TypeScript: `npm run test:unit:coverage` → artifact: `coverage/lcov.info`
108
- - Python: `poetry run pytest --cov` → artifact: `artifacts/python/lcov.info`
109
- - PowerShell: `mcp__drm-copilot__run_poshqc_test` → artifact: `artifacts/pester/powershell-coverage.xml`
117
+ - Python: `poetry run pytest -n auto --cov-report=term-missing --cov` → artifact: `artifacts/python/lcov.info`
118
+ - PowerShell: one `Invoke-PoshQCParallelTest` run → artifact: `artifacts/pester/powershell-coverage.xml`
110
119
  - C#: `dotnet test --collect:"XPlat Code Coverage"` → artifact: `artifacts/csharp/coverage.xml`
111
- - Coverage thresholds (threshold precedence per `.claude/rules/quality-tiers.md` and `.claude/rules/general-unit-test.md`: when the repository's root `CLAUDE.md` states line or branch coverage thresholds, those thresholds govern; otherwise the default thresholds below govern; each metric falls back independently, so a root `CLAUDE.md` that states only one of the two thresholds governs only that metric). The branch threshold applies only to branch-capable languages — TypeScript, Python, and C#. PowerShell is a coverage language and is fully subject to the line threshold and the no-regression requirement, but Pester measures command (instruction) coverage and line coverage only, so no branch percentage exists to evaluate and no branch threshold applies to it (see `.claude/rules/powershell.md`). Do not flag a missing PowerShell branch figure as FAIL:
120
+ - Coverage thresholds (threshold precedence per `.claude/rules/quality-tiers.md` and `.claude/rules/general-unit-test.md`: when the repository's root `CLAUDE.md` states line or branch coverage thresholds, those thresholds govern; otherwise the default thresholds below govern; each metric falls back independently, so a root `CLAUDE.md` that states only one of the two thresholds governs only that metric). The branch threshold applies only to branch-capable languages — TypeScript, Python, and C#. PowerShell is a coverage language and is fully subject to the line threshold, but Pester measures command (instruction) coverage and line coverage only, so no branch percentage exists to evaluate and no branch threshold applies to it (see `.claude/rules/powershell.md`). Do not flag a missing PowerShell branch figure as FAIL:
112
121
  - New code files (added in this feature): line coverage >= 85%, and branch coverage >= 75% for branch-capable languages. Flag as FAIL otherwise.
113
- - Modified files (changed but previously existing): line coverage >= 85%, branch coverage >= 75% for branch-capable languages, and no regression on changed lines relative to baseline. Flag as FAIL otherwise.
122
+ - Modified files (changed but previously existing): line coverage >= 85%, and branch coverage >= 75% for branch-capable languages, read from the single gate run, and new behavior is tested. Flag as FAIL otherwise.
114
123
  - Repo-wide per language: line coverage >= 85%, and branch coverage >= 75% for branch-capable languages. Flag as FAIL otherwise.
115
124
  - If coverage artifacts already exist from the executor run, inspect them instead of re-running.
116
125
  - If no coverage artifact exists for a language that has changed files, flag as FAIL — coverage verification is mandatory for all languages with changed files.
117
126
  - Tier classification findings: report a missing or incomplete `quality-tiers.yml` only when the repository has adopted tiers, that is, when `quality-tiers.yml` exists at the repository root on the resolved base branch or the repository's CI runs a tier-classification check. Otherwise record tier classification as not applicable; it is not a finding.
118
- - Run the smallest relevant subset first when the repo policy permits it.
127
+ - A focused run of one test file to debug a specific failure is debugging only; a review check does not rest on it.
119
128
  - If a tool cannot run in the environment, mark the affected section unverified or partial with a concrete reason.
120
129
 
121
130
  6. **Produce the code review**
@@ -146,14 +155,17 @@ If the branch diff modifies any path matching `.github/workflows/**`, `scripts/b
146
155
  - toolchain checks fail
147
156
  - the code review contains blockers
148
157
  - required acceptance criteria are FAIL or PARTIAL
149
- - coverage below the governing thresholds defined in step 5 (repo-wide per language, modified files including any regression on changed lines, or new files)
158
+ - coverage below the governing thresholds defined in step 5 (repo-wide per language, modified files, or new files)
150
159
  - coverage artifact absent for any language that has changed files
151
160
  - Classify each blocking finding before triggering remediation: each receives one remediability class (`autonomous`, `external_dependency`, `policy_hold`, `awaiting_ci`, or `human_decision_required`; `autonomous` when no class is stated). A published MCP runtime that lags the repository contract is classified `external_dependency`.
152
161
  - Trigger the remediation handoff only when at least one blocking finding is `autonomous`. When every blocking finding is non-remediable, write the remediation inputs with the `HALT_NON_REMEDIABLE` or `AWAITING_CI` verdict and create no remediation plan target.
162
+ - Choose the remediation path. Write one line `Remediation-Path: light` or `Remediation-Path: full` into the remediation inputs, and inside each blocking finding block one line `Finding-Scope: contained` or `Finding-Scope: design`. A missing path line reads as `full`, and a missing scope line reads as `design`. These lines never contain the substrings the orchestrator counts.
163
+ - Write `Remediation-Path: light` only when all of these hold: at most 3 blocking findings, every one `autonomous` with `Finding-Scope: contained`; no change to a spec acceptance criterion, a public interface, or the file inventory beyond the branch diff plus tests and mirrors; no change to a validator contract, hook decision, or production logic beyond the finding's named location; the cycle is opened by a review; and the prior cycle was not light. Otherwise write `Remediation-Path: full`. The orchestrator may move a cycle to a heavier path and never to a lighter one.
164
+ - With `Remediation-Path: light`, also write a `## Light Fix Tasks` section: one canonical `- [ ] [P0-T1] ...` line per fix, numbered in order, each naming the file and the finding it fixes. Write no checkbox line anywhere else in the file, because the executor stop hook counts every checkbox line of the file it executes.
153
165
  - Create `remediation-inputs.<timestamp>.md` first.
154
- - Create the target remediation plan file from the canonical plan template.
155
- - Hand off plan creation through `remediation-handoff-atomic-planner`.
156
- - Do not report completion unless the remediation plan file exists when remediation was triggered.
166
+ - With `Remediation-Path: full`, create the target remediation plan file from the canonical plan template.
167
+ - With `Remediation-Path: full`, hand off plan creation through `remediation-handoff-atomic-planner`.
168
+ - With `Remediation-Path: full`, do not report completion unless the remediation plan file exists when remediation was triggered. With `Remediation-Path: light`, create no remediation plan target: the inputs file is the light cycle's plan.
157
169
 
158
170
  9. **Finalize the review**
159
171
  - Verify every reported artifact exists on disk before reporting completion.