@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.
- 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/.claude-variants/csharp-legacy/rules/csharp.md +4 -4
- package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +5 -3
- 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/.agents-variants/csharp-legacy/skills/csharp/SKILL.md +3 -3
- package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +5 -3
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +21 -0
package/package.json
CHANGED
|
@@ -109,12 +109,14 @@ Coverage metrics are mandatory for every language that has changed files in the
|
|
|
109
109
|
|
|
110
110
|
Coverage thresholds follow the uniform tier rule (Authoritative Decision #2) defined in `.claude/rules/quality-tiers.md`:
|
|
111
111
|
|
|
112
|
-
- **New code files** (files added in this feature, not previously existing): line coverage >= 85%, branch coverage >= 75
|
|
113
|
-
- **Modified files** (files that existed before and were changed): line coverage >= 85%, branch coverage >= 75
|
|
114
|
-
- **Repo-wide per language**: line coverage >= 85%, branch coverage >= 75
|
|
112
|
+
- **New code files** (files added in this feature, not previously existing): line coverage >= 85%, branch coverage >= 75% for branch-capable languages.
|
|
113
|
+
- **Modified files** (files that existed before and were changed): line coverage >= 85%, branch coverage >= 75% for branch-capable languages, and no regression on changed lines relative to baseline.
|
|
114
|
+
- **Repo-wide per language**: line coverage >= 85%, branch coverage >= 75% for branch-capable languages.
|
|
115
115
|
|
|
116
116
|
Tier-specific lower thresholds are not used.
|
|
117
117
|
|
|
118
|
+
The branch threshold applies only to branch-capable languages — TypeScript, Python, and C#. PowerShell is a coverage language and is fully subject to the line threshold and the no-regression requirement, but Pester measures command (instruction) coverage and line coverage only, so no branch percentage exists to evaluate and no branch threshold applies to it (see `.claude/rules/powershell.md`). Do not record FAIL for an absent PowerShell branch figure.
|
|
119
|
+
|
|
118
120
|
### Verification Procedure
|
|
119
121
|
|
|
120
122
|
For each language that has changed files in the feature branch:
|
|
@@ -187,10 +187,17 @@ Read `cohorts[] { index, generation, item_keys[] }` and schedule from it exactly
|
|
|
187
187
|
|
|
188
188
|
Two scheduling rules govern every launch:
|
|
189
189
|
|
|
190
|
-
1. **Cohort barrier.**
|
|
191
|
-
`
|
|
192
|
-
`
|
|
193
|
-
|
|
190
|
+
1. **Cohort barrier (per-edge).** An item may start only when every conflicting neighbour
|
|
191
|
+
(`conflict_edges[]`) that sits in a strictly prior current-generation cohort has `merge_status`
|
|
192
|
+
of `merged` or `worktree_removed`. `ci_green` does not satisfy the barrier. Same-cohort and
|
|
193
|
+
later-cohort neighbours do not hold an item back, and an item with no conflicting prior-cohort
|
|
194
|
+
neighbour may start regardless of other cohorts' progress. Evaluate the predicate only against
|
|
195
|
+
durable state confirmed by `git` and `gh` commands, never from in-memory notifications. A
|
|
196
|
+
blocked item is neither `merged` nor `worktree_removed`, so it holds only its own conflicting
|
|
197
|
+
later-cohort neighbours and, transitively, the tail of its own conflict component; unrelated
|
|
198
|
+
lanes keep advancing. `current_cohort` is a PROGRESS INDICATOR — the lowest current-generation
|
|
199
|
+
cohort index still holding a non-terminal, non-withdrawn item, updated only on the same durable
|
|
200
|
+
confirmation — and gates nothing.
|
|
194
201
|
2. **`max_concurrency` slot filling.** `max_concurrency` caps the number of simultaneously in-flight
|
|
195
202
|
items independently of cohort size. Fill slots in ascending item-key (`issue_num`) order, and
|
|
196
203
|
refill each freed slot with the next unstarted item of the current cohort in the same ascending
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: parallel-planner
|
|
3
3
|
model: opus
|
|
4
|
-
description: Planning half of the parallel orchestration surface. It performs item intake over issue numbers and potential-entry paths, drives per-item preparation (promotion, research, spec/user-story, atomic plan, preflight clearance) through
|
|
4
|
+
description: Planning half of the parallel orchestration surface. It performs item intake over issue numbers and potential-entry paths, drives per-item preparation (promotion, research, spec/user-story, atomic plan, preflight clearance) through preparation-mode Agent(orchestrator) delegations launched in bounded waves of at most max_concurrency, computes and validates each item's blast radius, seeds the generation-0 cohort table, writes the parallel run manifest and the planner checkpoint, and emits the parallel-orchestrator kickoff prompt artifact. Performs no atomic execution, PR authoring, or CI monitoring.
|
|
5
5
|
tools:
|
|
6
6
|
- "Agent(orchestrator)"
|
|
7
7
|
- Read
|
|
@@ -90,7 +90,10 @@ On every invocation:
|
|
|
90
90
|
## Delegation Model
|
|
91
91
|
|
|
92
92
|
You delegate exclusively through `Agent(orchestrator)`, one delegation per item, each carrying the
|
|
93
|
-
preparation-mode kickoff line defined in the `parallel-plan` skill.
|
|
93
|
+
preparation-mode kickoff line defined in the `parallel-plan` skill. Delegations are launched in
|
|
94
|
+
BOUNDED WAVES of at most `max_concurrency`, computed with
|
|
95
|
+
`bash .claude/lib/bash/compute-concurrency-batches.sh`, with wave *k+1* launched only after every
|
|
96
|
+
child of wave *k* has terminated. Never launch every item's preparation at once. Each child `orchestrator` runs
|
|
94
97
|
promotion, research, feature documents, atomic planning, and preflight clearance under
|
|
95
98
|
`route_id: preparation`, commits and pushes its own branch, then stops before any execution. You do
|
|
96
99
|
not delegate directly to `atomic-planner`, `atomic-executor`, `task-researcher`, or `prd-feature`;
|
|
@@ -25,8 +25,10 @@
|
|
|
25
25
|
interprets the CLI's exit code and captured output.
|
|
26
26
|
|
|
27
27
|
.NOTES
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
Requires PowerShell 7.4+ (the shared validation module uses
|
|
29
|
+
`Test-Json -SchemaFile` Draft 2020-12 support). Read-only validation gate that
|
|
30
|
+
invokes NO external process: validation runs in-process through
|
|
31
|
+
`.claude/lib/discovery-validation/DiscoveryValidation.psm1` (issue #475).
|
|
30
32
|
#>
|
|
31
33
|
[CmdletBinding()]
|
|
32
34
|
param()
|
|
@@ -34,11 +36,23 @@ param()
|
|
|
34
36
|
function Invoke-DiscoveryValidatorExe {
|
|
35
37
|
<#
|
|
36
38
|
.SYNOPSIS
|
|
37
|
-
Wrapper around the discovery-artifact validator
|
|
39
|
+
Wrapper around the discovery-artifact validator. Mockable seam.
|
|
38
40
|
.DESCRIPTION
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
Delegates to the portable PowerShell implementation in
|
|
42
|
+
`.claude/lib/discovery-validation/DiscoveryValidation.psm1`, keeping this
|
|
43
|
+
function's name, its `-ValidatorArgs <string[]>` parameter, and its
|
|
44
|
+
`@{ ExitCode; Output }` return shape unchanged so existing mocks and
|
|
45
|
+
`Should -Invoke` assertions continue to bind.
|
|
46
|
+
|
|
47
|
+
This no longer invokes a Python interpreter (issue #475). The `.claude/**`
|
|
48
|
+
payload ships to destinations with no guaranteed Python, Poetry, or
|
|
49
|
+
`scripts/dev_tools`, where the previous `python -m ...` call failed
|
|
50
|
+
obscurely or blocked every operation.
|
|
51
|
+
|
|
52
|
+
Success is SILENT by contract: a passing validation returns `ExitCode = 0`
|
|
53
|
+
with an EMPTY `Output`. The caller denies on a non-zero exit code OR on
|
|
54
|
+
non-empty output, so any success chatter here would deny a passing
|
|
55
|
+
validation (defect D-2).
|
|
42
56
|
#>
|
|
43
57
|
[CmdletBinding()]
|
|
44
58
|
[OutputType([hashtable])]
|
|
@@ -47,8 +61,14 @@ function Invoke-DiscoveryValidatorExe {
|
|
|
47
61
|
[string[]] $ValidatorArgs
|
|
48
62
|
)
|
|
49
63
|
|
|
50
|
-
$
|
|
51
|
-
|
|
64
|
+
$modulePath = Join-Path -Path $PSScriptRoot `
|
|
65
|
+
-ChildPath '../lib/discovery-validation/DiscoveryValidation.psm1'
|
|
66
|
+
if (-not (Test-Path -LiteralPath $modulePath -PathType Leaf)) {
|
|
67
|
+
return @{ ExitCode = 1; Output = "Discovery-validation module not found: $modulePath" }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
Import-Module -Name $modulePath -Force -ErrorAction Stop
|
|
71
|
+
return Invoke-DiscoveryArtifactValidation -ValidatorArgs $ValidatorArgs
|
|
52
72
|
}
|
|
53
73
|
|
|
54
74
|
function Get-DiscoveryArtifactType {
|
|
@@ -28,8 +28,10 @@
|
|
|
28
28
|
captured output.
|
|
29
29
|
|
|
30
30
|
.NOTES
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
Requires PowerShell 7.4+ (the shared validation module uses
|
|
32
|
+
`Test-Json -SchemaFile` Draft 2020-12 support). Read-only validation gate that
|
|
33
|
+
invokes NO external process: validation runs in-process through
|
|
34
|
+
`.claude/lib/discovery-validation/DiscoveryValidation.psm1` (issue #475).
|
|
33
35
|
#>
|
|
34
36
|
[CmdletBinding()]
|
|
35
37
|
param()
|
|
@@ -37,11 +39,23 @@ param()
|
|
|
37
39
|
function Invoke-DiscoveryValidatorExe {
|
|
38
40
|
<#
|
|
39
41
|
.SYNOPSIS
|
|
40
|
-
Wrapper around the discovery-artifact validator
|
|
42
|
+
Wrapper around the discovery-artifact validator. Mockable seam.
|
|
41
43
|
.DESCRIPTION
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
Delegates to the portable PowerShell implementation in
|
|
45
|
+
`.claude/lib/discovery-validation/DiscoveryValidation.psm1`, keeping this
|
|
46
|
+
function's name, its `-ValidatorArgs <string[]>` parameter, and its
|
|
47
|
+
`@{ ExitCode; Output }` return shape unchanged so existing mocks and
|
|
48
|
+
`Should -Invoke` assertions continue to bind.
|
|
49
|
+
|
|
50
|
+
This no longer invokes a Python interpreter (issue #475). The `.claude/**`
|
|
51
|
+
payload ships to destinations with no guaranteed Python, Poetry, or
|
|
52
|
+
`scripts/dev_tools`, where the previous `python -m ...` call failed
|
|
53
|
+
obscurely or blocked every operation.
|
|
54
|
+
|
|
55
|
+
Success is SILENT by contract: a passing validation returns `ExitCode = 0`
|
|
56
|
+
with an EMPTY `Output`. The caller denies on a non-zero exit code OR on
|
|
57
|
+
non-empty output, so any success chatter here would deny a passing
|
|
58
|
+
validation (defect D-2).
|
|
45
59
|
#>
|
|
46
60
|
[CmdletBinding()]
|
|
47
61
|
[OutputType([hashtable])]
|
|
@@ -50,8 +64,14 @@ function Invoke-DiscoveryValidatorExe {
|
|
|
50
64
|
[string[]] $ValidatorArgs
|
|
51
65
|
)
|
|
52
66
|
|
|
53
|
-
$
|
|
54
|
-
|
|
67
|
+
$modulePath = Join-Path -Path $PSScriptRoot `
|
|
68
|
+
-ChildPath '../lib/discovery-validation/DiscoveryValidation.psm1'
|
|
69
|
+
if (-not (Test-Path -LiteralPath $modulePath -PathType Leaf)) {
|
|
70
|
+
return @{ ExitCode = 1; Output = "Discovery-validation module not found: $modulePath" }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
Import-Module -Name $modulePath -Force -ErrorAction Stop
|
|
74
|
+
return Invoke-DiscoveryArtifactValidation -ValidatorArgs $ValidatorArgs
|
|
55
75
|
}
|
|
56
76
|
|
|
57
77
|
function Get-DiscoveryArtifactType {
|
|
@@ -149,31 +149,94 @@ function Test-HumanInteractionShape {
|
|
|
149
149
|
return @{ Ok = $true; Message = $null }
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
+
function Test-OrchestratorCheckpointStructure {
|
|
153
|
+
<#
|
|
154
|
+
.SYNOPSIS
|
|
155
|
+
Type-scoped structural check for the epic and parallel checkpoint types.
|
|
156
|
+
.DESCRIPTION
|
|
157
|
+
PD-3 implementation. The Python reference exposes no validation surface for
|
|
158
|
+
`epic-orchestrator-state` or `parallel-orchestrator-state` under this hook's
|
|
159
|
+
flag pair: argparse rejects the pair and exits 2 without running a single
|
|
160
|
+
check, so parity is UNDEFINED in this region. Rather than inherit an
|
|
161
|
+
undefined behavior, this hook defines it: the checkpoint must exist, parse
|
|
162
|
+
as JSON, and have an object root. That is the largest assertion that holds
|
|
163
|
+
for every checkpoint type without importing a schema this hook does not own.
|
|
164
|
+
|
|
165
|
+
Deliberately NOT applied here:
|
|
166
|
+
- the standard-checkpoint REQUIRED_STATE_KEYS presence block, whose key
|
|
167
|
+
set belongs to the standard checkpoint and would produce false
|
|
168
|
+
ROUTING_CONTRACT_BLOCKED verdicts against a well-formed epic or
|
|
169
|
+
parallel checkpoint (defect D-1),
|
|
170
|
+
- the model-routing gate, whose receipts live on the standard checkpoint.
|
|
171
|
+
|
|
172
|
+
The check fails closed: a missing file, unreadable content, invalid JSON, or
|
|
173
|
+
a non-object root all yield ExitCode 1 with the load error as Output.
|
|
174
|
+
.PARAMETER CheckpointPath
|
|
175
|
+
The path to the checkpoint JSON file.
|
|
176
|
+
.OUTPUTS
|
|
177
|
+
System.Collections.Hashtable with keys ExitCode (int, 0 or 1) and Output (string).
|
|
178
|
+
#>
|
|
179
|
+
[CmdletBinding()]
|
|
180
|
+
[OutputType([hashtable])]
|
|
181
|
+
param(
|
|
182
|
+
[Parameter(Mandatory = $true)]
|
|
183
|
+
[string] $CheckpointPath
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
# Get-OrchestratorStateCheckpoint already implements exactly the three
|
|
187
|
+
# structural conditions (exists, parses, object root) and reports each as a
|
|
188
|
+
# fail-closed Error string, so the structural leg reuses it rather than
|
|
189
|
+
# duplicating the load contract.
|
|
190
|
+
$loaded = Get-OrchestratorStateCheckpoint -CheckpointPath $CheckpointPath
|
|
191
|
+
if (-not $loaded.Ok) {
|
|
192
|
+
return @{ ExitCode = 1; Output = $loaded.Error }
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return @{ ExitCode = 0; Output = '' }
|
|
196
|
+
}
|
|
197
|
+
|
|
152
198
|
function Invoke-RoutingContractValidation {
|
|
153
199
|
<#
|
|
154
200
|
.SYNOPSIS
|
|
155
|
-
Runs the
|
|
156
|
-
|
|
201
|
+
Runs the portable routing-contract validation against the on-disk
|
|
202
|
+
checkpoint and reports whether it emitted errors.
|
|
157
203
|
.DESCRIPTION
|
|
158
|
-
Invokes the
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
204
|
+
Invokes the validation through an injectable scriptblock seam. As of issue
|
|
205
|
+
#475 the default Invoker names no interpreter and starts no subprocess: the
|
|
206
|
+
portable PowerShell path is the ONLY path, so the hook behaves identically
|
|
207
|
+
in this repository and in every destination that received only the
|
|
208
|
+
pushed-down `.claude` pack. The former capability-detection probe and the
|
|
209
|
+
interpreter-subprocess leg it guarded are both gone.
|
|
210
|
+
|
|
211
|
+
The default Invoker dispatches on ArtifactType:
|
|
212
|
+
|
|
213
|
+
orchestrator-state
|
|
214
|
+
the COMPLETE-PARITY completion validation
|
|
215
|
+
(Test-OrchestratorStateCompletionReadiness), a row-by-row port of the
|
|
216
|
+
Python call surface `--require-complete --require-model-routing`.
|
|
217
|
+
|
|
218
|
+
epic-orchestrator-state, parallel-orchestrator-state
|
|
219
|
+
the type-scoped structural check
|
|
220
|
+
(Test-OrchestratorCheckpointStructure): exists, parses as JSON,
|
|
221
|
+
object root. PD-3: this is DEFINED behavior in a region where Python
|
|
222
|
+
parity is UNDEFINED (argparse exit 2, zero checks run). It is a
|
|
223
|
+
design decision, not a deferral. The standard-checkpoint
|
|
224
|
+
REQUIRED_STATE_KEYS block and the model-routing gate are deliberately
|
|
225
|
+
not applied, which is the D-1 fix.
|
|
226
|
+
|
|
227
|
+
anything else
|
|
228
|
+
fail closed, naming the unsupported type. An unrecognized type must
|
|
229
|
+
never read as a clean pass.
|
|
230
|
+
|
|
231
|
+
ArtifactType defaults to 'orchestrator-state' so every existing caller of
|
|
232
|
+
this hook keeps its current behavior.
|
|
166
233
|
|
|
167
234
|
Returns a hashtable with keys:
|
|
168
|
-
- HasErrors: $true only when the
|
|
235
|
+
- HasErrors: $true only when the validation reported a non-zero exit
|
|
169
236
|
code; $false when it exited 0. The exit code is the sole
|
|
170
|
-
discriminator
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
- ErrorText: the validator's combined captured output text, carried
|
|
174
|
-
through unchanged: the error lines on a failure, and the
|
|
175
|
-
success line (Python CLI) or empty (portable fallback)
|
|
176
|
-
on a clean pass.
|
|
237
|
+
discriminator.
|
|
238
|
+
- ErrorText: the validation's output text, carried through unchanged:
|
|
239
|
+
the error lines on a failure, empty on a clean pass.
|
|
177
240
|
#>
|
|
178
241
|
[CmdletBinding()]
|
|
179
242
|
[OutputType([hashtable])]
|
|
@@ -187,29 +250,36 @@ function Invoke-RoutingContractValidation {
|
|
|
187
250
|
[Parameter(Mandatory = $false)]
|
|
188
251
|
[scriptblock] $Invoker = {
|
|
189
252
|
param($Path, $Type)
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
253
|
+
switch ($Type) {
|
|
254
|
+
'orchestrator-state' {
|
|
255
|
+
# Import the portable completion module only when its function is not
|
|
256
|
+
# already available, so a repeated call (or a test that pre-imports and
|
|
257
|
+
# mocks the function) does not reload the module and reset the seam.
|
|
258
|
+
if (-not (Get-Command -Name Test-OrchestratorStateCompletionReadiness -ErrorAction SilentlyContinue)) {
|
|
259
|
+
Import-Module (Join-Path $PSScriptRoot '../lib/orchestrator-state/OrchestratorStateCompletion.psm1') -Force
|
|
260
|
+
}
|
|
261
|
+
$portable = Test-OrchestratorStateCompletionReadiness -CheckpointPath $Path
|
|
262
|
+
[pscustomobject]@{
|
|
263
|
+
ExitCode = $portable.ExitCode
|
|
264
|
+
Output = $portable.Output
|
|
265
|
+
}
|
|
201
266
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
267
|
+
{ $_ -in @('epic-orchestrator-state', 'parallel-orchestrator-state') } {
|
|
268
|
+
# PD-3: defined fail-closed structural behavior in an
|
|
269
|
+
# undefined-parity region. See Test-OrchestratorCheckpointStructure.
|
|
270
|
+
$structural = Test-OrchestratorCheckpointStructure -CheckpointPath $Path
|
|
271
|
+
[pscustomobject]@{
|
|
272
|
+
ExitCode = $structural.ExitCode
|
|
273
|
+
Output = $structural.Output
|
|
274
|
+
}
|
|
208
275
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
276
|
+
default {
|
|
277
|
+
# Fail closed on an unwired type: an unrecognized artifact type is
|
|
278
|
+
# not a clean pass.
|
|
279
|
+
[pscustomobject]@{
|
|
280
|
+
ExitCode = 1
|
|
281
|
+
Output = "orchestrator hook: unsupported artifact type '$Type'; no validation surface is wired for it. Supported types: orchestrator-state, epic-orchestrator-state, parallel-orchestrator-state."
|
|
282
|
+
}
|
|
213
283
|
}
|
|
214
284
|
}
|
|
215
285
|
}
|
|
@@ -225,10 +295,10 @@ function Invoke-RoutingContractValidation {
|
|
|
225
295
|
$outputText = ([string]$result.Output).Trim()
|
|
226
296
|
}
|
|
227
297
|
|
|
228
|
-
# The exit code is the complete failure discriminator:
|
|
229
|
-
#
|
|
230
|
-
#
|
|
231
|
-
#
|
|
298
|
+
# The exit code is the complete failure discriminator: every dispatch leg
|
|
299
|
+
# returns a non-zero ExitCode with its error text on failure and ExitCode 0
|
|
300
|
+
# with empty Output on a clean pass, so output text must not influence this
|
|
301
|
+
# decision.
|
|
232
302
|
$hasErrors = ($exitCode -ne 0)
|
|
233
303
|
return @{ HasErrors = $hasErrors; ErrorText = $outputText }
|
|
234
304
|
}
|
|
@@ -313,9 +383,10 @@ function Invoke-OrchestratorOutputValidation {
|
|
|
313
383
|
return @{ Ok = $false; Message = $hiResult.Message }
|
|
314
384
|
}
|
|
315
385
|
|
|
316
|
-
# Delegate to the
|
|
317
|
-
# optional RoutingInvoker seam lets tests inject a mock; the
|
|
318
|
-
#
|
|
386
|
+
# Delegate to the portable routing-contract validation, dispatched on
|
|
387
|
+
# ArtifactType. The optional RoutingInvoker seam lets tests inject a mock; the
|
|
388
|
+
# default seam runs the in-process PowerShell validation and starts no
|
|
389
|
+
# subprocess.
|
|
319
390
|
$routingArgs = @{ CheckpointPath = $CheckpointPath; ArtifactType = $ArtifactType }
|
|
320
391
|
if ($PSBoundParameters.ContainsKey('RoutingInvoker') -and $null -ne $RoutingInvoker) {
|
|
321
392
|
$routingArgs['Invoker'] = $RoutingInvoker
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# parallel-manifest-validate.sh: sourceable bash port of
|
|
3
3
|
# scripts/dev_tools/parallel_manifest_contract.py. Validates a parallel-run
|
|
4
|
-
# manifest document against invariants M1 through
|
|
4
|
+
# manifest document against invariants M1 through M8 and exposes the two
|
|
5
5
|
# default-resolving accessors that every consumer uses instead of reading
|
|
6
6
|
# `mode` and `max_concurrency` directly.
|
|
7
7
|
#
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
# frontmatter checks short-circuit with a single error; then run identity
|
|
10
10
|
# (M2 parallel, M3 mode, M4 max_concurrency, M5 created_at) in schema field
|
|
11
11
|
# order; then the M7 prohibited-key scan, deep results in document order
|
|
12
|
-
# followed by the top-level results; then the M6 items collection
|
|
12
|
+
# followed by the top-level results; then the M6 items collection; then the
|
|
13
|
+
# key-gated M8 expected_conflict_components assertion.
|
|
13
14
|
#
|
|
14
15
|
# Every error string begins with the literal prefix `Parallel manifest` and
|
|
15
16
|
# ends with a period. The Python module remains the repository authority.
|
|
@@ -43,7 +44,7 @@ PM_DEFAULT_MAX_CONCURRENCY=4
|
|
|
43
44
|
|
|
44
45
|
# Inclusive bounds on a present `max_concurrency` (invariant M4, A7).
|
|
45
46
|
PM_MIN_CONCURRENCY=1
|
|
46
|
-
PM_MAX_CONCURRENCY=
|
|
47
|
+
PM_MAX_CONCURRENCY=32
|
|
47
48
|
|
|
48
49
|
# Keys the manifest rejects at any nesting level (invariant M7).
|
|
49
50
|
PM_DEEP_PROHIBITED_KEYS="depends_on"
|
|
@@ -54,6 +55,11 @@ PM_TOP_LEVEL_PROHIBITED_KEYS="integration_branch"
|
|
|
54
55
|
# Detail recorded when the parser refuses an out-of-subset construct.
|
|
55
56
|
PM_SUBSET_DETAIL=""
|
|
56
57
|
|
|
58
|
+
# Issue numbers already claimed by an earlier expected_conflict_components
|
|
59
|
+
# entry (invariant M8). Threaded across components so cross-component duplicate
|
|
60
|
+
# membership is decided in one pass; reset by pm_validate_text.
|
|
61
|
+
PM_CLAIMED=""
|
|
62
|
+
|
|
57
63
|
pm_parse_manifest() {
|
|
58
64
|
# Parse manifest text into the shared node table (invariant M1).
|
|
59
65
|
#
|
|
@@ -128,6 +134,108 @@ pm_validate_identity() {
|
|
|
128
134
|
fi
|
|
129
135
|
}
|
|
130
136
|
|
|
137
|
+
pm_declared_issue_nums() {
|
|
138
|
+
# Echo the space-separated issue_num values the items collection declares.
|
|
139
|
+
#
|
|
140
|
+
# Supplies the resolution target for invariant M8 without re-running the M6
|
|
141
|
+
# item validation: a malformed entry is reported once by M6 and simply
|
|
142
|
+
# contributes no resolvable key here.
|
|
143
|
+
local declared="" count index entry_path issue_type issue_value
|
|
144
|
+
[[ $(yp_type_of "items") == seq ]] || return 0
|
|
145
|
+
count=$(yp_count_of "items")
|
|
146
|
+
# Accept only entries whose primary key is already well formed; admitting a
|
|
147
|
+
# malformed one would produce a second, confusing M8 error for one defect.
|
|
148
|
+
for ((index = 0; index < count; index++)); do
|
|
149
|
+
entry_path="items[${index}]"
|
|
150
|
+
[[ $(yp_type_of "$entry_path") == map ]] || continue
|
|
151
|
+
issue_type=$(yp_type_of "${entry_path}.issue_num")
|
|
152
|
+
issue_value=$(yp_value_of "${entry_path}.issue_num")
|
|
153
|
+
pc_is_positive_integer "$issue_type" "$issue_value" || continue
|
|
154
|
+
declared="$declared $issue_value"
|
|
155
|
+
done
|
|
156
|
+
printf '%s' "$declared"
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
pm_validate_component_members() {
|
|
160
|
+
# Validate one component's members list against invariant M8.
|
|
161
|
+
#
|
|
162
|
+
# Args: $1 = node path of the members list, $2 = component-scoped context
|
|
163
|
+
# prefix, $3 = space-separated declared issue_num values. Appends every
|
|
164
|
+
# accepted key to PM_CLAIMED so the caller's running set enforces the
|
|
165
|
+
# no-duplicate-membership rule across components.
|
|
166
|
+
local path="$1" ctx="$2" declared="$3"
|
|
167
|
+
local members_type count=0 index slot entry_path member_type member_value
|
|
168
|
+
members_type=$(yp_type_of "$path")
|
|
169
|
+
if [[ $members_type == seq ]]; then
|
|
170
|
+
count=$(yp_count_of "$path")
|
|
171
|
+
fi
|
|
172
|
+
# A missing key, a scalar, and an empty list are one violation: the
|
|
173
|
+
# component asserts no membership and therefore carries no information.
|
|
174
|
+
if [[ $members_type != seq ]] || ((count == 0)); then
|
|
175
|
+
pc_error_add "$ctx members must be a non-empty list of positive integers."
|
|
176
|
+
return 0
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
# Three successive gates per member; the first failure ends that member's
|
|
180
|
+
# checks, because an unusable value cannot be resolved and an unresolved key
|
|
181
|
+
# cannot meaningfully duplicate another.
|
|
182
|
+
for ((index = 0; index < count; index++)); do
|
|
183
|
+
entry_path="${path}[${index}]"
|
|
184
|
+
slot="$ctx members[${index}]"
|
|
185
|
+
member_type=$(yp_type_of "$entry_path")
|
|
186
|
+
member_value=$(yp_value_of "$entry_path")
|
|
187
|
+
if ! pc_is_positive_integer "$member_type" "$member_value"; then
|
|
188
|
+
pc_error_add "$slot must be a positive integer; found: $(pi_repr_at "$entry_path")."
|
|
189
|
+
continue
|
|
190
|
+
fi
|
|
191
|
+
if ! pc_contains_word "$declared" "$member_value"; then
|
|
192
|
+
pc_error_add "$slot does not resolve to an items[] issue_num; found: $member_value."
|
|
193
|
+
continue
|
|
194
|
+
fi
|
|
195
|
+
if pc_contains_word "$PM_CLAIMED" "$member_value"; then
|
|
196
|
+
pc_error_add "$slot repeats issue_num $member_value, already claimed by an earlier component."
|
|
197
|
+
continue
|
|
198
|
+
fi
|
|
199
|
+
PM_CLAIMED="$PM_CLAIMED $member_value"
|
|
200
|
+
done
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
pm_validate_expected_components() {
|
|
204
|
+
# Validate the optional expected_conflict_components key (invariant M8).
|
|
205
|
+
#
|
|
206
|
+
# Key gated: absence is the overwhelmingly common shape and must cost the
|
|
207
|
+
# caller nothing, so the whole invariant is skipped rather than defaulted
|
|
208
|
+
# and a pre-M8 manifest's error list is unchanged.
|
|
209
|
+
local root="expected_conflict_components"
|
|
210
|
+
yp_has "$root" || return 0
|
|
211
|
+
if [[ $(yp_type_of "$root") != seq ]]; then
|
|
212
|
+
pc_error_add "$PM_CONTEXT $root must be a list."
|
|
213
|
+
return 0
|
|
214
|
+
fi
|
|
215
|
+
|
|
216
|
+
local declared total position comp_path ctx name_type name_value
|
|
217
|
+
declared=$(pm_declared_issue_nums)
|
|
218
|
+
total=$(yp_count_of "$root")
|
|
219
|
+
for ((position = 0; position < total; position++)); do
|
|
220
|
+
comp_path="${root}[${position}]"
|
|
221
|
+
ctx="$PM_CONTEXT ${root}[${position}]"
|
|
222
|
+
if [[ $(yp_type_of "$comp_path") != map ]]; then
|
|
223
|
+
pc_error_add "$ctx must be an object."
|
|
224
|
+
continue
|
|
225
|
+
fi
|
|
226
|
+
# Field order follows the documented authoring order: the optional
|
|
227
|
+
# diagnostic label first, then the required membership list.
|
|
228
|
+
if yp_has "${comp_path}.name"; then
|
|
229
|
+
name_type=$(yp_type_of "${comp_path}.name")
|
|
230
|
+
name_value=$(yp_value_of "${comp_path}.name")
|
|
231
|
+
if ! pc_is_non_empty_string "$name_type" "$name_value"; then
|
|
232
|
+
pc_error_add "$ctx name must be a non-empty string."
|
|
233
|
+
fi
|
|
234
|
+
fi
|
|
235
|
+
pm_validate_component_members "${comp_path}.members" "$ctx" "$declared"
|
|
236
|
+
done
|
|
237
|
+
}
|
|
238
|
+
|
|
131
239
|
pm_validate_text() {
|
|
132
240
|
# Validate a manifest document against invariants M1 to M7.
|
|
133
241
|
#
|
|
@@ -139,6 +247,7 @@ pm_validate_text() {
|
|
|
139
247
|
local text="$1" parse_status=0
|
|
140
248
|
pc_errors_reset
|
|
141
249
|
PM_SUBSET_DETAIL=""
|
|
250
|
+
PM_CLAIMED=""
|
|
142
251
|
pm_parse_manifest "$text" || parse_status=$?
|
|
143
252
|
# An out-of-subset refusal propagates so the caller can distinguish it from
|
|
144
253
|
# a validation verdict; an M1 failure returns the single-element error list
|
|
@@ -156,6 +265,9 @@ pm_validate_text() {
|
|
|
156
265
|
# The manifest carries `kind` on every item, unlike the orchestrator
|
|
157
266
|
# checkpoint, so the shared item validator is asked to require it (S1).
|
|
158
267
|
pi_validate_items "items" "$PM_CONTEXT" 1
|
|
268
|
+
# M8 runs last because its membership check resolves against the same items
|
|
269
|
+
# collection the previous call validated.
|
|
270
|
+
pm_validate_expected_components
|
|
159
271
|
return 0
|
|
160
272
|
}
|
|
161
273
|
|