@danmoisan/drm-copilot-mcp 1.1.25 → 1.1.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/out/mcp-server.js +170 -52
  2. package/package.json +1 -1
  3. package/resources/claude-customizations/.claude/agents/atomic-executor.md +3 -3
  4. package/resources/claude-customizations/.claude/agents/atomic-planner.md +2 -2
  5. package/resources/claude-customizations/.claude/agents/csharp-typed-engineer.md +7 -7
  6. package/resources/claude-customizations/.claude/agents/feature-review.md +6 -3
  7. package/resources/claude-customizations/.claude/agents/orchestrator.md +11 -7
  8. package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +7 -3
  9. package/resources/claude-customizations/.claude/agents/parallel-planner.md +6 -2
  10. package/resources/claude-customizations/.claude/agents/powershell-typed-engineer.md +7 -7
  11. package/resources/claude-customizations/.claude/agents/python-typed-engineer.md +6 -6
  12. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-review.ps1 +699 -0
  13. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +16 -1
  14. package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +41 -0
  15. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-cleanup.ps1 +481 -0
  16. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +3 -1
  17. package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier-helpers.ps1 +69 -3
  18. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +2 -2
  19. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.cleanup.ps1 +246 -0
  20. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.docs-only.ps1 +1 -0
  21. package/resources/claude-customizations/.claude/hooks/hook-cleanup-context.ps1 +343 -0
  22. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-rules.ps1 +50 -16
  23. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorState.psm1 +6 -1
  24. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCompletion.psm1 +4 -2
  25. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCompletionChecks.psm1 +51 -2
  26. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateReceipts.psm1 +5 -2
  27. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRemediationAccounting.psm1 +116 -4
  28. package/resources/claude-customizations/.claude/lib/parallel-admission/Invoke-ParallelAdmission.ps1 +626 -0
  29. package/resources/claude-customizations/.claude/lib/parallel-admission/ParallelAdmission.psm1 +508 -0
  30. package/resources/claude-customizations/.claude/lib/parallel-admission/ParallelAdmissionJson.psm1 +141 -0
  31. package/resources/claude-customizations/.claude/lib/parallel-admission/ParallelAdmissionPrecedence.psm1 +508 -0
  32. package/resources/claude-customizations/.claude/rules/csharp.md +8 -8
  33. package/resources/claude-customizations/.claude/rules/orchestrator-state.md +28 -2
  34. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +17 -1
  35. package/resources/claude-customizations/.claude/rules/powershell.md +2 -2
  36. package/resources/claude-customizations/.claude/settings.json +1 -0
  37. package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +9 -13
  38. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +93 -14
  39. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup-worktrees.sh +23 -10
  40. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_preserve_lib.sh +54 -46
  41. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_preserve_merge_lib.sh +234 -0
  42. package/resources/claude-customizations/.claude/skills/csharp-qa-gate/SKILL.md +14 -15
  43. package/resources/claude-customizations/.claude/skills/epic-progress-report/SKILL.md +338 -0
  44. package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.Logic.ps1 +248 -0
  45. package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.Metrics.ps1 +232 -0
  46. package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.Render.ps1 +196 -0
  47. package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.State.ps1 +286 -0
  48. package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.ps1 +475 -0
  49. package/resources/claude-customizations/.claude/skills/execution-environment/SKILL.md +31 -16
  50. package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +21 -9
  51. package/resources/claude-customizations/.claude/skills/invoke-csharp-engineer/SKILL.md +5 -5
  52. package/resources/claude-customizations/.claude/skills/invoke-powershell-engineer/SKILL.md +4 -4
  53. package/resources/claude-customizations/.claude/skills/invoke-python-engineer/SKILL.md +4 -4
  54. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +24 -2
  55. package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +71 -7
  56. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +34 -2
  57. package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +82 -12
  58. package/resources/claude-customizations/.claude/skills/powershell-qa-gate/SKILL.md +16 -17
  59. package/resources/claude-customizations/.claude/skills/python-qa-gate/SKILL.md +15 -16
  60. package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +1 -1
  61. package/resources/claude-customizations/.claude-variants/csharp-legacy/agents/csharp-typed-engineer.md +6 -6
  62. package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +13 -14
  63. package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/invoke-csharp-engineer/SKILL.md +4 -4
  64. package/resources/claude-customizations/pack-manifests/core.json +15 -0
  65. package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +8 -12
  66. package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +1 -1
  67. package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +13 -14
  68. package/resources/codex-and-agents-customizations/.agents/skills/execution-environment/SKILL.md +31 -16
  69. package/resources/codex-and-agents-customizations/.agents/skills/feature-review/SKILL.md +5 -1
  70. package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +18 -5
  71. package/resources/codex-and-agents-customizations/.agents/skills/invoke-csharp-engineer/SKILL.md +4 -4
  72. package/resources/codex-and-agents-customizations/.agents/skills/invoke-powershell-engineer/SKILL.md +4 -4
  73. package/resources/codex-and-agents-customizations/.agents/skills/invoke-python-engineer/SKILL.md +4 -4
  74. package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +24 -2
  75. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-state/SKILL.md +16 -1
  76. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +3 -3
  77. package/resources/codex-and-agents-customizations/.agents/skills/powershell/SKILL.md +2 -2
  78. package/resources/codex-and-agents-customizations/.agents/skills/powershell-qa-gate/SKILL.md +15 -16
  79. package/resources/codex-and-agents-customizations/.agents/skills/python-qa-gate/SKILL.md +14 -15
  80. package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/SKILL.md +2 -0
  81. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +13 -14
  82. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/invoke-csharp-engineer/SKILL.md +4 -4
  83. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c1.toml +3 -3
  84. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +3 -3
  85. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +3 -3
  86. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +3 -3
  87. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +3 -3
  88. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +3 -3
  89. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +2 -2
  90. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +2 -2
  91. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +2 -2
  92. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +2 -2
  93. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +2 -2
  94. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +2 -2
  95. package/resources/codex-and-agents-customizations/.codex/agents/csharp-atomic-executor.toml +2 -2
  96. package/resources/codex-and-agents-customizations/.codex/agents/csharp-atomic-planning.toml +1 -1
  97. package/resources/codex-and-agents-customizations/.codex/agents/csharp-orchestrator.toml +1 -1
  98. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c1.toml +6 -6
  99. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c2.toml +6 -6
  100. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3-elevated.toml +6 -6
  101. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3.toml +6 -6
  102. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c4.toml +6 -6
  103. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer.toml +6 -6
  104. package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +2 -2
  105. package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-executor.toml +2 -2
  106. package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-planning.toml +1 -1
  107. package/resources/codex-and-agents-customizations/.codex/agents/powershell-di-unit-test-engineer.toml +1 -1
  108. package/resources/codex-and-agents-customizations/.codex/agents/powershell-orchestrator.toml +1 -1
  109. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c1.toml +7 -7
  110. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c2.toml +7 -7
  111. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3-elevated.toml +7 -7
  112. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3.toml +7 -7
  113. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c4.toml +7 -7
  114. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer.toml +7 -7
  115. package/resources/codex-and-agents-customizations/.codex/agents/pytest-unit-test-coding.toml +1 -1
  116. package/resources/codex-and-agents-customizations/.codex/agents/python-atomic-executor.toml +2 -2
  117. package/resources/codex-and-agents-customizations/.codex/agents/python-atomic-planning.toml +1 -1
  118. package/resources/codex-and-agents-customizations/.codex/agents/python-execution-only-typed.toml +1 -1
  119. package/resources/codex-and-agents-customizations/.codex/agents/python-orchestrator.toml +1 -1
  120. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c1.toml +6 -6
  121. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c2.toml +6 -6
  122. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3-elevated.toml +6 -6
  123. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3.toml +6 -6
  124. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c4.toml +6 -6
  125. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer.toml +6 -6
  126. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  127. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate-review.ps1 +699 -0
  128. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +29 -2
  129. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-cleanup.ps1 +481 -0
  130. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +4 -2
  131. package/resources/codex-and-agents-customizations/.codex/hooks/hook-cleanup-context.ps1 +343 -0
  132. package/resources/codex-and-agents-customizations/.codex/hooks/validate-feature-review-coverage.ps1 +51 -4
  133. package/resources/codex-and-agents-customizations/.codex-variants/csharp-legacy/agents/csharp-typed-engineer.toml +6 -6
  134. package/resources/codex-and-agents-customizations/pack-manifests/core.json +3 -0
  135. package/resources/customizations/.github/agents/5.1-Beast-adjusted.agent.md +1 -1
  136. package/resources/customizations/.github/agents/Powershell DI Unit Test Engineer.agent.md +20 -26
  137. package/resources/customizations/.github/agents/atomic_executor.agent.md +4 -4
  138. package/resources/customizations/.github/agents/atomic_planning.agent.md +4 -4
  139. package/resources/customizations/.github/agents/commentary-remediation.agent.md +1 -1
  140. package/resources/customizations/.github/agents/commit-steward.agent.md +1 -1
  141. package/resources/customizations/.github/agents/csharp-atomic-executor.agent.md +14 -14
  142. package/resources/customizations/.github/agents/csharp-atomic-planning.agent.md +2 -2
  143. package/resources/customizations/.github/agents/csharp-orchestrator.agent.md +4 -4
  144. package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +19 -22
  145. package/resources/customizations/.github/agents/epic-review.agent.md +1 -1
  146. package/resources/customizations/.github/agents/gpt-5-beast-mode.agent.md +3 -3
  147. package/resources/customizations/.github/agents/orchestrator.agent.md +8 -8
  148. package/resources/customizations/.github/agents/powershell-atomic-executor.agent.md +12 -12
  149. package/resources/customizations/.github/agents/powershell-atomic-planning.agent.md +4 -4
  150. package/resources/customizations/.github/agents/powershell-orchestrator.agent.md +6 -6
  151. package/resources/customizations/.github/agents/powershell-typed-engineer.agent.md +21 -24
  152. package/resources/customizations/.github/agents/pytest-unit-test-coding.agent.md +13 -17
  153. package/resources/customizations/.github/agents/python-atomic-executor.agent.md +11 -11
  154. package/resources/customizations/.github/agents/python-atomic-planning.agent.md +2 -2
  155. package/resources/customizations/.github/agents/python-execution-only-typed.agent.md +14 -17
  156. package/resources/customizations/.github/agents/python-orchestrator.agent.md +7 -7
  157. package/resources/customizations/.github/agents/python-typed-engineer.agent.md +17 -20
  158. package/resources/customizations/.github/agents/staged-review.agent.md +2 -2
  159. package/resources/customizations/.github/agents/tdd-green.agent.md +1 -1
  160. package/resources/customizations/.github/agents/tdd-red.agent.md +3 -3
  161. package/resources/customizations/.github/agents/tdd-refactor.agent.md +2 -2
  162. package/resources/customizations/.github/agents/typescript-engineer.agent.md +3 -3
  163. package/resources/customizations/.github/codex/execute-hard-lock.prompt.md +3 -3
  164. package/resources/customizations/.github/instructions/general-code-change.instructions.md +1 -1
  165. package/resources/customizations/.github/instructions/powershell-unit-test.instructions.md +1 -1
  166. package/resources/customizations/.github/instructions/python-code-change.instructions.md +5 -5
  167. package/resources/customizations/.github/instructions/python-suppressions.instructions.md +3 -3
  168. package/resources/customizations/.github/instructions/python-unit-test.instructions.md +1 -1
  169. package/resources/customizations/.github/prompts/execute-hard-lock.prompt.md +2 -2
  170. package/resources/customizations/.github/prompts/generate-commit-message-repo.prompt.md +2 -2
  171. package/resources/customizations/.github/prompts/remediate-comments.prompt.md +2 -2
  172. package/resources/customizations/.github/prompts/review-feature.prompt.md +1 -1
  173. package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +8 -12
  174. package/resources/customizations/.github/skills/execution-environment/SKILL.md +31 -16
  175. package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +15 -2
  176. package/resources/feature-templates/bug/plan.yyyy-MM-ddTHH-mm.md +4 -4
  177. package/resources/feature-templates/feature/plan.yyyy-MM-ddTHH-mm.md +3 -3
  178. package/resources/feature-templates/refactor/plan.yyyy-MM-ddTHH-mm.md +2 -2
  179. package/resources/powershell/PoshQC/PoshQC.ScanConfig.psm1 +3 -1
  180. package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +14 -12
  181. package/resources/powershell/PoshQC/PoshQC.psm1 +3 -3
  182. package/resources/powershell/PoshQC/README.md +8 -8
  183. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +2 -2
  184. package/resources/templates/policy_audit/AGENTS.md +9 -10
  185. package/resources/templates/policy_audit/policy-audit.yyyy-MM-ddTHH-mm.md +19 -20
