@danmoisan/drm-copilot-mcp 1.0.23 → 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.
Files changed (43) hide show
  1. package/out/mcp-server.js +504 -174
  2. package/package.json +1 -1
  3. package/resources/claude-customizations/.claude/agents/feature-review.md +5 -3
  4. package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +11 -4
  5. package/resources/claude-customizations/.claude/agents/parallel-planner.md +5 -2
  6. package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +28 -8
  7. package/resources/claude-customizations/.claude/hooks/validate-discovery-artifact-gate.ps1 +28 -8
  8. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +117 -46
  9. package/resources/claude-customizations/.claude/lib/bash/parallel-manifest-validate.sh +115 -3
  10. package/resources/claude-customizations/.claude/lib/codex-routing/CodexDeployment.psm1 +312 -0
  11. package/resources/claude-customizations/.claude/lib/codex-routing/CodexTopology.psm1 +392 -0
  12. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryValidation.psm1 +500 -0
  13. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorState.psm1 +58 -67
  14. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCheckpointValue.psm1 +383 -0
  15. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCodexModelReceipts.psm1 +297 -0
  16. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCodexTopologyReceipts.psm1 +298 -0
  17. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCompletion.psm1 +232 -43
  18. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCompletionChecks.psm1 +416 -0
  19. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateModelReceipts.psm1 +366 -0
  20. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateReceipts.psm1 +408 -0
  21. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRoutingContract.psm1 +428 -0
  22. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRoutingMatrix.psm1 +377 -0
  23. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateUnconditional.psm1 +166 -0
  24. package/resources/claude-customizations/.claude/rules/general-unit-test.md +1 -1
  25. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +28 -3
  26. package/resources/claude-customizations/.claude/rules/powershell.md +1 -1
  27. package/resources/claude-customizations/.claude/rules/quality-tiers.md +3 -3
  28. package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +4 -4
  29. package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +10 -5
  30. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +108 -34
  31. package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +71 -9
  32. package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +7 -3
  33. package/resources/claude-customizations/.claude/skills/powershell-qa-gate/SKILL.md +1 -1
  34. package/resources/claude-customizations/.claude-variants/csharp-legacy/rules/csharp.md +4 -4
  35. package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +5 -3
  36. package/resources/claude-customizations/config/blast-radius.json +1 -3
  37. package/resources/claude-customizations/pack-manifests/core.json +12 -0
  38. package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +1 -1
  39. package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +3 -3
  40. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp/SKILL.md +3 -3
  41. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +5 -3
  42. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  43. 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 8, and must not be a boolean.
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 8. When absent it defaults to `4`: the accessor `manifest_max_concurrency(mapping)` returns the default and the validator emits no error for absence.
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 8 inclusive and defaults to `4` when absent from the manifest. The design document sets only the default of 4; the upper bound of 8 is adopted here for symmetry with the epic surface, whose `max_parallel_features` is validated as `1..8`. The bound is recorded in this rule file so that downstream features do not re-litigate it. Booleans are rejected even though `True` and `False` are integers in Python.
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
- - Branch coverage must remain >= 75% across all tiers (T1–T4).
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% apply uniformly across T1–T4; tier-specific lower coverage floors are not used in this repository.
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`.