@danmoisan/drm-copilot-mcp 1.1.18 → 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 (206) hide show
  1. package/out/mcp-server.js +989 -662
  2. package/package.json +3 -2
  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 +6 -4
  8. package/resources/claude-customizations/.claude/agents/orchestrator.md +5 -4
  9. package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +31 -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-mutation-cli.sh +152 -0
  76. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-ops.sh +193 -0
  77. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation.sh +322 -0
  78. package/resources/claude-customizations/.claude/lib/bash/remove-parallel-item.sh +146 -0
  79. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +23 -33
  80. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusTokenShape.psm1 +86 -6
  81. package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-CiGateParser.ps1 +91 -19
  82. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateEpicWaveBarrier.psm1 +346 -0
  83. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1 +24 -3
  84. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +79 -2
  85. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1 +3 -3
  86. package/resources/claude-customizations/.claude/rules/architecture-boundaries.md +5 -5
  87. package/resources/claude-customizations/.claude/rules/csharp.md +2 -2
  88. package/resources/claude-customizations/.claude/rules/general-code-change.md +1 -1
  89. package/resources/claude-customizations/.claude/rules/general-unit-test.md +3 -2
  90. package/resources/claude-customizations/.claude/rules/orchestrator-state.md +37 -4
  91. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +50 -5
  92. package/resources/claude-customizations/.claude/rules/plan-acceptance-gates.md +47 -8
  93. package/resources/claude-customizations/.claude/rules/quality-tiers.md +11 -7
  94. package/resources/claude-customizations/.claude/rules/shell.md +10 -8
  95. package/resources/claude-customizations/.claude/rules/typescript.md +1 -1
  96. package/resources/claude-customizations/.claude/settings.json +3 -4
  97. package/resources/claude-customizations/.claude/skills/acceptance-criteria-tracking/SKILL.md +1 -1
  98. package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +5 -1
  99. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_detached_lib.sh +1 -1
  100. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_enumerate_lib.sh +2 -0
  101. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_scan_helper.sh +2 -0
  102. package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +33 -6
  103. package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +1 -1
  104. package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
  105. package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +1 -1
  106. package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +4 -3
  107. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +14 -6
  108. package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +52 -14
  109. package/resources/claude-customizations/.claude/skills/parallel-close/SKILL.md +22 -9
  110. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +79 -16
  111. package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +63 -7
  112. package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +35 -12
  113. package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +3 -1
  114. package/resources/claude-customizations/.claude/skills/quota-throttling/SKILL.md +1 -1
  115. package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +2 -2
  116. package/resources/claude-customizations/pack-manifests/core.json +23 -0
  117. package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +1 -1
  118. package/resources/codex-and-agents-customizations/.agents/skills/architecture-boundaries/SKILL.md +5 -5
  119. package/resources/codex-and-agents-customizations/.agents/skills/atomic-executor/SKILL.md +11 -0
  120. package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +4 -0
  121. package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +10 -0
  122. package/resources/codex-and-agents-customizations/.agents/skills/csharp/SKILL.md +2 -2
  123. package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +2 -2
  124. package/resources/codex-and-agents-customizations/.agents/skills/epic-orchestrate/SKILL.md +8 -0
  125. package/resources/codex-and-agents-customizations/.agents/skills/epic-plan/SKILL.md +17 -1
  126. package/resources/codex-and-agents-customizations/.agents/skills/epic-run/SKILL.md +2 -1
  127. package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
  128. package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +13 -5
  129. package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +1 -1
  130. package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +3 -2
  131. package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +15 -6
  132. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +1 -1
  133. package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +11 -7
  134. package/resources/codex-and-agents-customizations/.agents/skills/translate-claude-to-codex/SKILL.md +1 -1
  135. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp/SKILL.md +2 -2
  136. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +2 -2
  137. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c1.toml +11 -0
  138. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +11 -0
  139. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +11 -0
  140. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +11 -0
  141. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +11 -0
  142. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +11 -0
  143. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +10 -0
  144. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +10 -0
  145. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +10 -0
  146. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +10 -0
  147. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +10 -0
  148. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +10 -0
  149. package/resources/codex-and-agents-customizations/.codex/agents/epic-orchestrator.toml +2 -1
  150. package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +6 -6
  151. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c1.toml +2 -2
  152. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +2 -2
  153. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +2 -2
  154. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +2 -2
  155. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +2 -2
  156. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +2 -2
  157. package/resources/codex-and-agents-customizations/.codex/codex-web-setup.sh +318 -273
  158. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  159. package/resources/codex-and-agents-customizations/.codex/hooks/check-powershell-test-purity.ps1 +7 -1
  160. package/resources/codex-and-agents-customizations/.codex/hooks/check-python-test-purity.ps1 +7 -1
  161. package/resources/codex-and-agents-customizations/.codex/hooks/codex-epic-child-launch-attestation.ps1 +1 -1
  162. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
  163. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-codex-model-routing.ps1 +8 -2
  164. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +97 -44
  165. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-helpers.ps1 +105 -0
  166. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-child-worktree-binding.ps1 +7 -1
  167. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +37 -5
  168. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +6 -0
  169. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-root-invocation.ps1 +7 -1
  170. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-wave-barrier.ps1 +7 -1
  171. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-worktree-removal-gate.ps1 +54 -41
  172. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-evidence-locations.ps1 +7 -1
  173. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +35 -14
  174. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
  175. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
  176. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
  177. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +24 -11
  178. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +8 -2
  179. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +13 -4
  180. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +8 -2
  181. package/resources/codex-and-agents-customizations/.codex/hooks/feature-folder-resolution.ps1 +373 -0
  182. package/resources/codex-and-agents-customizations/.codex/hooks/feature-review-coverage-thresholds.ps1 +85 -0
  183. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-heredoc.ps1 +119 -0
  184. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation-operands.ps1 +198 -0
  185. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation.ps1 +254 -255
  186. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload-powershell.ps1 +201 -0
  187. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload.ps1 +484 -0
  188. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-scanner.ps1 +27 -110
  189. package/resources/codex-and-agents-customizations/.codex/hooks/hook-dependency-guard.ps1 +114 -0
  190. package/resources/codex-and-agents-customizations/.codex/hooks/hook-docs-only-diff.ps1 +493 -0
  191. package/resources/codex-and-agents-customizations/.codex/hooks/validate-bash.ps1 +8 -2
  192. package/resources/codex-and-agents-customizations/.codex/hooks/validate-codex-subagent-routing.ps1 +7 -1
  193. package/resources/codex-and-agents-customizations/.codex/hooks/validate-feature-review-coverage.ps1 +24 -8
  194. package/resources/codex-and-agents-customizations/pack-manifests/core.json +9 -0
  195. package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +2 -2
  196. package/resources/customizations/.github/instructions/csharp-code-change.instructions.md +4 -4
  197. package/resources/customizations/.github/instructions/csharp-unit-test.instructions.md +2 -2
  198. package/resources/customizations/.github/skills/acceptance-criteria-tracking/SKILL.md +1 -1
  199. package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +4 -0
  200. package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
  201. package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +1 -1
  202. package/resources/powershell/PoshQC/PoshQC.Coverage.psm1 +314 -0
  203. package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +25 -28
  204. package/resources/powershell/PoshQC/PoshQC.psm1 +1 -0
  205. package/resources/powershell/PoshQC/README.md +8 -2
  206. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +2 -319
