@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
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Portable codex_model_routing_receipts per-entry checks (inventory family U6.X).
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Destination-runtime PowerShell port of
|
|
7
|
+
`scripts/dev_tools/_orchestrator_state_codex_model_routing.py`, covering
|
|
8
|
+
parity-inventory rows U6.X1 through U6.X11: the list and object shape, the ten
|
|
9
|
+
required keys, the non-empty phase, the resolver-invalid-inputs surface, the
|
|
10
|
+
ceiling monotonicity rule, the three ceiling-transition rules, and the
|
|
11
|
+
resolved-key comparison.
|
|
12
|
+
|
|
13
|
+
SINGLE-IMPLEMENTATION RULE. The expected deployment is obtained by calling
|
|
14
|
+
`Resolve-CodexDeployment` from `.claude/lib/codex-routing/CodexDeployment.psm1`.
|
|
15
|
+
The profile table, the C3 overlay rule, and the forced-persona rule are never
|
|
16
|
+
re-implemented here.
|
|
17
|
+
|
|
18
|
+
Row U6.X11 renders both sides of a mismatch with Python `repr()` semantics
|
|
19
|
+
(`{expected!r}` / `{actual!r}`), so it uses the shared `ConvertTo-PythonReprText`
|
|
20
|
+
renderer. Row U6.X5 interpolates the resolver's exception text with Python
|
|
21
|
+
`str()` semantics, so the resolver's ArgumentException Message is used verbatim.
|
|
22
|
+
|
|
23
|
+
Every function is pure: it reads no file, starts no process, and never mutates
|
|
24
|
+
its input.
|
|
25
|
+
#>
|
|
26
|
+
|
|
27
|
+
Set-StrictMode -Version Latest
|
|
28
|
+
|
|
29
|
+
# Import the shared checkpoint-value primitives and the single Codex deployment
|
|
30
|
+
# resolver, resolved relative to this module's directory so both imports travel
|
|
31
|
+
# with the pushed-down pack regardless of the working directory.
|
|
32
|
+
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'OrchestratorStateCheckpointValue.psm1') -Force
|
|
33
|
+
$script:CodexDeploymentModulePath = Join-Path -Path (Join-Path -Path $PSScriptRoot -ChildPath '..') -ChildPath (Join-Path -Path 'codex-routing' -ChildPath 'CodexDeployment.psm1')
|
|
34
|
+
Import-Module $script:CodexDeploymentModulePath -Force
|
|
35
|
+
|
|
36
|
+
# The checkpoint key this family validates.
|
|
37
|
+
$script:CODEX_MODEL_ROUTING_RECEIPTS_KEY = 'codex_model_routing_receipts'
|
|
38
|
+
|
|
39
|
+
# The ten keys every receipt must carry, and the nine of them the resolver
|
|
40
|
+
# reproduces. Pinned to _REQUIRED_KEYS / _RESOLVED_KEYS in the Python reference;
|
|
41
|
+
# `phase` is checkpoint-only bookkeeping and is not resolver output.
|
|
42
|
+
$script:REQUIRED_RECEIPT_KEYS = @(
|
|
43
|
+
'logical_agent',
|
|
44
|
+
'deployment_agent',
|
|
45
|
+
'phase',
|
|
46
|
+
'complexity_band',
|
|
47
|
+
'execution_context',
|
|
48
|
+
'orchestration_complexity_ceiling',
|
|
49
|
+
'c3_overlay_applied',
|
|
50
|
+
'c3_overlay_reason',
|
|
51
|
+
'model',
|
|
52
|
+
'model_reasoning_effort'
|
|
53
|
+
)
|
|
54
|
+
$script:RESOLVED_RECEIPT_KEYS = @($script:REQUIRED_RECEIPT_KEYS | Where-Object { $_ -ne 'phase' })
|
|
55
|
+
|
|
56
|
+
# The complexity-band ordering used by the ceiling monotonicity comparison.
|
|
57
|
+
$script:BAND_ORDER = @('C1', 'C2', 'C3', 'C4')
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
function Get-CodexCeilingTransitionError {
|
|
61
|
+
<#
|
|
62
|
+
.SYNOPSIS
|
|
63
|
+
Return one receipt's ceiling-transition errors (rows U6.X7-U6.X10).
|
|
64
|
+
.DESCRIPTION
|
|
65
|
+
Private helper mirroring _validate_ceiling_transition. Transition evidence
|
|
66
|
+
is required exactly when the orchestration ceiling rises: absent when it
|
|
67
|
+
does not rise, and otherwise an object recording the exact from/to pair and
|
|
68
|
+
a non-empty unique list of affected delegation ids.
|
|
69
|
+
.PARAMETER Receipt
|
|
70
|
+
The deserialized receipt object.
|
|
71
|
+
.PARAMETER Prefix
|
|
72
|
+
The error-message prefix for this receipt position.
|
|
73
|
+
.PARAMETER PreviousCeiling
|
|
74
|
+
The previous receipt's resolved ceiling, or $null for the first receipt.
|
|
75
|
+
.PARAMETER CurrentCeiling
|
|
76
|
+
This receipt's resolved ceiling.
|
|
77
|
+
.OUTPUTS
|
|
78
|
+
System.String[] - zero or more error strings.
|
|
79
|
+
#>
|
|
80
|
+
[CmdletBinding()]
|
|
81
|
+
[OutputType([string[]])]
|
|
82
|
+
param(
|
|
83
|
+
[Parameter(Mandatory = $true)]
|
|
84
|
+
[psobject] $Receipt,
|
|
85
|
+
|
|
86
|
+
[Parameter(Mandatory = $true)]
|
|
87
|
+
[string] $Prefix,
|
|
88
|
+
|
|
89
|
+
[Parameter(Mandatory = $true)]
|
|
90
|
+
[AllowNull()]
|
|
91
|
+
[AllowEmptyString()]
|
|
92
|
+
[string] $PreviousCeiling,
|
|
93
|
+
|
|
94
|
+
[Parameter(Mandatory = $true)]
|
|
95
|
+
[string] $CurrentCeiling
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
$errors = [System.Collections.Generic.List[string]]::new()
|
|
99
|
+
$transition = (Get-CheckpointObjectMember -Owner $Receipt -Name 'ceiling_transition').Value
|
|
100
|
+
|
|
101
|
+
# U6.X7: with no previous ceiling, or an unchanged ceiling, transition
|
|
102
|
+
# evidence must be absent entirely.
|
|
103
|
+
if ([string]::IsNullOrEmpty($PreviousCeiling) -or ($CurrentCeiling -ceq $PreviousCeiling)) {
|
|
104
|
+
if ($null -ne $transition) {
|
|
105
|
+
$errors.Add("$Prefix.ceiling_transition must be absent unless the ceiling rises.")
|
|
106
|
+
}
|
|
107
|
+
return $errors.ToArray()
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
# U6.X8: a risen ceiling requires an object recording the increase.
|
|
111
|
+
if (-not (Test-CheckpointObjectValue -Value $transition)) {
|
|
112
|
+
$errors.Add("$Prefix.ceiling_transition must record a ceiling increase.")
|
|
113
|
+
return $errors.ToArray()
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
# U6.X9: the recorded from/to pair must be the actual transition.
|
|
117
|
+
$from = (Get-CheckpointObjectMember -Owner $transition -Name 'from').Value
|
|
118
|
+
$to = (Get-CheckpointObjectMember -Owner $transition -Name 'to').Value
|
|
119
|
+
if (-not (Test-PythonValueEqual -Actual $from -Expected $PreviousCeiling) -or
|
|
120
|
+
-not (Test-PythonValueEqual -Actual $to -Expected $CurrentCeiling)) {
|
|
121
|
+
$errors.Add("$Prefix.ceiling_transition must record $PreviousCeiling to $CurrentCeiling.")
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
# U6.X10: the affected delegation ids must be a non-empty list of distinct,
|
|
125
|
+
# non-blank strings. A non-list value is treated as empty, matching Python.
|
|
126
|
+
$affected = (Get-CheckpointObjectMember -Owner $transition -Name 'affected_delegation_ids').Value
|
|
127
|
+
$affectedItems = @()
|
|
128
|
+
if (Test-CheckpointListValue -Value $affected) { $affectedItems = @($affected) }
|
|
129
|
+
$malformed = $affectedItems.Count -eq 0
|
|
130
|
+
if (-not $malformed) {
|
|
131
|
+
$distinct = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::Ordinal)
|
|
132
|
+
foreach ($item in $affectedItems) {
|
|
133
|
+
if (-not ($item -is [string]) -or [string]::IsNullOrWhiteSpace([string]$item)) {
|
|
134
|
+
$malformed = $true
|
|
135
|
+
break
|
|
136
|
+
}
|
|
137
|
+
[void]$distinct.Add([string]$item)
|
|
138
|
+
}
|
|
139
|
+
if (-not $malformed -and $distinct.Count -ne $affectedItems.Count) { $malformed = $true }
|
|
140
|
+
}
|
|
141
|
+
if ($malformed) {
|
|
142
|
+
$errors.Add("$Prefix.ceiling_transition.affected_delegation_ids must be a non-empty unique string list.")
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return $errors.ToArray()
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function Get-CodexModelRoutingResolvedKeyError {
|
|
149
|
+
<#
|
|
150
|
+
.SYNOPSIS
|
|
151
|
+
Return the resolved-key mismatch errors for one receipt (row U6.X11).
|
|
152
|
+
.DESCRIPTION
|
|
153
|
+
Private helper comparing each of the nine resolver-reproduced keys against
|
|
154
|
+
the resolver output. Both sides render with Python repr() semantics because
|
|
155
|
+
the inventory template uses {expected!r} and {actual!r}.
|
|
156
|
+
.PARAMETER Receipt
|
|
157
|
+
The deserialized receipt object.
|
|
158
|
+
.PARAMETER Prefix
|
|
159
|
+
The error-message prefix for this receipt position.
|
|
160
|
+
.PARAMETER Expected
|
|
161
|
+
The resolver output hashtable.
|
|
162
|
+
.OUTPUTS
|
|
163
|
+
System.String[] - zero or more error strings.
|
|
164
|
+
#>
|
|
165
|
+
[CmdletBinding()]
|
|
166
|
+
[OutputType([string[]])]
|
|
167
|
+
param(
|
|
168
|
+
[Parameter(Mandatory = $true)]
|
|
169
|
+
[psobject] $Receipt,
|
|
170
|
+
|
|
171
|
+
[Parameter(Mandatory = $true)]
|
|
172
|
+
[string] $Prefix,
|
|
173
|
+
|
|
174
|
+
[Parameter(Mandatory = $true)]
|
|
175
|
+
[hashtable] $Expected
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
$errors = [System.Collections.Generic.List[string]]::new()
|
|
179
|
+
|
|
180
|
+
# Compare every resolver-reproduced key so a receipt reports all of its
|
|
181
|
+
# mismatches at once rather than only the first.
|
|
182
|
+
foreach ($key in $script:RESOLVED_RECEIPT_KEYS) {
|
|
183
|
+
$actual = (Get-CheckpointObjectMember -Owner $Receipt -Name $key).Value
|
|
184
|
+
if (-not (Test-PythonValueEqual -Actual $actual -Expected $Expected[$key])) {
|
|
185
|
+
$expectedText = ConvertTo-PythonReprText -Value $Expected[$key]
|
|
186
|
+
$actualText = ConvertTo-PythonReprText -Value $actual
|
|
187
|
+
$errors.Add("$Prefix.$key must be $expectedText, found $actualText.")
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return $errors.ToArray()
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function Get-OrchestratorStateCodexModelRoutingReceiptError {
|
|
195
|
+
<#
|
|
196
|
+
.SYNOPSIS
|
|
197
|
+
Return the codex_model_routing_receipts errors (rows U6.X1-U6.X11).
|
|
198
|
+
.DESCRIPTION
|
|
199
|
+
Public entry mirroring validate_codex_model_routing_receipts. Walks the
|
|
200
|
+
receipt array in order, carrying the previous resolved ceiling forward so
|
|
201
|
+
the monotonicity and transition rules can be applied, and reports every
|
|
202
|
+
malformed receipt with its own index.
|
|
203
|
+
|
|
204
|
+
Control flow reproduces the Python reference exactly: missing keys stop
|
|
205
|
+
that receipt; a resolver failure stops that receipt and leaves the carried
|
|
206
|
+
ceiling unchanged; a monotonicity violation suppresses the transition check
|
|
207
|
+
for that receipt but still advances the carried ceiling.
|
|
208
|
+
.PARAMETER Value
|
|
209
|
+
The raw deserialized value of the codex_model_routing_receipts key.
|
|
210
|
+
.OUTPUTS
|
|
211
|
+
System.String[] - zero or more error strings.
|
|
212
|
+
#>
|
|
213
|
+
[CmdletBinding()]
|
|
214
|
+
[OutputType([string[]])]
|
|
215
|
+
param(
|
|
216
|
+
[Parameter(Mandatory = $true)]
|
|
217
|
+
[AllowNull()]
|
|
218
|
+
[object] $Value
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
$errors = [System.Collections.Generic.List[string]]::new()
|
|
222
|
+
|
|
223
|
+
# U6.X1: the caller invokes this only when the key is present, so a non-list
|
|
224
|
+
# value is itself the error and nothing further can be inspected.
|
|
225
|
+
if (-not (Test-CheckpointListValue -Value $Value)) {
|
|
226
|
+
$errors.Add("Checkpoint $($script:CODEX_MODEL_ROUTING_RECEIPTS_KEY) must be a list when present.")
|
|
227
|
+
return $errors.ToArray()
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
$previousCeiling = $null
|
|
231
|
+
$index = 0
|
|
232
|
+
foreach ($item in @($Value)) {
|
|
233
|
+
$prefix = "Checkpoint $($script:CODEX_MODEL_ROUTING_RECEIPTS_KEY)[$index]"
|
|
234
|
+
$index++
|
|
235
|
+
|
|
236
|
+
# U6.X2: a non-object entry has no keys to inspect.
|
|
237
|
+
if (-not (Test-CheckpointObjectValue -Value $item)) {
|
|
238
|
+
$errors.Add("$prefix must be an object.")
|
|
239
|
+
continue
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
# U6.X3: a receipt missing any required key stops here, because the
|
|
243
|
+
# resolver cannot be called without complete inputs.
|
|
244
|
+
$names = @(Get-CheckpointObjectMemberName -Owner $item)
|
|
245
|
+
$missing = @($script:REQUIRED_RECEIPT_KEYS | Where-Object { $names -notcontains $_ })
|
|
246
|
+
if ($missing.Count -gt 0) {
|
|
247
|
+
$errors.Add("$prefix missing required keys: $($missing -join ', ').")
|
|
248
|
+
continue
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
# U6.X4: the phase is checkpoint bookkeeping; a malformed phase is
|
|
252
|
+
# reported but does not stop the resolver comparison.
|
|
253
|
+
$phase = (Get-CheckpointObjectMember -Owner $item -Name 'phase').Value
|
|
254
|
+
if (-not ($phase -is [string]) -or [string]::IsNullOrWhiteSpace([string]$phase)) {
|
|
255
|
+
$errors.Add("$prefix.phase must be a non-empty string.")
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
# U6.X5: resolve through the single Codex deployment resolver. Only the
|
|
259
|
+
# ValueError-equivalent surface is caught, matching the Python except
|
|
260
|
+
# clause; every input is coerced with Python str() semantics first.
|
|
261
|
+
$expected = $null
|
|
262
|
+
try {
|
|
263
|
+
$expected = Resolve-CodexDeployment `
|
|
264
|
+
-LogicalAgent (ConvertTo-PythonDisplayText -Value (Get-CheckpointObjectMember -Owner $item -Name 'logical_agent').Value) `
|
|
265
|
+
-ComplexityBand (ConvertTo-PythonDisplayText -Value (Get-CheckpointObjectMember -Owner $item -Name 'complexity_band').Value) `
|
|
266
|
+
-ExecutionContext (ConvertTo-PythonDisplayText -Value (Get-CheckpointObjectMember -Owner $item -Name 'execution_context').Value) `
|
|
267
|
+
-OrchestrationComplexityCeiling (ConvertTo-PythonDisplayText -Value (Get-CheckpointObjectMember -Owner $item -Name 'orchestration_complexity_ceiling').Value)
|
|
268
|
+
} catch [System.ArgumentException] {
|
|
269
|
+
$errors.Add("$prefix has invalid routing inputs: $($_.Exception.Message)")
|
|
270
|
+
continue
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
# U6.X6 and the transition rules. A ceiling that drops is a monotonicity
|
|
274
|
+
# violation and suppresses the transition check for this receipt; the
|
|
275
|
+
# carried ceiling advances either way.
|
|
276
|
+
$currentCeiling = [string]$expected['orchestration_complexity_ceiling']
|
|
277
|
+
if ($null -ne $previousCeiling -and
|
|
278
|
+
($script:BAND_ORDER.IndexOf($currentCeiling) -lt $script:BAND_ORDER.IndexOf([string]$previousCeiling))) {
|
|
279
|
+
$errors.Add("$prefix.orchestration_complexity_ceiling must be monotonic; found $currentCeiling after $previousCeiling.")
|
|
280
|
+
} else {
|
|
281
|
+
$errors.AddRange([string[]]@(
|
|
282
|
+
Get-CodexCeilingTransitionError -Receipt $item -Prefix $prefix `
|
|
283
|
+
-PreviousCeiling $previousCeiling -CurrentCeiling $currentCeiling
|
|
284
|
+
))
|
|
285
|
+
}
|
|
286
|
+
$previousCeiling = $currentCeiling
|
|
287
|
+
|
|
288
|
+
# U6.X11: every resolver-reproduced key must match the resolver output.
|
|
289
|
+
$errors.AddRange([string[]]@(Get-CodexModelRoutingResolvedKeyError -Receipt $item -Prefix $prefix -Expected $expected))
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return $errors.ToArray()
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
# Only the family entry point is exported; the transition and resolved-key helpers
|
|
296
|
+
# stay private so the ordered, ceiling-carrying walk cannot be bypassed.
|
|
297
|
+
Export-ModuleMember -Function Get-OrchestratorStateCodexModelRoutingReceiptError
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Portable codex_topology_receipts per-entry checks (inventory family U6.T).
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Destination-runtime PowerShell port of
|
|
7
|
+
`scripts/dev_tools/_orchestrator_state_codex_topology.py`, covering
|
|
8
|
+
parity-inventory rows U6.T1 through U6.T11: the list and object shape, the
|
|
9
|
+
thirteen required keys, the non-empty phase, the resolver input-type checks
|
|
10
|
+
(languages, the two file counts, the cross-cutting flag, the execution
|
|
11
|
+
context, and the root-persona enum), the resolver-invalid-inputs surface, and
|
|
12
|
+
the resolved-key comparison.
|
|
13
|
+
|
|
14
|
+
SINGLE-IMPLEMENTATION RULE. The expected topology is obtained by calling
|
|
15
|
+
`Resolve-CodexTopology`, and the permitted root personas are read from
|
|
16
|
+
`Get-CodexForcedRootPersona`, both from
|
|
17
|
+
`.claude/lib/codex-routing/CodexTopology.psm1`. The language-budget table and
|
|
18
|
+
the escalation precedence are never re-implemented here.
|
|
19
|
+
|
|
20
|
+
Row U6.T6 rejects a boolean where an integer is required, reproducing the
|
|
21
|
+
Python guard that exists because bool is a subclass of int. Row U6.T11 renders
|
|
22
|
+
both sides of a mismatch with Python `repr()` semantics, which for the
|
|
23
|
+
`languages` key means a Python list literal. Row U6.T10 interpolates the
|
|
24
|
+
resolver's exception text with Python `str()` semantics.
|
|
25
|
+
|
|
26
|
+
Every function is pure: it reads no file, starts no process, and never mutates
|
|
27
|
+
its input.
|
|
28
|
+
#>
|
|
29
|
+
|
|
30
|
+
Set-StrictMode -Version Latest
|
|
31
|
+
|
|
32
|
+
# Import the shared checkpoint-value primitives and the single Codex topology
|
|
33
|
+
# resolver, resolved relative to this module's directory so both imports travel
|
|
34
|
+
# with the pushed-down pack regardless of the working directory.
|
|
35
|
+
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'OrchestratorStateCheckpointValue.psm1') -Force
|
|
36
|
+
$script:CodexTopologyModulePath = Join-Path -Path (Join-Path -Path $PSScriptRoot -ChildPath '..') -ChildPath (Join-Path -Path 'codex-routing' -ChildPath 'CodexTopology.psm1')
|
|
37
|
+
Import-Module $script:CodexTopologyModulePath -Force
|
|
38
|
+
|
|
39
|
+
# The checkpoint key this family validates.
|
|
40
|
+
$script:CODEX_TOPOLOGY_RECEIPTS_KEY = 'codex_topology_receipts'
|
|
41
|
+
|
|
42
|
+
# The thirteen keys every receipt must carry, and the twelve of them the resolver
|
|
43
|
+
# reproduces. Pinned to _REQUIRED_KEYS / _RESOLVED_KEYS in the Python reference;
|
|
44
|
+
# `phase` is checkpoint-only bookkeeping and is not resolver output.
|
|
45
|
+
$script:REQUIRED_RECEIPT_KEYS = @(
|
|
46
|
+
'phase',
|
|
47
|
+
'execution_context',
|
|
48
|
+
'languages',
|
|
49
|
+
'production_file_count',
|
|
50
|
+
'test_file_count',
|
|
51
|
+
'cross_cutting',
|
|
52
|
+
'root_persona',
|
|
53
|
+
'route',
|
|
54
|
+
'topology',
|
|
55
|
+
'logical_agent',
|
|
56
|
+
'routing_reason',
|
|
57
|
+
'max_production_files',
|
|
58
|
+
'max_test_files'
|
|
59
|
+
)
|
|
60
|
+
$script:RESOLVED_RECEIPT_KEYS = @($script:REQUIRED_RECEIPT_KEYS | Where-Object { $_ -ne 'phase' })
|
|
61
|
+
|
|
62
|
+
# The two file-count keys subject to the integer-not-boolean rule.
|
|
63
|
+
$script:FILE_COUNT_KEYS = @('production_file_count', 'test_file_count')
|
|
64
|
+
|
|
65
|
+
# Rendered form of the Python sorted FORCED_ROOT_PERSONAS tuple, used verbatim in
|
|
66
|
+
# the root-persona message. The membership test itself reads the live set from the
|
|
67
|
+
# resolver module so the enum has one source.
|
|
68
|
+
$script:FORCED_ROOT_PERSONAS_PYTHON_TUPLE = "('epic-orchestrator', 'epic-planner')"
|
|
69
|
+
|
|
70
|
+
# The integral CLR types a JSON integer can deserialize to. A CLR boolean is
|
|
71
|
+
# deliberately absent, matching the Python bool rejection.
|
|
72
|
+
$script:INTEGRAL_TYPES = @([int], [long], [short], [byte])
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
function Get-CodexTopologyInputError {
|
|
76
|
+
<#
|
|
77
|
+
.SYNOPSIS
|
|
78
|
+
Return one receipt's resolver-input type errors (rows U6.T5-U6.T9).
|
|
79
|
+
.DESCRIPTION
|
|
80
|
+
Private helper mirroring _receipt_inputs. Every input the resolver
|
|
81
|
+
consumes is type-checked here first, in the Python order, so the resolver
|
|
82
|
+
is never called with a value it would reject by type. A non-empty result
|
|
83
|
+
means the receipt is skipped before resolution, matching the Python
|
|
84
|
+
`if inputs is None: continue` branch.
|
|
85
|
+
.PARAMETER Receipt
|
|
86
|
+
The deserialized receipt object.
|
|
87
|
+
.PARAMETER Prefix
|
|
88
|
+
The error-message prefix for this receipt position.
|
|
89
|
+
.OUTPUTS
|
|
90
|
+
System.String[] - zero or more error strings.
|
|
91
|
+
#>
|
|
92
|
+
[CmdletBinding()]
|
|
93
|
+
[OutputType([string[]])]
|
|
94
|
+
param(
|
|
95
|
+
[Parameter(Mandatory = $true)]
|
|
96
|
+
[psobject] $Receipt,
|
|
97
|
+
|
|
98
|
+
[Parameter(Mandatory = $true)]
|
|
99
|
+
[string] $Prefix
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
$errors = [System.Collections.Generic.List[string]]::new()
|
|
103
|
+
|
|
104
|
+
# U6.T5: languages must be a list in which every member is a non-blank string.
|
|
105
|
+
$languages = (Get-CheckpointObjectMember -Owner $Receipt -Name 'languages').Value
|
|
106
|
+
$languagesValid = Test-CheckpointListValue -Value $languages
|
|
107
|
+
if ($languagesValid) {
|
|
108
|
+
foreach ($language in @($languages)) {
|
|
109
|
+
if (-not ($language -is [string]) -or [string]::IsNullOrWhiteSpace([string]$language)) {
|
|
110
|
+
$languagesValid = $false
|
|
111
|
+
break
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (-not $languagesValid) {
|
|
116
|
+
$errors.Add("$Prefix.languages must be a list of non-empty strings.")
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
# U6.T6: both file counts must be integers, and a boolean is explicitly not an
|
|
120
|
+
# integer here even though Python's bool subclasses int.
|
|
121
|
+
foreach ($key in $script:FILE_COUNT_KEYS) {
|
|
122
|
+
$value = (Get-CheckpointObjectMember -Owner $Receipt -Name $key).Value
|
|
123
|
+
$isIntegral = $false
|
|
124
|
+
if ($null -ne $value -and -not ($value -is [bool])) {
|
|
125
|
+
foreach ($integralType in $script:INTEGRAL_TYPES) {
|
|
126
|
+
if ($value.GetType() -eq $integralType) { $isIntegral = $true; break }
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (-not $isIntegral) {
|
|
130
|
+
$errors.Add("$Prefix.$key must be an integer.")
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
# U6.T7 and U6.T8: the cross-cutting flag and the execution context.
|
|
135
|
+
$crossCutting = (Get-CheckpointObjectMember -Owner $Receipt -Name 'cross_cutting').Value
|
|
136
|
+
if (-not ($crossCutting -is [bool])) {
|
|
137
|
+
$errors.Add("$Prefix.cross_cutting must be a boolean.")
|
|
138
|
+
}
|
|
139
|
+
$receiptExecutionContext = (Get-CheckpointObjectMember -Owner $Receipt -Name 'execution_context').Value
|
|
140
|
+
if (-not ($receiptExecutionContext -is [string])) {
|
|
141
|
+
$errors.Add("$Prefix.execution_context must be a string.")
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# U6.T9: root_persona is optional, but a present value must be a forced root
|
|
145
|
+
# persona. The permitted set is read from the resolver module, not restated.
|
|
146
|
+
$rootPersona = (Get-CheckpointObjectMember -Owner $Receipt -Name 'root_persona').Value
|
|
147
|
+
if ($null -ne $rootPersona) {
|
|
148
|
+
$permitted = @(Get-CodexForcedRootPersona)
|
|
149
|
+
if (-not ($rootPersona -is [string]) -or ($permitted -cnotcontains [string]$rootPersona)) {
|
|
150
|
+
$errors.Add("$Prefix.root_persona must be null or one of $($script:FORCED_ROOT_PERSONAS_PYTHON_TUPLE).")
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return $errors.ToArray()
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function Get-CodexTopologyResolvedKeyError {
|
|
158
|
+
<#
|
|
159
|
+
.SYNOPSIS
|
|
160
|
+
Return the resolved-key mismatch errors for one receipt (row U6.T11).
|
|
161
|
+
.DESCRIPTION
|
|
162
|
+
Private helper comparing each of the twelve resolver-reproduced keys
|
|
163
|
+
against the resolver output. Both sides render with Python repr()
|
|
164
|
+
semantics, which for the languages key produces a Python list literal.
|
|
165
|
+
.PARAMETER Receipt
|
|
166
|
+
The deserialized receipt object.
|
|
167
|
+
.PARAMETER Prefix
|
|
168
|
+
The error-message prefix for this receipt position.
|
|
169
|
+
.PARAMETER Expected
|
|
170
|
+
The resolver output hashtable.
|
|
171
|
+
.OUTPUTS
|
|
172
|
+
System.String[] - zero or more error strings.
|
|
173
|
+
#>
|
|
174
|
+
[CmdletBinding()]
|
|
175
|
+
[OutputType([string[]])]
|
|
176
|
+
param(
|
|
177
|
+
[Parameter(Mandatory = $true)]
|
|
178
|
+
[psobject] $Receipt,
|
|
179
|
+
|
|
180
|
+
[Parameter(Mandatory = $true)]
|
|
181
|
+
[string] $Prefix,
|
|
182
|
+
|
|
183
|
+
[Parameter(Mandatory = $true)]
|
|
184
|
+
[hashtable] $Expected
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
$errors = [System.Collections.Generic.List[string]]::new()
|
|
188
|
+
|
|
189
|
+
# Compare every resolver-reproduced key so a receipt reports all of its
|
|
190
|
+
# mismatches at once rather than only the first.
|
|
191
|
+
foreach ($key in $script:RESOLVED_RECEIPT_KEYS) {
|
|
192
|
+
$actual = (Get-CheckpointObjectMember -Owner $Receipt -Name $key).Value
|
|
193
|
+
if (-not (Test-PythonValueEqual -Actual $actual -Expected $Expected[$key])) {
|
|
194
|
+
$expectedText = ConvertTo-PythonReprText -Value $Expected[$key]
|
|
195
|
+
$actualText = ConvertTo-PythonReprText -Value $actual
|
|
196
|
+
$errors.Add("$Prefix.$key must be $expectedText, found $actualText.")
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return $errors.ToArray()
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function Get-OrchestratorStateCodexTopologyReceiptError {
|
|
204
|
+
<#
|
|
205
|
+
.SYNOPSIS
|
|
206
|
+
Return the codex_topology_receipts errors (inventory rows U6.T1-U6.T11).
|
|
207
|
+
.DESCRIPTION
|
|
208
|
+
Public entry mirroring validate_codex_topology_receipts. Each receipt is
|
|
209
|
+
validated independently, in order, and reported with its own index.
|
|
210
|
+
|
|
211
|
+
Control flow reproduces the Python reference exactly: missing keys stop
|
|
212
|
+
that receipt before any type check; a malformed phase is reported but does
|
|
213
|
+
not stop the receipt; any resolver-input type error stops the receipt
|
|
214
|
+
before resolution; and a resolver failure stops the receipt before the
|
|
215
|
+
resolved-key comparison.
|
|
216
|
+
.PARAMETER Value
|
|
217
|
+
The raw deserialized value of the codex_topology_receipts key.
|
|
218
|
+
.OUTPUTS
|
|
219
|
+
System.String[] - zero or more error strings.
|
|
220
|
+
#>
|
|
221
|
+
[CmdletBinding()]
|
|
222
|
+
[OutputType([string[]])]
|
|
223
|
+
param(
|
|
224
|
+
[Parameter(Mandatory = $true)]
|
|
225
|
+
[AllowNull()]
|
|
226
|
+
[object] $Value
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
$errors = [System.Collections.Generic.List[string]]::new()
|
|
230
|
+
|
|
231
|
+
# U6.T1: the caller invokes this only when the key is present, so a non-list
|
|
232
|
+
# value is itself the error and nothing further can be inspected.
|
|
233
|
+
if (-not (Test-CheckpointListValue -Value $Value)) {
|
|
234
|
+
$errors.Add("Checkpoint $($script:CODEX_TOPOLOGY_RECEIPTS_KEY) must be a list when present.")
|
|
235
|
+
return $errors.ToArray()
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
$index = 0
|
|
239
|
+
foreach ($item in @($Value)) {
|
|
240
|
+
$prefix = "Checkpoint $($script:CODEX_TOPOLOGY_RECEIPTS_KEY)[$index]"
|
|
241
|
+
$index++
|
|
242
|
+
|
|
243
|
+
# U6.T2: a non-object entry has no keys to inspect.
|
|
244
|
+
if (-not (Test-CheckpointObjectValue -Value $item)) {
|
|
245
|
+
$errors.Add("$prefix must be an object.")
|
|
246
|
+
continue
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
# U6.T3: a receipt missing any required key stops here, because the
|
|
250
|
+
# resolver cannot be called without complete inputs.
|
|
251
|
+
$names = @(Get-CheckpointObjectMemberName -Owner $item)
|
|
252
|
+
$missing = @($script:REQUIRED_RECEIPT_KEYS | Where-Object { $names -notcontains $_ })
|
|
253
|
+
if ($missing.Count -gt 0) {
|
|
254
|
+
$errors.Add("$prefix missing required keys: $($missing -join ', ').")
|
|
255
|
+
continue
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
# U6.T4: the phase is checkpoint bookkeeping; a malformed phase is
|
|
259
|
+
# reported but does not stop the resolver comparison.
|
|
260
|
+
$phase = (Get-CheckpointObjectMember -Owner $item -Name 'phase').Value
|
|
261
|
+
if (-not ($phase -is [string]) -or [string]::IsNullOrWhiteSpace([string]$phase)) {
|
|
262
|
+
$errors.Add("$prefix.phase must be a non-empty string.")
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
# U6.T5-U6.T9: any resolver-input type error stops this receipt, so the
|
|
266
|
+
# resolver is never handed a value it would reject by type.
|
|
267
|
+
$inputErrors = @(Get-CodexTopologyInputError -Receipt $item -Prefix $prefix)
|
|
268
|
+
if ($inputErrors.Count -gt 0) {
|
|
269
|
+
$errors.AddRange([string[]]$inputErrors)
|
|
270
|
+
continue
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
# U6.T10: resolve through the single Codex topology resolver. Only the
|
|
274
|
+
# ValueError-equivalent surface is caught, matching the Python except.
|
|
275
|
+
$expected = $null
|
|
276
|
+
try {
|
|
277
|
+
$expected = Resolve-CodexTopology `
|
|
278
|
+
-Language (Get-CheckpointObjectMember -Owner $item -Name 'languages').Value `
|
|
279
|
+
-ProductionFileCount (Get-CheckpointObjectMember -Owner $item -Name 'production_file_count').Value `
|
|
280
|
+
-TestFileCount (Get-CheckpointObjectMember -Owner $item -Name 'test_file_count').Value `
|
|
281
|
+
-ExecutionContext ([string](Get-CheckpointObjectMember -Owner $item -Name 'execution_context').Value) `
|
|
282
|
+
-CrossCutting (Get-CheckpointObjectMember -Owner $item -Name 'cross_cutting').Value `
|
|
283
|
+
-RootPersona (Get-CheckpointObjectMember -Owner $item -Name 'root_persona').Value
|
|
284
|
+
} catch [System.ArgumentException] {
|
|
285
|
+
$errors.Add("$prefix has invalid routing inputs: $($_.Exception.Message)")
|
|
286
|
+
continue
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
# U6.T11: every resolver-reproduced key must match the resolver output.
|
|
290
|
+
$errors.AddRange([string[]]@(Get-CodexTopologyResolvedKeyError -Receipt $item -Prefix $prefix -Expected $expected))
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return $errors.ToArray()
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
# Only the family entry point is exported; the input-type and resolved-key helpers
|
|
297
|
+
# stay private so no consumer can skip the ordered per-receipt walk.
|
|
298
|
+
Export-ModuleMember -Function Get-OrchestratorStateCodexTopologyReceiptError
|