@danmoisan/drm-copilot-mcp 1.1.25 → 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 (185) 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/parallel-orchestrator.md +7 -3
  9. package/resources/claude-customizations/.claude/agents/parallel-planner.md +6 -2
  10. package/resources/claude-customizations/.claude/agents/powershell-typed-engineer.md +7 -7
  11. package/resources/claude-customizations/.claude/agents/python-typed-engineer.md +6 -6
  12. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-review.ps1 +699 -0
  13. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +16 -1
  14. package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +41 -0
  15. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-cleanup.ps1 +481 -0
  16. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +3 -1
  17. package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier-helpers.ps1 +69 -3
  18. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +2 -2
  19. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.cleanup.ps1 +246 -0
  20. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.docs-only.ps1 +1 -0
  21. package/resources/claude-customizations/.claude/hooks/hook-cleanup-context.ps1 +343 -0
  22. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-rules.ps1 +50 -16
  23. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorState.psm1 +6 -1
  24. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCompletion.psm1 +4 -2
  25. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCompletionChecks.psm1 +51 -2
  26. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateReceipts.psm1 +5 -2
  27. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRemediationAccounting.psm1 +116 -4
  28. package/resources/claude-customizations/.claude/lib/parallel-admission/Invoke-ParallelAdmission.ps1 +626 -0
  29. package/resources/claude-customizations/.claude/lib/parallel-admission/ParallelAdmission.psm1 +508 -0
  30. package/resources/claude-customizations/.claude/lib/parallel-admission/ParallelAdmissionJson.psm1 +141 -0
  31. package/resources/claude-customizations/.claude/lib/parallel-admission/ParallelAdmissionPrecedence.psm1 +508 -0
  32. package/resources/claude-customizations/.claude/rules/csharp.md +8 -8
  33. package/resources/claude-customizations/.claude/rules/orchestrator-state.md +28 -2
  34. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +17 -1
  35. package/resources/claude-customizations/.claude/rules/powershell.md +2 -2
  36. package/resources/claude-customizations/.claude/settings.json +1 -0
  37. package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +9 -13
  38. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +93 -14
  39. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup-worktrees.sh +23 -10
  40. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_preserve_lib.sh +54 -46
  41. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_preserve_merge_lib.sh +234 -0
  42. package/resources/claude-customizations/.claude/skills/csharp-qa-gate/SKILL.md +14 -15
  43. package/resources/claude-customizations/.claude/skills/epic-progress-report/SKILL.md +338 -0
  44. package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.Logic.ps1 +248 -0
  45. package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.Metrics.ps1 +232 -0
  46. package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.Render.ps1 +196 -0
  47. package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.State.ps1 +286 -0
  48. package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.ps1 +475 -0
  49. package/resources/claude-customizations/.claude/skills/execution-environment/SKILL.md +31 -16
  50. package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +21 -9
  51. package/resources/claude-customizations/.claude/skills/invoke-csharp-engineer/SKILL.md +5 -5
  52. package/resources/claude-customizations/.claude/skills/invoke-powershell-engineer/SKILL.md +4 -4
  53. package/resources/claude-customizations/.claude/skills/invoke-python-engineer/SKILL.md +4 -4
  54. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +24 -2
  55. package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +71 -7
  56. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +34 -2
  57. package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +82 -12
  58. package/resources/claude-customizations/.claude/skills/powershell-qa-gate/SKILL.md +16 -17
  59. package/resources/claude-customizations/.claude/skills/python-qa-gate/SKILL.md +15 -16
  60. package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +1 -1
  61. package/resources/claude-customizations/.claude-variants/csharp-legacy/agents/csharp-typed-engineer.md +6 -6
  62. package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +13 -14
  63. package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/invoke-csharp-engineer/SKILL.md +4 -4
  64. package/resources/claude-customizations/pack-manifests/core.json +15 -0
  65. package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +8 -12
  66. package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +1 -1
  67. package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +13 -14
  68. package/resources/codex-and-agents-customizations/.agents/skills/execution-environment/SKILL.md +31 -16
  69. package/resources/codex-and-agents-customizations/.agents/skills/feature-review/SKILL.md +5 -1
  70. package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +18 -5
  71. package/resources/codex-and-agents-customizations/.agents/skills/invoke-csharp-engineer/SKILL.md +4 -4
  72. package/resources/codex-and-agents-customizations/.agents/skills/invoke-powershell-engineer/SKILL.md +4 -4
  73. package/resources/codex-and-agents-customizations/.agents/skills/invoke-python-engineer/SKILL.md +4 -4
  74. package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +24 -2
  75. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-state/SKILL.md +16 -1
  76. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +3 -3
  77. package/resources/codex-and-agents-customizations/.agents/skills/powershell/SKILL.md +2 -2
  78. package/resources/codex-and-agents-customizations/.agents/skills/powershell-qa-gate/SKILL.md +15 -16
  79. package/resources/codex-and-agents-customizations/.agents/skills/python-qa-gate/SKILL.md +14 -15
  80. package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/SKILL.md +2 -0
  81. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +13 -14
  82. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/invoke-csharp-engineer/SKILL.md +4 -4
  83. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c1.toml +3 -3
  84. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +3 -3
  85. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +3 -3
  86. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +3 -3
  87. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +3 -3
  88. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +3 -3
  89. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +2 -2
  90. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +2 -2
  91. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +2 -2
  92. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +2 -2
  93. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +2 -2
  94. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +2 -2
  95. package/resources/codex-and-agents-customizations/.codex/agents/csharp-atomic-executor.toml +2 -2
  96. package/resources/codex-and-agents-customizations/.codex/agents/csharp-atomic-planning.toml +1 -1
  97. package/resources/codex-and-agents-customizations/.codex/agents/csharp-orchestrator.toml +1 -1
  98. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c1.toml +6 -6
  99. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c2.toml +6 -6
  100. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3-elevated.toml +6 -6
  101. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3.toml +6 -6
  102. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c4.toml +6 -6
  103. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer.toml +6 -6
  104. package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +2 -2
  105. package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-executor.toml +2 -2
  106. package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-planning.toml +1 -1
  107. package/resources/codex-and-agents-customizations/.codex/agents/powershell-di-unit-test-engineer.toml +1 -1
  108. package/resources/codex-and-agents-customizations/.codex/agents/powershell-orchestrator.toml +1 -1
  109. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c1.toml +7 -7
  110. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c2.toml +7 -7
  111. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3-elevated.toml +7 -7
  112. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3.toml +7 -7
  113. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c4.toml +7 -7
  114. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer.toml +7 -7
  115. package/resources/codex-and-agents-customizations/.codex/agents/pytest-unit-test-coding.toml +1 -1
  116. package/resources/codex-and-agents-customizations/.codex/agents/python-atomic-executor.toml +2 -2
  117. package/resources/codex-and-agents-customizations/.codex/agents/python-atomic-planning.toml +1 -1
  118. package/resources/codex-and-agents-customizations/.codex/agents/python-execution-only-typed.toml +1 -1
  119. package/resources/codex-and-agents-customizations/.codex/agents/python-orchestrator.toml +1 -1
  120. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c1.toml +6 -6
  121. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c2.toml +6 -6
  122. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3-elevated.toml +6 -6
  123. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3.toml +6 -6
  124. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c4.toml +6 -6
  125. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer.toml +6 -6
  126. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  127. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate-review.ps1 +699 -0
  128. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +29 -2
  129. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-cleanup.ps1 +481 -0
  130. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +4 -2
  131. package/resources/codex-and-agents-customizations/.codex/hooks/hook-cleanup-context.ps1 +343 -0
  132. package/resources/codex-and-agents-customizations/.codex/hooks/validate-feature-review-coverage.ps1 +51 -4
  133. package/resources/codex-and-agents-customizations/.codex-variants/csharp-legacy/agents/csharp-typed-engineer.toml +6 -6
  134. package/resources/codex-and-agents-customizations/pack-manifests/core.json +3 -0
  135. package/resources/customizations/.github/agents/5.1-Beast-adjusted.agent.md +1 -1
  136. package/resources/customizations/.github/agents/Powershell DI Unit Test Engineer.agent.md +20 -26
  137. package/resources/customizations/.github/agents/atomic_executor.agent.md +4 -4
  138. package/resources/customizations/.github/agents/atomic_planning.agent.md +4 -4
  139. package/resources/customizations/.github/agents/commentary-remediation.agent.md +1 -1
  140. package/resources/customizations/.github/agents/commit-steward.agent.md +1 -1
  141. package/resources/customizations/.github/agents/csharp-atomic-executor.agent.md +14 -14
  142. package/resources/customizations/.github/agents/csharp-atomic-planning.agent.md +2 -2
  143. package/resources/customizations/.github/agents/csharp-orchestrator.agent.md +4 -4
  144. package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +19 -22
  145. package/resources/customizations/.github/agents/epic-review.agent.md +1 -1
  146. package/resources/customizations/.github/agents/gpt-5-beast-mode.agent.md +3 -3
  147. package/resources/customizations/.github/agents/orchestrator.agent.md +8 -8
  148. package/resources/customizations/.github/agents/powershell-atomic-executor.agent.md +12 -12
  149. package/resources/customizations/.github/agents/powershell-atomic-planning.agent.md +4 -4
  150. package/resources/customizations/.github/agents/powershell-orchestrator.agent.md +6 -6
  151. package/resources/customizations/.github/agents/powershell-typed-engineer.agent.md +21 -24
  152. package/resources/customizations/.github/agents/pytest-unit-test-coding.agent.md +13 -17
  153. package/resources/customizations/.github/agents/python-atomic-executor.agent.md +11 -11
  154. package/resources/customizations/.github/agents/python-atomic-planning.agent.md +2 -2
  155. package/resources/customizations/.github/agents/python-execution-only-typed.agent.md +14 -17
  156. package/resources/customizations/.github/agents/python-orchestrator.agent.md +7 -7
  157. package/resources/customizations/.github/agents/python-typed-engineer.agent.md +17 -20
  158. package/resources/customizations/.github/agents/staged-review.agent.md +2 -2
  159. package/resources/customizations/.github/agents/tdd-green.agent.md +1 -1
  160. package/resources/customizations/.github/agents/tdd-red.agent.md +3 -3
  161. package/resources/customizations/.github/agents/tdd-refactor.agent.md +2 -2
  162. package/resources/customizations/.github/agents/typescript-engineer.agent.md +3 -3
  163. package/resources/customizations/.github/codex/execute-hard-lock.prompt.md +3 -3
  164. package/resources/customizations/.github/instructions/general-code-change.instructions.md +1 -1
  165. package/resources/customizations/.github/instructions/powershell-unit-test.instructions.md +1 -1
  166. package/resources/customizations/.github/instructions/python-code-change.instructions.md +5 -5
  167. package/resources/customizations/.github/instructions/python-suppressions.instructions.md +3 -3
  168. package/resources/customizations/.github/instructions/python-unit-test.instructions.md +1 -1
  169. package/resources/customizations/.github/prompts/execute-hard-lock.prompt.md +2 -2
  170. package/resources/customizations/.github/prompts/generate-commit-message-repo.prompt.md +2 -2
  171. package/resources/customizations/.github/prompts/remediate-comments.prompt.md +2 -2
  172. package/resources/customizations/.github/prompts/review-feature.prompt.md +1 -1
  173. package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +8 -12
  174. package/resources/customizations/.github/skills/execution-environment/SKILL.md +31 -16
  175. package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +15 -2
  176. package/resources/feature-templates/bug/plan.yyyy-MM-ddTHH-mm.md +4 -4
  177. package/resources/feature-templates/feature/plan.yyyy-MM-ddTHH-mm.md +3 -3
  178. package/resources/feature-templates/refactor/plan.yyyy-MM-ddTHH-mm.md +2 -2
  179. package/resources/powershell/PoshQC/PoshQC.ScanConfig.psm1 +3 -1
  180. package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +14 -12
  181. package/resources/powershell/PoshQC/PoshQC.psm1 +3 -3
  182. package/resources/powershell/PoshQC/README.md +8 -8
  183. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +2 -2
  184. package/resources/templates/policy_audit/AGENTS.md +9 -10
  185. 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
