@danmoisan/drm-copilot-mcp 1.1.17 → 1.1.19

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 (225) hide show
  1. package/out/mcp-server.js +1405 -684
  2. package/package.json +6 -4
  3. package/resources/claude-customizations/.claude/agents/atomic-executor.md +11 -0
  4. package/resources/claude-customizations/.claude/agents/atomic-planner.md +10 -0
  5. package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +12 -4
  6. package/resources/claude-customizations/.claude/agents/epic-planner.md +5 -4
  7. package/resources/claude-customizations/.claude/agents/feature-review.md +16 -5
  8. package/resources/claude-customizations/.claude/agents/orchestrator.md +21 -7
  9. package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +37 -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/hooks/check-powershell-test-purity.ps1 +7 -1
  14. package/resources/claude-customizations/.claude/hooks/check-python-test-purity.ps1 +7 -1
  15. package/resources/claude-customizations/.claude/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
  16. package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +111 -46
  17. package/resources/claude-customizations/.claude/hooks/enforce-completion-helpers.ps1 +105 -0
  18. package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +8 -1
  19. package/resources/claude-customizations/.claude/hooks/enforce-epic-invocation-origin.ps1 +7 -1
  20. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-resolution.ps1 +116 -59
  21. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +55 -23
  22. package/resources/claude-customizations/.claude/hooks/enforce-epic-wave-barrier.ps1 +74 -75
  23. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate-resolution.ps1 +127 -35
  24. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +61 -57
  25. package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +7 -1
  26. package/resources/claude-customizations/.claude/hooks/enforce-feature-folder-order.ps1 +129 -39
  27. package/resources/claude-customizations/.claude/hooks/enforce-mermaid-validation.ps1 +7 -1
  28. package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +9 -3
  29. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +39 -51
  30. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
  31. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
  32. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
  33. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +17 -12
  34. package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +9 -3
  35. package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier.ps1 +65 -50
  36. package/resources/claude-customizations/.claude/hooks/enforce-parallel-drift-gate.ps1 +49 -42
  37. package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +54 -72
  38. package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +8 -2
  39. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-command-allowlist.ps1 +279 -0
  40. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +129 -75
  41. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.artifact-root.ps1 +233 -0
  42. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.docs-only.ps1 +224 -0
  43. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +69 -24
  44. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner-helpers.ps1 +29 -18
  45. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +10 -4
  46. package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +14 -5
  47. package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +8 -2
  48. package/resources/claude-customizations/.claude/hooks/feature-folder-resolution.ps1 +373 -0
  49. package/resources/claude-customizations/.claude/hooks/feature-review-coverage-thresholds.ps1 +85 -0
  50. package/resources/claude-customizations/.claude/hooks/hook-command-heredoc.ps1 +119 -0
  51. package/resources/claude-customizations/.claude/hooks/hook-command-invocation-operands.ps1 +198 -0
  52. package/resources/claude-customizations/.claude/hooks/hook-command-invocation.ps1 +254 -255
  53. package/resources/claude-customizations/.claude/hooks/hook-command-payload-powershell.ps1 +201 -0
  54. package/resources/claude-customizations/.claude/hooks/hook-command-payload.ps1 +484 -0
  55. package/resources/claude-customizations/.claude/hooks/hook-command-scanner.ps1 +27 -110
  56. package/resources/claude-customizations/.claude/hooks/hook-dependency-guard.ps1 +114 -0
  57. package/resources/claude-customizations/.claude/hooks/hook-docs-only-diff.ps1 +493 -0
  58. package/resources/claude-customizations/.claude/hooks/subagentstop-envelope.ps1 +192 -0
  59. package/resources/claude-customizations/.claude/hooks/validate-bash.ps1 +9 -3
  60. package/resources/claude-customizations/.claude/hooks/validate-discovery-artifact-gate.ps1 +40 -42
  61. package/resources/claude-customizations/.claude/hooks/validate-executor-output.ps1 +66 -38
  62. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-metrics.ps1 +180 -0
  63. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-rules.ps1 +195 -0
  64. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage.ps1 +141 -395
  65. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-resolution.ps1 +311 -0
  66. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-routing.ps1 +170 -0
  67. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +115 -190
  68. package/resources/claude-customizations/.claude/hooks/validate-planner-output.ps1 +64 -32
  69. package/resources/claude-customizations/.claude/hooks/validate-pr-author-output.ps1 +39 -44
  70. package/resources/claude-customizations/.claude/hooks/validate-prd-feature-output.ps1 +43 -14
  71. package/resources/claude-customizations/.claude/hooks/validate-required-artifact-output.ps1 +66 -36
  72. package/resources/claude-customizations/.claude/hooks/validate-task-researcher-output.ps1 +31 -34
  73. package/resources/claude-customizations/.claude/lib/bash/mutate-parallel-run.sh +191 -0
  74. package/resources/claude-customizations/.claude/lib/bash/parallel-cohorts.sh +13 -3
  75. package/resources/claude-customizations/.claude/lib/bash/parallel-lane-assertion.sh +4 -2
  76. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-cli.sh +152 -0
  77. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-ops.sh +193 -0
  78. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation.sh +322 -0
  79. package/resources/claude-customizations/.claude/lib/bash/remove-parallel-item.sh +146 -0
  80. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +23 -33
  81. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusTokenShape.psm1 +86 -6
  82. package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-CiGateParser.ps1 +91 -19
  83. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorState.psm1 +6 -13
  84. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateEpicWaveBarrier.psm1 +346 -0
  85. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1 +396 -0
  86. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateReceipts.psm1 +22 -15
  87. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRemediationAccounting.psm1 +337 -0
  88. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRoutingContract.psm1 +9 -1
  89. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +79 -2
  90. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1 +3 -3
  91. package/resources/claude-customizations/.claude/rules/architecture-boundaries.md +5 -5
  92. package/resources/claude-customizations/.claude/rules/csharp.md +2 -2
  93. package/resources/claude-customizations/.claude/rules/general-code-change.md +1 -1
  94. package/resources/claude-customizations/.claude/rules/general-unit-test.md +3 -2
  95. package/resources/claude-customizations/.claude/rules/orchestrator-state.md +153 -4
  96. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +50 -5
  97. package/resources/claude-customizations/.claude/rules/plan-acceptance-gates.md +47 -8
  98. package/resources/claude-customizations/.claude/rules/quality-tiers.md +11 -7
  99. package/resources/claude-customizations/.claude/rules/shell.md +10 -8
  100. package/resources/claude-customizations/.claude/rules/typescript.md +1 -1
  101. package/resources/claude-customizations/.claude/settings.json +3 -4
  102. package/resources/claude-customizations/.claude/skills/acceptance-criteria-tracking/SKILL.md +9 -1
  103. package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +7 -1
  104. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +11 -3
  105. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup-worktrees.sh +11 -2
  106. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_detached_lib.sh +1 -1
  107. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_enumerate_lib.sh +171 -5
  108. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_preserve_lib.sh +1 -1
  109. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_report_records_lib.sh +6 -43
  110. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_scan_helper.sh +6 -15
  111. package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +45 -11
  112. package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +1 -1
  113. package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +13 -0
  114. package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +10 -0
  115. package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +7 -4
  116. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +42 -16
  117. package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +52 -14
  118. package/resources/claude-customizations/.claude/skills/parallel-close/SKILL.md +22 -9
  119. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +97 -19
  120. package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +63 -7
  121. package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +35 -12
  122. package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +3 -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 +7 -3
  125. package/resources/claude-customizations/pack-manifests/core.json +25 -0
  126. package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +9 -1
  127. package/resources/codex-and-agents-customizations/.agents/skills/architecture-boundaries/SKILL.md +5 -5
  128. package/resources/codex-and-agents-customizations/.agents/skills/atomic-executor/SKILL.md +11 -0
  129. package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +4 -0
  130. package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +10 -0
  131. package/resources/codex-and-agents-customizations/.agents/skills/csharp/SKILL.md +2 -2
  132. package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +2 -2
  133. package/resources/codex-and-agents-customizations/.agents/skills/epic-orchestrate/SKILL.md +13 -3
  134. package/resources/codex-and-agents-customizations/.agents/skills/epic-plan/SKILL.md +17 -1
  135. package/resources/codex-and-agents-customizations/.agents/skills/epic-run/SKILL.md +2 -1
  136. package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +13 -0
  137. package/resources/codex-and-agents-customizations/.agents/skills/feature-promotion-lifecycle/SKILL.md +1 -1
  138. package/resources/codex-and-agents-customizations/.agents/skills/feature-review/SKILL.md +30 -6
  139. package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +16 -6
  140. package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +1 -1
  141. package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +3 -2
  142. package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +43 -11
  143. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-state/SKILL.md +41 -2
  144. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +19 -4
  145. package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +11 -7
  146. package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +2 -0
  147. package/resources/codex-and-agents-customizations/.agents/skills/translate-claude-to-codex/SKILL.md +1 -1
  148. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp/SKILL.md +2 -2
  149. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +2 -2
  150. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c1.toml +11 -0
  151. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +11 -0
  152. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +11 -0
  153. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +11 -0
  154. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +11 -0
  155. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +11 -0
  156. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +10 -0
  157. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +10 -0
  158. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +10 -0
  159. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +10 -0
  160. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +10 -0
  161. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +10 -0
  162. package/resources/codex-and-agents-customizations/.codex/agents/epic-orchestrator.toml +2 -1
  163. package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +6 -6
  164. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c1.toml +1 -0
  165. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c2.toml +1 -0
  166. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c3-elevated.toml +1 -0
  167. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c3.toml +1 -0
  168. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c4.toml +1 -0
  169. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer.toml +1 -0
  170. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c1.toml +3 -3
  171. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +3 -3
  172. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +3 -3
  173. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +3 -3
  174. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +3 -3
  175. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +3 -3
  176. package/resources/codex-and-agents-customizations/.codex/codex-web-setup.sh +318 -273
  177. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  178. package/resources/codex-and-agents-customizations/.codex/hooks/check-powershell-test-purity.ps1 +7 -1
  179. package/resources/codex-and-agents-customizations/.codex/hooks/check-python-test-purity.ps1 +7 -1
  180. package/resources/codex-and-agents-customizations/.codex/hooks/codex-epic-child-launch-attestation.ps1 +1 -1
  181. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
  182. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-codex-model-routing.ps1 +8 -2
  183. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +97 -44
  184. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-helpers.ps1 +105 -0
  185. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-child-worktree-binding.ps1 +7 -1
  186. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +37 -5
  187. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +6 -0
  188. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-root-invocation.ps1 +7 -1
  189. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-wave-barrier.ps1 +7 -1
  190. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-worktree-removal-gate.ps1 +54 -41
  191. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-evidence-locations.ps1 +7 -1
  192. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +35 -14
  193. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
  194. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
  195. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
  196. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +24 -11
  197. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +8 -2
  198. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +13 -4
  199. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +8 -2
  200. package/resources/codex-and-agents-customizations/.codex/hooks/feature-folder-resolution.ps1 +373 -0
  201. package/resources/codex-and-agents-customizations/.codex/hooks/feature-review-coverage-thresholds.ps1 +85 -0
  202. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-heredoc.ps1 +119 -0
  203. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation-operands.ps1 +198 -0
  204. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation.ps1 +254 -255
  205. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload-powershell.ps1 +201 -0
  206. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload.ps1 +484 -0
  207. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-scanner.ps1 +27 -110
  208. package/resources/codex-and-agents-customizations/.codex/hooks/hook-dependency-guard.ps1 +114 -0
  209. package/resources/codex-and-agents-customizations/.codex/hooks/hook-docs-only-diff.ps1 +493 -0
  210. package/resources/codex-and-agents-customizations/.codex/hooks/validate-bash.ps1 +8 -2
  211. package/resources/codex-and-agents-customizations/.codex/hooks/validate-codex-subagent-routing.ps1 +7 -1
  212. package/resources/codex-and-agents-customizations/.codex/hooks/validate-feature-review-coverage.ps1 +24 -8
  213. package/resources/codex-and-agents-customizations/pack-manifests/core.json +9 -0
  214. package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +2 -2
  215. package/resources/customizations/.github/instructions/csharp-code-change.instructions.md +4 -4
  216. package/resources/customizations/.github/instructions/csharp-unit-test.instructions.md +2 -2
  217. package/resources/customizations/.github/skills/acceptance-criteria-tracking/SKILL.md +9 -1
  218. package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +4 -0
  219. package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +13 -0
  220. package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +1 -1
  221. package/resources/powershell/PoshQC/PoshQC.Coverage.psm1 +314 -0
  222. package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +25 -28
  223. package/resources/powershell/PoshQC/PoshQC.psm1 +1 -0
  224. package/resources/powershell/PoshQC/README.md +8 -2
  225. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +2 -315
