@danmoisan/drm-copilot-mcp 1.1.18 → 1.1.20

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 (264) hide show
  1. package/out/mcp-server.js +1149 -661
  2. package/package.json +3 -2
  3. package/resources/claude-customizations/.claude/agents/atomic-executor.md +28 -6
  4. package/resources/claude-customizations/.claude/agents/atomic-planner.md +20 -2
  5. package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +17 -4
  6. package/resources/claude-customizations/.claude/agents/epic-planner.md +5 -4
  7. package/resources/claude-customizations/.claude/agents/feature-review.md +6 -4
  8. package/resources/claude-customizations/.claude/agents/orchestrator.md +9 -5
  9. package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +35 -13
  10. package/resources/claude-customizations/.claude/agents/parallel-planner.md +9 -6
  11. package/resources/claude-customizations/.claude/agents/pr-author.md +15 -2
  12. package/resources/claude-customizations/.claude/agents/prd-feature.md +1 -1
  13. package/resources/claude-customizations/.claude/agents/task-researcher.md +8 -0
  14. package/resources/claude-customizations/.claude/hooks/check-powershell-test-purity.ps1 +7 -1
  15. package/resources/claude-customizations/.claude/hooks/check-python-test-purity.ps1 +7 -1
  16. package/resources/claude-customizations/.claude/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
  17. package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +111 -46
  18. package/resources/claude-customizations/.claude/hooks/enforce-completion-helpers.ps1 +105 -0
  19. package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +8 -1
  20. package/resources/claude-customizations/.claude/hooks/enforce-epic-invocation-origin.ps1 +7 -1
  21. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-command.ps1 +190 -0
  22. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-resolution.ps1 +116 -59
  23. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +78 -77
  24. package/resources/claude-customizations/.claude/hooks/enforce-epic-wave-barrier.ps1 +74 -75
  25. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate-resolution.ps1 +127 -35
  26. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +61 -57
  27. package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +7 -1
  28. package/resources/claude-customizations/.claude/hooks/enforce-feature-folder-order.ps1 +129 -39
  29. package/resources/claude-customizations/.claude/hooks/enforce-mermaid-validation.ps1 +7 -1
  30. package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +9 -3
  31. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +39 -51
  32. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
  33. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
  34. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
  35. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +17 -12
  36. package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +9 -3
  37. package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier.ps1 +65 -50
  38. package/resources/claude-customizations/.claude/hooks/enforce-parallel-drift-gate.ps1 +49 -42
  39. package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +54 -72
  40. package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +8 -2
  41. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-command-allowlist.ps1 +279 -0
  42. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +129 -75
  43. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.artifact-root.ps1 +233 -0
  44. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.docs-only.ps1 +224 -0
  45. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +69 -24
  46. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner-helpers.ps1 +29 -18
  47. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +10 -4
  48. package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +14 -5
  49. package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +8 -2
  50. package/resources/claude-customizations/.claude/hooks/feature-folder-resolution.ps1 +373 -0
  51. package/resources/claude-customizations/.claude/hooks/feature-review-coverage-thresholds.ps1 +85 -0
  52. package/resources/claude-customizations/.claude/hooks/hook-command-heredoc.ps1 +119 -0
  53. package/resources/claude-customizations/.claude/hooks/hook-command-invocation-operands.ps1 +198 -0
  54. package/resources/claude-customizations/.claude/hooks/hook-command-invocation.ps1 +254 -255
  55. package/resources/claude-customizations/.claude/hooks/hook-command-payload-powershell.ps1 +201 -0
  56. package/resources/claude-customizations/.claude/hooks/hook-command-payload.ps1 +484 -0
  57. package/resources/claude-customizations/.claude/hooks/hook-command-scanner.ps1 +27 -110
  58. package/resources/claude-customizations/.claude/hooks/hook-dependency-guard.ps1 +114 -0
  59. package/resources/claude-customizations/.claude/hooks/hook-docs-only-diff.ps1 +497 -0
  60. package/resources/claude-customizations/.claude/hooks/premortem-section.ps1 +149 -0
  61. package/resources/claude-customizations/.claude/hooks/subagentstop-envelope.ps1 +192 -0
  62. package/resources/claude-customizations/.claude/hooks/validate-bash.ps1 +9 -3
  63. package/resources/claude-customizations/.claude/hooks/validate-discovery-artifact-gate.ps1 +40 -42
  64. package/resources/claude-customizations/.claude/hooks/validate-executor-output.ps1 +267 -113
  65. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-metrics.ps1 +180 -0
  66. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-rules.ps1 +195 -0
  67. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage.ps1 +141 -395
  68. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-resolution.ps1 +311 -0
  69. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-routing.ps1 +170 -0
  70. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +115 -190
  71. package/resources/claude-customizations/.claude/hooks/validate-planner-output.ps1 +77 -33
  72. package/resources/claude-customizations/.claude/hooks/validate-pr-author-output.ps1 +39 -44
  73. package/resources/claude-customizations/.claude/hooks/validate-prd-feature-output.ps1 +43 -14
  74. package/resources/claude-customizations/.claude/hooks/validate-required-artifact-output.ps1 +66 -36
  75. package/resources/claude-customizations/.claude/hooks/validate-task-researcher-output.ps1 +52 -36
  76. package/resources/claude-customizations/.claude/lib/bash/mutate-parallel-run.sh +191 -0
  77. package/resources/claude-customizations/.claude/lib/bash/parallel-cohorts.sh +13 -3
  78. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-cli.sh +152 -0
  79. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-ops.sh +193 -0
  80. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation.sh +322 -0
  81. package/resources/claude-customizations/.claude/lib/bash/remove-parallel-item.sh +146 -0
  82. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +23 -33
  83. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusTokenShape.psm1 +86 -6
  84. package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-CiGateParser.ps1 +91 -19
  85. package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-MergeConfirmationParser.ps1 +203 -0
  86. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateEpicWaveBarrier.psm1 +346 -0
  87. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1 +24 -3
  88. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +79 -2
  89. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1 +3 -3
  90. package/resources/claude-customizations/.claude/rules/architecture-boundaries.md +5 -5
  91. package/resources/claude-customizations/.claude/rules/csharp.md +2 -2
  92. package/resources/claude-customizations/.claude/rules/general-code-change.md +1 -1
  93. package/resources/claude-customizations/.claude/rules/general-unit-test.md +3 -2
  94. package/resources/claude-customizations/.claude/rules/orchestrator-state.md +37 -4
  95. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +50 -5
  96. package/resources/claude-customizations/.claude/rules/plan-acceptance-gates.md +85 -10
  97. package/resources/claude-customizations/.claude/rules/quality-tiers.md +11 -7
  98. package/resources/claude-customizations/.claude/rules/shell.md +10 -8
  99. package/resources/claude-customizations/.claude/rules/typescript.md +1 -1
  100. package/resources/claude-customizations/.claude/settings.json +3 -4
  101. package/resources/claude-customizations/.claude/skills/acceptance-criteria-tracking/SKILL.md +1 -1
  102. package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +52 -16
  103. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +7 -3
  104. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_detached_lib.sh +1 -1
  105. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_enumerate_lib.sh +2 -0
  106. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_scan_helper.sh +2 -0
  107. package/resources/claude-customizations/.claude/skills/csharp-change-budget-router/SKILL.md +2 -2
  108. package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +45 -9
  109. package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +5 -3
  110. package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
  111. package/resources/claude-customizations/.claude/skills/execution-environment/SKILL.md +134 -0
  112. package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +1 -1
  113. package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +4 -3
  114. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +36 -15
  115. package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +52 -14
  116. package/resources/claude-customizations/.claude/skills/parallel-close/SKILL.md +22 -9
  117. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +108 -21
  118. package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +63 -7
  119. package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +35 -12
  120. package/resources/claude-customizations/.claude/skills/powershell-change-budget-router/SKILL.md +1 -1
  121. package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +3 -1
  122. package/resources/claude-customizations/.claude/skills/python-change-budget-router/SKILL.md +1 -1
  123. package/resources/claude-customizations/.claude/skills/quota-throttling/SKILL.md +1 -1
  124. package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +16 -8
  125. package/resources/claude-customizations/.claude/skills/research-issue/SKILL.md +6 -0
  126. package/resources/claude-customizations/pack-manifests/core.json +27 -0
  127. package/resources/codex-and-agents-customizations/.agents/skills/README.md +1 -0
  128. package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +1 -1
  129. package/resources/codex-and-agents-customizations/.agents/skills/architecture-boundaries/SKILL.md +5 -5
  130. package/resources/codex-and-agents-customizations/.agents/skills/atomic-executor/SKILL.md +19 -7
  131. package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +43 -4
  132. package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +18 -6
  133. package/resources/codex-and-agents-customizations/.agents/skills/csharp/SKILL.md +2 -2
  134. package/resources/codex-and-agents-customizations/.agents/skills/csharp-change-budget-router/SKILL.md +2 -2
  135. package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +2 -2
  136. package/resources/codex-and-agents-customizations/.agents/skills/epic-orchestrate/SKILL.md +16 -1
  137. package/resources/codex-and-agents-customizations/.agents/skills/epic-plan/SKILL.md +17 -1
  138. package/resources/codex-and-agents-customizations/.agents/skills/epic-run/SKILL.md +2 -1
  139. package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
  140. package/resources/codex-and-agents-customizations/.agents/skills/execution-environment/SKILL.md +134 -0
  141. package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +13 -5
  142. package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +1 -1
  143. package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +3 -2
  144. package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +27 -8
  145. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +1 -1
  146. package/resources/codex-and-agents-customizations/.agents/skills/powershell-change-budget-router/SKILL.md +1 -1
  147. package/resources/codex-and-agents-customizations/.agents/skills/python-change-budget-router/SKILL.md +1 -1
  148. package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +11 -7
  149. package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +7 -3
  150. package/resources/codex-and-agents-customizations/.agents/skills/research-issue/SKILL.md +6 -0
  151. package/resources/codex-and-agents-customizations/.agents/skills/translate-claude-to-codex/SKILL.md +1 -1
  152. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp/SKILL.md +2 -2
  153. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +2 -2
  154. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c1.toml +21 -6
  155. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +21 -6
  156. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +21 -6
  157. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +21 -6
  158. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +21 -6
  159. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +21 -6
  160. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +15 -2
  161. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +15 -2
  162. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +15 -2
  163. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +15 -2
  164. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +15 -2
  165. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +15 -2
  166. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planning.toml +1 -1
  167. package/resources/codex-and-agents-customizations/.codex/agents/csharp-atomic-planning.toml +1 -1
  168. package/resources/codex-and-agents-customizations/.codex/agents/epic-orchestrator.toml +4 -1
  169. package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +6 -6
  170. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c1.toml +7 -4
  171. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +7 -4
  172. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +7 -4
  173. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +7 -4
  174. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +7 -4
  175. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +7 -4
  176. package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-planning.toml +1 -1
  177. package/resources/codex-and-agents-customizations/.codex/agents/python-atomic-planning.toml +1 -1
  178. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c1.toml +6 -0
  179. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c2.toml +6 -0
  180. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c3-elevated.toml +6 -0
  181. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c3.toml +6 -0
  182. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c4.toml +6 -0
  183. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher.toml +6 -0
  184. package/resources/codex-and-agents-customizations/.codex/codex-web-setup.sh +318 -273
  185. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  186. package/resources/codex-and-agents-customizations/.codex/hooks/check-powershell-test-purity.ps1 +7 -1
  187. package/resources/codex-and-agents-customizations/.codex/hooks/check-python-test-purity.ps1 +7 -1
  188. package/resources/codex-and-agents-customizations/.codex/hooks/codex-epic-child-launch-attestation.ps1 +1 -1
  189. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
  190. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-codex-model-routing.ps1 +8 -2
  191. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +97 -44
  192. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-helpers.ps1 +105 -0
  193. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-child-worktree-binding.ps1 +7 -1
  194. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate-command.ps1 +190 -0
  195. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +54 -43
  196. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +6 -0
  197. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-root-invocation.ps1 +7 -1
  198. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-wave-barrier.ps1 +7 -1
  199. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-worktree-removal-gate.ps1 +54 -41
  200. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-evidence-locations.ps1 +7 -1
  201. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +35 -14
  202. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
  203. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
  204. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
  205. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +24 -11
  206. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +8 -2
  207. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +13 -4
  208. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +8 -2
  209. package/resources/codex-and-agents-customizations/.codex/hooks/feature-folder-resolution.ps1 +373 -0
  210. package/resources/codex-and-agents-customizations/.codex/hooks/feature-review-coverage-thresholds.ps1 +85 -0
  211. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-heredoc.ps1 +119 -0
  212. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation-operands.ps1 +198 -0
  213. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation.ps1 +254 -255
  214. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload-powershell.ps1 +201 -0
  215. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload.ps1 +484 -0
  216. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-scanner.ps1 +27 -110
  217. package/resources/codex-and-agents-customizations/.codex/hooks/hook-dependency-guard.ps1 +114 -0
  218. package/resources/codex-and-agents-customizations/.codex/hooks/hook-docs-only-diff.ps1 +497 -0
  219. package/resources/codex-and-agents-customizations/.codex/hooks/validate-bash.ps1 +8 -2
  220. package/resources/codex-and-agents-customizations/.codex/hooks/validate-codex-subagent-routing.ps1 +7 -1
  221. package/resources/codex-and-agents-customizations/.codex/hooks/validate-feature-review-coverage.ps1 +24 -8
  222. package/resources/codex-and-agents-customizations/.codex/prompts/feature-review-remediate.md +6 -4
  223. package/resources/codex-and-agents-customizations/pack-manifests/core.json +11 -0
  224. package/resources/customizations/.github/agents/atomic_executor.agent.md +21 -11
  225. package/resources/customizations/.github/agents/atomic_planning.agent.md +8 -3
  226. package/resources/customizations/.github/agents/csharp-atomic-executor.agent.md +13 -9
  227. package/resources/customizations/.github/agents/csharp-atomic-planning.agent.md +2 -2
  228. package/resources/customizations/.github/agents/csharp-orchestrator.agent.md +2 -2
  229. package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +5 -5
  230. package/resources/customizations/.github/agents/orchestrator.agent.md +8 -5
  231. package/resources/customizations/.github/agents/powershell-atomic-executor.agent.md +13 -9
  232. package/resources/customizations/.github/agents/powershell-atomic-planning.agent.md +2 -2
  233. package/resources/customizations/.github/agents/powershell-orchestrator.agent.md +2 -2
  234. package/resources/customizations/.github/agents/powershell-typed-engineer.agent.md +3 -3
  235. package/resources/customizations/.github/agents/python-atomic-executor.agent.md +13 -9
  236. package/resources/customizations/.github/agents/python-atomic-planning.agent.md +2 -2
  237. package/resources/customizations/.github/agents/python-orchestrator.agent.md +2 -2
  238. package/resources/customizations/.github/agents/task-researcher.agent.md +6 -0
  239. package/resources/customizations/.github/instructions/csharp-code-change.instructions.md +4 -4
  240. package/resources/customizations/.github/instructions/csharp-unit-test.instructions.md +2 -2
  241. package/resources/customizations/.github/prompts/generate-atomic-plan.prompt.md +16 -10
  242. package/resources/customizations/.github/prompts/orchestrate-csharp-work.prompt.md +2 -2
  243. package/resources/customizations/.github/prompts/orchestrate-powershell-work.prompt.md +1 -1
  244. package/resources/customizations/.github/prompts/orchestrate-python-work.prompt.md +1 -1
  245. package/resources/customizations/.github/prompts/orchestrate-work.prompt.md +2 -2
  246. package/resources/customizations/.github/skills/acceptance-criteria-tracking/SKILL.md +1 -1
  247. package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +43 -4
  248. package/resources/customizations/.github/skills/csharp-change-budget-router/SKILL.md +1 -1
  249. package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
  250. package/resources/customizations/.github/skills/execution-environment/SKILL.md +134 -0
  251. package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +1 -1
  252. package/resources/customizations/.github/skills/powershell-change-budget-router/SKILL.md +1 -1
  253. package/resources/customizations/.github/skills/remediation-handoff-atomic-planner/SKILL.md +7 -3
  254. package/resources/feature-templates/bug/plan.yyyy-MM-ddTHH-mm.md +10 -0
  255. package/resources/feature-templates/feature/plan.yyyy-MM-ddTHH-mm.md +11 -0
  256. package/resources/feature-templates/refactor/plan.yyyy-MM-ddTHH-mm.md +11 -0
  257. package/resources/powershell/PoshQC/PoshQC.Coverage.psm1 +314 -0
  258. package/resources/powershell/PoshQC/PoshQC.CoverageMerge.psm1 +163 -0
  259. package/resources/powershell/PoshQC/PoshQC.Shard.psm1 +296 -0
  260. package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +48 -32
  261. package/resources/powershell/PoshQC/PoshQC.psd1 +7 -1
  262. package/resources/powershell/PoshQC/PoshQC.psm1 +18 -5
  263. package/resources/powershell/PoshQC/README.md +21 -3
  264. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +5 -319
