@danmoisan/drm-copilot-mcp 1.1.18 → 1.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. package/out/mcp-server.js +1149 -661
  2. package/package.json +3 -2
  3. package/resources/claude-customizations/.claude/agents/atomic-executor.md +28 -6
  4. package/resources/claude-customizations/.claude/agents/atomic-planner.md +20 -2
  5. package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +17 -4
  6. package/resources/claude-customizations/.claude/agents/epic-planner.md +5 -4
  7. package/resources/claude-customizations/.claude/agents/feature-review.md +6 -4
  8. package/resources/claude-customizations/.claude/agents/orchestrator.md +9 -5
  9. package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +35 -13
  10. package/resources/claude-customizations/.claude/agents/parallel-planner.md +9 -6
  11. package/resources/claude-customizations/.claude/agents/pr-author.md +15 -2
  12. package/resources/claude-customizations/.claude/agents/prd-feature.md +1 -1
  13. package/resources/claude-customizations/.claude/agents/task-researcher.md +8 -0
  14. package/resources/claude-customizations/.claude/hooks/check-powershell-test-purity.ps1 +7 -1
  15. package/resources/claude-customizations/.claude/hooks/check-python-test-purity.ps1 +7 -1
  16. package/resources/claude-customizations/.claude/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
  17. package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +111 -46
  18. package/resources/claude-customizations/.claude/hooks/enforce-completion-helpers.ps1 +105 -0
  19. package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +8 -1
  20. package/resources/claude-customizations/.claude/hooks/enforce-epic-invocation-origin.ps1 +7 -1
  21. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-command.ps1 +190 -0
  22. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-resolution.ps1 +116 -59
  23. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +78 -77
  24. package/resources/claude-customizations/.claude/hooks/enforce-epic-wave-barrier.ps1 +74 -75
  25. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate-resolution.ps1 +127 -35
  26. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +61 -57
  27. package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +7 -1
  28. package/resources/claude-customizations/.claude/hooks/enforce-feature-folder-order.ps1 +129 -39
  29. package/resources/claude-customizations/.claude/hooks/enforce-mermaid-validation.ps1 +7 -1
  30. package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +9 -3
  31. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +39 -51
  32. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
  33. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
  34. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
  35. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +17 -12
  36. package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +9 -3
  37. package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier.ps1 +65 -50
  38. package/resources/claude-customizations/.claude/hooks/enforce-parallel-drift-gate.ps1 +49 -42
  39. package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +54 -72
  40. package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +8 -2
  41. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-command-allowlist.ps1 +279 -0
  42. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +129 -75
  43. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.artifact-root.ps1 +233 -0
  44. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.docs-only.ps1 +224 -0
  45. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +69 -24
  46. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner-helpers.ps1 +29 -18
  47. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +10 -4
  48. package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +14 -5
  49. package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +8 -2
  50. package/resources/claude-customizations/.claude/hooks/feature-folder-resolution.ps1 +373 -0
  51. package/resources/claude-customizations/.claude/hooks/feature-review-coverage-thresholds.ps1 +85 -0
  52. package/resources/claude-customizations/.claude/hooks/hook-command-heredoc.ps1 +119 -0
  53. package/resources/claude-customizations/.claude/hooks/hook-command-invocation-operands.ps1 +198 -0
  54. package/resources/claude-customizations/.claude/hooks/hook-command-invocation.ps1 +254 -255
  55. package/resources/claude-customizations/.claude/hooks/hook-command-payload-powershell.ps1 +201 -0
  56. package/resources/claude-customizations/.claude/hooks/hook-command-payload.ps1 +484 -0
  57. package/resources/claude-customizations/.claude/hooks/hook-command-scanner.ps1 +27 -110
  58. package/resources/claude-customizations/.claude/hooks/hook-dependency-guard.ps1 +114 -0
  59. package/resources/claude-customizations/.claude/hooks/hook-docs-only-diff.ps1 +497 -0
  60. package/resources/claude-customizations/.claude/hooks/premortem-section.ps1 +149 -0
  61. package/resources/claude-customizations/.claude/hooks/subagentstop-envelope.ps1 +192 -0
  62. package/resources/claude-customizations/.claude/hooks/validate-bash.ps1 +9 -3
  63. package/resources/claude-customizations/.claude/hooks/validate-discovery-artifact-gate.ps1 +40 -42
  64. package/resources/claude-customizations/.claude/hooks/validate-executor-output.ps1 +267 -113
  65. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-metrics.ps1 +180 -0
  66. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-rules.ps1 +195 -0
  67. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage.ps1 +141 -395
  68. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-resolution.ps1 +311 -0
  69. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-routing.ps1 +170 -0
  70. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +115 -190
  71. package/resources/claude-customizations/.claude/hooks/validate-planner-output.ps1 +77 -33
  72. package/resources/claude-customizations/.claude/hooks/validate-pr-author-output.ps1 +39 -44
  73. package/resources/claude-customizations/.claude/hooks/validate-prd-feature-output.ps1 +43 -14
  74. package/resources/claude-customizations/.claude/hooks/validate-required-artifact-output.ps1 +66 -36
  75. package/resources/claude-customizations/.claude/hooks/validate-task-researcher-output.ps1 +52 -36
  76. package/resources/claude-customizations/.claude/lib/bash/mutate-parallel-run.sh +191 -0
  77. package/resources/claude-customizations/.claude/lib/bash/parallel-cohorts.sh +13 -3
  78. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-cli.sh +152 -0
  79. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-ops.sh +193 -0
  80. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation.sh +322 -0
  81. package/resources/claude-customizations/.claude/lib/bash/remove-parallel-item.sh +146 -0
  82. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +23 -33
  83. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusTokenShape.psm1 +86 -6
  84. package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-CiGateParser.ps1 +91 -19
  85. package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-MergeConfirmationParser.ps1 +203 -0
  86. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateEpicWaveBarrier.psm1 +346 -0
  87. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1 +24 -3
  88. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +79 -2
  89. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1 +3 -3
  90. package/resources/claude-customizations/.claude/rules/architecture-boundaries.md +5 -5
  91. package/resources/claude-customizations/.claude/rules/csharp.md +2 -2
  92. package/resources/claude-customizations/.claude/rules/general-code-change.md +1 -1
  93. package/resources/claude-customizations/.claude/rules/general-unit-test.md +3 -2
  94. package/resources/claude-customizations/.claude/rules/orchestrator-state.md +37 -4
  95. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +50 -5
  96. package/resources/claude-customizations/.claude/rules/plan-acceptance-gates.md +85 -10
  97. package/resources/claude-customizations/.claude/rules/quality-tiers.md +11 -7
  98. package/resources/claude-customizations/.claude/rules/shell.md +10 -8
  99. package/resources/claude-customizations/.claude/rules/typescript.md +1 -1
  100. package/resources/claude-customizations/.claude/settings.json +3 -4
  101. package/resources/claude-customizations/.claude/skills/acceptance-criteria-tracking/SKILL.md +1 -1
  102. package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +52 -16
  103. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +7 -3
  104. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_detached_lib.sh +1 -1
  105. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_enumerate_lib.sh +2 -0
  106. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_scan_helper.sh +2 -0
  107. package/resources/claude-customizations/.claude/skills/csharp-change-budget-router/SKILL.md +2 -2
  108. package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +45 -9
  109. package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +5 -3
  110. package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
  111. package/resources/claude-customizations/.claude/skills/execution-environment/SKILL.md +134 -0
  112. package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +1 -1
  113. package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +4 -3
  114. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +36 -15
  115. package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +52 -14
  116. package/resources/claude-customizations/.claude/skills/parallel-close/SKILL.md +22 -9
  117. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +108 -21
  118. package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +63 -7
  119. package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +35 -12
  120. package/resources/claude-customizations/.claude/skills/powershell-change-budget-router/SKILL.md +1 -1
  121. package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +3 -1
  122. package/resources/claude-customizations/.claude/skills/python-change-budget-router/SKILL.md +1 -1
  123. package/resources/claude-customizations/.claude/skills/quota-throttling/SKILL.md +1 -1
  124. package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +16 -8
  125. package/resources/claude-customizations/.claude/skills/research-issue/SKILL.md +6 -0
  126. package/resources/claude-customizations/pack-manifests/core.json +27 -0
  127. package/resources/codex-and-agents-customizations/.agents/skills/README.md +1 -0
  128. package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +1 -1
  129. package/resources/codex-and-agents-customizations/.agents/skills/architecture-boundaries/SKILL.md +5 -5
  130. package/resources/codex-and-agents-customizations/.agents/skills/atomic-executor/SKILL.md +19 -7
  131. package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +43 -4
  132. package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +18 -6
  133. package/resources/codex-and-agents-customizations/.agents/skills/csharp/SKILL.md +2 -2
  134. package/resources/codex-and-agents-customizations/.agents/skills/csharp-change-budget-router/SKILL.md +2 -2
  135. package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +2 -2
  136. package/resources/codex-and-agents-customizations/.agents/skills/epic-orchestrate/SKILL.md +16 -1
  137. package/resources/codex-and-agents-customizations/.agents/skills/epic-plan/SKILL.md +17 -1
  138. package/resources/codex-and-agents-customizations/.agents/skills/epic-run/SKILL.md +2 -1
  139. package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
  140. package/resources/codex-and-agents-customizations/.agents/skills/execution-environment/SKILL.md +134 -0
  141. package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +13 -5
  142. package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +1 -1
  143. package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +3 -2
  144. package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +27 -8
  145. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +1 -1
  146. package/resources/codex-and-agents-customizations/.agents/skills/powershell-change-budget-router/SKILL.md +1 -1
  147. package/resources/codex-and-agents-customizations/.agents/skills/python-change-budget-router/SKILL.md +1 -1
  148. package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +11 -7
  149. package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +7 -3
  150. package/resources/codex-and-agents-customizations/.agents/skills/research-issue/SKILL.md +6 -0
  151. package/resources/codex-and-agents-customizations/.agents/skills/translate-claude-to-codex/SKILL.md +1 -1
  152. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp/SKILL.md +2 -2
  153. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +2 -2
  154. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c1.toml +21 -6
  155. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +21 -6
  156. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +21 -6
  157. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +21 -6
  158. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +21 -6
  159. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +21 -6
  160. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +15 -2
  161. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +15 -2
  162. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +15 -2
  163. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +15 -2
  164. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +15 -2
  165. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +15 -2
  166. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planning.toml +1 -1
  167. package/resources/codex-and-agents-customizations/.codex/agents/csharp-atomic-planning.toml +1 -1
  168. package/resources/codex-and-agents-customizations/.codex/agents/epic-orchestrator.toml +4 -1
  169. package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +6 -6
  170. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c1.toml +7 -4
  171. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +7 -4
  172. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +7 -4
  173. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +7 -4
  174. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +7 -4
  175. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +7 -4
  176. package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-planning.toml +1 -1
  177. package/resources/codex-and-agents-customizations/.codex/agents/python-atomic-planning.toml +1 -1
  178. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c1.toml +6 -0
  179. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c2.toml +6 -0
  180. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c3-elevated.toml +6 -0
  181. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c3.toml +6 -0
  182. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c4.toml +6 -0
  183. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher.toml +6 -0
  184. package/resources/codex-and-agents-customizations/.codex/codex-web-setup.sh +318 -273
  185. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  186. package/resources/codex-and-agents-customizations/.codex/hooks/check-powershell-test-purity.ps1 +7 -1
  187. package/resources/codex-and-agents-customizations/.codex/hooks/check-python-test-purity.ps1 +7 -1
  188. package/resources/codex-and-agents-customizations/.codex/hooks/codex-epic-child-launch-attestation.ps1 +1 -1
  189. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
  190. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-codex-model-routing.ps1 +8 -2
  191. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +97 -44
  192. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-helpers.ps1 +105 -0
  193. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-child-worktree-binding.ps1 +7 -1
  194. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate-command.ps1 +190 -0
  195. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +54 -43
  196. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +6 -0
  197. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-root-invocation.ps1 +7 -1
  198. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-wave-barrier.ps1 +7 -1
  199. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-worktree-removal-gate.ps1 +54 -41
  200. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-evidence-locations.ps1 +7 -1
  201. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +35 -14
  202. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
  203. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
  204. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
  205. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +24 -11
  206. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +8 -2
  207. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +13 -4
  208. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +8 -2
  209. package/resources/codex-and-agents-customizations/.codex/hooks/feature-folder-resolution.ps1 +373 -0
  210. package/resources/codex-and-agents-customizations/.codex/hooks/feature-review-coverage-thresholds.ps1 +85 -0
  211. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-heredoc.ps1 +119 -0
  212. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation-operands.ps1 +198 -0
  213. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation.ps1 +254 -255
  214. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload-powershell.ps1 +201 -0
  215. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload.ps1 +484 -0
  216. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-scanner.ps1 +27 -110
  217. package/resources/codex-and-agents-customizations/.codex/hooks/hook-dependency-guard.ps1 +114 -0
  218. package/resources/codex-and-agents-customizations/.codex/hooks/hook-docs-only-diff.ps1 +497 -0
  219. package/resources/codex-and-agents-customizations/.codex/hooks/validate-bash.ps1 +8 -2
  220. package/resources/codex-and-agents-customizations/.codex/hooks/validate-codex-subagent-routing.ps1 +7 -1
  221. package/resources/codex-and-agents-customizations/.codex/hooks/validate-feature-review-coverage.ps1 +24 -8
  222. package/resources/codex-and-agents-customizations/.codex/prompts/feature-review-remediate.md +6 -4
  223. package/resources/codex-and-agents-customizations/pack-manifests/core.json +11 -0
  224. package/resources/customizations/.github/agents/atomic_executor.agent.md +21 -11
  225. package/resources/customizations/.github/agents/atomic_planning.agent.md +8 -3
  226. package/resources/customizations/.github/agents/csharp-atomic-executor.agent.md +13 -9
  227. package/resources/customizations/.github/agents/csharp-atomic-planning.agent.md +2 -2
  228. package/resources/customizations/.github/agents/csharp-orchestrator.agent.md +2 -2
  229. package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +5 -5
  230. package/resources/customizations/.github/agents/orchestrator.agent.md +8 -5
  231. package/resources/customizations/.github/agents/powershell-atomic-executor.agent.md +13 -9
  232. package/resources/customizations/.github/agents/powershell-atomic-planning.agent.md +2 -2
  233. package/resources/customizations/.github/agents/powershell-orchestrator.agent.md +2 -2
  234. package/resources/customizations/.github/agents/powershell-typed-engineer.agent.md +3 -3
  235. package/resources/customizations/.github/agents/python-atomic-executor.agent.md +13 -9
  236. package/resources/customizations/.github/agents/python-atomic-planning.agent.md +2 -2
  237. package/resources/customizations/.github/agents/python-orchestrator.agent.md +2 -2
  238. package/resources/customizations/.github/agents/task-researcher.agent.md +6 -0
  239. package/resources/customizations/.github/instructions/csharp-code-change.instructions.md +4 -4
  240. package/resources/customizations/.github/instructions/csharp-unit-test.instructions.md +2 -2
  241. package/resources/customizations/.github/prompts/generate-atomic-plan.prompt.md +16 -10
  242. package/resources/customizations/.github/prompts/orchestrate-csharp-work.prompt.md +2 -2
  243. package/resources/customizations/.github/prompts/orchestrate-powershell-work.prompt.md +1 -1
  244. package/resources/customizations/.github/prompts/orchestrate-python-work.prompt.md +1 -1
  245. package/resources/customizations/.github/prompts/orchestrate-work.prompt.md +2 -2
  246. package/resources/customizations/.github/skills/acceptance-criteria-tracking/SKILL.md +1 -1
  247. package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +43 -4
  248. package/resources/customizations/.github/skills/csharp-change-budget-router/SKILL.md +1 -1
  249. package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
  250. package/resources/customizations/.github/skills/execution-environment/SKILL.md +134 -0
  251. package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +1 -1
  252. package/resources/customizations/.github/skills/powershell-change-budget-router/SKILL.md +1 -1
  253. package/resources/customizations/.github/skills/remediation-handoff-atomic-planner/SKILL.md +7 -3
  254. package/resources/feature-templates/bug/plan.yyyy-MM-ddTHH-mm.md +10 -0
  255. package/resources/feature-templates/feature/plan.yyyy-MM-ddTHH-mm.md +11 -0
  256. package/resources/feature-templates/refactor/plan.yyyy-MM-ddTHH-mm.md +11 -0
  257. package/resources/powershell/PoshQC/PoshQC.Coverage.psm1 +314 -0
  258. package/resources/powershell/PoshQC/PoshQC.CoverageMerge.psm1 +163 -0
  259. package/resources/powershell/PoshQC/PoshQC.Shard.psm1 +296 -0
  260. package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +48 -32
  261. package/resources/powershell/PoshQC/PoshQC.psd1 +7 -1
  262. package/resources/powershell/PoshQC/PoshQC.psm1 +18 -5
  263. package/resources/powershell/PoshQC/README.md +21 -3
  264. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +5 -319
