@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.
- package/out/mcp-server.js +170 -52
- package/package.json +1 -1
- package/resources/claude-customizations/.claude/agents/atomic-executor.md +3 -3
- package/resources/claude-customizations/.claude/agents/atomic-planner.md +2 -2
- package/resources/claude-customizations/.claude/agents/csharp-typed-engineer.md +7 -7
- package/resources/claude-customizations/.claude/agents/feature-review.md +6 -3
- package/resources/claude-customizations/.claude/agents/orchestrator.md +11 -7
- package/resources/claude-customizations/.claude/agents/powershell-typed-engineer.md +7 -7
- package/resources/claude-customizations/.claude/agents/python-typed-engineer.md +6 -6
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-review.ps1 +699 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +16 -1
- package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-rules.ps1 +50 -16
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorState.psm1 +6 -1
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCompletion.psm1 +4 -2
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCompletionChecks.psm1 +51 -2
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateReceipts.psm1 +5 -2
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRemediationAccounting.psm1 +116 -4
- package/resources/claude-customizations/.claude/rules/csharp.md +8 -8
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +28 -2
- package/resources/claude-customizations/.claude/rules/powershell.md +2 -2
- package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +8 -12
- package/resources/claude-customizations/.claude/skills/csharp-qa-gate/SKILL.md +14 -15
- package/resources/claude-customizations/.claude/skills/epic-progress-report/SKILL.md +338 -0
- package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.Logic.ps1 +248 -0
- package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.Metrics.ps1 +232 -0
- package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.Render.ps1 +196 -0
- package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.State.ps1 +286 -0
- package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.ps1 +475 -0
- package/resources/claude-customizations/.claude/skills/execution-environment/SKILL.md +18 -13
- package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +21 -9
- package/resources/claude-customizations/.claude/skills/invoke-csharp-engineer/SKILL.md +5 -5
- package/resources/claude-customizations/.claude/skills/invoke-powershell-engineer/SKILL.md +4 -4
- package/resources/claude-customizations/.claude/skills/invoke-python-engineer/SKILL.md +4 -4
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +24 -2
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +8 -0
- package/resources/claude-customizations/.claude/skills/powershell-qa-gate/SKILL.md +16 -17
- package/resources/claude-customizations/.claude/skills/python-qa-gate/SKILL.md +15 -16
- package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +1 -1
- package/resources/claude-customizations/.claude-variants/csharp-legacy/agents/csharp-typed-engineer.md +6 -6
- package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +13 -14
- package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/invoke-csharp-engineer/SKILL.md +4 -4
- package/resources/claude-customizations/pack-manifests/core.json +7 -0
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +8 -12
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +13 -14
- package/resources/codex-and-agents-customizations/.agents/skills/execution-environment/SKILL.md +18 -13
- package/resources/codex-and-agents-customizations/.agents/skills/feature-review/SKILL.md +5 -1
- package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +18 -5
- package/resources/codex-and-agents-customizations/.agents/skills/invoke-csharp-engineer/SKILL.md +4 -4
- package/resources/codex-and-agents-customizations/.agents/skills/invoke-powershell-engineer/SKILL.md +4 -4
- package/resources/codex-and-agents-customizations/.agents/skills/invoke-python-engineer/SKILL.md +4 -4
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +24 -2
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-state/SKILL.md +16 -1
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +3 -3
- package/resources/codex-and-agents-customizations/.agents/skills/powershell/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.agents/skills/powershell-qa-gate/SKILL.md +15 -16
- package/resources/codex-and-agents-customizations/.agents/skills/python-qa-gate/SKILL.md +14 -15
- package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/SKILL.md +2 -0
- package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +13 -14
- package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/invoke-csharp-engineer/SKILL.md +4 -4
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c1.toml +3 -3
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +3 -3
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +3 -3
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +3 -3
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +3 -3
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +3 -3
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-atomic-executor.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-atomic-planning.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-orchestrator.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c1.toml +6 -6
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c2.toml +6 -6
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3-elevated.toml +6 -6
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3.toml +6 -6
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c4.toml +6 -6
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer.toml +6 -6
- package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-executor.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-planning.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-di-unit-test-engineer.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-orchestrator.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c1.toml +7 -7
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c2.toml +7 -7
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3-elevated.toml +7 -7
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3.toml +7 -7
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c4.toml +7 -7
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer.toml +7 -7
- package/resources/codex-and-agents-customizations/.codex/agents/pytest-unit-test-coding.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-atomic-executor.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/python-atomic-planning.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-execution-only-typed.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-orchestrator.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c1.toml +6 -6
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c2.toml +6 -6
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3-elevated.toml +6 -6
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3.toml +6 -6
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c4.toml +6 -6
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer.toml +6 -6
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate-review.ps1 +699 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +29 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-feature-review-coverage.ps1 +51 -4
- package/resources/codex-and-agents-customizations/.codex-variants/csharp-legacy/agents/csharp-typed-engineer.toml +6 -6
- package/resources/codex-and-agents-customizations/pack-manifests/core.json +1 -0
- package/resources/customizations/.github/agents/5.1-Beast-adjusted.agent.md +1 -1
- package/resources/customizations/.github/agents/Powershell DI Unit Test Engineer.agent.md +20 -26
- package/resources/customizations/.github/agents/atomic_executor.agent.md +4 -4
- package/resources/customizations/.github/agents/atomic_planning.agent.md +4 -4
- package/resources/customizations/.github/agents/commentary-remediation.agent.md +1 -1
- package/resources/customizations/.github/agents/commit-steward.agent.md +1 -1
- package/resources/customizations/.github/agents/csharp-atomic-executor.agent.md +14 -14
- package/resources/customizations/.github/agents/csharp-atomic-planning.agent.md +2 -2
- package/resources/customizations/.github/agents/csharp-orchestrator.agent.md +4 -4
- package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +19 -22
- package/resources/customizations/.github/agents/epic-review.agent.md +1 -1
- package/resources/customizations/.github/agents/gpt-5-beast-mode.agent.md +3 -3
- package/resources/customizations/.github/agents/orchestrator.agent.md +8 -8
- package/resources/customizations/.github/agents/powershell-atomic-executor.agent.md +12 -12
- package/resources/customizations/.github/agents/powershell-atomic-planning.agent.md +4 -4
- package/resources/customizations/.github/agents/powershell-orchestrator.agent.md +6 -6
- package/resources/customizations/.github/agents/powershell-typed-engineer.agent.md +21 -24
- package/resources/customizations/.github/agents/pytest-unit-test-coding.agent.md +13 -17
- package/resources/customizations/.github/agents/python-atomic-executor.agent.md +11 -11
- package/resources/customizations/.github/agents/python-atomic-planning.agent.md +2 -2
- package/resources/customizations/.github/agents/python-execution-only-typed.agent.md +14 -17
- package/resources/customizations/.github/agents/python-orchestrator.agent.md +7 -7
- package/resources/customizations/.github/agents/python-typed-engineer.agent.md +17 -20
- package/resources/customizations/.github/agents/staged-review.agent.md +2 -2
- package/resources/customizations/.github/agents/tdd-green.agent.md +1 -1
- package/resources/customizations/.github/agents/tdd-red.agent.md +3 -3
- package/resources/customizations/.github/agents/tdd-refactor.agent.md +2 -2
- package/resources/customizations/.github/agents/typescript-engineer.agent.md +3 -3
- package/resources/customizations/.github/codex/execute-hard-lock.prompt.md +3 -3
- package/resources/customizations/.github/instructions/general-code-change.instructions.md +1 -1
- package/resources/customizations/.github/instructions/powershell-unit-test.instructions.md +1 -1
- package/resources/customizations/.github/instructions/python-code-change.instructions.md +5 -5
- package/resources/customizations/.github/instructions/python-suppressions.instructions.md +3 -3
- package/resources/customizations/.github/instructions/python-unit-test.instructions.md +1 -1
- package/resources/customizations/.github/prompts/execute-hard-lock.prompt.md +2 -2
- package/resources/customizations/.github/prompts/generate-commit-message-repo.prompt.md +2 -2
- package/resources/customizations/.github/prompts/remediate-comments.prompt.md +2 -2
- package/resources/customizations/.github/prompts/review-feature.prompt.md +1 -1
- package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +8 -12
- package/resources/customizations/.github/skills/execution-environment/SKILL.md +18 -13
- package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +15 -2
- package/resources/feature-templates/bug/plan.yyyy-MM-ddTHH-mm.md +4 -4
- package/resources/feature-templates/feature/plan.yyyy-MM-ddTHH-mm.md +3 -3
- package/resources/feature-templates/refactor/plan.yyyy-MM-ddTHH-mm.md +2 -2
- package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +2 -2
- package/resources/powershell/PoshQC/PoshQC.psm1 +3 -3
- package/resources/powershell/PoshQC/README.md +8 -8
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +2 -2
- package/resources/templates/policy_audit/AGENTS.md +9 -10
- 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
|
|
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
|
|
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-
|
|
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
|
-
-
|
|
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
|
|
148
|
-
- Pester:
|
|
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
|
-
-
|
|
155
|
-
-
|
|
156
|
-
-
|
|
157
|
-
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
-
|
|
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: `
|
|
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
|
|
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,
|
|
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
|
-
-
|
|
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
|
|
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
|
-
-
|
|
155
|
-
-
|
|
156
|
-
-
|
|
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.
|