@danmoisan/drm-copilot-mcp 1.0.24 → 1.0.26
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 +504 -174
- 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/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/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/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/parallel-orchestration.md +28 -3
- 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/skills/feature-review-workflow/SKILL.md +4 -4
- package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +10 -5
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +108 -34
- package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +71 -9
- 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 +1 -3
- package/resources/claude-customizations/pack-manifests/core.json +12 -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/powershell/PoshQC/settings/pester.runsettings.psd1 +21 -0
|
@@ -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.
|
|
@@ -26,7 +26,7 @@ Enforced by `validate_parallel_orchestrator_state_text(text, *, require_complete
|
|
|
26
26
|
|
|
27
27
|
3. **Mode enum.** `mode` must be `closed` or `open`.
|
|
28
28
|
|
|
29
|
-
4. **Bounded concurrency.** `max_concurrency` must be an integer from 1 through
|
|
29
|
+
4. **Bounded concurrency.** `max_concurrency` must be an integer from 1 through 32, and must not be a boolean.
|
|
30
30
|
|
|
31
31
|
5. **Item uniqueness and shape.** Each `items[]` entry must be an object whose `issue_num` is a positive integer unique across items and whose `feature_folder` is a non-empty string.
|
|
32
32
|
|
|
@@ -98,7 +98,7 @@ Enforced by `validate_parallel_manifest_text(text)` in `scripts/dev_tools/parall
|
|
|
98
98
|
|
|
99
99
|
- **M3 — Mode default.** `mode`, when present, must be `closed` or `open`. When absent it defaults to `closed`: the accessor `manifest_mode(mapping)` returns the default and the validator emits no error for absence.
|
|
100
100
|
|
|
101
|
-
- **M4 — Concurrency default.** `max_concurrency`, when present, must be an integer from 1 through
|
|
101
|
+
- **M4 — Concurrency default.** `max_concurrency`, when present, must be an integer from 1 through 32. When absent it defaults to `4`: the accessor `manifest_max_concurrency(mapping)` returns the default and the validator emits no error for absence.
|
|
102
102
|
|
|
103
103
|
- **M5 — Created-at.** `created_at` must be a non-empty string.
|
|
104
104
|
|
|
@@ -106,6 +106,22 @@ Enforced by `validate_parallel_manifest_text(text)` in `scripts/dev_tools/parall
|
|
|
106
106
|
|
|
107
107
|
- **M7 — Prohibited keys.** No `depends_on` key may appear at any level, and no `integration_branch` key may appear at top level. Presence is an explicit rejection.
|
|
108
108
|
|
|
109
|
+
- **M8 — Expected conflict components (optional assertion).** `expected_conflict_components`, when present, must be a list. Each entry must be an object carrying a required `members` list that is non-empty and holds positive integers, each of which resolves to an `items[].issue_num`, with no `issue_num` appearing in more than one component; and an optional `name` that, when present, must be a non-empty string. When the key is ABSENT the invariant contributes zero errors and the manifest's error list is byte-identical to what it was before M8 existed.
|
|
110
|
+
|
|
111
|
+
The value must be authored as a YAML BLOCK sequence. The destination-runtime bash YAML subset parser (`.claude/lib/bash/parallel-yaml-scan.sh`) rejects a non-empty flow collection, so a flow-style value such as `members: [101, 102]` is outside the supported subset and is not accepted on the bash path.
|
|
112
|
+
|
|
113
|
+
`expected_conflict_components` is an ASSERTION, not a declaration. It NEVER overrides a derived conflict edge, NEVER feeds `compute_cohorts`, and NEVER influences scheduling. It is consumed by a planner diagnostic (`scripts/dev_tools/parallel_lane_assertion.py`), invoked advisory-only, whose findings never block. Its name deliberately references the DERIVED conflict graph: the field asserts what the operator expects blast-radius derivation to produce, and a mismatch is a signal to re-examine the radii, never a licence to edit the graph. The prohibition on narrowing a radius to suppress an edge is unaffected, as is the `depends_on` prohibition of invariant 10, P3, and M7 — this key is not a dependency edge and does not express ordering.
|
|
114
|
+
|
|
115
|
+
Example, in the mandatory block-sequence form:
|
|
116
|
+
|
|
117
|
+
```yaml
|
|
118
|
+
expected_conflict_components:
|
|
119
|
+
- name: hooks-lane # optional, diagnostic label only
|
|
120
|
+
members: # required, non-empty, positive ints
|
|
121
|
+
- 101
|
|
122
|
+
- 102
|
|
123
|
+
```
|
|
124
|
+
|
|
109
125
|
## Cache Doctrine — the checkpoint is not the source of truth
|
|
110
126
|
|
|
111
127
|
The parallel-orchestrator checkpoint is a CACHE of durable state, not the source of truth. Every field it records is re-derivable from the repository and from GitHub:
|
|
@@ -135,10 +151,19 @@ Per-item `merge_commit_sha` is retained; only the run-level merge-pull-request b
|
|
|
135
151
|
|
|
136
152
|
## Concurrency Bound (A7)
|
|
137
153
|
|
|
138
|
-
`max_concurrency` is bounded at 1 through
|
|
154
|
+
`max_concurrency` is bounded at 1 through 32 inclusive and defaults to `4` when absent from the manifest. The design document sets only the default of 4. Booleans are rejected even though `True` and `False` are integers in Python.
|
|
155
|
+
|
|
156
|
+
The upper bound is derived from a constraint analysis of this surface alone. No other surface's bound is a reason for it. The findings recorded here so that downstream features do not re-litigate them:
|
|
157
|
+
|
|
158
|
+
- **No constraint binds hard below O(100) concurrent worktrees.** Git worktrees, per-item feature branches, checkpoint size, and the cohort-coloring computation all scale well past a hundred concurrent items; none of them fails, or degrades sharply, anywhere near 32.
|
|
159
|
+
- **The first-binding constraint is GitHub Actions job concurrency**, which begins to bite at roughly 10 to 20 concurrent items on a typical plan. It binds by QUEUING, not by failing: excess jobs wait for a runner and the run completes more slowly. A `max_concurrency` above that point is therefore not an error, merely a setting whose marginal throughput is absorbed by the queue.
|
|
160
|
+
- **The ceiling of 32 is a SANITY limit, not a capacity limit.** Its purpose is to reject an order-of-magnitude operator typo (`320` for `32`), not to express a supported maximum. Do not read a value at or below 32 as an assurance that the runner pool can serve it.
|
|
161
|
+
- **Under the per-edge cohort barrier `max_concurrency` is a pure throughput throttle.** Mutual exclusion inside a conflict component is automatic: a conflicting neighbour in a strictly prior current-generation cohort must be `merged` or `worktree_removed` before an item starts, so raising the cap can never co-schedule two conflicting items. Raising it changes only how many independent lanes advance at once.
|
|
139
162
|
|
|
140
163
|
The bound is enforced in three places with the same semantics: orchestrator invariant 4, planner invariant P2, and manifest invariant M4.
|
|
141
164
|
|
|
165
|
+
The epic surface is unaffected. `max_parallel_features` remains bounded at `1..8`; it is a different field on a different surface and is not changed by this bound.
|
|
166
|
+
|
|
142
167
|
## Drift-Event Recording Rule (A8)
|
|
143
168
|
|
|
144
169
|
`drift_events[].action` is the two-member enum `{raised_blocking_finding, halted_later_started_item}`. The recording rule is: one event per drift occurrence, carrying the STRONGEST action taken. `halted_later_started_item` subsumes `raised_blocking_finding`, so an occurrence that halted a later-started item records exactly one event with `action == 'halted_later_started_item'` and does not additionally record a `raised_blocking_finding` event for the same occurrence.
|
|
@@ -61,7 +61,7 @@ Introduce the smallest seam that enables reliable mocking. Apply these options i
|
|
|
61
61
|
- Mock sparingly; prefer real code paths.
|
|
62
62
|
- No external dependencies in unit tests.
|
|
63
63
|
- Line coverage must remain >= 85% across all tiers (T1–T4) per `.claude/rules/quality-tiers.md`.
|
|
64
|
-
-
|
|
64
|
+
- Pester reports **command (instruction) coverage and line coverage only**. The uniform line-coverage threshold (>= 85% per `.claude/rules/quality-tiers.md`) applies. Branch coverage is not measurable by Pester for PowerShell; there is no PowerShell branch-coverage gate. This removes an unevaluable threshold, not a measurement obligation: PowerShell production files remain in the coverage denominator per the Coverage Exclusion Policy in `.claude/rules/general-unit-test.md`, and command coverage is reported for information only, with no threshold attached.
|
|
65
65
|
- Coverage regression on changed lines is a blocking finding.
|
|
66
66
|
|
|
67
67
|
### Deterministic Test Requirements
|
|
@@ -22,7 +22,7 @@ This rule defines the T1–T4 module rigor tier system used by all CI gates in t
|
|
|
22
22
|
|
|
23
23
|
## Uniform-vs-Tier-Dependent Gate Matrix
|
|
24
24
|
|
|
25
|
-
Per Authoritative Decision #2, line and branch coverage thresholds are uniform across all tiers. Other gates remain tier-dependent.
|
|
25
|
+
Per Authoritative Decision #2, line and branch coverage thresholds are uniform across all tiers. The line threshold applies to every coverage language; the branch threshold applies to languages whose coverage tooling measures branch coverage. Other gates remain tier-dependent.
|
|
26
26
|
|
|
27
27
|
### Uniform across all tiers (T1–T4)
|
|
28
28
|
|
|
@@ -31,7 +31,7 @@ Per Authoritative Decision #2, line and branch coverage thresholds are uniform a
|
|
|
31
31
|
- Type errors: 0.
|
|
32
32
|
- Architecture violations: 0.
|
|
33
33
|
- Line coverage: >= 85%.
|
|
34
|
-
- Branch coverage: >= 75
|
|
34
|
+
- Branch coverage: >= 75% for languages whose coverage tooling measures branch coverage. PowerShell (Pester) and bash (kcov) are exempt from this threshold because neither tool measures branch coverage; no branch-coverage gate applies to them.
|
|
35
35
|
- No regression on changed lines.
|
|
36
36
|
|
|
37
37
|
### Tier-dependent
|
|
@@ -48,4 +48,4 @@ Per Authoritative Decision #2, line and branch coverage thresholds are uniform a
|
|
|
48
48
|
|
|
49
49
|
## Rationale (uniform coverage thresholds)
|
|
50
50
|
|
|
51
|
-
High test coverage is a fundamental quality-control design choice that enables autonomous agentic development and trust in the work product. For that reason, line coverage >= 85% and branch coverage >= 75%
|
|
51
|
+
High test coverage is a fundamental quality-control design choice that enables autonomous agentic development and trust in the work product. For that reason, line coverage >= 85% applies uniformly across T1–T4 to every coverage language, and branch coverage >= 75% applies uniformly across T1–T4 to every language whose coverage tooling measures branch coverage; tier-specific lower coverage floors are not used in this repository. The branch threshold is not applied to PowerShell or bash because Pester and kcov do not measure branch coverage. That exemption is a capability limit on an unevaluable threshold, not a licence to exclude files from measurement: PowerShell and bash production files remain in the coverage denominator under the Coverage Exclusion Policy in `.claude/rules/general-unit-test.md`.
|