@@ -0,0 +1,433 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Per-segment target resolution for the epic-scope decision of the preimplementation gate (issue #738).
4
+ .DESCRIPTION
5
+ Pure string logic only: no disk, process, network, clock, or environment access, and no
6
+ dot-source of its own. Every command segment and every path of a gated call is resolved to
7
+ the absolute worktree target it operates on, or the call is reported unresolvable with one
8
+ rule token, so the epic-scope decision can evaluate every target instead of the first
9
+ segment only. Resolution rules R0 to R7 and verdict rules V1 to V7 are defined in the
10
+ issue #732 feature folder plan, section 4.
11
+
12
+ Dot-sourced by enforce-orchestration-preimplementation-gate-epic-scope.ps1 on both the
13
+ Claude and the Codex surface; the four copies are byte-identical.
14
+ .NOTES
15
+ PowerShell 7+. Call-time dependencies, loaded by each gate's existing dot-source of
16
+ hook-command-invocation.ps1 (which itself dot-sources hook-command-scanner.ps1 and
17
+ hook-command-payload.ps1): Read-CommandLineSegment (segment records with IsWrapperLed and
18
+ HasLiveSubstitution), Get-CommandLineGlobalOption (the git global-option table), and
19
+ Get-CommandLineInvocation (the token-aware git add and git commit matcher, read for its
20
+ match count only). Never calls the command-line splitter or the command-line tokenizer
21
+ of the sibling helpers file.
22
+ #>
23
+
24
+ # Command words that change the working directory of later segments (rule R3).
25
+ $script:OrchestrationTargetDirectoryChangeWords = @(
26
+ 'cd', 'pushd', 'popd', 'chdir', 'Set-Location', 'sl', 'Push-Location', 'Pop-Location'
27
+ )
28
+
29
+ # Environment names that relocate the git directory or work tree (rule R5).
30
+ $script:OrchestrationTargetRelocationNames = @('GIT_DIR', 'GIT_WORK_TREE', 'GIT_COMMON_DIR', 'GIT_INDEX_FILE')
31
+ # A relocation name not embedded in a longer identifier (issue #903, #732 CR-2).
32
+ $script:OrchestrationTargetRelocationPattern = '(?i)(?<![A-Za-z0-9_])(?:' +
33
+ ($script:OrchestrationTargetRelocationNames -join '|') + ')(?![A-Za-z0-9_])'
34
+
35
+ # An absolute target: a drive-lettered or a rooted, non-UNC path after separator normalization.
36
+ $script:OrchestrationTargetAbsolutePattern = '^([A-Za-z]:/|/(?!/))'
37
+
38
+ # A non-local path-leg spelling, tested before normalization (issue #937): UNC, extended-length,
39
+ # device, or NT-object prefixes in either separator style, a drive-relative drive letter, a
40
+ # current-drive-rooted single backslash, a URI scheme, a ~-prefixed home path, or surrounding whitespace.
41
+ $script:OrchestrationTargetNonLocalPattern = '^(?:\s|[\\/]{2}|[\\/]\?\?[\\/]|\\(?![\\/])|[A-Za-z]:(?![\\/])' +
42
+ '|[A-Za-z][A-Za-z0-9+.-]+:|~)|\s$'
43
+
44
+ # The apply_patch markers whose path names a file the patch writes (Codex path leg).
45
+ $script:OrchestrationPatchMarkers = @('*** Add File:', '*** Update File:', '*** Delete File:', '*** Move to:')
46
+
47
+ function ConvertTo-OrchestrationTargetPath {
48
+ <#
49
+ .SYNOPSIS
50
+ Normalizes a session root or a path-leg input to forward slashes.
51
+ .DESCRIPTION
52
+ Replaces every backslash with a forward slash and removes one trailing slash, except
53
+ from a bare root. Never applied to a -C selector value, which is judged as written.
54
+ .OUTPUTS
55
+ System.String
56
+ #>
57
+ [CmdletBinding()]
58
+ [OutputType([string])]
59
+ param([Parameter(Mandatory)][AllowEmptyString()][string] $Path)
60
+
61
+ $normalized = $Path.Replace('\', '/')
62
+ if ($normalized.Length -gt 1 -and $normalized.EndsWith('/') -and $normalized -notmatch '^[A-Za-z]:/$') {
63
+ $normalized = $normalized.Substring(0, $normalized.Length - 1)
64
+ }
65
+ return $normalized
66
+ }
67
+
68
+ function Get-OrchestrationPatchMarkerPath {
69
+ <#
70
+ .SYNOPSIS
71
+ Returns the file paths an apply_patch text names, or an empty array.
72
+ .DESCRIPTION
73
+ Recognized only when the first non-empty line is `*** Begin Patch`. Each Add File,
74
+ Update File, Delete File, and Move to marker contributes its trimmed path.
75
+ .OUTPUTS
76
+ System.String[]
77
+ #>
78
+ [CmdletBinding()]
79
+ [OutputType([string[]])]
80
+ param([Parameter(Mandatory)][AllowEmptyString()][string] $PatchText)
81
+
82
+ $lines = @($PatchText -split "`r?`n")
83
+ $first = @($lines | Where-Object { $_.Trim() }) | Select-Object -First 1
84
+ if ($null -eq $first -or $first.Trim() -cne '*** Begin Patch') {
85
+ return [string[]]@()
86
+ }
87
+ $paths = [System.Collections.Generic.List[string]]::new()
88
+ foreach ($line in $lines) {
89
+ foreach ($marker in $script:OrchestrationPatchMarkers) {
90
+ if ($line.StartsWith($marker, [System.StringComparison]::Ordinal)) {
91
+ $paths.Add($line.Substring($marker.Length).Trim())
92
+ }
93
+ }
94
+ }
95
+ return [string[]]$paths.ToArray()
96
+ }
97
+
98
+ function Test-OrchestrationTargetDotSegment {
99
+ # True when a forward-slash path carries a '.' or '..' segment.
100
+ [CmdletBinding()]
101
+ [OutputType([bool])]
102
+ param([Parameter(Mandatory)][AllowEmptyString()][string] $Path)
103
+
104
+ $segments = @($Path -split '/')
105
+ return ($segments -contains '.' -or $segments -contains '..')
106
+ }
107
+
108
+ function New-OrchestrationTargetResult {
109
+ # Builds the FR-3 result object; an unresolved result carries no targets.
110
+ [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
111
+ 'PSUseShouldProcessForStateChangingFunctions', '',
112
+ Justification = 'Pure in-memory factory that changes no system state.')]
113
+ [CmdletBinding()]
114
+ [OutputType([pscustomobject])]
115
+ param(
116
+ [AllowEmptyCollection()][string[]] $Targets = @(),
117
+ [AllowEmptyString()][string] $Rule = '',
118
+ [AllowEmptyString()][string] $Value = ''
119
+ )
120
+
121
+ if ($Rule) {
122
+ Write-Debug "PREIMPL_TARGET_UNRESOLVABLE: ${Rule}: $Value"
123
+ return [pscustomobject]@{
124
+ Resolved = $false; Targets = [string[]]@(); ReasonCode = 'target-unresolvable'; Detail = "${Rule}: $Value"
125
+ }
126
+ }
127
+ return [pscustomobject]@{ Resolved = $true; Targets = [string[]]$Targets; ReasonCode = ''; Detail = '' }
128
+ }
129
+
130
+ function Get-OrchestrationGitSelectorTarget {
131
+ <#
132
+ .SYNOPSIS
133
+ Walks the git global options of one segment record and returns its -C targets or a failure.
134
+ .DESCRIPTION
135
+ Rules R5 (option form) and R6. Returns an object with Rule and Value set on failure, and
136
+ Selectors holding every -C value otherwise (empty when the segment has no -C).
137
+ .OUTPUTS
138
+ System.Management.Automation.PSCustomObject
139
+ #>
140
+ [CmdletBinding()]
141
+ [OutputType([pscustomobject])]
142
+ param([Parameter(Mandatory)][AllowEmptyCollection()][AllowEmptyString()][string[]] $Token)
143
+
144
+ $table = Get-CommandLineGlobalOption -CommandWord 'git'
145
+ $gitIndex = [array]::IndexOf($Token, 'git')
146
+ if ($gitIndex -lt 0) {
147
+ return [pscustomobject]@{ Rule = 'git-command-word-missing'; Value = ''; Selectors = [string[]]@() }
148
+ }
149
+ $start = $gitIndex + 1
150
+ $selectors = [System.Collections.Generic.List[string]]::new()
151
+ $relocation = ''
152
+ $unmodeled = ''
153
+ $missing = $false
154
+ $index = $start
155
+ while ($index -lt $Token.Count -and $Token[$index].StartsWith('-')) {
156
+ $name = $Token[$index]
157
+ $value = $null
158
+ $equals = $name.IndexOf('=')
159
+ if ($name.StartsWith('--') -and $equals -gt 0) {
160
+ $value = $name.Substring($equals + 1)
161
+ $name = $name.Substring(0, $equals)
162
+ }
163
+ if ($table.WithArgument -ccontains $name) {
164
+ if ($null -eq $value) {
165
+ $index++
166
+ if ($index -lt $Token.Count) {
167
+ $value = $Token[$index]
168
+ }
169
+ elseif ($name -ceq '-C') {
170
+ $missing = $true
171
+ }
172
+ else {
173
+ $value = ''
174
+ }
175
+ }
176
+ } elseif ($table.Standalone -notcontains $name -and $table.Terminal -notcontains $name) {
177
+ $unmodeled = $name
178
+ break
179
+ }
180
+ $isWorktreeConfig = $name -ceq '-c' -and $null -ne $value -and
181
+ $value.StartsWith('core.worktree', [System.StringComparison]::OrdinalIgnoreCase)
182
+ if ($name -ceq '--git-dir' -or $name -ceq '--work-tree' -or $isWorktreeConfig) {
183
+ if (-not $relocation) { $relocation = "$name $value".Trim() }
184
+ }
185
+ if ($name -ceq '-C' -and -not $missing) { $selectors.Add($value) }
186
+ $index++
187
+ }
188
+
189
+ $rule = ''; $value = ''
190
+ if ($relocation) { $rule = 'git-relocation'; $value = $relocation }
191
+ elseif ($unmodeled) { $rule = 'git-option-unmodeled'; $value = $unmodeled }
192
+ elseif ($missing) { $rule = 'selector-missing-value'; $value = '-C' }
193
+ foreach ($selector in $selectors) {
194
+ if ($rule) { break }
195
+ if ($selector.Contains('\')) { $rule = 'selector-backslash' }
196
+ elseif ($selector -notmatch $script:OrchestrationTargetAbsolutePattern) { $rule = 'selector-not-absolute' }
197
+ elseif (Test-OrchestrationTargetDotSegment -Path $selector) { $rule = 'selector-dot-segment' }
198
+ $value = $selector
199
+ }
200
+ if ($rule) { return [pscustomobject]@{ Rule = $rule; Value = $value; Selectors = [string[]]@() } }
201
+ return [pscustomobject]@{ Rule = ''; Value = ''; Selectors = [string[]]$selectors.ToArray() }
202
+ }
203
+
204
+ function Get-OrchestrationCommandTarget {
205
+ <#
206
+ .SYNOPSIS
207
+ Resolves every command segment and path of a gated call to its absolute target worktree.
208
+ .DESCRIPTION
209
+ Rules R0 to R7 (first failing rule decides). Returns Resolved, Targets (ordinal
210
+ case-insensitive distinct, first-seen order), ReasonCode ('' or target-unresolvable),
211
+ and Detail (a rule token, ': ', and the offending text or value).
212
+ .OUTPUTS
213
+ System.Management.Automation.PSCustomObject
214
+ #>
215
+ [CmdletBinding()]
216
+ [OutputType([pscustomobject])]
217
+ param(
218
+ [AllowNull()][AllowEmptyString()][string] $Command = '',
219
+ [AllowNull()][AllowEmptyCollection()][string[]] $FilePath = @(),
220
+ [Parameter(Mandatory)][AllowEmptyString()][string] $SessionRoot
221
+ )
222
+
223
+ $root = ConvertTo-OrchestrationTargetPath -Path $SessionRoot
224
+ if ($root -notmatch $script:OrchestrationTargetAbsolutePattern) {
225
+ return (New-OrchestrationTargetResult -Rule 'session-root-not-absolute' -Value $SessionRoot)
226
+ }
227
+ $targets = [System.Collections.Generic.List[string]]::new()
228
+
229
+ foreach ($rawPath in @($FilePath | Where-Object { $_ })) {
230
+ if ($rawPath -match $script:OrchestrationTargetNonLocalPattern) {
231
+ return (New-OrchestrationTargetResult -Rule 'path-not-local' -Value $rawPath)
232
+ }
233
+ $path = ConvertTo-OrchestrationTargetPath -Path $rawPath
234
+ if ($path -notmatch $script:OrchestrationTargetAbsolutePattern) { $targets.Add($root); continue }
235
+ if (Test-OrchestrationTargetDotSegment -Path $path) {
236
+ return (New-OrchestrationTargetResult -Rule 'path-dot-segment' -Value $rawPath)
237
+ }
238
+ $targets.Add($path)
239
+ }
240
+
241
+ if ($Command) {
242
+ $records = @(Read-CommandLineSegment -CommandText $Command)
243
+ if ($records.Count -eq 0) { $targets.Add($root) }
244
+ foreach ($record in $records) {
245
+ $text = ([string]$record.RawText).Trim()
246
+ if ($record.Unbalanced) { return (New-OrchestrationTargetResult -Rule 'segment-unbalanced' -Value $text) }
247
+ if ($script:OrchestrationTargetDirectoryChangeWords -contains [string]$record.CommandWord) {
248
+ return (New-OrchestrationTargetResult -Rule 'directory-change' -Value $text)
249
+ }
250
+ $wrapped = [bool]$record.IsWrapperLed -or [bool]$record.HasLiveSubstitution
251
+ if ($wrapped) {
252
+ $matchCount = 0
253
+ foreach ($sub in @('add', 'commit')) {
254
+ $invocationArguments = @{
255
+ CommandText = $record.RawText; CommandWord = 'git'; SubcommandPath = @($sub)
256
+ }
257
+ $matchCount += @(Get-CommandLineInvocation @invocationArguments).Count
258
+ }
259
+ if ($matchCount -gt 0) { return (New-OrchestrationTargetResult -Rule 'wrapper-git' -Value $text) }
260
+ }
261
+ if ($text -match $script:OrchestrationTargetRelocationPattern) {
262
+ return (New-OrchestrationTargetResult -Rule 'git-relocation' -Value $text)
263
+ }
264
+ if ($wrapped -or [string]$record.CommandWord -cne 'git') { $targets.Add($root); continue }
265
+ $git = Get-OrchestrationGitSelectorTarget -Token ([string[]]@($record.Tokens))
266
+ if ($git.Rule) { return (New-OrchestrationTargetResult -Rule $git.Rule -Value $git.Value) }
267
+ if ($git.Selectors.Count -eq 0) {
268
+ $targets.Add($root)
269
+ }
270
+ else {
271
+ foreach ($selector in $git.Selectors) { $targets.Add($selector) }
272
+ }
273
+ }
274
+ }
275
+
276
+ $distinct = [System.Collections.Generic.List[string]]::new()
277
+ foreach ($target in $targets) {
278
+ $seen = $distinct | Where-Object { [string]::Equals($_, $target, [System.StringComparison]::OrdinalIgnoreCase) }
279
+ if (-not $seen) { $distinct.Add($target) }
280
+ }
281
+ return (New-OrchestrationTargetResult -Targets $distinct.ToArray())
282
+ }
283
+
284
+ function Resolve-OrchestrationEpicTargetVerdict {
285
+ <#
286
+ .SYNOPSIS
287
+ Decides the epic-scope verdict for a resolved or unresolved target result.
288
+ .DESCRIPTION
289
+ Verdict rules V1 to V7. Resolves the session-root scope with an empty selector and each
290
+ target scope with the target as selector, each distinct key (ordinal
291
+ case-insensitive) at most once; a target equal to the session root reuses its scope.
292
+ Returns Verdict ('none', 'deny', or 'evaluate'), ReasonCode, Detail, and Evaluations
293
+ (one Target, IsSessionRoot, Scope entry per target when the verdict is evaluate).
294
+ .OUTPUTS
295
+ System.Management.Automation.PSCustomObject
296
+ #>
297
+ [CmdletBinding()]
298
+ [OutputType([pscustomobject])]
299
+ param(
300
+ [Parameter(Mandatory)][AllowEmptyString()][string] $SessionRoot,
301
+ [Parameter(Mandatory)][pscustomobject] $TargetResult,
302
+ [Parameter(Mandatory)][scriptblock] $ScopeResolver
303
+ )
304
+
305
+ $root = ConvertTo-OrchestrationTargetPath -Path $SessionRoot
306
+ $cache = [System.Collections.Generic.Dictionary[string, object]]::new([System.StringComparer]::OrdinalIgnoreCase)
307
+ $cache[$root] = & $ScopeResolver ''
308
+ $evaluations = [System.Collections.Generic.List[pscustomobject]]::new()
309
+ if ($TargetResult.Resolved) {
310
+ foreach ($target in @($TargetResult.Targets)) {
311
+ if (-not $cache.ContainsKey($target)) { $cache[$target] = & $ScopeResolver $target }
312
+ $isSessionRoot = [string]::Equals($target, $root, [System.StringComparison]::OrdinalIgnoreCase)
313
+ $evaluations.Add([pscustomobject]@{
314
+ Target = $target; IsSessionRoot = $isSessionRoot; Scope = $cache[$target]
315
+ })
316
+ }
317
+ }
318
+
319
+ $relevant = @($cache.Values | Where-Object { $_.IsEpicScope -or $_.Reason -eq 'target-worktree-ambiguous' })
320
+ $code = ''; $detail = ''
321
+ if ($relevant.Count -eq 0) {
322
+ return [pscustomobject]@{ Verdict = 'none'; ReasonCode = ''; Detail = ''; Evaluations = @() }
323
+ }
324
+ if (-not $TargetResult.Resolved) {
325
+ $code = 'target-unresolvable'; $detail = [string]$TargetResult.Detail
326
+ }
327
+ $ambiguous = @($evaluations | Where-Object { $_.Scope.Reason -eq 'target-worktree-ambiguous' })
328
+ $unresolved = @($evaluations | Where-Object { $_.Scope.Reason -eq 'selector-unresolved' })
329
+ $mixed = @($evaluations | Where-Object { -not $_.Scope.IsEpicScope })
330
+ if (-not $code -and $ambiguous.Count -gt 0) { $code = 'target-ambiguous'; $detail = $ambiguous[0].Target }
331
+ if (-not $code -and $unresolved.Count -gt 0) {
332
+ $code = 'target-unresolvable'; $detail = "selector-unresolved: $($unresolved[0].Target)"
333
+ }
334
+ if (-not $code -and $mixed.Count -gt 0) { $code = 'target-mixed'; $detail = $mixed[0].Target }
335
+ if ($code) {
336
+ return [pscustomobject]@{ Verdict = 'deny'; ReasonCode = $code; Detail = $detail; Evaluations = @() }
337
+ }
338
+ return [pscustomobject]@{ Verdict = 'evaluate'; ReasonCode = ''; Detail = ''; Evaluations = $evaluations.ToArray() }
339
+ }
340
+
341
+ function Get-OrchestrationEpicTargetDenyReason {
342
+ <#
343
+ .SYNOPSIS
344
+ Builds the deny reason of an epic-scope target verdict or a not-ready target.
345
+ .DESCRIPTION
346
+ One string per code, behind the unchanged PREIMPLEMENTATION_GATE_BLOCKED prefix:
347
+ target-unresolvable (Detail is the rule detail), target-ambiguous and target-mixed
348
+ (Detail is the target), and target-not-ready (Detail is the target, with the
349
+ consulted checkpoint path and the failed readiness predicate).
350
+ .OUTPUTS
351
+ System.String
352
+ #>
353
+ [CmdletBinding()]
354
+ [OutputType([string])]
355
+ param(
356
+ [Parameter(Mandatory)][string] $ReasonCode,
357
+ [AllowEmptyString()][string] $Detail = '',
358
+ [AllowEmptyString()][string] $CheckpointPath = '',
359
+ [AllowEmptyString()][string] $Failure = ''
360
+ )
361
+
362
+ $prefix = "PREIMPLEMENTATION_GATE_BLOCKED: ${ReasonCode}: "
363
+ switch ($ReasonCode) {
364
+ 'target-ambiguous' {
365
+ return $prefix + "more than one worktree claims the epic integration branch for target $Detail. " +
366
+ 'Implementation operations in epic scope require an unambiguous target worktree.'
367
+ }
368
+ 'target-mixed' {
369
+ return $prefix + "target $Detail is not in the epic scope that governs this operation. " +
370
+ 'Implementation operations in epic scope may not also target a worktree outside it.'
371
+ }
372
+ 'target-not-ready' {
373
+ return $prefix + "the epic-scope target $Detail was evaluated against $CheckpointPath, " +
374
+ "and the failed readiness predicate is '$Failure'. " +
375
+ 'Implementation operations in epic scope require that checkpoint to satisfy every ' +
376
+ 'readiness predicate, and a production path may be staged or edited only while a merge is in progress.'
377
+ }
378
+ }
379
+ return $prefix + "the target worktree of this epic-scope operation cannot be resolved ($Detail). " +
380
+ 'Implementation operations in epic scope require every command segment and path to name ' +
381
+ 'an absolute, resolvable target.'
382
+ }
383
+
384
+ function Resolve-OrchestrationEpicTargetOutcome {
385
+ <#
386
+ .SYNOPSIS
387
+ Decides the shared epic-scope outcome of a target verdict (issue #903, #732 CR-3).
388
+ .DESCRIPTION
389
+ Pure decision logic shared by the Claude and the Codex epic-scope gates; reads no file,
390
+ process, or environment value. Returns Outcome 'none' (not epic scope), 'deny' (with
391
+ the deny Reason), or 'allow' (empty Reason). The caller supplies the readiness predicate,
392
+ invoked as & $ReadinessFailure $evaluation.Scope; the first evaluation with a non-empty
393
+ failure denies, and the session-root wording is unchanged.
394
+ .OUTPUTS
395
+ System.Management.Automation.PSCustomObject
396
+ #>
397
+ [CmdletBinding()]
398
+ [OutputType([pscustomobject])]
399
+ param(
400
+ [Parameter(Mandatory)][pscustomobject] $Verdict,
401
+ [Parameter(Mandatory)][scriptblock] $ReadinessFailure
402
+ )
403
+
404
+ if ($Verdict.Verdict -eq 'none') {
405
+ return [pscustomobject]@{ Outcome = 'none'; Reason = '' }
406
+ }
407
+ if ($Verdict.Verdict -eq 'deny') {
408
+ $reason = Get-OrchestrationEpicTargetDenyReason -ReasonCode $Verdict.ReasonCode -Detail $Verdict.Detail
409
+ return [pscustomobject]@{ Outcome = 'deny'; Reason = $reason }
410
+ }
411
+ foreach ($evaluation in $Verdict.Evaluations) {
412
+ $scope = $evaluation.Scope
413
+ $failure = & $ReadinessFailure $evaluation.Scope
414
+ if (-not $failure) {
415
+ continue
416
+ }
417
+ if ($evaluation.IsSessionRoot) {
418
+ $reason = 'PREIMPLEMENTATION_GATE_BLOCKED: this epic-scope operation was evaluated against ' +
419
+ "$($scope.CheckpointPath), and the failed readiness predicate is '$failure'. " +
420
+ 'Implementation operations in epic scope require that checkpoint to satisfy every ' +
421
+ 'readiness predicate, and a production path may be staged or edited only while a merge is in progress.'
422
+ return [pscustomobject]@{ Outcome = 'deny'; Reason = $reason }
423
+ }
424
+ $reasonArguments = @{
425
+ ReasonCode = 'target-not-ready'
426
+ Detail = $evaluation.Target
427
+ CheckpointPath = $scope.CheckpointPath
428
+ Failure = $failure
429
+ }
430
+ return [pscustomobject]@{ Outcome = 'deny'; Reason = (Get-OrchestrationEpicTargetDenyReason @reasonArguments) }
431
+ }
432
+ return [pscustomobject]@{ Outcome = 'allow'; Reason = '' }
433
+ }
@@ -4,29 +4,30 @@
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
 
9
- Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force
10
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'HookPayload.psm1' -ErrorRecord $_ }
10
11
 
11
12
  # Pure pathspec classifier for the issue #539 orchestration-bookkeeping staging exemption.
12
13
  # Extracted to a dot-sourced sibling so this file stays inside the 500-line cap, following
13
14
  # the enforce-pr-author-skill.ps1 headroom-split precedent.
14
- . (Join-Path $PSScriptRoot 'enforce-orchestration-preimplementation-gate-helpers.ps1')
15
+ try { . (Join-Path $PSScriptRoot 'enforce-orchestration-preimplementation-gate-helpers.ps1') } catch { Add-HookDependencyFailure -Name 'enforce-orchestration-preimplementation-gate-helpers.ps1' -ErrorRecord $_ }
15
16
 
16
17
  # Pure mode dispatch and per-mode readiness predicates for issue #554. A new sibling
17
18
  # rather than an addition to the helpers file above, whose header declares a different
18
19
  # normative contract and which lacks headroom; leaving that file byte-untouched is the
19
20
  # proof the issue #539 exemption is behaviourally unchanged.
20
- . (Join-Path $PSScriptRoot 'enforce-orchestration-preimplementation-gate-modes.ps1')
21
+ try { . (Join-Path $PSScriptRoot 'enforce-orchestration-preimplementation-gate-modes.ps1') } catch { Add-HookDependencyFailure -Name 'enforce-orchestration-preimplementation-gate-modes.ps1' -ErrorRecord $_ }
21
22
 
22
23
  # Epic-scope command and path legs (issue #663, decision D2), the three checkpoint read
23
24
  # seams, the mode deny reason, and the target resolution with its import guard (issue
24
25
  # #690), all relocated to this sibling to keep the gate inside the 500-line cap.
25
- . (Join-Path $PSScriptRoot 'enforce-orchestration-preimplementation-gate-epic-scope.ps1')
26
+ try { . (Join-Path $PSScriptRoot 'enforce-orchestration-preimplementation-gate-epic-scope.ps1') } catch { Add-HookDependencyFailure -Name 'enforce-orchestration-preimplementation-gate-epic-scope.ps1' -ErrorRecord $_ }
26
27
 
27
28
  # Shared command-line parser (issue #545): per-segment scan text and structural matching.
28
- . (Join-Path $PSScriptRoot 'hook-command-scanner.ps1')
29
- . (Join-Path $PSScriptRoot 'hook-command-invocation.ps1')
29
+ try { . (Join-Path $PSScriptRoot 'hook-command-scanner.ps1') } catch { Add-HookDependencyFailure -Name 'hook-command-scanner.ps1' -ErrorRecord $_ }
30
+ try { . (Join-Path $PSScriptRoot 'hook-command-invocation.ps1') } catch { Add-HookDependencyFailure -Name 'hook-command-invocation.ps1' -ErrorRecord $_ }
30
31
 
31
32
  # Every orchestration checkpoint a planner or orchestrator surface writes. Writing one
32
33
  # of these is orchestration bookkeeping, not implementation, so the gate must not
@@ -306,9 +307,8 @@ function Invoke-OrchestrationPreimplementationGateDecision {
306
307
  [AllowEmptyString()]
307
308
  [string] $ParallelCheckpointRaw
308
309
  )
309
-
310
- # A failed worktree-resolution import denies before any other logic (issue #690).
311
- $importFailure = Get-OrchestrationGateImportFailureDecision; if ($null -ne $importFailure) { return $importFailure }
310
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'PREIMPLEMENTATION_GATE_BLOCKED:'
311
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
312
312
 
313
313
  $payload = Resolve-ClaudeHookToolInput -Raw $ToolInputRaw
314
314
  if (-not $payload.IsValid) {
@@ -383,11 +383,13 @@ function Invoke-OrchestrationPreimplementationGateDecision {
383
383
  $modeCheckpoint = ConvertFrom-CheckpointJson -Json ([string]$modeRaw)
384
384
  } catch { $modeCheckpoint = $null }
385
385
  $folder = Find-OrchestrationDelegationTargetFolder -Prompt $prompt
386
- $issue = Find-OrchestrationDelegationIssueNumber -Prompt $prompt
386
+ # Only the keyed issue number breaks a tie among cited folders (#565 CR-1); the bare hash form feeds the D3 record lookup only.
387
+ $issue = Find-OrchestrationDelegationIssueNumber -Prompt $prompt -KeyedOnly
388
+ $fallbackIssue = Find-OrchestrationDelegationIssueNumber -Prompt $prompt
387
389
  $failure = if ($isEpic) {
388
- Get-EpicOrchestrationReadinessFailure -Checkpoint $modeCheckpoint -TargetFolder $folder -IssueNumber $issue
390
+ Get-EpicOrchestrationReadinessFailure -Checkpoint $modeCheckpoint -TargetFolder $folder -IssueNumber $issue -FallbackIssueNumber $fallbackIssue
389
391
  } else {
390
- Get-ParallelOrchestrationReadinessFailure -Checkpoint $modeCheckpoint -TargetFolder $folder -IssueNumber $issue
392
+ Get-ParallelOrchestrationReadinessFailure -Checkpoint $modeCheckpoint -TargetFolder $folder -IssueNumber $issue -FallbackIssueNumber $fallbackIssue
391
393
  }
392
394
  if (-not $failure) { return Get-OrchestrationPreimplementationGateAllowDecision }
393
395
  return Get-OrchestrationPreimplementationGateBlockDecision -Reason (
@@ -454,6 +456,9 @@ function Invoke-OrchestrationPreimplementationGateEntryPoint {
454
456
  if ($MyInvocation.InvocationName -eq '.') {
455
457
  return
456
458
  }
459
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('PREIMPLEMENTATION_GATE_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
460
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'PREIMPLEMENTATION_GATE_BLOCKED:'
461
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
457
462
 
458
463
  # The entry point returns its [int] exit code as the last pipeline element and the
459
464
  # decision JSON before it. `exit (<call>)` would capture BOTH into the exit
@@ -32,9 +32,10 @@
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
  # The two tokens this gate matches on. These are the ONLY places either token literal
39
40
  # appears in this file; the seam test extracts the consumer-side values from exactly
40
41
  # these two named assignments.
@@ -48,8 +49,8 @@ $script:AbandonBlockedReasonCode = 'PARALLEL_ABANDON_BLOCKED'
48
49
  # rather than above them, deliberately: the assignments keep their existing line numbers,
49
50
  # so the diff shows no change at either line. Both are still declared before any function
50
51
  # body runs, because the file executes top to bottom before any of its functions is called.
51
- . (Join-Path $PSScriptRoot 'hook-command-scanner.ps1')
52
- . (Join-Path $PSScriptRoot 'hook-command-invocation.ps1')
52
+ try { . (Join-Path $PSScriptRoot 'hook-command-scanner.ps1') } catch { Add-HookDependencyFailure -Name 'hook-command-scanner.ps1' -ErrorRecord $_ }
53
+ try { . (Join-Path $PSScriptRoot 'hook-command-invocation.ps1') } catch { Add-HookDependencyFailure -Name 'hook-command-invocation.ps1' -ErrorRecord $_ }
53
54
 
54
55
  function Get-ParallelAbandonGateToolInput {
55
56
  <#
@@ -314,6 +315,8 @@ function Invoke-ParallelAbandonGateDecision {
314
315
  [AllowEmptyString()]
315
316
  [string] $ToolInputRaw
316
317
  )
318
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'enforce-parallel-abandon-gate:'
319
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
317
320
 
318
321
  # An absent payload carries no command to gate, so there is nothing in scope. This is
319
322
  # allow rather than deny because the gate constrains one specific destructive command
@@ -345,6 +348,9 @@ function Invoke-ParallelAbandonGateDecision {
345
348
  if ($MyInvocation.InvocationName -eq '.') {
346
349
  return
347
350
  }
351
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('enforce-parallel-abandon-gate: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
352
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'enforce-parallel-abandon-gate:'
353
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
348
354
 
349
355
  $decision = Invoke-ParallelAbandonGateDecision -ToolInputRaw (Get-ParallelAbandonGateToolInput)
350
356