@@ -0,0 +1,234 @@
1
+ #!/usr/bin/env bash
2
+ # cleanup_worktrees_preserve_merge_lib.sh: sourceable staging-decision function group for
3
+ # the preserve pass of the cleanup-worktrees tool. For each preserve record whose target
4
+ # already exists as a regular file on the consolidation branch, it decides whether the
5
+ # source may be copied, whether a three-way union merge may be staged instead, or whether
6
+ # the record must be refused with ACTION|preserve-stage|<target-path>|REFUSED-WOULD-REGRESS.
7
+ #
8
+ # Sourcing contract: this library defines functions only and runs no work at source time.
9
+ # It MUST be sourced before cleanup_worktrees_preserve_lib.sh, whose preserve_plan_one calls
10
+ # preserve_merge_check_duplicate and preserve_merge_decide. Its own git calls go through
11
+ # cleanup_wt_git, defined in cleanup_worktrees_enumerate_lib.sh.
12
+ #
13
+ # Phase-1 write exception. The preserve planning phase writes no index, ref, or working-tree
14
+ # file. The one write it performs is the creation of unreferenced blob objects in the object
15
+ # store: `git hash-object -w` hashes the two merge inputs and `git merge-file --object-id`
16
+ # records the merged result. Nothing refers to those blobs unless the writing phase stages
17
+ # the merged result, and an unreferenced blob is removed by the normal object pruning.
18
+ #
19
+ # Line-comparison rule. Every guard compares lines. Content is split with the
20
+ # `while IFS= read -r line || [[ -n $line ]]` idiom, so an unterminated final line counts as
21
+ # a line, and one trailing carriage return is stripped from each line before comparison, so
22
+ # a CRLF working-tree file and its LF-normalized blob compare equal.
23
+ #
24
+ # No git call in this library passes -f or --force, and no process substitution is passed
25
+ # to git as a file argument: native git on Windows cannot open an MSYS /dev/fd path.
26
+
27
+ preserve_merge_split_lines() {
28
+ # Split one content string into the global array PRESERVE_MERGE_LINES under the
29
+ # line-comparison rule stated in the header. An empty string yields no line at all.
30
+ #
31
+ # Args: $1 = the content.
32
+ PRESERVE_MERGE_LINES=()
33
+ # The carriage return is held in a variable: ANSI-C quoting nested inside a
34
+ # double-quoted expansion is not interpreted consistently across bash versions.
35
+ local line cr=$'\r'
36
+ while IFS= read -r line || [[ -n $line ]]; do
37
+ PRESERVE_MERGE_LINES+=("${line%"$cr"}")
38
+ done < <(printf '%s' "${1-}")
39
+ }
40
+
41
+ preserve_merge_is_subsequence() {
42
+ # Return 0 when every line of the needle appears in the haystack in the same order, not
43
+ # necessarily adjacent, and 1 otherwise. An empty needle is a subsequence of anything.
44
+ #
45
+ # Args: $1 = needle content, $2 = haystack content.
46
+ local -a needle=() hay=()
47
+ preserve_merge_split_lines "${1-}"
48
+ needle=("${PRESERVE_MERGE_LINES[@]}")
49
+ preserve_merge_split_lines "${2-}"
50
+ hay=("${PRESERVE_MERGE_LINES[@]}")
51
+ local n=${#needle[@]} i=0 line
52
+ if ((n == 0)); then
53
+ return 0
54
+ fi
55
+ for line in "${hay[@]}"; do
56
+ if [[ $line == "${needle[$i]}" ]]; then
57
+ i=$((i + 1))
58
+ if ((i == n)); then
59
+ return 0
60
+ fi
61
+ fi
62
+ done
63
+ return 1
64
+ }
65
+
66
+ preserve_merge_extra_within() {
67
+ # Guard G3. Return 0 when multiset(merged) minus multiset(main) is contained in
68
+ # multiset(worktree) minus multiset(base), and 1 otherwise. A merged line that main does
69
+ # not carry is admitted only as one of the worktree's own additions, so a base line that
70
+ # main deleted and the merge re-emitted is refused. Counts are kept per line, so a line
71
+ # the worktree added once may appear at most once beyond main's own copies.
72
+ #
73
+ # Keys carry a fixed prefix because bash refuses an empty associative-array subscript,
74
+ # and a blank line is an ordinary line.
75
+ #
76
+ # Args: $1 = merged content, $2 = main content, $3 = worktree content, $4 = base content.
77
+ local -A extra=() added=()
78
+ local line key
79
+ preserve_merge_split_lines "${1-}"
80
+ for line in "${PRESERVE_MERGE_LINES[@]}"; do
81
+ key="k$line"
82
+ extra["$key"]=$((${extra["$key"]:-0} + 1))
83
+ done
84
+ preserve_merge_split_lines "${2-}"
85
+ for line in "${PRESERVE_MERGE_LINES[@]}"; do
86
+ key="k$line"
87
+ extra["$key"]=$((${extra["$key"]:-0} - 1))
88
+ done
89
+ preserve_merge_split_lines "${3-}"
90
+ for line in "${PRESERVE_MERGE_LINES[@]}"; do
91
+ key="k$line"
92
+ added["$key"]=$((${added["$key"]:-0} + 1))
93
+ done
94
+ preserve_merge_split_lines "${4-}"
95
+ for line in "${PRESERVE_MERGE_LINES[@]}"; do
96
+ key="k$line"
97
+ added["$key"]=$((${added["$key"]:-0} - 1))
98
+ done
99
+ for key in "${!extra[@]}"; do
100
+ if ((${extra["$key"]} > ${added["$key"]:-0} && ${extra["$key"]} > 0)); then
101
+ return 1
102
+ fi
103
+ done
104
+ return 0
105
+ }
106
+
107
+ preserve_merge_refuse() {
108
+ # Refuse one record: emit ACTION|preserve-stage|<target-path>|REFUSED-WOULD-REGRESS on
109
+ # stdout, a `cleanup-worktrees: refused` diagnostic naming the reason on stderr, set
110
+ # PRESERVE_EXIT to 1, and return 1. A refusal is not a hard stop: the remaining records
111
+ # are still planned and the pass exits 1.
112
+ #
113
+ # Args: $1 = target_path, $2 = the reason, which begins with the failed guard's token.
114
+ printf 'ACTION|preserve-stage|%s|REFUSED-WOULD-REGRESS\n' "${1-}"
115
+ printf 'cleanup-worktrees: refused %s: %s\n' "${1-}" "${2-}" >&2
116
+ # Read by preserve_plan and run_preserve across a source boundary shellcheck does not
117
+ # follow, so SC2034 reports it unused here; the suppression is line-scoped.
118
+ # shellcheck disable=SC2034
119
+ PRESERVE_EXIT=1
120
+ return 1
121
+ }
122
+
123
+ preserve_merge_check_duplicate() {
124
+ # Refuse a record whose target_path an earlier record of the same pass already named. A
125
+ # target counts as seen as soon as a record reaches this check, whatever that record's
126
+ # later outcome, so the first record keeps the target and every later one is refused.
127
+ # The seen set is the newline-delimited global PRESERVE_SEEN_TARGETS, which preserve_plan
128
+ # resets at the start of every pass.
129
+ #
130
+ # Args: $1 = target_path. Returns 0 when the target is new, 1 when it was refused.
131
+ local tgt=${1-}
132
+ local seen=${PRESERVE_SEEN_TARGETS:-$'\n'}
133
+ if [[ $seen == *$'\n'"$tgt"$'\n'* ]]; then
134
+ preserve_merge_refuse "$tgt" "duplicate-target: an earlier record in this pass names the same target_path"
135
+ return 1
136
+ fi
137
+ PRESERVE_SEEN_TARGETS="$seen$tgt"$'\n'
138
+ return 0
139
+ }
140
+
141
+ preserve_merge_resolve_base() {
142
+ # Echo the blob id of the source file at the merge base of the worktree HEAD and the
143
+ # consolidation HEAD, or return 1 when no base is available. The base is unavailable
144
+ # when source_path differs from target_path (no git call is made in that case), when
145
+ # the worktree HEAD does not resolve, when the two HEADs have no merge base, or when the
146
+ # file is absent at the merge base. An empty answer from git counts as unavailable.
147
+ #
148
+ # Args: $1 = worktree path, $2 = source_path, $3 = target_path, $4 = consolidation path.
149
+ local wt=${1-} src=${2-} tgt=${3-} cwt=${4-} head="" mb="" oid=""
150
+ if [[ $src != "$tgt" ]]; then
151
+ return 1
152
+ fi
153
+ head=$(cleanup_wt_git -C "$wt" rev-parse --verify --quiet HEAD) || return 1
154
+ [[ -n $head ]] || return 1
155
+ mb=$(cleanup_wt_git -C "$cwt" merge-base "$head" HEAD) || return 1
156
+ [[ -n $mb ]] || return 1
157
+ oid=$(cleanup_wt_git -C "$cwt" rev-parse --verify --quiet "$mb:$src") || return 1
158
+ [[ -n $oid ]] || return 1
159
+ printf '%s\n' "$oid"
160
+ }
161
+
162
+ preserve_merge_guard_b() {
163
+ # Guard (b), the fallback when no merge base is available: admit a whole-file copy only
164
+ # when main's version is a line-subsequence of the source, so no main line is lost.
165
+ #
166
+ # Args: $1 = target_path, $2 = main content, $3 = source content.
167
+ if preserve_merge_is_subsequence "${2-}" "${3-}"; then
168
+ PRESERVE_STAGE_MODE="copy"
169
+ return 0
170
+ fi
171
+ preserve_merge_refuse "${1-}" "guard-b: the target holds lines the source lacks"
172
+ return 1
173
+ }
174
+
175
+ # PRESERVE_STAGE_MODE and PRESERVE_STAGE_OID are read by preserve_plan_one across a source
176
+ # boundary shellcheck does not follow, so SC2034 reports them unused; the suppression is
177
+ # scoped to this one function.
178
+ # shellcheck disable=SC2034
179
+ preserve_merge_decide() {
180
+ # Decide how one record is staged. Sets PRESERVE_STAGE_MODE to `copy` or `merge` and
181
+ # PRESERVE_STAGE_OID to the merged blob id (empty for `copy`), or refuses the record.
182
+ #
183
+ # - target not a regular file: `copy`, with no git call at all;
184
+ # - `untracked` record, or `modified` record with no merge base: guard (b);
185
+ # - `modified` record with a merge base: G1 (the worktree change is insertion-only),
186
+ # then a `git merge-file --union --object-id` merge, then G2 (every main line
187
+ # survives) and G3 (no line beyond the worktree's own additions); `merge` on success.
188
+ #
189
+ # The source and target are read with plain file I/O. The base and merged contents are
190
+ # read from the object store with `cat-file blob`.
191
+ #
192
+ # Args: $1 = change_class, $2 = worktree path, $3 = source_path, $4 = target_path,
193
+ # $5 = consolidation path. Returns 0 when the record may be staged, 1 when it was refused.
194
+ local cls=${1-} wt=${2-} src=${3-} tgt=${4-} cwt=${5-}
195
+ PRESERVE_STAGE_MODE="copy"
196
+ PRESERVE_STAGE_OID=""
197
+ if [[ ! -f "$cwt/$tgt" ]]; then
198
+ return 0
199
+ fi
200
+ local main_c wt_c base_oid base_c main_oid wt_oid merged_oid merged_c mrc=0
201
+ main_c=$(<"$cwt/$tgt")
202
+ wt_c=$(<"$wt/$src")
203
+ if [[ $cls == "untracked" ]] || ! base_oid=$(preserve_merge_resolve_base "$wt" "$src" "$tgt" "$cwt"); then
204
+ preserve_merge_guard_b "$tgt" "$main_c" "$wt_c"
205
+ return
206
+ fi
207
+ base_c=$(cleanup_wt_git -C "$cwt" cat-file blob "$base_oid") ||
208
+ { preserve_merge_refuse "$tgt" "G1: the base blob could not be read" || return 1; }
209
+ if ! preserve_merge_is_subsequence "$base_c" "$wt_c"; then
210
+ preserve_merge_refuse "$tgt" "G1: the worktree removed or changed a base line"
211
+ return 1
212
+ fi
213
+ main_oid=$(cleanup_wt_git -C "$cwt" hash-object -w --path="$tgt" -- "$tgt") || main_oid=""
214
+ wt_oid=$(cleanup_wt_git -C "$wt" hash-object -w --path="$tgt" -- "$src") || wt_oid=""
215
+ merged_oid=$(cleanup_wt_git -C "$cwt" merge-file --union --object-id "$main_oid" "$base_oid" "$wt_oid") || mrc=$?
216
+ if ((mrc != 0)) || [[ -z $main_oid || -z $wt_oid || -z $merged_oid ]]; then
217
+ preserve_merge_refuse "$tgt" "merge-file rc=$mrc: no merged blob was produced"
218
+ return 1
219
+ fi
220
+ merged_c=$(cleanup_wt_git -C "$cwt" cat-file blob "$merged_oid") ||
221
+ { preserve_merge_refuse "$tgt" "G2: the merged blob could not be read" || return 1; }
222
+ if ! preserve_merge_is_subsequence "$main_c" "$merged_c"; then
223
+ preserve_merge_refuse "$tgt" "G2: the merged result lost a main line"
224
+ return 1
225
+ fi
226
+ if ! preserve_merge_extra_within "$merged_c" "$main_c" "$wt_c" "$base_c"; then
227
+ preserve_merge_refuse "$tgt" "G3: the merged result carries a line the worktree did not add"
228
+ return 1
229
+ fi
230
+ PRESERVE_STAGE_MODE="merge"
231
+ PRESERVE_STAGE_OID=$merged_oid
232
+ printf 'cleanup-worktrees: merged %s\n' "$tgt" >&2
233
+ return 0
234
+ }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: csharp-qa-gate
3
- description: Final QA gate for C# changes. Executes the full CSharpier -> .NET Analyzers -> Nullable Analysis -> xUnit toolchain (with architecture tests), compares against a captured baseline, enforces zero-regression deltas, and produces the required reporting block before the agent declares the change complete.
3
+ description: Final QA gate for C# changes. Executes the full CSharpier -> .NET Analyzers -> Nullable Analysis -> MSTest toolchain (with architecture tests), compares static tool results against the static baseline, reads test and coverage results from the single gate run, and produces the required reporting block before the agent declares the change complete.
4
4
  ---
5
5
 
6
6
  # C# QA Gate
@@ -12,14 +12,14 @@ Canonical procedure for the Phase D final quality gate that every C# change must
12
12
  Use this skill when:
13
13
 
14
14
  - `csharp-typed-engineer` is about to declare a change complete.
15
- - An executor has finished applying a planned batch and must verify zero regressions against the baseline captured in Phase A.
15
+ - An executor has finished applying a planned batch and must verify the static tools against the static baseline recorded in Phase A, and the tests in the single gate run.
16
16
  - A reviewer needs to confirm the toolchain was actually run and produced a clean pass.
17
17
 
18
18
  ## Required Inputs
19
19
 
20
20
  Before invoking this gate, the agent must have:
21
21
 
22
- - a baseline record produced in Phase A, containing analyzer findings, compiler/nullable diagnostics, xUnit pass/fail status, and per-file coverage status for the in-scope files,
22
+ - a static baseline record produced in Phase A, containing analyzer findings and compiler/nullable diagnostics for the in-scope files,
23
23
  - the exact list of touched production and test files,
24
24
  - a clean working tree (all planned edits committed to the working copy).
25
25
 
@@ -48,36 +48,35 @@ Analyzer settings (`AnalysisLevel`, `AnalysisMode`, `TreatWarningsAsErrors`, `Nu
48
48
 
49
49
  If the environment prevents running any tool, stop and report the change as **unverified**. Do not declare completion.
50
50
 
51
- ## Delta Requirements (Zero-Regression Hard Gate)
51
+ ## Gate Requirements (Hard Gate)
52
52
 
53
- Compare the final results to the Phase A baseline. All of the following must hold:
53
+ Compare the final static tool results to the static baseline recorded in Phase A, and read test and coverage results from the single gate run. All of the following must hold:
54
54
 
55
55
  - **Analyzer delta**: 0 new findings across the repository.
56
56
  - **Compiler / nullable delta**: 0 new diagnostics across the repository.
57
- - **xUnit delta**: 0 new failing tests.
58
- - **Architecture-test delta**: 0 new failing facts in the `*.ArchitectureTests` project.
59
- - **Per-file coverage delta**: coverage for every touched file is greater than or equal to the baseline for that file.
60
- - **Overall coverage delta** (when the repo enforces it): overall coverage is greater than or equal to the baseline.
57
+ - **MSTest gate**: zero failures in the single gate run.
58
+ - **Architecture tests**: zero failures in the `*.ArchitectureTests` project.
59
+ - **Per-file coverage**: every changed file meets the coverage thresholds, read from the single gate run, and new behavior is tested.
61
60
  - **New modules, classes, or methods**: coverage >= 90% for each new unit introduced in the batch.
62
61
 
63
- If any delta check fails, the agent must revert or fix immediately and rerun the full toolchain. Do not proceed to reporting until all deltas are clean.
62
+ If any check fails, the agent must revert or fix immediately and rerun the full toolchain. Do not proceed to reporting until all checks are clean.
64
63
 
65
64
  ## Required Reporting Block
66
65
 
67
66
  Every completion response must include the following sections:
68
67
 
69
68
  1. **Scope** — exact file list touched in this change.
70
- 2. **Baseline** — analyzer, compiler/nullable, xUnit, and coverage status recorded in Phase A.
69
+ 2. **Baseline** — analyzer and compiler/nullable findings recorded in Phase A.
71
70
  3. **Plan** — design and test-strategy summary, referencing the approved plan.
72
71
  4. **Diffs** — patch-style or full-file replacements for scoped files only.
73
- 5. **QA Gate Results** — analyzer, compiler/nullable, xUnit, architecture-test, and coverage deltas. If any step could not be run, mark the corresponding line **unverified** and state why.
72
+ 5. **QA Gate Results** — analyzer and compiler/nullable deltas, failures in the single gate run and the architecture tests, and per-file coverage against the thresholds. If any step could not be run, mark the corresponding line **unverified** and state why.
74
73
 
75
74
  ## Evidence Storage
76
75
 
77
76
  Persist toolchain output according to `evidence-and-timestamp-conventions`:
78
77
 
79
- - store baseline outputs under `<FEATURE>/evidence/baseline/<timestamp>/`,
80
- - store post-change outputs under `<FEATURE>/evidence/qa-gates/<timestamp>/`,
78
+ - store static baseline outputs under `<FEATURE>/evidence/baseline/<timestamp>/`,
79
+ - store final gate outputs under `<FEATURE>/evidence/qa-gates/<timestamp>/`,
81
80
  - use ISO-8601 UTC timestamps in folder names.
82
81
 
83
82
  This location is canonical per evidence-and-timestamp-conventions and is not overridable.
@@ -89,4 +88,4 @@ The evidence paths must be referenced in the agent's completion message to satis
89
88
 
90
89
  - Do not disable, skip, or narrow any step of the toolchain to reach a clean result.
91
90
  - Do not add analyzer suppressions, `#pragma warning disable`, `#nullable disable`, or test `[Ignore]` attributes to suppress new findings introduced by the change.
92
- - Do not report success based on partial or targeted runs alone. Targeted runs are allowed mid-batch, but the final gate requires a full-solution pass.
91
+ - Do not report success based on partial or targeted runs. A focused run of one test file to debug a specific failure is debugging only; it is never a gate or evidence, and the final gate is the single gate run.
@@ -0,0 +1,338 @@
1
+ ---
2
+ name: epic-progress-report
3
+ description: 'Post recurring epic progress reports from the coordinator session: a four-line TL;DR every 10 minutes and an hourly full status update with a push notification. Use when an epic is running and the owner wants scheduled progress, velocity, and time-remaining reports, or when the reports must be started, reset after a pause, moved to a new cadence, stopped, or recreated after a restart.'
4
+ argument-hint: "[epic-folder]"
5
+ allowed-tools:
6
+ - Read
7
+ - Glob
8
+ - "Bash(pwsh -NoProfile -File .claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.ps1 *)"
9
+ - "Bash(cswap list*)"
10
+ - CronCreate
11
+ - CronList
12
+ - CronDelete
13
+ - PushNotification
14
+ ---
15
+
16
+ # Epic Progress Report
17
+
18
+ This skill runs the epic progress report script and schedules two recurring reports in the
19
+ coordinator's main session: a four-line TL;DR and an hourly full status update. The script is
20
+ `.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.ps1`. It reads the epic
21
+ kickoff, the feature plans, the epic checkpoint, the pull-request state, and the weekly quota,
22
+ prints the report, and keeps its samples and baseline in a state file.
23
+
24
+ ## When to Use This Skill
25
+
26
+ - An epic is running and the owner wants scheduled progress reports.
27
+ - The reports must be started for an epic, or recreated after the session restarted.
28
+ - Work resumed after a pause or an outage and the velocity baseline must be reset.
29
+ - The TL;DR cadence changes (for example from 5 to 10 minutes).
30
+ - The epic is finished and the reports must be stopped.
31
+
32
+ ## Prerequisites
33
+
34
+ - PowerShell 7 and `git`. `gh` and `cswap` are optional: without `gh` the pull-request state
35
+ is empty, and without `cswap` (or without parseable output) the weekly quota prints `n/a`.
36
+ - The epic kickoff at `docs/features/epics/<epic-folder>/epic-kickoff.md` on
37
+ `origin/epic/<epic-folder>-integration`, with a feature-summary table (header first cell
38
+ `issue_num` or `Issue`) and an `Epic issue: #<n>` line. Pass `-EpicIssue` when the line
39
+ is missing.
40
+ - The epic checkpoint `artifacts/orchestration/epic-orchestrator-state.json` under the
41
+ repository root (optional; without it every feature is classified from its plan and PR).
42
+ - Run from the repository root in the coordinator's main session. An agent-isolated worktree
43
+ refuses command text containing `pwsh`, and the scheduled jobs and notifications belong to
44
+ the main session.
45
+
46
+ ## Inputs
47
+
48
+ | Parameter | Default | Meaning |
49
+ |---|---|---|
50
+ | `-EpicFolder` | (mandatory) | Epic folder name under `docs/features/epics/`; also the epic name in the status heading. |
51
+ | `-StartUtc` | stored start | Start of the measurement. Resolution: the passed value, else now with `-ResetBaseline`, else the stored start. With none of these the script stops before any git call with an error naming `-StartUtc` and `-ResetBaseline`. |
52
+ | `-BaselineDone` | stored value, else 0 | Tasks already complete at the start. With `-ResetBaseline` and no passed value, the current done count. |
53
+ | `-BaselineWeeklyPct` | stored value, else 0 | Weekly quota percentage at the start. `-ResetBaseline` does not change it. |
54
+ | `-EpicIssue` | the kickoff's `Epic issue: #<n>` line | Epic issue number printed in the report. |
55
+ | `-IntegrationBranch` | `origin/epic/<epic-folder>-integration` | Branch that holds the kickoff and the plans. |
56
+ | `-KickoffPath` | `docs/features/epics/<epic-folder>/epic-kickoff.md` | Kickoff path on the integration branch. |
57
+ | `-RepositoryRoot` | current location | Repository root in which git runs. |
58
+ | `-CheckpointPath` | `artifacts/orchestration/epic-orchestrator-state.json` | Epic checkpoint under the repository root. |
59
+ | `-StatePath` | `.claude/state/epic-progress-<epic-folder>.json` | State file under the repository root; the directory is created when missing. |
60
+ | `-WaveOverheadHours` | 1.25 | Review and CI hours added per open wave. |
61
+ | `-SampleCap` | 14 | Maximum number of samples kept. |
62
+ | `-Mode` | `Full` | `TLDR` prints the four TL;DR lines; `Full` prints them followed by the full report. |
63
+ | `-ResetBaseline` | off | Discards the stored samples and restarts the baseline now (see the reset section). |
64
+ | `-PairSamples` | off | One-shot: pairs the stored samples for a cadence change (see the cadence section). Cannot be combined with `-ResetBaseline`. |
65
+
66
+ A passed `-StartUtc`, `-BaselineDone`, or `-BaselineWeeklyPct` overrides the stored value and
67
+ is persisted with the next state write.
68
+
69
+ ## Report Formats
70
+
71
+ The wording, order, and punctuation below are the script's output and do not change. Values in
72
+ angle brackets are placeholders; the examples use the fictional epic folder `example-epic` and
73
+ epic issue `42`.
74
+
75
+ TL;DR (both modes print these four lines first):
76
+
77
+ ```text
78
+ **TL;DR:** Epic #<issue> is at <done> of <total> tasks (<pct>%). <done-sentence> <running-sentence>
79
+ **Elapsed:** <H h MM min>, with about <hours> hours left on the wave-parallel estimate. **Weekly** quota is at <weekly>.
80
+ **Avg Velocity:** <avg> tasks / hr. **Recent Velocity:** <recent> tasks / hr since last check-in. (SD of recent velocity: <sd>, from <n> intervals)
81
+ **Estimated time remaining:** <range>
82
+ ```
83
+
84
+ Variants:
85
+
86
+ - done-sentence: `None of the <N> features is done yet.` or `<K> of <N> features are done.`
87
+ - running-sentence: `No feature is running.`, `#<i> is running (<d> of <t>).`, or
88
+ `#<a> (<d> of <t>), #<b> (<d> of <t>) are running.`
89
+ - range: `<HH:MM> hrs (range needs 3 or more check-in intervals)`, `<HH:MM> hrs to <HH:MM> hrs`,
90
+ or `<HH:MM> hrs to no upper bound (average velocity minus 2.05 SD is at or below 0.5 tasks / hr)`.
91
+ - `<pct>`, `<avg>`, `<recent>`, and `<sd>` are rounded to one decimal and `<hours>` to zero
92
+ decimals, with invariant culture and default (to-even) rounding; an integral value prints
93
+ without a decimal (`1`, not `1.0`). `<weekly>` is `<value>%` or `n/a`.
94
+ - Any non-finite value prints `n/a`; no report prints `NaN` or `Infinity`.
95
+
96
+ Example (fictional):
97
+
98
+ ```text
99
+ **TL;DR:** Epic #42 is at 3 of 10 tasks (30%). 1 of 3 features are done. #12345 is running (1 of 5).
100
+ **Elapsed:** 3 h 00 min, with about 10 hours left on the wave-parallel estimate. **Weekly** quota is at 42.5%.
101
+ **Avg Velocity:** 1 tasks / hr. **Recent Velocity:** 1 tasks / hr since last check-in. (SD of recent velocity: 0, from 3 intervals)
102
+ **Estimated time remaining:** 09:22 hrs to 09:39 hrs
103
+ ```
104
+
105
+ Full mode prints the TL;DR lines, then these lines:
106
+
107
+ ```text
108
+
109
+ ## Epic #<issue> <epic-folder>: status at <HH:mm> UTC, <MMM d>
110
+
111
+ **Elapsed:** <H h MM min> since <HH:mm> UTC. **Progress:** <done> of <total> tasks complete, **<pct>%**.
112
+
113
+ | State | Features |
114
+ |---|---|
115
+ | Completed | <completed> |
116
+ | Running | #<i> (wave <w>, <band>): <d> of <t> tasks (<share>%).<phase><pr> |
117
+ | Pending, wave <w> | #<i> (<t>), #<j> (<t>) |
118
+
119
+ | Wave | Children | Tasks | Done |
120
+ |---|---|---|---|
121
+ | <w> | <count> | <tasks> | <done> |
122
+ | **Total** | **<features>** | **<total>** | **<done>** |
123
+
124
+ **Velocity:** <avg> tasks per hour (one child's pace). **Remaining:** <remaining> tasks.
125
+ - Straight line, no parallelism: about <H h MM min> (upper bound).
126
+ - Wave-parallel (longest chain <tasks> tasks plus about <overhead> h per open wave for review and CI): about **<H h MM min>**.
127
+ **Weekly quota (active account):** <weekly> used, <delta> points since the epic started. This pool also covers the other runs in this session, so it is not the epic's cost alone.
128
+ ```
129
+
130
+ Full report contents:
131
+
132
+ - `<completed>` is `None yet` or the completed issues as `#<i>` joined with `, `.
133
+ - One `Running` row per running feature. `<phase>` is ` Now at: <phase>.` when a current
134
+ phase exists, else empty; `<pr>` is ` PR <state>.` when a pull request exists, else empty;
135
+ `<share>` is the feature percentage rounded to zero decimals.
136
+ - One `Pending, wave <w>` row per wave that holds pending features, in ascending wave order.
137
+ - One Wave-table row per wave in ascending order, then the bold Total row.
138
+ - `<overhead>` is `-WaveOverheadHours` unrounded. `<delta>` is the weekly percentage minus
139
+ the weekly baseline, with a leading `+` when it is finite and not negative, else `n/a`.
140
+ - Times use invariant culture and UTC.
141
+
142
+ ## Calculation Rules
143
+
144
+ - Task counts: plan checkboxes `- [ ] [P#-T#]` and `- [x] [P#-T#]` of each feature, read from
145
+ the feature's worktree when its branch has one, otherwise from the plan on the integration
146
+ branch. The feature list, waves, bands, and plan paths come from the kickoff's
147
+ feature-summary table.
148
+ - Feature state: Completed when the checkpoint's `merge_status` is `merged` or
149
+ `worktree_removed`, or the feature PR is merged; Running when any task is done, the
150
+ checkpoint status is past `not_started`, or a PR exists; otherwise Pending.
151
+ - Elapsed: from the start instant to now, clamped at 0; printed as `H h MM min` from rounded
152
+ total minutes.
153
+ - Average velocity: (done now - baseline done) / elapsed hours; 0 when elapsed is not above 0.
154
+ - Straight-line estimate: remaining tasks / average velocity; `n/a` when the average is not
155
+ above 0.
156
+ - Wave-parallel estimate: the sum, over every wave with a non-completed feature, of the largest
157
+ remaining task count among that wave's non-completed features, divided by the average
158
+ velocity, plus `-WaveOverheadHours` (default 1.25 hours) per open wave; `n/a` when the
159
+ average is not above 0.
160
+ - Samples: a check-in sample (Unix epoch seconds, done count) is added when there is no
161
+ previous sample or the previous one is at least 90 seconds old; the newest `-SampleCap`
162
+ (default 14) samples are kept. Epoch seconds are used because `ConvertFrom-Json` converts ISO
163
+ date strings into local-time values.
164
+ - Interval velocity: max(0, change in done) / change in hours, for consecutive samples at
165
+ least 2 minutes apart; closer pairs are skipped.
166
+ - Recent velocity: the latest interval's value; `n/a` without intervals.
167
+ - SD: the sample standard deviation (n-1 denominator) of the interval velocities; `n/a` below
168
+ 3 intervals. The report prints the interval count.
169
+ - Range: below 3 intervals, the wave-parallel estimate followed by
170
+ `(range needs 3 or more check-in intervals)`. With 3 or more intervals the range is the
171
+ average velocity plus and minus 2.05 SD: fast end = critical-path tasks /
172
+ (average + 2.05 x SD) + overhead; slow velocity = average - 2.05 x SD; when the slow
173
+ velocity is above 0.5 tasks per hour the slow end = critical-path tasks / slow velocity +
174
+ overhead, otherwise the slow end prints the no-upper-bound text. Hours print as `HH:MM`.
175
+ - Weekly quota: the seven-day percentage of the active account in `cswap list --json`;
176
+ `n/a` when `cswap` is missing or prints nothing parseable.
177
+ - `n/a`: every printed number passes through a formatter that returns `n/a` for NaN or
178
+ infinity.
179
+
180
+ ## Starting the Reports
181
+
182
+ 1. Run the script once from the repository root to set the baseline. Either pass the start
183
+ and the tasks already done at that start:
184
+
185
+ ```text
186
+ 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
187
+ ```
188
+
189
+ or start the measurement now:
190
+
191
+ ```text
192
+ pwsh -NoProfile -File .claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.ps1 -EpicFolder example-epic -ResetBaseline
193
+ ```
194
+
195
+ The start and baseline are stored in the state file, so later runs need no `-StartUtc`.
196
+ 2. Create the two recurring jobs with `CronCreate`, using the prompts below with
197
+ `<epic-folder>` replaced by the epic folder name.
198
+ 3. Confirm both jobs with `CronList`.
199
+
200
+ ## Scheduling Prompts
201
+
202
+ TL;DR job: cron `*/10 * * * *`, recurring.
203
+
204
+ ```text
205
+ Run `pwsh -NoProfile -File .claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.ps1 -EpicFolder <epic-folder> -Mode TLDR` from the repository root and post its output verbatim as the TL;DR epic update (no push notification). Do not add commentary unless a run is blocked or a child has finished and needs relaying.
206
+ ```
207
+
208
+ Hourly job: cron `7 * * * *`, recurring.
209
+
210
+ ```text
211
+ Run `pwsh -NoProfile -File .claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.ps1 -EpicFolder <epic-folder>` (full mode) from the repository root and post its output as the hourly epic update, then send a PushNotification with the TL;DR line (under 200 characters). Check `cswap list` for the weekly pool; stop and commit at 93%.
212
+ ```
213
+
214
+ Notes on the prompts:
215
+
216
+ - Push-notification text: the first TL;DR output line with every `**` removed. When that text
217
+ is 200 characters or longer, drop the running-features sentence; the remaining text (prefix,
218
+ task counts, percentage, and done-sentence) is well under 200 characters.
219
+ - The prompts omit `-StartUtc`, `-BaselineDone`, `-ResetBaseline`, and `-PairSamples`. The
220
+ baseline is read from the state file, so a later reset is never overridden by a stale
221
+ scheduled argument, and pairing is never repeated by a recurring job.
222
+ - Cron times are local time, not UTC.
223
+ - Jitter: a 10-minute job fires up to 5 minutes late and an hourly job up to 30 minutes late,
224
+ by a constant delay per job.
225
+
226
+ ## Resetting the Baseline After a Pause
227
+
228
+ Whenever work resumes after a pause or an outage, run the script once with `-ResetBaseline`:
229
+
230
+ ```text
231
+ pwsh -NoProfile -File .claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.ps1 -EpicFolder example-epic -ResetBaseline
232
+ ```
233
+
234
+ - The stored samples are discarded, the start becomes now, and the baseline done count becomes
235
+ the current done count. Pass `-StartUtc` and `-BaselineDone` with `-ResetBaseline` when the
236
+ resume instant is in the past.
237
+ - The weekly-quota baseline is kept, so the Full report's points since the epic started stay
238
+ accurate.
239
+ - The scheduled prompts are not changed.
240
+ - The first reports after a reset show `0 h 00 min` elapsed and `n/a` estimates, and the
241
+ estimates are unreliable for about two hours.
242
+ - Reason: earlier measures include the paused time. A 9-hour outage once distorted the averages
243
+ until the baseline was reset.
244
+
245
+ ## Changing the Cadence
246
+
247
+ To move the TL;DR job to a new interval (for example from 5 to 10 minutes):
248
+
249
+ 1. Run the script once with `-PairSamples`. It keeps the newest sample and every second older
250
+ sample, so the stored intervals match the doubled cadence. 14 samples become 7.
251
+ 2. Delete the TL;DR job with `CronDelete` and create it again with `CronCreate` at the new
252
+ interval.
253
+
254
+ `-PairSamples` is a one-shot command: each run halves the samples again. Never put it in a
255
+ scheduled prompt.
256
+
257
+ Worked example from recorded samples: thirteen 5-minute intervals gave mean 26.2 and SD 31.4
258
+ tasks per hour; after pairing, six 10-minute intervals give mean 27.3 and SD 23.2. The
259
+ conversation that defined the reports quoted 31.5 for the 5-minute SD; the recorded samples
260
+ give 31.4 (unrounded 31.4495).
261
+
262
+ ## Stopping the Reports
263
+
264
+ 1. Run `CronList` and note the IDs of the TL;DR and hourly jobs.
265
+ 2. Run `CronDelete` for each report job ID.
266
+ 3. Keep the state file for a later restart of the reports, or delete it.
267
+
268
+ ## After a Restart
269
+
270
+ - `CronCreate` jobs are session-only: they fire only while the session runs and is idle, and a
271
+ missed run fires once when the session becomes idle again.
272
+ - `claude --resume` or `claude --continue` restores jobs that have not expired.
273
+ - Recurring jobs expire 7 days after creation; recreate both before the 7-day expiry.
274
+ - Run `CronList` and recreate only the missing jobs with the prompts above.
275
+ - If work paused during the restart, reset the baseline first.
276
+ - The state file survives under `.claude/state/` wherever the checkout persists, so the
277
+ baseline and samples are kept.
278
+
279
+ ## State File
280
+
281
+ - Location: `.claude/state/epic-progress-<epic-folder>.json` under the repository root.
282
+ `-StatePath` overrides it.
283
+ - The state is never committed: `.claude/state/` is gitignored in this repository and the
284
+ push-down adds it to a destination's `.gitignore`.
285
+ - The state is per checkout: a different worktree or `-RepositoryRoot` starts a separate
286
+ history. Run from the same root, or pass `-StatePath`.
287
+ - `git clean -fdX` deletes it.
288
+ - A state file under the system temporary directory, written by an earlier version, is not
289
+ migrated: pass its path once with `-StatePath`, or reset the baseline.
290
+ - The legacy format (a JSON array of samples) is still read. The current document is:
291
+
292
+ ```json
293
+ {
294
+ "version": 1,
295
+ "startUnixSeconds": 1767603600,
296
+ "baselineDone": 0,
297
+ "baselineWeeklyPct": 0,
298
+ "samples": [ { "ts": 1767614400, "done": 3 } ]
299
+ }
300
+ ```
301
+
302
+ - Blank text, unparseable text, or a version other than 1 is read as empty state; a missing
303
+ field is absent.
304
+ - The state is written when a sample is added, or when `-ResetBaseline` or `-PairSamples` is
305
+ given. A run within 90 seconds of the last sample with neither switch writes nothing.
306
+
307
+ ## Known Behaviors and Measured Rates
308
+
309
+ - Plan checkboxes tick at batch boundaries, so 5-minute interval velocity is bursty: 0 to
310
+ about 150 tasks per hour, with an SD of about 30 to 45 around a mean near 26.
311
+ - Aggregate throughput with three or four features in parallel is about 25 to 30 tasks per
312
+ hour. Per-feature pace is about 9 to 13 tasks per hour; a lone feature, including its review
313
+ and PR tail, about 4.
314
+ - The wave-parallel estimate is unreliable for about two hours after a baseline: it swung
315
+ between 35 and 110 hours in the first hour, and once estimated 26 to 30 hours for about 10
316
+ active hours.
317
+ - Task totals drift when planners revise plans, so read the percentage against the live total.
318
+ - Flat stretches of 20 to 60 minutes are normal during review, PR, CI, and merge.
319
+ - The tail from plan complete to merge is 0.8 to 4 hours per feature, and each remediation
320
+ cycle adds 45 to 90 minutes.
321
+ - Weekly quota burn is 3 to 7 points per hour (4.3 to 7.6 tasks per point).
322
+ - The hourly Full run also records a sample when it runs more than 90 seconds after the
323
+ previous one. This adds a shorter interval about once an hour and raises the SD.
324
+ - A negative average (done below the baseline after a plan revision) makes the straight-line
325
+ and wave-parallel estimates `n/a`, while the range can print negative or very large values.
326
+ - Integral velocities print without a decimal (`1`, not `1.0`).
327
+
328
+ ## Notes
329
+
330
+ - Delivery: the push-down serves the installed extension payload, so this skill reaches a
331
+ destination repository only after the extension is rebuilt and reinstalled.
332
+ - Quota: the 93% stop in the hourly prompt is coordinator policy. The commit-before-the-wall
333
+ procedure is governed by `.claude/skills/quota-throttling/SKILL.md`.
334
+ - The script is read-only toward git and GitHub: it runs only `git fetch`, `git show`,
335
+ `git worktree list`, `gh pr list`, and `cswap list --json`, and the only file it writes is
336
+ the state file.
337
+ - In this repository, `scripts/dev-tools/Get-EpicProgressReport.ps1` forwards every argument
338
+ to the canonical script; the skill always invokes the canonical script.