@danmoisan/drm-copilot-mcp 1.1.17 → 1.1.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/out/mcp-server.js +1405 -684
  2. package/package.json +6 -4
  3. package/resources/claude-customizations/.claude/agents/atomic-executor.md +11 -0
  4. package/resources/claude-customizations/.claude/agents/atomic-planner.md +10 -0
  5. package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +12 -4
  6. package/resources/claude-customizations/.claude/agents/epic-planner.md +5 -4
  7. package/resources/claude-customizations/.claude/agents/feature-review.md +16 -5
  8. package/resources/claude-customizations/.claude/agents/orchestrator.md +21 -7
  9. package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +37 -13
  10. package/resources/claude-customizations/.claude/agents/parallel-planner.md +9 -6
  11. package/resources/claude-customizations/.claude/agents/pr-author.md +15 -2
  12. package/resources/claude-customizations/.claude/agents/prd-feature.md +1 -1
  13. package/resources/claude-customizations/.claude/hooks/check-powershell-test-purity.ps1 +7 -1
  14. package/resources/claude-customizations/.claude/hooks/check-python-test-purity.ps1 +7 -1
  15. package/resources/claude-customizations/.claude/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
  16. package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +111 -46
  17. package/resources/claude-customizations/.claude/hooks/enforce-completion-helpers.ps1 +105 -0
  18. package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +8 -1
  19. package/resources/claude-customizations/.claude/hooks/enforce-epic-invocation-origin.ps1 +7 -1
  20. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-resolution.ps1 +116 -59
  21. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +55 -23
  22. package/resources/claude-customizations/.claude/hooks/enforce-epic-wave-barrier.ps1 +74 -75
  23. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate-resolution.ps1 +127 -35
  24. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +61 -57
  25. package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +7 -1
  26. package/resources/claude-customizations/.claude/hooks/enforce-feature-folder-order.ps1 +129 -39
  27. package/resources/claude-customizations/.claude/hooks/enforce-mermaid-validation.ps1 +7 -1
  28. package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +9 -3
  29. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +39 -51
  30. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
  31. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
  32. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
  33. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +17 -12
  34. package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +9 -3
  35. package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier.ps1 +65 -50
  36. package/resources/claude-customizations/.claude/hooks/enforce-parallel-drift-gate.ps1 +49 -42
  37. package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +54 -72
  38. package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +8 -2
  39. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-command-allowlist.ps1 +279 -0
  40. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +129 -75
  41. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.artifact-root.ps1 +233 -0
  42. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.docs-only.ps1 +224 -0
  43. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +69 -24
  44. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner-helpers.ps1 +29 -18
  45. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +10 -4
  46. package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +14 -5
  47. package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +8 -2
  48. package/resources/claude-customizations/.claude/hooks/feature-folder-resolution.ps1 +373 -0
  49. package/resources/claude-customizations/.claude/hooks/feature-review-coverage-thresholds.ps1 +85 -0
  50. package/resources/claude-customizations/.claude/hooks/hook-command-heredoc.ps1 +119 -0
  51. package/resources/claude-customizations/.claude/hooks/hook-command-invocation-operands.ps1 +198 -0
  52. package/resources/claude-customizations/.claude/hooks/hook-command-invocation.ps1 +254 -255
  53. package/resources/claude-customizations/.claude/hooks/hook-command-payload-powershell.ps1 +201 -0
  54. package/resources/claude-customizations/.claude/hooks/hook-command-payload.ps1 +484 -0
  55. package/resources/claude-customizations/.claude/hooks/hook-command-scanner.ps1 +27 -110
  56. package/resources/claude-customizations/.claude/hooks/hook-dependency-guard.ps1 +114 -0
  57. package/resources/claude-customizations/.claude/hooks/hook-docs-only-diff.ps1 +493 -0
  58. package/resources/claude-customizations/.claude/hooks/subagentstop-envelope.ps1 +192 -0
  59. package/resources/claude-customizations/.claude/hooks/validate-bash.ps1 +9 -3
  60. package/resources/claude-customizations/.claude/hooks/validate-discovery-artifact-gate.ps1 +40 -42
  61. package/resources/claude-customizations/.claude/hooks/validate-executor-output.ps1 +66 -38
  62. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-metrics.ps1 +180 -0
  63. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-rules.ps1 +195 -0
  64. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage.ps1 +141 -395
  65. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-resolution.ps1 +311 -0
  66. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-routing.ps1 +170 -0
  67. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +115 -190
  68. package/resources/claude-customizations/.claude/hooks/validate-planner-output.ps1 +64 -32
  69. package/resources/claude-customizations/.claude/hooks/validate-pr-author-output.ps1 +39 -44
  70. package/resources/claude-customizations/.claude/hooks/validate-prd-feature-output.ps1 +43 -14
  71. package/resources/claude-customizations/.claude/hooks/validate-required-artifact-output.ps1 +66 -36
  72. package/resources/claude-customizations/.claude/hooks/validate-task-researcher-output.ps1 +31 -34
  73. package/resources/claude-customizations/.claude/lib/bash/mutate-parallel-run.sh +191 -0
  74. package/resources/claude-customizations/.claude/lib/bash/parallel-cohorts.sh +13 -3
  75. package/resources/claude-customizations/.claude/lib/bash/parallel-lane-assertion.sh +4 -2
  76. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-cli.sh +152 -0
  77. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-ops.sh +193 -0
  78. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation.sh +322 -0
  79. package/resources/claude-customizations/.claude/lib/bash/remove-parallel-item.sh +146 -0
  80. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +23 -33
  81. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusTokenShape.psm1 +86 -6
  82. package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-CiGateParser.ps1 +91 -19
  83. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorState.psm1 +6 -13
  84. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateEpicWaveBarrier.psm1 +346 -0
  85. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1 +396 -0
  86. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateReceipts.psm1 +22 -15
  87. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRemediationAccounting.psm1 +337 -0
  88. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRoutingContract.psm1 +9 -1
  89. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +79 -2
  90. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1 +3 -3
  91. package/resources/claude-customizations/.claude/rules/architecture-boundaries.md +5 -5
  92. package/resources/claude-customizations/.claude/rules/csharp.md +2 -2
  93. package/resources/claude-customizations/.claude/rules/general-code-change.md +1 -1
  94. package/resources/claude-customizations/.claude/rules/general-unit-test.md +3 -2
  95. package/resources/claude-customizations/.claude/rules/orchestrator-state.md +153 -4
  96. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +50 -5
  97. package/resources/claude-customizations/.claude/rules/plan-acceptance-gates.md +47 -8
  98. package/resources/claude-customizations/.claude/rules/quality-tiers.md +11 -7
  99. package/resources/claude-customizations/.claude/rules/shell.md +10 -8
  100. package/resources/claude-customizations/.claude/rules/typescript.md +1 -1
  101. package/resources/claude-customizations/.claude/settings.json +3 -4
  102. package/resources/claude-customizations/.claude/skills/acceptance-criteria-tracking/SKILL.md +9 -1
  103. package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +7 -1
  104. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +11 -3
  105. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup-worktrees.sh +11 -2
  106. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_detached_lib.sh +1 -1
  107. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_enumerate_lib.sh +171 -5
  108. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_preserve_lib.sh +1 -1
  109. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_report_records_lib.sh +6 -43
  110. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_scan_helper.sh +6 -15
  111. package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +45 -11
  112. package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +1 -1
  113. package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +13 -0
  114. package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +10 -0
  115. package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +7 -4
  116. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +42 -16
  117. package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +52 -14
  118. package/resources/claude-customizations/.claude/skills/parallel-close/SKILL.md +22 -9
  119. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +97 -19
  120. package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +63 -7
  121. package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +35 -12
  122. package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +3 -1
  123. package/resources/claude-customizations/.claude/skills/quota-throttling/SKILL.md +1 -1
  124. package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +7 -3
  125. package/resources/claude-customizations/pack-manifests/core.json +25 -0
  126. package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +9 -1
  127. package/resources/codex-and-agents-customizations/.agents/skills/architecture-boundaries/SKILL.md +5 -5
  128. package/resources/codex-and-agents-customizations/.agents/skills/atomic-executor/SKILL.md +11 -0
  129. package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +4 -0
  130. package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +10 -0
  131. package/resources/codex-and-agents-customizations/.agents/skills/csharp/SKILL.md +2 -2
  132. package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +2 -2
  133. package/resources/codex-and-agents-customizations/.agents/skills/epic-orchestrate/SKILL.md +13 -3
  134. package/resources/codex-and-agents-customizations/.agents/skills/epic-plan/SKILL.md +17 -1
  135. package/resources/codex-and-agents-customizations/.agents/skills/epic-run/SKILL.md +2 -1
  136. package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +13 -0
  137. package/resources/codex-and-agents-customizations/.agents/skills/feature-promotion-lifecycle/SKILL.md +1 -1
  138. package/resources/codex-and-agents-customizations/.agents/skills/feature-review/SKILL.md +30 -6
  139. package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +16 -6
  140. package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +1 -1
  141. package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +3 -2
  142. package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +43 -11
  143. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-state/SKILL.md +41 -2
  144. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +19 -4
  145. package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +11 -7
  146. package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +2 -0
  147. package/resources/codex-and-agents-customizations/.agents/skills/translate-claude-to-codex/SKILL.md +1 -1
  148. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp/SKILL.md +2 -2
  149. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +2 -2
  150. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c1.toml +11 -0
  151. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +11 -0
  152. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +11 -0
  153. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +11 -0
  154. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +11 -0
  155. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +11 -0
  156. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +10 -0
  157. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +10 -0
  158. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +10 -0
  159. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +10 -0
  160. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +10 -0
  161. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +10 -0
  162. package/resources/codex-and-agents-customizations/.codex/agents/epic-orchestrator.toml +2 -1
  163. package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +6 -6
  164. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c1.toml +1 -0
  165. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c2.toml +1 -0
  166. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c3-elevated.toml +1 -0
  167. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c3.toml +1 -0
  168. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c4.toml +1 -0
  169. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer.toml +1 -0
  170. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c1.toml +3 -3
  171. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +3 -3
  172. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +3 -3
  173. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +3 -3
  174. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +3 -3
  175. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +3 -3
  176. package/resources/codex-and-agents-customizations/.codex/codex-web-setup.sh +318 -273
  177. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  178. package/resources/codex-and-agents-customizations/.codex/hooks/check-powershell-test-purity.ps1 +7 -1
  179. package/resources/codex-and-agents-customizations/.codex/hooks/check-python-test-purity.ps1 +7 -1
  180. package/resources/codex-and-agents-customizations/.codex/hooks/codex-epic-child-launch-attestation.ps1 +1 -1
  181. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
  182. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-codex-model-routing.ps1 +8 -2
  183. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +97 -44
  184. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-helpers.ps1 +105 -0
  185. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-child-worktree-binding.ps1 +7 -1
  186. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +37 -5
  187. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +6 -0
  188. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-root-invocation.ps1 +7 -1
  189. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-wave-barrier.ps1 +7 -1
  190. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-worktree-removal-gate.ps1 +54 -41
  191. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-evidence-locations.ps1 +7 -1
  192. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +35 -14
  193. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
  194. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
  195. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
  196. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +24 -11
  197. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +8 -2
  198. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +13 -4
  199. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +8 -2
  200. package/resources/codex-and-agents-customizations/.codex/hooks/feature-folder-resolution.ps1 +373 -0
  201. package/resources/codex-and-agents-customizations/.codex/hooks/feature-review-coverage-thresholds.ps1 +85 -0
  202. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-heredoc.ps1 +119 -0
  203. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation-operands.ps1 +198 -0
  204. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation.ps1 +254 -255
  205. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload-powershell.ps1 +201 -0
  206. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload.ps1 +484 -0
  207. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-scanner.ps1 +27 -110
  208. package/resources/codex-and-agents-customizations/.codex/hooks/hook-dependency-guard.ps1 +114 -0
  209. package/resources/codex-and-agents-customizations/.codex/hooks/hook-docs-only-diff.ps1 +493 -0
  210. package/resources/codex-and-agents-customizations/.codex/hooks/validate-bash.ps1 +8 -2
  211. package/resources/codex-and-agents-customizations/.codex/hooks/validate-codex-subagent-routing.ps1 +7 -1
  212. package/resources/codex-and-agents-customizations/.codex/hooks/validate-feature-review-coverage.ps1 +24 -8
  213. package/resources/codex-and-agents-customizations/pack-manifests/core.json +9 -0
  214. package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +2 -2
  215. package/resources/customizations/.github/instructions/csharp-code-change.instructions.md +4 -4
  216. package/resources/customizations/.github/instructions/csharp-unit-test.instructions.md +2 -2
  217. package/resources/customizations/.github/skills/acceptance-criteria-tracking/SKILL.md +9 -1
  218. package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +4 -0
  219. package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +13 -0
  220. package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +1 -1
  221. package/resources/powershell/PoshQC/PoshQC.Coverage.psm1 +314 -0
  222. package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +25 -28
  223. package/resources/powershell/PoshQC/PoshQC.psm1 +1 -0
  224. package/resources/powershell/PoshQC/README.md +8 -2
  225. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +2 -315
