@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
@@ -8,8 +8,9 @@
8
8
  #>
9
9
  [CmdletBinding()]
10
10
  param()
11
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
11
12
 
12
- . (Join-Path $PSScriptRoot 'codex-authority-store.ps1')
13
+ try { . (Join-Path $PSScriptRoot 'codex-authority-store.ps1') } catch { Add-HookDependencyFailure -Name 'codex-authority-store.ps1' -ErrorRecord $_ }
13
14
 
14
15
  function ConvertFrom-EpicRootGatePayload {
15
16
  [CmdletBinding()]
@@ -54,6 +55,8 @@ function Invoke-EpicRootInvocationDecision {
54
55
  [Parameter(Mandatory)][string] $PayloadRaw,
55
56
  [AllowNull()][AllowEmptyString()][string] $AttestationRaw
56
57
  )
58
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EPIC_INVOCATION_ORIGIN_BLOCKED:'
59
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
57
60
 
58
61
  $payload = ConvertFrom-EpicRootGatePayload -Raw $PayloadRaw -Name 'PreToolUse input'
59
62
  if ([string]::IsNullOrWhiteSpace($AttestationRaw)) {
@@ -87,6 +90,9 @@ function Invoke-EpicRootInvocationDecision {
87
90
  if ($MyInvocation.InvocationName -eq '.') {
88
91
  return
89
92
  }
93
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('EPIC_INVOCATION_ORIGIN_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
94
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EPIC_INVOCATION_ORIGIN_BLOCKED:'
95
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
90
96
 
91
97
  try {
92
98
  $payloadRaw = [Console]::In.ReadToEnd()
@@ -10,8 +10,9 @@
10
10
  #>
11
11
  [CmdletBinding()]
12
12
  param()
13
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
13
14
 
14
- . (Join-Path $PSScriptRoot 'codex-epic-child-launch-attestation.ps1')
15
+ try { . (Join-Path $PSScriptRoot 'codex-epic-child-launch-attestation.ps1') } catch { Add-HookDependencyFailure -Name 'codex-epic-child-launch-attestation.ps1' -ErrorRecord $_ }
15
16
 
16
17
  $script:AllowedDependencyStatuses = @('merged', 'worktree_removed')
17
18
 
@@ -178,6 +179,8 @@ function Invoke-CodexEpicWaveDecision {
178
179
  [AllowEmptyString()][string] $RepositoryRoot = '',
179
180
  [datetimeoffset] $Now = [datetimeoffset]::UtcNow
180
181
  )
182
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EPIC_WAVE_BARRIER_BLOCKED:'
183
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
181
184
 
182
185
  $payload = ConvertFrom-CodexWaveJson -Raw $PayloadRaw -Name 'PreToolUse input'
183
186
  if (-not (Test-CodexWaveMutation -Payload $payload)) {
@@ -252,6 +255,9 @@ function Get-CodexPrimaryWorktreeRoot {
252
255
  if ($MyInvocation.InvocationName -eq '.') {
253
256
  return
254
257
  }
258
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('EPIC_WAVE_BARRIER_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
259
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EPIC_WAVE_BARRIER_BLOCKED:'
260
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
255
261
 
256
262
  try {
257
263
  $payloadRaw = [Console]::In.ReadToEnd()
@@ -4,12 +4,13 @@
4
4
  #>
5
5
  [CmdletBinding()]
6
6
  param()
7
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
7
8
 
8
9
  # Shared command-line parser (issue #545). The scope filter and the operand extractor below
9
10
  # both run against the segment that structurally invokes `git worktree remove`, which is what
10
11
  # keeps the two runtimes on one implementation of the same concern.
11
- . (Join-Path $PSScriptRoot 'hook-command-scanner.ps1')
12
- . (Join-Path $PSScriptRoot 'hook-command-invocation.ps1')
12
+ try { . (Join-Path $PSScriptRoot 'hook-command-scanner.ps1') } catch { Add-HookDependencyFailure -Name 'hook-command-scanner.ps1' -ErrorRecord $_ }
13
+ try { . (Join-Path $PSScriptRoot 'hook-command-invocation.ps1') } catch { Add-HookDependencyFailure -Name 'hook-command-invocation.ps1' -ErrorRecord $_ }
13
14
 
14
15
  $script:SafeWorktreeStatuses = @('merged', 'worktree_removed')
15
16
 
@@ -33,40 +34,6 @@ function ConvertFrom-CodexWorktreeJson {
33
34
  }
34
35
  }
35
36
 
36
- function Get-CodexWorktreeRemovalPath {
37
- <#
38
- .SYNOPSIS
39
- Extract the target worktree path from a git worktree remove command.
40
- .DESCRIPTION
41
- The operand comes from the segment that structurally invokes git worktree remove, so
42
- a chained 'cd <path> &&' segment contributes nothing and a quoted mention of the
43
- phrase resolves to no operand. The tokenizer strips balanced double and single
44
- quotes, which is what the previous pattern's `double` and `single` alternatives did.
45
-
46
- The previous pattern accepted '--force' only immediately after 'remove'. That
47
- spelling is preserved and the trailing spelling now works too, because '--force' is a
48
- zero-argument flag that never contributes an operand wherever it is written. Its
49
- presence is read structurally through Test-CommandLineFlag rather than by a raw-text
50
- search. The empty-string-on-miss contract is unchanged: callers test `if ($target)`.
51
- .OUTPUTS
52
- System.String
53
- #>
54
- [CmdletBinding()]
55
- [OutputType([string])]
56
- param([Parameter(Mandatory)][AllowEmptyString()][string] $Command)
57
-
58
- $hasForce = Test-CommandLineFlag -CommandText $Command -CommandWord 'git' -SubcommandPath @('worktree', 'remove') -FlagName '--force'
59
- $operands = @(Get-CommandLineOperand -CommandText $Command -CommandWord 'git' -SubcommandPath @('worktree', 'remove'))
60
-
61
- if ($operands.Count -gt 0) {
62
- return [string]$operands[0]
63
- }
64
- if ($hasForce) {
65
- return '--force'
66
- }
67
- return ''
68
- }
69
-
70
37
  function Get-NormalizedCodexWorktreePath {
71
38
  [CmdletBinding()]
72
39
  [OutputType([string])]
@@ -114,6 +81,8 @@ function Invoke-CodexWorktreeRemovalDecision {
114
81
  [Parameter(Mandatory)][string] $PayloadRaw,
115
82
  [AllowNull()][AllowEmptyString()][string] $EpicCheckpointRaw
116
83
  )
84
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EPIC_WORKTREE_REMOVAL_BLOCKED:'
85
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
117
86
 
118
87
  $payload = ConvertFrom-CodexWorktreeJson -Raw $PayloadRaw -Name 'PreToolUse input'
119
88
  if ([string]$payload.tool_name -ne 'Bash') {
@@ -126,18 +95,59 @@ function Invoke-CodexWorktreeRemovalDecision {
126
95
  if (-not (Test-CommandLineInvocation -CommandText $command -CommandWord 'git' -SubcommandPath @('worktree', 'remove'))) {
127
96
  return $null
128
97
  }
129
- $target = Get-CodexWorktreeRemovalPath -Command $command
98
+ # Issue #824: the removal targets are derived structurally from every invocation. A
99
+ # target that cannot be derived denies before the checkpoint is read, and every derived
100
+ # target must be authorized on its own.
101
+ $resolution = Resolve-CommandLineInvocationTarget -CommandText $command -CommandWord 'git' -SubcommandPath @('worktree', 'remove')
102
+ if ($resolution.Status -eq 'NoMatch') {
103
+ return $null
104
+ }
105
+ if ($resolution.Status -ne 'Targets') {
106
+ return [ordered]@{
107
+ hookSpecificOutput = [ordered]@{
108
+ hookEventName = 'PreToolUse'
109
+ permissionDecision = 'deny'
110
+ permissionDecisionReason = 'EPIC_WORKTREE_REMOVAL_BLOCKED: TARGET_WORKTREE_NOT_DERIVABLE: the git worktree remove target cannot be derived from the command text. No checkpoint can authorize this removal.'
111
+ }
112
+ }
113
+ }
130
114
  $checkpoint = ConvertFrom-CodexWorktreeJson -Raw $EpicCheckpointRaw -Name 'epic checkpoint' -Optional
131
115
  $workingDirectory = if ([string]::IsNullOrWhiteSpace([string]$payload.cwd)) {
132
116
  (Get-Location).Path
133
117
  } else {
134
118
  [string]$payload.cwd
135
119
  }
136
- $feature = if ($target) {
137
- Find-CodexWorktreeFeature -Checkpoint $checkpoint -TargetPath $target -WorkingDirectory $workingDirectory
138
- } else {
139
- $null
120
+ foreach ($target in @($resolution.Targets)) {
121
+ $denial = Get-CodexWorktreeRemovalTargetDenial -Target $target -Checkpoint $checkpoint -WorkingDirectory $workingDirectory
122
+ if ($null -ne $denial) {
123
+ return $denial
124
+ }
140
125
  }
126
+ return $null
127
+ }
128
+
129
+ function Get-CodexWorktreeRemovalTargetDenial {
130
+ <#
131
+ .SYNOPSIS
132
+ Evaluate one derived removal target and return its deny decision, or $null when authorized.
133
+ .PARAMETER Target
134
+ One removal target derived by Resolve-CommandLineInvocationTarget.
135
+ .PARAMETER Checkpoint
136
+ The parsed epic checkpoint, or $null when absent or unreadable.
137
+ .PARAMETER WorkingDirectory
138
+ The directory a relative target resolves against.
139
+ .OUTPUTS
140
+ System.Collections.Specialized.OrderedDictionary or $null
141
+ #>
142
+ [CmdletBinding()]
143
+ [OutputType([System.Collections.Specialized.OrderedDictionary])]
144
+ param(
145
+ [Parameter(Mandatory)][string] $Target,
146
+ [AllowNull()] $Checkpoint,
147
+ [Parameter(Mandatory)][string] $WorkingDirectory
148
+ )
149
+
150
+ $feature = Find-CodexWorktreeFeature -Checkpoint $Checkpoint -TargetPath $Target -WorkingDirectory $WorkingDirectory
141
151
  if ($null -ne $feature -and
142
152
  @($feature.PSObject.Properties.Name) -contains 'merge_status' -and
143
153
  $script:SafeWorktreeStatuses -contains [string]$feature.merge_status) {
@@ -156,6 +166,9 @@ function Invoke-CodexWorktreeRemovalDecision {
156
166
  if ($MyInvocation.InvocationName -eq '.') {
157
167
  return
158
168
  }
169
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('EPIC_WORKTREE_REMOVAL_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
170
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EPIC_WORKTREE_REMOVAL_BLOCKED:'
171
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
159
172
 
160
173
  try {
161
174
  $payloadRaw = [Console]::In.ReadToEnd()
@@ -40,10 +40,11 @@
40
40
  #>
41
41
  [CmdletBinding()]
42
42
  param()
43
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
43
44
 
44
45
  # Shared Codex PreToolUse transport: stdin payload parsing and tool_input-to-file
45
46
  # mapping for every tool name the ^(apply_patch|Edit|Write)$ matcher admits.
46
- . (Join-Path $PSScriptRoot 'codex-pretooluse-file-mapping.ps1')
47
+ try { . (Join-Path $PSScriptRoot 'codex-pretooluse-file-mapping.ps1') } catch { Add-HookDependencyFailure -Name 'codex-pretooluse-file-mapping.ps1' -ErrorRecord $_ }
47
48
 
48
49
  function Test-EvidenceLocationForbidden {
49
50
  <#
@@ -122,6 +123,8 @@ function Invoke-EvidenceLocationDecision {
122
123
  param(
123
124
  [string] $ToolInputRaw
124
125
  )
126
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EVIDENCE_LOCATION_BLOCKED:'
127
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
125
128
 
126
129
  if (-not $ToolInputRaw) {
127
130
  return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
@@ -192,5 +195,8 @@ function Invoke-EvidenceLocationEntryPoint {
192
195
  if ($MyInvocation.InvocationName -eq '.') {
193
196
  return
194
197
  }
198
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('EVIDENCE_LOCATION_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
199
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EVIDENCE_LOCATION_BLOCKED:'
200
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
195
201
 
196
202
  exit (Invoke-EvidenceLocationEntryPoint -PayloadRaw ([Console]::In.ReadToEnd()))
@@ -26,12 +26,16 @@
26
26
  Get-OrchestrationDelegationCheckpointPath (modes file), and the gate's allow and block
27
27
  decision constructors Get-OrchestrationPreimplementationGateAllowDecision and
28
28
  Get-OrchestrationPreimplementationGateBlockDecision. The resolver and its seams come
29
- from the resolution sibling dot-sourced below.
29
+ from the resolution sibling dot-sourced below. The per-segment target resolver comes
30
+ from the targets file dot-sourced below (issue #738), which calls
31
+ Read-CommandLineSegment, Get-CommandLineGlobalOption, and Get-CommandLineInvocation,
32
+ loaded by the gate's dot-source of hook-command-invocation.ps1.
30
33
  Mirrored byte-identically under extensions/drm-copilot/resources/codex-and-agents-customizations/.
31
34
  AUTHORITY: PowerShell-authoritative. The epic readiness predicate has no reference implementation in another language, and this hook starts no interpreter process (issue #707, D15).
32
35
  #>
33
36
 
34
37
  . (Join-Path $PSScriptRoot 'enforce-orchestration-preimplementation-gate-epic-resolution.ps1')
38
+ . (Join-Path $PSScriptRoot 'enforce-orchestration-preimplementation-gate-targets.ps1')
35
39
 
36
40
  # An epic manifest lives under the epics tree; the pattern is tested after backslash
37
41
  # normalisation so either separator spelling is accepted.
@@ -152,11 +156,14 @@ function Get-OrchestrationEpicScopeDecision {
152
156
  .SYNOPSIS
153
157
  Returns the epic-scope decision for an implementation-classified command or path, or $null.
154
158
  .DESCRIPTION
155
- Resolves epic scope with worktree-HEAD matching and the command's -C selector. When
156
- the call is not epic scope, returns $null so the caller's single-feature path runs
157
- unchanged. In epic scope, returns an allow decision when the epic command-leg
158
- readiness predicate passes, and otherwise a deny naming the epic checkpoint and the
159
- failed conjunct.
159
+ Resolves the target of every command segment, apply_patch file marker, and path
160
+ (issue #738), then the epic scope of the session root and of each target. A command
161
+ whose text is an apply_patch body is decided as a path leg over its marker paths.
162
+ When neither is epic scope, returns $null so the caller's single-feature path runs
163
+ unchanged. In epic scope, denies an unresolvable, ambiguous, or out-of-scope target,
164
+ and otherwise evaluates the epic command-leg readiness predicate against every
165
+ target: the first failure denies (the session-root wording is unchanged), and no
166
+ failure allows.
160
167
  .PARAMETER Command
161
168
  The command line of a command or apply_patch leg; empty for a path leg.
162
169
  .PARAMETER FilePath
@@ -175,15 +182,29 @@ function Get-OrchestrationEpicScopeDecision {
175
182
  if (-not $Command -and -not $FilePath) {
176
183
  return $null
177
184
  }
178
- $selector = if ($Command) { Get-OrchestrationEpicScopeSelector -Command $Command } else { $null }
179
- $scope = Resolve-EpicScopeCheckpoint -SessionRoot (Get-Location).Path -WorktreeSelector $selector
180
- if (-not $scope.IsEpicScope) {
185
+ $epicScopeSessionRoot = (Get-Location).Path
186
+ $legCommand = [string]$Command
187
+ $legPaths = @($FilePath)
188
+ $markerPaths = @(Get-OrchestrationPatchMarkerPath -PatchText $legCommand)
189
+ if ($markerPaths.Count -gt 0) {
190
+ $legCommand = ''
191
+ $legPaths = $markerPaths
192
+ }
193
+ $targetResult = Get-OrchestrationCommandTarget -Command $legCommand -FilePath $legPaths -SessionRoot $epicScopeSessionRoot
194
+ $scopeResolver = {
195
+ param([string] $Selector)
196
+ Resolve-EpicScopeCheckpoint -SessionRoot $epicScopeSessionRoot -WorktreeSelector $Selector
197
+ }
198
+ $verdict = Resolve-OrchestrationEpicTargetVerdict -SessionRoot $epicScopeSessionRoot -TargetResult $targetResult -ScopeResolver $scopeResolver
199
+ $outcome = Resolve-OrchestrationEpicTargetOutcome -Verdict $verdict -ReadinessFailure {
200
+ param($Scope)
201
+ Get-EpicCommandLegReadinessFailure -Checkpoint $Scope.Checkpoint -MergeInProgress $Scope.MergeInProgress
202
+ }
203
+ if ($outcome.Outcome -eq 'none') {
181
204
  return $null
182
205
  }
183
-
184
- $failure = Get-EpicCommandLegReadinessFailure -Checkpoint $scope.Checkpoint -MergeInProgress $scope.MergeInProgress
185
- if (-not $failure) {
186
- return Get-OrchestrationPreimplementationGateAllowDecision
206
+ if ($outcome.Outcome -eq 'deny') {
207
+ return Get-OrchestrationPreimplementationGateBlockDecision -Reason $outcome.Reason
187
208
  }
188
- return Get-OrchestrationPreimplementationGateBlockDecision -Reason ("PREIMPLEMENTATION_GATE_BLOCKED: this epic-scope operation was evaluated against $($scope.CheckpointPath), and the failed readiness predicate is '$failure'. Implementation operations in epic scope require that checkpoint to satisfy every readiness predicate, and a production path may be staged or edited only while a merge is in progress.")
209
+ return Get-OrchestrationPreimplementationGateAllowDecision
189
210
  }
@@ -9,6 +9,11 @@
9
9
  tree? Every parse ambiguity answers false, so the caller's pre-change deny is the
10
10
  fallback for every unmodeled form.
11
11
 
12
+ Shell-agnostic parsing (issue #735): the shell that executes a hooked command is
13
+ undetermined per command, so every shape whose meaning differs between a POSIX shell
14
+ and PowerShell denies - a backslash anywhere, and { } , ( ) @ outside quotes. Evidence:
15
+ research/research.2026-10-08T14-00.md (Q1) in the issue #732 feature folder.
16
+
12
17
  The normative contract is the D4 fail-closed rule table in
13
18
  docs/features/active/2026-08-24-preimplementation-gate-blocks-planner-integration-commits-539/spec.md.
14
19
  Rule-table row numbers are cited inline against the code that realizes them.
@@ -28,15 +33,15 @@ $script:OrchestrationBookkeepingTrees = @(
28
33
  )
29
34
 
30
35
  # Characters that make a command line statically unresolvable (D4 row 12, as narrowed by
31
- # issues #663 and #713): interpolation characters outside single quotes; outside-quote
32
- # characters (`<`, `>`, and the `#` comment introducer) outside any quote; and typographic
33
- # quotes (U+2018 to U+201E) anywhere, because a PowerShell host reads them as quotes.
36
+ # issues #663, #713, #732, and #735): interpolation characters outside single quotes;
37
+ # outside-quote characters (`<`, `>`, the `#` comment introducer, and the shell-divergent
38
+ # `{ } , ( ) @`) outside any quote; and typographic quotes (U+2018 to U+201E) anywhere.
34
39
  $script:InterpolationCommandCharacters = [char[]]@('$', '`')
35
- $script:OutsideQuoteCommandCharacters = [char[]]@('>', '<', '#')
40
+ $script:OutsideQuoteCommandCharacters = [char[]]@('>', '<', '#', '{', '}', ',', '(', ')', '@')
36
41
  $script:TypographicQuoteCharacters = [char[]]@(0x2018, 0x2019, 0x201A, 0x201B, 0x201C, 0x201D, 0x201E)
37
42
 
38
- # Wildcards that make an operand a glob (D4 row 15). Only the literal prefix before the
39
- # first of these is prefix-tested.
43
+ # Wildcards a selector value may not carry (LACS L6). An operand carrying one fails the
44
+ # operand allowlist (issue #732).
40
45
  $script:PathspecWildcardCharacters = [char[]]@('*', '?', '[')
41
46
 
42
47
  # The single repository selector modelled between the command name and the subcommand: D4
@@ -112,12 +117,12 @@ function Test-OrchestrationCommandTextUnresolvable {
112
117
  .SYNOPSIS
113
118
  Reports whether a command line carries a statically unresolvable character.
114
119
  .DESCRIPTION
115
- Realizes D4 row 12 as narrowed by issues #663 and #713, with quote state tracked as in
116
- Split-OrchestrationCommandLine. `$` or backtick answers true outside quotes or inside
117
- double quotes (single quotes keep it literal); `<`, `>`, or a `#` comment answers true
118
- only outside a quoted span. A typographic quote (U+2018 to U+201E), a backslash before
119
- any quote character, or a backslash inside a double-quoted span answers true anywhere:
120
- a shell may end the span where this scan does not (fail closed).
120
+ Realizes D4 row 12 as narrowed by issues #663, #713, #732, and #735, with quote state
121
+ tracked as in Split-OrchestrationCommandLine. `$` or backtick answers true outside
122
+ quotes or inside double quotes (single quotes keep it literal); `<`, `>`, `#`, and
123
+ `{ } , ( ) @` answer true only outside a quoted span. A typographic quote (U+2018 to
124
+ U+201E) or a backslash answers true anywhere, because POSIX shells and PowerShell read
125
+ them differently and the executing shell is undetermined (fail closed).
121
126
  .PARAMETER CommandText
122
127
  The full command line as the shell would receive it.
123
128
  .OUTPUTS
@@ -127,8 +132,8 @@ function Test-OrchestrationCommandTextUnresolvable {
127
132
  [OutputType([bool])]
128
133
  param([Parameter(Mandatory)][AllowEmptyString()][string] $CommandText)
129
134
 
130
- # An escaped or typographic quote moves a span boundary the scan cannot model (#663, #713).
131
- if ($CommandText.Contains('\"') -or $CommandText.Contains("\'") -or $CommandText.IndexOfAny($script:TypographicQuoteCharacters) -ge 0) {
135
+ # A backslash or typographic quote is read differently by POSIX shells and PowerShell (#735).
136
+ if ($CommandText.Contains('\') -or $CommandText.IndexOfAny($script:TypographicQuoteCharacters) -ge 0) {
132
137
  return $true
133
138
  }
134
139
 
@@ -141,13 +146,9 @@ function Test-OrchestrationCommandTextUnresolvable {
141
146
  return $true
142
147
  }
143
148
 
144
- # Decide by quote state: inside a span only the closing quote matters, and any
145
- # backslash inside a double-quoted span is an unmodelled escape; outside a span a
146
- # quote opens one and a redirection character is unresolvable.
149
+ # Decide by quote state: inside a span only the closing quote matters; outside a
150
+ # span a quote opens one and an outside-quote character is unresolvable.
147
151
  if ($openQuote -ne [char]0) {
148
- if ($openQuote -eq '"' -and $character -eq '\') {
149
- return $true
150
- }
151
152
  if ($character -eq $openQuote) {
152
153
  $openQuote = [char]0
153
154
  }
@@ -222,9 +223,10 @@ function Test-ExemptOrchestrationOperand {
222
223
  .SYNOPSIS
223
224
  Tests one pathspec operand against the five exempt orchestration trees.
224
225
  .DESCRIPTION
225
- Realizes D4 rows 3, 9, 15, 16, 17, and 18. Backslashes normalize to forward slashes
226
- before the prefix test (row 18); pathspec magic, absolute spellings, parent-directory
227
- segments, and globs whose literal prefix escapes the exempt set all deny.
226
+ Realizes D4 rows 3, 9, 15, 16, and 17 as narrowed by issues #732 and #735. An operand
227
+ is exempt only when it matches the plain ASCII allowlist A-Z a-z 0-9 . _ / - (which
228
+ excludes pathspec magic, drive letters, backslashes, globs, braces, and non-ASCII
229
+ look-alikes), is not rooted, carries no '..' segment, and starts with an exempt tree.
228
230
  .OUTPUTS
229
231
  System.Boolean
230
232
  #>
@@ -232,43 +234,14 @@ function Test-ExemptOrchestrationOperand {
232
234
  [OutputType([bool])]
233
235
  param([Parameter(Mandatory)][AllowEmptyString()][string] $Operand)
234
236
 
235
- if (-not $Operand) {
236
- return $false
237
- }
238
-
239
- # Row 3b and row 9: any leading colon is pathspec magic, which can escape or invert the
240
- # tree scope, so no colon-led operand is ever resolvable by a prefix test.
241
- if ($Operand.StartsWith(':')) {
237
+ if ($Operand -cnotmatch '^[A-Za-z0-9._/-]+$' -or $Operand.StartsWith('/')) {
242
238
  return $false
243
239
  }
244
-
245
- # Row 18: separator normalization precedes every prefix comparison below.
246
- $normalized = $Operand -replace '\\', '/'
247
-
248
- # Row 16: rooted, drive-lettered, and UNC spellings all deny; the exempt prefixes stay
249
- # repo-relative (this is the posture issue #516 later composes with).
250
- if ($normalized.StartsWith('/')) {
240
+ if (($Operand -split '/') -contains '..') {
251
241
  return $false
252
242
  }
253
- if ($normalized -match '^[A-Za-z]:') {
254
- return $false
255
- }
256
-
257
- # Rows 15c and 17: any parent-directory component escapes the prefix.
258
- if (($normalized -split '/') -contains '..') {
259
- return $false
260
- }
261
-
262
- # Row 15: only the literal prefix before the first wildcard is prefix-tested, so a glob
263
- # whose wildcard occupies or truncates an ancestor segment cannot pass.
264
- $literalPrefix = $normalized
265
- $wildcardIndex = $normalized.IndexOfAny($script:PathspecWildcardCharacters)
266
- if ($wildcardIndex -ge 0) {
267
- $literalPrefix = $normalized.Substring(0, $wildcardIndex)
268
- }
269
-
270
243
  foreach ($tree in $script:OrchestrationBookkeepingTrees) {
271
- if ($literalPrefix.StartsWith($tree)) {
244
+ if ($Operand.StartsWith($tree)) {
272
245
  return $true
273
246
  }
274
247
  }
@@ -444,7 +417,7 @@ function Test-ExemptOrchestrationStagingCommand {
444
417
  command line splits cleanly into segments and EVERY segment parses as a complete,
445
418
  recognized staging or integration invocation carrying at least one pathspec operand,
446
419
  with every operand resolving inside one of the five exempt orchestration-bookkeeping
447
- trees after balanced-quote stripping and separator normalization.
420
+ trees after balanced-quote stripping.
448
421
 
449
422
  The all-segments reading is deliberate and fail-closed: a chained line denies unless
450
423
  each of its segments is independently a recognized all-exempt invocation, so a
@@ -464,8 +437,8 @@ function Test-ExemptOrchestrationStagingCommand {
464
437
  return $false
465
438
  }
466
439
 
467
- # Row 12: `$` or backtick outside single quotes, `<`, `>`, or `#` outside quotes, any
468
- # typographic quote, and unmodelled backslash escapes make the operand list untrustworthy.
440
+ # Row 12: `$` or backtick outside single quotes, `< > # { } , ( ) @` outside quotes, and
441
+ # any typographic quote or backslash make the operand list untrustworthy (#732, #735).
469
442
  if (Test-OrchestrationCommandTextUnresolvable -CommandText $CommandText) {
470
443
  return $false
471
444
  }