@danmoisan/drm-copilot-mcp 1.0.24 → 1.0.27
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 +1045 -214
- package/package.json +1 -1
- package/resources/claude-customizations/.claude/agents/feature-review.md +5 -3
- package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +11 -4
- package/resources/claude-customizations/.claude/agents/parallel-planner.md +5 -2
- package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +28 -8
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +109 -5
- package/resources/claude-customizations/.claude/hooks/enforce-mermaid-validation.ps1 +390 -0
- package/resources/claude-customizations/.claude/hooks/validate-discovery-artifact-gate.ps1 +28 -8
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +117 -46
- package/resources/claude-customizations/.claude/lib/bash/parallel-manifest-validate.sh +115 -3
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadius.psm1 +105 -2
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusConfig.psm1 +32 -52
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +107 -99
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusNormalization.psm1 +295 -0
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusValidation.psm1 +9 -3
- package/resources/claude-customizations/.claude/lib/codex-routing/CodexDeployment.psm1 +312 -0
- package/resources/claude-customizations/.claude/lib/codex-routing/CodexTopology.psm1 +392 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryValidation.psm1 +500 -0
- package/resources/claude-customizations/.claude/lib/mermaid/MermaidGrammar.psm1 +491 -0
- package/resources/claude-customizations/.claude/lib/mermaid/MermaidLineScanner.psm1 +488 -0
- package/resources/claude-customizations/.claude/lib/mermaid/MermaidMarkdownFences.psm1 +298 -0
- package/resources/claude-customizations/.claude/lib/mermaid/MermaidValidation.psm1 +496 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorState.psm1 +58 -67
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCheckpointValue.psm1 +383 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCodexModelReceipts.psm1 +297 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCodexTopologyReceipts.psm1 +298 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCompletion.psm1 +232 -43
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCompletionChecks.psm1 +416 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateModelReceipts.psm1 +366 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateReceipts.psm1 +408 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRoutingContract.psm1 +428 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRoutingMatrix.psm1 +377 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateUnconditional.psm1 +166 -0
- package/resources/claude-customizations/.claude/rules/general-unit-test.md +1 -1
- package/resources/claude-customizations/.claude/rules/mermaid.md +142 -0
- package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +88 -3
- package/resources/claude-customizations/.claude/rules/plan-acceptance-gates.md +116 -0
- package/resources/claude-customizations/.claude/rules/powershell.md +1 -1
- package/resources/claude-customizations/.claude/rules/quality-tiers.md +3 -3
- package/resources/claude-customizations/.claude/settings.json +5 -0
- package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +15 -0
- package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +13 -0
- package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +6 -0
- package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +4 -4
- package/resources/claude-customizations/.claude/skills/mermaid-diagram/SKILL.md +184 -0
- package/resources/claude-customizations/.claude/skills/mermaid-diagram/references/c4.md +50 -0
- package/resources/claude-customizations/.claude/skills/mermaid-diagram/references/class.md +63 -0
- package/resources/claude-customizations/.claude/skills/mermaid-diagram/references/er.md +56 -0
- package/resources/claude-customizations/.claude/skills/mermaid-diagram/references/flowchart.md +68 -0
- package/resources/claude-customizations/.claude/skills/mermaid-diagram/references/gantt.md +51 -0
- package/resources/claude-customizations/.claude/skills/mermaid-diagram/references/other-types.md +82 -0
- package/resources/claude-customizations/.claude/skills/mermaid-diagram/references/pie.md +32 -0
- package/resources/claude-customizations/.claude/skills/mermaid-diagram/references/sequence.md +63 -0
- package/resources/claude-customizations/.claude/skills/mermaid-diagram/references/state.md +49 -0
- package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +10 -5
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +116 -41
- package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +95 -13
- package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +7 -3
- package/resources/claude-customizations/.claude/skills/powershell-qa-gate/SKILL.md +1 -1
- package/resources/claude-customizations/config/blast-radius.json +9 -3
- package/resources/claude-customizations/pack-manifests/core.json +31 -1
- package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +13 -0
- package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +3 -3
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +13 -0
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +37 -1
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Pinned routing-matrix constants and route accessors for the portable checks.
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Implements deliberate deviation PD-1 exactly as the feature spec records it.
|
|
7
|
+
The subset of `config/orchestration-routing.json` that the completion checks
|
|
8
|
+
consume - each route's `requires_pr_gate`, `requires_ci_gate`,
|
|
9
|
+
`required_agents`, `required_skills`, and `required_mcp_tools` - is embedded
|
|
10
|
+
here as pinned constants, following the established `ModelRouting.psm1:33-39`
|
|
11
|
+
pattern.
|
|
12
|
+
|
|
13
|
+
NO DISK READ AT VALIDATION TIME. This module never opens
|
|
14
|
+
`config/orchestration-routing.json`. That file is deliberately not shipped to
|
|
15
|
+
consumer repositories, and the Python reference crashes with an uncaught
|
|
16
|
+
FileNotFoundError in a repository that lacks it - even on a plain validator
|
|
17
|
+
call. A missing-config crash, or a blanket block, is precisely the portability
|
|
18
|
+
failure this feature exists to remove, so fail-closed-on-missing-config was
|
|
19
|
+
rejected in favour of pinned constants. The config is read only by the static
|
|
20
|
+
config-parity Pester test, which runs in drm-copilot where the file exists and
|
|
21
|
+
is the oracle that keeps these constants honest.
|
|
22
|
+
|
|
23
|
+
Route-value resolution is exported too, because two different Python rules
|
|
24
|
+
exist and both must be reproduced: the routing-contract and preparation
|
|
25
|
+
checks read the raw `route_id` value (falling back to `path_selected` only
|
|
26
|
+
when the `route_id` KEY is absent), while the gate helpers additionally
|
|
27
|
+
require that value to be a non-blank string.
|
|
28
|
+
|
|
29
|
+
Every accessor takes an optional -RoutingMatrix override so a caller can
|
|
30
|
+
supply an alternative matrix, mirroring the Python `routing_matrix` keyword.
|
|
31
|
+
The override exists for testability and for the malformed-matrix check; it is
|
|
32
|
+
never used to read from disk.
|
|
33
|
+
|
|
34
|
+
Every function is pure: it reads no file, starts no process, and never mutates
|
|
35
|
+
its input.
|
|
36
|
+
#>
|
|
37
|
+
|
|
38
|
+
Set-StrictMode -Version Latest
|
|
39
|
+
|
|
40
|
+
# Import the shared checkpoint-value primitives, resolved relative to this
|
|
41
|
+
# module's directory so the import travels with the pushed-down pack.
|
|
42
|
+
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'OrchestratorStateCheckpointValue.psm1') -Force
|
|
43
|
+
|
|
44
|
+
# The pinned routing-matrix subset. Each route records the two gate flags and the
|
|
45
|
+
# three required-name lists the completion checks consume. A gate flag of $null
|
|
46
|
+
# means the key is ABSENT from the config, which is semantically distinct from
|
|
47
|
+
# $false: an absent requires_ci_gate keeps the CI gate required, while an absent
|
|
48
|
+
# requires_pr_gate leaves the PR gate not required.
|
|
49
|
+
$script:PINNED_ROUTES = @{
|
|
50
|
+
small = @{
|
|
51
|
+
requires_pr_gate = $null
|
|
52
|
+
requires_ci_gate = $null
|
|
53
|
+
required_agents = @('atomic-planner', 'atomic-executor', 'feature-review')
|
|
54
|
+
required_skills = @('orchestrate', 'feature-promotion-lifecycle', 'atomic-plan-contract', 'acceptance-criteria-tracking', 'pr-context-artifacts', 'pr-base-branch-merge-base')
|
|
55
|
+
required_mcp_tools = @('new_potential_entry', 'potential_to_issue', 'new_active_feature_folder', 'collect_pr_context', 'validate_orchestration_artifacts')
|
|
56
|
+
}
|
|
57
|
+
large = @{
|
|
58
|
+
requires_pr_gate = $true
|
|
59
|
+
requires_ci_gate = $null
|
|
60
|
+
required_agents = @('task-researcher', 'prd-feature', 'atomic-planner', 'atomic-executor', 'feature-review', 'pr-author')
|
|
61
|
+
required_skills = @('orchestrate', 'feature-promotion-lifecycle', 'atomic-plan-contract', 'acceptance-criteria-tracking', 'pr-context-artifacts', 'pr-base-branch-merge-base')
|
|
62
|
+
required_mcp_tools = @('new_potential_entry', 'potential_to_issue', 'new_active_feature_folder', 'collect_pr_context', 'validate_orchestration_artifacts')
|
|
63
|
+
}
|
|
64
|
+
remediation = @{
|
|
65
|
+
requires_pr_gate = $null
|
|
66
|
+
requires_ci_gate = $null
|
|
67
|
+
required_agents = @('atomic-planner', 'atomic-executor', 'feature-review')
|
|
68
|
+
required_skills = @('orchestrate', 'atomic-plan-contract', 'acceptance-criteria-tracking', 'pr-context-artifacts')
|
|
69
|
+
required_mcp_tools = @('collect_pr_context', 'validate_orchestration_artifacts')
|
|
70
|
+
}
|
|
71
|
+
preparation = @{
|
|
72
|
+
requires_pr_gate = $null
|
|
73
|
+
requires_ci_gate = $false
|
|
74
|
+
required_agents = @('task-researcher', 'prd-feature', 'atomic-planner', 'atomic-executor')
|
|
75
|
+
required_skills = @('orchestrate', 'feature-promotion-lifecycle', 'atomic-plan-contract')
|
|
76
|
+
required_mcp_tools = @('new_potential_entry', 'potential_to_issue', 'new_active_feature_folder', 'validate_orchestration_artifacts')
|
|
77
|
+
}
|
|
78
|
+
parallel = @{
|
|
79
|
+
requires_pr_gate = $false
|
|
80
|
+
requires_ci_gate = $null
|
|
81
|
+
required_agents = @('orchestrator', 'pr-author')
|
|
82
|
+
required_skills = @('parallel-orchestrate', 'orchestrate', 'feature-promotion-lifecycle', 'atomic-plan-contract', 'acceptance-criteria-tracking', 'evidence-and-timestamp-conventions', 'pr-context-artifacts', 'pr-base-branch-merge-base')
|
|
83
|
+
required_mcp_tools = @('collect_pr_context', 'validate_orchestration_artifacts')
|
|
84
|
+
}
|
|
85
|
+
epic = @{
|
|
86
|
+
requires_pr_gate = $true
|
|
87
|
+
requires_ci_gate = $null
|
|
88
|
+
required_agents = @('orchestrator', 'pr-author')
|
|
89
|
+
required_skills = @('epic-orchestrate', 'orchestrate', 'feature-promotion-lifecycle', 'atomic-plan-contract', 'acceptance-criteria-tracking', 'evidence-and-timestamp-conventions', 'pr-context-artifacts', 'pr-base-branch-merge-base')
|
|
90
|
+
required_mcp_tools = @('collect_pr_context', 'validate_orchestration_artifacts')
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# The three per-route list names the completion and routing-contract checks read.
|
|
95
|
+
$script:ROUTE_LIST_NAMES = @('required_agents', 'required_skills', 'required_mcp_tools')
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
function Get-OrchestratorStateRoutingMatrix {
|
|
99
|
+
<#
|
|
100
|
+
.SYNOPSIS
|
|
101
|
+
Return the pinned routing matrix in the shape the Python matrix has.
|
|
102
|
+
.DESCRIPTION
|
|
103
|
+
Returns a hashtable with a single `routes` member, mirroring the top-level
|
|
104
|
+
shape of config/orchestration-routing.json so the accessors and the
|
|
105
|
+
malformed-matrix check operate on the same structure whether the matrix is
|
|
106
|
+
the pinned default or a caller-supplied override. No file is read.
|
|
107
|
+
.OUTPUTS
|
|
108
|
+
System.Collections.Hashtable with a `routes` member.
|
|
109
|
+
#>
|
|
110
|
+
[CmdletBinding()]
|
|
111
|
+
[OutputType([hashtable])]
|
|
112
|
+
param()
|
|
113
|
+
|
|
114
|
+
return @{ routes = $script:PINNED_ROUTES }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function Get-OrchestratorStateRoutingMatrixRouteMap {
|
|
118
|
+
<#
|
|
119
|
+
.SYNOPSIS
|
|
120
|
+
Return a matrix's routes mapping, or $null when the matrix is malformed.
|
|
121
|
+
.DESCRIPTION
|
|
122
|
+
Private-shape accessor mirroring the Python `matrix.get("routes")` guard.
|
|
123
|
+
A matrix whose `routes` member is absent or is not a mapping yields $null,
|
|
124
|
+
which the routing-contract check reports as a malformed matrix.
|
|
125
|
+
.PARAMETER RoutingMatrix
|
|
126
|
+
The matrix to inspect. When omitted, the pinned matrix is used.
|
|
127
|
+
.OUTPUTS
|
|
128
|
+
System.Collections.Hashtable, or $null when the matrix carries no routes
|
|
129
|
+
mapping.
|
|
130
|
+
#>
|
|
131
|
+
[CmdletBinding()]
|
|
132
|
+
[OutputType([hashtable])]
|
|
133
|
+
param(
|
|
134
|
+
[Parameter(Mandatory = $false)]
|
|
135
|
+
[AllowNull()]
|
|
136
|
+
[hashtable] $RoutingMatrix = $null
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
$matrix = if ($null -ne $RoutingMatrix) { $RoutingMatrix } else { Get-OrchestratorStateRoutingMatrix }
|
|
140
|
+
if (-not $matrix.ContainsKey('routes')) { return $null }
|
|
141
|
+
$routes = $matrix['routes']
|
|
142
|
+
if ($routes -isnot [hashtable]) { return $null }
|
|
143
|
+
return $routes
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function Get-OrchestratorStateRoute {
|
|
147
|
+
<#
|
|
148
|
+
.SYNOPSIS
|
|
149
|
+
Return one route's pinned entry, or $null when the route is unknown.
|
|
150
|
+
.DESCRIPTION
|
|
151
|
+
Accessor mirroring the Python `routes.get(route_id)` lookup plus its
|
|
152
|
+
`isinstance(raw_route, dict)` guard. A null or unknown route id, or a
|
|
153
|
+
malformed matrix, yields $null.
|
|
154
|
+
.PARAMETER RouteId
|
|
155
|
+
The route identifier. May be $null.
|
|
156
|
+
.PARAMETER RoutingMatrix
|
|
157
|
+
Optional matrix override. When omitted, the pinned matrix is used.
|
|
158
|
+
.OUTPUTS
|
|
159
|
+
System.Collections.Hashtable, or $null when the route is unknown.
|
|
160
|
+
#>
|
|
161
|
+
[CmdletBinding()]
|
|
162
|
+
[OutputType([hashtable])]
|
|
163
|
+
param(
|
|
164
|
+
[Parameter(Mandatory = $true)]
|
|
165
|
+
[AllowNull()]
|
|
166
|
+
[AllowEmptyString()]
|
|
167
|
+
[string] $RouteId,
|
|
168
|
+
|
|
169
|
+
[Parameter(Mandatory = $false)]
|
|
170
|
+
[AllowNull()]
|
|
171
|
+
[hashtable] $RoutingMatrix = $null
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
if ([string]::IsNullOrEmpty($RouteId)) { return $null }
|
|
175
|
+
$routes = Get-OrchestratorStateRoutingMatrixRouteMap -RoutingMatrix $RoutingMatrix
|
|
176
|
+
if ($null -eq $routes -or -not $routes.ContainsKey($RouteId)) { return $null }
|
|
177
|
+
$route = $routes[$RouteId]
|
|
178
|
+
if ($route -isnot [hashtable]) { return $null }
|
|
179
|
+
return $route
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function Get-OrchestratorStateRawRouteValue {
|
|
183
|
+
<#
|
|
184
|
+
.SYNOPSIS
|
|
185
|
+
Return the checkpoint's raw route value without a string requirement.
|
|
186
|
+
.DESCRIPTION
|
|
187
|
+
Reproduces the Python expression `state.get("route_id",
|
|
188
|
+
state.get("path_selected"))`. The distinction matters: when the `route_id`
|
|
189
|
+
KEY is present its value is used even if that value is null, and only an
|
|
190
|
+
ABSENT `route_id` key falls back to `path_selected`. The preparation
|
|
191
|
+
terminal check compares this raw value directly.
|
|
192
|
+
.PARAMETER State
|
|
193
|
+
The parsed checkpoint object.
|
|
194
|
+
.OUTPUTS
|
|
195
|
+
System.Object - the raw route value, which may be $null or a non-string.
|
|
196
|
+
#>
|
|
197
|
+
[CmdletBinding()]
|
|
198
|
+
[OutputType([object])]
|
|
199
|
+
param(
|
|
200
|
+
[Parameter(Mandatory = $true)]
|
|
201
|
+
[psobject] $State
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
$routeIdField = Get-CheckpointObjectMember -Owner $State -Name 'route_id'
|
|
205
|
+
if ($routeIdField.Present) { return $routeIdField.Value }
|
|
206
|
+
return (Get-CheckpointObjectMember -Owner $State -Name 'path_selected').Value
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function Get-OrchestratorStateSelectedRouteId {
|
|
210
|
+
<#
|
|
211
|
+
.SYNOPSIS
|
|
212
|
+
Return the checkpoint's selected route id, or $null when unusable.
|
|
213
|
+
.DESCRIPTION
|
|
214
|
+
Reproduces the Python `_selected_route_id` helper: the raw route value is
|
|
215
|
+
usable only when it is a non-blank string. Every gate accessor and the
|
|
216
|
+
phase-completeness check resolve the route through this rule.
|
|
217
|
+
.PARAMETER State
|
|
218
|
+
The parsed checkpoint object.
|
|
219
|
+
.OUTPUTS
|
|
220
|
+
System.String - the route id, or $null when absent, non-string, or blank.
|
|
221
|
+
#>
|
|
222
|
+
[CmdletBinding()]
|
|
223
|
+
[OutputType([string])]
|
|
224
|
+
param(
|
|
225
|
+
[Parameter(Mandatory = $true)]
|
|
226
|
+
[psobject] $State
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
$value = Get-OrchestratorStateRawRouteValue -State $State
|
|
230
|
+
if (-not ($value -is [string]) -or [string]::IsNullOrWhiteSpace([string]$value)) { return $null }
|
|
231
|
+
return [string]$value
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function Test-OrchestratorStateRouteRequiresPrGate {
|
|
235
|
+
<#
|
|
236
|
+
.SYNOPSIS
|
|
237
|
+
Report whether a route requires the completion PR gate.
|
|
238
|
+
.DESCRIPTION
|
|
239
|
+
Mirrors `route_requires_pr_gate`. The gate applies only when the route
|
|
240
|
+
exists and its `requires_pr_gate` value is exactly the boolean true, so a
|
|
241
|
+
missing route id, an unknown route, and an absent flag all report false.
|
|
242
|
+
.PARAMETER RouteId
|
|
243
|
+
The route identifier. May be $null.
|
|
244
|
+
.PARAMETER RoutingMatrix
|
|
245
|
+
Optional matrix override. When omitted, the pinned matrix is used.
|
|
246
|
+
.OUTPUTS
|
|
247
|
+
System.Boolean
|
|
248
|
+
#>
|
|
249
|
+
[CmdletBinding()]
|
|
250
|
+
[OutputType([bool])]
|
|
251
|
+
param(
|
|
252
|
+
[Parameter(Mandatory = $true)]
|
|
253
|
+
[AllowNull()]
|
|
254
|
+
[AllowEmptyString()]
|
|
255
|
+
[string] $RouteId,
|
|
256
|
+
|
|
257
|
+
[Parameter(Mandatory = $false)]
|
|
258
|
+
[AllowNull()]
|
|
259
|
+
[hashtable] $RoutingMatrix = $null
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
$route = Get-OrchestratorStateRoute -RouteId $RouteId -RoutingMatrix $RoutingMatrix
|
|
263
|
+
if ($null -eq $route -or -not $route.ContainsKey('requires_pr_gate')) { return $false }
|
|
264
|
+
return (($route['requires_pr_gate'] -is [bool]) -and [bool]$route['requires_pr_gate'])
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function Test-OrchestratorStateRouteRequiresCiGate {
|
|
268
|
+
<#
|
|
269
|
+
.SYNOPSIS
|
|
270
|
+
Report whether a route requires the completion CI gate.
|
|
271
|
+
.DESCRIPTION
|
|
272
|
+
Mirrors `route_requires_ci_gate`. Only an explicit boolean false opts a
|
|
273
|
+
route out, so a missing route id, an unknown route, and an absent flag all
|
|
274
|
+
keep the CI gate required. The asymmetry with the PR gate is deliberate
|
|
275
|
+
and is the historical behaviour the Python reference preserves.
|
|
276
|
+
.PARAMETER RouteId
|
|
277
|
+
The route identifier. May be $null.
|
|
278
|
+
.PARAMETER RoutingMatrix
|
|
279
|
+
Optional matrix override. When omitted, the pinned matrix is used.
|
|
280
|
+
.OUTPUTS
|
|
281
|
+
System.Boolean
|
|
282
|
+
#>
|
|
283
|
+
[CmdletBinding()]
|
|
284
|
+
[OutputType([bool])]
|
|
285
|
+
param(
|
|
286
|
+
[Parameter(Mandatory = $true)]
|
|
287
|
+
[AllowNull()]
|
|
288
|
+
[AllowEmptyString()]
|
|
289
|
+
[string] $RouteId,
|
|
290
|
+
|
|
291
|
+
[Parameter(Mandatory = $false)]
|
|
292
|
+
[AllowNull()]
|
|
293
|
+
[hashtable] $RoutingMatrix = $null
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
$route = Get-OrchestratorStateRoute -RouteId $RouteId -RoutingMatrix $RoutingMatrix
|
|
297
|
+
if ($null -eq $route -or -not $route.ContainsKey('requires_ci_gate')) { return $true }
|
|
298
|
+
return -not (($route['requires_ci_gate'] -is [bool]) -and -not [bool]$route['requires_ci_gate'])
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function Get-OrchestratorStateRouteRequiredList {
|
|
302
|
+
<#
|
|
303
|
+
.SYNOPSIS
|
|
304
|
+
Return one of a route's three required-name lists.
|
|
305
|
+
.DESCRIPTION
|
|
306
|
+
Mirrors the Python `_route_list` helper: a route that does not carry the
|
|
307
|
+
named list, or carries a value that is not a list of non-blank strings,
|
|
308
|
+
contributes an empty list rather than an error.
|
|
309
|
+
.PARAMETER RouteId
|
|
310
|
+
The route identifier. May be $null.
|
|
311
|
+
.PARAMETER ListName
|
|
312
|
+
One of required_agents, required_skills, required_mcp_tools.
|
|
313
|
+
.PARAMETER RoutingMatrix
|
|
314
|
+
Optional matrix override. When omitted, the pinned matrix is used.
|
|
315
|
+
.OUTPUTS
|
|
316
|
+
System.String[] - the required names in matrix order, possibly empty.
|
|
317
|
+
#>
|
|
318
|
+
[CmdletBinding()]
|
|
319
|
+
[OutputType([string[]])]
|
|
320
|
+
param(
|
|
321
|
+
[Parameter(Mandatory = $true)]
|
|
322
|
+
[AllowNull()]
|
|
323
|
+
[AllowEmptyString()]
|
|
324
|
+
[string] $RouteId,
|
|
325
|
+
|
|
326
|
+
[Parameter(Mandatory = $true)]
|
|
327
|
+
[ValidateSet('required_agents', 'required_skills', 'required_mcp_tools')]
|
|
328
|
+
[string] $ListName,
|
|
329
|
+
|
|
330
|
+
[Parameter(Mandatory = $false)]
|
|
331
|
+
[AllowNull()]
|
|
332
|
+
[hashtable] $RoutingMatrix = $null
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
$route = Get-OrchestratorStateRoute -RouteId $RouteId -RoutingMatrix $RoutingMatrix
|
|
336
|
+
if ($null -eq $route -or -not $route.ContainsKey($ListName)) { return [string[]]@() }
|
|
337
|
+
|
|
338
|
+
# A malformed list contributes nothing, matching the Python helper's
|
|
339
|
+
# None-to-empty-list conversion rather than raising.
|
|
340
|
+
$value = $route[$ListName]
|
|
341
|
+
if ($value -isnot [System.Array]) { return [string[]]@() }
|
|
342
|
+
foreach ($item in $value) {
|
|
343
|
+
if (-not ($item -is [string]) -or [string]::IsNullOrWhiteSpace([string]$item)) { return [string[]]@() }
|
|
344
|
+
}
|
|
345
|
+
return [string[]]@($value)
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function Get-OrchestratorStateRouteListName {
|
|
349
|
+
<#
|
|
350
|
+
.SYNOPSIS
|
|
351
|
+
Return the three per-route required-name list names.
|
|
352
|
+
.DESCRIPTION
|
|
353
|
+
Read-only accessor so the routing-contract check and the config-parity
|
|
354
|
+
test iterate one declared set of list names instead of restating it.
|
|
355
|
+
.OUTPUTS
|
|
356
|
+
System.String[] - the three list names.
|
|
357
|
+
#>
|
|
358
|
+
[CmdletBinding()]
|
|
359
|
+
[OutputType([string[]])]
|
|
360
|
+
param()
|
|
361
|
+
|
|
362
|
+
return [string[]]@($script:ROUTE_LIST_NAMES)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
# The matrix, the route lookup, both gate predicates, the required-list accessor,
|
|
366
|
+
# and both route-value resolvers are exported for the completion-checks and
|
|
367
|
+
# routing-contract modules and for the static config-parity test.
|
|
368
|
+
Export-ModuleMember -Function `
|
|
369
|
+
Get-OrchestratorStateRoutingMatrix, `
|
|
370
|
+
Get-OrchestratorStateRoutingMatrixRouteMap, `
|
|
371
|
+
Get-OrchestratorStateRoute, `
|
|
372
|
+
Get-OrchestratorStateRawRouteValue, `
|
|
373
|
+
Get-OrchestratorStateSelectedRouteId, `
|
|
374
|
+
Test-OrchestratorStateRouteRequiresPrGate, `
|
|
375
|
+
Test-OrchestratorStateRouteRequiresCiGate, `
|
|
376
|
+
Get-OrchestratorStateRouteRequiredList, `
|
|
377
|
+
Get-OrchestratorStateRouteListName
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Single entry point for the orchestrator-state unconditional check block.
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Composes the whole U family of the issue #475 parity inventory into one
|
|
7
|
+
portable call, mirroring the unconditional block of
|
|
8
|
+
`validate_orchestrator_state_text` in
|
|
9
|
+
`scripts/dev_tools/validate_orchestrator_state.py`:
|
|
10
|
+
|
|
11
|
+
U2-U4 required keys, step-status validity, blocked_reason validity, from
|
|
12
|
+
the existing `Get-OrchestratorStateBasePresenceError` in
|
|
13
|
+
`OrchestratorState.psm1`
|
|
14
|
+
U5 delegation_receipts shape, from `OrchestratorStateReceipts.psm1`
|
|
15
|
+
U6.R remediation_loop cycles, same module
|
|
16
|
+
U6.H human_interaction shape, same module
|
|
17
|
+
U6.C complexity_assessments per-entry, from `OrchestratorStateModelReceipts.psm1`
|
|
18
|
+
U6.M model_routing_receipts per-entry, same module
|
|
19
|
+
U6.X codex_model_routing_receipts, from `OrchestratorStateCodexModelReceipts.psm1`
|
|
20
|
+
U6.T codex_topology_receipts, from `OrchestratorStateCodexTopologyReceipts.psm1`
|
|
21
|
+
|
|
22
|
+
U1 (parse failure and non-object root) is the LOADER's contract, produced by
|
|
23
|
+
`Get-OrchestratorStateCheckpoint` in `OrchestratorState.psm1`. Every caller
|
|
24
|
+
runs the loader first and fails closed on its error before reaching this
|
|
25
|
+
function, so the loader and this function together are the complete U family.
|
|
26
|
+
The loader's path-prefixed message text is the one documented parity
|
|
27
|
+
divergence from the Python strings, recorded in the feature spec.
|
|
28
|
+
|
|
29
|
+
KEY-GATED SEMANTICS ARE PRESERVED. Each optional-key family runs only when
|
|
30
|
+
its key is PRESENT on the checkpoint, exactly as the Python
|
|
31
|
+
`optional_key_validators` loop does. An absent key contributes zero errors and
|
|
32
|
+
never produces a "must be a list when present" message. The distinction
|
|
33
|
+
matters: a present key holding null is validated, an absent key is not.
|
|
34
|
+
|
|
35
|
+
Families run in the Python reference's order so accumulated error output is
|
|
36
|
+
ordered identically.
|
|
37
|
+
|
|
38
|
+
The function is pure: it reads no file, starts no process, and never mutates
|
|
39
|
+
its input.
|
|
40
|
+
#>
|
|
41
|
+
|
|
42
|
+
Set-StrictMode -Version Latest
|
|
43
|
+
|
|
44
|
+
# Import the four leaf check modules eagerly. None of them imports this module,
|
|
45
|
+
# so this import graph has no cycle. OrchestratorState.psm1 is deliberately NOT
|
|
46
|
+
# imported here; it is loaded lazily inside the function, because the preflight
|
|
47
|
+
# path in that module imports this one and an eager import in both directions
|
|
48
|
+
# would couple their load order.
|
|
49
|
+
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'OrchestratorStateReceipts.psm1') -Force
|
|
50
|
+
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'OrchestratorStateModelReceipts.psm1') -Force
|
|
51
|
+
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'OrchestratorStateCodexModelReceipts.psm1') -Force
|
|
52
|
+
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'OrchestratorStateCodexTopologyReceipts.psm1') -Force
|
|
53
|
+
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'OrchestratorStateCheckpointValue.psm1') -Force
|
|
54
|
+
|
|
55
|
+
# The checkpoint key whose value carries the delegation receipts. It is read with
|
|
56
|
+
# the Python `is not None` guard rather than a presence guard, matching the
|
|
57
|
+
# reference.
|
|
58
|
+
$script:DELEGATION_RECEIPTS_KEY = 'delegation_receipts'
|
|
59
|
+
|
|
60
|
+
# The optional-key families, in the Python reference's evaluation order. The
|
|
61
|
+
# dispatch below routes each present key to its validator by name; the list is
|
|
62
|
+
# declared here so the order is stated once and is visible at a glance.
|
|
63
|
+
$script:OPTIONAL_KEYS = @(
|
|
64
|
+
'remediation_loop',
|
|
65
|
+
'human_interaction',
|
|
66
|
+
'complexity_assessments',
|
|
67
|
+
'model_routing_receipts',
|
|
68
|
+
'codex_model_routing_receipts',
|
|
69
|
+
'codex_topology_receipts'
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
function Import-OrchestratorStateBaseModule {
|
|
74
|
+
<#
|
|
75
|
+
.SYNOPSIS
|
|
76
|
+
Ensure the shared base-presence command is available, importing it lazily.
|
|
77
|
+
.DESCRIPTION
|
|
78
|
+
Private helper following the guarded lazy-import pattern used by
|
|
79
|
+
.claude/hooks/validate-orchestrator-output.ps1. The base module is
|
|
80
|
+
imported only when its command is not already resolvable, so this module
|
|
81
|
+
can be loaded from inside that module's own call path without an
|
|
82
|
+
eager two-way import.
|
|
83
|
+
.OUTPUTS
|
|
84
|
+
None.
|
|
85
|
+
#>
|
|
86
|
+
[CmdletBinding()]
|
|
87
|
+
[OutputType([void])]
|
|
88
|
+
param()
|
|
89
|
+
|
|
90
|
+
if (Get-Command -Name 'Get-OrchestratorStateBasePresenceError' -ErrorAction SilentlyContinue) { return }
|
|
91
|
+
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'OrchestratorState.psm1')
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function Get-OrchestratorStateUnconditionalError {
|
|
95
|
+
<#
|
|
96
|
+
.SYNOPSIS
|
|
97
|
+
Return every unconditional-block error for a parsed checkpoint.
|
|
98
|
+
.DESCRIPTION
|
|
99
|
+
The single U-family entry point. Runs the base-presence checks (U2-U4),
|
|
100
|
+
the delegation-receipt shape checks (U5), and each optional-key family
|
|
101
|
+
(U6.R, U6.H, U6.C, U6.M, U6.X, U6.T) in the Python reference's order.
|
|
102
|
+
Every optional family is key-gated: it runs only when its key is present
|
|
103
|
+
on the checkpoint, so an absent key contributes zero errors.
|
|
104
|
+
|
|
105
|
+
U1 is not produced here. Parse failure and a non-object root are the
|
|
106
|
+
loader's contract; callers run Get-OrchestratorStateCheckpoint first and
|
|
107
|
+
fail closed on its error before reaching this function.
|
|
108
|
+
.PARAMETER State
|
|
109
|
+
The parsed checkpoint object, as returned by the loader.
|
|
110
|
+
.OUTPUTS
|
|
111
|
+
System.String[] - zero or more error strings, in Python reference order.
|
|
112
|
+
#>
|
|
113
|
+
[CmdletBinding()]
|
|
114
|
+
[OutputType([string[]])]
|
|
115
|
+
param(
|
|
116
|
+
[Parameter(Mandatory = $true)]
|
|
117
|
+
[psobject] $State
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
Import-OrchestratorStateBaseModule
|
|
121
|
+
|
|
122
|
+
$errors = [System.Collections.Generic.List[string]]::new()
|
|
123
|
+
|
|
124
|
+
# U2-U4: required keys, step-status validity, blocked_reason validity.
|
|
125
|
+
$errors.AddRange([string[]]@(Get-OrchestratorStateBasePresenceError -State $State))
|
|
126
|
+
|
|
127
|
+
# U5: the delegation-receipt shape, guarded on a non-null value rather than
|
|
128
|
+
# key presence, matching the Python `receipts is not None` test.
|
|
129
|
+
$receipts = (Get-CheckpointObjectMember -Owner $State -Name $script:DELEGATION_RECEIPTS_KEY).Value
|
|
130
|
+
$errors.AddRange([string[]]@(Get-OrchestratorStateDelegationReceiptError -Value $receipts))
|
|
131
|
+
|
|
132
|
+
# U6: each optional-key family, in reference order, key-gated so an absent key
|
|
133
|
+
# never produces a "must be a list when present" message. The dispatch names
|
|
134
|
+
# every validator explicitly rather than invoking a command stored in a
|
|
135
|
+
# variable, so the enforcement-hook AST guard sees only constant command
|
|
136
|
+
# names and no dynamic invocation.
|
|
137
|
+
foreach ($key in $script:OPTIONAL_KEYS) {
|
|
138
|
+
$field = Get-CheckpointObjectMember -Owner $State -Name $key
|
|
139
|
+
if (-not $field.Present) { continue }
|
|
140
|
+
switch ($key) {
|
|
141
|
+
'remediation_loop' {
|
|
142
|
+
$errors.AddRange([string[]]@(Get-OrchestratorStateRemediationLoopError -Value $field.Value))
|
|
143
|
+
}
|
|
144
|
+
'human_interaction' {
|
|
145
|
+
$errors.AddRange([string[]]@(Get-OrchestratorStateHumanInteractionError -Value $field.Value))
|
|
146
|
+
}
|
|
147
|
+
'complexity_assessments' {
|
|
148
|
+
$errors.AddRange([string[]]@(Get-OrchestratorStateComplexityAssessmentError -Value $field.Value))
|
|
149
|
+
}
|
|
150
|
+
'model_routing_receipts' {
|
|
151
|
+
$errors.AddRange([string[]]@(Get-OrchestratorStateModelRoutingReceiptError -Value $field.Value))
|
|
152
|
+
}
|
|
153
|
+
'codex_model_routing_receipts' {
|
|
154
|
+
$errors.AddRange([string[]]@(Get-OrchestratorStateCodexModelRoutingReceiptError -Value $field.Value))
|
|
155
|
+
}
|
|
156
|
+
'codex_topology_receipts' {
|
|
157
|
+
$errors.AddRange([string[]]@(Get-OrchestratorStateCodexTopologyReceiptError -Value $field.Value))
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return $errors.ToArray()
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
# Only the aggregate entry point is exported; the lazy-import helper is private.
|
|
166
|
+
Export-ModuleMember -Function Get-OrchestratorStateUnconditionalError
|
|
@@ -21,7 +21,7 @@ Every unit test must satisfy all five of these properties:
|
|
|
21
21
|
## Coverage Requirements
|
|
22
22
|
|
|
23
23
|
- **Line coverage must remain >= 85% across all tiers (T1–T4).**
|
|
24
|
-
- **Branch coverage must remain >= 75% across all tiers (T1–T4).**
|
|
24
|
+
- **Branch coverage must remain >= 75% across all tiers (T1–T4) for languages whose coverage tooling measures branch coverage.** PowerShell (Pester) and bash (kcov) are the exceptions: neither tool measures branch coverage in any output format, so only the line threshold applies to them and there is no branch-coverage gate. This is a threshold exemption only; PowerShell and bash production files remain in the coverage denominator under the Coverage Exclusion Policy below.
|
|
25
25
|
- Code changes or refactors must not reduce coverage for the lines that were changed.
|
|
26
26
|
- Tier-specific lower coverage thresholds are not used in this repository. See `.claude/rules/quality-tiers.md` for the full tier system.
|
|
27
27
|
- Coverage is a supporting metric, not the sole quality gate. Untested critical behavior is not acceptable even if the overall percentage looks good.
|