@@ -37,13 +37,14 @@ param(
37
37
  [Parameter(Position = 0, Mandatory = $false)]
38
38
  [string]$CommandInput
39
39
  )
40
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
40
41
 
41
- Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force
42
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'HookPayload.psm1' -ErrorRecord $_ }
42
43
  # Shared command-line parser (issue #545). Both detectors below run against the segment
43
44
  # list rather than against the unsegmented command string, so a dangerous phrase quoted
44
45
  # inside a message body is no longer a match and a relocating spelling no longer escapes.
45
- . (Join-Path $PSScriptRoot 'hook-command-scanner.ps1')
46
- . (Join-Path $PSScriptRoot 'hook-command-invocation.ps1')
46
+ try { . (Join-Path $PSScriptRoot 'hook-command-scanner.ps1') } catch { Add-HookDependencyFailure -Name 'hook-command-scanner.ps1' -ErrorRecord $_ }
47
+ try { . (Join-Path $PSScriptRoot 'hook-command-invocation.ps1') } catch { Add-HookDependencyFailure -Name 'hook-command-invocation.ps1' -ErrorRecord $_ }
47
48
 
48
49
  function Get-BlockedBashPattern {
49
50
  [CmdletBinding()]
@@ -417,6 +418,8 @@ function Invoke-ValidateBashDecision {
417
418
  [AllowNull()]
418
419
  [string] $PositionalInput
419
420
  )
421
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'HOOK_DEPENDENCY_LOAD_FAILED:'
422
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
420
423
 
421
424
  $commandToCheck = Get-BashCommandToCheck -ToolInputRaw $ToolInputRaw -PositionalInput $PositionalInput
422
425
 
@@ -431,6 +434,9 @@ function Invoke-ValidateBashDecision {
431
434
  if ($MyInvocation.InvocationName -eq '.') {
432
435
  return
433
436
  }
437
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('HOOK_DEPENDENCY_LOAD_FAILED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
438
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'HOOK_DEPENDENCY_LOAD_FAILED:'
439
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
434
440
 
435
441
  $toolInputRaw = Read-ClaudeHookRawPayload
436
442
 
@@ -5,19 +5,24 @@
5
5
 
6
6
  .DESCRIPTION
7
7
  Invoked by the Claude Code SubagentStop hook under the existing broad
8
- generic-agent matcher group. The hook reads $env:CLAUDE_HOOK_INPUT JSON
9
- containing .output (the terminating subagent's final text), scans that
10
- text for discovery-artifact path references via Get-DiscoveryArtifactType,
11
- and for each recognized reference (when a required-artifact declaration is
12
- present) invokes the discovery validator CLI via
13
- Invoke-DiscoveryValidatorExe.
8
+ generic-agent matcher group. The hook reads the SubagentStop envelope JSON
9
+ from stdin through the shared envelope helper (subagentstop-envelope.ps1),
10
+ takes its last_assistant_message field (the terminating subagent's last
11
+ assistant message), scans that text for discovery-artifact path references
12
+ via Get-DiscoveryArtifactType, and for each recognized reference (when a
13
+ required-artifact declaration is present) invokes the discovery validator
14
+ via Invoke-DiscoveryValidatorExe.
14
15
 
15
16
  Any referenced artifact that fails validation blocks the subagent's
16
- termination: the hook writes an error with a
17
- DISCOVERY_ARTIFACT_GATE_BLOCKED: prefix and exits with a non-zero code.
18
- When no reference fails validation, or when no discovery-artifact path is
19
- referenced, or when the required-artifact declaration is absent
20
- (fail-open), the hook allows termination.
17
+ termination: the hook writes the reason with a
18
+ DISCOVERY_ARTIFACT_GATE_BLOCKED: prefix to stderr and exits 2 through the
19
+ shared envelope helper. An invalid envelope (empty, not valid JSON, not a
20
+ JSON object, or without a string last_assistant_message) also blocks, with
21
+ a "DISCOVERY_ARTIFACT_GATE_BLOCKED: payload anomaly" reason. The hook
22
+ allows termination when the last assistant message is blank, when it
23
+ references no discovery-artifact path, when the required-artifact
24
+ declaration is absent (fail-open), or when every referenced artifact
25
+ passes validation.
21
26
 
22
27
  This hook provides the authoritative, defense-in-depth check of final
23
28
  workspace state, regardless of which tool produced the artifact,
@@ -32,9 +37,13 @@
32
37
  `Test-Json -SchemaFile` Draft 2020-12 support). Read-only validation gate that
33
38
  invokes NO external process: validation runs in-process through
34
39
  `.claude/lib/discovery-validation/DiscoveryValidation.psm1` (issue #475).
40
+ An unhandled error during validation blocks with exit 2 and a reason (issue #928).
35
41
  #>
36
42
  [CmdletBinding()]
37
43
  param()
44
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
45
+ try { . (Join-Path $PSScriptRoot 'subagentstop-envelope.ps1') } catch { Add-HookDependencyFailure -Name 'subagentstop-envelope.ps1' -ErrorRecord $_ }
46
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/discovery-validation/DiscoveryValidation.psm1') -Force -Global -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'DiscoveryValidation.psm1' -ErrorRecord $_ }
38
47
 
39
48
  function Invoke-DiscoveryValidatorExe {
40
49
  <#
@@ -179,7 +188,7 @@ function Find-DiscoveryArtifactReference {
179
188
  $foundReferences = New-Object System.Collections.Generic.List[string]
180
189
  foreach ($token in $tokens) {
181
190
  $trimmed = $token.Trim('`', '"', "'", ',', ';', '(', ')', '[', ']')
182
- if (Get-DiscoveryArtifactType -Path $trimmed) {
191
+ if ($trimmed -and (Get-DiscoveryArtifactType -Path $trimmed)) {
183
192
  if (-not $foundReferences.Contains($trimmed)) {
184
193
  $foundReferences.Add($trimmed)
185
194
  }
@@ -192,35 +201,24 @@ function Find-DiscoveryArtifactReference {
192
201
  function Invoke-DiscoveryArtifactGateValidation {
193
202
  <#
194
203
  .SYNOPSIS
195
- Parses CLAUDE_HOOK_INPUT and returns an Ok/Message validation result
196
- for a discovery-artifact completion gate.
204
+ Reads the SubagentStop envelope and returns an Ok/Message validation
205
+ result for a discovery-artifact completion gate.
197
206
  #>
198
207
  [CmdletBinding()]
199
208
  [OutputType([hashtable])]
200
209
  param(
201
- [string] $RawPayload,
210
+ [AllowNull()] [AllowEmptyString()] [string] $InputText,
202
211
 
203
212
  [Parameter(Mandatory = $false)]
204
213
  [scriptblock] $RequiredArtifactReader = { Get-RequiredDiscoveryArtifactDeclaration }
205
214
  )
215
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent SubagentStop -ReasonPrefix 'DISCOVERY_ARTIFACT_GATE_BLOCKED:'
216
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
217
+ $envelope = if ($PSBoundParameters.ContainsKey('InputText')) { Read-SubagentStopEnvelope -InputText $InputText } else { Read-SubagentStopEnvelope }
218
+ if (-not $envelope.IsValid) { return @{ Ok = $false; Message = "DISCOVERY_ARTIFACT_GATE_BLOCKED: payload anomaly - $(Get-SubagentStopEnvelopeAnomalyReason -Anomaly $envelope.Anomaly)" } }
219
+ $assistantMessage = [string]$envelope.AssistantText
206
220
 
207
- if ([string]::IsNullOrWhiteSpace($RawPayload)) {
208
- return @{ Ok = $false; Message = 'discovery artifact gate hook: CLAUDE_HOOK_INPUT is empty' }
209
- }
210
-
211
- try {
212
- $payload = $RawPayload | ConvertFrom-Json -ErrorAction Stop
213
- }
214
- catch {
215
- return @{ Ok = $false; Message = "discovery artifact gate hook: CLAUDE_HOOK_INPUT is not valid JSON: $_" }
216
- }
217
-
218
- $outputText = ''
219
- if ($null -ne $payload -and ($payload.PSObject.Properties.Name -contains 'output')) {
220
- $outputText = [string]$payload.output
221
- }
222
-
223
- $references = Find-DiscoveryArtifactReference -OutputText $outputText
221
+ $references = Find-DiscoveryArtifactReference -OutputText $assistantMessage
224
222
  if ($references.Count -eq 0) {
225
223
  return @{ Ok = $true; Message = $null }
226
224
  }
@@ -233,10 +231,10 @@ function Invoke-DiscoveryArtifactGateValidation {
233
231
 
234
232
  foreach ($reference in $references) {
235
233
  $artifactType = Get-DiscoveryArtifactType -Path $reference
236
- $result = Invoke-DiscoveryValidatorExe -ValidatorArgs @($artifactType, $reference)
237
- $hasErrorOutput = -not [string]::IsNullOrWhiteSpace($result.Output)
238
- if ($result.ExitCode -ne 0 -or $hasErrorOutput) {
239
- return @{ Ok = $false; Message = "DISCOVERY_ARTIFACT_GATE_BLOCKED: $($result.Output)" }
234
+ $validatorResult = Invoke-DiscoveryValidatorExe -ValidatorArgs @($artifactType, $reference)
235
+ $hasErrorOutput = -not [string]::IsNullOrWhiteSpace($validatorResult['Output'])
236
+ if ($validatorResult['ExitCode'] -ne 0 -or $hasErrorOutput) {
237
+ return @{ Ok = $false; Message = "DISCOVERY_ARTIFACT_GATE_BLOCKED: $($validatorResult['Output'])" }
240
238
  }
241
239
  }
242
240
 
@@ -247,11 +245,11 @@ function Invoke-DiscoveryArtifactGateValidation {
247
245
  if ($MyInvocation.InvocationName -eq '.') {
248
246
  return
249
247
  }
248
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('DISCOVERY_ARTIFACT_GATE_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
249
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent SubagentStop -ReasonPrefix 'DISCOVERY_ARTIFACT_GATE_BLOCKED:'
250
+ if ($null -ne $dependencyDecision) { [Console]::Error.WriteLine($dependencyDecision.Reason); exit $dependencyDecision.ExitCode }
250
251
 
251
- $result = Invoke-DiscoveryArtifactGateValidation -RawPayload $env:CLAUDE_HOOK_INPUT
252
- if (-not $result.Ok) {
253
- Write-Error $result.Message
254
- exit 1
255
- }
256
-
252
+ try { $result = Invoke-DiscoveryArtifactGateValidation }
253
+ catch { Exit-SubagentStopBlock -Message ('DISCOVERY_ARTIFACT_GATE_BLOCKED: 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.') }
254
+ if (-not $result.Ok) { Exit-SubagentStopBlock -Message $result.Message }
257
255
  exit 0
@@ -7,7 +7,7 @@
7
7
  plan state satisfy the executor's mechanical contract.
8
8
 
9
9
  Enforced checks:
10
- - hook payload is valid JSON and contains non-empty output,
10
+ - the SubagentStop envelope read from stdin carries a non-empty last_assistant_message,
11
11
  - output advertises `plan-path: <path>` (or supported equivalent forms),
12
12
  - the advertised plan exists on disk,
13
13
  - preflight-only runs return an exact preflight signal and may not claim
@@ -20,17 +20,25 @@
20
20
  output includes a PASS or FAIL status line for that language.
21
21
 
22
22
  .NOTES
23
- Reads the hook payload from CLAUDE_HOOK_INPUT as JSON. Exits 0 to allow
24
- termination; exits 1 with an error message to block. Plan file contents are
25
- read through Get-PlanFileContent so tests can mock the filesystem boundary
26
- without writing temporary files.
23
+ Reads the SubagentStop envelope from stdin through Read-SubagentStopEnvelope
24
+ (subagentstop-envelope.ps1); the assistant text comes from the helper's
25
+ AssistantText. A non-repairable condition blocks once: when StopHookActive is
26
+ set, the re-entered stop is allowed. Exits 0 to allow termination; a block is
27
+ written to stderr with exit 2 through Exit-SubagentStopBlock. The dependency
28
+ guard and the E1 helper are guarded loads: a failure blocks with exit 2,
29
+ naming the file. Plan file contents are read through Get-PlanFileContent so
30
+ tests can mock the filesystem boundary without writing temporary files.
31
+ An unhandled error during validation blocks with exit 2 and a reason (issue #928).
27
32
  #>
28
33
 
29
34
  [CmdletBinding()]
30
35
  param()
36
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
31
37
 
32
38
  Set-StrictMode -Version Latest
39
+ try { . (Join-Path $PSScriptRoot 'subagentstop-envelope.ps1') } catch { Add-HookDependencyFailure -Name 'subagentstop-envelope.ps1' -ErrorRecord $_ }
33
40
  $ErrorActionPreference = 'Stop'
41
+ $script:SubagentStopReportAndHaltLine = 'This condition is not repairable by the subagent. Report it to the operator and halt.'
34
42
 
35
43
  function Get-PlanFileContent {
36
44
  [CmdletBinding()]
@@ -83,7 +91,7 @@ function Test-IsPreflightPlan {
83
91
  [OutputType([bool])]
84
92
  param(
85
93
  [Parameter(Mandatory = $true)]
86
- [string[]] $Lines
94
+ [AllowEmptyString()] [string[]] $Lines
87
95
  )
88
96
 
89
97
  return @($Lines | Where-Object { $_ -match '(?i)^\s*DIRECTIVE:\s*PREFLIGHT VALIDATION ONLY\s*$' }).Count -gt 0
@@ -94,7 +102,7 @@ function Get-TouchedLanguagesFromPlan {
94
102
  [OutputType([string[]])]
95
103
  param(
96
104
  [Parameter(Mandatory = $true)]
97
- [string[]] $Lines
105
+ [AllowEmptyString()] [string[]] $Lines
98
106
  )
99
107
 
100
108
  $languages = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
@@ -176,54 +184,74 @@ function Test-HasCanonicalBlockedText {
176
184
  }
177
185
 
178
186
  function Invoke-ExecutorOutputValidation {
187
+ <#
188
+ .SYNOPSIS
189
+ Reads the SubagentStop envelope, validates the assistant text, and applies the re-entry policy.
190
+ #>
179
191
  [CmdletBinding()]
180
192
  [OutputType([hashtable])]
181
193
  param(
182
- [string] $RawPayload
194
+ [AllowNull()] [AllowEmptyString()] [string] $InputText
183
195
  )
184
196
 
185
- if ([string]::IsNullOrWhiteSpace($RawPayload)) {
186
- return @{ Ok = $false; Message = 'atomic-executor hook: CLAUDE_HOOK_INPUT is empty; cannot validate executor output.' }
197
+ $envelope = if ($PSBoundParameters.ContainsKey('InputText')) { Read-SubagentStopEnvelope -InputText $InputText } else { Read-SubagentStopEnvelope }
198
+ if (-not $envelope.IsValid) { $decision = @{ Ok = $false; Repairable = $false; Message = ('{0} payload anomaly - {1}' -f 'atomic-executor hook:', (Get-SubagentStopEnvelopeAnomalyReason -Anomaly $envelope.Anomaly)) } }
199
+ else {
200
+ $agentOutput = $envelope.AssistantText
201
+ $decision = Get-ExecutorOutputBlockDecision -AgentOutput ([string]$agentOutput)
187
202
  }
188
-
189
- try {
190
- $payload = $RawPayload | ConvertFrom-Json -ErrorAction Stop
191
- }
192
- catch {
193
- return @{ Ok = $false; Message = "atomic-executor hook: failed to parse CLAUDE_HOOK_INPUT as JSON: $($_.Exception.Message)" }
203
+ if ($decision.Ok) { return @{ Ok = $true; Message = $null } }
204
+ if (-not $decision.Repairable) {
205
+ if ($envelope.StopHookActive) { return @{ Ok = $true; Message = $null } }
206
+ if (-not $decision.Message.Contains('to the operator and halt.')) { $decision.Message = $decision.Message + "`n" + $script:SubagentStopReportAndHaltLine }
194
207
  }
208
+ return @{ Ok = $false; Message = $decision.Message }
209
+ }
210
+
211
+ function Get-ExecutorOutputBlockDecision {
212
+ <#
213
+ .SYNOPSIS
214
+ Applies the executor completion checks to the assistant text and returns the block decision.
215
+ .DESCRIPTION
216
+ Returns @{ Ok = $true; Message = $null } to allow, or a hashtable with Ok = $false, a
217
+ Repairable boolean, and Message. Repairable is $false for a non-preflight BLOCKED claim
218
+ and for a plan with no task checkboxes; every other block is repairable by the subagent.
219
+ #>
220
+ [CmdletBinding()]
221
+ [OutputType([hashtable])]
222
+ param(
223
+ [Parameter(Mandatory = $true)]
224
+ [AllowEmptyString()]
225
+ [string] $AgentOutput
226
+ )
195
227
 
196
- $agentOutput = $null
197
- if ($payload.PSObject.Properties.Name -contains 'output') {
198
- $agentOutput = $payload.output
199
- }
200
228
  if ([string]::IsNullOrWhiteSpace($agentOutput)) {
201
- return @{ Ok = $false; Message = 'atomic-executor hook: agent output is empty; executor must return plan-path and final completion summary.' }
229
+ return @{ Ok = $false; Repairable = $true; Message = 'atomic-executor hook: agent output is empty (last_assistant_message carries no text); executor must return plan-path and final completion summary.' }
202
230
  }
203
231
 
204
232
  $planPath = Get-PlanPathFromOutput -AgentOutput $agentOutput
205
233
  if ($null -eq $planPath) {
206
- return @{ Ok = $false; Message = 'atomic-executor hook: agent output does not advertise a plan-path. Executor must report `plan-path: <path>` per atomic-plan-contract.' }
234
+ return @{ Ok = $false; Repairable = $true; Message = 'atomic-executor hook: agent output does not advertise a plan-path. Executor must report `plan-path: <path>` per atomic-plan-contract.' }
207
235
  }
208
236
 
209
237
  $file = Get-PlanFileContent -Path $planPath
210
238
  if (-not $file.Exists) {
211
- return @{ Ok = $false; Message = "atomic-executor hook: executor advertised plan-path '$planPath' but no file exists at that location." }
239
+ return @{ Ok = $false; Repairable = $true; Message = "atomic-executor hook: executor advertised plan-path '$planPath' but no file exists at that location." }
212
240
  }
213
241
 
214
242
  $isPreflight = Test-IsPreflightPlan -Lines $file.Lines
215
243
  if ($isPreflight) {
216
244
  if (-not (Test-HasCanonicalBlockedText -AgentOutput $agentOutput)) {
217
- return @{ Ok = $false; Message = 'atomic-executor hook: blocked preflight output must include the exact text `BLOCKED at preflight (before [P0-T1])` plus a concrete plan delta.' }
245
+ return @{ Ok = $false; Repairable = $true; Message = 'atomic-executor hook: blocked preflight output must include the exact text `BLOCKED at preflight (before [P0-T1])` plus a concrete plan delta.' }
218
246
  }
219
247
  if ($agentOutput -notmatch 'BLOCKED' -and -not (Test-HasPreflightSignal -AgentOutput $agentOutput)) {
220
- return @{ Ok = $false; Message = 'atomic-executor hook: preflight output must include `PREFLIGHT: ALL CLEAR` or `PREFLIGHT: REVISIONS REQUIRED`.' }
248
+ return @{ Ok = $false; Repairable = $true; Message = 'atomic-executor hook: preflight output must include `PREFLIGHT: ALL CLEAR` or `PREFLIGHT: REVISIONS REQUIRED`.' }
221
249
  }
222
250
  return @{ Ok = $true; Message = $null }
223
251
  }
224
252
 
225
253
  if ($agentOutput -match 'BLOCKED') {
226
- return @{ Ok = $false; Message = 'atomic-executor hook: blocking is permitted only during preflight validation (before [P0-T1]).' }
254
+ return @{ Ok = $false; Repairable = $false; Message = 'atomic-executor hook: blocking is permitted only during preflight validation (before [P0-T1]).' }
227
255
  }
228
256
 
229
257
  $checkboxLineCount = 0
@@ -243,7 +271,7 @@ function Invoke-ExecutorOutputValidation {
243
271
  }
244
272
 
245
273
  if ($checkboxLineCount -eq 0) {
246
- return @{ Ok = $false; Message = "atomic-executor hook: plan file '$planPath' contains no task checkboxes; executor cannot complete against an empty plan." }
274
+ return @{ Ok = $false; Repairable = $false; Message = "atomic-executor hook: plan file '$planPath' contains no task checkboxes; executor cannot complete against an empty plan." }
247
275
  }
248
276
 
249
277
  if ($uncheckedLineNumbers.Count -gt 0) {
@@ -255,17 +283,17 @@ function Invoke-ExecutorOutputValidation {
255
283
  }
256
284
  $lineList = ($reported -join ', ')
257
285
  $message = "atomic-executor hook: plan file '$planPath' has $($uncheckedLineNumbers.Count) unchecked task(s) at line(s) ${lineList}${suffix}. All `- [ ]` items must be ticked to `- [x]` before termination."
258
- return @{ Ok = $false; Message = $message }
286
+ return @{ Ok = $false; Repairable = $true; Message = $message }
259
287
  }
260
288
 
261
289
  if ($agentOutput -notmatch '(?i)\bAC Status Summary\b') {
262
- return @{ Ok = $false; Message = 'atomic-executor hook: completion output must include an `AC Status Summary` section.' }
290
+ return @{ Ok = $false; Repairable = $true; Message = 'atomic-executor hook: completion output must include an `AC Status Summary` section.' }
263
291
  }
264
292
 
265
- $hasCommandEvidence = $agentOutput -match '(?i)(Commands Run|Command[s]?:|poetry run |npx |pwsh |git |mcp__drm-copilot__)'
293
+ $hasCommandEvidence = $agentOutput -match '(?i)(Commands Run|Command[s]?:|poetry[ ]run |npx |pwsh |git |mcp__drm-copilot__)'
266
294
  $hasStatusEvidence = $agentOutput -match '(?i)\b(PASS|FAIL)\b'
267
295
  if (-not $hasCommandEvidence -or -not $hasStatusEvidence) {
268
- return @{ Ok = $false; Message = 'atomic-executor hook: completion output must report commands run and pass/fail status results.' }
296
+ return @{ Ok = $false; Repairable = $true; Message = 'atomic-executor hook: completion output must report commands run and pass/fail status results.' }
269
297
  }
270
298
 
271
299
  $missingLanguageStatuses = [System.Collections.Generic.List[string]]::new()
@@ -277,7 +305,7 @@ function Invoke-ExecutorOutputValidation {
277
305
 
278
306
  if ($missingLanguageStatuses.Count -gt 0) {
279
307
  $missingList = $missingLanguageStatuses -join ', '
280
- return @{ Ok = $false; Message = "atomic-executor hook: completion output is missing explicit PASS/FAIL toolchain status lines for: $missingList." }
308
+ return @{ Ok = $false; Repairable = $true; Message = "atomic-executor hook: completion output is missing explicit PASS/FAIL toolchain status lines for: $missingList." }
281
309
  }
282
310
 
283
311
  return @{ Ok = $true; Message = $null }
@@ -287,10 +315,10 @@ if ($MyInvocation.InvocationName -eq '.') {
287
315
  return
288
316
  }
289
317
 
290
- $result = Invoke-ExecutorOutputValidation -RawPayload $env:CLAUDE_HOOK_INPUT
291
- if (-not $result.Ok) {
292
- Write-Error $result.Message
293
- exit 1
294
- }
295
-
318
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('validate-executor-output: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
319
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent SubagentStop -ReasonPrefix 'validate-executor-output:'
320
+ if ($null -ne $dependencyDecision) { [Console]::Error.WriteLine($dependencyDecision.Reason); exit $dependencyDecision.ExitCode }
321
+ try { $result = Invoke-ExecutorOutputValidation }
322
+ catch { Exit-SubagentStopBlock -Message ('validate-executor-output: validator error: ' + ([string]$_.Exception.Message -split "`r?`n")[0] + '; the gate fails closed.' + "`n" + $script:SubagentStopReportAndHaltLine) }
323
+ if (-not $result.Ok) { Exit-SubagentStopBlock -Message $result.Message }
296
324
  exit 0
@@ -0,0 +1,180 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Artifact reader and coverage-report parsers for the feature-review coverage hook.
4
+
5
+ .DESCRIPTION
6
+ Dot-sourced by validate-feature-review-coverage.ps1. Defines the review-artifact file reader
7
+ (Get-ArtifactFileContent), the LCOV and JaCoCo line and branch coverage parsers, and the
8
+ per-language dispatchers that select a coverage report for a changed language.
9
+
10
+ The file has no param() block, writes nothing to the output stream, and runs no top-level
11
+ statement other than function definitions.
12
+
13
+ .NOTES
14
+ Requires PowerShell 7+.
15
+ #>
16
+
17
+ function Get-ArtifactFileContent {
18
+ [CmdletBinding()]
19
+ [OutputType([hashtable])]
20
+ param(
21
+ [Parameter(Mandatory = $true)]
22
+ [string] $Path
23
+ )
24
+
25
+ if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
26
+ return @{ Exists = $false; Text = $null; Lines = @() }
27
+ }
28
+
29
+ $text = Get-Content -LiteralPath $Path -Raw -ErrorAction Stop
30
+ $lines = Get-Content -LiteralPath $Path -ErrorAction Stop
31
+ if ($null -eq $lines) {
32
+ $lines = @()
33
+ }
34
+ elseif ($lines -isnot [array]) {
35
+ $lines = @($lines)
36
+ }
37
+
38
+ return @{ Exists = $true; Text = $text; Lines = $lines }
39
+ }
40
+
41
+ function Get-FeatureReviewClaudeMdText {
42
+ <#
43
+ .SYNOPSIS
44
+ Reads the repository's root CLAUDE.md for the coverage-floor lookup (CR-4).
45
+ .DESCRIPTION
46
+ Returns a hashtable with Text and ReadError. An absent file yields Text = $null and
47
+ ReadError = $null, so the caller applies the default floors. A file that exists but
48
+ cannot be read yields Text = $null and ReadError set to the first line of the
49
+ exception message, so the caller fails closed. The function never throws.
50
+ #>
51
+ [CmdletBinding()]
52
+ [OutputType([hashtable])]
53
+ param()
54
+
55
+ try {
56
+ $file = Get-ArtifactFileContent -Path 'CLAUDE.md'
57
+ return @{ Text = $file.Text; ReadError = $null }
58
+ }
59
+ catch {
60
+ return @{ Text = $null; ReadError = ([string]$_.Exception.Message -split "`r?`n")[0] }
61
+ }
62
+ }
63
+
64
+ function Get-LcovRepoCoverage {
65
+ [OutputType([Nullable[double]])]
66
+ param([string]$Path)
67
+
68
+ $file = Get-ArtifactFileContent -Path $Path
69
+ if (-not $file.Exists) { return $null }
70
+
71
+ $totalFound = 0
72
+ $totalHit = 0
73
+ foreach ($line in $file.Lines) {
74
+ if ($line.StartsWith('LF:')) {
75
+ $totalFound += [int]($line.Substring(3))
76
+ }
77
+ elseif ($line.StartsWith('LH:')) {
78
+ $totalHit += [int]($line.Substring(3))
79
+ }
80
+ }
81
+ if ($totalFound -le 0) { return $null }
82
+ return [math]::Round(($totalHit * 100.0) / $totalFound, 2)
83
+ }
84
+
85
+ function Get-LcovBranchCoverage {
86
+ # Parses LCOV branch counters (BRF: branches found, BRH: branches hit) and returns a percent.
87
+ # LCOV emits one BRF/BRH line per source file in the report; we sum across the file to compute
88
+ # repo-wide branch coverage. Returns $null when the artifact is absent or when no branch
89
+ # information is recorded (BRF total = 0).
90
+ [OutputType([Nullable[double]])]
91
+ param([string]$Path)
92
+
93
+ $file = Get-ArtifactFileContent -Path $Path
94
+ if (-not $file.Exists) { return $null }
95
+
96
+ $totalFound = 0
97
+ $totalHit = 0
98
+ foreach ($line in $file.Lines) {
99
+ if ($line.StartsWith('BRF:')) {
100
+ $totalFound += [int]($line.Substring(4))
101
+ }
102
+ elseif ($line.StartsWith('BRH:')) {
103
+ $totalHit += [int]($line.Substring(4))
104
+ }
105
+ }
106
+ if ($totalFound -le 0) { return $null }
107
+ return [math]::Round(($totalHit * 100.0) / $totalFound, 2)
108
+ }
109
+
110
+ function Get-JacocoBranchCoverage {
111
+ [OutputType([Nullable[double]])]
112
+ param([string]$Path)
113
+
114
+ $file = Get-ArtifactFileContent -Path $Path
115
+ if (-not $file.Exists) { return $null }
116
+
117
+ [xml]$doc = $file.Text
118
+ $counters = $doc.SelectNodes('//counter[@type="BRANCH"]')
119
+ if (-not $counters -or $counters.Count -eq 0) { return $null }
120
+
121
+ $missed = 0
122
+ $covered = 0
123
+ foreach ($counter in $counters) {
124
+ $missed += [int]$counter.missed
125
+ $covered += [int]$counter.covered
126
+ }
127
+ $total = $missed + $covered
128
+ if ($total -le 0) { return $null }
129
+ return [math]::Round(($covered * 100.0) / $total, 2)
130
+ }
131
+
132
+ function Get-LanguageBranchCoverage {
133
+ [OutputType([Nullable[double]])]
134
+ param([string]$Language)
135
+
136
+ switch ($Language) {
137
+ 'TypeScript' { return Get-LcovBranchCoverage -Path 'coverage/lcov.info' }
138
+ 'Python' { return Get-LcovBranchCoverage -Path 'artifacts/python/lcov.info' }
139
+ 'PowerShell' { return Get-JacocoBranchCoverage -Path 'artifacts/pester/powershell-coverage.xml' }
140
+ 'CSharp' { return Get-JacocoBranchCoverage -Path 'artifacts/csharp/coverage.xml' }
141
+ }
142
+ return $null
143
+ }
144
+
145
+ function Get-JacocoRepoCoverage {
146
+ [OutputType([Nullable[double]])]
147
+ param([string]$Path)
148
+
149
+ $file = Get-ArtifactFileContent -Path $Path
150
+ if (-not $file.Exists) { return $null }
151
+
152
+ [xml]$doc = $file.Text
153
+ $counters = $doc.SelectNodes('//counter[@type="LINE"]')
154
+ if (-not $counters -or $counters.Count -eq 0) { return $null }
155
+
156
+ $missed = 0
157
+ $covered = 0
158
+ foreach ($counter in $counters) {
159
+ $missed += [int]$counter.missed
160
+ $covered += [int]$counter.covered
161
+ }
162
+ $total = $missed + $covered
163
+ if ($total -le 0) { return $null }
164
+ return [math]::Round(($covered * 100.0) / $total, 2)
165
+ }
166
+
167
+ function Get-LanguageRepoCoverage {
168
+ [OutputType([Nullable[double]])]
169
+ param(
170
+ [string]$Language
171
+ )
172
+
173
+ switch ($Language) {
174
+ 'TypeScript' { return Get-LcovRepoCoverage -Path 'coverage/lcov.info' }
175
+ 'Python' { return Get-LcovRepoCoverage -Path 'artifacts/python/lcov.info' }
176
+ 'PowerShell' { return Get-JacocoRepoCoverage -Path 'artifacts/pester/powershell-coverage.xml' }
177
+ 'CSharp' { return Get-JacocoRepoCoverage -Path 'artifacts/csharp/coverage.xml' }
178
+ }
179
+ return $null
180
+ }