@@ -8,45 +8,59 @@
8
8
  and the serialized prompt contains the epic-mode kickoff marker "Epic mode: true".
9
9
 
10
10
  Resolution and decision procedure:
11
- 1. Resolve the target child feature_folder from the prompt text by scanning for a
12
- docs/features/active/<token> path, mirroring
13
- enforce-prd-feature-before-planner.ps1's Find-PrdFeatureFolderFromPrompt
14
- technique (longest match wins; a .md-suffixed match uses its parent directory).
15
- 2. Resolve the worktree whose epic checkpoint records the prompt's
16
- integration_branch: value (issue #690), deny an unresolved or ambiguous target,
17
- then read artifacts/orchestration/epic-orchestrator-state.json beneath that
18
- worktree and locate the features[] record whose feature_folder equals the
19
- resolved basename.
20
- 3. Look up that feature's depends_on list, and for every dependency, locate its own
21
- features[] record.
22
- 4. Deny with reason EPIC_WAVE_BARRIER_BLOCKED unless every dependency's merge_status
11
+ 1. Resolve the worktree whose epic checkpoint records the prompt's
12
+ integration_branch: value (issue #690) and deny an unresolved or ambiguous target.
13
+ 2. Collect the cited feature-folder candidates with the shared resolver
14
+ (feature-folder-resolution.ps1, issue #565): every docs/features/active/<token>
15
+ path is truncated to its feature-folder segment, so a nested research/ or
16
+ evidence/ citation names its own folder. No candidate denies.
17
+ 3. Read artifacts/orchestration/epic-orchestrator-state.json beneath that worktree
18
+ and select the target among the candidates with Select-FeatureFolderTarget
19
+ -DependencyAware: a cited upstream dependency of another cited candidate is
20
+ pruned, and two or more remaining candidates deny as ambiguous. Neither string
21
+ length nor prompt position takes part in the selection.
22
+ 4. Locate the target's features[] record and, for every depends_on entry, its own
23
+ record. An entry resolves through the union index, so an issue number matches
24
+ issue_num and a folder value matches the normalized feature_folder.
25
+ 5. Deny with reason EPIC_WAVE_BARRIER_BLOCKED unless every dependency's merge_status
23
26
  is merged or worktree_removed. A missing/unreadable checkpoint, an unresolved
24
27
  target feature_folder, or a missing dependency record also denies (fail-closed).
25
28
 
26
- This is the per-call deterrent (Layer 1) of the two-layer wave-barrier design; the
27
- retrospective backstop (Layer 2) is the wave-barrier ordering invariant inside
28
- validate_epic_orchestrator_state_text, enforced separately at epic-orchestrator
29
- SubagentStop time.
29
+ This is the per-call deterrent (Layer 1) of the two-layer wave-barrier design. The
30
+ retrospective backstop (Layer 2) is the wave-barrier ordering invariant of
31
+ validate_epic_orchestrator_state_text, run at epic-orchestrator SubagentStop time by
32
+ validate-orchestrator-output.ps1 through its PowerShell port
33
+ OrchestratorStateEpicWaveBarrier.psm1, with parity pinned by tests/fixtures/epic_wave_barrier/.
34
+ The SubagentStop transport and exit-code defect that hook depended on was resolved by epic #904
35
+ and issue #928: a violation blocks the stop with exit 2 and a stderr reason.
36
+ The two layers share no code.
30
37
 
31
38
  .NOTES
32
- Compatible with PowerShell 7+. Depends on WorktreeRunResolution.psm1 (issue #690),
33
- imported inside a guard: a failed import is recorded and the decision denies naming
34
- the module. Filesystem reads go through an injectable wrapper function so tests can
35
- mock the boundary without writing temporary files.
39
+ Compatible with PowerShell 7+. Depends on WorktreeRunResolution.psm1 (issue #690) and
40
+ the pure sibling feature-folder-resolution.ps1 (issue #565), each loaded inside a
41
+ guard: a failure is recorded and the decision denies naming the dependency. Filesystem
42
+ reads go through an injectable wrapper function so tests can mock the boundary without
43
+ writing temporary files.
36
44
  #>
37
45
  [CmdletBinding()]
38
46
  param()
47
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
39
48
 
40
49
 
41
- Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force
50
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'HookPayload.psm1' -ErrorRecord $_ }
42
51
 
43
- # Import guard (issue #690): a failed import denies instead of failing open.
52
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeRunResolution.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'WorktreeRunResolution.psm1' -ErrorRecord $_ }
44
53
  $script:EpicWaveBarrierResolutionImportFailure = $null
54
+
55
+ # Shared feature-folder resolution (issue #565). Guarded so a failed dot-source denies
56
+ # rather than failing open; an earlier recorded failure is kept.
45
57
  try {
46
- Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeRunResolution.psm1') -Force -ErrorAction Stop
58
+ . (Join-Path $PSScriptRoot 'feature-folder-resolution.ps1')
47
59
  }
48
60
  catch {
49
- $script:EpicWaveBarrierResolutionImportFailure = 'WorktreeRunResolution.psm1'
61
+ if (-not $script:EpicWaveBarrierResolutionImportFailure) {
62
+ $script:EpicWaveBarrierResolutionImportFailure = 'feature-folder-resolution.ps1'
63
+ }
50
64
  }
51
65
 
52
66
  $script:AllowedMergeStatuses = @('merged', 'worktree_removed')
@@ -95,56 +109,35 @@ function Resolve-EpicWaveBarrierTarget {
95
109
  function Find-EpicWaveBarrierFeatureFolderFromPrompt {
96
110
  <#
97
111
  .SYNOPSIS
98
- Scans a prompt string for docs/features/active/<...> path tokens and returns the
99
- longest unique match's basename. Returns $null when no match is found.
112
+ Returns the distinct feature-folder basenames cited in a prompt, in first-occurrence
113
+ order. Returns no output when no folder is cited.
100
114
  .DESCRIPTION
101
- Mirrors enforce-prd-feature-before-planner.ps1's
102
- Find-PrdFeatureFolderFromPrompt technique: forward- or backslash-separated path
103
- tokens are accepted, the longest match wins, and a .md-suffixed match resolves to
104
- its parent directory before the basename is extracted.
115
+ Delegates to Find-FeatureFolderCandidate in the shared resolver (issue #565): each
116
+ docs/features/active/<token> path, in either separator style, is truncated to its
117
+ feature-folder segment, so a nested research/ or evidence/ citation names its own
118
+ folder. Selection among several candidates is made by Select-FeatureFolderTarget.
105
119
  .PARAMETER Prompt
106
120
  The delegation prompt text under evaluation.
107
121
  .OUTPUTS
108
- System.String or $null
122
+ System.String[]
109
123
  #>
110
124
  [CmdletBinding()]
111
- [OutputType([string])]
125
+ [OutputType([string[]])]
112
126
  param(
113
127
  [Parameter(Mandatory)]
114
128
  [AllowEmptyString()]
115
129
  [string] $Prompt
116
130
  )
117
131
 
118
- if (-not $Prompt) {
119
- return $null
120
- }
121
-
122
- $pattern = 'docs[\\/]+features[\\/]+active[\\/]+[^\s"''`]+'
123
- $matchList = [regex]::Matches($Prompt, $pattern)
124
- if ($matchList.Count -eq 0) {
125
- return $null
126
- }
127
-
128
- $unique = @{}
129
- foreach ($m in $matchList) {
130
- $normalized = ($m.Value -replace '\\', '/').TrimEnd('/')
131
- $unique[$normalized] = $true
132
- }
133
-
134
- $candidates = @(@($unique.Keys) | Sort-Object -Property Length -Descending)
135
- $best = $candidates[0]
136
-
137
- if ($best -match '\.md$') {
138
- $best = $best -replace '/[^/]+\.md$', ''
139
- }
140
-
141
- return ($best -split '/')[-1]
132
+ return [string[]]@(Find-FeatureFolderCandidate -Text $Prompt)
142
133
  }
143
134
 
144
135
  function Find-EpicWaveBarrierFeatureRecord {
145
136
  <#
146
137
  .SYNOPSIS
147
- Locate the features[] record whose feature_folder equals the target basename.
138
+ Locate the features[] record the target basename identifies, through the shared
139
+ union index (Find-FeatureFolderRecord). A lifecycle-prefixed feature_folder value
140
+ such as active/<folder> matches its basename; zero or several matches return $null.
148
141
  .PARAMETER Checkpoint
149
142
  Parsed epic checkpoint, or $null when absent/unreadable.
150
143
  .PARAMETER FeatureFolder
@@ -169,17 +162,7 @@ function Find-EpicWaveBarrierFeatureRecord {
169
162
  return $null
170
163
  }
171
164
 
172
- # Scan every recorded feature for a feature_folder that equals the target basename.
173
- foreach ($feature in @($Checkpoint.features)) {
174
- $featureProps = @($feature.PSObject.Properties.Name)
175
- if ($featureProps -notcontains 'feature_folder') {
176
- continue
177
- }
178
- if (([string]$feature.feature_folder) -eq $FeatureFolder) {
179
- return $feature
180
- }
181
- }
182
- return $null
165
+ return Find-FeatureFolderRecord -Records @($Checkpoint.features) -Reference $FeatureFolder
183
166
  }
184
167
 
185
168
  function Test-EpicWaveBarrierDependenciesMerged {
@@ -219,9 +202,11 @@ function Test-EpicWaveBarrierDependenciesMerged {
219
202
  }
220
203
 
221
204
  # Every dependency edge must be durably confirmed merged or worktree_removed before
222
- # this wave's feature is allowed to start.
223
- foreach ($dependencyFolder in $dependsOn) {
224
- $dependencyRecord = Find-EpicWaveBarrierFeatureRecord -Checkpoint $Checkpoint -FeatureFolder ([string]$dependencyFolder)
205
+ # this wave's feature is allowed to start. Each raw entry resolves through the union
206
+ # index, so an issue-number edge matches issue_num (issue #565).
207
+ $features = if (@($Checkpoint.PSObject.Properties.Name) -contains 'features') { @($Checkpoint.features) } else { @() }
208
+ foreach ($dependency in $dependsOn) {
209
+ $dependencyRecord = Find-FeatureFolderRecord -Records $features -Reference $dependency
225
210
  if ($null -eq $dependencyRecord) {
226
211
  return $false
227
212
  }
@@ -280,11 +265,13 @@ function Invoke-EpicWaveBarrierDecision {
280
265
  param(
281
266
  [string] $ToolInputRaw
282
267
  )
268
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EPIC_WAVE_BARRIER_BLOCKED:'
269
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
283
270
 
284
- # A failed worktree-resolution import denies before any other logic (issue #690).
271
+ # A failed dependency import denies before any other logic (issue #565).
285
272
  if ($script:EpicWaveBarrierResolutionImportFailure) {
286
273
  return Get-EpicWaveBarrierBlockDecision -Reason (
287
- "EPIC_WAVE_BARRIER_BLOCKED: the worktree-resolution module '$($script:EpicWaveBarrierResolutionImportFailure)' " +
274
+ "EPIC_WAVE_BARRIER_BLOCKED: the dependency '$($script:EpicWaveBarrierResolutionImportFailure)' " +
288
275
  'failed to import, so the epic checkpoint that governs this delegation cannot be located; the gate fails closed.')
289
276
  }
290
277
 
@@ -312,8 +299,8 @@ function Invoke-EpicWaveBarrierDecision {
312
299
  return Get-EpicWaveBarrierBlockDecision -Reason "EPIC_WAVE_BARRIER_BLOCKED: $($target.ReasonCode): $($target.Detail)"
313
300
  }
314
301
 
315
- $featureFolder = Find-EpicWaveBarrierFeatureFolderFromPrompt -Prompt $prompt
316
- if (-not $featureFolder) {
302
+ $candidates = @(Find-EpicWaveBarrierFeatureFolderFromPrompt -Prompt $prompt)
303
+ if ($candidates.Count -eq 0) {
317
304
  return Get-EpicWaveBarrierBlockDecision -Reason 'EPIC_WAVE_BARRIER_BLOCKED: an epic-mode orchestrator delegation must reference the target feature folder in the prompt so its dependency edges can be verified.'
318
305
  }
319
306
 
@@ -327,6 +314,15 @@ function Invoke-EpicWaveBarrierDecision {
327
314
  }
328
315
  }
329
316
 
317
+ # The target is selected among the cited candidates (issue #565): a cited upstream
318
+ # dependency of another candidate is pruned, and an unresolved tie denies.
319
+ $records = if ($null -ne $checkpoint -and @($checkpoint.PSObject.Properties.Name) -contains 'features') { @($checkpoint.features) } else { @() }
320
+ $selection = Select-FeatureFolderTarget -Records $records -Candidate $candidates -DependencyAware
321
+ if ($selection.Status -eq 'Ambiguous') {
322
+ return Get-EpicWaveBarrierBlockDecision -Reason "EPIC_WAVE_BARRIER_BLOCKED: $($selection.Detail)."
323
+ }
324
+ $featureFolder = $selection.Basename
325
+
330
326
  $featureRecord = Find-EpicWaveBarrierFeatureRecord -Checkpoint $checkpoint -FeatureFolder $featureFolder
331
327
  if (Test-EpicWaveBarrierDependenciesMerged -Checkpoint $checkpoint -FeatureRecord $featureRecord) {
332
328
  return Get-EpicWaveBarrierAllowDecision
@@ -377,5 +373,8 @@ function Invoke-EpicWaveBarrierEntryPoint {
377
373
  if ($MyInvocation.InvocationName -eq '.') {
378
374
  return
379
375
  }
376
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('EPIC_WAVE_BARRIER_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
377
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EPIC_WAVE_BARRIER_BLOCKED:'
378
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
380
379
 
381
380
  exit (Invoke-EpicWaveBarrierEntryPoint)
@@ -7,49 +7,29 @@
7
7
  dot-sources. Holds:
8
8
 
9
9
  - The import guard for WorktreeRunResolution.psm1. A failed import is recorded in
10
- $script:EpicWorktreeGateResolutionImportFailure, and
11
- Get-EpicWorktreeGateImportFailureDecision turns it into a deny, so a missing module
10
+ $script:HookDependencyFailures, and the gate's call to
11
+ Get-HookDependencyFailureDecision turns it into a deny, so a missing module
12
12
  cannot make the gate exit non-zero and fail open.
13
13
  - The two checkpoint read seams, relocated from the gate file with their names kept.
14
14
  Each takes a mandatory absolute path composed beneath a resolved worktree root.
15
15
  - The resolution seam Resolve-EpicWorktreeGateRunTarget, which locates the epic or
16
16
  parallel checkpoint that records the removal target under worktree_path.
17
- - Read-EpicWorktreeGateRunCheckpoint, which returns the target and the parsed
18
- checkpoint beneath it ($null when the target is unresolved).
17
+ - Read-EpicWorktreeGateRunCheckpoint, which returns the target, the parsed checkpoint
18
+ beneath it ($null when the target is unresolved), and the Path it read ($null when
19
+ the target is unresolved; issue #851).
20
+ - Find-EpicWorktreeGateParallelItemRecord, which locates the parallel items[] record
21
+ for the removal target, and Get-EpicWorktreeGateDenyDiagnostics, which builds the
22
+ diagnostics clause of the final deny from two read results without reading any
23
+ file (issue #851).
19
24
 
20
25
  .NOTES
21
- PowerShell 7+. Depends on Get-EpicWorktreeGateBlockDecision and
22
- ConvertFrom-EpicWorktreeGateJson from the gate file, resolved at call time. Mirrored
23
- byte-identically under extensions/drm-copilot/resources/claude-customizations/.
26
+ PowerShell 7+. Depends on Get-EpicWorktreeGateBlockDecision,
27
+ ConvertFrom-EpicWorktreeGateJson, and Find-EpicWorktreeFeatureRecord from the gate
28
+ file, resolved at call time. Mirrored byte-identically under
29
+ extensions/drm-copilot/resources/claude-customizations/.
24
30
  #>
25
31
 
26
- # Import guard (issue #690): a failed import denies instead of failing open.
27
- $script:EpicWorktreeGateResolutionImportFailure = $null
28
- try {
29
- Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeRunResolution.psm1') -Force -ErrorAction Stop
30
- }
31
- catch {
32
- $script:EpicWorktreeGateResolutionImportFailure = 'WorktreeRunResolution.psm1'
33
- }
34
-
35
- function Get-EpicWorktreeGateImportFailureDecision {
36
- <#
37
- .SYNOPSIS
38
- Returns the deny for a failed worktree-resolution import, or $null.
39
- .OUTPUTS
40
- System.Collections.Specialized.OrderedDictionary, or $null when the import succeeded.
41
- #>
42
- [CmdletBinding()]
43
- [OutputType([System.Collections.Specialized.OrderedDictionary])]
44
- param()
45
-
46
- if (-not $script:EpicWorktreeGateResolutionImportFailure) {
47
- return $null
48
- }
49
- return Get-EpicWorktreeGateBlockDecision -Reason (
50
- "EPIC_WORKTREE_REMOVAL_BLOCKED: the worktree-resolution module '$($script:EpicWorktreeGateResolutionImportFailure)' " +
51
- 'failed to import, so the run checkpoint that governs this removal cannot be located; the gate fails closed.')
52
- }
32
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeRunResolution.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'WorktreeRunResolution.psm1' -ErrorRecord $_ }
53
33
 
54
34
  function Get-EpicWorktreeGateCheckpointContent {
55
35
  <#
@@ -121,7 +101,9 @@ function Read-EpicWorktreeGateRunCheckpoint {
121
101
  .PARAMETER WorktreePath
122
102
  The removal target the command names.
123
103
  .OUTPUTS
124
- System.Management.Automation.PSCustomObject with Target and Checkpoint ($null when unresolved).
104
+ System.Management.Automation.PSCustomObject with Target, Checkpoint ($null when
105
+ unresolved, absent, or unparseable), and Path (the composed absolute checkpoint
106
+ path, or $null when the target is unresolved).
125
107
  #>
126
108
  [CmdletBinding()]
127
109
  [OutputType([pscustomobject])]
@@ -132,6 +114,7 @@ function Read-EpicWorktreeGateRunCheckpoint {
132
114
 
133
115
  $target = Resolve-EpicWorktreeGateRunTarget -Kind $Kind -WorktreePath $WorktreePath
134
116
  $checkpoint = $null
117
+ $path = $null
135
118
  if ($target.Status -eq 'SessionRoot' -or $target.Status -eq 'OtherWorktree') {
136
119
  $path = Get-WorktreeRunCheckpointPath -Kind $Kind -WorktreeRoot $target.WorktreeRoot
137
120
  $raw = if ($Kind -eq 'epic') { Get-EpicWorktreeGateCheckpointContent -Path $path } else { Get-EpicWorktreeGateParallelCheckpointContent -Path $path }
@@ -140,5 +123,114 @@ function Read-EpicWorktreeGateRunCheckpoint {
140
123
  return [pscustomobject]@{
141
124
  Target = $target
142
125
  Checkpoint = $checkpoint
126
+ Path = $path
127
+ }
128
+ }
129
+
130
+ function Find-EpicWorktreeGateParallelItemRecord {
131
+ <#
132
+ .SYNOPSIS
133
+ Locate the parallel items[] record whose worktree_path matches the target path.
134
+ .DESCRIPTION
135
+ Pure. Normalizes both paths exactly as Test-ParallelCheckpointAllowsWorktreeRemoval
136
+ does (backslash to slash, trailing slash trimmed, PowerShell -eq) and returns the
137
+ first matching entry (issue #851).
138
+ .PARAMETER Checkpoint
139
+ Parsed parallel-orchestrator checkpoint, or $null when absent/unreadable.
140
+ .PARAMETER WorktreePath
141
+ The removal target the command names.
142
+ .OUTPUTS
143
+ System.Object or $null
144
+ #>
145
+ [CmdletBinding()]
146
+ param(
147
+ [AllowNull()]
148
+ $Checkpoint,
149
+
150
+ [AllowNull()]
151
+ [string] $WorktreePath
152
+ )
153
+
154
+ if ($null -eq $Checkpoint -or [string]::IsNullOrWhiteSpace($WorktreePath)) {
155
+ return $null
156
+ }
157
+ $props = @($Checkpoint.PSObject.Properties.Name)
158
+ if ($props -notcontains 'items' -or $null -eq $Checkpoint.items) {
159
+ return $null
160
+ }
161
+
162
+ $normalizedTarget = ($WorktreePath -replace '\\', '/').TrimEnd('/')
163
+ foreach ($item in @($Checkpoint.items)) {
164
+ if ($null -eq $item) {
165
+ continue
166
+ }
167
+ $itemProps = @($item.PSObject.Properties.Name)
168
+ if ($itemProps -notcontains 'worktree_path') {
169
+ continue
170
+ }
171
+ $normalizedItemPath = (([string]$item.worktree_path) -replace '\\', '/').TrimEnd('/')
172
+ if ($normalizedItemPath -eq $normalizedTarget) {
173
+ return $item
174
+ }
175
+ }
176
+ return $null
177
+ }
178
+
179
+ function Get-EpicWorktreeGateDenyDiagnostics {
180
+ <#
181
+ .SYNOPSIS
182
+ Build the diagnostics clause of the final deny from the two read results (issue #851).
183
+ .DESCRIPTION
184
+ Pure; reads no file. Names, for each run kind, the target status, the checkpoint
185
+ path read (or that none was read), and the outcome of the record match: the
186
+ matched record's merge_status, an absent merge_status, no matching record, an
187
+ absent or unparseable checkpoint, or not evaluated when no checkpoint was read.
188
+ .PARAMETER EpicRead
189
+ The epic read result (Target, Checkpoint, Path).
190
+ .PARAMETER ParallelRead
191
+ The parallel read result (Target, Checkpoint, Path).
192
+ .PARAMETER WorktreePath
193
+ The removal target the command names.
194
+ .OUTPUTS
195
+ System.String
196
+ #>
197
+ [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = 'The name is fixed by the issue #851 plan contract; the plural noun names the one diagnostics clause it returns.')]
198
+ [CmdletBinding()]
199
+ [OutputType([string])]
200
+ param(
201
+ [Parameter(Mandatory)] $EpicRead,
202
+ [Parameter(Mandatory)] $ParallelRead,
203
+ [AllowNull()][AllowEmptyString()][string] $WorktreePath
204
+ )
205
+
206
+ $parts = foreach ($kind in @('epic', 'parallel')) {
207
+ $read = if ($kind -eq 'epic') { $EpicRead } else { $ParallelRead }
208
+ $pathText = if ($null -ne $read.Path) { "checkpoint '$($read.Path)'" } else { 'no checkpoint read' }
209
+ if ($null -eq $read.Path) {
210
+ $recordText = 'not evaluated'
211
+ }
212
+ elseif ($null -eq $read.Checkpoint) {
213
+ $recordText = 'checkpoint absent or unparseable'
214
+ }
215
+ else {
216
+ $record = if ($kind -eq 'epic') {
217
+ Find-EpicWorktreeFeatureRecord -Checkpoint $read.Checkpoint -WorktreePath $WorktreePath
218
+ }
219
+ else {
220
+ Find-EpicWorktreeGateParallelItemRecord -Checkpoint $read.Checkpoint -WorktreePath $WorktreePath
221
+ }
222
+ $arrayName = if ($kind -eq 'epic') { 'features' } else { 'items' }
223
+ $recordText = if ($null -eq $record) {
224
+ "no matching $arrayName[] record"
225
+ }
226
+ elseif (@($record.PSObject.Properties.Name) -contains 'merge_status') {
227
+ "merge_status '$([string]$record.merge_status)'"
228
+ }
229
+ else {
230
+ 'merge_status absent'
231
+ }
232
+ }
233
+ '{0} run {1} ({2}), {3}' -f $kind, $read.Target.Status, $pathText, $recordText
143
234
  }
235
+ return ('Diagnostics: {0}; {1}.' -f $parts[0], $parts[1])
144
236
  }
@@ -55,6 +55,10 @@
55
55
  enforce-epic-worktree-removal-gate-resolution.ps1. An ambiguous target denies before
56
56
  any allow; when neither kind resolves, the deny names TARGET_WORKTREE_NOT_DERIVABLE.
57
57
 
58
+ Issues #789 and #851: the final deny carries one leading EPIC_WORKTREE_REMOVAL_BLOCKED
59
+ token and ends with a diagnostics clause naming each run kind's status, checkpoint
60
+ path, and matched record; the allow-or-deny decision is unchanged.
61
+
58
62
  .NOTES
59
63
  Compatible with PowerShell 7+. Depends on WorktreeRunResolution.psm1 through the
60
64
  dot-sourced resolution sibling, whose import guard denies on a failed import.
@@ -63,18 +67,19 @@
63
67
  #>
64
68
  [CmdletBinding()]
65
69
  param()
70
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
66
71
 
67
72
 
68
- Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force
73
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'HookPayload.psm1' -ErrorRecord $_ }
69
74
  # Sanctioned-removal manifest reader (issue #635), consumed by the manifest branch in
70
75
  # Invoke-EpicWorktreeRemovalGateDecision.
71
- Import-Module (Join-Path $PSScriptRoot '../lib/cleanup-manifest/CleanupWorktreeManifest.psm1') -Force
76
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/cleanup-manifest/CleanupWorktreeManifest.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'CleanupWorktreeManifest.psm1' -ErrorRecord $_ }
72
77
  # Shared command-line parser (issue #545), consumed by the scope filter in
73
- # Invoke-EpicWorktreeRemovalGateDecision and by Get-EpicWorktreeRemovalCommandPath.
74
- . (Join-Path $PSScriptRoot 'hook-command-scanner.ps1')
75
- . (Join-Path $PSScriptRoot 'hook-command-invocation.ps1')
78
+ # Invoke-EpicWorktreeRemovalGateDecision and by Resolve-CommandLineInvocationTarget.
79
+ try { . (Join-Path $PSScriptRoot 'hook-command-scanner.ps1') } catch { Add-HookDependencyFailure -Name 'hook-command-scanner.ps1' -ErrorRecord $_ }
80
+ try { . (Join-Path $PSScriptRoot 'hook-command-invocation.ps1') } catch { Add-HookDependencyFailure -Name 'hook-command-invocation.ps1' -ErrorRecord $_ }
76
81
  # Checkpoint read seams, the import guard, and run-target resolution (issue #690).
77
- . (Join-Path $PSScriptRoot 'enforce-epic-worktree-removal-gate-resolution.ps1')
82
+ try { . (Join-Path $PSScriptRoot 'enforce-epic-worktree-removal-gate-resolution.ps1') } catch { Add-HookDependencyFailure -Name 'enforce-epic-worktree-removal-gate-resolution.ps1' -ErrorRecord $_ }
78
83
  $script:AllowedMergeStatuses = @('merged', 'worktree_removed')
79
84
  # Sanctioned-removal manifest location. Recorded here so every hook-read document this
80
85
  # gate consults is named; the module owns the read itself.
@@ -108,47 +113,6 @@ function ConvertFrom-EpicWorktreeGateJson {
108
113
  }
109
114
  }
110
115
 
111
- function Get-EpicWorktreeRemovalCommandPath {
112
- <#
113
- .SYNOPSIS
114
- Extract the target worktree path argument from a git worktree remove command.
115
- .DESCRIPTION
116
- The operand comes from the segment that structurally invokes git worktree remove,
117
- so a 'cd <path> &&' segment chained before the removal contributes nothing and a
118
- quoted mention of the phrase resolves to no operand at all. Quotes around the path
119
- are already stripped by the tokenizer.
120
-
121
- '--force' is a zero-argument flag, so it never contributes an operand and may be
122
- written on either side of the target path. Its presence is read structurally through
123
- Test-CommandLineFlag rather than by searching the raw text, so a '--force' spelling
124
- that appears inside an unrelated quoted argument cannot change how the operand list
125
- is read. When no operand resolves, a present '--force' is reported in its place, so
126
- the checkpoint lookup fails closed on a value that matches no recorded worktree_path
127
- - the same value the previous raw-text pattern returned for that input.
128
- .PARAMETER CommandText
129
- The Bash command text under evaluation.
130
- .OUTPUTS
131
- System.String or $null
132
- #>
133
- [CmdletBinding()]
134
- [OutputType([string])]
135
- param(
136
- [Parameter(Mandatory)]
137
- [string] $CommandText
138
- )
139
-
140
- $hasForce = Test-CommandLineFlag -CommandText $CommandText -CommandWord 'git' -SubcommandPath @('worktree', 'remove') -FlagName '--force'
141
- $operands = @(Get-CommandLineOperand -CommandText $CommandText -CommandWord 'git' -SubcommandPath @('worktree', 'remove'))
142
-
143
- if ($operands.Count -gt 0) {
144
- return $operands[0]
145
- }
146
- if ($hasForce) {
147
- return '--force'
148
- }
149
- return $null
150
- }
151
-
152
116
  function Find-EpicWorktreeFeatureRecord {
153
117
  <#
154
118
  .SYNOPSIS
@@ -331,10 +295,8 @@ function Invoke-EpicWorktreeRemovalGateDecision {
331
295
  )
332
296
 
333
297
  # A failed worktree-resolution import denies before any other logic (issue #690).
334
- $importFailure = Get-EpicWorktreeGateImportFailureDecision
335
- if ($null -ne $importFailure) {
336
- return $importFailure
337
- }
298
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EPIC_WORKTREE_REMOVAL_BLOCKED:'
299
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
338
300
 
339
301
  $payload = Resolve-ClaudeHookToolInput -Raw $ToolInputRaw
340
302
  if (-not $payload.IsValid) {
@@ -356,7 +318,43 @@ function Invoke-EpicWorktreeRemovalGateDecision {
356
318
  return Get-EpicWorktreeGateAllowDecision
357
319
  }
358
320
 
359
- $worktreePath = Get-EpicWorktreeRemovalCommandPath -CommandText $commandText
321
+ # Issue #824: the removal targets are derived structurally from every invocation.
322
+ # A target that cannot be derived denies before any checkpoint is read, and every
323
+ # derived target must be authorized on its own.
324
+ $resolution = Resolve-CommandLineInvocationTarget -CommandText $commandText -CommandWord 'git' -SubcommandPath @('worktree', 'remove')
325
+ if ($resolution.Status -eq 'NoMatch') {
326
+ return Get-EpicWorktreeGateAllowDecision
327
+ }
328
+ if ($resolution.Status -ne 'Targets') {
329
+ return Get-EpicWorktreeGateBlockDecision -Reason 'EPIC_WORKTREE_REMOVAL_BLOCKED: TARGET_WORKTREE_NOT_DERIVABLE: the git worktree remove target cannot be derived from the command text. No checkpoint can authorize this removal.'
330
+ }
331
+ foreach ($target in @($resolution.Targets)) {
332
+ $denial = Get-EpicWorktreeRemovalTargetDenial -WorktreePath $target
333
+ if ($null -ne $denial) {
334
+ return $denial
335
+ }
336
+ }
337
+ return Get-EpicWorktreeGateAllowDecision
338
+ }
339
+
340
+ function Get-EpicWorktreeRemovalTargetDenial {
341
+ <#
342
+ .SYNOPSIS
343
+ Evaluate one derived removal target and return its deny decision, or $null when authorized.
344
+ .DESCRIPTION
345
+ Holds the per-path authorization cascade (epic checkpoint, parallel checkpoint,
346
+ sanctioned-removal manifest) for a single worktree path (issue #824).
347
+ .PARAMETER WorktreePath
348
+ One removal target derived by Resolve-CommandLineInvocationTarget.
349
+ .OUTPUTS
350
+ System.Collections.Specialized.OrderedDictionary or $null
351
+ #>
352
+ [CmdletBinding()]
353
+ [OutputType([System.Collections.Specialized.OrderedDictionary])]
354
+ param(
355
+ [Parameter(Mandatory)]
356
+ [string] $WorktreePath
357
+ )
360
358
 
361
359
  # Each run checkpoint is read beneath the worktree that records this path (issue #690);
362
360
  # an ambiguous target denies before any allow or manifest evaluation.
@@ -371,12 +369,12 @@ function Invoke-EpicWorktreeRemovalGateDecision {
371
369
 
372
370
  $featureRecord = Find-EpicWorktreeFeatureRecord -Checkpoint $checkpoint -WorktreePath $worktreePath
373
371
  if (Test-EpicWorktreeRemovalAllowed -FeatureRecord $featureRecord) {
374
- return Get-EpicWorktreeGateAllowDecision
372
+ return $null
375
373
  }
376
374
 
377
375
  $parallelCheckpoint = $parallelRead.Checkpoint
378
376
  if (Test-ParallelCheckpointAllowsWorktreeRemoval -Checkpoint $parallelCheckpoint -WorktreePath $worktreePath) {
379
- return Get-EpicWorktreeGateAllowDecision
377
+ return $null
380
378
  }
381
379
 
382
380
  # Sanctioned-removal manifest branch (issue #635). It runs last, so an
@@ -392,15 +390,18 @@ function Invoke-EpicWorktreeRemovalGateDecision {
392
390
  if (-not (Test-CleanupManifestCheckpointCoversPath -Checkpoint $checkpoint -RecordArrayName 'features' -WorktreePath $worktreePath) -and
393
391
  -not (Test-CleanupManifestCheckpointCoversPath -Checkpoint $parallelCheckpoint -RecordArrayName 'items' -WorktreePath $worktreePath) -and
394
392
  (Test-CleanupWorktreeManifestAuthorizesRemoval -WorktreePath $worktreePath)) {
395
- return Get-EpicWorktreeGateAllowDecision
393
+ return $null
396
394
  }
397
395
 
398
396
  # When neither kind resolved, the deny names the resolution reason first (issue #690).
397
+ # The gate token leads once and a diagnostics clause for this target closes the text
398
+ # (issues #789, #851).
399
399
  $prefix = ''
400
400
  if ($epicRead.Target.Status -eq 'NoTarget' -and $parallelRead.Target.Status -eq 'NoTarget') {
401
- $prefix = "EPIC_WORKTREE_REMOVAL_BLOCKED: $($epicRead.Target.ReasonCode): $($epicRead.Target.Detail). "
401
+ $prefix = "$($epicRead.Target.ReasonCode): $($epicRead.Target.Detail). "
402
402
  }
403
- return Get-EpicWorktreeGateBlockDecision -Reason ($prefix + "EPIC_WORKTREE_REMOVAL_BLOCKED: git worktree remove for '$worktreePath' requires either an epic checkpoint features[] record with merge_status in {merged, worktree_removed}, or a parallel-orchestrator checkpoint with route_id == ""parallel"" whose matching items[] record (matched by worktree_path) has merge_status in {merged, worktree_removed}. No checkpoint authorized this removal.")
403
+ $diagnostics = Get-EpicWorktreeGateDenyDiagnostics -EpicRead $epicRead -ParallelRead $parallelRead -WorktreePath $worktreePath
404
+ return Get-EpicWorktreeGateBlockDecision -Reason ('EPIC_WORKTREE_REMOVAL_BLOCKED: ' + $prefix + "git worktree remove for '$worktreePath' requires either an epic checkpoint features[] record with merge_status in {merged, worktree_removed}, or a parallel-orchestrator checkpoint with route_id == ""parallel"" whose matching items[] record (matched by worktree_path) has merge_status in {merged, worktree_removed}. No checkpoint authorized this removal. " + $diagnostics)
404
405
  }
405
406
 
406
407
  function Invoke-EpicWorktreeRemovalGateEntryPoint {
@@ -445,6 +446,9 @@ function Invoke-EpicWorktreeRemovalGateEntryPoint {
445
446
  if ($MyInvocation.InvocationName -eq '.') {
446
447
  return
447
448
  }
449
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('EPIC_WORKTREE_REMOVAL_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
450
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EPIC_WORKTREE_REMOVAL_BLOCKED:'
451
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
448
452
 
449
453
  # The entry point returns its [int] exit code as the last pipeline element and the
450
454
  # decision JSON before it. `exit (<call>)` would capture BOTH into the exit
@@ -41,8 +41,9 @@
41
41
  #>
42
42
  [CmdletBinding()]
43
43
  param()
44
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
44
45
 
45
- Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force
46
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'HookPayload.psm1' -ErrorRecord $_ }
46
47
 
47
48
  function Test-EvidenceLocationForbidden {
48
49
  <#
@@ -150,6 +151,8 @@ function Invoke-EvidenceLocationDecision {
150
151
  [AllowEmptyString()]
151
152
  [string] $ToolInputRaw
152
153
  )
154
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EVIDENCE_LOCATION_BLOCKED:'
155
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
153
156
 
154
157
  $payload = Resolve-ClaudeHookToolInput -Raw $ToolInputRaw
155
158
  if (-not $payload.IsValid) {
@@ -211,6 +214,9 @@ function Invoke-EvidenceLocationEntryPoint {
211
214
  if ($MyInvocation.InvocationName -eq '.') {
212
215
  return
213
216
  }
217
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('EVIDENCE_LOCATION_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
218
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EVIDENCE_LOCATION_BLOCKED:'
219
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
214
220
 
215
221
  # The entry point returns its [int] exit code as the last pipeline element and the
216
222
  # decision JSON before it. `exit (<call>)` would capture BOTH into the exit