@@ -13,24 +13,29 @@
13
13
  conflict_edges[], and position is read from the current cohort coloring.
14
14
 
15
15
  Resolution and decision procedure:
16
- 1. Resolve the target item's feature folder from the prompt text by scanning for a
17
- docs/features/active/<token> path, mirroring the epic hook's technique
18
- (longest match wins; a .md-suffixed match uses its parent directory).
19
- 2. Resolve the worktree whose parallel checkpoint records the prompt's
20
- parallel_slug: value (issue #690), deny an unresolved or ambiguous target, then
21
- read artifacts/orchestration/parallel-orchestrator-state.json beneath that
22
- worktree and locate the items[] record whose feature_folder resolves to the same
23
- basename.
24
- 3. Project the cohort coloring to the cohorts[] rows whose generation equals the
16
+ 1. Resolve the worktree whose parallel checkpoint records the prompt's
17
+ parallel_slug: value (issue #690) and deny an unresolved or ambiguous target.
18
+ 2. Collect the cited feature-folder candidates with the shared resolver
19
+ (feature-folder-resolution.ps1, issue #565): every docs/features/active/<token>
20
+ path is truncated to its feature-folder segment, so a nested research/ or
21
+ evidence/ citation names its own folder. No candidate denies.
22
+ 3. Read artifacts/orchestration/parallel-orchestrator-state.json beneath that
23
+ worktree and select the target among the candidates with
24
+ Select-FeatureFolderTarget. The canonical issue-number line, when it names exactly
25
+ one number, breaks a tie among the cited run items; two or more remaining
26
+ candidates otherwise deny as ambiguous. Neither string length nor prompt position
27
+ takes part in the selection. Then locate the items[] record whose feature_folder
28
+ resolves to the selected basename.
29
+ 4. Project the cohort coloring to the cohorts[] rows whose generation equals the
25
30
  top-level recolor_generation, and read the target item's cohort index from that
26
31
  projection.
27
- 4. Collect every conflict_edges[] neighbor of the target item.
28
- 5. Deny with reason PARALLEL_COHORT_BARRIER_BLOCKED unless every neighbor that sits
32
+ 5. Collect every conflict_edges[] neighbor of the target item.
33
+ 6. Deny with reason PARALLEL_COHORT_BARRIER_BLOCKED unless every neighbor that sits
29
34
  in a strictly prior current-generation cohort has merge_status in
30
35
  {merged, worktree_removed}. ci_green does NOT satisfy the barrier: an item whose
31
36
  CI is green has not merged, so its worktree is still live and its contention is
32
37
  unresolved. Same-cohort and later-cohort neighbors do not block Layer 1.
33
- 6. A missing or unparseable checkpoint, an unresolved feature-folder token, a
38
+ 7. A missing or unparseable checkpoint, an unresolved feature-folder token, a
34
39
  missing items[] record, a target with no current-generation cohort assignment, a
35
40
  missing neighbor record, and a missing merge_status all deny (fail-closed).
36
41
 
@@ -43,24 +48,33 @@
43
48
  SubagentStop time.
44
49
 
45
50
  .NOTES
46
- Compatible with PowerShell 7+. Depends on WorktreeRunResolution.psm1 (issue #690),
47
- imported inside a guard: a failed import is recorded and the decision denies naming
48
- the module. Filesystem reads go through an injectable wrapper function so tests can
49
- mock the boundary without writing temporary files.
51
+ Compatible with PowerShell 7+. Depends on WorktreeItemResolution.psm1 and
52
+ WorktreeRunResolution.psm1 (issue #690) and the pure sibling
53
+ feature-folder-resolution.ps1 (issue #565), each loaded inside a guard: the first
54
+ failure is recorded and the decision denies naming the dependency. Filesystem reads go
55
+ through an injectable wrapper function so tests can mock the boundary without writing
56
+ temporary files.
50
57
  #>
51
58
  [CmdletBinding()]
52
59
  param()
60
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
53
61
 
54
62
 
55
- Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force
63
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'HookPayload.psm1' -ErrorRecord $_ }
56
64
 
57
- # Import guard (issue #690): a failed import denies instead of failing open.
65
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeItemResolution.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'WorktreeItemResolution.psm1' -ErrorRecord $_ }
66
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeRunResolution.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'WorktreeRunResolution.psm1' -ErrorRecord $_ }
58
67
  $script:ParallelCohortBarrierResolutionImportFailure = $null
68
+
69
+ # Shared feature-folder resolution (issue #565). Guarded so a failed dot-source denies
70
+ # rather than failing open; an earlier recorded failure is kept.
59
71
  try {
60
- Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeRunResolution.psm1') -Force -ErrorAction Stop
72
+ . (Join-Path $PSScriptRoot 'feature-folder-resolution.ps1')
61
73
  }
62
74
  catch {
63
- $script:ParallelCohortBarrierResolutionImportFailure = 'WorktreeRunResolution.psm1'
75
+ if (-not $script:ParallelCohortBarrierResolutionImportFailure) {
76
+ $script:ParallelCohortBarrierResolutionImportFailure = 'feature-folder-resolution.ps1'
77
+ }
64
78
  }
65
79
 
66
80
  $script:AllowedMergeStatuses = @('merged', 'worktree_removed')
@@ -68,7 +82,7 @@ $script:ParallelModeMarker = 'Parallel mode: true'
68
82
 
69
83
  # Dot-source the record-resolution and barrier helpers. Guarded so dot-sourcing this
70
84
  # hook in tests loads the helpers too (issue #501 headroom split).
71
- . (Join-Path $PSScriptRoot 'enforce-parallel-cohort-barrier-helpers.ps1')
85
+ try { . (Join-Path $PSScriptRoot 'enforce-parallel-cohort-barrier-helpers.ps1') } catch { Add-HookDependencyFailure -Name 'enforce-parallel-cohort-barrier-helpers.ps1' -ErrorRecord $_ }
72
86
 
73
87
  function Get-ParallelCohortBarrierCheckpointContent {
74
88
  <#
@@ -147,43 +161,27 @@ function Get-ParallelCohortBarrierFolderBasename {
147
161
  function Find-ParallelCohortBarrierFeatureFolderFromPrompt {
148
162
  <#
149
163
  .SYNOPSIS
150
- Scans a prompt string for docs/features/active/<...> path tokens and returns the
151
- longest unique match's basename. Returns $null when no match is found.
164
+ Returns the distinct feature-folder basenames cited in a prompt, in first-occurrence
165
+ order. Returns no output when no folder is cited.
152
166
  .DESCRIPTION
153
- Mirrors the epic wave-barrier hook's technique: forward- or backslash-separated
154
- path tokens are accepted, the longest match wins, and a .md-suffixed match resolves
155
- to its parent directory before the basename is extracted.
167
+ Delegates to Find-FeatureFolderCandidate in the shared resolver (issue #565): each
168
+ docs/features/active/<token> path, in either separator style, is truncated to its
169
+ feature-folder segment, so a nested research/ or evidence/ citation names its own
170
+ folder. Selection among several candidates is made by Select-FeatureFolderTarget.
156
171
  .PARAMETER Prompt
157
172
  The delegation prompt text under evaluation.
158
173
  .OUTPUTS
159
- System.String or $null
174
+ System.String[]
160
175
  #>
161
176
  [CmdletBinding()]
162
- [OutputType([string])]
177
+ [OutputType([string[]])]
163
178
  param(
164
179
  [Parameter(Mandatory)]
165
180
  [AllowEmptyString()]
166
181
  [string] $Prompt
167
182
  )
168
183
 
169
- if (-not $Prompt) {
170
- return $null
171
- }
172
-
173
- $pattern = 'docs[\\/]+features[\\/]+active[\\/]+[^\s"''`]+'
174
- $matchList = [regex]::Matches($Prompt, $pattern)
175
- if ($matchList.Count -eq 0) {
176
- return $null
177
- }
178
-
179
- $unique = @{}
180
- foreach ($m in $matchList) {
181
- $normalized = ($m.Value -replace '\\', '/').TrimEnd('/')
182
- $unique[$normalized] = $true
183
- }
184
-
185
- $candidates = @(@($unique.Keys) | Sort-Object -Property Length -Descending)
186
- return (Get-ParallelCohortBarrierFolderBasename -FolderPath $candidates[0])
184
+ return [string[]]@(Find-FeatureFolderCandidate -Text $Prompt)
187
185
  }
188
186
 
189
187
  function Get-ParallelCohortBarrierAllowDecision {
@@ -230,11 +228,13 @@ function Invoke-ParallelCohortBarrierDecision {
230
228
  param(
231
229
  [string] $ToolInputRaw
232
230
  )
231
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'PARALLEL_COHORT_BARRIER_BLOCKED:'
232
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
233
233
 
234
- # A failed worktree-resolution import denies before any other logic (issue #690).
234
+ # A failed dependency import denies before any other logic (issue #565).
235
235
  if ($script:ParallelCohortBarrierResolutionImportFailure) {
236
236
  return Get-ParallelCohortBarrierBlockDecision -Reason (
237
- "PARALLEL_COHORT_BARRIER_BLOCKED: the worktree-resolution module '$($script:ParallelCohortBarrierResolutionImportFailure)' " +
237
+ "PARALLEL_COHORT_BARRIER_BLOCKED: the dependency '$($script:ParallelCohortBarrierResolutionImportFailure)' " +
238
238
  'failed to import, so the parallel checkpoint that governs this delegation cannot be located; the gate fails closed.')
239
239
  }
240
240
 
@@ -262,8 +262,8 @@ function Invoke-ParallelCohortBarrierDecision {
262
262
  return Get-ParallelCohortBarrierBlockDecision -Reason "PARALLEL_COHORT_BARRIER_BLOCKED: $($target.ReasonCode): $($target.Detail)"
263
263
  }
264
264
 
265
- $featureFolder = Find-ParallelCohortBarrierFeatureFolderFromPrompt -Prompt $prompt
266
- if (-not $featureFolder) {
265
+ $candidates = @(Find-ParallelCohortBarrierFeatureFolderFromPrompt -Prompt $prompt)
266
+ if ($candidates.Count -eq 0) {
267
267
  return Get-ParallelCohortBarrierBlockDecision -Reason 'PARALLEL_COHORT_BARRIER_BLOCKED: a parallel-mode orchestrator delegation must reference the target item feature folder path (docs/features/active/<folder>) in the prompt so its conflict edges and cohort position can be verified.'
268
268
  }
269
269
 
@@ -277,6 +277,18 @@ function Invoke-ParallelCohortBarrierDecision {
277
277
  }
278
278
  }
279
279
 
280
+ # The target is selected among the cited candidates (issue #565). The canonical
281
+ # issue-number line breaks a tie only when it names exactly one number.
282
+ $records = if ($null -ne $checkpoint -and @($checkpoint.PSObject.Properties.Name) -contains 'items') { @($checkpoint.items) } else { @() }
283
+ $selectionArguments = @{ Records = $records; Candidate = $candidates }
284
+ $declared = Find-WorktreeItemIssueSignal -Text $prompt
285
+ if (@($declared).Count -eq 1) { $selectionArguments['DeclaredIssueNumber'] = [int]@($declared)[0] }
286
+ $selection = Select-FeatureFolderTarget @selectionArguments
287
+ if ($selection.Status -eq 'Ambiguous') {
288
+ return Get-ParallelCohortBarrierBlockDecision -Reason "PARALLEL_COHORT_BARRIER_BLOCKED: $($selection.Detail)."
289
+ }
290
+ $featureFolder = $selection.Basename
291
+
280
292
  $itemRecord = Find-ParallelCohortBarrierItemRecord -Checkpoint $checkpoint -FeatureFolder $featureFolder
281
293
  if (Test-ParallelCohortBarrierClear -Checkpoint $checkpoint -ItemRecord $itemRecord) {
282
294
  return Get-ParallelCohortBarrierAllowDecision
@@ -327,5 +339,8 @@ function Invoke-ParallelCohortBarrierEntryPoint {
327
339
  if ($MyInvocation.InvocationName -eq '.') {
328
340
  return
329
341
  }
342
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('PARALLEL_COHORT_BARRIER_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
343
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'PARALLEL_COHORT_BARRIER_BLOCKED:'
344
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
330
345
 
331
346
  exit (Invoke-ParallelCohortBarrierEntryPoint)
@@ -8,13 +8,14 @@
8
8
  parallel-mode kickoff marker defined in the "Parallel-Mode Kickoff Parameter" section
9
9
  of .claude/skills/parallel-orchestrate/SKILL.md, matched byte-for-byte (ordinal).
10
10
 
11
- Decision procedure: resolve the target item's feature folder from the prompt by scanning
12
- for a docs/features/active/<token> path, the shape the parallel kickoff contract emits
13
- for exactly this purpose (adapted from enforce-epic-wave-barrier.ps1's
14
- Find-EpicWaveBarrierFeatureFolderFromPrompt: longest match wins, a .md-suffixed match
15
- uses its parent directory); read the parallel checkpoint and locate the items[] record
16
- whose feature_folder basename matches; derive which item keys have an unresolved latest
17
- drift event; deny with PARALLEL_DRIFT_GATE_BLOCKED when the resolved item is one of them
11
+ Decision procedure: collect the cited feature-folder candidates with the shared resolver
12
+ (feature-folder-resolution.ps1, issue #565), which truncates every docs/features/active/
13
+ path to its feature-folder segment; read the parallel checkpoint and select the target
14
+ among the candidates with Select-FeatureFolderTarget, using the canonical issue-number
15
+ line as a tie-break when it names exactly one number and denying an unresolved tie as
16
+ ambiguous; locate the items[] record whose feature_folder basename matches the selected
17
+ basename, which is also the folder the finding probe inspects; derive which item keys
18
+ have an unresolved latest drift event; deny with PARALLEL_DRIFT_GATE_BLOCKED when the resolved item is one of them
18
19
  and no synthetic Blocking finding file dated at or after that item's latest drift event has
19
20
  been written. Allowed: a non-feature-review target, a prompt without the marker, a resolved
20
21
  latest event, and an unresolved event whose finding file is dated at or after it, so the
@@ -50,8 +51,10 @@
50
51
  ambiguous target denies after the subagent and marker filter and before the folder check.
51
52
 
52
53
  .NOTES
53
- PowerShell 7+. Depends on WorktreeRunResolution.psm1 (issue #690), imported inside a
54
- guard: a failed import is recorded and the decision denies naming the module. Both read
54
+ PowerShell 7+. Depends on WorktreeItemResolution.psm1 and WorktreeRunResolution.psm1
55
+ (issue #690) and the pure sibling feature-folder-resolution.ps1 (issue #565), each
56
+ loaded inside a guard: the first failure is recorded and the decision denies naming
57
+ the dependency. Both read
55
58
  boundaries -- the checkpoint read and the finding-file existence check -- are injectable
56
59
  wrapper functions, so tests mock them without writing temporary files.
57
60
 
@@ -65,23 +68,27 @@
65
68
  #>
66
69
  [CmdletBinding()]
67
70
  param()
71
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
68
72
 
69
73
 
70
- Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force
74
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'HookPayload.psm1' -ErrorRecord $_ }
71
75
 
72
- # Import guard (issue #690): a failed import denies instead of failing open.
76
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeItemResolution.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'WorktreeItemResolution.psm1' -ErrorRecord $_ }
77
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeRunResolution.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'WorktreeRunResolution.psm1' -ErrorRecord $_ }
73
78
  $script:ParallelDriftGateResolutionImportFailure = $null
79
+
80
+ # Shared feature-folder resolution (issue #565). Guarded so a failed dot-source denies
81
+ # rather than failing open; an earlier recorded failure is kept.
74
82
  try {
75
- Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeRunResolution.psm1') -Force -ErrorAction Stop
83
+ . (Join-Path $PSScriptRoot 'feature-folder-resolution.ps1')
76
84
  }
77
85
  catch {
78
- $script:ParallelDriftGateResolutionImportFailure = 'WorktreeRunResolution.psm1'
86
+ if (-not $script:ParallelDriftGateResolutionImportFailure) { $script:ParallelDriftGateResolutionImportFailure = 'feature-folder-resolution.ps1' }
79
87
  }
80
88
 
81
89
  # Dot-source the shape-and-derivation helpers. Guarded so a missing file produces a clear error
82
90
  # and so dot-sourcing this hook in tests loads the helpers too.
83
- $script:ParallelDriftGateHelpersPath = Join-Path $PSScriptRoot 'enforce-parallel-drift-gate-helpers.ps1'
84
- . $script:ParallelDriftGateHelpersPath
91
+ try { . (Join-Path $PSScriptRoot 'enforce-parallel-drift-gate-helpers.ps1') } catch { Add-HookDependencyFailure -Name 'enforce-parallel-drift-gate-helpers.ps1' -ErrorRecord $_ }
85
92
 
86
93
  $script:ParallelModeMarker = 'Parallel mode: true'
87
94
  $script:ReviewSubagentType = 'feature-review'
@@ -209,32 +216,16 @@ function Test-ParallelDriftFindingPresent {
209
216
  function Find-ParallelDriftGateFeatureFolderFromPrompt {
210
217
  <#
211
218
  .SYNOPSIS
212
- Scan a delegation prompt for docs/features/active/<...> path tokens and return the
213
- longest unique match's basename, or $null when none is found.
219
+ Return the distinct feature-folder basenames cited in a delegation prompt, in
220
+ first-occurrence order, through the shared Find-FeatureFolderCandidate (issue #565).
221
+ A nested research/ or evidence/ citation names its own folder. No output when no
222
+ folder is cited; selection among several is made by Select-FeatureFolderTarget.
214
223
  #>
215
224
  [CmdletBinding()]
216
- [OutputType([string])]
225
+ [OutputType([string[]])]
217
226
  param([Parameter(Mandatory)][AllowEmptyString()][string] $Prompt)
218
227
 
219
- if (-not $Prompt) {
220
- return $null
221
- }
222
- $matchList = [regex]::Matches($Prompt, 'docs[\\/]+features[\\/]+active[\\/]+[^\s"''`]+')
223
- if ($matchList.Count -eq 0) {
224
- return $null
225
- }
226
-
227
- # Collect distinct normalized tokens so the longest, most specific one can be selected.
228
- $unique = @{}
229
- foreach ($found in $matchList) {
230
- $unique[($found.Value -replace '\\', '/').TrimEnd('/')] = $true
231
- }
232
-
233
- $best = @(@($unique.Keys) | Sort-Object -Property Length -Descending)[0]
234
- if ($best -match '\.md$') {
235
- $best = $best -replace '/[^/]+\.md$', ''
236
- }
237
- return ($best -split '/')[-1]
228
+ return [string[]]@(Find-FeatureFolderCandidate -Text $Prompt)
238
229
  }
239
230
 
240
231
  function Find-ParallelDriftGateItemRecord {
@@ -318,11 +309,13 @@ function Invoke-ParallelDriftGateDecision {
318
309
  [AllowEmptyString()]
319
310
  [string] $ToolInputRaw
320
311
  )
312
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'PARALLEL_DRIFT_GATE_BLOCKED:'
313
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
321
314
 
322
- # A failed worktree-resolution import denies before any other logic (issue #690).
315
+ # A failed dependency import denies before any other logic (issue #565).
323
316
  if ($script:ParallelDriftGateResolutionImportFailure) {
324
317
  return Get-ParallelDriftGateBlockDecision -Reason (
325
- "PARALLEL_DRIFT_GATE_BLOCKED: the worktree-resolution module '$($script:ParallelDriftGateResolutionImportFailure)' " +
318
+ "PARALLEL_DRIFT_GATE_BLOCKED: the dependency '$($script:ParallelDriftGateResolutionImportFailure)' " +
326
319
  'failed to import, so the parallel checkpoint that governs this review cannot be located; the gate fails closed.')
327
320
  }
328
321
 
@@ -352,8 +345,8 @@ function Invoke-ParallelDriftGateDecision {
352
345
  return Get-ParallelDriftGateBlockDecision -Reason "PARALLEL_DRIFT_GATE_BLOCKED: $($target.ReasonCode): $($target.Detail)"
353
346
  }
354
347
 
355
- $featureFolder = Find-ParallelDriftGateFeatureFolderFromPrompt -Prompt $prompt
356
- if (-not $featureFolder) {
348
+ $candidates = @(Find-ParallelDriftGateFeatureFolderFromPrompt -Prompt $prompt)
349
+ if ($candidates.Count -eq 0) {
357
350
  return Get-ParallelDriftGateBlockDecision -Reason 'PARALLEL_DRIFT_GATE_BLOCKED: a parallel-mode feature-review delegation must reference the target item feature folder in the prompt so its drift state can be verified.'
358
351
  }
359
352
 
@@ -367,8 +360,19 @@ function Invoke-ParallelDriftGateDecision {
367
360
  }
368
361
  }
369
362
  if ($null -eq $checkpoint) {
370
- return Get-ParallelDriftGateBlockDecision -Reason "PARALLEL_DRIFT_GATE_BLOCKED: the parallel checkpoint 'artifacts/orchestration/parallel-orchestrator-state.json' in worktree '$($target.WorktreeRoot)' is missing or unreadable, so the drift state of '$featureFolder' cannot be verified."
363
+ return Get-ParallelDriftGateBlockDecision -Reason "PARALLEL_DRIFT_GATE_BLOCKED: the parallel checkpoint 'artifacts/orchestration/parallel-orchestrator-state.json' in worktree '$($target.WorktreeRoot)' is missing or unreadable, so the drift state of '$($candidates -join ', ')' cannot be verified."
364
+ }
365
+
366
+ # The target is selected among the cited candidates (issue #565). The canonical
367
+ # issue-number line breaks a tie only when it names exactly one number.
368
+ $selectionArguments = @{ Records = @($checkpoint.items | Where-Object { $null -ne $_ }); Candidate = $candidates }
369
+ $declared = Find-WorktreeItemIssueSignal -Text $prompt
370
+ if (@($declared).Count -eq 1) { $selectionArguments['DeclaredIssueNumber'] = [int]@($declared)[0] }
371
+ $selection = Select-FeatureFolderTarget @selectionArguments
372
+ if ($selection.Status -eq 'Ambiguous') {
373
+ return Get-ParallelDriftGateBlockDecision -Reason "PARALLEL_DRIFT_GATE_BLOCKED: $($selection.Detail)."
371
374
  }
375
+ $featureFolder = $selection.Basename
372
376
 
373
377
  $item = Find-ParallelDriftGateItemRecord -Checkpoint $checkpoint -FeatureFolder $featureFolder
374
378
  if ($null -eq $item -or -not (Test-ParallelDriftGateItemKey -Value $item.issue_num)) {
@@ -440,5 +444,8 @@ function Invoke-ParallelDriftGateEntryPoint {
440
444
  if ($MyInvocation.InvocationName -eq '.') {
441
445
  return
442
446
  }
447
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('PARALLEL_DRIFT_GATE_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
448
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'PARALLEL_DRIFT_GATE_BLOCKED:'
449
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
443
450
 
444
451
  exit (Invoke-ParallelDriftGateEntryPoint)
@@ -32,27 +32,21 @@
32
32
  #>
33
33
  [CmdletBinding()]
34
34
  param()
35
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
35
36
 
36
37
 
37
- Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force
38
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'HookPayload.psm1' -ErrorRecord $_ }
38
39
  # Sanctioned-removal manifest reader (issue #635), consumed by the manifest branch in
39
40
  # Invoke-ParallelWorktreeRemovalGateDecision. Same module the epic gate imports.
40
- Import-Module (Join-Path $PSScriptRoot '../lib/cleanup-manifest/CleanupWorktreeManifest.psm1') -Force
41
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/cleanup-manifest/CleanupWorktreeManifest.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'CleanupWorktreeManifest.psm1' -ErrorRecord $_ }
41
42
  # Shared command-line parser (issue #545), consumed by the scope filter in
42
- # Invoke-ParallelWorktreeRemovalGateDecision and by Get-ParallelWorktreeRemovalCommandPath.
43
+ # Invoke-ParallelWorktreeRemovalGateDecision and by Resolve-CommandLineInvocationTarget.
43
44
  # Both call sites are byte-for-byte the calls the epic gate makes, so the duplicated
44
45
  # concern now has one implementation.
45
- . (Join-Path $PSScriptRoot 'hook-command-scanner.ps1')
46
- . (Join-Path $PSScriptRoot 'hook-command-invocation.ps1')
46
+ try { . (Join-Path $PSScriptRoot 'hook-command-scanner.ps1') } catch { Add-HookDependencyFailure -Name 'hook-command-scanner.ps1' -ErrorRecord $_ }
47
+ try { . (Join-Path $PSScriptRoot 'hook-command-invocation.ps1') } catch { Add-HookDependencyFailure -Name 'hook-command-invocation.ps1' -ErrorRecord $_ }
47
48
 
48
- # Import guard (issue #690): a failed import denies instead of failing open.
49
- $script:ParallelWorktreeGateResolutionImportFailure = $null
50
- try {
51
- Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeRunResolution.psm1') -Force -ErrorAction Stop
52
- }
53
- catch {
54
- $script:ParallelWorktreeGateResolutionImportFailure = 'WorktreeRunResolution.psm1'
55
- }
49
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeRunResolution.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'WorktreeRunResolution.psm1' -ErrorRecord $_ }
56
50
 
57
51
  $script:AllowedMergeStatuses = @('merged', 'worktree_removed')
58
52
  # Sanctioned-removal manifest location. Recorded here so every hook-read document this
@@ -164,52 +158,6 @@ function Read-ParallelWorktreeGateRunCheckpoint {
164
158
  }
165
159
  }
166
160
 
167
- function Get-ParallelWorktreeRemovalCommandPath {
168
- <#
169
- .SYNOPSIS
170
- Extract the target worktree path argument from a git worktree remove command.
171
- .DESCRIPTION
172
- The operand comes from the segment that structurally invokes git worktree remove,
173
- so a 'cd <path> &&' segment chained before the removal contributes nothing and a
174
- quoted mention of the phrase resolves to no operand at all. Quotes around the path
175
- are already stripped by the tokenizer.
176
-
177
- '--force' is a zero-argument flag, so it never contributes an operand and may be
178
- written on either side of the target path. Its presence is read structurally through
179
- Test-CommandLineFlag rather than by searching the raw text, so a '--force' spelling
180
- that appears inside an unrelated quoted argument cannot change how the operand list
181
- is read. When no operand resolves, a present '--force' is reported in its place, so
182
- the checkpoint lookup fails closed on a value that matches no recorded worktree_path
183
- - the same value the previous raw-text pattern returned for that input.
184
-
185
- This body is identical to Get-EpicWorktreeRemovalCommandPath in
186
- enforce-epic-worktree-removal-gate.ps1. The two gates fire on the same command and
187
- now delegate the shared concern to one parser rather than to two patterns that had
188
- already diverged from the Codex copy.
189
- .PARAMETER CommandText
190
- The Bash command text under evaluation.
191
- .OUTPUTS
192
- System.String or $null
193
- #>
194
- [CmdletBinding()]
195
- [OutputType([string])]
196
- param(
197
- [Parameter(Mandatory)]
198
- [string] $CommandText
199
- )
200
-
201
- $hasForce = Test-CommandLineFlag -CommandText $CommandText -CommandWord 'git' -SubcommandPath @('worktree', 'remove') -FlagName '--force'
202
- $operands = @(Get-CommandLineOperand -CommandText $CommandText -CommandWord 'git' -SubcommandPath @('worktree', 'remove'))
203
-
204
- if ($operands.Count -gt 0) {
205
- return $operands[0]
206
- }
207
- if ($hasForce) {
208
- return '--force'
209
- }
210
- return $null
211
- }
212
-
213
161
  function Find-ParallelWorktreeItemRecord {
214
162
  <#
215
163
  .SYNOPSIS
@@ -334,13 +282,8 @@ function Invoke-ParallelWorktreeRemovalGateDecision {
334
282
  param(
335
283
  [string] $ToolInputRaw
336
284
  )
337
-
338
- # A failed worktree-resolution import denies before any other logic (issue #690).
339
- if ($script:ParallelWorktreeGateResolutionImportFailure) {
340
- return Get-ParallelWorktreeGateBlockDecision -Reason (
341
- "PARALLEL_WORKTREE_REMOVAL_BLOCKED: the worktree-resolution module '$($script:ParallelWorktreeGateResolutionImportFailure)' " +
342
- 'failed to import, so the run checkpoint that governs this removal cannot be located; the gate fails closed.')
343
- }
285
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'PARALLEL_WORKTREE_REMOVAL_BLOCKED:'
286
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
344
287
 
345
288
  $payload = Resolve-ClaudeHookToolInput -Raw $ToolInputRaw
346
289
  if (-not $payload.IsValid) {
@@ -362,7 +305,43 @@ function Invoke-ParallelWorktreeRemovalGateDecision {
362
305
  return Get-ParallelWorktreeGateAllowDecision
363
306
  }
364
307
 
365
- $worktreePath = Get-ParallelWorktreeRemovalCommandPath -CommandText $commandText
308
+ # Issue #824: the removal targets are derived structurally from every invocation.
309
+ # A target that cannot be derived denies before any checkpoint is read, and every
310
+ # derived target must be authorized on its own.
311
+ $resolution = Resolve-CommandLineInvocationTarget -CommandText $commandText -CommandWord 'git' -SubcommandPath @('worktree', 'remove')
312
+ if ($resolution.Status -eq 'NoMatch') {
313
+ return Get-ParallelWorktreeGateAllowDecision
314
+ }
315
+ if ($resolution.Status -ne 'Targets') {
316
+ return Get-ParallelWorktreeGateBlockDecision -Reason 'PARALLEL_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.'
317
+ }
318
+ foreach ($target in @($resolution.Targets)) {
319
+ $denial = Get-ParallelWorktreeRemovalTargetDenial -WorktreePath $target
320
+ if ($null -ne $denial) {
321
+ return $denial
322
+ }
323
+ }
324
+ return Get-ParallelWorktreeGateAllowDecision
325
+ }
326
+
327
+ function Get-ParallelWorktreeRemovalTargetDenial {
328
+ <#
329
+ .SYNOPSIS
330
+ Evaluate one derived removal target and return its deny decision, or $null when authorized.
331
+ .DESCRIPTION
332
+ Holds the per-path authorization cascade (parallel checkpoint, epic checkpoint,
333
+ sanctioned-removal manifest) for a single worktree path (issue #824).
334
+ .PARAMETER WorktreePath
335
+ One removal target derived by Resolve-CommandLineInvocationTarget.
336
+ .OUTPUTS
337
+ System.Collections.Specialized.OrderedDictionary or $null
338
+ #>
339
+ [CmdletBinding()]
340
+ [OutputType([System.Collections.Specialized.OrderedDictionary])]
341
+ param(
342
+ [Parameter(Mandatory)]
343
+ [string] $WorktreePath
344
+ )
366
345
 
367
346
  # Each run checkpoint is read beneath the worktree that records this path (issue #690),
368
347
  # parallel kind first; an ambiguous target denies before any allow or manifest check.
@@ -377,7 +356,7 @@ function Invoke-ParallelWorktreeRemovalGateDecision {
377
356
 
378
357
  $itemRecord = Find-ParallelWorktreeItemRecord -Checkpoint $checkpoint -WorktreePath $worktreePath
379
358
  if (Test-ParallelWorktreeRemovalAllowed -ItemRecord $itemRecord) {
380
- return Get-ParallelWorktreeGateAllowDecision
359
+ return $null
381
360
  }
382
361
 
383
362
  # Epic-authorization branch (issue #688). It runs after the parallel allow above and
@@ -394,7 +373,7 @@ function Invoke-ParallelWorktreeRemovalGateDecision {
394
373
 
395
374
  $featureRecord = Find-ParallelWorktreeItemRecord -Checkpoint $epicCheckpoint -WorktreePath $worktreePath -RecordArrayName 'features'
396
375
  if (Test-ParallelWorktreeRemovalAllowed -ItemRecord $featureRecord) {
397
- return Get-ParallelWorktreeGateAllowDecision
376
+ return $null
398
377
  }
399
378
 
400
379
  # Sanctioned-removal manifest branch (issue #635). It runs last, so a
@@ -409,15 +388,15 @@ function Invoke-ParallelWorktreeRemovalGateDecision {
409
388
  # only; the epic gate covers the features array with its own exclusion.
410
389
  if (-not (Test-CleanupManifestCheckpointCoversPath -Checkpoint $checkpoint -RecordArrayName 'items' -WorktreePath $worktreePath) -and
411
390
  (Test-CleanupWorktreeManifestAuthorizesRemoval -WorktreePath $worktreePath)) {
412
- return Get-ParallelWorktreeGateAllowDecision
391
+ return $null
413
392
  }
414
393
 
415
394
  # When neither kind resolved, the deny names the resolution reason first (issue #690).
416
395
  $prefix = ''
417
396
  if ($parallelRead.Target.Status -eq 'NoTarget' -and $epicRead.Target.Status -eq 'NoTarget') {
418
- $prefix = "PARALLEL_WORKTREE_REMOVAL_BLOCKED: $($parallelRead.Target.ReasonCode): $($parallelRead.Target.Detail). "
397
+ $prefix = "$($parallelRead.Target.ReasonCode): $($parallelRead.Target.Detail). "
419
398
  }
420
- return Get-ParallelWorktreeGateBlockDecision -Reason ($prefix + "PARALLEL_WORKTREE_REMOVAL_BLOCKED: git worktree remove for '$worktreePath' requires a matching parallel checkpoint items[] record with merge_status in {merged, worktree_removed}. The checkpoint was unreadable, no matching record was found, or merge_status was not yet safe for removal.")
399
+ return Get-ParallelWorktreeGateBlockDecision -Reason ('PARALLEL_WORKTREE_REMOVAL_BLOCKED: ' + $prefix + "git worktree remove for '$worktreePath' requires a matching parallel checkpoint items[] record with merge_status in {merged, worktree_removed}. The checkpoint was unreadable, no matching record was found, or merge_status was not yet safe for removal.")
421
400
  }
422
401
 
423
402
  function Invoke-ParallelWorktreeRemovalGateEntryPoint {
@@ -462,6 +441,9 @@ function Invoke-ParallelWorktreeRemovalGateEntryPoint {
462
441
  if ($MyInvocation.InvocationName -eq '.') {
463
442
  return
464
443
  }
444
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('PARALLEL_WORKTREE_REMOVAL_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
445
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'PARALLEL_WORKTREE_REMOVAL_BLOCKED:'
446
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
465
447
 
466
448
  # The entry point returns its [int] exit code as the last pipeline element and the
467
449
  # decision JSON before it. `exit (<call>)` would capture BOTH into the exit
@@ -57,10 +57,11 @@
57
57
  #>
58
58
  [CmdletBinding()]
59
59
  param()
60
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
60
61
 
61
62
 
62
- Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force
63
- . (Join-Path $PSScriptRoot 'enforce-batch-budget-route.ps1')
63
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'HookPayload.psm1' -ErrorRecord $_ }
64
+ try { . (Join-Path $PSScriptRoot 'enforce-batch-budget-route.ps1') } catch { Add-HookDependencyFailure -Name 'enforce-batch-budget-route.ps1' -ErrorRecord $_ }
64
65
 
65
66
  function Test-PowerShellBatchBudgetPathInRoot {
66
67
  <#
@@ -277,6 +278,8 @@ function Invoke-PowerShellBatchBudgetDecision {
277
278
  [AllowEmptyString()]
278
279
  [string] $ObservedRoute = ''
279
280
  )
281
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'POWERSHELL_LARGE_PATH_REQUIRED:'
282
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
280
283
 
281
284
  $normalized = $FilePath -replace '\\', '/'
282
285
  if ($normalized -notmatch '\.(ps1|psm1|psd1)$') {
@@ -474,6 +477,9 @@ function Invoke-PowerShellBatchBudgetEntryPoint {
474
477
  if ($MyInvocation.InvocationName -eq '.') {
475
478
  return
476
479
  }
480
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('POWERSHELL_LARGE_PATH_REQUIRED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
481
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'POWERSHELL_LARGE_PATH_REQUIRED:'
482
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
477
483
 
478
484
  # The entry point returns its [int] exit code as the last pipeline element and the
479
485
  # decision JSON before it. `exit (<call>)` would capture BOTH into the exit