@@ -0,0 +1,497 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Docs-only diff classification and the merge-side docs-only exemption (issue #927).
4
+
5
+ .DESCRIPTION
6
+ Shared by enforce-pr-author-skill.docs-only.ps1 (create side) and by the Claude and Codex
7
+ copies of enforce-epic-merge-gate.ps1 (merge side). The file defines functions only: it
8
+ imports nothing, dot-sources nothing, and has no entry point. At call time
9
+ Get-DocsOnlyMergeExemption uses Read-CommandLineSegment, Test-CommandLineSegmentRawScan,
10
+ Get-CommandLineStructuralMatch, Test-CommandLineFlag, and Get-CommandLineFlagValue from
11
+ the command parsers every consumer already loads.
12
+
13
+ Docs-only merge branch: a merge gate calls Get-DocsOnlyMergeExemption only for an explicit
14
+ pull request number that no checkpoint records. The exemption fails closed; conditions
15
+ are evaluated in order and the first failure returns its code:
16
+
17
+ M1 DOCS_ONLY_EXEMPTION_PR_UNRESOLVED - a positive number; exactly one segment that is
18
+ not wrapper-led, has no live substitution, and
19
+ is balanced; a structural gh pr merge; no inline
20
+ VAR= prefix token (GH_REPO=, GH_HOST=); no
21
+ single-dash token containing R; no --repo or
22
+ --repo= token; no --auto token; no token
23
+ containing /pull/ (a URL selector).
24
+ M2 DOCS_ONLY_EXEMPTION_PR_UNRESOLVED - the worktree root is an existing directory.
25
+ M3 DOCS_ONLY_EXEMPTION_PR_UNRESOLVED - gh pr view <N> --json baseRefName,headRefOid
26
+ exits 0 and returns a JSON object; headRefOid
27
+ is 40 or 64 lowercase hex; a --match-head-commit
28
+ value, when present, equals headRefOid (OD-2).
29
+ DOCS_ONLY_EXEMPTION_BASE_UNRESOLVED - baseRefName is a conservative branch spelling
30
+ without '..'.
31
+ M4 DOCS_ONLY_EXEMPTION_BASE_UNRESOLVED - refs/remotes/origin/<base> resolves to a commit.
32
+ M5 DOCS_ONLY_EXEMPTION_DIFF_FAILED - headRefOid is present in the local repository.
33
+ M6 DOCS_ONLY_EXEMPTION_DIFF_FAILED - git diff --name-only -z --no-renames --no-color
34
+ --no-ext-diff --no-relative <base>...<head>
35
+ exits 0.
36
+ M7 DOCS_ONLY_EXEMPTION_DIFF_FAILED - non-empty diff output ends with NUL.
37
+ M8 DOCS_ONLY_EXEMPTION_EMPTY_DIFF - the diff lists at least one path.
38
+ M9 DOCS_ONLY_EXEMPTION_NON_DOCS_PATH - every listed path starts with docs/ (ordinal).
39
+
40
+ No-fetch policy: no function here fetches or writes a ref. A missing origin/<base> ref or
41
+ a head commit that is not present locally denies, and the M5 reason names the command
42
+ the operator can run before retrying.
43
+
44
+ Residual limits: a GH_REPO or GH_HOST value exported by an earlier shell command or
45
+ inherited from the session environment is not visible in the command text; gh and git
46
+ run in the WorktreeRoot the gate passes (the Claude session worktree root or the Codex
47
+ repository root), so a working directory set by an earlier shell call is not visible
48
+ either; a push between the gh pr view lookup and the merge can change the head, which
49
+ only --match-head-commit closes; the control is policy-level, not cryptographic.
50
+
51
+ Time limit: Claude Code lets a tool call continue through the permission flow when a
52
+ PreToolUse hook exceeds its timeout, and the repository records no Codex behavior for
53
+ that case, so this file bounds every gh and git call itself. The default invokers run
54
+ the executable through Invoke-DocsOnlyBoundedNative: at most 7000 ms for gh and 3000 ms
55
+ for each git call, plus at most 1000 ms for the output. An expired wait stops the
56
+ process tree and reports exit code 124, which denies. The worst case for one gh and
57
+ three git calls is 20 s, below the 30 s Codex registration timeout.
58
+
59
+ .NOTES
60
+ PowerShell 7+. Byte-identical at .claude/hooks, .codex/hooks, and both bundle mirrors.
61
+ git and gh run only through Invoke-DocsOnlyGitExe and Invoke-DocsOnlyGhExe, which never
62
+ throw; unit tests pass a test-local scriptblock through -GitInvoker and -GhInvoker so
63
+ the seam bodies run without starting a process, and pass a test-local process starter
64
+ through the -ProcessStarter parameter of Invoke-DocsOnlyBoundedNative.
65
+ #>
66
+
67
+ function ConvertFrom-DocsOnlyNameList {
68
+ <#
69
+ .SYNOPSIS
70
+ Split NUL-separated git output into paths, discarding empty elements.
71
+ .DESCRIPTION
72
+ Callers wrap the result in @() so that an empty listing is an empty array. A path that
73
+ contains a line feed stays one element, because only NUL separates entries.
74
+ .PARAMETER Text
75
+ The captured git output.
76
+ .OUTPUTS
77
+ System.String, one per path
78
+ #>
79
+ [CmdletBinding()]
80
+ [OutputType([string])]
81
+ param(
82
+ [AllowNull()][AllowEmptyString()][string] $Text
83
+ )
84
+
85
+ if ([string]::IsNullOrEmpty($Text)) {
86
+ return
87
+ }
88
+ foreach ($entry in $Text.Split([char]0)) {
89
+ if ($entry.Length -gt 0) {
90
+ $entry
91
+ }
92
+ }
93
+ }
94
+
95
+ function Get-DocsOnlyPathViolation {
96
+ <#
97
+ .SYNOPSIS
98
+ Return $null when every path is under docs/, else the denial sub-code text.
99
+ .DESCRIPTION
100
+ An empty set returns DOCS_ONLY_EXEMPTION_EMPTY_DIFF. Otherwise the first path that does
101
+ not start with docs/ under ordinal comparison, or that is not longer than docs/ itself,
102
+ returns DOCS_ONLY_EXEMPTION_NON_DOCS_PATH naming that path.
103
+ .PARAMETER Path
104
+ The changed paths, repository-relative, as git reports them.
105
+ .OUTPUTS
106
+ System.String or $null
107
+ #>
108
+ [CmdletBinding()]
109
+ [OutputType([string])]
110
+ param(
111
+ [AllowNull()][AllowEmptyCollection()][string[]] $Path
112
+ )
113
+
114
+ $paths = @($Path | Where-Object { $null -ne $_ })
115
+ if ($paths.Count -eq 0) {
116
+ return 'DOCS_ONLY_EXEMPTION_EMPTY_DIFF: the branch diff lists no path.'
117
+ }
118
+ foreach ($entry in $paths) {
119
+ if ($entry.Length -le 5 -or -not $entry.StartsWith('docs/', [System.StringComparison]::Ordinal)) {
120
+ return "DOCS_ONLY_EXEMPTION_NON_DOCS_PATH: '$entry' is outside docs/."
121
+ }
122
+ }
123
+ return $null
124
+ }
125
+
126
+ function Get-DocsOnlyDiffTextViolation {
127
+ <#
128
+ .SYNOPSIS
129
+ Return $null when NUL-terminated diff output lists only docs/ paths, else the sub-code text.
130
+ .DESCRIPTION
131
+ Pure. Non-empty text that does not end with NUL returns DOCS_ONLY_EXEMPTION_DIFF_FAILED,
132
+ because entries could otherwise run together into one path that starts with docs/.
133
+ Otherwise the text is split on NUL and classified by Get-DocsOnlyPathViolation, so empty
134
+ text reaches DOCS_ONLY_EXEMPTION_EMPTY_DIFF.
135
+ .PARAMETER DiffText
136
+ The captured output of git diff --name-only -z.
137
+ .PARAMETER BaseName
138
+ The base branch named in the denial text.
139
+ .OUTPUTS
140
+ System.String or $null
141
+ #>
142
+ [CmdletBinding()]
143
+ [OutputType([string])]
144
+ param(
145
+ [AllowNull()][AllowEmptyString()][string] $DiffText,
146
+ [AllowNull()][AllowEmptyString()][string] $BaseName
147
+ )
148
+
149
+ $text = [string]$DiffText
150
+ if ($text.Length -gt 0 -and -not $text.EndsWith([char]0)) {
151
+ return "DOCS_ONLY_EXEMPTION_DIFF_FAILED: git diff against origin/$BaseName did not return NUL-terminated output."
152
+ }
153
+ return (Get-DocsOnlyPathViolation -Path @(ConvertFrom-DocsOnlyNameList -Text $text))
154
+ }
155
+
156
+ function Invoke-DocsOnlyBoundedNative {
157
+ <#
158
+ .SYNOPSIS
159
+ Run git or gh with a fail-closed time limit; the body of the default seam invokers.
160
+ .DESCRIPTION
161
+ Starts FileName in the current file-system location with each argument passed
162
+ unchanged and standard output and standard error redirected, then waits at most
163
+ TimeoutMilliseconds for the exit and at most 1000 ms more for the output. When a
164
+ wait expires, the process tree is stopped, nothing is emitted, and $LASTEXITCODE is
165
+ set to 124, so the calling seam reports a non-zero exit and the exemption denies.
166
+ Otherwise the output lines are emitted and $LASTEXITCODE is set to the exit code.
167
+ Standard error is drained and discarded. A start failure throws to the calling
168
+ seam, which returns its failure object. $LASTEXITCODE is set in the global scope
169
+ because the seams read it after the invoker, as they do after a native command.
170
+ The process object is disposed on every return path once it has started.
171
+ After an expired wait, each read is checked once without waiting before disposal.
172
+ .PARAMETER FileName
173
+ The executable, git or gh, resolved from PATH.
174
+ .PARAMETER ArgumentList
175
+ The arguments, one element per argument.
176
+ .PARAMETER TimeoutMilliseconds
177
+ The longest wait for the process to exit.
178
+ .PARAMETER ProcessStarter
179
+ The scriptblock that starts the process from its start description; a unit test
180
+ passes a test-local stand-in that returns a fake process object.
181
+ .OUTPUTS
182
+ System.String, one per output line
183
+ #>
184
+ [CmdletBinding()]
185
+ [OutputType([string])]
186
+ param(
187
+ [Parameter(Mandatory)][string] $FileName,
188
+ [AllowEmptyCollection()][string[]] $ArgumentList = @(),
189
+ [ValidateRange(1, 10000)][int] $TimeoutMilliseconds = 3000,
190
+ [ValidateNotNull()][scriptblock] $ProcessStarter = { param($StartInfo) [System.Diagnostics.Process]::Start($StartInfo) }
191
+ )
192
+
193
+ $startInfo = [System.Diagnostics.ProcessStartInfo]::new($FileName)
194
+ foreach ($argument in $ArgumentList) {
195
+ $startInfo.ArgumentList.Add($argument)
196
+ }
197
+ $startInfo.WorkingDirectory = $ExecutionContext.SessionState.Path.CurrentFileSystemLocation.ProviderPath
198
+ $startInfo.UseShellExecute = $false
199
+ $startInfo.RedirectStandardOutput = $true
200
+ $startInfo.RedirectStandardError = $true
201
+ $startInfo.StandardOutputEncoding = [System.Text.Encoding]::UTF8
202
+ $process = & $ProcessStarter $startInfo
203
+ try {
204
+ $output = $process.StandardOutput.ReadToEndAsync()
205
+ $errorRead = $process.StandardError.ReadToEndAsync()
206
+ if (-not $process.WaitForExit($TimeoutMilliseconds) -or -not $output.Wait(1000)) {
207
+ try {
208
+ $process.Kill($true)
209
+ }
210
+ catch {
211
+ $null = $_
212
+ }
213
+ foreach ($pendingRead in @($output, $errorRead)) {
214
+ try {
215
+ $null = $pendingRead.Wait(0)
216
+ }
217
+ catch {
218
+ $null = $_
219
+ }
220
+ }
221
+ Set-Variable -Name LASTEXITCODE -Value 124 -Scope Global
222
+ return
223
+ }
224
+ Set-Variable -Name LASTEXITCODE -Value ([int]$process.ExitCode) -Scope Global
225
+ $text = [string]$output.Result
226
+ if ($text.EndsWith("`n")) {
227
+ $text = $text.Substring(0, $text.Length - 1)
228
+ }
229
+ if ($text.Length -eq 0) {
230
+ return
231
+ }
232
+ foreach ($line in $text.Split("`n")) {
233
+ $line.TrimEnd("`r")
234
+ }
235
+ }
236
+ finally {
237
+ $process.Dispose()
238
+ }
239
+ }
240
+
241
+ function Invoke-DocsOnlyGitExe {
242
+ <#
243
+ .SYNOPSIS
244
+ Run git -C <root> with the given arguments; the git seam of the merge exemption.
245
+ .DESCRIPTION
246
+ Never throws. A root that is not an existing directory returns ExitCode -1 without
247
+ running the command, and so does a command that cannot start. Standard error is
248
+ discarded so that it is never read as path data. Output lines are joined with a line
249
+ feed.
250
+ .PARAMETER WorktreeRoot
251
+ The directory git runs in.
252
+ .PARAMETER GitArgs
253
+ The git arguments that follow -C <root>.
254
+ .PARAMETER GitInvoker
255
+ The scriptblock that runs git; a unit test passes a test-local stand-in instead.
256
+ .OUTPUTS
257
+ System.Management.Automation.PSCustomObject with ExitCode and Output.
258
+ #>
259
+ [CmdletBinding()]
260
+ [OutputType([pscustomobject])]
261
+ param(
262
+ [AllowNull()][AllowEmptyString()][string] $WorktreeRoot,
263
+ [Parameter(Mandatory)][string[]] $GitArgs,
264
+ [ValidateNotNull()][scriptblock] $GitInvoker = { Invoke-DocsOnlyBoundedNative -FileName 'git' -ArgumentList $args -TimeoutMilliseconds 3000 }
265
+ )
266
+
267
+ $failure = [pscustomobject]@{ ExitCode = -1; Output = '' }
268
+ if ([string]::IsNullOrWhiteSpace($WorktreeRoot) -or -not (Test-Path -LiteralPath $WorktreeRoot -PathType Container)) {
269
+ return $failure
270
+ }
271
+ try {
272
+ $lines = @(& $GitInvoker -C $WorktreeRoot @GitArgs 2>$null)
273
+ $code = $LASTEXITCODE
274
+ }
275
+ catch {
276
+ return $failure
277
+ }
278
+ return [pscustomobject]@{ ExitCode = [int]$code; Output = ($lines -join "`n") }
279
+ }
280
+
281
+ function Invoke-DocsOnlyGhExe {
282
+ <#
283
+ .SYNOPSIS
284
+ Run gh with the given arguments inside a working root; the gh seam.
285
+ .DESCRIPTION
286
+ Never throws. gh has no -C option, so the seam pushes the working root and pops it in
287
+ finally, restoring the caller's location after success and after a throw. A root that
288
+ is not an existing directory returns ExitCode -1 without running the command.
289
+ Standard error is discarded. Output lines are joined with a line feed.
290
+ .PARAMETER WorkingRoot
291
+ The directory gh runs in.
292
+ .PARAMETER GhArgs
293
+ The gh arguments.
294
+ .PARAMETER GhInvoker
295
+ The scriptblock that runs gh; a unit test passes a test-local stand-in instead.
296
+ .OUTPUTS
297
+ System.Management.Automation.PSCustomObject with ExitCode and Output.
298
+ #>
299
+ [CmdletBinding()]
300
+ [OutputType([pscustomobject])]
301
+ param(
302
+ [AllowNull()][AllowEmptyString()][string] $WorkingRoot,
303
+ [Parameter(Mandatory)][string[]] $GhArgs,
304
+ [ValidateNotNull()][scriptblock] $GhInvoker = { Invoke-DocsOnlyBoundedNative -FileName 'gh' -ArgumentList $args -TimeoutMilliseconds 7000 }
305
+ )
306
+
307
+ $failure = [pscustomobject]@{ ExitCode = -1; Output = '' }
308
+ if ([string]::IsNullOrWhiteSpace($WorkingRoot) -or -not (Test-Path -LiteralPath $WorkingRoot -PathType Container)) {
309
+ return $failure
310
+ }
311
+ try {
312
+ Push-Location -LiteralPath $WorkingRoot -ErrorAction Stop
313
+ }
314
+ catch {
315
+ return $failure
316
+ }
317
+ try {
318
+ $lines = @(& $GhInvoker @GhArgs 2>$null)
319
+ $code = $LASTEXITCODE
320
+ }
321
+ catch {
322
+ return $failure
323
+ }
324
+ finally {
325
+ Pop-Location
326
+ }
327
+ return [pscustomobject]@{ ExitCode = [int]$code; Output = ($lines -join "`n") }
328
+ }
329
+
330
+ function Get-DocsOnlyMergeShapeViolation {
331
+ <#
332
+ .SYNOPSIS
333
+ Return $null when the merge command has the M1 shape, else the denial text.
334
+ .DESCRIPTION
335
+ Pure; runs before any process. See M1 in the file header.
336
+ .PARAMETER CommandText
337
+ The Bash command text under evaluation.
338
+ .PARAMETER PrNumber
339
+ The explicit pull request number the gate read, or $null.
340
+ .OUTPUTS
341
+ System.String or $null
342
+ #>
343
+ [CmdletBinding()]
344
+ [OutputType([string])]
345
+ param(
346
+ [AllowNull()][AllowEmptyString()][string] $CommandText,
347
+ [AllowNull()][Nullable[int]] $PrNumber
348
+ )
349
+
350
+ $prefix = 'DOCS_ONLY_EXEMPTION_PR_UNRESOLVED: '
351
+ if ($null -eq $PrNumber -or $PrNumber -le 0) {
352
+ return $prefix + 'the command names no positive pull request number.'
353
+ }
354
+ $segments = @(Read-CommandLineSegment -CommandText $CommandText)
355
+ if ($segments.Count -ne 1 -or (Test-CommandLineSegmentRawScan -Segment $segments[0])) {
356
+ return $prefix + 'the command must be one gh pr merge segment with no chain, wrapper, or substitution.'
357
+ }
358
+ if ($null -eq (Get-CommandLineStructuralMatch -CommandText $CommandText -CommandWord 'gh' -SubcommandPath @('pr', 'merge'))) {
359
+ return $prefix + 'the command is not a structural gh pr merge.'
360
+ }
361
+ foreach ($token in @($segments[0].Tokens)) {
362
+ $text = [string]$token
363
+ if ($text -cmatch '^[A-Za-z_][A-Za-z0-9_]*=') {
364
+ return $prefix + "the inline environment prefix '$text' is not accepted."
365
+ }
366
+ if ($text.Length -ge 2 -and $text[0] -eq '-' -and $text[1] -ne '-' -and $text.Substring(1).Contains('R')) {
367
+ return $prefix + "the repository override '$text' is not accepted."
368
+ }
369
+ if ($text -eq '--repo' -or $text.StartsWith('--repo=', [System.StringComparison]::OrdinalIgnoreCase)) {
370
+ return $prefix + "the repository override '$text' is not accepted."
371
+ }
372
+ if ($text.Contains('/pull/')) {
373
+ return $prefix + "the URL selector '$text' is not accepted."
374
+ }
375
+ if ($text -eq '--auto' -or $text.StartsWith('--auto=', [System.StringComparison]::OrdinalIgnoreCase)) {
376
+ return $prefix + 'auto-merge (--auto) is not eligible.'
377
+ }
378
+ }
379
+ return $null
380
+ }
381
+
382
+ function Get-DocsOnlyPullRequestTarget {
383
+ <#
384
+ .SYNOPSIS
385
+ Read the base branch and head commit of a pull request through the gh seam (M3).
386
+ .DESCRIPTION
387
+ Returns an object with Reason, Base, and Head. Reason is $null when the lookup
388
+ succeeded and every M3 check passed; otherwise it carries the denial text.
389
+ .PARAMETER CommandText
390
+ The Bash command text, read for an optional --match-head-commit value.
391
+ .PARAMETER PrNumber
392
+ The pull request number.
393
+ .PARAMETER WorktreeRoot
394
+ The directory gh runs in.
395
+ .OUTPUTS
396
+ System.Management.Automation.PSCustomObject with Reason, Base, and Head.
397
+ #>
398
+ [CmdletBinding()]
399
+ [OutputType([pscustomobject])]
400
+ param(
401
+ [AllowNull()][AllowEmptyString()][string] $CommandText,
402
+ [Parameter(Mandatory)][int] $PrNumber,
403
+ [Parameter(Mandatory)][string] $WorktreeRoot
404
+ )
405
+
406
+ $unresolved = 'DOCS_ONLY_EXEMPTION_PR_UNRESOLVED: '
407
+ $lookup = Invoke-DocsOnlyGhExe -WorkingRoot $WorktreeRoot -GhArgs @('pr', 'view', [string]$PrNumber, '--json', 'baseRefName,headRefOid')
408
+ $pr = $null
409
+ if ($null -ne $lookup -and $lookup.ExitCode -eq 0) {
410
+ try {
411
+ $pr = ConvertFrom-Json -InputObject ([string]$lookup.Output) -ErrorAction Stop
412
+ }
413
+ catch {
414
+ $pr = $null
415
+ }
416
+ }
417
+ if ($pr -isnot [System.Management.Automation.PSCustomObject]) {
418
+ return [pscustomobject]@{ Reason = $unresolved + "gh pr view $PrNumber did not return a readable pull request."; Base = $null; Head = $null }
419
+ }
420
+ $base = [string]$pr.baseRefName
421
+ if ($base -cnotmatch '^[A-Za-z0-9][A-Za-z0-9._/-]*$' -or $base.Contains('..')) {
422
+ return [pscustomobject]@{ Reason = "DOCS_ONLY_EXEMPTION_BASE_UNRESOLVED: the base branch '$base' of pull request $PrNumber is not a conservative branch name."; Base = $null; Head = $null }
423
+ }
424
+ $head = [string]$pr.headRefOid
425
+ if ($head -cnotmatch '^[0-9a-f]{40}([0-9a-f]{24})?$') {
426
+ return [pscustomobject]@{ Reason = $unresolved + "the head commit of pull request $PrNumber is not a full lowercase object id."; Base = $null; Head = $null }
427
+ }
428
+ $subcommandPath = @('pr', 'merge')
429
+ if (Test-CommandLineFlag -CommandText $CommandText -CommandWord 'gh' -SubcommandPath $subcommandPath -FlagName '--match-head-commit') {
430
+ $pinned = Get-CommandLineFlagValue -CommandText $CommandText -CommandWord 'gh' -SubcommandPath $subcommandPath -FlagName '--match-head-commit'
431
+ if (-not [string]::Equals($pinned, $head, [System.StringComparison]::Ordinal)) {
432
+ return [pscustomobject]@{ Reason = $unresolved + "--match-head-commit does not equal the head commit $head of pull request $PrNumber."; Base = $null; Head = $null }
433
+ }
434
+ }
435
+ return [pscustomobject]@{ Reason = $null; Base = $base; Head = $head }
436
+ }
437
+
438
+ function Get-DocsOnlyMergeExemption {
439
+ <#
440
+ .SYNOPSIS
441
+ Decide whether gh pr merge <N> --merge is exempt because the pull request changes only docs/.
442
+ .DESCRIPTION
443
+ Evaluates M1 to M9 of the file header in order and returns at the first failure. M1
444
+ and M2 run before any process. Every seam failure denies.
445
+ .PARAMETER CommandText
446
+ The Bash command text under evaluation.
447
+ .PARAMETER PrNumber
448
+ The explicit pull request number the gate read from the command, or $null.
449
+ .PARAMETER WorktreeRoot
450
+ The directory gh and git run in.
451
+ .OUTPUTS
452
+ System.Collections.Specialized.OrderedDictionary with Exempt and Reason.
453
+ #>
454
+ [CmdletBinding()]
455
+ [OutputType([System.Collections.Specialized.OrderedDictionary])]
456
+ param(
457
+ [AllowNull()][AllowEmptyString()][string] $CommandText,
458
+ [AllowNull()][Nullable[int]] $PrNumber,
459
+ [AllowNull()][AllowEmptyString()][string] $WorktreeRoot
460
+ )
461
+
462
+ $shape = Get-DocsOnlyMergeShapeViolation -CommandText $CommandText -PrNumber $PrNumber
463
+ if ($shape) {
464
+ return [ordered]@{ Exempt = $false; Reason = $shape }
465
+ }
466
+ if ([string]::IsNullOrWhiteSpace($WorktreeRoot) -or -not (Test-Path -LiteralPath $WorktreeRoot -PathType Container)) {
467
+ return [ordered]@{ Exempt = $false; Reason = 'DOCS_ONLY_EXEMPTION_PR_UNRESOLVED: the worktree root for the lookup is not an existing directory.' }
468
+ }
469
+ $target = Get-DocsOnlyPullRequestTarget -CommandText $CommandText -PrNumber ([int]$PrNumber) -WorktreeRoot $WorktreeRoot
470
+ if ($target.Reason) {
471
+ return [ordered]@{ Exempt = $false; Reason = $target.Reason }
472
+ }
473
+ $base = $target.Base
474
+ $head = $target.Head
475
+
476
+ $baseRev = Invoke-DocsOnlyGitExe -WorktreeRoot $WorktreeRoot -GitArgs @('rev-parse', '--verify', '--quiet', ('refs/remotes/origin/' + $base + '^{commit}'))
477
+ $baseCommit = if ($null -ne $baseRev) { ([string]$baseRev.Output).Trim() } else { '' }
478
+ if ($null -eq $baseRev -or $baseRev.ExitCode -ne 0 -or $baseCommit -cnotmatch '^[0-9a-f]{40}([0-9a-f]{24})?$') {
479
+ return [ordered]@{ Exempt = $false; Reason = "DOCS_ONLY_EXEMPTION_BASE_UNRESOLVED: refs/remotes/origin/$base does not resolve to a commit in the worktree." }
480
+ }
481
+
482
+ $headRev = Invoke-DocsOnlyGitExe -WorktreeRoot $WorktreeRoot -GitArgs @('rev-parse', '--verify', '--quiet', ($head + '^{commit}'))
483
+ $headCommit = if ($null -ne $headRev) { ([string]$headRev.Output).Trim() } else { '' }
484
+ if ($null -eq $headRev -or $headRev.ExitCode -ne 0 -or -not [string]::Equals($headCommit, $head, [System.StringComparison]::Ordinal)) {
485
+ return [ordered]@{ Exempt = $false; Reason = "DOCS_ONLY_EXEMPTION_DIFF_FAILED: the head commit $head of pull request $PrNumber is not present locally; run git fetch origin pull/$PrNumber/head, then retry. This hook does not fetch." }
486
+ }
487
+
488
+ $diff = Invoke-DocsOnlyGitExe -WorktreeRoot $WorktreeRoot -GitArgs @('diff', '--name-only', '-z', '--no-renames', '--no-color', '--no-ext-diff', '--no-relative', ($baseCommit + '...' + $head))
489
+ if ($null -eq $diff -or $diff.ExitCode -ne 0) {
490
+ return [ordered]@{ Exempt = $false; Reason = "DOCS_ONLY_EXEMPTION_DIFF_FAILED: git diff against origin/$base could not be computed." }
491
+ }
492
+ $violation = Get-DocsOnlyDiffTextViolation -DiffText ([string]$diff.Output) -BaseName $base
493
+ if ($violation) {
494
+ return [ordered]@{ Exempt = $false; Reason = $violation }
495
+ }
496
+ return [ordered]@{ Exempt = $true; Reason = $null }
497
+ }
@@ -13,13 +13,14 @@
13
13
  #>
14
14
  [CmdletBinding()]
15
15
  param()
16
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
16
17
 
17
18
  # Shared command-line parser (issue #545). The denylist comparison below runs against the
18
19
  # segment list rather than against the unsegmented command string, which is what keeps the
19
20
  # two runtimes on one implementation of the same concern. This copy has no cd-chained leg
20
21
  # and none is added.
21
- . (Join-Path $PSScriptRoot 'hook-command-scanner.ps1')
22
- . (Join-Path $PSScriptRoot 'hook-command-invocation.ps1')
22
+ try { . (Join-Path $PSScriptRoot 'hook-command-scanner.ps1') } catch { Add-HookDependencyFailure -Name 'hook-command-scanner.ps1' -ErrorRecord $_ }
23
+ try { . (Join-Path $PSScriptRoot 'hook-command-invocation.ps1') } catch { Add-HookDependencyFailure -Name 'hook-command-invocation.ps1' -ErrorRecord $_ }
23
24
 
24
25
  function Get-BlockedBashPattern {
25
26
  [CmdletBinding()]
@@ -263,6 +264,8 @@ function Invoke-ValidateBashDecision {
263
264
  [AllowNull()]
264
265
  [string] $PositionalInput
265
266
  )
267
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'HOOK_DEPENDENCY_LOAD_FAILED:'
268
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
266
269
 
267
270
  $commandToCheck = Get-BashCommandToCheck -ToolInputRaw $ToolInputRaw -PositionalInput $PositionalInput
268
271
 
@@ -298,6 +301,9 @@ function ConvertFrom-CodexBashHookPayload {
298
301
  if ($MyInvocation.InvocationName -eq '.') {
299
302
  return
300
303
  }
304
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('HOOK_DEPENDENCY_LOAD_FAILED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
305
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'HOOK_DEPENDENCY_LOAD_FAILED:'
306
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
301
307
 
302
308
  try {
303
309
  $payload = ConvertFrom-CodexBashHookPayload -PayloadRaw ([Console]::In.ReadToEnd())
@@ -8,8 +8,9 @@
8
8
  #>
9
9
  [CmdletBinding()]
10
10
  param()
11
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
11
12
 
12
- . (Join-Path $PSScriptRoot 'codex-authority-store.ps1')
13
+ try { . (Join-Path $PSScriptRoot 'codex-authority-store.ps1') } catch { Add-HookDependencyFailure -Name 'codex-authority-store.ps1' -ErrorRecord $_ }
13
14
 
14
15
  function ConvertFrom-CodexStopJson {
15
16
  [CmdletBinding()]
@@ -68,6 +69,8 @@ function Invoke-CodexSubagentStopDecision {
68
69
  [Parameter(Mandatory)][string] $PayloadRaw,
69
70
  [AllowNull()][AllowEmptyString()][string] $AttestationRaw
70
71
  )
72
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent SubagentStop -ReasonPrefix 'MODEL_ROUTING_ATTESTATION_BLOCKED:'
73
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
71
74
 
72
75
  $payload = ConvertFrom-CodexStopJson -Raw $PayloadRaw -Name 'SubagentStop input'
73
76
  $agentType = [string]$payload.agent_type
@@ -130,6 +133,9 @@ function Find-CodexStopAttestationRaw {
130
133
  if ($MyInvocation.InvocationName -eq '.') {
131
134
  return
132
135
  }
136
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('MODEL_ROUTING_ATTESTATION_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
137
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent SubagentStop -ReasonPrefix 'MODEL_ROUTING_ATTESTATION_BLOCKED:'
138
+ if ($null -ne $dependencyDecision) { [Console]::Error.WriteLine($dependencyDecision.Reason); exit $dependencyDecision.ExitCode }
133
139
 
134
140
  try {
135
141
  $payloadRaw = [Console]::In.ReadToEnd()
@@ -20,9 +20,14 @@
20
20
  "informational only", "context only", "out of plan scope", "out of scope",
21
21
  "not applicable", "N/A", "UNVERIFIED".
22
22
  - If the coverage artifact for a language is present, repo-wide line coverage is
23
- parsed and compared against the 80 percent floor. When repo-wide coverage is
24
- below 80 percent and the audit does not carry a FAIL verdict on a coverage row
25
- for that language, validation fails.
23
+ parsed and compared against the governing line floor: the line coverage
24
+ threshold the repository's root CLAUDE.md states, otherwise the 85 percent
25
+ default (read by Get-FeatureReviewCoverageThreshold from this directory's
26
+ feature-review-coverage-thresholds.ps1); an absent root CLAUDE.md uses the
27
+ defaults, and an unreadable root CLAUDE.md fails closed. When repo-wide coverage
28
+ is below the governing line floor and the audit does not carry a FAIL verdict on
29
+ a coverage row for that language, validation fails. The Codex hook enforces the
30
+ line floor only.
26
31
 
27
32
  Coverage artifact paths by language:
28
33
  TypeScript coverage/lcov.info (LCOV text)
@@ -40,6 +45,8 @@
40
45
  #>
41
46
  [CmdletBinding()]
42
47
  param()
48
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
49
+ try { . (Join-Path $PSScriptRoot 'feature-review-coverage-thresholds.ps1') } catch { Add-HookDependencyFailure -Name 'feature-review-coverage-thresholds.ps1' -ErrorRecord $_ }
43
50
 
44
51
  $ErrorActionPreference = 'Stop'
45
52
 
@@ -143,7 +150,8 @@ function Test-LanguageCoverageRow {
143
150
  param(
144
151
  [string]$AuditText,
145
152
  [string]$Language,
146
- [Nullable[double]]$RepoWidePct
153
+ [Nullable[double]]$RepoWidePct,
154
+ [double] $LineFloor = 85.0
147
155
  )
148
156
 
149
157
  $languageLabelMap = @{
@@ -192,12 +200,12 @@ function Test-LanguageCoverageRow {
192
200
  }
193
201
  }
194
202
 
195
- if ($null -ne $RepoWidePct -and $RepoWidePct -lt 80.0) {
203
+ if ($null -ne $RepoWidePct -and $RepoWidePct -lt $LineFloor) {
196
204
  $failLines = $coverageLines | Where-Object { $_ -match '\bFAIL\b' }
197
205
  if (-not $failLines -or $failLines.Count -eq 0) {
198
206
  return @{
199
207
  Ok = $false
200
- Reason = ("{0} repo-wide coverage is {1}% (below the 80% floor) but the policy-audit contains no FAIL verdict on a coverage row for {0}." -f $Language, $RepoWidePct)
208
+ Reason = ("{0} repo-wide coverage is {1}% (below the {2}% line coverage floor) but the policy-audit contains no FAIL verdict on a coverage row for {0}." -f $Language, $RepoWidePct, $LineFloor)
201
209
  }
202
210
  }
203
211
  }
@@ -229,6 +237,9 @@ function Get-FeatureReviewCoverageContinuation {
229
237
  if ($MyInvocation.InvocationName -eq '.') {
230
238
  return
231
239
  }
240
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('validate-feature-review-coverage: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
241
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent SubagentStop -ReasonPrefix 'validate-feature-review-coverage:'
242
+ if ($null -ne $dependencyDecision) { [Console]::Error.WriteLine($dependencyDecision.Reason); exit $dependencyDecision.ExitCode }
232
243
 
233
244
  try {
234
245
  $payloadRaw = [Console]::In.ReadToEnd()
@@ -264,11 +275,16 @@ try {
264
275
  exit 0
265
276
  }
266
277
 
278
+ $claudeMdPath = Join-Path $repoRoot 'CLAUDE.md'
279
+ $claudeMdText = $null
280
+ if (Test-Path -Path $claudeMdPath) { try { $claudeMdText = Get-Content -Path $claudeMdPath -Raw -ErrorAction Stop } catch { throw "root CLAUDE.md exists but could not be read ($(([string]$_.Exception.Message -split "`r?`n")[0])); coverage floors cannot be resolved, so the gate fails closed." } }
281
+ $thresholds = Get-FeatureReviewCoverageThreshold -ClaudeMdText $claudeMdText
282
+
267
283
  $failures = [System.Collections.Generic.List[string]]::new()
268
284
 
269
285
  foreach ($lang in $changedLanguages.Keys) {
270
286
  $repoPct = Get-LanguageRepoCoverage -RepoRoot $repoRoot -Language $lang
271
- $result = Test-LanguageCoverageRow -AuditText $auditText -Language $lang -RepoWidePct $repoPct
287
+ $result = Test-LanguageCoverageRow -AuditText $auditText -Language $lang -RepoWidePct $repoPct -LineFloor $thresholds.Line
272
288
  if (-not $result.Ok) {
273
289
  $failures.Add($result.Reason)
274
290
  }
@@ -277,7 +293,7 @@ try {
277
293
  if ($failures.Count -gt 0) {
278
294
  $header = "Feature-review coverage validation failed against branch diff."
279
295
  $body = " - " + ($failures -join "`n - ")
280
- $footer = "Audit file: $($audit.FullName)`nFix the policy-audit to carry an explicit PASS or FAIL verdict per language with changed files, and reflect repo-wide coverage below 80% as FAIL."
296
+ $footer = "Audit file: $($audit.FullName)`nFix the policy-audit to carry an explicit PASS or FAIL verdict per language with changed files, and reflect repo-wide coverage below the governing line coverage floor ($($thresholds.Line)%) as FAIL."
281
297
  $reason = "{0}`n{1}`n{2}" -f $header, $body, $footer
282
298
  Get-FeatureReviewCoverageContinuation -Reason $reason -StopHookActive $stopHookActive |
283
299
  ConvertTo-Json -Compress -Depth 5 |
@@ -4,7 +4,9 @@ description: Run feature review and, if needed, remediation planning with execut
4
4
 
5
5
  $feature-review
6
6
  Use pr_context as authoritative if present and valid. Only fall back to an explicit base branch if pr_context is missing, stale, or ambiguous.
7
- If remediation is required, explicitly spawn atomic-planner to create or revise the remediation plan.
8
- Before atomic-planner finalizes the plan, it must explicitly spawn atomic-executor in preflight-validation-only mode against that same file.
9
- If atomic-executor returns PREFLIGHT: REVISIONS REQUIRED, atomic-planner must revise the same file and repeat until PREFLIGHT: ALL CLEAR.
10
- Only then finalize the remediation plan and report all generated artifacts.
7
+ If remediation is required, write exactly one mode line into the remediation inputs: `Remediation-Mode: proportionate` when every autonomous finding can be fixed without changing a spec acceptance criterion, a public interface, or the file inventory beyond the branch diff plus tests and mirrors, otherwise `Remediation-Mode: design`. CI-failure findings are normally proportionate.
8
+ Then explicitly spawn atomic-planner to create or revise the remediation plan.
9
+ Before atomic-planner finalizes the plan, it must explicitly spawn atomic-executor in preflight-validation-only mode against that same file, with the preflight directive in the delegation prompt and never in the plan file. Preflight is single-pass: trivial defects are fixed in place.
10
+ If atomic-executor returns PREFLIGHT: REVISIONS REQUIRED, atomic-planner must revise the same file once with the full PREFLIGHT-DEFECT list, and one confirming preflight runs; a second substantive result halts the cycle with `blocked_preflight_iteration_limit`.
11
+ For `Remediation-Mode: proportionate`, preflight and execution may run as one atomic-executor delegation that stops with `BLOCKED at preflight (before [P0-T1])` on a substantive defect, which converts the cycle to `Remediation-Mode: design`.
12
+ Only after PREFLIGHT: ALL CLEAR finalize the remediation plan and report all generated artifacts.