@danmoisan/drm-copilot-mcp 1.1.18 → 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.
- package/out/mcp-server.js +989 -662
- package/package.json +3 -2
- package/resources/claude-customizations/.claude/agents/atomic-executor.md +11 -0
- package/resources/claude-customizations/.claude/agents/atomic-planner.md +10 -0
- package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +12 -4
- package/resources/claude-customizations/.claude/agents/epic-planner.md +5 -4
- package/resources/claude-customizations/.claude/agents/feature-review.md +6 -4
- package/resources/claude-customizations/.claude/agents/orchestrator.md +5 -4
- package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +31 -13
- package/resources/claude-customizations/.claude/agents/parallel-planner.md +9 -6
- package/resources/claude-customizations/.claude/agents/pr-author.md +15 -2
- package/resources/claude-customizations/.claude/agents/prd-feature.md +1 -1
- package/resources/claude-customizations/.claude/hooks/check-powershell-test-purity.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/check-python-test-purity.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +111 -46
- package/resources/claude-customizations/.claude/hooks/enforce-completion-helpers.ps1 +105 -0
- package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +8 -1
- package/resources/claude-customizations/.claude/hooks/enforce-epic-invocation-origin.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-resolution.ps1 +116 -59
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +55 -23
- package/resources/claude-customizations/.claude/hooks/enforce-epic-wave-barrier.ps1 +74 -75
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate-resolution.ps1 +127 -35
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +61 -57
- package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-feature-folder-order.ps1 +129 -39
- package/resources/claude-customizations/.claude/hooks/enforce-mermaid-validation.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +9 -3
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +39 -51
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +17 -12
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +9 -3
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier.ps1 +65 -50
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-drift-gate.ps1 +49 -42
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +54 -72
- package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +8 -2
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-command-allowlist.ps1 +279 -0
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +129 -75
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.artifact-root.ps1 +233 -0
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.docs-only.ps1 +224 -0
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +69 -24
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner-helpers.ps1 +29 -18
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +10 -4
- package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +14 -5
- package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +8 -2
- package/resources/claude-customizations/.claude/hooks/feature-folder-resolution.ps1 +373 -0
- package/resources/claude-customizations/.claude/hooks/feature-review-coverage-thresholds.ps1 +85 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-heredoc.ps1 +119 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-invocation-operands.ps1 +198 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-invocation.ps1 +254 -255
- package/resources/claude-customizations/.claude/hooks/hook-command-payload-powershell.ps1 +201 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-payload.ps1 +484 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-scanner.ps1 +27 -110
- package/resources/claude-customizations/.claude/hooks/hook-dependency-guard.ps1 +114 -0
- package/resources/claude-customizations/.claude/hooks/hook-docs-only-diff.ps1 +493 -0
- package/resources/claude-customizations/.claude/hooks/subagentstop-envelope.ps1 +192 -0
- package/resources/claude-customizations/.claude/hooks/validate-bash.ps1 +9 -3
- package/resources/claude-customizations/.claude/hooks/validate-discovery-artifact-gate.ps1 +40 -42
- package/resources/claude-customizations/.claude/hooks/validate-executor-output.ps1 +66 -38
- package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-metrics.ps1 +180 -0
- package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-rules.ps1 +195 -0
- package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage.ps1 +141 -395
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-resolution.ps1 +311 -0
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-routing.ps1 +170 -0
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +115 -190
- package/resources/claude-customizations/.claude/hooks/validate-planner-output.ps1 +64 -32
- package/resources/claude-customizations/.claude/hooks/validate-pr-author-output.ps1 +39 -44
- package/resources/claude-customizations/.claude/hooks/validate-prd-feature-output.ps1 +43 -14
- package/resources/claude-customizations/.claude/hooks/validate-required-artifact-output.ps1 +66 -36
- package/resources/claude-customizations/.claude/hooks/validate-task-researcher-output.ps1 +31 -34
- package/resources/claude-customizations/.claude/lib/bash/mutate-parallel-run.sh +191 -0
- package/resources/claude-customizations/.claude/lib/bash/parallel-cohorts.sh +13 -3
- package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-cli.sh +152 -0
- package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-ops.sh +193 -0
- package/resources/claude-customizations/.claude/lib/bash/parallel-mutation.sh +322 -0
- package/resources/claude-customizations/.claude/lib/bash/remove-parallel-item.sh +146 -0
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +23 -33
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusTokenShape.psm1 +86 -6
- package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-CiGateParser.ps1 +91 -19
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateEpicWaveBarrier.psm1 +346 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1 +24 -3
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +79 -2
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1 +3 -3
- package/resources/claude-customizations/.claude/rules/architecture-boundaries.md +5 -5
- package/resources/claude-customizations/.claude/rules/csharp.md +2 -2
- package/resources/claude-customizations/.claude/rules/general-code-change.md +1 -1
- package/resources/claude-customizations/.claude/rules/general-unit-test.md +3 -2
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +37 -4
- package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +50 -5
- package/resources/claude-customizations/.claude/rules/plan-acceptance-gates.md +47 -8
- package/resources/claude-customizations/.claude/rules/quality-tiers.md +11 -7
- package/resources/claude-customizations/.claude/rules/shell.md +10 -8
- package/resources/claude-customizations/.claude/rules/typescript.md +1 -1
- package/resources/claude-customizations/.claude/settings.json +3 -4
- package/resources/claude-customizations/.claude/skills/acceptance-criteria-tracking/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +5 -1
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_detached_lib.sh +1 -1
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_enumerate_lib.sh +2 -0
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_scan_helper.sh +2 -0
- package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +33 -6
- package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
- package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +4 -3
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +14 -6
- package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +52 -14
- package/resources/claude-customizations/.claude/skills/parallel-close/SKILL.md +22 -9
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +79 -16
- package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +63 -7
- package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +35 -12
- package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +3 -1
- package/resources/claude-customizations/.claude/skills/quota-throttling/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +2 -2
- package/resources/claude-customizations/pack-manifests/core.json +23 -0
- package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/architecture-boundaries/SKILL.md +5 -5
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-executor/SKILL.md +11 -0
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +4 -0
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +10 -0
- package/resources/codex-and-agents-customizations/.agents/skills/csharp/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.agents/skills/epic-orchestrate/SKILL.md +8 -0
- package/resources/codex-and-agents-customizations/.agents/skills/epic-plan/SKILL.md +17 -1
- package/resources/codex-and-agents-customizations/.agents/skills/epic-run/SKILL.md +2 -1
- package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
- package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +13 -5
- package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +3 -2
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +15 -6
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +11 -7
- package/resources/codex-and-agents-customizations/.agents/skills/translate-claude-to-codex/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c1.toml +11 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +11 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +11 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +11 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +11 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +11 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +10 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +10 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +10 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +10 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +10 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +10 -0
- package/resources/codex-and-agents-customizations/.codex/agents/epic-orchestrator.toml +2 -1
- package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +6 -6
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c1.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/codex-web-setup.sh +318 -273
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/check-powershell-test-purity.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/check-python-test-purity.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/codex-epic-child-launch-attestation.ps1 +1 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-codex-model-routing.ps1 +8 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +97 -44
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-helpers.ps1 +105 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-child-worktree-binding.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +37 -5
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +6 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-root-invocation.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-wave-barrier.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-worktree-removal-gate.ps1 +54 -41
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-evidence-locations.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +35 -14
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +24 -11
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +8 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +13 -4
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +8 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/feature-folder-resolution.ps1 +373 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/feature-review-coverage-thresholds.ps1 +85 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-heredoc.ps1 +119 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation-operands.ps1 +198 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation.ps1 +254 -255
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload-powershell.ps1 +201 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload.ps1 +484 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-scanner.ps1 +27 -110
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-dependency-guard.ps1 +114 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-docs-only-diff.ps1 +493 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-bash.ps1 +8 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-codex-subagent-routing.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-feature-review-coverage.ps1 +24 -8
- package/resources/codex-and-agents-customizations/pack-manifests/core.json +9 -0
- package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +2 -2
- package/resources/customizations/.github/instructions/csharp-code-change.instructions.md +4 -4
- package/resources/customizations/.github/instructions/csharp-unit-test.instructions.md +2 -2
- package/resources/customizations/.github/skills/acceptance-criteria-tracking/SKILL.md +1 -1
- package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +4 -0
- package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
- package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +1 -1
- package/resources/powershell/PoshQC/PoshQC.Coverage.psm1 +314 -0
- package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +25 -28
- package/resources/powershell/PoshQC/PoshQC.psm1 +1 -0
- package/resources/powershell/PoshQC/README.md +8 -2
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +2 -319
|
@@ -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
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
(exit 0) when
|
|
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
|
|
14
|
-
|
|
15
|
-
- the
|
|
16
|
-
|
|
17
|
-
- the
|
|
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
|
|
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
|
-
|
|
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
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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] $
|
|
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($
|
|
103
|
+
if ([string]::IsNullOrWhiteSpace($assistantMessage)) {
|
|
91
104
|
return [ordered]@{
|
|
92
105
|
allowed = $false
|
|
93
|
-
reason = '
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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] $
|
|
74
|
-
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
$
|
|
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 $
|
|
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
|
-
|
|
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
|
-
$
|
|
90
|
-
|
|
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
|
|
7
|
-
|
|
8
|
-
advertised path matches the configured contract regex, and the file
|
|
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
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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] $
|
|
110
|
+
[AllowNull()] [AllowEmptyString()] [string] $InputText,
|
|
97
111
|
[string] $AgentName,
|
|
98
112
|
[string[]] $RequiredArtifact
|
|
99
113
|
)
|
|
100
114
|
|
|
101
|
-
if (
|
|
102
|
-
|
|
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
|
-
|
|
106
|
-
$
|
|
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
|
-
|
|
159
|
-
exit
|
|
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
|
|
@@ -11,14 +11,22 @@
|
|
|
11
11
|
convention, and the file exists on disk.
|
|
12
12
|
|
|
13
13
|
.NOTES
|
|
14
|
-
Reads the
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
Reads the SubagentStop envelope JSON from stdin through the shared
|
|
15
|
+
envelope helper (subagentstop-envelope.ps1) and validates its
|
|
16
|
+
last_assistant_message field, the researcher's last assistant message.
|
|
17
|
+
Exits 0 to allow termination. A block writes the reason to stderr and
|
|
18
|
+
exits 2 through the shared envelope helper; an invalid envelope blocks
|
|
19
|
+
with a payload anomaly reason. A hook dependency that fails to load
|
|
20
|
+
blocks with exit 2 and the validate-task-researcher-output: prefix.
|
|
21
|
+
Filesystem reads go through Test-ResearchFile so tests can mock the
|
|
22
|
+
boundary without writing temporary files.
|
|
23
|
+
An unhandled error during validation blocks with exit 2 and a reason (issue #928).
|
|
18
24
|
#>
|
|
19
25
|
|
|
20
26
|
[CmdletBinding()]
|
|
21
27
|
param()
|
|
28
|
+
$script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
|
|
29
|
+
try { . (Join-Path $PSScriptRoot 'subagentstop-envelope.ps1') } catch { Add-HookDependencyFailure -Name 'subagentstop-envelope.ps1' -ErrorRecord $_ }
|
|
22
30
|
|
|
23
31
|
Set-StrictMode -Version Latest
|
|
24
32
|
$ErrorActionPreference = 'Stop'
|
|
@@ -94,7 +102,7 @@ function Test-IsValidResearchFileName {
|
|
|
94
102
|
$fileName = [System.IO.Path]::GetFileName($normalized)
|
|
95
103
|
return [regex]::IsMatch(
|
|
96
104
|
$fileName,
|
|
97
|
-
'
|
|
105
|
+
'^(?:(?:\d{4}-\d{2}-\d{2}(?:T\d{2}-?\d{2})?|\d{8})-[A-Za-z0-9][A-Za-z0-9-]*[-.]research|research(?:-addendum)?\.\d{4}-\d{2}-\d{2}(?:T\d{2}-\d{2})?)\.md$'
|
|
98
106
|
)
|
|
99
107
|
}
|
|
100
108
|
|
|
@@ -192,7 +200,7 @@ function Test-NumericDerivationEvidence {
|
|
|
192
200
|
if (-not $match.Success) { return @{ Ok = $false; Message = "task-researcher hook: numeric derivation evidence is missing $label." } }
|
|
193
201
|
$values[$label] = $match.Groups['value'].Value.Trim()
|
|
194
202
|
}
|
|
195
|
-
if ($values['Exhaustive Search Scope'] -notmatch '(?i)\b(entire|all|complete)\b.*\b(repository|repo|source tree|tree)\b') {
|
|
203
|
+
if ($values['Exhaustive Search Scope'] -notmatch '(?i)\b(entire|whole|all|complete)\b.*\b(repository|repo|worktree|source tree|tree)\b') {
|
|
196
204
|
return @{ Ok = $false; Message = 'task-researcher hook: numeric derivation evidence does not declare an exhaustive repository search scope.' }
|
|
197
205
|
}
|
|
198
206
|
$primaryStrategy = $values['Primary Search Strategy or Query Expression']
|
|
@@ -227,29 +235,18 @@ function Invoke-TaskResearcherOutputValidation {
|
|
|
227
235
|
[CmdletBinding()]
|
|
228
236
|
[OutputType([hashtable])]
|
|
229
237
|
param(
|
|
230
|
-
[string] $
|
|
238
|
+
[AllowNull()] [AllowEmptyString()] [string] $InputText
|
|
231
239
|
)
|
|
232
|
-
|
|
233
|
-
if (
|
|
234
|
-
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
$payload = $RawPayload | ConvertFrom-Json -ErrorAction Stop
|
|
239
|
-
}
|
|
240
|
-
catch {
|
|
241
|
-
return @{ Ok = $false; Message = "task-researcher hook: failed to parse CLAUDE_HOOK_INPUT as JSON: $($_.Exception.Message)" }
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
$agentOutput = $null
|
|
245
|
-
if ($payload.PSObject.Properties.Name -contains 'output') {
|
|
246
|
-
$agentOutput = $payload.output
|
|
247
|
-
}
|
|
248
|
-
if ([string]::IsNullOrWhiteSpace($agentOutput)) {
|
|
240
|
+
$dependencyDecision = Get-HookDependencyFailureDecision -HookEvent SubagentStop -ReasonPrefix 'validate-task-researcher-output:'
|
|
241
|
+
if ($null -ne $dependencyDecision) { return $dependencyDecision }
|
|
242
|
+
$envelope = if ($PSBoundParameters.ContainsKey('InputText')) { Read-SubagentStopEnvelope -InputText $InputText } else { Read-SubagentStopEnvelope }
|
|
243
|
+
if (-not $envelope.IsValid) { return @{ Ok = $false; Message = "task-researcher hook: payload anomaly - $(Get-SubagentStopEnvelopeAnomalyReason -Anomaly $envelope.Anomaly)" } }
|
|
244
|
+
$assistantMessage = [string]$envelope.AssistantText
|
|
245
|
+
if ([string]::IsNullOrWhiteSpace($assistantMessage)) {
|
|
249
246
|
return @{ Ok = $false; Message = 'task-researcher hook: agent output is empty; researcher must return research-path before termination.' }
|
|
250
247
|
}
|
|
251
248
|
|
|
252
|
-
$researchPath = Get-ResearchPathFromOutput -AgentOutput $
|
|
249
|
+
$researchPath = Get-ResearchPathFromOutput -AgentOutput $assistantMessage
|
|
253
250
|
if ($null -eq $researchPath) {
|
|
254
251
|
return @{ Ok = $false; Message = 'task-researcher hook: agent output does not advertise a research-path. Researcher must report `research-path: <path>` pointing to docs/features/<feature>/research/ (feature-associated) or docs/research/ (one-off).' }
|
|
255
252
|
}
|
|
@@ -266,13 +263,14 @@ function Invoke-TaskResearcherOutputValidation {
|
|
|
266
263
|
return @{ Ok = $false; Message = "task-researcher hook: researcher advertised research-path '$researchPath' but no file exists at that location." }
|
|
267
264
|
}
|
|
268
265
|
|
|
269
|
-
$feasibilityResult = Test-AutomationFeasibilitySection -ResearchFilePath $researchPath -AgentOutput $
|
|
266
|
+
$feasibilityResult = Test-AutomationFeasibilitySection -ResearchFilePath $researchPath -AgentOutput $assistantMessage
|
|
270
267
|
if (-not $feasibilityResult.Ok) {
|
|
271
268
|
return @{ Ok = $false; Message = $feasibilityResult.Message }
|
|
272
269
|
}
|
|
273
270
|
|
|
274
271
|
if (Test-Path -LiteralPath $researchPath -PathType Leaf) {
|
|
275
|
-
$
|
|
272
|
+
try { $researchContent = Get-Content -LiteralPath $researchPath -Raw -ErrorAction Stop } catch { return @{ Ok = $false; Message = "task-researcher hook: failed to read research-path '$researchPath': $($_.Exception.Message)" } }
|
|
273
|
+
$numericEvidenceResult = Test-NumericDerivationEvidence -Content $researchContent
|
|
276
274
|
if (-not $numericEvidenceResult.Ok) {
|
|
277
275
|
return $numericEvidenceResult
|
|
278
276
|
}
|
|
@@ -284,11 +282,10 @@ function Invoke-TaskResearcherOutputValidation {
|
|
|
284
282
|
if ($MyInvocation.InvocationName -eq '.') {
|
|
285
283
|
return
|
|
286
284
|
}
|
|
287
|
-
|
|
288
|
-
$
|
|
289
|
-
if (-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
293
|
-
|
|
285
|
+
if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('validate-task-researcher-output: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
|
|
286
|
+
$dependencyDecision = Get-HookDependencyFailureDecision -HookEvent SubagentStop -ReasonPrefix 'validate-task-researcher-output:'
|
|
287
|
+
if ($null -ne $dependencyDecision) { [Console]::Error.WriteLine($dependencyDecision.Reason); exit $dependencyDecision.ExitCode }
|
|
288
|
+
try { $result = Invoke-TaskResearcherOutputValidation }
|
|
289
|
+
catch { Exit-SubagentStopBlock -Message ('validate-task-researcher-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.') }
|
|
290
|
+
if (-not $result.Ok) { Exit-SubagentStopBlock -Message $result.Message }
|
|
294
291
|
exit 0
|