@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,392 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Codex implementation-topology resolver, ported from the Python reference.
4
+
5
+ .DESCRIPTION
6
+ Destination-runtime PowerShell port of
7
+ `scripts/dev_tools/resolve_codex_topology.py`. It resolves the initial
8
+ implementation agent for one change from deterministic scope data: the
9
+ languages touched, the production and test file counts, the execution
10
+ context, the cross-cutting indicator, and an optional forced root persona.
11
+
12
+ Routing summary. A standalone, single-language change inside that language's
13
+ canonical direct-mode budget selects the language's typed engineer on the
14
+ `small` route. Every escalation condition selects the orchestrator on the
15
+ `large` route. An explicit root epic persona selects itself on the `epic`
16
+ route. The escalation conditions are evaluated in a fixed precedence order,
17
+ and the first match wins:
18
+
19
+ epic_child_context -> invalid_estimate -> cross_language ->
20
+ unsupported_language -> cross_cutting -> direct_mode_disabled ->
21
+ production_budget_exceeded
22
+
23
+ SINGLE-IMPLEMENTATION RULE. The orchestrator-state U6.T checks
24
+ (`OrchestratorStateCodexTopologyReceipts.psm1`) MUST call
25
+ `Resolve-CodexTopology` and read `Get-CodexForcedRootPersona` from this
26
+ module. They must never re-implement the language-budget table or the
27
+ escalation precedence. This module is the one PowerShell implementation of
28
+ the Codex topology axis.
29
+
30
+ Every constant below is pinned to `config/orchestration-routing.json`
31
+ (`codex_topology_policy`). Following the `ModelRouting.psm1` pattern, the
32
+ values are hard-coded here and never read from disk, because this module is
33
+ pushed down to consumer repositories that do not receive that config.
34
+
35
+ Error surface, relied on by the U6.T receipt checks:
36
+ - [System.ArgumentException] is the ValueError-equivalent surface. Its
37
+ Message text reproduces the Python message verbatim, including Python
38
+ tuple and repr() rendering, because inventory row U6.T10 interpolates
39
+ the exception text into its error string.
40
+
41
+ Documented divergence: Python raises TypeError (not ValueError) when
42
+ `languages` is not iterable, and the receipt validator does not catch
43
+ TypeError. This port treats a null Language argument as an empty collection,
44
+ which routes to the `unsupported_language` escalation. The difference is
45
+ unreachable from the U6.T checks, which reject a non-list `languages` before
46
+ the resolver is ever called.
47
+
48
+ The function is pure: it reads no file, starts no process, and never mutates
49
+ its input.
50
+ #>
51
+
52
+ Set-StrictMode -Version Latest
53
+
54
+ # The three permitted execution contexts, the sorted tuple rendering the Python
55
+ # message interpolates, and the two contexts that mark epic child work.
56
+ $script:VALID_EXECUTION_CONTEXTS = @('standalone', 'epic_preparation_child', 'epic_execution_child')
57
+ $script:VALID_EXECUTION_CONTEXTS_PYTHON_TUPLE = "('epic_execution_child', 'epic_preparation_child', 'standalone')"
58
+ $script:EPIC_CHILD_CONTEXTS = @('epic_preparation_child', 'epic_execution_child')
59
+ $script:STANDALONE_CONTEXT = 'standalone'
60
+
61
+ # The two personas that may be forced as the root of a run, and the logical agent
62
+ # every escalation routes to.
63
+ $script:FORCED_ROOT_PERSONAS = @('epic-planner', 'epic-orchestrator')
64
+ $script:ORCHESTRATOR_LOGICAL_AGENT = 'orchestrator'
65
+
66
+ # The per-language direct-mode budgets. A language absent from this table is an
67
+ # unsupported language and escalates.
68
+ $script:LANGUAGE_BUDGETS = @{
69
+ python = @{ direct_mode_enabled = $true; max_production_files = 3; max_test_files = 3; logical_agent = 'python-typed-engineer' }
70
+ powershell = @{ direct_mode_enabled = $true; max_production_files = 2; max_test_files = 3; logical_agent = 'powershell-typed-engineer' }
71
+ csharp = @{ direct_mode_enabled = $true; max_production_files = 3; max_test_files = 3; logical_agent = 'csharp-typed-engineer' }
72
+ typescript = @{ direct_mode_enabled = $false; max_production_files = 0; max_test_files = 0; logical_agent = 'typescript-engineer' }
73
+ }
74
+
75
+ # The integral CLR types a JSON integer can deserialize to. A CLR boolean is
76
+ # deliberately absent: Python rejects bool where an int is required.
77
+ $script:INTEGRAL_TYPES = @([int], [long], [short], [byte])
78
+
79
+
80
+ function Get-CodexForcedRootPersona {
81
+ <#
82
+ .SYNOPSIS
83
+ Return the two personas that may be forced as the root of a run.
84
+ .DESCRIPTION
85
+ Read-only accessor for FORCED_ROOT_PERSONAS. Exported so the U6.T
86
+ root_persona enum check reads the same set the resolver enforces instead
87
+ of restating it.
88
+ .OUTPUTS
89
+ System.String[] - the forced root persona names, in declaration order.
90
+ The names are emitted to the pipeline individually, so a caller collects
91
+ them with @(...) in the usual way.
92
+ #>
93
+ [CmdletBinding()]
94
+ [OutputType([string[]])]
95
+ param()
96
+
97
+ return [string[]]@($script:FORCED_ROOT_PERSONAS)
98
+ }
99
+
100
+ function Test-CodexIntegralValue {
101
+ <#
102
+ .SYNOPSIS
103
+ Report whether a value is an integer, rejecting booleans.
104
+ .DESCRIPTION
105
+ Private predicate mirroring the Python guard
106
+ `isinstance(value, bool) or not isinstance(value, int)`. Python's bool is
107
+ a subclass of int, so the reference rejects it explicitly; this port
108
+ compares the exact CLR type so no boolean satisfies the test.
109
+ .PARAMETER Value
110
+ The candidate value. May be $null.
111
+ .OUTPUTS
112
+ System.Boolean - $true only for a non-boolean integral value.
113
+ #>
114
+ [CmdletBinding()]
115
+ [OutputType([bool])]
116
+ param(
117
+ [Parameter(Mandatory = $true)]
118
+ [AllowNull()]
119
+ [object] $Value
120
+ )
121
+
122
+ if ($null -eq $Value -or $Value -is [bool]) { return $false }
123
+ foreach ($integralType in $script:INTEGRAL_TYPES) {
124
+ if ($Value.GetType() -eq $integralType) { return $true }
125
+ }
126
+ return $false
127
+ }
128
+
129
+ function Get-CodexNormalizedLanguage {
130
+ <#
131
+ .SYNOPSIS
132
+ Return unique, lowercased language names in stable ordinal order.
133
+ .DESCRIPTION
134
+ Private helper mirroring _normalize_languages. Every element must be a
135
+ non-blank string; anything else throws the ValueError-equivalent. The
136
+ result is deduplicated and ordinally sorted so the resolved receipt is
137
+ order-independent, matching Python's sorted(set(...)).
138
+ .PARAMETER Language
139
+ The raw language collection. A null value is treated as empty.
140
+ .OUTPUTS
141
+ System.String[] - the normalized language names. Declared additionally as
142
+ System.Object[] because the array is emitted through the unary-comma form
143
+ that stops PowerShell unrolling it.
144
+ #>
145
+ [CmdletBinding()]
146
+ [OutputType([string[]], [object[]])]
147
+ param(
148
+ [Parameter(Mandatory = $true)]
149
+ [AllowNull()]
150
+ [object] $Language
151
+ )
152
+
153
+ $normalized = [System.Collections.Generic.HashSet[string]]::new()
154
+
155
+ # Reject the whole collection on the first malformed member so a partially
156
+ # valid language list can never route a delegation.
157
+ foreach ($item in @($Language)) {
158
+ if (-not ($item -is [string]) -or [string]::IsNullOrWhiteSpace([string]$item)) {
159
+ throw [System.ArgumentException]::new('languages must contain non-empty strings.')
160
+ }
161
+ [void]$normalized.Add(([string]$item).Trim().ToLowerInvariant())
162
+ }
163
+
164
+ # Ordinal ordering matches Python's sorted() over the same names; PowerShell's
165
+ # culture-aware Sort-Object would not be guaranteed to. The unary comma stops
166
+ # PowerShell from unrolling the array on return, which would otherwise turn a
167
+ # single-language result into a bare string and a zero-language result into
168
+ # nothing at all.
169
+ $sorted = [string[]]@($normalized)
170
+ [Array]::Sort($sorted, [System.StringComparer]::Ordinal)
171
+ return , $sorted
172
+ }
173
+
174
+ function Get-CodexEscalationReceipt {
175
+ <#
176
+ .SYNOPSIS
177
+ Build a large-route orchestrator receipt for one escalation reason.
178
+ .DESCRIPTION
179
+ Private pure builder mirroring _orchestrator_receipt. Every escalation returns
180
+ the same shape and differs only in routing_reason and in whether a
181
+ language budget was known at the point of escalation.
182
+ .PARAMETER ExecutionContext
183
+ The validated execution context.
184
+ .PARAMETER Language
185
+ The normalized language names.
186
+ .PARAMETER ProductionFileCount
187
+ The validated production file count.
188
+ .PARAMETER TestFileCount
189
+ The validated test file count.
190
+ .PARAMETER CrossCutting
191
+ The validated cross-cutting indicator.
192
+ .PARAMETER Reason
193
+ The escalation reason recorded in routing_reason.
194
+ .PARAMETER Budget
195
+ The language budget when one was resolved, otherwise $null. When absent,
196
+ both max file counts are reported as null.
197
+ .OUTPUTS
198
+ System.Collections.Hashtable - the twelve-key topology receipt.
199
+ #>
200
+ [CmdletBinding()]
201
+ [OutputType([hashtable])]
202
+ param(
203
+ [Parameter(Mandatory = $true)][string] $ExecutionContext,
204
+ [Parameter(Mandatory = $true)][AllowEmptyCollection()][string[]] $Language,
205
+ [Parameter(Mandatory = $true)][int] $ProductionFileCount,
206
+ [Parameter(Mandatory = $true)][int] $TestFileCount,
207
+ [Parameter(Mandatory = $true)][bool] $CrossCutting,
208
+ [Parameter(Mandatory = $true)][string] $Reason,
209
+ [Parameter(Mandatory = $false)][AllowNull()][hashtable] $Budget = $null
210
+ )
211
+
212
+ return @{
213
+ execution_context = $ExecutionContext
214
+ languages = $Language
215
+ production_file_count = $ProductionFileCount
216
+ test_file_count = $TestFileCount
217
+ cross_cutting = $CrossCutting
218
+ root_persona = $null
219
+ route = 'large'
220
+ topology = 'orchestrator'
221
+ logical_agent = $script:ORCHESTRATOR_LOGICAL_AGENT
222
+ routing_reason = $Reason
223
+ max_production_files = $(if ($null -ne $Budget) { $Budget['max_production_files'] } else { $null })
224
+ max_test_files = $(if ($null -ne $Budget) { $Budget['max_test_files'] } else { $null })
225
+ }
226
+ }
227
+
228
+ function Resolve-CodexTopology {
229
+ <#
230
+ .SYNOPSIS
231
+ Resolve the initial Codex implementation agent from scope data.
232
+ .DESCRIPTION
233
+ Faithful PowerShell port of resolve_codex_topology
234
+ (scripts/dev_tools/resolve_codex_topology.py). Validates the execution
235
+ context, the language collection, the two file counts, and the
236
+ cross-cutting indicator, then applies the forced-root-persona branch or
237
+ the fixed escalation precedence documented in the module header.
238
+ .PARAMETER Language
239
+ The languages the change touches. Normalized to unique lowercase names.
240
+ .PARAMETER ProductionFileCount
241
+ The production file count. Must be a non-boolean integer.
242
+ .PARAMETER TestFileCount
243
+ The test file count. Must be a non-boolean integer.
244
+ .PARAMETER ExecutionContext
245
+ One of standalone, epic_preparation_child, epic_execution_child.
246
+ .PARAMETER CrossCutting
247
+ Whether the change is cross-cutting. Must be a boolean.
248
+ .PARAMETER RootPersona
249
+ An optional forced root persona, which requires a standalone context.
250
+ .OUTPUTS
251
+ System.Collections.Hashtable with the twelve resolved keys:
252
+ execution_context, languages, production_file_count, test_file_count,
253
+ cross_cutting, root_persona, route, topology, logical_agent,
254
+ routing_reason, max_production_files, max_test_files.
255
+ #>
256
+ [CmdletBinding()]
257
+ [OutputType([hashtable])]
258
+ param(
259
+ [Parameter(Mandatory = $true)]
260
+ [AllowNull()]
261
+ [object] $Language,
262
+
263
+ [Parameter(Mandatory = $true)]
264
+ [AllowNull()]
265
+ [object] $ProductionFileCount,
266
+
267
+ [Parameter(Mandatory = $true)]
268
+ [AllowNull()]
269
+ [object] $TestFileCount,
270
+
271
+ [Parameter(Mandatory = $true)]
272
+ [AllowNull()]
273
+ [AllowEmptyString()]
274
+ [string] $ExecutionContext,
275
+
276
+ [Parameter(Mandatory = $false)]
277
+ [AllowNull()]
278
+ [object] $CrossCutting = $false,
279
+
280
+ [Parameter(Mandatory = $false)]
281
+ [AllowNull()]
282
+ [object] $RootPersona = $null
283
+ )
284
+
285
+ if ($script:VALID_EXECUTION_CONTEXTS -cnotcontains $ExecutionContext) {
286
+ throw [System.ArgumentException]::new(
287
+ "execution_context must be one of $($script:VALID_EXECUTION_CONTEXTS_PYTHON_TUPLE), found '$ExecutionContext'."
288
+ )
289
+ }
290
+ $context = $ExecutionContext
291
+ $languages = Get-CodexNormalizedLanguage -Language $Language
292
+
293
+ # Counts are validated before any routing so a malformed estimate can never
294
+ # be silently coerced into a route decision.
295
+ if (-not (Test-CodexIntegralValue -Value $ProductionFileCount)) {
296
+ throw [System.ArgumentException]::new('production_file_count must be an integer.')
297
+ }
298
+ if (-not (Test-CodexIntegralValue -Value $TestFileCount)) {
299
+ throw [System.ArgumentException]::new('test_file_count must be an integer.')
300
+ }
301
+ if (-not ($CrossCutting -is [bool])) {
302
+ throw [System.ArgumentException]::new('cross_cutting must be a boolean.')
303
+ }
304
+ $productionCount = [int]$ProductionFileCount
305
+ $testCount = [int]$TestFileCount
306
+ $isCrossCutting = [bool]$CrossCutting
307
+
308
+ # A forced root persona short-circuits all escalation logic and selects
309
+ # itself, but only from a standalone context.
310
+ if ($null -ne $RootPersona) {
311
+ if (-not ($RootPersona -is [string]) -or ($script:FORCED_ROOT_PERSONAS -cnotcontains [string]$RootPersona)) {
312
+ throw [System.ArgumentException]::new("Unsupported Codex root persona: '$RootPersona'.")
313
+ }
314
+ if ($context -cne $script:STANDALONE_CONTEXT) {
315
+ throw [System.ArgumentException]::new('A forced root persona requires standalone context.')
316
+ }
317
+ return @{
318
+ execution_context = $context
319
+ languages = $languages
320
+ production_file_count = $productionCount
321
+ test_file_count = $testCount
322
+ cross_cutting = $isCrossCutting
323
+ root_persona = [string]$RootPersona
324
+ route = 'epic'
325
+ topology = 'epic_persona'
326
+ logical_agent = [string]$RootPersona
327
+ routing_reason = 'forced_root_persona'
328
+ max_production_files = $null
329
+ max_test_files = $null
330
+ }
331
+ }
332
+
333
+ # Escalation precedence. The first matching condition wins, so the order of
334
+ # these guards is part of the contract, not an implementation detail: epic
335
+ # child work escalates before any estimate is trusted, an invalid estimate
336
+ # escalates before language analysis, and the budget checks run last because
337
+ # they require a resolved single-language budget.
338
+ $escalationArgument = @{
339
+ ExecutionContext = $context
340
+ Language = $languages
341
+ ProductionFileCount = $productionCount
342
+ TestFileCount = $testCount
343
+ CrossCutting = $isCrossCutting
344
+ }
345
+
346
+ if ($script:EPIC_CHILD_CONTEXTS -ccontains $context) {
347
+ return Get-CodexEscalationReceipt @escalationArgument -Reason 'epic_child_context'
348
+ }
349
+ if ($productionCount -le 0 -or $testCount -lt 0) {
350
+ return Get-CodexEscalationReceipt @escalationArgument -Reason 'invalid_estimate'
351
+ }
352
+ if ($languages.Count -gt 1) {
353
+ return Get-CodexEscalationReceipt @escalationArgument -Reason 'cross_language'
354
+ }
355
+ if ($languages.Count -ne 1) {
356
+ return Get-CodexEscalationReceipt @escalationArgument -Reason 'unsupported_language'
357
+ }
358
+ if (-not $script:LANGUAGE_BUDGETS.ContainsKey($languages[0])) {
359
+ return Get-CodexEscalationReceipt @escalationArgument -Reason 'unsupported_language'
360
+ }
361
+
362
+ $budget = $script:LANGUAGE_BUDGETS[$languages[0]]
363
+ if ($isCrossCutting) {
364
+ return Get-CodexEscalationReceipt @escalationArgument -Reason 'cross_cutting' -Budget $budget
365
+ }
366
+ if (-not $budget['direct_mode_enabled']) {
367
+ return Get-CodexEscalationReceipt @escalationArgument -Reason 'direct_mode_disabled' -Budget $budget
368
+ }
369
+ if ($productionCount -gt $budget['max_production_files']) {
370
+ return Get-CodexEscalationReceipt @escalationArgument -Reason 'production_budget_exceeded' -Budget $budget
371
+ }
372
+
373
+ return @{
374
+ execution_context = $context
375
+ languages = $languages
376
+ production_file_count = $productionCount
377
+ test_file_count = $testCount
378
+ cross_cutting = $isCrossCutting
379
+ root_persona = $null
380
+ route = 'small'
381
+ topology = 'typed_engineer'
382
+ logical_agent = $budget['logical_agent']
383
+ routing_reason = 'within_language_budget'
384
+ max_production_files = $budget['max_production_files']
385
+ max_test_files = $budget['max_test_files']
386
+ }
387
+ }
388
+
389
+ # The resolver and the forced-root-persona accessor are exported; the validation,
390
+ # normalization, and receipt-construction helpers stay private so no consumer can
391
+ # bypass the single entry point.
392
+ Export-ModuleMember -Function Resolve-CodexTopology, Get-CodexForcedRootPersona