@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
@@ -0,0 +1,190 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Scope predicate and pull-request selector reader for the epic merge gate (issue #952).
4
+ .DESCRIPTION
5
+ Dot-sourced by enforce-epic-merge-gate.ps1 on both runtimes; this file is byte-identical in
6
+ .claude/hooks and .codex/hooks. Under a merge queue every gh pr merge spelling (--merge, -m,
7
+ --squash, -s, --rebase, -r, --auto, --admin, or no method flag) adds the pull request to the
8
+ queue, and the gh api mutations enqueuePullRequest, enablePullRequestAutoMerge, and
9
+ mergePullRequest and the REST pulls/<n>/merge endpoint also reach the base branch. All of
10
+ them are in scope. A gh pr merge whose only action flag is --disable-auto is out of scope.
11
+
12
+ Pure string logic only: no disk, process, network, clock, or environment access. The file
13
+ dot-sources and imports nothing; it consumes Get-CommandLineInvocation and
14
+ Get-CommandLineStructuralMatch, which the gate loads from hook-command-invocation.ps1 first.
15
+
16
+ Known limitation (issue #952 code review CR-5): the gh api scope reads the command text
17
+ only. A GraphQL mutation supplied from a file (gh api graphql -F query=@mutation.graphql,
18
+ or gh api --input payload.json) is not visible to this text-based scope and is not gated.
19
+ #>
20
+
21
+ # gh pr merge flags that perform or schedule a merge. Any of them keeps a command in scope even
22
+ # when it also carries --disable-auto.
23
+ $script:EpicMergeGateActionFlags = @('--merge', '-m', '--squash', '-s', '--rebase', '-r', '--auto', '--admin')
24
+
25
+ # gh pr merge flags, including the inherited --repo, that consume the following token as a value.
26
+ $script:EpicMergeGateValueFlags = @('-b', '--body', '-F', '--body-file', '-t', '--subject', '-A', '--author-email', '--match-head-commit', '-R', '--repo')
27
+
28
+ # GraphQL mutations that merge, enqueue, or schedule the merge of a pull request.
29
+ $script:EpicMergeGateMutationNames = @('enqueuePullRequest', 'enablePullRequestAutoMerge', 'mergePullRequest')
30
+
31
+ # The REST merge endpoint path; group 1 is the pull-request number.
32
+ $script:EpicMergeGateRestMergePattern = '(?:^|/)pulls/(\d+)/merge(?:$|[^\w-])'
33
+
34
+ function Get-EpicMergeGateFlagName {
35
+ <#
36
+ .SYNOPSIS
37
+ Return the flag name of a dash-led token without any '=value' suffix.
38
+ .OUTPUTS
39
+ System.String
40
+ #>
41
+ [CmdletBinding()]
42
+ [OutputType([string])]
43
+ param([Parameter(Mandatory)][AllowEmptyString()][string] $Token)
44
+
45
+ $split = $Token.IndexOf('=')
46
+ if ($split -gt 0) { return $Token.Substring(0, $split) }
47
+ return $Token
48
+ }
49
+
50
+ function ConvertFrom-EpicMergeGateSelector {
51
+ <#
52
+ .SYNOPSIS
53
+ Convert a gh pr merge selector to a pull-request number, or $null.
54
+ .DESCRIPTION
55
+ Accepts an all-digit selector or a URL whose path ends in /pull/<n>. A branch-name
56
+ selector carries no number and yields $null.
57
+ .OUTPUTS
58
+ System.Nullable[int]
59
+ #>
60
+ [CmdletBinding()]
61
+ [OutputType([int])]
62
+ param([Parameter(Mandatory)][AllowEmptyString()][string] $Selector)
63
+
64
+ $digits = $null
65
+ if ($Selector -match '^\d+$') {
66
+ $digits = $Selector
67
+ } elseif ($Selector -match '/pull/(\d+)/?(?:[?#].*)?$') {
68
+ $digits = $Matches[1]
69
+ }
70
+ $number = 0
71
+ if ($null -ne $digits -and [int]::TryParse($digits, [ref] $number)) { return $number }
72
+ return $null
73
+ }
74
+
75
+ function Test-EpicMergeGateDisableAutoOnly {
76
+ <#
77
+ .SYNOPSIS
78
+ Report whether a Structural gh pr merge match carries --disable-auto and no action flag.
79
+ .OUTPUTS
80
+ System.Boolean
81
+ #>
82
+ [CmdletBinding()]
83
+ [OutputType([bool])]
84
+ param([Parameter(Mandatory)][pscustomobject] $Match)
85
+
86
+ $tokens = [string[]]@($Match.Segment.Tokens)
87
+ $hasDisableAuto = $false
88
+ for ($index = $Match.OperandIndex; $index -lt $tokens.Count; $index++) {
89
+ $name = Get-EpicMergeGateFlagName -Token $tokens[$index]
90
+ if ($script:EpicMergeGateActionFlags -ccontains $name) { return $false }
91
+ if ($name -ceq '--disable-auto') { $hasDisableAuto = $true }
92
+ }
93
+ return $hasDisableAuto
94
+ }
95
+
96
+ function Test-EpicMergeGateApiMutationText {
97
+ <#
98
+ .SYNOPSIS
99
+ Report whether gh api text names a merge or enqueue mutation or the REST merge endpoint.
100
+ .OUTPUTS
101
+ System.Boolean
102
+ #>
103
+ [CmdletBinding()]
104
+ [OutputType([bool])]
105
+ param([Parameter(Mandatory)][AllowEmptyString()][AllowNull()][string] $Text)
106
+
107
+ if ([string]::IsNullOrEmpty($Text)) { return $false }
108
+ foreach ($name in $script:EpicMergeGateMutationNames) {
109
+ if ($Text.IndexOf($name, [System.StringComparison]::OrdinalIgnoreCase) -ge 0) { return $true }
110
+ }
111
+ return [regex]::IsMatch($Text, $script:EpicMergeGateRestMergePattern)
112
+ }
113
+
114
+ function Test-EpicMergeGateCommandInScope {
115
+ <#
116
+ .SYNOPSIS
117
+ Report whether a command line is a gated merge or enqueue action (issue #952).
118
+ .DESCRIPTION
119
+ True for any gh pr merge match, Structural or Indeterminate, unless the match is Structural
120
+ and its only action flag is --disable-auto; true for any gh api match whose tokens name a
121
+ merge or enqueue mutation or a pulls/<n>/merge path. Text that only mentions these words
122
+ produces no match and stays out of scope. An Indeterminate gh pr merge match stays in scope
123
+ (fail closed).
124
+ .OUTPUTS
125
+ System.Boolean
126
+ #>
127
+ [CmdletBinding()]
128
+ [OutputType([bool])]
129
+ param([Parameter(Mandatory)][AllowEmptyString()][AllowNull()][string] $CommandText)
130
+
131
+ foreach ($match in @(Get-CommandLineInvocation -CommandText $CommandText -CommandWord 'gh' -SubcommandPath @('pr', 'merge'))) {
132
+ if ($match.Status -ne 'Structural' -or -not (Test-EpicMergeGateDisableAutoOnly -Match $match)) { return $true }
133
+ }
134
+ foreach ($match in @(Get-CommandLineInvocation -CommandText $CommandText -CommandWord 'gh' -SubcommandPath @('api'))) {
135
+ if (Test-EpicMergeGateApiMutationText -Text (@($match.Segment.Tokens) -join ' ')) { return $true }
136
+ }
137
+ return $false
138
+ }
139
+
140
+ function Get-EpicMergeGateSelectorPrNumber {
141
+ <#
142
+ .SYNOPSIS
143
+ Resolve the pull-request number of a gated merge or enqueue command, or $null.
144
+ .DESCRIPTION
145
+ For gh pr merge, only the first Structural match is read. Value flags are skipped with
146
+ their values, other flags are skipped, an equals-joined --merge=<digits> yields its
147
+ digits (an equals-joined value that is not a number is skipped like any other flag),
148
+ and the first non-flag token (or the token after a bare '--') is the selector.
149
+ For gh api, the number comes from a pulls/<n>/merge token. Any other command yields
150
+ $null, which the gate handles through its no-number branches.
151
+ .OUTPUTS
152
+ System.Nullable[int]
153
+ #>
154
+ [CmdletBinding()]
155
+ [OutputType([int])]
156
+ param([Parameter(Mandatory)][AllowEmptyString()][AllowNull()][string] $CommandText)
157
+
158
+ $match = Get-CommandLineStructuralMatch -CommandText $CommandText -CommandWord 'gh' -SubcommandPath @('pr', 'merge')
159
+ if ($null -ne $match) {
160
+ $tokens = [string[]]@($match.Segment.Tokens)
161
+ $index = $match.OperandIndex
162
+ while ($index -lt $tokens.Count) {
163
+ $token = $tokens[$index]
164
+ if ($token -ceq '--') {
165
+ if ($index + 1 -lt $tokens.Count) { return ConvertFrom-EpicMergeGateSelector -Selector $tokens[$index + 1] }
166
+ return $null
167
+ }
168
+ if ($token.StartsWith('-') -and $token.Length -gt 1) {
169
+ $name = Get-EpicMergeGateFlagName -Token $token
170
+ if ($name -ceq '--merge' -and $token.Length -gt $name.Length) {
171
+ $equalsNumber = ConvertFrom-EpicMergeGateSelector -Selector $token.Substring($name.Length + 1)
172
+ if ($null -ne $equalsNumber) { return $equalsNumber }
173
+ }
174
+ $index += if ($script:EpicMergeGateValueFlags -ccontains $token) { 2 } else { 1 }
175
+ continue
176
+ }
177
+ return ConvertFrom-EpicMergeGateSelector -Selector $token
178
+ }
179
+ return $null
180
+ }
181
+
182
+ foreach ($apiMatch in @(Get-CommandLineInvocation -CommandText $CommandText -CommandWord 'gh' -SubcommandPath @('api'))) {
183
+ foreach ($token in @($apiMatch.Segment.Tokens)) {
184
+ $rest = [regex]::Match([string]$token, $script:EpicMergeGateRestMergePattern)
185
+ $number = 0
186
+ if ($rest.Success -and [int]::TryParse($rest.Groups[1].Value, [ref] $number)) { return $number }
187
+ }
188
+ }
189
+ return $null
190
+ }
@@ -1,15 +1,40 @@
1
1
  <#
2
2
  .SYNOPSIS
3
3
  Gates epic child and integration PR merges behind checkpointed green state.
4
+
5
+ .DESCRIPTION
6
+ Trigger scope (issue #952): every gh pr merge invocation (any merge method, --auto, --admin,
7
+ or no method flag) and every gh api merge or enqueue mutation is gated, because under a merge
8
+ queue each of them reaches the base branch; only a pure --disable-auto is out of scope. The
9
+ predicate and the PR selector reader live in enforce-epic-merge-gate-command.ps1.
10
+
11
+ Docs-only branch (issue #927): after the standalone branch denies an explicit PR number,
12
+ and only when no checkpoint is present at the repository root (both checkpoint texts are
13
+ blank; operator decision OD-1, stricter than the Claude per-PR rule), the gate calls
14
+ Get-DocsOnlyMergeExemption from hook-docs-only-diff.ps1 with the repository root. That
15
+ function runs gh pr view and git there and allows the merge when its conditions M1-M9
16
+ hold; otherwise the deny gains " (docs-only exemption not applied: <Reason>)", where the
17
+ reason may be DOCS_ONLY_EXEMPTION_PR_UNRESOLVED. A blank RepositoryRoot denies at M2
18
+ before any process starts. The hook never fetches; --match-head-commit, when present,
19
+ must equal headRefOid (OD-2). Residual limits: an ambient GH_REPO or GH_HOST is not
20
+ visible in the command text; gh and git run in the repository root, so a cd made by an
21
+ earlier shell call is not visible either; a push after the lookup can change the head
22
+ unless --match-head-commit is passed; and the control is policy-level, not cryptographic.
4
23
  #>
5
24
  [CmdletBinding()]
6
25
  param()
26
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
7
27
 
8
28
  # Shared command-line parser (issue #545). The scope filter and the PR-number resolver below
9
29
  # both run against the segment that structurally invokes `gh pr merge`, which is what keeps
10
30
  # the two runtimes on one implementation of the same concern.
11
- . (Join-Path $PSScriptRoot 'hook-command-scanner.ps1')
12
- . (Join-Path $PSScriptRoot 'hook-command-invocation.ps1')
31
+ try { . (Join-Path $PSScriptRoot 'hook-command-scanner.ps1') } catch { Add-HookDependencyFailure -Name 'hook-command-scanner.ps1' -ErrorRecord $_ }
32
+ try { . (Join-Path $PSScriptRoot 'hook-command-invocation.ps1') } catch { Add-HookDependencyFailure -Name 'hook-command-invocation.ps1' -ErrorRecord $_ }
33
+ # Merge and enqueue scope predicate and the PR selector reader (issue #952), byte-identical to the
34
+ # Claude copy.
35
+ try { . (Join-Path $PSScriptRoot 'enforce-epic-merge-gate-command.ps1') } catch { Add-HookDependencyFailure -Name 'enforce-epic-merge-gate-command.ps1' -ErrorRecord $_ }
36
+ # Shared docs-only diff classification and the merge-side docs-only exemption (issue #927).
37
+ try { . (Join-Path $PSScriptRoot 'hook-docs-only-diff.ps1') } catch { Add-HookDependencyFailure -Name 'hook-docs-only-diff.ps1' -ErrorRecord $_ }
13
38
 
14
39
  # Reason codes for the standalone-merge authorization branch (issue #670), spelled
15
40
  # byte-identically to the Claude helpers file. Each of the four assignments below is the only
@@ -44,15 +69,11 @@ function Get-CodexMergeCommandPrNumber {
44
69
  .SYNOPSIS
45
70
  Resolve the explicit pull-request number of a gh pr merge invocation, or $null.
46
71
  .DESCRIPTION
47
- The number is taken from the segment that structurally invokes `gh pr merge` and
48
- from nowhere else: the positional spelling through Get-CommandLineOperand, the
49
- flag-led and equals-joined spellings through Get-CommandLineFlagValue. A leading
50
- `cd <path>` segment contributes no operand and no flag value, so no digit run
72
+ The number is resolved by Get-EpicMergeGateSelectorPrNumber in
73
+ enforce-epic-merge-gate-command.ps1 (issue #952), the file shared byte-identically with
74
+ the Claude runtime, from the segment that structurally invokes gh pr merge or gh api and
75
+ from nowhere else. A leading cd <path> segment contributes no selector, so no digit run
51
76
  outside the merge segment can be mistaken for a pull-request number.
52
-
53
- This copy has never carried the Claude copy's unanchored whole-text digit scan and
54
- deliberately does not acquire one. No regular expression matching a bare digit run
55
- is introduced here; the only pattern below anchors an all-digit token end to end.
56
77
  .OUTPUTS
57
78
  System.Nullable[int]
58
79
  #>
@@ -60,18 +81,7 @@ function Get-CodexMergeCommandPrNumber {
60
81
  [OutputType([int])]
61
82
  param([Parameter(Mandatory)][string] $Command)
62
83
 
63
- foreach ($operand in @(Get-CommandLineOperand -CommandText $Command -CommandWord 'gh' -SubcommandPath @('pr', 'merge'))) {
64
- if ($operand -match '^\d+$') {
65
- return [int]$operand
66
- }
67
- }
68
-
69
- $flagValue = Get-CommandLineFlagValue -CommandText $Command -CommandWord 'gh' -SubcommandPath @('pr', 'merge') -FlagName '--merge'
70
- if ($null -ne $flagValue -and $flagValue -match '^\d+$') {
71
- return [int]$flagValue
72
- }
73
-
74
- return $null
84
+ return Get-EpicMergeGateSelectorPrNumber -CommandText $Command
75
85
  }
76
86
 
77
87
  function Test-CodexChildMergeReady {
@@ -125,31 +135,20 @@ function Invoke-CodexEpicMergeDecision {
125
135
  param(
126
136
  [Parameter(Mandatory)][string] $PayloadRaw,
127
137
  [AllowNull()][AllowEmptyString()][string] $ChildCheckpointRaw,
128
- [AllowNull()][AllowEmptyString()][string] $EpicCheckpointRaw
138
+ [AllowNull()][AllowEmptyString()][string] $EpicCheckpointRaw,
139
+ [AllowNull()][AllowEmptyString()][string] $RepositoryRoot
129
140
  )
141
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EPIC_MERGE_GATE_BLOCKED:'
142
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
130
143
 
131
144
  $payload = ConvertFrom-CodexMergeJson -Raw $PayloadRaw -Name 'PreToolUse input'
132
145
  if ([string]$payload.tool_name -ne 'Bash') {
133
146
  return $null
134
147
  }
135
148
  $command = [string]$payload.tool_input.command
136
- # Both legs are read structurally from the segment that invokes the command, so a
137
- # quoted mention of the phrase no longer brings a command into scope. The flag leg needs
138
- # a raw-scan fallback: a wrapper's quoted argument collapses into ONE token, so a flag
139
- # inside it is never read as a token, and the token-only read took
140
- # bash -c "gh pr merge --merge 688" out of scope.
141
- $isMergeInvocation = Test-CommandLineInvocation -CommandText $command -CommandWord 'gh' -SubcommandPath @('pr', 'merge')
142
- $hasMergeFlag = Test-CommandLineFlag -CommandText $command -CommandWord 'gh' -SubcommandPath @('pr', 'merge') -FlagName '--merge'
143
- if (-not $hasMergeFlag) {
144
- foreach ($segment in @(Read-CommandLineSegment -CommandText $command)) {
145
- if ((Test-CommandLineSegmentRawScan -Segment $segment) -and
146
- $segment.ScanText.IndexOf('--merge', [System.StringComparison]::OrdinalIgnoreCase) -ge 0) {
147
- $hasMergeFlag = $true
148
- break
149
- }
150
- }
151
- }
152
- if (-not $isMergeInvocation -or -not $hasMergeFlag) {
149
+ # Scope (issue #952): every gh pr merge invocation except a pure --disable-auto, and every gh api
150
+ # merge or enqueue mutation, is gated; Test-EpicMergeGateCommandInScope holds the predicate.
151
+ if (-not (Test-EpicMergeGateCommandInScope -CommandText $command)) {
153
152
  return $null
154
153
  }
155
154
 
@@ -172,11 +171,20 @@ function Invoke-CodexEpicMergeDecision {
172
171
  if ($verdict.Allowed) {
173
172
  return $null
174
173
  }
174
+ # Docs-only branch (issue #927, OD-1): only when no checkpoint is present at the root.
175
+ $docsOnlySuffix = ''
176
+ if ([string]::IsNullOrWhiteSpace($ChildCheckpointRaw) -and [string]::IsNullOrWhiteSpace($EpicCheckpointRaw)) {
177
+ $docsOnly = Get-DocsOnlyMergeExemption -CommandText $command -PrNumber $prNumber -WorktreeRoot $RepositoryRoot
178
+ if ($docsOnly.Exempt) {
179
+ return $null
180
+ }
181
+ $docsOnlySuffix = ' (docs-only exemption not applied: ' + $docsOnly.Reason + ')'
182
+ }
175
183
  return [ordered]@{
176
184
  hookSpecificOutput = [ordered]@{
177
185
  hookEventName = 'PreToolUse'
178
186
  permissionDecision = 'deny'
179
- permissionDecisionReason = 'EPIC_MERGE_GATE_BLOCKED: ' + $verdict.ReasonCode + ': ' + $verdict.Message
187
+ permissionDecisionReason = 'EPIC_MERGE_GATE_BLOCKED: ' + $verdict.ReasonCode + ': ' + $verdict.Message + $docsOnlySuffix
180
188
  }
181
189
  }
182
190
  }
@@ -185,7 +193,7 @@ function Invoke-CodexEpicMergeDecision {
185
193
  hookSpecificOutput = [ordered]@{
186
194
  hookEventName = 'PreToolUse'
187
195
  permissionDecision = 'deny'
188
- permissionDecisionReason = 'EPIC_MERGE_GATE_BLOCKED: gh pr merge --merge requires a safe epic child checkpoint or a successful final epic CI gate with a matching PR number.'
196
+ permissionDecisionReason = 'EPIC_MERGE_GATE_BLOCKED: a pull-request merge or merge-queue enqueue (gh pr merge with any method, --auto, or --admin, or a gh api merge or enqueue mutation) requires a safe epic child checkpoint or a successful final epic CI gate with a matching PR number.'
189
197
  }
190
198
  }
191
199
  }
@@ -360,6 +368,9 @@ function Test-CodexStandaloneMergeAuthorization {
360
368
  if ($MyInvocation.InvocationName -eq '.') {
361
369
  return
362
370
  }
371
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('EPIC_MERGE_GATE_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
372
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EPIC_MERGE_GATE_BLOCKED:'
373
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
363
374
 
364
375
  try {
365
376
  $payloadRaw = [Console]::In.ReadToEnd()
@@ -368,7 +379,7 @@ try {
368
379
  $epicPath = Join-Path $repositoryRoot 'artifacts/orchestration/epic-orchestrator-state.json'
369
380
  $childRaw = if (Test-Path -LiteralPath $childPath -PathType Leaf) { Get-Content -Raw -LiteralPath $childPath } else { '' }
370
381
  $epicRaw = if (Test-Path -LiteralPath $epicPath -PathType Leaf) { Get-Content -Raw -LiteralPath $epicPath } else { '' }
371
- $decision = Invoke-CodexEpicMergeDecision -PayloadRaw $payloadRaw -ChildCheckpointRaw $childRaw -EpicCheckpointRaw $epicRaw
382
+ $decision = Invoke-CodexEpicMergeDecision -PayloadRaw $payloadRaw -ChildCheckpointRaw $childRaw -EpicCheckpointRaw $epicRaw -RepositoryRoot $repositoryRoot
372
383
  if ($null -ne $decision) {
373
384
  $decision | ConvertTo-Json -Compress -Depth 5 | Write-Output
374
385
  }
@@ -9,6 +9,7 @@
9
9
  #>
10
10
  [CmdletBinding()]
11
11
  param()
12
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
12
13
 
13
14
  $script:AllowedPreparationMcpTools = @(
14
15
  'mcp__drm-copilot__new_potential_entry',
@@ -209,6 +210,8 @@ function Invoke-EpicPlanningOnlyDecision {
209
210
  [AllowNull()][AllowEmptyString()][string] $CurrentBranch,
210
211
  [string] $RegistryPath = (Join-Path $script:EpicPlanningRepositoryRoot 'config/orchestration-handoff-registry.json')
211
212
  )
213
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EPIC_PLANNING_ONLY_BLOCKED:'
214
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
212
215
 
213
216
  $payload = ConvertFrom-EpicPlanningJson -Raw $PayloadRaw -Name 'PreToolUse input'
214
217
  $attestedPreparation = $EpicExecutionContext -eq 'epic_preparation_child'
@@ -325,6 +328,9 @@ function Get-EpicPlanningCurrentBranch {
325
328
  if ($MyInvocation.InvocationName -eq '.') {
326
329
  return
327
330
  }
331
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('EPIC_PLANNING_ONLY_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
332
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EPIC_PLANNING_ONLY_BLOCKED:'
333
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
328
334
 
329
335
  try {
330
336
  $payloadRaw = [Console]::In.ReadToEnd()
@@ -8,8 +8,9 @@
8
8
  #>
9
9
  [CmdletBinding()]
10
10
  param()
11
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
11
12
 
12
- . (Join-Path $PSScriptRoot 'codex-authority-store.ps1')
13
+ try { . (Join-Path $PSScriptRoot 'codex-authority-store.ps1') } catch { Add-HookDependencyFailure -Name 'codex-authority-store.ps1' -ErrorRecord $_ }
13
14
 
14
15
  function ConvertFrom-EpicRootGatePayload {
15
16
  [CmdletBinding()]
@@ -54,6 +55,8 @@ function Invoke-EpicRootInvocationDecision {
54
55
  [Parameter(Mandatory)][string] $PayloadRaw,
55
56
  [AllowNull()][AllowEmptyString()][string] $AttestationRaw
56
57
  )
58
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EPIC_INVOCATION_ORIGIN_BLOCKED:'
59
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
57
60
 
58
61
  $payload = ConvertFrom-EpicRootGatePayload -Raw $PayloadRaw -Name 'PreToolUse input'
59
62
  if ([string]::IsNullOrWhiteSpace($AttestationRaw)) {
@@ -87,6 +90,9 @@ function Invoke-EpicRootInvocationDecision {
87
90
  if ($MyInvocation.InvocationName -eq '.') {
88
91
  return
89
92
  }
93
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('EPIC_INVOCATION_ORIGIN_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
94
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EPIC_INVOCATION_ORIGIN_BLOCKED:'
95
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
90
96
 
91
97
  try {
92
98
  $payloadRaw = [Console]::In.ReadToEnd()
@@ -10,8 +10,9 @@
10
10
  #>
11
11
  [CmdletBinding()]
12
12
  param()
13
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
13
14
 
14
- . (Join-Path $PSScriptRoot 'codex-epic-child-launch-attestation.ps1')
15
+ try { . (Join-Path $PSScriptRoot 'codex-epic-child-launch-attestation.ps1') } catch { Add-HookDependencyFailure -Name 'codex-epic-child-launch-attestation.ps1' -ErrorRecord $_ }
15
16
 
16
17
  $script:AllowedDependencyStatuses = @('merged', 'worktree_removed')
17
18
 
@@ -178,6 +179,8 @@ function Invoke-CodexEpicWaveDecision {
178
179
  [AllowEmptyString()][string] $RepositoryRoot = '',
179
180
  [datetimeoffset] $Now = [datetimeoffset]::UtcNow
180
181
  )
182
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EPIC_WAVE_BARRIER_BLOCKED:'
183
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
181
184
 
182
185
  $payload = ConvertFrom-CodexWaveJson -Raw $PayloadRaw -Name 'PreToolUse input'
183
186
  if (-not (Test-CodexWaveMutation -Payload $payload)) {
@@ -252,6 +255,9 @@ function Get-CodexPrimaryWorktreeRoot {
252
255
  if ($MyInvocation.InvocationName -eq '.') {
253
256
  return
254
257
  }
258
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('EPIC_WAVE_BARRIER_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
259
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EPIC_WAVE_BARRIER_BLOCKED:'
260
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
255
261
 
256
262
  try {
257
263
  $payloadRaw = [Console]::In.ReadToEnd()
@@ -4,12 +4,13 @@
4
4
  #>
5
5
  [CmdletBinding()]
6
6
  param()
7
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
7
8
 
8
9
  # Shared command-line parser (issue #545). The scope filter and the operand extractor below
9
10
  # both run against the segment that structurally invokes `git worktree remove`, which is what
10
11
  # keeps the two runtimes on one implementation of the same concern.
11
- . (Join-Path $PSScriptRoot 'hook-command-scanner.ps1')
12
- . (Join-Path $PSScriptRoot 'hook-command-invocation.ps1')
12
+ try { . (Join-Path $PSScriptRoot 'hook-command-scanner.ps1') } catch { Add-HookDependencyFailure -Name 'hook-command-scanner.ps1' -ErrorRecord $_ }
13
+ try { . (Join-Path $PSScriptRoot 'hook-command-invocation.ps1') } catch { Add-HookDependencyFailure -Name 'hook-command-invocation.ps1' -ErrorRecord $_ }
13
14
 
14
15
  $script:SafeWorktreeStatuses = @('merged', 'worktree_removed')
15
16
 
@@ -33,40 +34,6 @@ function ConvertFrom-CodexWorktreeJson {
33
34
  }
34
35
  }
35
36
 
36
- function Get-CodexWorktreeRemovalPath {
37
- <#
38
- .SYNOPSIS
39
- Extract the target worktree path from a git worktree remove command.
40
- .DESCRIPTION
41
- The operand comes from the segment that structurally invokes git worktree remove, so
42
- a chained 'cd <path> &&' segment contributes nothing and a quoted mention of the
43
- phrase resolves to no operand. The tokenizer strips balanced double and single
44
- quotes, which is what the previous pattern's `double` and `single` alternatives did.
45
-
46
- The previous pattern accepted '--force' only immediately after 'remove'. That
47
- spelling is preserved and the trailing spelling now works too, because '--force' is a
48
- zero-argument flag that never contributes an operand wherever it is written. Its
49
- presence is read structurally through Test-CommandLineFlag rather than by a raw-text
50
- search. The empty-string-on-miss contract is unchanged: callers test `if ($target)`.
51
- .OUTPUTS
52
- System.String
53
- #>
54
- [CmdletBinding()]
55
- [OutputType([string])]
56
- param([Parameter(Mandatory)][AllowEmptyString()][string] $Command)
57
-
58
- $hasForce = Test-CommandLineFlag -CommandText $Command -CommandWord 'git' -SubcommandPath @('worktree', 'remove') -FlagName '--force'
59
- $operands = @(Get-CommandLineOperand -CommandText $Command -CommandWord 'git' -SubcommandPath @('worktree', 'remove'))
60
-
61
- if ($operands.Count -gt 0) {
62
- return [string]$operands[0]
63
- }
64
- if ($hasForce) {
65
- return '--force'
66
- }
67
- return ''
68
- }
69
-
70
37
  function Get-NormalizedCodexWorktreePath {
71
38
  [CmdletBinding()]
72
39
  [OutputType([string])]
@@ -114,6 +81,8 @@ function Invoke-CodexWorktreeRemovalDecision {
114
81
  [Parameter(Mandatory)][string] $PayloadRaw,
115
82
  [AllowNull()][AllowEmptyString()][string] $EpicCheckpointRaw
116
83
  )
84
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EPIC_WORKTREE_REMOVAL_BLOCKED:'
85
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
117
86
 
118
87
  $payload = ConvertFrom-CodexWorktreeJson -Raw $PayloadRaw -Name 'PreToolUse input'
119
88
  if ([string]$payload.tool_name -ne 'Bash') {
@@ -126,18 +95,59 @@ function Invoke-CodexWorktreeRemovalDecision {
126
95
  if (-not (Test-CommandLineInvocation -CommandText $command -CommandWord 'git' -SubcommandPath @('worktree', 'remove'))) {
127
96
  return $null
128
97
  }
129
- $target = Get-CodexWorktreeRemovalPath -Command $command
98
+ # Issue #824: the removal targets are derived structurally from every invocation. A
99
+ # target that cannot be derived denies before the checkpoint is read, and every derived
100
+ # target must be authorized on its own.
101
+ $resolution = Resolve-CommandLineInvocationTarget -CommandText $command -CommandWord 'git' -SubcommandPath @('worktree', 'remove')
102
+ if ($resolution.Status -eq 'NoMatch') {
103
+ return $null
104
+ }
105
+ if ($resolution.Status -ne 'Targets') {
106
+ return [ordered]@{
107
+ hookSpecificOutput = [ordered]@{
108
+ hookEventName = 'PreToolUse'
109
+ permissionDecision = 'deny'
110
+ permissionDecisionReason = '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.'
111
+ }
112
+ }
113
+ }
130
114
  $checkpoint = ConvertFrom-CodexWorktreeJson -Raw $EpicCheckpointRaw -Name 'epic checkpoint' -Optional
131
115
  $workingDirectory = if ([string]::IsNullOrWhiteSpace([string]$payload.cwd)) {
132
116
  (Get-Location).Path
133
117
  } else {
134
118
  [string]$payload.cwd
135
119
  }
136
- $feature = if ($target) {
137
- Find-CodexWorktreeFeature -Checkpoint $checkpoint -TargetPath $target -WorkingDirectory $workingDirectory
138
- } else {
139
- $null
120
+ foreach ($target in @($resolution.Targets)) {
121
+ $denial = Get-CodexWorktreeRemovalTargetDenial -Target $target -Checkpoint $checkpoint -WorkingDirectory $workingDirectory
122
+ if ($null -ne $denial) {
123
+ return $denial
124
+ }
140
125
  }
126
+ return $null
127
+ }
128
+
129
+ function Get-CodexWorktreeRemovalTargetDenial {
130
+ <#
131
+ .SYNOPSIS
132
+ Evaluate one derived removal target and return its deny decision, or $null when authorized.
133
+ .PARAMETER Target
134
+ One removal target derived by Resolve-CommandLineInvocationTarget.
135
+ .PARAMETER Checkpoint
136
+ The parsed epic checkpoint, or $null when absent or unreadable.
137
+ .PARAMETER WorkingDirectory
138
+ The directory a relative target resolves against.
139
+ .OUTPUTS
140
+ System.Collections.Specialized.OrderedDictionary or $null
141
+ #>
142
+ [CmdletBinding()]
143
+ [OutputType([System.Collections.Specialized.OrderedDictionary])]
144
+ param(
145
+ [Parameter(Mandatory)][string] $Target,
146
+ [AllowNull()] $Checkpoint,
147
+ [Parameter(Mandatory)][string] $WorkingDirectory
148
+ )
149
+
150
+ $feature = Find-CodexWorktreeFeature -Checkpoint $Checkpoint -TargetPath $Target -WorkingDirectory $WorkingDirectory
141
151
  if ($null -ne $feature -and
142
152
  @($feature.PSObject.Properties.Name) -contains 'merge_status' -and
143
153
  $script:SafeWorktreeStatuses -contains [string]$feature.merge_status) {
@@ -156,6 +166,9 @@ function Invoke-CodexWorktreeRemovalDecision {
156
166
  if ($MyInvocation.InvocationName -eq '.') {
157
167
  return
158
168
  }
169
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('EPIC_WORKTREE_REMOVAL_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
170
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EPIC_WORKTREE_REMOVAL_BLOCKED:'
171
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
159
172
 
160
173
  try {
161
174
  $payloadRaw = [Console]::In.ReadToEnd()
@@ -40,10 +40,11 @@
40
40
  #>
41
41
  [CmdletBinding()]
42
42
  param()
43
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
43
44
 
44
45
  # Shared Codex PreToolUse transport: stdin payload parsing and tool_input-to-file
45
46
  # mapping for every tool name the ^(apply_patch|Edit|Write)$ matcher admits.
46
- . (Join-Path $PSScriptRoot 'codex-pretooluse-file-mapping.ps1')
47
+ try { . (Join-Path $PSScriptRoot 'codex-pretooluse-file-mapping.ps1') } catch { Add-HookDependencyFailure -Name 'codex-pretooluse-file-mapping.ps1' -ErrorRecord $_ }
47
48
 
48
49
  function Test-EvidenceLocationForbidden {
49
50
  <#
@@ -122,6 +123,8 @@ function Invoke-EvidenceLocationDecision {
122
123
  param(
123
124
  [string] $ToolInputRaw
124
125
  )
126
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EVIDENCE_LOCATION_BLOCKED:'
127
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
125
128
 
126
129
  if (-not $ToolInputRaw) {
127
130
  return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
@@ -192,5 +195,8 @@ function Invoke-EvidenceLocationEntryPoint {
192
195
  if ($MyInvocation.InvocationName -eq '.') {
193
196
  return
194
197
  }
198
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('EVIDENCE_LOCATION_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
199
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EVIDENCE_LOCATION_BLOCKED:'
200
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
195
201
 
196
202
  exit (Invoke-EvidenceLocationEntryPoint -PayloadRaw ([Console]::In.ReadToEnd()))