@danmoisan/drm-copilot-mcp 1.1.18 → 1.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. package/out/mcp-server.js +1149 -661
  2. package/package.json +3 -2
  3. package/resources/claude-customizations/.claude/agents/atomic-executor.md +28 -6
  4. package/resources/claude-customizations/.claude/agents/atomic-planner.md +20 -2
  5. package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +17 -4
  6. package/resources/claude-customizations/.claude/agents/epic-planner.md +5 -4
  7. package/resources/claude-customizations/.claude/agents/feature-review.md +6 -4
  8. package/resources/claude-customizations/.claude/agents/orchestrator.md +9 -5
  9. package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +35 -13
  10. package/resources/claude-customizations/.claude/agents/parallel-planner.md +9 -6
  11. package/resources/claude-customizations/.claude/agents/pr-author.md +15 -2
  12. package/resources/claude-customizations/.claude/agents/prd-feature.md +1 -1
  13. package/resources/claude-customizations/.claude/agents/task-researcher.md +8 -0
  14. package/resources/claude-customizations/.claude/hooks/check-powershell-test-purity.ps1 +7 -1
  15. package/resources/claude-customizations/.claude/hooks/check-python-test-purity.ps1 +7 -1
  16. package/resources/claude-customizations/.claude/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
  17. package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +111 -46
  18. package/resources/claude-customizations/.claude/hooks/enforce-completion-helpers.ps1 +105 -0
  19. package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +8 -1
  20. package/resources/claude-customizations/.claude/hooks/enforce-epic-invocation-origin.ps1 +7 -1
  21. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-command.ps1 +190 -0
  22. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-resolution.ps1 +116 -59
  23. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +78 -77
  24. package/resources/claude-customizations/.claude/hooks/enforce-epic-wave-barrier.ps1 +74 -75
  25. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate-resolution.ps1 +127 -35
  26. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +61 -57
  27. package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +7 -1
  28. package/resources/claude-customizations/.claude/hooks/enforce-feature-folder-order.ps1 +129 -39
  29. package/resources/claude-customizations/.claude/hooks/enforce-mermaid-validation.ps1 +7 -1
  30. package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +9 -3
  31. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +39 -51
  32. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
  33. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
  34. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
  35. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +17 -12
  36. package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +9 -3
  37. package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier.ps1 +65 -50
  38. package/resources/claude-customizations/.claude/hooks/enforce-parallel-drift-gate.ps1 +49 -42
  39. package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +54 -72
  40. package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +8 -2
  41. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-command-allowlist.ps1 +279 -0
  42. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +129 -75
  43. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.artifact-root.ps1 +233 -0
  44. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.docs-only.ps1 +224 -0
  45. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +69 -24
  46. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner-helpers.ps1 +29 -18
  47. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +10 -4
  48. package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +14 -5
  49. package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +8 -2
  50. package/resources/claude-customizations/.claude/hooks/feature-folder-resolution.ps1 +373 -0
  51. package/resources/claude-customizations/.claude/hooks/feature-review-coverage-thresholds.ps1 +85 -0
  52. package/resources/claude-customizations/.claude/hooks/hook-command-heredoc.ps1 +119 -0
  53. package/resources/claude-customizations/.claude/hooks/hook-command-invocation-operands.ps1 +198 -0
  54. package/resources/claude-customizations/.claude/hooks/hook-command-invocation.ps1 +254 -255
  55. package/resources/claude-customizations/.claude/hooks/hook-command-payload-powershell.ps1 +201 -0
  56. package/resources/claude-customizations/.claude/hooks/hook-command-payload.ps1 +484 -0
  57. package/resources/claude-customizations/.claude/hooks/hook-command-scanner.ps1 +27 -110
  58. package/resources/claude-customizations/.claude/hooks/hook-dependency-guard.ps1 +114 -0
  59. package/resources/claude-customizations/.claude/hooks/hook-docs-only-diff.ps1 +497 -0
  60. package/resources/claude-customizations/.claude/hooks/premortem-section.ps1 +149 -0
  61. package/resources/claude-customizations/.claude/hooks/subagentstop-envelope.ps1 +192 -0
  62. package/resources/claude-customizations/.claude/hooks/validate-bash.ps1 +9 -3
  63. package/resources/claude-customizations/.claude/hooks/validate-discovery-artifact-gate.ps1 +40 -42
  64. package/resources/claude-customizations/.claude/hooks/validate-executor-output.ps1 +267 -113
  65. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-metrics.ps1 +180 -0
  66. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-rules.ps1 +195 -0
  67. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage.ps1 +141 -395
  68. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-resolution.ps1 +311 -0
  69. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-routing.ps1 +170 -0
  70. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +115 -190
  71. package/resources/claude-customizations/.claude/hooks/validate-planner-output.ps1 +77 -33
  72. package/resources/claude-customizations/.claude/hooks/validate-pr-author-output.ps1 +39 -44
  73. package/resources/claude-customizations/.claude/hooks/validate-prd-feature-output.ps1 +43 -14
  74. package/resources/claude-customizations/.claude/hooks/validate-required-artifact-output.ps1 +66 -36
  75. package/resources/claude-customizations/.claude/hooks/validate-task-researcher-output.ps1 +52 -36
  76. package/resources/claude-customizations/.claude/lib/bash/mutate-parallel-run.sh +191 -0
  77. package/resources/claude-customizations/.claude/lib/bash/parallel-cohorts.sh +13 -3
  78. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-cli.sh +152 -0
  79. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-ops.sh +193 -0
  80. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation.sh +322 -0
  81. package/resources/claude-customizations/.claude/lib/bash/remove-parallel-item.sh +146 -0
  82. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +23 -33
  83. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusTokenShape.psm1 +86 -6
  84. package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-CiGateParser.ps1 +91 -19
  85. package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-MergeConfirmationParser.ps1 +203 -0
  86. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateEpicWaveBarrier.psm1 +346 -0
  87. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1 +24 -3
  88. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +79 -2
  89. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1 +3 -3
  90. package/resources/claude-customizations/.claude/rules/architecture-boundaries.md +5 -5
  91. package/resources/claude-customizations/.claude/rules/csharp.md +2 -2
  92. package/resources/claude-customizations/.claude/rules/general-code-change.md +1 -1
  93. package/resources/claude-customizations/.claude/rules/general-unit-test.md +3 -2
  94. package/resources/claude-customizations/.claude/rules/orchestrator-state.md +37 -4
  95. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +50 -5
  96. package/resources/claude-customizations/.claude/rules/plan-acceptance-gates.md +85 -10
  97. package/resources/claude-customizations/.claude/rules/quality-tiers.md +11 -7
  98. package/resources/claude-customizations/.claude/rules/shell.md +10 -8
  99. package/resources/claude-customizations/.claude/rules/typescript.md +1 -1
  100. package/resources/claude-customizations/.claude/settings.json +3 -4
  101. package/resources/claude-customizations/.claude/skills/acceptance-criteria-tracking/SKILL.md +1 -1
  102. package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +52 -16
  103. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +7 -3
  104. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_detached_lib.sh +1 -1
  105. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_enumerate_lib.sh +2 -0
  106. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_scan_helper.sh +2 -0
  107. package/resources/claude-customizations/.claude/skills/csharp-change-budget-router/SKILL.md +2 -2
  108. package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +45 -9
  109. package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +5 -3
  110. package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
  111. package/resources/claude-customizations/.claude/skills/execution-environment/SKILL.md +134 -0
  112. package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +1 -1
  113. package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +4 -3
  114. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +36 -15
  115. package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +52 -14
  116. package/resources/claude-customizations/.claude/skills/parallel-close/SKILL.md +22 -9
  117. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +108 -21
  118. package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +63 -7
  119. package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +35 -12
  120. package/resources/claude-customizations/.claude/skills/powershell-change-budget-router/SKILL.md +1 -1
  121. package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +3 -1
  122. package/resources/claude-customizations/.claude/skills/python-change-budget-router/SKILL.md +1 -1
  123. package/resources/claude-customizations/.claude/skills/quota-throttling/SKILL.md +1 -1
  124. package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +16 -8
  125. package/resources/claude-customizations/.claude/skills/research-issue/SKILL.md +6 -0
  126. package/resources/claude-customizations/pack-manifests/core.json +27 -0
  127. package/resources/codex-and-agents-customizations/.agents/skills/README.md +1 -0
  128. package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +1 -1
  129. package/resources/codex-and-agents-customizations/.agents/skills/architecture-boundaries/SKILL.md +5 -5
  130. package/resources/codex-and-agents-customizations/.agents/skills/atomic-executor/SKILL.md +19 -7
  131. package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +43 -4
  132. package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +18 -6
  133. package/resources/codex-and-agents-customizations/.agents/skills/csharp/SKILL.md +2 -2
  134. package/resources/codex-and-agents-customizations/.agents/skills/csharp-change-budget-router/SKILL.md +2 -2
  135. package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +2 -2
  136. package/resources/codex-and-agents-customizations/.agents/skills/epic-orchestrate/SKILL.md +16 -1
  137. package/resources/codex-and-agents-customizations/.agents/skills/epic-plan/SKILL.md +17 -1
  138. package/resources/codex-and-agents-customizations/.agents/skills/epic-run/SKILL.md +2 -1
  139. package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
  140. package/resources/codex-and-agents-customizations/.agents/skills/execution-environment/SKILL.md +134 -0
  141. package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +13 -5
  142. package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +1 -1
  143. package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +3 -2
  144. package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +27 -8
  145. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +1 -1
  146. package/resources/codex-and-agents-customizations/.agents/skills/powershell-change-budget-router/SKILL.md +1 -1
  147. package/resources/codex-and-agents-customizations/.agents/skills/python-change-budget-router/SKILL.md +1 -1
  148. package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +11 -7
  149. package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +7 -3
  150. package/resources/codex-and-agents-customizations/.agents/skills/research-issue/SKILL.md +6 -0
  151. package/resources/codex-and-agents-customizations/.agents/skills/translate-claude-to-codex/SKILL.md +1 -1
  152. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp/SKILL.md +2 -2
  153. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +2 -2
  154. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c1.toml +21 -6
  155. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +21 -6
  156. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +21 -6
  157. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +21 -6
  158. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +21 -6
  159. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +21 -6
  160. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +15 -2
  161. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +15 -2
  162. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +15 -2
  163. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +15 -2
  164. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +15 -2
  165. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +15 -2
  166. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planning.toml +1 -1
  167. package/resources/codex-and-agents-customizations/.codex/agents/csharp-atomic-planning.toml +1 -1
  168. package/resources/codex-and-agents-customizations/.codex/agents/epic-orchestrator.toml +4 -1
  169. package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +6 -6
  170. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c1.toml +7 -4
  171. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +7 -4
  172. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +7 -4
  173. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +7 -4
  174. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +7 -4
  175. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +7 -4
  176. package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-planning.toml +1 -1
  177. package/resources/codex-and-agents-customizations/.codex/agents/python-atomic-planning.toml +1 -1
  178. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c1.toml +6 -0
  179. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c2.toml +6 -0
  180. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c3-elevated.toml +6 -0
  181. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c3.toml +6 -0
  182. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c4.toml +6 -0
  183. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher.toml +6 -0
  184. package/resources/codex-and-agents-customizations/.codex/codex-web-setup.sh +318 -273
  185. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  186. package/resources/codex-and-agents-customizations/.codex/hooks/check-powershell-test-purity.ps1 +7 -1
  187. package/resources/codex-and-agents-customizations/.codex/hooks/check-python-test-purity.ps1 +7 -1
  188. package/resources/codex-and-agents-customizations/.codex/hooks/codex-epic-child-launch-attestation.ps1 +1 -1
  189. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
  190. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-codex-model-routing.ps1 +8 -2
  191. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +97 -44
  192. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-helpers.ps1 +105 -0
  193. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-child-worktree-binding.ps1 +7 -1
  194. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate-command.ps1 +190 -0
  195. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +54 -43
  196. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +6 -0
  197. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-root-invocation.ps1 +7 -1
  198. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-wave-barrier.ps1 +7 -1
  199. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-worktree-removal-gate.ps1 +54 -41
  200. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-evidence-locations.ps1 +7 -1
  201. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +35 -14
  202. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
  203. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
  204. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
  205. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +24 -11
  206. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +8 -2
  207. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +13 -4
  208. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +8 -2
  209. package/resources/codex-and-agents-customizations/.codex/hooks/feature-folder-resolution.ps1 +373 -0
  210. package/resources/codex-and-agents-customizations/.codex/hooks/feature-review-coverage-thresholds.ps1 +85 -0
  211. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-heredoc.ps1 +119 -0
  212. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation-operands.ps1 +198 -0
  213. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation.ps1 +254 -255
  214. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload-powershell.ps1 +201 -0
  215. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload.ps1 +484 -0
  216. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-scanner.ps1 +27 -110
  217. package/resources/codex-and-agents-customizations/.codex/hooks/hook-dependency-guard.ps1 +114 -0
  218. package/resources/codex-and-agents-customizations/.codex/hooks/hook-docs-only-diff.ps1 +497 -0
  219. package/resources/codex-and-agents-customizations/.codex/hooks/validate-bash.ps1 +8 -2
  220. package/resources/codex-and-agents-customizations/.codex/hooks/validate-codex-subagent-routing.ps1 +7 -1
  221. package/resources/codex-and-agents-customizations/.codex/hooks/validate-feature-review-coverage.ps1 +24 -8
  222. package/resources/codex-and-agents-customizations/.codex/prompts/feature-review-remediate.md +6 -4
  223. package/resources/codex-and-agents-customizations/pack-manifests/core.json +11 -0
  224. package/resources/customizations/.github/agents/atomic_executor.agent.md +21 -11
  225. package/resources/customizations/.github/agents/atomic_planning.agent.md +8 -3
  226. package/resources/customizations/.github/agents/csharp-atomic-executor.agent.md +13 -9
  227. package/resources/customizations/.github/agents/csharp-atomic-planning.agent.md +2 -2
  228. package/resources/customizations/.github/agents/csharp-orchestrator.agent.md +2 -2
  229. package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +5 -5
  230. package/resources/customizations/.github/agents/orchestrator.agent.md +8 -5
  231. package/resources/customizations/.github/agents/powershell-atomic-executor.agent.md +13 -9
  232. package/resources/customizations/.github/agents/powershell-atomic-planning.agent.md +2 -2
  233. package/resources/customizations/.github/agents/powershell-orchestrator.agent.md +2 -2
  234. package/resources/customizations/.github/agents/powershell-typed-engineer.agent.md +3 -3
  235. package/resources/customizations/.github/agents/python-atomic-executor.agent.md +13 -9
  236. package/resources/customizations/.github/agents/python-atomic-planning.agent.md +2 -2
  237. package/resources/customizations/.github/agents/python-orchestrator.agent.md +2 -2
  238. package/resources/customizations/.github/agents/task-researcher.agent.md +6 -0
  239. package/resources/customizations/.github/instructions/csharp-code-change.instructions.md +4 -4
  240. package/resources/customizations/.github/instructions/csharp-unit-test.instructions.md +2 -2
  241. package/resources/customizations/.github/prompts/generate-atomic-plan.prompt.md +16 -10
  242. package/resources/customizations/.github/prompts/orchestrate-csharp-work.prompt.md +2 -2
  243. package/resources/customizations/.github/prompts/orchestrate-powershell-work.prompt.md +1 -1
  244. package/resources/customizations/.github/prompts/orchestrate-python-work.prompt.md +1 -1
  245. package/resources/customizations/.github/prompts/orchestrate-work.prompt.md +2 -2
  246. package/resources/customizations/.github/skills/acceptance-criteria-tracking/SKILL.md +1 -1
  247. package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +43 -4
  248. package/resources/customizations/.github/skills/csharp-change-budget-router/SKILL.md +1 -1
  249. package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
  250. package/resources/customizations/.github/skills/execution-environment/SKILL.md +134 -0
  251. package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +1 -1
  252. package/resources/customizations/.github/skills/powershell-change-budget-router/SKILL.md +1 -1
  253. package/resources/customizations/.github/skills/remediation-handoff-atomic-planner/SKILL.md +7 -3
  254. package/resources/feature-templates/bug/plan.yyyy-MM-ddTHH-mm.md +10 -0
  255. package/resources/feature-templates/feature/plan.yyyy-MM-ddTHH-mm.md +11 -0
  256. package/resources/feature-templates/refactor/plan.yyyy-MM-ddTHH-mm.md +11 -0
  257. package/resources/powershell/PoshQC/PoshQC.Coverage.psm1 +314 -0
  258. package/resources/powershell/PoshQC/PoshQC.CoverageMerge.psm1 +163 -0
  259. package/resources/powershell/PoshQC/PoshQC.Shard.psm1 +296 -0
  260. package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +48 -32
  261. package/resources/powershell/PoshQC/PoshQC.psd1 +7 -1
  262. package/resources/powershell/PoshQC/PoshQC.psm1 +18 -5
  263. package/resources/powershell/PoshQC/README.md +21 -3
  264. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +5 -319