+ }
@@ -11,6 +11,14 @@ This brief states the environment facts that every researcher, planner, prefligh
11
11
 
12
12
  Maintenance rule: this brief is updated in the same change that alters any fact it states. It carries no line numbers and no commit identifiers; it cites files, symbols, headings, job names, and test names. The same text is byte-identical in six locations: `.claude/skills/execution-environment/SKILL.md`, `.agents/skills/execution-environment/SKILL.md`, `.github/skills/execution-environment/SKILL.md`, and their bundled copies under `extensions/drm-copilot/resources/`. Where the Claude and Codex runtimes differ, both paths are named (`.claude/hooks/...` and `.codex/hooks/...`).
13
13
 
14
+ ## Development Toolchain Baseline
15
+
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
+
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
+
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
+
14
22
  ## Tool Routes by Agent
15
23
 
16
24
  - `atomic-executor` has no PowerShell tool. Its `tools:` list is Read, Grep, Glob, Edit, Write, several `Bash(...)` entries, the four `mcp__drm-copilot__run_poshqc_*` tools, and `mcp__drm-copilot__validate_orchestration_artifacts`. A `(pattern)` suffix on a `tools:` entry admits the bare tool, so the executor has Bash, subject to the repository hooks and the runtime guard described under Command-Text Restrictions.
