@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
@@ -9,7 +9,7 @@
9
9
  structure requirements.
10
10
 
11
11
  Enforced checks:
12
- - hook payload is valid JSON and contains non-empty output,
12
+ - the SubagentStop envelope read from stdin carries a non-empty last_assistant_message,
13
13
  - output advertises `plan-path: <path>` (or supported equivalent forms),
14
14
  - output contains `PREFLIGHT: ALL CLEAR` or
15
15
  `PREFLIGHT: REVISIONS REQUIRED`,
@@ -19,20 +19,31 @@
19
19
  - each task uses `- [ ] [P#-T#]` (or checked equivalent),
20
20
  - task numbering is sequential within each phase,
21
21
  - every task includes at least one explicit path token,
22
- - the final phase contains QA-oriented work.
22
+ - the final phase contains QA-oriented work,
23
+ - the plan carries a complete `## Pre-Mortem` section (Test-PreMortemSection, issue #964),
24
+ - no internal-review `CITATION:` locator is a line reference (issue #964).
23
25
 
24
26
  .NOTES
25
- Reads the hook payload from CLAUDE_HOOK_INPUT as JSON. Exits 0 to allow
26
- termination; exits 1 with an error message to block. Filesystem reads go
27
- through Get-PlanFileContent so tests can mock the boundary without writing
28
- temporary files.
27
+ Reads the SubagentStop envelope from stdin through Read-SubagentStopEnvelope
28
+ (subagentstop-envelope.ps1); the assistant text comes from the helper's
29
+ AssistantText. A non-repairable condition blocks once: when StopHookActive is
30
+ set, the re-entered stop is allowed. Exits 0 to allow termination; a block is
31
+ written to stderr with exit 2 through Exit-SubagentStopBlock. The dependency
32
+ guard and the E1 helper are guarded loads: a failure blocks with exit 2,
33
+ naming the file. Filesystem reads go through Get-PlanFileContent so tests can
34
+ mock the boundary without writing temporary files.
35
+ An unhandled error during validation blocks with exit 2 and a reason (issue #928).
29
36
  #>
30
37
 
31
38
  [CmdletBinding()]
32
39
  param()
40
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
33
41
 
34
42
  Set-StrictMode -Version Latest
43
+ try { . (Join-Path $PSScriptRoot 'subagentstop-envelope.ps1') } catch { Add-HookDependencyFailure -Name 'subagentstop-envelope.ps1' -ErrorRecord $_ }
44
+ try { . (Join-Path $PSScriptRoot 'premortem-section.ps1') } catch { Add-HookDependencyFailure -Name 'premortem-section.ps1' -ErrorRecord $_ }
35
45
  $ErrorActionPreference = 'Stop'
46
+ $script:SubagentStopReportAndHaltLine = 'This condition is not repairable by the subagent. Report it to the operator and halt.'
36
47
 
37
48
  function Get-PlanFileContent {
38
49
  [CmdletBinding()]
@@ -178,6 +189,10 @@ function Get-PlannerInternalReviewValidation {
178
189
  if (-not $citation.Success -or $citation.Groups['Path'].Value -notmatch '^(?![A-Za-z]:)(?!/)(?!\\)(?:\.?[^/\\|\s]+)(?:/[^/\\|\s]+)+$') {
179
190
  return @{ Ok = $false; Message = 'planner internal review citations require a repository-relative path and nonblank locator.' }
180
191
  }
192
+ # Issue #964: a line reference goes stale when main moves (consistent with validator rule G11).
193
+ if ($citation.Groups['Locator'].Value.Trim() -match '(?i)^(?:\d+(?:\s*-\s*\d+)?|#?L\d+(?:-L?\d+)?|lines?\s+\d+.*|[^\s:|]+:\d+(?:-\d+)?)$') {
194
+ return @{ Ok = $false; Message = "planner internal review citation locator ``$($citation.Groups['Locator'].Value.Trim())`` is a line reference; cite a symbol, heading, or test name instead." }
195
+ }
181
196
  }
182
197
 
183
198
  $inventoryLines = @($recordLines | Where-Object { $_ -match '^\s*AC-INVENTORY\s*:' })
@@ -232,7 +247,7 @@ function Get-PlanStructureValidationReport {
232
247
  [OutputType([string[]])]
233
248
  param(
234
249
  [Parameter(Mandatory = $true)]
235
- [string[]] $Lines
250
+ [AllowEmptyString()] [string[]] $Lines
236
251
  )
237
252
 
238
253
  $phasePattern = '^### Phase (?<Phase>\d+)\s+—\s+(?<Title>.+)$'
@@ -243,12 +258,14 @@ function Get-PlanStructureValidationReport {
243
258
  $currentPhase = $null
244
259
  $expectedPhase = 0
245
260
  $foundTask = $false
261
+ $insidePhaseSection = $false
246
262
 
247
263
  for ($i = 0; $i -lt $Lines.Count; $i++) {
248
264
  $lineNumber = $i + 1
249
265
  $line = $Lines[$i]
250
266
 
251
267
  if ($line -match '^### Phase ') {
268
+ $insidePhaseSection = $true
252
269
  $phaseMatch = [regex]::Match($line, $phasePattern)
253
270
  if (-not $phaseMatch.Success) {
254
271
  $errors.Add("Line ${lineNumber}: phase heading must match `### Phase N — <Title>`.")
@@ -269,10 +286,12 @@ function Get-PlanStructureValidationReport {
269
286
  continue
270
287
  }
271
288
 
289
+ if ($line -match '^#{1,2} ') { $insidePhaseSection = $false; continue }
272
290
  if ($line -notmatch '^- \[') {
273
291
  continue
274
292
  }
275
293
 
294
+ if (-not $insidePhaseSection -and $line -notmatch $taskPattern) { continue }
276
295
  $foundTask = $true
277
296
  $taskMatch = [regex]::Match($line, $taskPattern)
278
297
  if (-not $taskMatch.Success) {
@@ -344,54 +363,79 @@ function Get-PlanStructureValidationReport {
344
363
  }
345
364
 
346
365
  function Invoke-PlannerOutputValidation {
366
+ <#
367
+ .SYNOPSIS
368
+ Reads the SubagentStop envelope, validates the assistant text, and applies the re-entry policy.
369
+ #>
347
370
  [CmdletBinding()]
348
371
  [OutputType([hashtable])]
349
372
  param(
350
- [string] $RawPayload
373
+ [AllowNull()] [AllowEmptyString()] [string] $InputText
351
374
  )
352
-
353
- if ([string]::IsNullOrWhiteSpace($RawPayload)) {
354
- return @{ Ok = $false; Message = 'atomic-planner hook: CLAUDE_HOOK_INPUT is empty; cannot validate planner output.' }
355
- }
356
-
357
- try {
358
- $payload = $RawPayload | ConvertFrom-Json -ErrorAction Stop
375
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent SubagentStop -ReasonPrefix 'validate-planner-output:'
376
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
377
+ $envelope = if ($PSBoundParameters.ContainsKey('InputText')) { Read-SubagentStopEnvelope -InputText $InputText } else { Read-SubagentStopEnvelope }
378
+ if (-not $envelope.IsValid) { $decision = @{ Ok = $false; Repairable = $false; Message = ('{0} payload anomaly - {1}' -f 'atomic-planner hook:', (Get-SubagentStopEnvelopeAnomalyReason -Anomaly $envelope.Anomaly)) } }
379
+ else {
380
+ $agentOutput = $envelope.AssistantText
381
+ $decision = Get-PlannerOutputBlockDecision -AgentOutput ([string]$agentOutput)
359
382
  }
360
- catch {
361
- return @{ Ok = $false; Message = "atomic-planner hook: failed to parse CLAUDE_HOOK_INPUT as JSON: $($_.Exception.Message)" }
383
+ if ($decision.Ok) { return @{ Ok = $true; Message = $null } }
384
+ if (-not $decision.Repairable) {
385
+ if ($envelope.StopHookActive) { return @{ Ok = $true; Message = $null } }
386
+ if (-not $decision.Message.Contains('to the operator and halt.')) { $decision.Message = $decision.Message + "`n" + $script:SubagentStopReportAndHaltLine }
362
387
  }
388
+ return @{ Ok = $false; Message = $decision.Message }
389
+ }
390
+
391
+ function Get-PlannerOutputBlockDecision {
392
+ <#
393
+ .SYNOPSIS
394
+ Applies the planner completion checks to the assistant text and returns the block decision.
395
+ .DESCRIPTION
396
+ Returns @{ Ok = $true; Message = $null } to allow, or a hashtable with Ok = $false, a
397
+ Repairable boolean, and Message. Every planner content check is repairable by the subagent.
398
+ #>
399
+ [CmdletBinding()]
400
+ [OutputType([hashtable])]
401
+ param(
402
+ [Parameter(Mandatory = $true)]
403
+ [AllowEmptyString()]
404
+ [string] $AgentOutput
405
+ )
363
406
 
364
- $agentOutput = $null
365
- if ($null -ne $payload.PSObject.Properties['output']) {
366
- $agentOutput = $payload.output
367
- }
368
407
  if ([string]::IsNullOrWhiteSpace($agentOutput)) {
369
- return @{ Ok = $false; Message = 'atomic-planner hook: agent output is empty; planner must return plan-path and final preflight signal.' }
408
+ return @{ Ok = $false; Repairable = $true; Message = 'atomic-planner hook: agent output is empty (last_assistant_message carries no text); planner must return plan-path and final preflight signal.' }
370
409
  }
371
410
 
372
411
  if (-not (Test-HasPreflightSignal -AgentOutput $agentOutput)) {
373
- return @{ Ok = $false; Message = 'atomic-planner hook: agent output must include `PREFLIGHT: ALL CLEAR` or `PREFLIGHT: REVISIONS REQUIRED`.' }
412
+ return @{ Ok = $false; Repairable = $true; Message = 'atomic-planner hook: agent output must include `PREFLIGHT: ALL CLEAR` or `PREFLIGHT: REVISIONS REQUIRED`.' }
374
413
  }
375
414
 
376
415
  $planPath = Get-PlanPathFromOutput -AgentOutput $agentOutput
377
416
  if ($null -eq $planPath) {
378
- return @{ Ok = $false; Message = 'atomic-planner hook: agent output does not advertise a plan-path. Planner must report `plan-path: <path>` per atomic-plan-contract.' }
417
+ return @{ Ok = $false; Repairable = $true; Message = 'atomic-planner hook: agent output does not advertise a plan-path. Planner must report `plan-path: <path>` per atomic-plan-contract.' }
379
418
  }
380
419
 
381
420
  $file = Get-PlanFileContent -Path $planPath
382
421
  if (-not $file.Exists) {
383
- return @{ Ok = $false; Message = "atomic-planner hook: planner advertised plan-path '$planPath' but no file exists at that location." }
422
+ return @{ Ok = $false; Repairable = $true; Message = "atomic-planner hook: planner advertised plan-path '$planPath' but no file exists at that location." }
384
423
  }
385
424
 
386
425
  $errors = @(Get-PlanStructureValidationReport -Lines $file.Lines)
387
426
  if ($errors.Count -gt 0) {
388
427
  $message = "atomic-planner hook: plan '$planPath' violates the atomic plan contract:`n - " + ($errors -join "`n - ")
389
- return @{ Ok = $false; Message = $message }
428
+ return @{ Ok = $false; Repairable = $true; Message = $message }
429
+ }
430
+
431
+ $preMortem = Test-PreMortemSection -Content ($file.Lines -join "`n")
432
+ if (-not $preMortem.Ok) {
433
+ return @{ Ok = $false; Repairable = $true; Message = "atomic-planner hook: plan '$planPath' $($preMortem.Message) See ``## Design-Level Plan Contract (Mandatory)`` in atomic-plan-contract." }
390
434
  }
391
435
 
392
436
  $review = Get-PlannerInternalReviewValidation -AgentOutput $agentOutput
393
437
  if (-not $review.Ok) {
394
- return @{ Ok = $false; Message = "atomic-planner hook: $($review.Message)" }
438
+ return @{ Ok = $false; Repairable = $true; Message = "atomic-planner hook: $($review.Message)" }
395
439
  }
396
440
 
397
441
  return @{ Ok = $true; Message = $null }
@@ -400,11 +444,11 @@ function Invoke-PlannerOutputValidation {
400
444
  if ($MyInvocation.InvocationName -eq '.') {
401
445
  return
402
446
  }
447
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('validate-planner-output: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
448
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent SubagentStop -ReasonPrefix 'validate-planner-output:'
449
+ if ($null -ne $dependencyDecision) { [Console]::Error.WriteLine($dependencyDecision.Reason); exit $dependencyDecision.ExitCode }
403
450
 
404
- $result = Invoke-PlannerOutputValidation -RawPayload $env:CLAUDE_HOOK_INPUT
405
- if (-not $result.Ok) {
406
- Write-Error $result.Message
407
- exit 1
408
- }
409
-
451
+ try { $result = Invoke-PlannerOutputValidation }
452
+ catch { Exit-SubagentStopBlock -Message ('validate-planner-output: validator error: ' + ([string]$_.Exception.Message -split "`r?`n")[0] + '; the gate fails closed.' + "`n" + $script:SubagentStopReportAndHaltLine) }
453
+ if (-not $result.Ok) { Exit-SubagentStopBlock -Message $result.Message }
410
454
  exit 0
@@ -3,18 +3,21 @@
3
3
  SubagentStop hook that verifies the pr-author agent reported a created or updated PR.
4
4
 
5
5
  .DESCRIPTION
6
- Invoked by the Claude Code SubagentStop hook for the pr-author matcher. Reads the agent
7
- transcript JSON from the CLAUDE_HOOK_INPUT environment variable, extracts the .output field,
8
- and confirms the output reports that a pull request was created or updated. The hook allows
9
- (exit 0) when the output contains:
6
+ Invoked by the Claude Code SubagentStop hook for the pr-author matcher. Reads the SubagentStop
7
+ envelope JSON from stdin through the shared envelope helper (subagentstop-envelope.ps1) and
8
+ validates its last_assistant_message field, the pr-author agent's last assistant message. The
9
+ hook allows (exit 0) when that message contains:
10
10
  - a GitHub PR URL (github.com/<owner>/<repo>/pull/<n>), or
11
11
  - a PR reference of the form "PR #<n>", or
12
12
  - a "gh pr create"/"gh pr edit" confirmation that includes a PR number.
13
- The hook blocks (exit 1) when:
14
- - CLAUDE_HOOK_INPUT is empty,
15
- - the input is not valid JSON,
16
- - the .output field is empty, or
17
- - the output contains no PR URL or PR number.
13
+ The hook blocks, writing the reason to stderr and exiting 2 through the shared envelope
14
+ helper, when:
15
+ - the envelope is empty, is not valid JSON, is not a JSON object, or carries no string
16
+ last_assistant_message (reason prefix "PR_AUTHOR_OUTPUT_MALFORMED: payload anomaly"),
17
+ - the last assistant message is blank (PR_AUTHOR_OUTPUT_EMPTY), or
18
+ - the last assistant message contains no PR URL or PR number (PR_AUTHOR_OUTPUT_NO_PR).
19
+ A hook dependency that fails to load blocks with exit 2 and the PR_AUTHOR_OUTPUT_MISSING:
20
+ prefix.
18
21
 
19
22
  .NOTES
20
23
  Compatible with PowerShell 7+. No external module dependencies.
@@ -24,9 +27,12 @@
24
27
  that the referenced PR actually exists on GitHub, and the output text is forgeable by any actor
25
28
  that controls the agent transcript. It MUST NOT be described as tamper-proof or as a security
26
29
  boundary.
30
+ An unhandled error during validation blocks with exit 2 and a reason (issue #928).
27
31
  #>
28
32
  [CmdletBinding()]
29
33
  param()
34
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
35
+ try { . (Join-Path $PSScriptRoot 'subagentstop-envelope.ps1') } catch { Add-HookDependencyFailure -Name 'subagentstop-envelope.ps1' -ErrorRecord $_ }
30
36
 
31
37
  function Test-PrAuthorOutputReportsPr {
32
38
  <#
@@ -36,7 +42,7 @@ function Test-PrAuthorOutputReportsPr {
36
42
  Detection helper (injectable boundary for tests). Matches a GitHub PR URL, a "PR #<n>"
37
43
  reference, or a "gh pr create"/"gh pr edit" confirmation that contains a PR number.
38
44
  .PARAMETER OutputText
39
- The pr-author agent's final output text extracted from CLAUDE_HOOK_INPUT.output.
45
+ The pr-author agent's last assistant message text.
40
46
  .OUTPUTS
41
47
  System.Boolean
42
48
  #>
@@ -71,53 +77,43 @@ function Test-PrAuthorOutputReportsPr {
71
77
  function Get-PrAuthorOutputDecision {
72
78
  <#
73
79
  .SYNOPSIS
74
- Parse CLAUDE_HOOK_INPUT and return an allow-or-block decision with exit code semantics.
80
+ Read the SubagentStop envelope and return an allow-or-block decision.
75
81
  .DESCRIPTION
76
82
  Returns an ordered dictionary with 'allowed' (bool) and 'reason' (string). Allowed is $true
77
- only when the parsed .output reports a PR per Test-PrAuthorOutputReportsPr. Empty input,
78
- malformed JSON, empty output, and PR-less output all yield allowed = $false.
79
- .PARAMETER HookInputRaw
80
- The raw JSON transcript payload supplied by Claude Code via CLAUDE_HOOK_INPUT.
83
+ only when the envelope's last assistant message reports a PR per
84
+ Test-PrAuthorOutputReportsPr. An invalid envelope (payload anomaly), a blank last assistant
85
+ message, and a message without a PR reference all yield allowed = $false.
86
+ .PARAMETER InputText
87
+ The SubagentStop envelope JSON. When the parameter is not bound, Read-SubagentStopEnvelope
88
+ reads the envelope from stdin.
81
89
  .OUTPUTS
82
90
  System.Collections.Specialized.OrderedDictionary
83
91
  #>
84
92
  [CmdletBinding()]
85
93
  [OutputType([System.Collections.Specialized.OrderedDictionary])]
86
94
  param(
87
- [string] $HookInputRaw
95
+ [AllowNull()] [AllowEmptyString()] [string] $InputText
88
96
  )
97
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent SubagentStop -ReasonPrefix 'PR_AUTHOR_OUTPUT_MISSING:'
98
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
99
+ $envelope = if ($PSBoundParameters.ContainsKey('InputText')) { Read-SubagentStopEnvelope -InputText $InputText } else { Read-SubagentStopEnvelope }
100
+ if (-not $envelope.IsValid) { return [ordered]@{ allowed = $false; reason = "PR_AUTHOR_OUTPUT_MALFORMED: payload anomaly - $(Get-SubagentStopEnvelopeAnomalyReason -Anomaly $envelope.Anomaly)" } }
101
+ $assistantMessage = [string]$envelope.AssistantText
89
102
 
90
- if ([string]::IsNullOrWhiteSpace($HookInputRaw)) {
103
+ if ([string]::IsNullOrWhiteSpace($assistantMessage)) {
91
104
  return [ordered]@{
92
105
  allowed = $false
93
- reason = 'PR_AUTHOR_OUTPUT_MISSING: CLAUDE_HOOK_INPUT is empty; the pr-author agent produced no transcript to validate.'
106
+ reason = 'PR_AUTHOR_OUTPUT_EMPTY: the pr-author last assistant message is empty; it must report the created or updated PR URL or number.'
94
107
  }
95
108
  }
96
109
 
97
- try {
98
- $parsed = $HookInputRaw | ConvertFrom-Json -ErrorAction Stop
99
- } catch {
100
- return [ordered]@{
101
- allowed = $false
102
- reason = 'PR_AUTHOR_OUTPUT_MALFORMED: CLAUDE_HOOK_INPUT is not valid JSON.'
103
- }
104
- }
105
-
106
- $outputText = [string]$parsed.output
107
- if ([string]::IsNullOrWhiteSpace($outputText)) {
108
- return [ordered]@{
109
- allowed = $false
110
- reason = 'PR_AUTHOR_OUTPUT_EMPTY: the pr-author agent output is empty; it must report the created or updated PR URL or number.'
111
- }
112
- }
113
-
114
- if (Test-PrAuthorOutputReportsPr -OutputText $outputText) {
110
+ if (Test-PrAuthorOutputReportsPr -OutputText $assistantMessage) {
115
111
  return [ordered]@{ allowed = $true }
116
112
  }
117
113
 
118
114
  return [ordered]@{
119
115
  allowed = $false
120
- reason = 'PR_AUTHOR_OUTPUT_NO_PR: the pr-author agent output does not reference a PR URL or PR number; it must report the created or updated pull request.'
116
+ reason = 'PR_AUTHOR_OUTPUT_NO_PR: the pr-author last assistant message does not reference a PR URL or PR number; it must report the created or updated pull request.'
121
117
  }
122
118
  }
123
119
 
@@ -125,12 +121,11 @@ function Get-PrAuthorOutputDecision {
125
121
  if ($MyInvocation.InvocationName -eq '.') {
126
122
  return
127
123
  }
124
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('PR_AUTHOR_OUTPUT_MISSING: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
125
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent SubagentStop -ReasonPrefix 'PR_AUTHOR_OUTPUT_MISSING:'
126
+ if ($null -ne $dependencyDecision) { [Console]::Error.WriteLine($dependencyDecision.Reason); exit $dependencyDecision.ExitCode }
128
127
 
129
- $decision = Get-PrAuthorOutputDecision -HookInputRaw $env:CLAUDE_HOOK_INPUT
130
-
131
- if (-not $decision['allowed']) {
132
- Write-Error $decision['reason']
133
- exit 1
134
- }
135
-
128
+ try { $decision = Get-PrAuthorOutputDecision }
129
+ catch { Exit-SubagentStopBlock -Message ('PR_AUTHOR_OUTPUT_MISSING: validator error: ' + ([string]$_.Exception.Message -split "`r?`n")[0] + '; the gate fails closed.' + "`n" + 'This condition is not repairable by the subagent. Report it to the operator and halt.') }
130
+ if (-not $decision['allowed']) { Exit-SubagentStopBlock -Message $decision['reason'] }
136
131
  exit 0
@@ -1,10 +1,21 @@
1
1
  <#
2
2
  .SYNOPSIS
3
3
  Validates numeric acceptance criteria produced by the prd-feature worker.
4
+
5
+ .NOTES
6
+ Reads the SubagentStop envelope JSON from stdin through the shared envelope helper
7
+ (subagentstop-envelope.ps1) and validates its last_assistant_message field, the prd-feature
8
+ worker's last assistant message. Exits 0 to allow. A block writes the reason to stderr and
9
+ exits 2 through the shared envelope helper; an invalid envelope blocks with a payload anomaly
10
+ reason. A hook dependency that fails to load blocks with exit 2 and the
11
+ validate-prd-feature-output: prefix.
12
+ An unhandled error during validation blocks with exit 2 and a reason (issue #928).
4
13
  #>
5
14
 
6
15
  [CmdletBinding()]
7
16
  param()
17
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
18
+ try { . (Join-Path $PSScriptRoot 'subagentstop-envelope.ps1') } catch { Add-HookDependencyFailure -Name 'subagentstop-envelope.ps1' -ErrorRecord $_ }
8
19
 
9
20
  Set-StrictMode -Version Latest
10
21
  $ErrorActionPreference = 'Stop'
@@ -38,7 +49,7 @@ function Test-NumericDerivationEvidence {
38
49
  if (-not $match.Success) { return @{ Ok = $false; Message = "prd-feature hook: numeric derivation evidence is missing $label." } }
39
50
  $values[$label] = $match.Groups['value'].Value.Trim()
40
51
  }
41
- if ($values['Exhaustive Search Scope'] -notmatch '(?i)\b(entire|all|complete)\b.*\b(repository|repo|source tree|tree)\b') { return @{ Ok = $false; Message = 'prd-feature hook: numeric derivation evidence does not declare an exhaustive repository search scope.' } }
52
+ if ($values['Exhaustive Search Scope'] -notmatch '(?i)\b(entire|whole|all|complete)\b.*\b(repository|repo|worktree|source tree|tree)\b') { return @{ Ok = $false; Message = 'prd-feature hook: numeric derivation evidence does not declare an exhaustive repository search scope.' } }
42
53
  $primaryStrategy = $values['Primary Search Strategy or Query Expression']
43
54
  $crossCheckStrategy = $values['Cross-check Search Strategy or Query Expression']
44
55
  if ($primaryStrategy -match '(?i)\b(single|narrow|named[- ]?pattern)\b' -or $crossCheckStrategy -match '(?i)\b(single|narrow|named[- ]?pattern)\b') { return @{ Ok = $false; Message = 'prd-feature hook: numeric derivation evidence uses a narrow named-pattern search.' } }
@@ -64,28 +75,46 @@ function Test-SpecNumericCriterion {
64
75
  param([Parameter(Mandatory = $true)][AllowEmptyString()][string] $SpecContent)
65
76
 
66
77
  $section = [regex]::Match($SpecContent, '(?ims)^##\s+Acceptance\s+Criteria\s*$.*?(?=^##\s|\z)')
67
- return $section.Success -and [regex]::IsMatch($section.Value, '(?m)^\s*[-*]\s*\[.\].*\b\d+\b')
78
+ if (-not $section.Success) { return $false }
79
+ # Rule R2 (issue #905): a criterion is numeric only when it states a determiner-led cardinal count of a
80
+ # plural noun and cites no policy file.
81
+ $derivedCountPattern = '\b(?:all|exactly|the|these|those)\s+(?:[1-9]\d*|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty)\s+(?:(?!(?:seconds|minutes|hours|days|weeks|milliseconds|lines|bytes|characters|percent|is|as|us|has|was|does|this|its|thus|plus|less|unless|across|always|yes)\b)[\w`./-]+\s+){0,3}?(?!(?:seconds|minutes|hours|days|weeks|milliseconds|lines|bytes|characters|percent|is|as|us|has|was|does|this|its|thus|plus|less|unless|across|always|yes)\b)[a-z][\w-]*s\b'
82
+ $policyMarkers = @('.claude/rules/', '.github/instructions/', '.github/copilot-instructions.md')
83
+ foreach ($line in ($section.Value -split "`r?`n")) {
84
+ if ($line -notmatch '^\s*[-*]\s*\[.\]') { continue }
85
+ if (-not [regex]::IsMatch($line, $derivedCountPattern, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)) { continue }
86
+ $normalized = $line -replace '\\', '/'
87
+ if (@($policyMarkers | Where-Object { $normalized.Contains($_) }).Count -gt 0) { continue }
88
+ return $true
89
+ }
90
+ return $false
68
91
  }
69
92
 
70
93
  function Invoke-PrdFeatureOutputValidation {
71
94
  [CmdletBinding()]
72
95
  [OutputType([hashtable])]
73
- param([string] $RawPayload)
74
-
75
- if ([string]::IsNullOrWhiteSpace($RawPayload)) { return @{ Ok = $false; Message = 'prd-feature hook: CLAUDE_HOOK_INPUT is empty.' } }
76
- try { $payload = $RawPayload | ConvertFrom-Json -ErrorAction Stop } catch { return @{ Ok = $false; Message = "prd-feature hook: failed to parse CLAUDE_HOOK_INPUT as JSON: $($_.Exception.Message)" } }
77
- $output = if ($null -ne $payload.PSObject.Properties['output']) { $payload.output } else { $null }
78
- if ([string]::IsNullOrWhiteSpace($output)) { return @{ Ok = $false; Message = 'prd-feature hook: agent output is empty.' } }
79
- $specPath = Get-ArtifactPathFromOutput -Output $output -Label 'spec-path'
96
+ param([AllowNull()] [AllowEmptyString()] [string] $InputText)
97
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent SubagentStop -ReasonPrefix 'validate-prd-feature-output:'
98
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
99
+ $envelope = if ($PSBoundParameters.ContainsKey('InputText')) { Read-SubagentStopEnvelope -InputText $InputText } else { Read-SubagentStopEnvelope }
100
+ if (-not $envelope.IsValid) { return @{ Ok = $false; Message = "prd-feature hook: payload anomaly - $(Get-SubagentStopEnvelopeAnomalyReason -Anomaly $envelope.Anomaly)" } }
101
+ $assistantMessage = [string]$envelope.AssistantText
102
+ if ([string]::IsNullOrWhiteSpace($assistantMessage)) { return @{ Ok = $false; Message = 'prd-feature hook: last assistant message is empty.' } }
103
+ $specPath = Get-ArtifactPathFromOutput -Output $assistantMessage -Label 'spec-path'
80
104
  if ([string]::IsNullOrWhiteSpace($specPath) -or -not (Test-Path -LiteralPath $specPath -PathType Leaf)) { return @{ Ok = $false; Message = 'prd-feature hook: spec-path is required and must exist.' } }
81
- $specContent = Get-Content -LiteralPath $specPath -Raw -ErrorAction Stop
105
+ try { $specContent = Get-Content -LiteralPath $specPath -Raw -ErrorAction Stop } catch { return @{ Ok = $false; Message = "prd-feature hook: failed to read spec-path '$specPath': $($_.Exception.Message)" } }
82
106
  if (-not (Test-SpecNumericCriterion -SpecContent $specContent)) { return @{ Ok = $true; Message = $null } }
83
- $researchPath = Get-ArtifactPathFromOutput -Output $output -Label 'research-path'
107
+ $researchPath = Get-ArtifactPathFromOutput -Output $assistantMessage -Label 'research-path'
84
108
  if ([string]::IsNullOrWhiteSpace($researchPath) -or -not (Test-Path -LiteralPath $researchPath -PathType Leaf)) { return @{ Ok = $false; Message = 'prd-feature hook: numeric acceptance criterion requires an existing research-path.' } }
85
- return Test-NumericDerivationEvidence -Content (Get-Content -LiteralPath $researchPath -Raw -ErrorAction Stop)
109
+ try { $researchContent = Get-Content -LiteralPath $researchPath -Raw -ErrorAction Stop } catch { return @{ Ok = $false; Message = "prd-feature hook: failed to read research-path '$researchPath': $($_.Exception.Message)" } }
110
+ return Test-NumericDerivationEvidence -Content $researchContent
86
111
  }
87
112
 
88
113
  if ($MyInvocation.InvocationName -eq '.') { return }
89
- $result = Invoke-PrdFeatureOutputValidation -RawPayload $env:CLAUDE_HOOK_INPUT
90
- if (-not $result.Ok) { Write-Error $result.Message; exit 1 }
114
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('validate-prd-feature-output: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
115
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent SubagentStop -ReasonPrefix 'validate-prd-feature-output:'
116
+ if ($null -ne $dependencyDecision) { [Console]::Error.WriteLine($dependencyDecision.Reason); exit $dependencyDecision.ExitCode }
117
+ try { $result = Invoke-PrdFeatureOutputValidation }
118
+ catch { Exit-SubagentStopBlock -Message ('validate-prd-feature-output: validator error: ' + ([string]$_.Exception.Message -split "`r?`n")[0] + '; the gate fails closed.' + "`n" + 'This condition is not repairable by the subagent. Report it to the operator and halt.') }
119
+ if (-not $result.Ok) { Exit-SubagentStopBlock -Message $result.Message }
91
120
  exit 0
@@ -3,10 +3,10 @@
3
3
  Generic SubagentStop hook for agents that must advertise exact artifact paths.
4
4
 
5
5
  .DESCRIPTION
6
- Validates that the hook payload is well-formed JSON, the agent output is
7
- non-empty, each required artifact token is advertised in the output, the
8
- advertised path matches the configured contract regex, and the file exists
9
- on disk.
6
+ Validates that the SubagentStop envelope read from stdin carries a non-empty
7
+ last_assistant_message, each required artifact token is advertised in that
8
+ text, the advertised path matches the configured contract regex, and the file
9
+ exists on disk.
10
10
 
11
11
  Each required artifact spec must use:
12
12
  token|regex|description
@@ -15,10 +15,17 @@
15
15
  spec-path|^docs/features/active/.+/spec\.md$|spec artifact
16
16
 
17
17
  .NOTES
18
- Reads the hook payload from CLAUDE_HOOK_INPUT as JSON. Exits 0 to allow
19
- termination; exits 1 with an error message to block. Filesystem existence
20
- checks go through Test-ArtifactFile so tests can mock the boundary without
21
- writing temporary files.
18
+ Reads the SubagentStop envelope from stdin through Read-SubagentStopEnvelope
19
+ (subagentstop-envelope.ps1); the assistant text comes from the helper's
20
+ AssistantText. A non-repairable condition blocks once: when StopHookActive is
21
+ set, the re-entered stop is allowed. Exits 0 to allow termination; a block is
22
+ written to stderr with exit 2 through Exit-SubagentStopBlock. A missing
23
+ AgentName or RequiredArtifact argument exits 2 inline and blocks once. The
24
+ dependency guard and the E1 helper are guarded loads: a failure blocks with
25
+ exit 2, naming the file. Filesystem existence checks go through
26
+ Test-ArtifactFile so tests can mock the boundary without writing temporary
27
+ files.
28
+ An unhandled error during validation blocks with exit 2 and a reason (issue #928).
22
29
  #>
23
30
 
24
31
  [CmdletBinding()]
@@ -27,9 +34,12 @@ param(
27
34
 
28
35
  [string[]] $RequiredArtifact
29
36
  )
37
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
30
38
 
31
39
  Set-StrictMode -Version Latest
40
+ try { . (Join-Path $PSScriptRoot 'subagentstop-envelope.ps1') } catch { Add-HookDependencyFailure -Name 'subagentstop-envelope.ps1' -ErrorRecord $_ }
32
41
  $ErrorActionPreference = 'Stop'
42
+ $script:SubagentStopReportAndHaltLine = 'This condition is not repairable by the subagent. Report it to the operator and halt.'
33
43
 
34
44
  function Test-ArtifactFile {
35
45
  [CmdletBinding()]
@@ -90,31 +100,53 @@ function Get-ArtifactPathFromOutput {
90
100
  }
91
101
 
92
102
  function Invoke-RequiredArtifactOutputValidation {
103
+ <#
104
+ .SYNOPSIS
105
+ Reads the SubagentStop envelope, validates the assistant text, and applies the re-entry policy.
106
+ #>
93
107
  [CmdletBinding()]
94
108
  [OutputType([hashtable])]
95
109
  param(
96
- [string] $RawPayload,
110
+ [AllowNull()] [AllowEmptyString()] [string] $InputText,
97
111
  [string] $AgentName,
98
112
  [string[]] $RequiredArtifact
99
113
  )
100
114
 
101
- if ([string]::IsNullOrWhiteSpace($RawPayload)) {
102
- return @{ Ok = $false; Message = "$AgentName hook: CLAUDE_HOOK_INPUT is empty; cannot validate required artifact output." }
115
+ $envelope = if ($PSBoundParameters.ContainsKey('InputText')) { Read-SubagentStopEnvelope -InputText $InputText } else { Read-SubagentStopEnvelope }
116
+ if (-not $envelope.IsValid) { $decision = @{ Ok = $false; Repairable = $false; Message = ('{0} payload anomaly - {1}' -f ("$AgentName hook:"), (Get-SubagentStopEnvelopeAnomalyReason -Anomaly $envelope.Anomaly)) } }
117
+ else {
118
+ $agentOutput = $envelope.AssistantText
119
+ $decision = Get-RequiredArtifactOutputBlockDecision -AgentOutput ([string]$agentOutput) -AgentName $AgentName -RequiredArtifact $RequiredArtifact
103
120
  }
104
-
105
- try {
106
- $payload = $RawPayload | ConvertFrom-Json -ErrorAction Stop
107
- }
108
- catch {
109
- return @{ Ok = $false; Message = "$AgentName hook: failed to parse CLAUDE_HOOK_INPUT as JSON: $($_.Exception.Message)" }
121
+ if ($decision.Ok) { return @{ Ok = $true; Message = $null } }
122
+ if (-not $decision.Repairable) {
123
+ if ($envelope.StopHookActive) { return @{ Ok = $true; Message = $null } }
124
+ if (-not $decision.Message.Contains('to the operator and halt.')) { $decision.Message = $decision.Message + "`n" + $script:SubagentStopReportAndHaltLine }
110
125
  }
126
+ return @{ Ok = $false; Message = $decision.Message }
127
+ }
128
+
129
+ function Get-RequiredArtifactOutputBlockDecision {
130
+ <#
131
+ .SYNOPSIS
132
+ Applies the required-artifact checks to the assistant text and returns the block decision.
133
+ .DESCRIPTION
134
+ Returns @{ Ok = $true; Message = $null } to allow, or a hashtable with Ok = $false, a
135
+ Repairable boolean, and Message. A malformed RequiredArtifact spec is a registration
136
+ defect the subagent cannot clear, so it is not repairable; the other blocks are.
137
+ #>
138
+ [CmdletBinding()]
139
+ [OutputType([hashtable])]
140
+ param(
141
+ [Parameter(Mandatory = $true)]
142
+ [AllowEmptyString()]
143
+ [string] $AgentOutput,
144
+ [string] $AgentName,
145
+ [string[]] $RequiredArtifact
146
+ )
111
147
 
112
- $agentOutput = $null
113
- if ($payload.PSObject.Properties.Name -contains 'output') {
114
- $agentOutput = $payload.output
115
- }
116
148
  if ([string]::IsNullOrWhiteSpace($agentOutput)) {
117
- return @{ Ok = $false; Message = "$AgentName hook: agent output is empty; artifact paths must be reported before termination." }
149
+ return @{ Ok = $false; Repairable = $true; Message = "$AgentName hook: agent output is empty (last_assistant_message carries no text); artifact paths must be reported before termination." }
118
150
  }
119
151
 
120
152
  $errors = [System.Collections.Generic.List[string]]::new()
@@ -123,7 +155,7 @@ function Invoke-RequiredArtifactOutputValidation {
123
155
  $spec = ConvertTo-ArtifactSpec -Spec $specText
124
156
  }
125
157
  catch {
126
- return @{ Ok = $false; Message = "$AgentName hook: $($_.Exception.Message)" }
158
+ return @{ Ok = $false; Repairable = $false; Message = "$AgentName hook: $($_.Exception.Message)" }
127
159
  }
128
160
 
129
161
  $path = Get-ArtifactPathFromOutput -AgentOutput $agentOutput -Token $spec.Token
@@ -144,7 +176,7 @@ function Invoke-RequiredArtifactOutputValidation {
144
176
 
145
177
  if ($errors.Count -gt 0) {
146
178
  $message = "$AgentName hook: required artifact validation failed:`n - " + ($errors -join "`n - ")
147
- return @{ Ok = $false; Message = $message }
179
+ return @{ Ok = $false; Repairable = $true; Message = $message }
148
180
  }
149
181
 
150
182
  return @{ Ok = $true; Message = $null }
@@ -154,18 +186,16 @@ if ($MyInvocation.InvocationName -eq '.') {
154
186
  return
155
187
  }
156
188
 
189
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('validate-required-artifact-output: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
190
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent SubagentStop -ReasonPrefix 'validate-required-artifact-output:'
191
+ if ($null -ne $dependencyDecision) { [Console]::Error.WriteLine($dependencyDecision.Reason); exit $dependencyDecision.ExitCode }
157
192
  if ([string]::IsNullOrWhiteSpace($AgentName) -or $null -eq $RequiredArtifact -or $RequiredArtifact.Count -eq 0) {
158
- Write-Error 'validate-required-artifact-output hook: AgentName and at least one RequiredArtifact spec are required.'
159
- exit 1
160
- }
161
-
162
- $result = Invoke-RequiredArtifactOutputValidation `
163
- -RawPayload $env:CLAUDE_HOOK_INPUT `
164
- -AgentName $AgentName `
165
- -RequiredArtifact $RequiredArtifact
166
- if (-not $result.Ok) {
167
- Write-Error $result.Message
168
- exit 1
193
+ $argumentEnvelope = Read-SubagentStopEnvelope
194
+ if ($argumentEnvelope.StopHookActive) { exit 0 }
195
+ [Console]::Error.WriteLine("validate-required-artifact-output hook: AgentName and at least one RequiredArtifact spec are required.`n$($script:SubagentStopReportAndHaltLine)")
196
+ exit 2
169
197
  }
170
-
198
+ try { $result = Invoke-RequiredArtifactOutputValidation -AgentName $AgentName -RequiredArtifact $RequiredArtifact }
199
+ catch { Exit-SubagentStopBlock -Message ('validate-required-artifact-output: validator error: ' + ([string]$_.Exception.Message -split "`r?`n")[0] + '; the gate fails closed.' + "`n" + $script:SubagentStopReportAndHaltLine) }
200
+ if (-not $result.Ok) { Exit-SubagentStopBlock -Message $result.Message }
171
201
  exit 0