@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.
- 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/parallel-orchestrator.md +7 -3
- package/resources/claude-customizations/.claude/agents/parallel-planner.md +6 -2
- 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/enforce-model-routing-receipt.ps1 +41 -0
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-cleanup.ps1 +481 -0
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +3 -1
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier-helpers.ps1 +69 -3
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +2 -2
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.cleanup.ps1 +246 -0
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.docs-only.ps1 +1 -0
- package/resources/claude-customizations/.claude/hooks/hook-cleanup-context.ps1 +343 -0
- 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/lib/parallel-admission/Invoke-ParallelAdmission.ps1 +626 -0
- package/resources/claude-customizations/.claude/lib/parallel-admission/ParallelAdmission.psm1 +508 -0
- package/resources/claude-customizations/.claude/lib/parallel-admission/ParallelAdmissionJson.psm1 +141 -0
- package/resources/claude-customizations/.claude/lib/parallel-admission/ParallelAdmissionPrecedence.psm1 +508 -0
- 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/parallel-orchestration.md +17 -1
- package/resources/claude-customizations/.claude/rules/powershell.md +2 -2
- package/resources/claude-customizations/.claude/settings.json +1 -0
- package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +9 -13
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +93 -14
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup-worktrees.sh +23 -10
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_preserve_lib.sh +54 -46
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_preserve_merge_lib.sh +234 -0
- 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 +31 -16
- 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-add/SKILL.md +71 -7
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +34 -2
- package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +82 -12
- 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 +15 -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 +31 -16
- 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/enforce-orchestration-preimplementation-gate-cleanup.ps1 +481 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +4 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-cleanup-context.ps1 +343 -0
- 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 +3 -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 +31 -16
- 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.ScanConfig.psm1 +3 -1
- package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +14 -12
- 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
|
+
}
|
|
@@ -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-
|
|
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`);
|
|
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
|
|
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,
|
|
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
|
-
-
|
|
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
|
|
138
|
-
- 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`.
|
|
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
|
-
-
|
|
145
|
-
-
|
|
146
|
-
-
|
|
147
|
-
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
-
|
|
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.
|