@@ -31,7 +39,8 @@ Two separate mechanisms restrict Bash command text. They are kept apart here bec
31
39
 
32
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.
33
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`.
34
- - 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>/`.
35
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.
36
45
 
37
46
  ## Batch Budget
@@ -50,6 +59,8 @@ Two separate mechanisms restrict Bash command text. They are kept apart here bec
50
59
  - Hooks for an isolated subagent run inside the subagent's worktree with that worktree's hook scripts, which are `main`'s copies. A hook change on a branch is not exercised by isolated subagents until it merges.
51
60
  - Hook behavior on a branch is therefore proven by Pester suites, not by a live subagent run. A live check of a branch hook needs a non-isolated delegation from the item worktree.
52
61
  - The batch-budget hooks evaluate the checkpoint of the target worktree that `Resolve-HookTargetWorktree` resolves for the edited file, so an edit made from a coordinating session into an item worktree is evaluated against the item worktree's checkpoint. The preimplementation gate (#1010) resolves the file path of a Write or Edit call and the `-C` values and absolute operands of `git add` and `git commit` through the same resolver, and denies when the worktree listing fails or names no worktree; its delegation checks still resolve by portable identity. The PR authoring gate (#1011) resolves the branch that a `gh pr create` or `gh pr edit` command names (`--head`, `--branch`, or a `branch:` label) through the same resolver when the command carries no canonical issue number line, and otherwise keeps its portable-identity and current-branch resolution. The other gates that read `artifacts/orchestration/orchestrator-state.json` are unchanged and still use the root that holds the hook script.