@@ -17,14 +17,19 @@
17
17
 
18
18
  Both helpers read the script-scoped configuration the parent hook declares and call the
19
19
  parent hook's injectable read seams, so this file is only ever dot-sourced from that hook
20
- and never invoked on its own. $script:PrContextArtifactPath is a process-directory-relative
21
- artifact path and stays one. $script:OrchestratorStateCheckpointPath is different after
20
+ and never invoked on its own. $script:PrContextArtifactPath is the repository-relative
21
+ literal used in messages and as the composition input; every artifact read is composed
22
+ beneath the root Resolve-PrAuthorArtifactRoot selects (issue #850).
23
+ $script:OrchestratorStateCheckpointPath is different after
22
24
  issue #673: the parent declares it null, and Get-PrAuthorBypassReason assigns it the
23
25
  absolute checkpoint path of the worktree identity resolution selected, so every later
24
26
  reader receives one resolved path rather than deriving its own.
25
27
 
26
28
  .NOTES
27
- Compatible with PowerShell 7+. No external module dependencies.
29
+ Compatible with PowerShell 7+. Dependencies are in-repository only: the command parsers
30
+ and worktree-resolution modules loaded below, and the artifact-root and docs-only siblings.
31
+ No PowerShell Gallery module is required.
32
+ The docs-only sibling runs the git executable at call time (issue #901).
28
33
  #>
29
34
  [CmdletBinding()]
30
35
  param()
@@ -47,6 +52,10 @@ Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeItemR
47
52
  # Epic scope (issue #663): the epic integration pull request is gated against the epic checkpoint.
48
53
  Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/EpicScopeResolution.psm1') -Force -ErrorAction Stop
49
54
  Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/EpicScopeReadiness.psm1') -Force -ErrorAction Stop
55
+ # Artifact-root resolution and the Check 1 body-path binding (issue #850).
56
+ . (Join-Path $PSScriptRoot 'enforce-pr-author-skill.artifact-root.ps1')
57
+ # Docs-only exemption from the orchestrator-state preflight (issue #901).
58
+ . (Join-Path $PSScriptRoot 'enforce-pr-author-skill.docs-only.ps1')
50
59
 
51
60
  function Resolve-PrAuthorWorktreeTarget {
52
61
  <#
@@ -98,7 +107,9 @@ function Get-PrAuthorTargetCheckpointResolution {
98
107
  refuses to answer rather than answering from unrelated state. Neither reason-code
99
108
  literal appears in this file; both come from the accessors named above.
100
109
  .OUTPUTS
101
- System.Collections.Specialized.OrderedDictionary with CheckpointPath and Reason.
110
+ System.Collections.Specialized.OrderedDictionary with CheckpointPath, Reason,
111
+ WorktreeRoot (the resolved root, or $null when unresolved), and Status (the target's
112
+ status), so the artifact root is taken from the same resolution (issue #850).
102
113
  #>
103
114
  [CmdletBinding()]
104
115
  [OutputType([System.Collections.Specialized.OrderedDictionary])]
@@ -111,15 +122,51 @@ function Get-PrAuthorTargetCheckpointResolution {
111
122
 
112
123
  switch ($target.Status) {
113
124
  { $_ -in @('SessionRoot', 'OtherWorktree') } {
114
- return [ordered]@{ CheckpointPath = (Get-WorktreeItemCheckpointPath -WorktreeRoot $target.WorktreeRoot); Reason = $null }
125
+ return [ordered]@{ CheckpointPath = (Get-WorktreeItemCheckpointPath -WorktreeRoot $target.WorktreeRoot); Reason = $null; WorktreeRoot = $target.WorktreeRoot; Status = $target.Status }
115
126
  }
116
127
  default {
117
128
  $reason = "$($target.ReasonCode): $($target.Detail) The pr-author gate will not validate this call against the session root's checkpoint, because that checkpoint may belong to a different item. Pass --head <branch> on the gh pr create command so the call names its own target."
118
- return [ordered]@{ CheckpointPath = $null; Reason = $reason }
129
+ return [ordered]@{ CheckpointPath = $null; Reason = $reason; WorktreeRoot = $null; Status = $target.Status }
119
130
  }
120
131
  }
121
132
  }
122
133
 
134
+ function Get-PrAuthorBodyFileValue {
135
+ <#
136
+ .SYNOPSIS
137
+ Return the normalized --body-file value of the matched gh pr create or gh pr edit, or $null.
138
+ .DESCRIPTION
139
+ Reads the flag value from the first Structural gh pr create match, else gh pr edit.
140
+ Backslashes become forward slashes and one leading './' is removed (issue #824). A
141
+ rooted value is returned as written, with separators normalized, and is bound to the
142
+ resolved worktree by Get-PrAuthorBodyPathBindingReason (issue #850).
143
+ .PARAMETER CommandText
144
+ The Bash command text under evaluation.
145
+ .OUTPUTS
146
+ System.String or $null
147
+ #>
148
+ [CmdletBinding()]
149
+ [OutputType([string])]
150
+ param(
151
+ [Parameter(Mandatory)]
152
+ [string] $CommandText
153
+ )
154
+
155
+ $subcommandPath = @('pr', 'edit')
156
+ if (Test-CommandLineInvocation -CommandText $CommandText -CommandWord 'gh' -SubcommandPath @('pr', 'create')) {
157
+ $subcommandPath = @('pr', 'create')
158
+ }
159
+ $value = Get-CommandLineFlagValue -CommandText $CommandText -CommandWord 'gh' -SubcommandPath $subcommandPath -FlagName '--body-file'
160
+ if ($null -eq $value) {
161
+ return $null
162
+ }
163
+ $value = $value.Replace('\', '/')
164
+ if ($value.StartsWith('./')) {
165
+ $value = $value.Substring(2)
166
+ }
167
+ return $value
168
+ }
169
+
123
170
  function Test-PrAuthorReceiptVerification {
124
171
  <#
125
172
  .SYNOPSIS
@@ -127,8 +174,9 @@ function Test-PrAuthorReceiptVerification {
127
174
  .DESCRIPTION
128
175
  Runs the six ordered receipt checks on the --body-file-with-context path. Each check is its
129
176
  own short-circuiting branch; the first failure returns its reason code:
130
- 1. PR_BODY_PATH_NONCANONICAL - --body-file path does not match the canonical
131
- artifacts/pr_body_<N>.md pattern (case-sensitive).
177
+ 1. PR_BODY_PATH_NONCANONICAL - --body-file path is not the canonical
178
+ artifacts/pr_body_<N>.md beneath the artifact
179
+ root (relative only for the session worktree).
132
180
  2. PR_AUTHOR_RECEIPT_MISSING - sibling artifacts/pr_body_<N>.receipt.json absent.
133
181
  3. PR_AUTHOR_RECEIPT_NUMBER_MISMATCH- receipt.number (integer) != <N> from the path.
134
182
  4. PR_AUTHOR_RECEIPT_HASH_MISMATCH - inline SHA-256 (lowercase hex) of the body bytes
@@ -139,9 +187,10 @@ function Test-PrAuthorReceiptVerification {
139
187
  matching --base <epic_context.integration_branch>.
140
188
  Returns $null when all six checks pass (allow). All disk access flows through the four
141
189
  injectable seams (Get-PrBodyFileBytes, Get-PrAuthorReceiptContent,
142
- Get-PrContextSummaryLastWriteUtc, Get-PrAuthorCheckpointContent); SHA-256 is computed
143
- inline. This is a policy-level integrity check, not a cryptographic control: any actor
144
- with Write access to artifacts/ can replace the body file and the receipt together.
190
+ Get-PrContextSummaryLastWriteUtc, Get-PrAuthorCheckpointContent), each given an absolute
191
+ path beneath the artifact root (issue #850); SHA-256 is computed inline. This is a
192
+ policy-level integrity check, not a cryptographic control: any actor with Write access
193
+ to artifacts/ can replace the body file and the receipt together.
145
194
  .PARAMETER CommandText
146
195
  The Bash command text containing the --body-file argument.
147
196
  .PARAMETER CheckpointPath
@@ -150,6 +199,10 @@ function Test-PrAuthorReceiptVerification {
150
199
  .PARAMETER EpicScope
151
200
  Optional. The epic-scope result Get-PrAuthorBypassReason resolved (issue #663), passed
152
201
  through to check 6 so the call is resolved once; $null outside epic scope.
202
+ .PARAMETER ArtifactRoot
203
+ The resolved worktree root the body, receipt, and summary are read beneath.
204
+ .PARAMETER RelativeBodyAllowed
205
+ Whether a relative --body-file is read from the resolved worktree (the session worktree).
153
206
  .OUTPUTS
154
207
  System.String or $null
155
208
  #>
@@ -163,18 +216,25 @@ function Test-PrAuthorReceiptVerification {
163
216
  [string] $CheckpointPath,
164
217
 
165
218
  [AllowNull()]
166
- [object] $EpicScope
219
+ [object] $EpicScope,
220
+
221
+ [Parameter(Mandatory)]
222
+ [string] $ArtifactRoot,
223
+
224
+ [bool] $RelativeBodyAllowed
167
225
  )
168
226
 
169
- # Check 1: the --body-file argument must match the canonical artifacts/pr_body_<N>.md pattern.
170
- # The match is case-sensitive (-cmatch) so a non-canonical path is rejected before any read.
171
- if ($CommandText -cnotmatch '--body-file\s+artifacts/pr_body_(\d+)\.md\b') {
172
- return "PR_BODY_PATH_NONCANONICAL: ``--body-file`` must reference a canonical ``artifacts/pr_body_<N>.md`` file produced by the pr-author skill. The path supplied does not match ``artifacts/pr_body_<N>.md``."
227
+ # Check 1: the --body-file value must be the canonical artifacts/pr_body_<N>.md beneath the
228
+ # artifact root (issue #850); C1a's structural read and spelling normalization are reused.
229
+ $binding = Get-PrAuthorBodyPathBindingReason -CommandText $CommandText -ArtifactRoot $ArtifactRoot -RelativeBodyAllowed $RelativeBodyAllowed
230
+ if ($binding.Reason) {
231
+ return $binding.Reason
173
232
  }
174
233
 
175
- $bodyNumber = [int]$Matches[1]
176
- $bodyFilePath = "artifacts/pr_body_$bodyNumber.md"
177
- $receiptFilePath = "artifacts/pr_body_$bodyNumber.receipt.json"
234
+ $bodyNumber = $binding.BodyNumber
235
+ $bodyFilePath = Join-WorktreeResolutionPath -WorktreeRoot $ArtifactRoot -RepoRelativePath "artifacts/pr_body_$bodyNumber.md"
236
+ $receiptFilePath = Join-WorktreeResolutionPath -WorktreeRoot $ArtifactRoot -RepoRelativePath "artifacts/pr_body_$bodyNumber.receipt.json"
237
+ $summaryPath = Join-WorktreeResolutionPath -WorktreeRoot $ArtifactRoot -RepoRelativePath $script:PrContextArtifactPath
178
238
 
179
239
  # Check 2: the sibling receipt file must exist (read via the injectable seam).
180
240
  $receiptRaw = Get-PrAuthorReceiptContent -ReceiptFilePath $receiptFilePath
@@ -228,7 +288,7 @@ function Test-PrAuthorReceiptVerification {
228
288
  return "PR_AUTHOR_RECEIPT_STALE: ``$receiptFilePath`` has a missing or unparseable ``created_at``. The pr-author agent must record a UTC ISO-8601 ``created_at`` strictly newer than ``$script:PrContextArtifactPath``."
229
289
  }
230
290
 
231
- $contextLastWrite = Get-PrContextSummaryLastWriteUtc
291
+ $contextLastWrite = Get-PrContextSummaryLastWriteUtc -Path $summaryPath
232
292
  if (($null -eq $contextLastWrite) -or ($createdAt -le $contextLastWrite)) {
233
293
  return "PR_AUTHOR_RECEIPT_STALE: ``$receiptFilePath`` ``created_at`` is not strictly newer than the last-write time of ``$script:PrContextArtifactPath``. The pr-author agent must regenerate the body and receipt after refreshing the PR context."
234
294
  }
@@ -248,17 +308,16 @@ function Get-PrAuthorBypassReason {
248
308
  Inspect the command text and return a block reason string, or $null when the command is allowed.
249
309
  .DESCRIPTION
250
310
  Returns PR_AUTHOR_SKILL_BLOCKED when gh pr create or gh pr edit is run with --body (inline,
251
- no --body-file), or when gh pr create is run with no body flag at all. Returns
252
- PR_CONTEXT_MISSING when --body-file is present but the context artifact does not exist on
253
- disk. When --body-file is present and the context artifact exists, verifies the SHA-256
254
- receipt via Test-PrAuthorReceiptVerification and returns its block reason
255
- (PR_BODY_PATH_NONCANONICAL / PR_AUTHOR_RECEIPT_*) when verification fails. Returns $null for
256
- all allowed patterns. Cases A, B, and C are evaluated first and unchanged; receipt
257
- verification only extends the previously-allowed --body-file-with-context path.
311
+ no --body-file), or when gh pr create is run with no body flag at all; these cases and the
312
+ gh pr edit no-body allow perform no resolution. For a --body-file command the artifact root
313
+ is resolved once (issue #850), and the deny precedence is: the resolution reason
314
+ (no-target or ambiguity code, or an underivable epic worktree), PR_CONTEXT_MISSING when the
315
+ context summary is absent beneath the resolved worktree, ORCHESTRATOR_STATE_PREFLIGHT_FAILED
316
+ unless Get-PrAuthorDocsOnlyExemption exempts a docs-only gh pr create (issue #901), then
317
+ receipt checks 1-6 via Test-PrAuthorReceiptVerification. Returns $null for all allowed
318
+ patterns.
258
319
  .PARAMETER CommandText
259
320
  The Bash command text extracted from the envelope's tool_input.
260
- .PARAMETER ContextExists
261
- Whether artifacts/pr_context.summary.txt currently exists on disk.
262
321
  .OUTPUTS
263
322
  System.String or $null
264
323
  #>
@@ -266,10 +325,7 @@ function Get-PrAuthorBypassReason {
266
325
  [OutputType([string])]
267
326
  param(
268
327
  [Parameter(Mandatory)]
269
- [string] $CommandText,
270
-
271
- [Parameter(Mandatory)]
272
- [bool] $ContextExists
328
+ [string] $CommandText
273
329
  )
274
330
 
275
331
  # Only act on gh pr create or gh pr edit subcommands. The test is structural: a segment
@@ -290,22 +346,21 @@ function Get-PrAuthorBypassReason {
290
346
  $hasBodyFile = Test-CommandLineFlag -CommandText $CommandText -CommandWord 'gh' -SubcommandPath $subcommandPath -FlagName '--body-file'
291
347
  $hasInlineBody = Test-CommandLineFlag -CommandText $CommandText -CommandWord 'gh' -SubcommandPath $subcommandPath -FlagName '--body'
292
348
 
293
- # Raw-scan fallback for a wrapper-led, live-substitution, or unbalanced segment. A
294
- # wrapper's quoted argument collapses into ONE token, so both flags read absent there and
295
- # the gh pr edit no-body branch below allowed bash -c "gh pr edit 42 --body 'x'". The
296
- # --body-file test runs first and wins, so a --body-file carried inside a wrapper is never
297
- # misread as an inline body. That ordering restores the pre-parser routing, in which one
298
- # whole-text --body-file match set $hasBodyFile for exactly this input.
349
+ # Raw-scan fallback (issue #824). Wrapper payloads are now read structurally, so the flags
350
+ # above already come from a payload's own tokens. The raw text is scanned only when the
351
+ # invocation could not be read structurally at all: no Structural match exists for the
352
+ # subcommand path and at least one Indeterminate match does. The --body-file test runs
353
+ # first and wins, so a --body-file is never misread as an inline body.
299
354
  if (-not $hasBodyFile -and -not $hasInlineBody) {
300
- $comparison = [System.StringComparison]::OrdinalIgnoreCase
301
- foreach ($segment in @(Read-CommandLineSegment -CommandText $CommandText)) {
302
- if (-not (Test-CommandLineSegmentRawScan -Segment $segment)) {
303
- continue
304
- }
305
- if ($segment.ScanText.IndexOf('--body-file', $comparison) -ge 0) {
306
- $hasBodyFile = $true
307
- } elseif ($segment.ScanText.IndexOf('--body', $comparison) -ge 0) {
308
- $hasInlineBody = $true
355
+ $matchList = @(Get-CommandLineInvocation -CommandText $CommandText -CommandWord 'gh' -SubcommandPath $subcommandPath)
356
+ if (@($matchList | Where-Object { $_.Status -eq 'Structural' }).Count -eq 0) {
357
+ $comparison = [System.StringComparison]::OrdinalIgnoreCase
358
+ foreach ($match in @($matchList | Where-Object { $_.Status -eq 'Indeterminate' })) {
359
+ if ($match.Segment.RawText.IndexOf('--body-file', $comparison) -ge 0) {
360
+ $hasBodyFile = $true
361
+ } elseif ($match.Segment.RawText.IndexOf('--body', $comparison) -ge 0) {
362
+ $hasInlineBody = $true
363
+ }
309
364
  }
310
365
  }
311
366
  }
@@ -330,36 +385,34 @@ function Get-PrAuthorBypassReason {
330
385
  }
331
386
  }
332
387
 
333
- # Case C: --body-file present but context artifact is absent.
334
- if ($hasBodyFile -and -not $ContextExists) {
335
- return "PR_CONTEXT_MISSING: ``$script:PrContextArtifactPath`` is absent. Run ``mcp__drm-copilot__collect_pr_context`` before creating or editing the PR body."
388
+ # Issue #850: the worktree whose artifacts are read is resolved once, before Case C. Epic
389
+ # scope (issue #663) selects the epic checkpoint's worktree and reads no per-feature
390
+ # checkpoint; otherwise identity resolution selects the item worktree, and an unresolved
391
+ # target denies here, ahead of PR_CONTEXT_MISSING.
392
+ $artifact = Resolve-PrAuthorArtifactRoot -CommandText $CommandText
393
+ if ($artifact.Reason) {
394
+ return $artifact.Reason
336
395
  }
337
396
 
338
- # Epic scope (issue #663): when the call's --head equals the epic checkpoint's
339
- # integration_branch, the integration pull request is gated by the epic PR-creation
340
- # readiness predicate on artifacts/orchestration/epic-orchestrator-state.json, and no
341
- # per-feature checkpoint is resolved or read. Resolved once; check 6 reuses the result.
342
- $epicScope = $null
343
- if ($hasBodyFile -and $ContextExists) {
344
- $epicScope = Resolve-EpicScopeCheckpoint -Text $CommandText -SessionRoot (Get-Location).Path
397
+ # Case C: --body-file present but the context artifact is absent beneath the resolved worktree.
398
+ $summaryPath = Join-WorktreeResolutionPath -WorktreeRoot $artifact.ArtifactRoot -RepoRelativePath $script:PrContextArtifactPath
399
+ if (-not (Get-PrContextArtifactExistence -Path $summaryPath)) {
400
+ return "PR_CONTEXT_MISSING: '$script:PrContextArtifactPath' in worktree '$($artifact.ArtifactRoot)' is absent. Run ``mcp__drm-copilot__collect_pr_context`` in that worktree before creating or editing the PR body."
345
401
  }
346
402
 
347
403
  # Orchestrator-state preflight: runs inside this same PreToolUse hook (so it cannot be
348
- # bypassed by invoking gh pr create/edit directly) before receipt verification.
349
- if ($null -ne $epicScope -and $epicScope.IsEpicScope) {
404
+ # bypassed by invoking gh pr create/edit directly) before receipt verification. The
405
+ # checkpoint path is assigned once here and read by every later consumer, so the
406
+ # preflight, the receipt verifier, and the epic base-branch check gate one item.
407
+ if ($null -ne $artifact.EpicScope) {
408
+ $epicScope = $artifact.EpicScope
350
409
  $failure = Get-EpicPrCreationReadinessFailure -Checkpoint $epicScope.Checkpoint -HeadBranch $epicScope.Branch
351
410
  if ($failure) {
352
411
  return "ORCHESTRATOR_STATE_PREFLIGHT_FAILED: this epic-scope pull request was evaluated against $($epicScope.CheckpointPath), and the failed readiness predicate is '$failure'."
353
412
  }
354
- $script:OrchestratorStateCheckpointPath = $epicScope.CheckpointPath
355
- } elseif ($hasBodyFile -and $ContextExists) {
356
- $resolution = Get-PrAuthorTargetCheckpointResolution -CommandText $CommandText
357
- if ($resolution.Reason) {
358
- return $resolution.Reason
359
- }
360
- # Assigned once here and read by every later consumer, so the preflight, the receipt
361
- # verifier, and the epic base-branch check cannot disagree about which item is gated.
362
- $script:OrchestratorStateCheckpointPath = $resolution.CheckpointPath
413
+ $script:OrchestratorStateCheckpointPath = $artifact.CheckpointPath
414
+ } else {
415
+ $script:OrchestratorStateCheckpointPath = $artifact.CheckpointPath
363
416
  $checkpointPath = $script:OrchestratorStateCheckpointPath
364
417
  $preflightResult = Invoke-OrchestratorStatePreflight -CheckpointPath $checkpointPath
365
418
  if ($preflightResult.HasErrors) {
@@ -368,16 +421,17 @@ function Get-PrAuthorBypassReason {
368
421
  } else {
369
422
  $preflightResult.ErrorText
370
423
  }
371
- return "ORCHESTRATOR_STATE_PREFLIGHT_FAILED: $preflightSummary"
424
+ $exemption = Get-PrAuthorDocsOnlyExemption -CommandText $CommandText -WorktreeRoot $artifact.ArtifactRoot -CheckpointPath $checkpointPath
425
+ if (-not $exemption.Exempt) {
426
+ return "ORCHESTRATOR_STATE_PREFLIGHT_FAILED: $preflightSummary (docs-only exemption not applied: $($exemption.Reason))"
427
+ }
372
428
  }
373
429
  }
374
430
 
375
431
  # Receipt verification: extends, and does not replace, the previously-allowed path.
376
- if ($hasBodyFile -and $ContextExists) {
377
- $receiptReason = Test-PrAuthorReceiptVerification -CommandText $CommandText -CheckpointPath $script:OrchestratorStateCheckpointPath -EpicScope $epicScope
378
- if ($receiptReason) {
379
- return $receiptReason
380
- }
432
+ $receiptReason = Test-PrAuthorReceiptVerification -CommandText $CommandText -CheckpointPath $script:OrchestratorStateCheckpointPath -EpicScope $artifact.EpicScope -ArtifactRoot $artifact.ArtifactRoot -RelativeBodyAllowed $artifact.RelativeBodyAllowed
433
+ if ($receiptReason) {
434
+ return $receiptReason
381
435
  }
382
436
 
383
437
  return $null
@@ -0,0 +1,233 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Artifact-root resolution and the Check 1 body-path binding for enforce-pr-author-skill.ps1 (issue #850).
4
+
5
+ .DESCRIPTION
6
+ Dot-sourced by enforce-pr-author-skill-helpers.ps1 after its module imports. Holds:
7
+
8
+ - Resolve-PrAuthorArtifactRoot, which resolves once per call the worktree whose
9
+ artifacts/ tree the gate reads: the epic checkpoint's worktree under epic scope, or
10
+ the item worktree identity resolution selects. The PR context summary, the body, and
11
+ the receipt are composed beneath that root, so a gate running in the session worktree
12
+ no longer reads another item's artifacts from its own process directory.
13
+ - Get-PrAuthorBodyPathBindingReason, the Check 1 binding: a relative canonical
14
+ --body-file value is accepted only when the resolved worktree is the session worktree,
15
+ because gh reads a relative path from its own process directory; an absolute value is
16
+ accepted only when it equals the canonical path beneath the resolved root.
17
+ - Test-PrAuthorBodyPathEqual and Get-PrAuthorEpicArtifactRoot, the pure path helpers.
18
+ - ConvertTo-PrAuthorBodyNumber, the strict body-number parse (issue #901, #850 note L19).
19
+
20
+ .NOTES
21
+ PowerShell 7+. Depends, at call time, on Get-PrAuthorBodyFileValue and
22
+ Get-PrAuthorTargetCheckpointResolution from the helpers file and on the worktree-resolution
23
+ modules it imports. It reads no file. Mirrored byte-identically under
24
+ extensions/drm-copilot/resources/claude-customizations/.
25
+ #>
26
+ [CmdletBinding()]
27
+ param()
28
+
29
+ function Test-PrAuthorBodyPathEqual {
30
+ <#
31
+ .SYNOPSIS
32
+ Compare two paths: case-insensitive for drive-letter and UNC paths, case-sensitive otherwise.
33
+ .PARAMETER Left
34
+ The first path, in either separator style.
35
+ .PARAMETER Right
36
+ The second path, in either separator style.
37
+ .OUTPUTS
38
+ System.Boolean
39
+ #>
40
+ [CmdletBinding()]
41
+ [OutputType([bool])]
42
+ param(
43
+ [AllowNull()][AllowEmptyString()][string] $Left,
44
+ [AllowNull()][AllowEmptyString()][string] $Right
45
+ )
46
+
47
+ $a = ConvertTo-WorktreeResolutionNormalizedPath -Path $Left
48
+ $b = ConvertTo-WorktreeResolutionNormalizedPath -Path $Right
49
+ if ($null -eq $a -or $null -eq $b) {
50
+ return $false
51
+ }
52
+ $comparison = if ($a -match '^([A-Za-z]:|//)' -or $b -match '^([A-Za-z]:|//)') {
53
+ [System.StringComparison]::OrdinalIgnoreCase
54
+ }
55
+ else {
56
+ [System.StringComparison]::Ordinal
57
+ }
58
+ return [string]::Equals($a, $b, $comparison)
59
+ }
60
+
61
+ function Get-PrAuthorEpicArtifactRoot {
62
+ <#
63
+ .SYNOPSIS
64
+ Return the worktree root that holds an epic checkpoint path, or $null.
65
+ .PARAMETER CheckpointPath
66
+ The absolute epic checkpoint path the epic-scope resolver composed.
67
+ .OUTPUTS
68
+ System.String or $null
69
+ #>
70
+ [CmdletBinding()]
71
+ [OutputType([string])]
72
+ param(
73
+ [AllowNull()][AllowEmptyString()][string] $CheckpointPath
74
+ )
75
+
76
+ $normalized = ConvertTo-WorktreeResolutionNormalizedPath -Path $CheckpointPath
77
+ $suffix = '/' + (Get-EpicScopeCheckpointRelativePath)
78
+ if ($null -eq $normalized -or -not $normalized.EndsWith($suffix, [System.StringComparison]::Ordinal)) {
79
+ return $null
80
+ }
81
+ $root = $normalized.Substring(0, $normalized.Length - $suffix.Length)
82
+ if ($root.Length -eq 0) {
83
+ return $null
84
+ }
85
+ return $root
86
+ }
87
+
88
+ function Resolve-PrAuthorArtifactRoot {
89
+ <#
90
+ .SYNOPSIS
91
+ Resolve, once per call, the worktree whose artifacts the gate reads (issue #850).
92
+ .DESCRIPTION
93
+ Epic scope: the root is the worktree holding the epic checkpoint, and a relative body
94
+ path is allowed only when that root is the session worktree. Otherwise the item target
95
+ is resolved by identity; an unresolved target returns its resolution reason unchanged,
96
+ and a relative body path is allowed only for a SessionRoot target.
97
+ .PARAMETER CommandText
98
+ The Bash command text under evaluation.
99
+ .OUTPUTS
100
+ System.Collections.Specialized.OrderedDictionary with Reason, ArtifactRoot,
101
+ CheckpointPath, EpicScope, Status, and RelativeBodyAllowed.
102
+ #>
103
+ [CmdletBinding()]
104
+ [OutputType([System.Collections.Specialized.OrderedDictionary])]
105
+ param(
106
+ [Parameter(Mandatory)]
107
+ [string] $CommandText
108
+ )
109
+
110
+ $epicScope = Resolve-EpicScopeCheckpoint -Text $CommandText -SessionRoot (Get-Location).Path
111
+ if ($null -ne $epicScope -and $epicScope.IsEpicScope) {
112
+ $root = Get-PrAuthorEpicArtifactRoot -CheckpointPath $epicScope.CheckpointPath
113
+ $reason = $null
114
+ $relativeAllowed = $false
115
+ if ($null -eq $root) {
116
+ $reason = "ORCHESTRATOR_STATE_PREFLIGHT_FAILED: the epic checkpoint path '$($epicScope.CheckpointPath)' does not end with '$(Get-EpicScopeCheckpointRelativePath)', so the worktree that holds it cannot be derived."
117
+ }
118
+ else {
119
+ $relativeAllowed = Test-PrAuthorBodyPathEqual -Left $root -Right $epicScope.WorktreeRoot
120
+ }
121
+ return [ordered]@{
122
+ Reason = $reason
123
+ ArtifactRoot = $root
124
+ CheckpointPath = $epicScope.CheckpointPath
125
+ EpicScope = $epicScope
126
+ Status = 'Epic'
127
+ RelativeBodyAllowed = $relativeAllowed
128
+ }
129
+ }
130
+
131
+ $resolution = Get-PrAuthorTargetCheckpointResolution -CommandText $CommandText
132
+ $resolved = -not $resolution.Reason
133
+ return [ordered]@{
134
+ Reason = $resolution.Reason
135
+ ArtifactRoot = if ($resolved) { ConvertTo-WorktreeResolutionNormalizedPath -Path $resolution.WorktreeRoot } else { $null }
136
+ CheckpointPath = $resolution.CheckpointPath
137
+ EpicScope = $null
138
+ Status = $resolution.Status
139
+ RelativeBodyAllowed = ($resolved -and $resolution.Status -eq 'SessionRoot')
140
+ }
141
+ }
142
+
143
+ function ConvertTo-PrAuthorBodyNumber {
144
+ <#
145
+ .SYNOPSIS
146
+ Parse the <N> of artifacts/pr_body_<N>.md, or return $null.
147
+ .DESCRIPTION
148
+ Accepts only ASCII digits that parse as an Int32 and whose canonical invariant spelling
149
+ equals the input, so a value above Int32.MaxValue, a leading zero, and a non-ASCII
150
+ decimal digit all return $null instead of throwing or naming a different file.
151
+ .PARAMETER Digits
152
+ The digit run captured from the body-file name.
153
+ .OUTPUTS
154
+ System.Int32 or $null
155
+ #>
156
+ [CmdletBinding()]
157
+ [OutputType([int])]
158
+ param(
159
+ [AllowNull()][AllowEmptyString()][string] $Digits
160
+ )
161
+
162
+ $number = 0
163
+ $parsed = [int]::TryParse($Digits, [System.Globalization.NumberStyles]::None, [System.Globalization.CultureInfo]::InvariantCulture, [ref] $number)
164
+ if (-not $parsed -or $number.ToString([System.Globalization.CultureInfo]::InvariantCulture) -cne $Digits) {
165
+ return $null
166
+ }
167
+ return $number
168
+ }
169
+
170
+ function Get-PrAuthorBodyPathBindingReason {
171
+ <#
172
+ .SYNOPSIS
173
+ Check 1: bind the --body-file value to the canonical body path beneath the artifact root.
174
+ .DESCRIPTION
175
+ Reads the value once through Get-PrAuthorBodyFileValue. A relative
176
+ artifacts/pr_body_<N>.md (case-sensitive) is accepted only when RelativeBodyAllowed is
177
+ true; otherwise the deny names the absolute canonical path to pass instead. An absolute
178
+ value is accepted only when it equals that canonical path under Test-PrAuthorBodyPathEqual.
179
+ Any other value, including no readable value, denies with the generic text.
180
+ .PARAMETER CommandText
181
+ The Bash command text under evaluation.
182
+ .PARAMETER ArtifactRoot
183
+ The resolved worktree root the artifacts are read beneath.
184
+ .PARAMETER RelativeBodyAllowed
185
+ Whether a relative body path is read from the resolved worktree.
186
+ .OUTPUTS
187
+ System.Collections.Specialized.OrderedDictionary with Reason and BodyNumber.
188
+ #>
189
+ [CmdletBinding()]
190
+ [OutputType([System.Collections.Specialized.OrderedDictionary])]
191
+ param(
192
+ [Parameter(Mandatory)]
193
+ [string] $CommandText,
194
+
195
+ [Parameter(Mandatory)]
196
+ [string] $ArtifactRoot,
197
+
198
+ [bool] $RelativeBodyAllowed
199
+ )
200
+
201
+ $generic = "PR_BODY_PATH_NONCANONICAL: ``--body-file`` must reference the canonical ``artifacts/pr_body_<N>.md`` file produced by the pr-author skill beneath the target worktree '$ArtifactRoot' (relative only when that worktree is the session worktree). The path supplied does not match."
202
+ $value = ConvertTo-WorktreeResolutionNormalizedPath -Path (Get-PrAuthorBodyFileValue -CommandText $CommandText)
203
+ if ($null -eq $value) {
204
+ return [ordered]@{ Reason = $generic; BodyNumber = $null }
205
+ }
206
+
207
+ if ($value -cmatch '^artifacts/pr_body_(\d+)\.md$') {
208
+ $number = ConvertTo-PrAuthorBodyNumber -Digits $Matches[1]
209
+ if ($null -eq $number) {
210
+ return [ordered]@{ Reason = $generic; BodyNumber = $null }
211
+ }
212
+ if (-not $RelativeBodyAllowed) {
213
+ $canonical = Join-WorktreeResolutionPath -WorktreeRoot $ArtifactRoot -RepoRelativePath "artifacts/pr_body_$number.md"
214
+ return [ordered]@{
215
+ Reason = "PR_BODY_PATH_NONCANONICAL: the target worktree '$ArtifactRoot' is not the session worktree, so gh would read a relative --body-file from the session worktree. Pass the absolute canonical path '$canonical'."
216
+ BodyNumber = $null
217
+ }
218
+ }
219
+ return [ordered]@{ Reason = $null; BodyNumber = $number }
220
+ }
221
+
222
+ if ($value -match '/artifacts/pr_body_(\d+)\.md$') {
223
+ $number = ConvertTo-PrAuthorBodyNumber -Digits $Matches[1]
224
+ if ($null -ne $number) {
225
+ $canonical = Join-WorktreeResolutionPath -WorktreeRoot $ArtifactRoot -RepoRelativePath "artifacts/pr_body_$number.md"
226
+ if (Test-PrAuthorBodyPathEqual -Left $value -Right $canonical) {
227
+ return [ordered]@{ Reason = $null; BodyNumber = $number }
228
+ }
229
+ }
230
+ }
231
+
232
+ return [ordered]@{ Reason = $generic; BodyNumber = $null }
233
+ }