@danmoisan/drm-copilot-mcp 1.1.19 → 1.1.21
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 +172 -11
- package/package.json +1 -1
- package/resources/claude-customizations/.claude/agents/atomic-executor.md +17 -6
- package/resources/claude-customizations/.claude/agents/atomic-planner.md +10 -2
- package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +5 -0
- package/resources/claude-customizations/.claude/agents/orchestrator.md +4 -1
- package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +4 -0
- package/resources/claude-customizations/.claude/agents/task-researcher.md +8 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-command.ps1 +190 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +22 -53
- package/resources/claude-customizations/.claude/hooks/hook-docs-only-diff.ps1 +5 -1
- package/resources/claude-customizations/.claude/hooks/premortem-section.ps1 +149 -0
- package/resources/claude-customizations/.claude/hooks/validate-executor-output.ps1 +210 -84
- package/resources/claude-customizations/.claude/hooks/validate-planner-output.ps1 +13 -1
- package/resources/claude-customizations/.claude/hooks/validate-task-researcher-output.ps1 +24 -5
- package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-MergeConfirmationParser.ps1 +203 -0
- package/resources/claude-customizations/.claude/lib/codex-routing/CodexDeployment.psm1 +10 -10
- package/resources/claude-customizations/.claude/rules/plan-acceptance-gates.md +46 -10
- package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +48 -16
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +7 -3
- package/resources/claude-customizations/.claude/skills/csharp-change-budget-router/SKILL.md +2 -2
- package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +12 -3
- package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +4 -2
- package/resources/claude-customizations/.claude/skills/execution-environment/SKILL.md +134 -0
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +22 -9
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +29 -5
- package/resources/claude-customizations/.claude/skills/powershell-change-budget-router/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/python-change-budget-router/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +14 -6
- package/resources/claude-customizations/.claude/skills/research-issue/SKILL.md +6 -0
- package/resources/claude-customizations/config/orchestration-routing.json +7 -7
- package/resources/claude-customizations/pack-manifests/core.json +4 -0
- package/resources/codex-and-agents-customizations/.agents/skills/README.md +1 -0
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-executor/SKILL.md +8 -7
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +39 -4
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +8 -6
- package/resources/codex-and-agents-customizations/.agents/skills/codex-model-routing/SKILL.md +6 -6
- package/resources/codex-and-agents-customizations/.agents/skills/csharp-change-budget-router/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.agents/skills/epic-orchestrate/SKILL.md +15 -8
- package/resources/codex-and-agents-customizations/.agents/skills/epic-plan/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.agents/skills/execution-environment/SKILL.md +134 -0
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +16 -6
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/powershell-change-budget-router/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/python-change-budget-router/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +7 -3
- package/resources/codex-and-agents-customizations/.agents/skills/research-issue/SKILL.md +6 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c1.toml +11 -7
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +11 -7
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +11 -7
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +11 -7
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +11 -7
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +11 -7
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +6 -3
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +6 -3
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +6 -3
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +6 -3
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +6 -3
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +6 -3
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planning.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/commit-steward-c1.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/commit-steward-c2.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/commit-steward-c3-elevated.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/commit-steward-c3.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/commit-steward-c4.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/commit-steward.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-atomic-planning.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c1.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c2.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3-elevated.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c4.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/epic-orchestrator.toml +4 -2
- package/resources/codex-and-agents-customizations/.codex/agents/epic-planner.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c1.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c2.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c3-elevated.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c3.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c4.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c1.toml +8 -5
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +8 -5
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +8 -5
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +8 -5
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +8 -5
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +8 -5
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-planning.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c1.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c2.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3-elevated.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c4.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/pr-author-c1.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/pr-author-c2.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/pr-author-c3-elevated.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/pr-author-c3.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/pr-author-c4.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/pr-author.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/prd-feature-c1.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/prd-feature-c2.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/prd-feature-c3-elevated.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/prd-feature-c3.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/prd-feature-c4.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/prd-feature.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-atomic-planning.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c1.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c2.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3-elevated.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c4.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c1.toml +7 -1
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c2.toml +7 -1
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c3-elevated.toml +7 -1
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c3.toml +7 -1
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c4.toml +7 -1
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher.toml +7 -1
- package/resources/codex-and-agents-customizations/.codex/agents/typescript-engineer-c1.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/typescript-engineer-c2.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/typescript-engineer-c3-elevated.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/typescript-engineer-c3.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/typescript-engineer-c4.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/typescript-engineer.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate-command.ps1 +190 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +17 -38
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-docs-only-diff.ps1 +5 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/record-subagent-routing-attestation.ps1 +82 -70
- package/resources/codex-and-agents-customizations/.codex/prompts/feature-review-remediate.md +6 -4
- package/resources/codex-and-agents-customizations/.codex-variants/csharp-legacy/agents/csharp-typed-engineer.toml +1 -1
- package/resources/codex-and-agents-customizations/pack-manifests/core.json +2 -0
- package/resources/config/orchestration-routing.json +7 -7
- package/resources/customizations/.github/agents/atomic_executor.agent.md +21 -11
- package/resources/customizations/.github/agents/atomic_planning.agent.md +8 -3
- package/resources/customizations/.github/agents/csharp-atomic-executor.agent.md +13 -9
- package/resources/customizations/.github/agents/csharp-atomic-planning.agent.md +2 -2
- package/resources/customizations/.github/agents/csharp-orchestrator.agent.md +2 -2
- package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +3 -3
- package/resources/customizations/.github/agents/orchestrator.agent.md +8 -5
- package/resources/customizations/.github/agents/powershell-atomic-executor.agent.md +13 -9
- package/resources/customizations/.github/agents/powershell-atomic-planning.agent.md +2 -2
- package/resources/customizations/.github/agents/powershell-orchestrator.agent.md +2 -2
- package/resources/customizations/.github/agents/powershell-typed-engineer.agent.md +3 -3
- package/resources/customizations/.github/agents/python-atomic-executor.agent.md +13 -9
- package/resources/customizations/.github/agents/python-atomic-planning.agent.md +2 -2
- package/resources/customizations/.github/agents/python-orchestrator.agent.md +2 -2
- package/resources/customizations/.github/agents/task-researcher.agent.md +6 -0
- package/resources/customizations/.github/prompts/generate-atomic-plan.prompt.md +16 -10
- package/resources/customizations/.github/prompts/orchestrate-csharp-work.prompt.md +2 -2
- package/resources/customizations/.github/prompts/orchestrate-powershell-work.prompt.md +1 -1
- package/resources/customizations/.github/prompts/orchestrate-python-work.prompt.md +1 -1
- package/resources/customizations/.github/prompts/orchestrate-work.prompt.md +2 -2
- package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +39 -4
- package/resources/customizations/.github/skills/csharp-change-budget-router/SKILL.md +1 -1
- package/resources/customizations/.github/skills/execution-environment/SKILL.md +134 -0
- package/resources/customizations/.github/skills/powershell-change-budget-router/SKILL.md +1 -1
- package/resources/customizations/.github/skills/remediation-handoff-atomic-planner/SKILL.md +7 -3
- package/resources/feature-templates/bug/plan.yyyy-MM-ddTHH-mm.md +10 -0
- package/resources/feature-templates/feature/plan.yyyy-MM-ddTHH-mm.md +11 -0
- package/resources/feature-templates/refactor/plan.yyyy-MM-ddTHH-mm.md +11 -0
- package/resources/lib/codex-routing/CodexDeployment.psm1 +10 -10
- package/resources/powershell/PoshQC/PoshQC.CoverageMerge.psm1 +163 -0
- package/resources/powershell/PoshQC/PoshQC.Shard.psm1 +296 -0
- package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +23 -4
- package/resources/powershell/PoshQC/PoshQC.psd1 +7 -1
- package/resources/powershell/PoshQC/PoshQC.psm1 +17 -5
- package/resources/powershell/PoshQC/README.md +13 -1
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +3 -0
|
@@ -10,9 +10,14 @@
|
|
|
10
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
|
-
-
|
|
14
|
-
|
|
15
|
-
-
|
|
13
|
+
- a run is classified as preflight from the delegation prompt in the subagent
|
|
14
|
+
transcript (Test-IsPreflightRun), falling back to the plan file or one output signal,
|
|
15
|
+
- preflight runs return exactly one preflight signal consistent with their
|
|
16
|
+
PREFLIGHT-DEFECT lines, name an existing fix record for any PREFLIGHT-FIX line,
|
|
17
|
+
and may not claim non-canonical blocked text,
|
|
18
|
+
- non-preflight runs may not stop in a blocked state, except a canonical preflight
|
|
19
|
+
block with a plan delta and no checked task (combined proportionate run); only the
|
|
20
|
+
uppercase BLOCKED token counts as a blocked claim, and lowercase prose does not,
|
|
16
21
|
- non-preflight plans contain checkboxes and all are checked,
|
|
17
22
|
- non-preflight output includes `AC Status Summary`,
|
|
18
23
|
- non-preflight output includes command/status reporting,
|
|
@@ -43,33 +48,21 @@ $script:SubagentStopReportAndHaltLine = 'This condition is not repairable by the
|
|
|
43
48
|
function Get-PlanFileContent {
|
|
44
49
|
[CmdletBinding()]
|
|
45
50
|
[OutputType([hashtable])]
|
|
46
|
-
param(
|
|
47
|
-
[Parameter(Mandatory = $true)]
|
|
48
|
-
[string] $Path
|
|
49
|
-
)
|
|
51
|
+
param([Parameter(Mandatory = $true)] [string] $Path)
|
|
50
52
|
|
|
51
53
|
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
|
|
52
54
|
return @{ Exists = $false; Lines = @() }
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
$lines = @()
|
|
58
|
-
}
|
|
59
|
-
elseif ($lines -isnot [array]) {
|
|
60
|
-
$lines = @($lines)
|
|
61
|
-
}
|
|
62
|
-
|
|
57
|
+
# @() yields an array for any line count; the filter drops a $null emitted at the read boundary.
|
|
58
|
+
$lines = @(Get-Content -LiteralPath $Path -ErrorAction Stop | Where-Object { $null -ne $_ })
|
|
63
59
|
return @{ Exists = $true; Lines = $lines }
|
|
64
60
|
}
|
|
65
61
|
|
|
66
62
|
function Get-PlanPathFromOutput {
|
|
67
63
|
[CmdletBinding()]
|
|
68
64
|
[OutputType([string])]
|
|
69
|
-
param(
|
|
70
|
-
[Parameter(Mandatory = $true)]
|
|
71
|
-
[string] $AgentOutput
|
|
72
|
-
)
|
|
65
|
+
param([Parameter(Mandatory = $true)] [string] $AgentOutput)
|
|
73
66
|
|
|
74
67
|
$pattern = 'plan-path\s*[:=]\s*["'']?([^\s"''\)`]+)|\[plan-path\]\(([^)]+)\)'
|
|
75
68
|
$match = [regex]::Match($AgentOutput, $pattern, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
|
|
@@ -89,22 +82,180 @@ function Get-PlanPathFromOutput {
|
|
|
89
82
|
function Test-IsPreflightPlan {
|
|
90
83
|
[CmdletBinding()]
|
|
91
84
|
[OutputType([bool])]
|
|
92
|
-
param(
|
|
93
|
-
[Parameter(Mandatory = $true)]
|
|
94
|
-
[AllowEmptyString()] [string[]] $Lines
|
|
95
|
-
)
|
|
85
|
+
param([Parameter(Mandatory = $true)] [AllowEmptyCollection()] [AllowEmptyString()] [string[]] $Lines)
|
|
96
86
|
|
|
97
87
|
return @($Lines | Where-Object { $_ -match '(?i)^\s*DIRECTIVE:\s*PREFLIGHT VALIDATION ONLY\s*$' }).Count -gt 0
|
|
98
88
|
}
|
|
99
89
|
|
|
100
|
-
function Get-
|
|
90
|
+
function Get-JsonPropertyValue {
|
|
91
|
+
# Returns a named property of a parsed JSON object, or $null; safe under StrictMode.
|
|
92
|
+
[CmdletBinding()]
|
|
93
|
+
[OutputType([object])]
|
|
94
|
+
param([AllowNull()] [object] $Object, [Parameter(Mandatory = $true)] [string] $Name)
|
|
95
|
+
|
|
96
|
+
if ($null -eq $Object -or -not ($Object -is [System.Management.Automation.PSCustomObject])) {
|
|
97
|
+
return $null
|
|
98
|
+
}
|
|
99
|
+
$property = @($Object.PSObject.Properties | Where-Object { $_.Name -ceq $Name })
|
|
100
|
+
if ($property.Count -eq 0) {
|
|
101
|
+
return $null
|
|
102
|
+
}
|
|
103
|
+
return $property[0].Value
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function Get-TranscriptPathFromEnvelope {
|
|
107
|
+
# Returns agent_transcript_path from the SubagentStop envelope with a leading ~ expanded, or $null.
|
|
108
|
+
[CmdletBinding()]
|
|
109
|
+
[OutputType([string])]
|
|
110
|
+
param([AllowNull()] [object] $Envelope)
|
|
111
|
+
|
|
112
|
+
$path = Get-JsonPropertyValue -Object $Envelope -Name 'agent_transcript_path'
|
|
113
|
+
if (-not ($path -is [string]) -or [string]::IsNullOrWhiteSpace($path)) {
|
|
114
|
+
return $null
|
|
115
|
+
}
|
|
116
|
+
if ($path.StartsWith('~')) {
|
|
117
|
+
$path = $HOME + $path.Substring(1)
|
|
118
|
+
}
|
|
119
|
+
return $path
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function Get-TranscriptFirstUserText {
|
|
123
|
+
# Returns the text of the first `user` entry of transcript JSONL lines, or $null when none parses.
|
|
124
|
+
[CmdletBinding()]
|
|
125
|
+
[OutputType([string])]
|
|
126
|
+
param([AllowEmptyString()] [string[]] $Lines = @())
|
|
127
|
+
|
|
128
|
+
foreach ($line in $Lines) {
|
|
129
|
+
if ([string]::IsNullOrWhiteSpace($line)) {
|
|
130
|
+
continue
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
$entry = ConvertFrom-Json -InputObject $line -ErrorAction Stop
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
continue
|
|
137
|
+
}
|
|
138
|
+
if ((Get-JsonPropertyValue -Object $entry -Name 'type') -ne 'user') {
|
|
139
|
+
continue
|
|
140
|
+
}
|
|
141
|
+
$container = Get-JsonPropertyValue -Object $entry -Name 'message'
|
|
142
|
+
if ($null -eq $container) {
|
|
143
|
+
$container = $entry
|
|
144
|
+
}
|
|
145
|
+
$content = Get-JsonPropertyValue -Object $container -Name 'content'
|
|
146
|
+
if ($content -is [string]) {
|
|
147
|
+
return $content
|
|
148
|
+
}
|
|
149
|
+
$textBlocks = @(@($content) | Where-Object { (Get-JsonPropertyValue -Object $_ -Name 'type') -eq 'text' })
|
|
150
|
+
$texts = @($textBlocks | ForEach-Object { [string](Get-JsonPropertyValue -Object $_ -Name 'text') })
|
|
151
|
+
if ($texts.Count -gt 0) {
|
|
152
|
+
return ($texts -join "`n")
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return $null
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function Read-TranscriptHead {
|
|
159
|
+
# File seam: returns the first 50 lines of the transcript at Path, or $null when no file exists.
|
|
101
160
|
[CmdletBinding()]
|
|
102
161
|
[OutputType([string[]])]
|
|
162
|
+
param([Parameter(Mandatory = $true)] [string] $Path)
|
|
163
|
+
|
|
164
|
+
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
|
|
165
|
+
return $null
|
|
166
|
+
}
|
|
167
|
+
return [string[]]@(Get-Content -LiteralPath $Path -TotalCount 50 -ErrorAction Stop)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function Test-IsPreflightRun {
|
|
171
|
+
<#
|
|
172
|
+
.SYNOPSIS
|
|
173
|
+
Classifies an executor run as preflight or execution (issue #964).
|
|
174
|
+
.DESCRIPTION
|
|
175
|
+
A readable transcript is authoritative: its first `user` entry (the delegation prompt) decides, even
|
|
176
|
+
against a stale plan-file directive. Otherwise the plan-file directive, then exactly one output
|
|
177
|
+
`PREFLIGHT:` signal, decide (self-declared). ReadTranscriptHead returns JSONL lines or $null.
|
|
178
|
+
#>
|
|
179
|
+
[CmdletBinding()]
|
|
180
|
+
[OutputType([bool])]
|
|
103
181
|
param(
|
|
104
|
-
[Parameter(Mandatory = $true)]
|
|
105
|
-
[AllowEmptyString()] [string[]] $
|
|
182
|
+
[Parameter(Mandatory = $true)] [AllowEmptyString()] [string] $AgentOutput,
|
|
183
|
+
[Parameter(Mandatory = $true)] [AllowEmptyCollection()] [AllowEmptyString()] [string[]] $PlanLines,
|
|
184
|
+
[AllowNull()] [AllowEmptyString()] [string] $TranscriptPath,
|
|
185
|
+
[scriptblock] $ReadTranscriptHead = ${function:Read-TranscriptHead}
|
|
106
186
|
)
|
|
107
187
|
|
|
188
|
+
if (-not [string]::IsNullOrWhiteSpace($TranscriptPath)) {
|
|
189
|
+
$head = $null
|
|
190
|
+
try {
|
|
191
|
+
$head = & $ReadTranscriptHead $TranscriptPath
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
$head = $null
|
|
195
|
+
}
|
|
196
|
+
if ($null -ne $head) {
|
|
197
|
+
$prompt = Get-TranscriptFirstUserText -Lines ([string[]]@($head))
|
|
198
|
+
if ($null -ne $prompt) {
|
|
199
|
+
return [regex]::IsMatch($prompt, '(?im)^\s*DIRECTIVE:\s*PREFLIGHT VALIDATION ONLY\s*$')
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (Test-IsPreflightPlan -Lines $PlanLines) {
|
|
204
|
+
return $true
|
|
205
|
+
}
|
|
206
|
+
return [regex]::Matches($AgentOutput, '(?m)^\s*PREFLIGHT:\s*(ALL CLEAR|REVISIONS REQUIRED)\s*$').Count -eq 1
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function Test-PreflightFixRecord {
|
|
210
|
+
# File seam for the preflight-fix-record path.
|
|
211
|
+
[CmdletBinding()]
|
|
212
|
+
[OutputType([bool])]
|
|
213
|
+
param([Parameter(Mandatory = $true)] [string] $Path)
|
|
214
|
+
return [bool](Test-Path -LiteralPath $Path -PathType Leaf)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function Get-PreflightRunBlockDecision {
|
|
218
|
+
<#
|
|
219
|
+
.SYNOPSIS
|
|
220
|
+
Applies the single-pass preflight output rules (issue #964); every block is repairable.
|
|
221
|
+
#>
|
|
222
|
+
[CmdletBinding()]
|
|
223
|
+
[OutputType([hashtable])]
|
|
224
|
+
param([Parameter(Mandatory = $true)] [string] $AgentOutput)
|
|
225
|
+
|
|
226
|
+
if (-not (Test-HasCanonicalBlockedText -AgentOutput $AgentOutput)) {
|
|
227
|
+
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.' }
|
|
228
|
+
}
|
|
229
|
+
if ($AgentOutput -cmatch 'BLOCKED') {
|
|
230
|
+
return @{ Ok = $true; Message = $null }
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
$signals = @([regex]::Matches($AgentOutput, '(?m)^\s*PREFLIGHT:\s*(?<Signal>ALL CLEAR|REVISIONS REQUIRED)\s*$'))
|
|
234
|
+
if ($signals.Count -ne 1) {
|
|
235
|
+
return @{ Ok = $false; Repairable = $true; Message = "atomic-executor hook: preflight output must carry exactly one ``PREFLIGHT: ALL CLEAR`` or ``PREFLIGHT: REVISIONS REQUIRED`` signal line; found $($signals.Count)." }
|
|
236
|
+
}
|
|
237
|
+
$defectCount = [regex]::Matches($AgentOutput, '(?m)^\s*PREFLIGHT-DEFECT:\s*\S').Count
|
|
238
|
+
if ($signals[0].Groups['Signal'].Value -eq 'ALL CLEAR' -and $defectCount -gt 0) {
|
|
239
|
+
return @{ Ok = $false; Repairable = $true; Message = 'atomic-executor hook: `PREFLIGHT: ALL CLEAR` must not carry a `PREFLIGHT-DEFECT:` line; fix trivial defects in place or return `PREFLIGHT: REVISIONS REQUIRED`.' }
|
|
240
|
+
}
|
|
241
|
+
if ($signals[0].Groups['Signal'].Value -eq 'REVISIONS REQUIRED' -and $defectCount -eq 0) {
|
|
242
|
+
return @{ Ok = $false; Repairable = $true; Message = 'atomic-executor hook: `PREFLIGHT: REVISIONS REQUIRED` must list every substantive defect as a `PREFLIGHT-DEFECT:` line.' }
|
|
243
|
+
}
|
|
244
|
+
if ([regex]::IsMatch($AgentOutput, '(?m)^\s*PREFLIGHT-FIX:\s*\S')) {
|
|
245
|
+
$record = [regex]::Match($AgentOutput, '(?im)^\s*preflight-fix-record:\s*`?(?<Path>[^\s`]+)`?\s*$')
|
|
246
|
+
$recordPath = if ($record.Success) { ($record.Groups['Path'].Value -replace '\\', '/') -replace '^\./', '' } else { '' }
|
|
247
|
+
if (-not $recordPath.StartsWith('docs/features/', [System.StringComparison]::OrdinalIgnoreCase) -or -not (Test-PreflightFixRecord -Path $recordPath)) {
|
|
248
|
+
return @{ Ok = $false; Repairable = $true; Message = 'atomic-executor hook: a `PREFLIGHT-FIX:` line requires a `preflight-fix-record: <path>` line naming an existing record under docs/features/.' }
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return @{ Ok = $true; Message = $null }
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function Get-TouchedLanguagesFromPlan {
|
|
255
|
+
[CmdletBinding()]
|
|
256
|
+
[OutputType([string[]])]
|
|
257
|
+
param([Parameter(Mandatory = $true)] [AllowEmptyString()] [string[]] $Lines)
|
|
258
|
+
|
|
108
259
|
$languages = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
|
|
109
260
|
$pathPattern = '(?i)(?:[.\w*?-]+[\\/])+[.\w*?-]+'
|
|
110
261
|
|
|
@@ -126,13 +277,7 @@ function Get-TouchedLanguagesFromPlan {
|
|
|
126
277
|
function Test-OutputHasLanguageStatus {
|
|
127
278
|
[CmdletBinding()]
|
|
128
279
|
[OutputType([bool])]
|
|
129
|
-
param(
|
|
130
|
-
[Parameter(Mandatory = $true)]
|
|
131
|
-
[string] $AgentOutput,
|
|
132
|
-
|
|
133
|
-
[Parameter(Mandatory = $true)]
|
|
134
|
-
[string] $Language
|
|
135
|
-
)
|
|
280
|
+
param([Parameter(Mandatory = $true)] [string] $AgentOutput, [Parameter(Mandatory = $true)] [string] $Language)
|
|
136
281
|
|
|
137
282
|
$labelMap = @{
|
|
138
283
|
'TypeScript' = @('TypeScript', 'typescript')
|
|
@@ -149,34 +294,28 @@ function Test-OutputHasLanguageStatus {
|
|
|
149
294
|
function Test-HasPreflightSignal {
|
|
150
295
|
[CmdletBinding()]
|
|
151
296
|
[OutputType([bool])]
|
|
152
|
-
param(
|
|
153
|
-
[Parameter(Mandatory = $true)]
|
|
154
|
-
[string] $AgentOutput
|
|
155
|
-
)
|
|
297
|
+
param([Parameter(Mandatory = $true)] [string] $AgentOutput)
|
|
156
298
|
|
|
157
|
-
return [regex]::IsMatch(
|
|
158
|
-
$AgentOutput,
|
|
159
|
-
'(?m)^\s*PREFLIGHT:\s*(ALL CLEAR|REVISIONS REQUIRED)\s*$'
|
|
160
|
-
)
|
|
299
|
+
return [regex]::IsMatch($AgentOutput, '(?m)^\s*PREFLIGHT:\s*(ALL CLEAR|REVISIONS REQUIRED)\s*$')
|
|
161
300
|
}
|
|
162
301
|
|
|
163
302
|
function Test-HasCanonicalBlockedText {
|
|
164
303
|
[CmdletBinding()]
|
|
165
304
|
[OutputType([bool])]
|
|
166
|
-
param(
|
|
167
|
-
[Parameter(Mandatory = $true)]
|
|
168
|
-
[string] $AgentOutput
|
|
169
|
-
)
|
|
305
|
+
param([Parameter(Mandatory = $true)] [string] $AgentOutput)
|
|
170
306
|
|
|
171
|
-
|
|
307
|
+
# Case-sensitive: lowercase prose such as `blocked_reason` is not a BLOCKED claim (issue #964).
|
|
308
|
+
if ($AgentOutput -cnotmatch 'BLOCKED') {
|
|
172
309
|
return $true
|
|
173
310
|
}
|
|
174
311
|
|
|
175
|
-
if ($AgentOutput -
|
|
312
|
+
if ($AgentOutput -cnotmatch 'BLOCKED at preflight \(before \[P0-T1\]\)') {
|
|
176
313
|
return $false
|
|
177
314
|
}
|
|
178
315
|
|
|
179
|
-
|
|
316
|
+
# The canonical phrase itself carries [P0-T1], so it is removed before looking for a plan delta.
|
|
317
|
+
$remainder = $AgentOutput -creplace 'BLOCKED at preflight \(before \[P0-T1\]\)', ''
|
|
318
|
+
if ($remainder -notmatch '(?i)plan delta' -and $remainder -notmatch '\[P\d+-T\d+\]') {
|
|
180
319
|
return $false
|
|
181
320
|
}
|
|
182
321
|
|
|
@@ -190,15 +329,13 @@ function Invoke-ExecutorOutputValidation {
|
|
|
190
329
|
#>
|
|
191
330
|
[CmdletBinding()]
|
|
192
331
|
[OutputType([hashtable])]
|
|
193
|
-
param(
|
|
194
|
-
[AllowNull()] [AllowEmptyString()] [string] $InputText
|
|
195
|
-
)
|
|
332
|
+
param([AllowNull()] [AllowEmptyString()] [string] $InputText)
|
|
196
333
|
|
|
197
334
|
$envelope = if ($PSBoundParameters.ContainsKey('InputText')) { Read-SubagentStopEnvelope -InputText $InputText } else { Read-SubagentStopEnvelope }
|
|
198
335
|
if (-not $envelope.IsValid) { $decision = @{ Ok = $false; Repairable = $false; Message = ('{0} payload anomaly - {1}' -f 'atomic-executor hook:', (Get-SubagentStopEnvelopeAnomalyReason -Anomaly $envelope.Anomaly)) } }
|
|
199
336
|
else {
|
|
200
337
|
$agentOutput = $envelope.AssistantText
|
|
201
|
-
$decision = Get-ExecutorOutputBlockDecision -AgentOutput ([string]$agentOutput)
|
|
338
|
+
$decision = Get-ExecutorOutputBlockDecision -AgentOutput ([string]$agentOutput) -TranscriptPath (Get-TranscriptPathFromEnvelope -Envelope $envelope.Envelope)
|
|
202
339
|
}
|
|
203
340
|
if ($decision.Ok) { return @{ Ok = $true; Message = $null } }
|
|
204
341
|
if (-not $decision.Repairable) {
|
|
@@ -216,13 +353,16 @@ function Get-ExecutorOutputBlockDecision {
|
|
|
216
353
|
Returns @{ Ok = $true; Message = $null } to allow, or a hashtable with Ok = $false, a
|
|
217
354
|
Repairable boolean, and Message. Repairable is $false for a non-preflight BLOCKED claim
|
|
218
355
|
and for a plan with no task checkboxes; every other block is repairable by the subagent.
|
|
356
|
+
TranscriptPath and ReadTranscriptHead feed Test-IsPreflightRun. An execution run that
|
|
357
|
+
stops with the canonical preflight BLOCKED text, a plan delta, and no checked task is a
|
|
358
|
+
preflight block inside a combined proportionate run and is allowed.
|
|
219
359
|
#>
|
|
220
360
|
[CmdletBinding()]
|
|
221
361
|
[OutputType([hashtable])]
|
|
222
362
|
param(
|
|
223
|
-
[Parameter(Mandatory = $true)]
|
|
224
|
-
[AllowEmptyString()]
|
|
225
|
-
[
|
|
363
|
+
[Parameter(Mandatory = $true)] [AllowEmptyString()] [string] $AgentOutput,
|
|
364
|
+
[AllowNull()] [AllowEmptyString()] [string] $TranscriptPath,
|
|
365
|
+
[scriptblock] $ReadTranscriptHead
|
|
226
366
|
)
|
|
227
367
|
|
|
228
368
|
if ([string]::IsNullOrWhiteSpace($agentOutput)) {
|
|
@@ -239,35 +379,29 @@ function Get-ExecutorOutputBlockDecision {
|
|
|
239
379
|
return @{ Ok = $false; Repairable = $true; Message = "atomic-executor hook: executor advertised plan-path '$planPath' but no file exists at that location." }
|
|
240
380
|
}
|
|
241
381
|
|
|
242
|
-
$
|
|
243
|
-
if ($
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}
|
|
247
|
-
if ($agentOutput -notmatch 'BLOCKED' -and -not (Test-HasPreflightSignal -AgentOutput $agentOutput)) {
|
|
382
|
+
$runArguments = @{ AgentOutput = $agentOutput; PlanLines = [string[]]@($file.Lines); TranscriptPath = $TranscriptPath }
|
|
383
|
+
if ($null -ne $ReadTranscriptHead) { $runArguments['ReadTranscriptHead'] = $ReadTranscriptHead }
|
|
384
|
+
if (Test-IsPreflightRun @runArguments) {
|
|
385
|
+
if ($agentOutput -cnotmatch 'BLOCKED' -and -not (Test-HasPreflightSignal -AgentOutput $agentOutput)) {
|
|
248
386
|
return @{ Ok = $false; Repairable = $true; Message = 'atomic-executor hook: preflight output must include `PREFLIGHT: ALL CLEAR` or `PREFLIGHT: REVISIONS REQUIRED`.' }
|
|
249
387
|
}
|
|
250
|
-
return
|
|
388
|
+
return Get-PreflightRunBlockDecision -AgentOutput $agentOutput
|
|
251
389
|
}
|
|
252
390
|
|
|
253
|
-
if ($agentOutput -
|
|
391
|
+
if ($agentOutput -cmatch 'BLOCKED') {
|
|
392
|
+
$hasCheckedTask = @($file.Lines | Where-Object { $_ -match '^\s*-\s\[[xX]\]\s\[P\d+-T\d+\]' }).Count -gt 0
|
|
393
|
+
if ($agentOutput -cmatch 'BLOCKED at preflight \(before \[P0-T1\]\)' -and (Test-HasCanonicalBlockedText -AgentOutput $agentOutput) -and -not $hasCheckedTask) {
|
|
394
|
+
return @{ Ok = $true; Message = $null }
|
|
395
|
+
}
|
|
254
396
|
return @{ Ok = $false; Repairable = $false; Message = 'atomic-executor hook: blocking is permitted only during preflight validation (before [P0-T1]).' }
|
|
255
397
|
}
|
|
256
398
|
|
|
257
|
-
$checkboxLineCount =
|
|
399
|
+
$checkboxLineCount = @($file.Lines | Where-Object { $_ -match '^\s*-\s\[(?:\s|[xX])\]\s' }).Count
|
|
258
400
|
$uncheckedLineNumbers = [System.Collections.Generic.List[int]]::new()
|
|
259
|
-
$uncheckedPattern = '^\s*-\s\[\s\]\s'
|
|
260
|
-
$checkedPattern = '^\s*-\s\[[xX]\]\s'
|
|
261
|
-
|
|
262
401
|
for ($i = 0; $i -lt $file.Lines.Count; $i++) {
|
|
263
|
-
|
|
264
|
-
if ($line -match $uncheckedPattern) {
|
|
265
|
-
$checkboxLineCount++
|
|
402
|
+
if ($file.Lines[$i] -match '^\s*-\s\[\s\]\s') {
|
|
266
403
|
$uncheckedLineNumbers.Add($i + 1)
|
|
267
404
|
}
|
|
268
|
-
elseif ($line -match $checkedPattern) {
|
|
269
|
-
$checkboxLineCount++
|
|
270
|
-
}
|
|
271
405
|
}
|
|
272
406
|
|
|
273
407
|
if ($checkboxLineCount -eq 0) {
|
|
@@ -281,8 +415,7 @@ function Get-ExecutorOutputBlockDecision {
|
|
|
281
415
|
$reported = $uncheckedLineNumbers.GetRange(0, 5)
|
|
282
416
|
$suffix = ' (showing first 5)'
|
|
283
417
|
}
|
|
284
|
-
$
|
|
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."
|
|
418
|
+
$message = "atomic-executor hook: plan file '$planPath' has $($uncheckedLineNumbers.Count) unchecked task(s) at line(s) $($reported -join ', ')${suffix}. All `- [ ]` items must be ticked to `- [x]` before termination."
|
|
286
419
|
return @{ Ok = $false; Repairable = $true; Message = $message }
|
|
287
420
|
}
|
|
288
421
|
|
|
@@ -296,16 +429,9 @@ function Get-ExecutorOutputBlockDecision {
|
|
|
296
429
|
return @{ Ok = $false; Repairable = $true; Message = 'atomic-executor hook: completion output must report commands run and pass/fail status results.' }
|
|
297
430
|
}
|
|
298
431
|
|
|
299
|
-
$missingLanguageStatuses =
|
|
300
|
-
foreach ($language in Get-TouchedLanguagesFromPlan -Lines $file.Lines) {
|
|
301
|
-
if (-not (Test-OutputHasLanguageStatus -AgentOutput $agentOutput -Language $language)) {
|
|
302
|
-
$missingLanguageStatuses.Add($language)
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
432
|
+
$missingLanguageStatuses = @(Get-TouchedLanguagesFromPlan -Lines $file.Lines | Where-Object { -not (Test-OutputHasLanguageStatus -AgentOutput $agentOutput -Language $_) })
|
|
306
433
|
if ($missingLanguageStatuses.Count -gt 0) {
|
|
307
|
-
$
|
|
308
|
-
return @{ Ok = $false; Repairable = $true; Message = "atomic-executor hook: completion output is missing explicit PASS/FAIL toolchain status lines for: $missingList." }
|
|
434
|
+
return @{ Ok = $false; Repairable = $true; Message = "atomic-executor hook: completion output is missing explicit PASS/FAIL toolchain status lines for: $($missingLanguageStatuses -join ', ')." }
|
|
309
435
|
}
|
|
310
436
|
|
|
311
437
|
return @{ Ok = $true; Message = $null }
|
|
@@ -19,7 +19,9 @@
|
|
|
19
19
|
- each task uses `- [ ] [P#-T#]` (or checked equivalent),
|
|
20
20
|
- task numbering is sequential within each phase,
|
|
21
21
|
- every task includes at least one explicit path token,
|
|
22
|
-
- the final phase contains QA-oriented work
|
|
22
|
+
- the final phase contains QA-oriented work,
|
|
23
|
+
- the plan carries a complete `## Pre-Mortem` section (Test-PreMortemSection, issue #964),
|
|
24
|
+
- no internal-review `CITATION:` locator is a line reference (issue #964).
|
|
23
25
|
|
|
24
26
|
.NOTES
|
|
25
27
|
Reads the SubagentStop envelope from stdin through Read-SubagentStopEnvelope
|
|
@@ -39,6 +41,7 @@ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot
|
|
|
39
41
|
|
|
40
42
|
Set-StrictMode -Version Latest
|
|
41
43
|
try { . (Join-Path $PSScriptRoot 'subagentstop-envelope.ps1') } catch { Add-HookDependencyFailure -Name 'subagentstop-envelope.ps1' -ErrorRecord $_ }
|
|
44
|
+
try { . (Join-Path $PSScriptRoot 'premortem-section.ps1') } catch { Add-HookDependencyFailure -Name 'premortem-section.ps1' -ErrorRecord $_ }
|
|
42
45
|
$ErrorActionPreference = 'Stop'
|
|
43
46
|
$script:SubagentStopReportAndHaltLine = 'This condition is not repairable by the subagent. Report it to the operator and halt.'
|
|
44
47
|
|
|
@@ -186,6 +189,10 @@ function Get-PlannerInternalReviewValidation {
|
|
|
186
189
|
if (-not $citation.Success -or $citation.Groups['Path'].Value -notmatch '^(?![A-Za-z]:)(?!/)(?!\\)(?:\.?[^/\\|\s]+)(?:/[^/\\|\s]+)+$') {
|
|
187
190
|
return @{ Ok = $false; Message = 'planner internal review citations require a repository-relative path and nonblank locator.' }
|
|
188
191
|
}
|
|
192
|
+
# Issue #964: a line reference goes stale when main moves (consistent with validator rule G11).
|
|
193
|
+
if ($citation.Groups['Locator'].Value.Trim() -match '(?i)^(?:\d+(?:\s*-\s*\d+)?|#?L\d+(?:-L?\d+)?|lines?\s+\d+.*|[^\s:|]+:\d+(?:-\d+)?)$') {
|
|
194
|
+
return @{ Ok = $false; Message = "planner internal review citation locator ``$($citation.Groups['Locator'].Value.Trim())`` is a line reference; cite a symbol, heading, or test name instead." }
|
|
195
|
+
}
|
|
189
196
|
}
|
|
190
197
|
|
|
191
198
|
$inventoryLines = @($recordLines | Where-Object { $_ -match '^\s*AC-INVENTORY\s*:' })
|
|
@@ -421,6 +428,11 @@ function Get-PlannerOutputBlockDecision {
|
|
|
421
428
|
return @{ Ok = $false; Repairable = $true; Message = $message }
|
|
422
429
|
}
|
|
423
430
|
|
|
431
|
+
$preMortem = Test-PreMortemSection -Content ($file.Lines -join "`n")
|
|
432
|
+
if (-not $preMortem.Ok) {
|
|
433
|
+
return @{ Ok = $false; Repairable = $true; Message = "atomic-planner hook: plan '$planPath' $($preMortem.Message) See ``## Design-Level Plan Contract (Mandatory)`` in atomic-plan-contract." }
|
|
434
|
+
}
|
|
435
|
+
|
|
424
436
|
$review = Get-PlannerInternalReviewValidation -AgentOutput $agentOutput
|
|
425
437
|
if (-not $review.Ok) {
|
|
426
438
|
return @{ Ok = $false; Repairable = $true; Message = "atomic-planner hook: $($review.Message)" }
|
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
under one of the two tracked research roots
|
|
9
9
|
(docs/features/<feature>/research/ for feature-associated research, or
|
|
10
10
|
docs/research/ for one-off research), matches the documented filename
|
|
11
|
-
convention, and the file exists on disk.
|
|
11
|
+
convention, and the file exists on disk. Feature-associated research under
|
|
12
|
+
docs/features/ must also carry a complete `## Pre-Mortem` section
|
|
13
|
+
(Test-PreMortemSection in premortem-section.ps1, issue #964); one-off
|
|
14
|
+
research under docs/research/ is exempt.
|
|
12
15
|
|
|
13
16
|
.NOTES
|
|
14
17
|
Reads the SubagentStop envelope JSON from stdin through the shared
|
|
@@ -18,8 +21,8 @@
|
|
|
18
21
|
exits 2 through the shared envelope helper; an invalid envelope blocks
|
|
19
22
|
with a payload anomaly reason. A hook dependency that fails to load
|
|
20
23
|
blocks with exit 2 and the validate-task-researcher-output: prefix.
|
|
21
|
-
Filesystem reads go through Test-ResearchFile
|
|
22
|
-
boundary without writing temporary files.
|
|
24
|
+
Filesystem reads go through Test-ResearchFile and Get-ResearchFileContent
|
|
25
|
+
so tests can mock the boundary without writing temporary files.
|
|
23
26
|
An unhandled error during validation blocks with exit 2 and a reason (issue #928).
|
|
24
27
|
#>
|
|
25
28
|
|
|
@@ -27,6 +30,7 @@
|
|
|
27
30
|
param()
|
|
28
31
|
$script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
|
|
29
32
|
try { . (Join-Path $PSScriptRoot 'subagentstop-envelope.ps1') } catch { Add-HookDependencyFailure -Name 'subagentstop-envelope.ps1' -ErrorRecord $_ }
|
|
33
|
+
try { . (Join-Path $PSScriptRoot 'premortem-section.ps1') } catch { Add-HookDependencyFailure -Name 'premortem-section.ps1' -ErrorRecord $_ }
|
|
30
34
|
|
|
31
35
|
Set-StrictMode -Version Latest
|
|
32
36
|
$ErrorActionPreference = 'Stop'
|
|
@@ -42,6 +46,16 @@ function Test-ResearchFile {
|
|
|
42
46
|
return [bool](Test-Path -LiteralPath $Path -PathType Leaf)
|
|
43
47
|
}
|
|
44
48
|
|
|
49
|
+
function Get-ResearchFileContent {
|
|
50
|
+
# File seam: returns the research file text, or $null when no file exists at the path.
|
|
51
|
+
[CmdletBinding()]
|
|
52
|
+
[OutputType([string])]
|
|
53
|
+
param([Parameter(Mandatory = $true)] [string] $Path)
|
|
54
|
+
|
|
55
|
+
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { return $null }
|
|
56
|
+
return Get-Content -LiteralPath $Path -Raw -ErrorAction Stop
|
|
57
|
+
}
|
|
58
|
+
|
|
45
59
|
function Get-ResearchPathFromOutput {
|
|
46
60
|
[CmdletBinding()]
|
|
47
61
|
[OutputType([string])]
|
|
@@ -268,12 +282,17 @@ function Invoke-TaskResearcherOutputValidation {
|
|
|
268
282
|
return @{ Ok = $false; Message = $feasibilityResult.Message }
|
|
269
283
|
}
|
|
270
284
|
|
|
271
|
-
|
|
272
|
-
|
|
285
|
+
try { $researchContent = Get-ResearchFileContent -Path $researchPath } catch { return @{ Ok = $false; Message = "task-researcher hook: failed to read research-path '$researchPath': $($_.Exception.Message)" } }
|
|
286
|
+
if ($null -ne $researchContent) {
|
|
273
287
|
$numericEvidenceResult = Test-NumericDerivationEvidence -Content $researchContent
|
|
274
288
|
if (-not $numericEvidenceResult.Ok) {
|
|
275
289
|
return $numericEvidenceResult
|
|
276
290
|
}
|
|
291
|
+
# Issue #964: feature research carries a Pre-Mortem; one-off research under docs/research/ is exempt.
|
|
292
|
+
if ((($researchPath -replace '\\', '/') -replace '^\./', '').StartsWith('docs/features/', [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
293
|
+
$preMortem = Test-PreMortemSection -Content ([string]$researchContent)
|
|
294
|
+
if (-not $preMortem.Ok) { return @{ Ok = $false; Message = "task-researcher hook: feature research '$researchPath' $($preMortem.Message)" } }
|
|
295
|
+
}
|
|
277
296
|
}
|
|
278
297
|
|
|
279
298
|
return @{ Ok = $true; Message = $null }
|