62
+ - Cleanup legs (#1026): a `cleanup-merged-worktrees` run writes no checkpoint, so the preimplementation gate (`.claude/hooks/enforce-orchestration-preimplementation-gate-cleanup.ps1` and its `.codex/hooks/` twin), the PR-authoring gate (`.claude/hooks/enforce-pr-author-skill.cleanup.ps1`), and the model-routing gate (`.claude/hooks/enforce-model-routing-receipt.ps1`) carry allow-side cleanup legs, evaluated only where each gate would otherwise deny, that share the pure predicates of `hook-cleanup-context.ps1`. The legs admit a Write of the session-root manifest `artifacts/orchestration/cleanup-worktrees-manifest.json` with valid content (an Edit stays denied), Write or Edit of paths under `docs/` or `.claude/agent-memory/` and of the pr-author receipt inside the consolidation worktree on branch `documentationandmemories`, `git -C <consolidation root> add` and `commit` of those paths, `Agent(pr-author)` with `branch: documentationandmemories`, and `gh pr create --head documentationandmemories --base main` while a fresh valid manifest exists at the session root and the pushed `refs/remotes/origin/documentationandmemories` resolves to the same commit as the consolidation worktree `HEAD`. A preimplementation-gate denial appends a cleanup sub-code (`Cleanup leg not applied`) only after a cleanup leg has identified the consolidation worktree as the target and a later condition fails; a call rejected by a prefilter keeps the unchanged deny text even when its target is the consolidation worktree, as does every other call.
63
+ - Scratch rule (#1026): the preimplementation gate allows a Write or Edit of an absolute local path that lies outside every registered worktree and the session root and under a temporary root, read through the seam `Get-OrchestrationGateTemporaryRoot` (the long-name `GetTempPath()` value and the `TEMP`, `TMP`, and `TMPDIR` values, which can carry the 8.3 short-name spelling). The rule fails closed when any registered worktree lies under a temporary root.
53
64
  - Enforcement hooks do not invoke Python; bash is preferred and PowerShell is acceptable (`tests/scripts/claude-runtime/enforcement-hooks-no-python-invocation.Tests.ps1`).
54
65
 
55
66
  ## MCP Surfaces Run the Installed Extension
@@ -61,14 +72,14 @@ Two separate mechanisms restrict Bash command text. They are kept apart here bec
61
72
  ## CI Shape and Required Checks
62
73
 
63
74
  - `.github/workflows/ci.yml` triggers on `push` to `main` and `development`, on `pull_request` to `main`, `development`, and `epic/**`, on `workflow_dispatch`, and on `merge_group` (`types: [checks_requested]`, added by issue #952), with no path filters. When the merge queue on `main` is enabled, every required check is produced by a `merge_group` run of `ci.yml` as well as by the pull-request run; `.github/workflows/README.md` (`## Triggers`) records the trigger set.
64
- - Jobs in `ci.yml` and their check names: `Change scope` (job `changes`, the change-scope decision below); `CI full run` (job `full-run-marker`, which runs only on a full run, marks that run as a valid base for later decisions, and is not a required check); `quality-checks7 / Code Quality & Tests (<python-version>)` on Python 3.11, 3.12, and 3.13 (`_quality-checks.yml`: Black check, Ruff, Pyright, the Codex generator `--check`, the tier classification, and Pytest on every version; Pytest with line and branch coverage and the coverage-threshold check on the JSON report totals run on 3.13 only); `security-scan / Security Scanning` (its `safety check` step carries `continue-on-error: true` and does not fail the check); `docs-validation / Documentation Validation`; `build-check / Build Package`; `shell-coverage / Shell Coverage (Bats + kcov)`; `drm-copilot-extension-tests / drm-copilot Extension Tests (<os>)` on `ubuntu-latest` and `windows-latest`; `root-typescript-tests / Root TypeScript Tests (<os>)` on `ubuntu-latest` and `windows-latest`; `NPM Audit Gate / npm audit (<manifest>)` (the calling job carries the name `NPM Audit Gate`); and the `poshqc` group below.
75
+ - Jobs in `ci.yml` and their check names: `Change scope` (job `changes`, the change-scope decision below); `CI full run` (job `full-run-marker`, which runs only on a full run, marks that run as a valid base for later decisions, and is not a required check); `quality-checks7 / Code Quality & Tests (<python-version>)` on Python 3.11, 3.12, and 3.13 (`_quality-checks.yml`: Black check, Ruff, Pyright, the Codex generator `--check`, the tier classification, and Pytest on every version; Pytest with line and branch coverage and the coverage-threshold check on the JSON report totals run on 3.13 only); `security-scan / Security Scanning` (its `safety check` step carries `continue-on-error: true` and does not fail the check); `docs-validation / Documentation Validation`; `build-check / Build Package`; `shell-coverage / Shell Coverage (Bats + kcov)`; `drm-copilot-extension-tests / drm-copilot Extension Tests (<os>)` on `ubuntu-latest` and `windows-latest`; `root-typescript-tests / Root TypeScript Tests (<os>)` on `ubuntu-latest` and `windows-latest`; `NPM Audit Gate / npm audit (<manifest>)` (the calling job carries the name `NPM Audit Gate`); the `poshqc` group below; and `CI Required Gate` (job `ci-required-gate`, issue #1074), which needs every other `ci.yml` job, runs under `if: always()` on `ubuntu-latest`, and passes `toJSON(needs)` to `scripts/ci/Invoke-CiRequiredGate.ps1`. The gate is green on a full run only when every needed job is `success`, and on a docs-only run only when `CI full run`, `root-typescript-tests`, and `NPM Audit Gate` are `success` or `skipped` and every other needed job is `success`; a `failure`, `cancelled`, or unknown result, a `Change scope` result other than `success`, and unreadable input are red. `tests/scripts/workflows/CiRequiredGateWorkflow.Tests.ps1` pins its `needs:` set against the other job ids of `ci.yml`.
65
76
  - PowerShell CI (`_poshqc.yml`): `poshqc / PowerShell QC static` (format and analyze on `windows-latest`); the shard matrix `poshqc / PowerShell QC shard 1`, `shard 2`, and `shard 3` (`Invoke-PoshQCTest -ShardIndex -ShardCount` over the scan folders of `config/poshqc-scan.json`, with coverage, on `windows-latest`); the aggregator `poshqc / PowerShell QC`, which requires the static and shard jobs to succeed, verifies shard completeness against a Pester discovery count, and merges shard coverage; and `poshqc / PowerShell hook suites (Linux)`, which runs only `tests/scripts/claude-hooks` and `tests/scripts/codex-hooks` on `ubuntu-latest` with coverage disabled. A new test file under a scanned folder is assigned to a shard automatically and is counted by the completeness check.
66
77
  - Change-scope decision (issue #962): the `Change scope` job runs `scripts/ci/Invoke-CiChangeScope.ps1` and publishes the job output `code`. The decision is `code=false` (docs-only) only on a `pull_request` event from this repository, through one of two comparisons. Green-run base: every file changed since the newest successful `ci.yml` run on the pull-request branch whose `CI full run` job succeeded, and whose head commit is present and an ancestor of the current head, is on the allowlist `config/ci-docs-only-paths.json`; the reason is `DOCS_ONLY_SINCE_<green head sha>`. Merge-base fallback (issue #973): when the green-run step ends with no earlier full green run (`NO_FULL_GREEN_RUN`), a missing base (`BASE_NOT_PRESENT`), or a non-ancestor base (`BASE_NOT_ANCESTOR`), and the pull request's base ref is exactly `main`, the script computes `git merge-base` of the payload base SHA (`BASE_SHA`, from `github.event.pull_request.base.sha`; the base ref arrives as `BASE_REF`) and the head, lists the files changed since that merge base with the same diff arguments, and decides `DOCS_ONLY_SINCE_MERGE_BASE_<merge base sha>` when every changed file is allowlisted. Both comparisons run the diff with rename detection off, so both the old and the new path of a rename are classified. A path is allowlisted when it starts with `docs/features/active/`, `docs/features/archive/`, `docs/features/completed/`, `docs/features/epics/`, `docs/features/parallel/`, `docs/features/potential/`, or `docs/research/`, does not start with `docs/features/templates/` or `docs/features/potential/template.md`, and ends in `.md`, `.txt`, `.log`, `.json`, or `.xml`; the allowlist file itself is not allowlisted. Every other case is a full run (`code=true`): `push`, `merge_group`, and `workflow_dispatch` events, a fork pull request, unreadable run or job data, no earlier full green run or a missing or non-ancestor base when the base ref is not `main`, a merge base that cannot be resolved (`MERGE_BASE_UNRESOLVED`, for example a blank base SHA or a base commit missing from the checkout), a failed ancestry check, an empty diff, any path outside the allowlist, and any error. The decision step carries `continue-on-error: true` and the callers compare the output with `!= 'false'`, so a failed or missing decision also gives a full run.
67
- - Docs-only behavior (`code=false`): `ci.yml` passes `run-heavy: false` to `_quality-checks.yml`, `_security-scan.yml`, `_build-check.yml`, `_poshqc.yml`, `_shell-coverage.yml`, and `_drm-copilot-extension-tests.yml`. Each required job still starts under its full-run check name with every step skipped, so every required context reports `success` without running its suite. In `_poshqc.yml` the aggregator `poshqc / PowerShell QC` runs with every step gated, and `poshqc / PowerShell QC static`, the shard jobs, and `poshqc / PowerShell hook suites (Linux)` are skipped at job level. `docs-validation / Documentation Validation` runs in full. `root-typescript-tests`, `NPM Audit Gate`, and `CI full run` are skipped at job level. A `success` on a docs-only commit is therefore not evidence that the heavy suites pass for that commit; that evidence is the newest run whose `CI full run` job succeeded.
78
+ - Docs-only behavior (`code=false`): `ci.yml` passes `run-heavy: false` to `_quality-checks.yml`, `_security-scan.yml`, `_build-check.yml`, `_poshqc.yml`, `_shell-coverage.yml`, and `_drm-copilot-extension-tests.yml`. Each job that runs with its steps gated still starts under its full-run check name with every step skipped, so it reports `success` without running its suite. In `_poshqc.yml` the aggregator `poshqc / PowerShell QC` runs with every step gated, and `poshqc / PowerShell QC static`, the shard jobs, and `poshqc / PowerShell hook suites (Linux)` are skipped at job level. `docs-validation / Documentation Validation` runs in full. `root-typescript-tests`, `NPM Audit Gate`, and `CI full run` are skipped at job level. Eight currently required contexts therefore never report on a docs-only run: `poshqc / PowerShell QC shard 1`, `poshqc / PowerShell QC shard 2`, `poshqc / PowerShell QC shard 3`, `root-typescript-tests / Root TypeScript Tests (ubuntu-latest)`, `root-typescript-tests / Root TypeScript Tests (windows-latest)`, `NPM Audit Gate / npm audit (.)`, `NPM Audit Gate / npm audit (extensions/drm-copilot)`, and `NPM Audit Gate / npm audit (packages/mcp-server)`, so a docs-only head stays blocked under the 20-context ruleset. `CI Required Gate` passes on a docs-only run, because it accepts `skipped` for exactly those skipped callers. A `success` on a docs-only commit is therefore not evidence that the heavy suites pass for that commit; that evidence is the newest run whose `CI full run` job succeeded.
68
79
  - A commit that changes any path outside the allowlist (any code, workflow, configuration, script, test, or customization file) runs the full set of jobs, and its `CI full run` job marks it as a base for later docs-only decisions. A standalone `workflow_dispatch` of a reusable workflow, or a caller that omits `run-heavy`, also runs in full. `.github/workflows/README.md` (`## Change-Scope Decision (Issue #962)`) records the reason codes and the per-context behavior table.
69
- - Required contexts of ruleset 15241672, confirmed by the orchestrator through `gh api` on 2026-10-16: `drm-copilot-extension-tests / drm-copilot Extension Tests (ubuntu-latest)`, `drm-copilot-extension-tests / drm-copilot Extension Tests (windows-latest)`, `docs-validation / Documentation Validation`, `poshqc / PowerShell QC`, `shell-coverage / Shell Coverage (Bats + kcov)`, `security-scan / Security Scanning`, `quality-checks7 / Code Quality & Tests (3.11)`, `quality-checks7 / Code Quality & Tests (3.12)`, `quality-checks7 / Code Quality & Tests (3.13)`, `build-check / Build Package`.
70
- - Refresh rule: a change that renames a job, adds or removes a matrix entry, or changes the ruleset refreshes this list in the same change, and the orchestrator re-confirms the contexts with a read-only `gh api repos/drmoisan/drm-copilot/rulesets/15241672` call before relying on them.
71
- - Skipped-check semantics: a job skipped by an `if:` condition reports success to a required check; a workflow that does not run at all reports nothing, which leaves the required check pending.
80
+ - Required contexts of ruleset 15241672, read through a read-only `gh api` call on 2026-11-04 (20 contexts, each with `integration_id` 15368): `drm-copilot-extension-tests / drm-copilot Extension Tests (ubuntu-latest)`, `drm-copilot-extension-tests / drm-copilot Extension Tests (windows-latest)`, `docs-validation / Documentation Validation`, `poshqc / PowerShell QC`, `shell-coverage / Shell Coverage (Bats + kcov)`, `security-scan / Security Scanning`, `quality-checks7 / Code Quality & Tests (3.11)`, `quality-checks7 / Code Quality & Tests (3.12)`, `quality-checks7 / Code Quality & Tests (3.13)`, `build-check / Build Package`, `NPM Audit Gate / npm audit (extensions/drm-copilot)`, `NPM Audit Gate / npm audit (.)`, `NPM Audit Gate / npm audit (packages/mcp-server)`, `root-typescript-tests / Root TypeScript Tests (ubuntu-latest)`, `root-typescript-tests / Root TypeScript Tests (windows-latest)`, `poshqc / PowerShell QC shard 1`, `poshqc / PowerShell QC shard 2`, `poshqc / PowerShell QC shard 3`, `poshqc / PowerShell QC static`, `poshqc / PowerShell hook suites (Linux)`. After the owner's post-merge ruleset edit (issue #1074 runbook), the single required context is `CI Required Gate` (`integration_id` 15368), and the individual job contexts remain visible but are no longer required.
81
+ - Refresh rule: a change that renames a job, adds or removes a `ci.yml` job or a matrix entry, or changes the ruleset refreshes this list in the same change, and the orchestrator re-confirms the contexts with a read-only `gh api repos/drmoisan/drm-copilot/rulesets/15241672` call before relying on them. A new `ci.yml` job must also be added to the `needs:` list of `ci-required-gate`; `tests/scripts/workflows/CiRequiredGateWorkflow.Tests.ps1` fails until it is.
82
+ - Skipped-check semantics: a job skipped by an `if:` condition reports success to a required check; a workflow that does not run at all reports nothing, which leaves the required check pending. A matrix job skipped at job level reports no expanded context (its matrix name is never expanded), and a reusable-workflow caller skipped in `ci.yml` reports no nested contexts, so each such required context stays pending.
72
83
  - A pull request into a branch other than `main`, `development`, or `epic/**` gets no CI run; dispatch `ci.yml` manually for it.
73
84
 
74
85
  ## CI-Only Failure Classes
@@ -78,7 +89,7 @@ Two separate mechanisms restrict Bash command text. They are kept apart here bec
78
89
  - `_shell-coverage.yml` checks out at depth one, so `origin/main` does not exist in that job; no test may diff against it.
79
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.
80
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.
81
- - 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.
82
93
 
83
94
  ## Contract Tests That Pin Mirrored Files
84
95
 
@@ -95,6 +106,7 @@ Run these after editing any mirrored or bundled file; each is under `tests/scrip
95
106
  - `test_push_down_issue_824_follow_ups.py`: pushed rules and skills name no consuming product.
96
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`.
97
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.
98
110
 
99
111
  ## Preflight Defect Classes the Validator Cannot See
100
112
 
@@ -125,6 +137,7 @@ These pass every validator rule and are found only by reading the plan against t
125
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.
126
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/`.
127
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.
128
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.
129
142
 
130
143
  ## Test Runtime
@@ -134,17 +147,19 @@ These pass every validator rule and are found only by reading the plan against t
134
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.
135
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.
136
149
  - Parallel workers reduce wall-clock time only: they are capped by the slowest test and leave the cost in the product.
137
- - 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.
138
- - 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`.
139
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`.
140
153
 
141
154
  ## Test Isolation Checks
142
155
 
143
156
  - Tests create no temporary files; file and transcript reads go through injected seams with in-memory content.
144
- - Before review, run each isolation check and record it under `<FEATURE>/evidence/regression-testing/`:
145
- - a seeded shuffled Jest run (`node run-jest.cjs --randomize --seed=<n>` in `extensions/drm-copilot/`) with the seed recorded;
146
- - a reversed-order run of the new or changed Python test modules in one process;
147
- - 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;
148
- - a fresh single-process Pester run over `tests/scripts`, which is the CI shape;
149
- - in addition to the serial runs, a concurrent-shard Pester run through `Invoke-PoshQCParallelTest` and a `pytest -n auto` run, both with zero failures.
150
- - 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.