@danmoisan/drm-copilot-mcp 1.1.10 → 1.1.12

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 (82) hide show
  1. package/out/mcp-server.js +3517 -1104
  2. package/package.json +1 -1
  3. package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +1 -1
  4. package/resources/claude-customizations/.claude/agents/epic-planner.md +4 -1
  5. package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +15 -2
  6. package/resources/claude-customizations/.claude/agents/parallel-planner.md +3 -0
  7. package/resources/claude-customizations/.claude/agents/pr-author.md +15 -0
  8. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-authorization.ps1 +443 -0
  9. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +82 -51
  10. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +52 -3
  11. package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +120 -5
  12. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +127 -0
  13. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +163 -15
  14. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +21 -28
  15. package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +103 -6
  16. package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +124 -8
  17. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +171 -15
  18. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.epic-base-branch.ps1 +50 -10
  19. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +3 -1
  20. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner-helpers.ps1 +332 -0
  21. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +211 -182
  22. package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +35 -7
  23. package/resources/claude-customizations/.claude/hooks/hook-command-invocation.ps1 +483 -0
  24. package/resources/claude-customizations/.claude/hooks/hook-command-scanner.ps1 +483 -0
  25. package/resources/claude-customizations/.claude/hooks/validate-bash.ps1 +218 -6
  26. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadius.psm1 +18 -75
  27. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusConflict.psm1 +290 -0
  28. package/resources/claude-customizations/.claude/lib/cleanup-manifest/CleanupWorktreeManifest.psm1 +415 -0
  29. package/resources/claude-customizations/.claude/lib/codex-routing/CodexDeployment.psm1 +1 -0
  30. package/resources/claude-customizations/.claude/lib/project-file-merge/ProjectFileMerge.psm1 +355 -0
  31. package/resources/claude-customizations/.claude/lib/project-file-merge/ProjectFileMergeGrammar.psm1 +318 -0
  32. package/resources/claude-customizations/.claude/lib/project-file-merge/Resolve-MergeableConflict.ps1 +229 -0
  33. package/resources/claude-customizations/.claude/lib/worktree-resolution/EpicScopeReadiness.psm1 +176 -0
  34. package/resources/claude-customizations/.claude/lib/worktree-resolution/EpicScopeResolution.psm1 +369 -0
  35. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +392 -0
  36. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeResolution.psm1 +500 -0
  37. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeTargetResolution.psm1 +347 -0
  38. package/resources/claude-customizations/.claude/rules/orchestrator-state.md +27 -0
  39. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +66 -2
  40. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +323 -16
  41. package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +14 -1
  42. package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +4 -0
  43. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +46 -1
  44. package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +3 -1
  45. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +75 -8
  46. package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +6 -0
  47. package/resources/claude-customizations/.claude/skills/powershell-orchestration-state-machine/SKILL.md +29 -1
  48. package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +15 -0
  49. package/resources/claude-customizations/.claude/skills/quota-throttling/SKILL.md +221 -0
  50. package/resources/claude-customizations/config/blast-radius.json +7 -0
  51. package/resources/claude-customizations/pack-manifests/core.json +16 -0
  52. package/resources/codex-and-agents-customizations/.agents/skills/codex-model-routing/SKILL.md +13 -14
  53. package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +41 -0
  54. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-state/SKILL.md +41 -0
  55. package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/SKILL.md +26 -0
  56. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  57. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +242 -3
  58. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +67 -12
  59. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-worktree-removal-gate.ps1 +34 -8
  60. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +163 -15
  61. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +6 -1
  62. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +34 -7
  63. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation.ps1 +483 -0
  64. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-scanner.ps1 +483 -0
  65. package/resources/codex-and-agents-customizations/.codex/hooks/validate-bash.ps1 +130 -2
  66. package/resources/codex-and-agents-customizations/.codex/scripts/Resolve-CodexDeployment.ps1 +87 -0
  67. package/resources/codex-and-agents-customizations/.codex/scripts/Resolve-CodexTopology.ps1 +96 -0
  68. package/resources/codex-and-agents-customizations/.codex/scripts/codex-routing-cli-common.ps1 +370 -0
  69. package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-launch-contract.ps1 +475 -0
  70. package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-launch-runtime.ps1 +445 -0
  71. package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-persistence-runtime.ps1 +118 -0
  72. package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-sandbox-preflight.ps1 +69 -0
  73. package/resources/codex-and-agents-customizations/.codex/scripts/launch-epic-child-wave.ps1 +471 -0
  74. package/resources/codex-and-agents-customizations/.codex/scripts/post-codex-worktree-session.ps1 +280 -0
  75. package/resources/codex-and-agents-customizations/.codex/scripts/resume-epic-child.ps1 +263 -0
  76. package/resources/codex-and-agents-customizations/.codex-variants/csharp-legacy/agents/csharp-typed-engineer.toml +2 -1
  77. package/resources/codex-and-agents-customizations/pack-manifests/core.json +23 -1
  78. package/resources/config/orchestration-handoff-registry.json +138 -0
  79. package/resources/config/orchestration-handoff.schema.json +472 -0
  80. package/resources/lib/codex-routing/CodexDeployment.psm1 +315 -0
  81. package/resources/lib/codex-routing/CodexTopology.psm1 +394 -0
  82. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +64 -0
@@ -0,0 +1,394 @@
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
+ CONVENTION: this module fails fast at module scope and imports its siblings with -ErrorAction Stop.
51
+ #>
52
+
53
+ Set-StrictMode -Version Latest
54
+ $ErrorActionPreference = 'Stop'
55
+
56
+ # The three permitted execution contexts, the sorted tuple rendering the Python
57
+ # message interpolates, and the two contexts that mark epic child work.
58
+ $script:VALID_EXECUTION_CONTEXTS = @('standalone', 'epic_preparation_child', 'epic_execution_child')
59
+ $script:VALID_EXECUTION_CONTEXTS_PYTHON_TUPLE = "('epic_execution_child', 'epic_preparation_child', 'standalone')"
60
+ $script:EPIC_CHILD_CONTEXTS = @('epic_preparation_child', 'epic_execution_child')
61
+ $script:STANDALONE_CONTEXT = 'standalone'
62
+
63
+ # The two personas that may be forced as the root of a run, and the logical agent
64
+ # every escalation routes to.
65
+ $script:FORCED_ROOT_PERSONAS = @('epic-planner', 'epic-orchestrator')
66
+ $script:ORCHESTRATOR_LOGICAL_AGENT = 'orchestrator'
67
+
68
+ # The per-language direct-mode budgets. A language absent from this table is an
69
+ # unsupported language and escalates.
70
+ $script:LANGUAGE_BUDGETS = @{
71
+ python = @{ direct_mode_enabled = $true; max_production_files = 3; max_test_files = 3; logical_agent = 'python-typed-engineer' }
72
+ powershell = @{ direct_mode_enabled = $true; max_production_files = 2; max_test_files = 3; logical_agent = 'powershell-typed-engineer' }
73
+ csharp = @{ direct_mode_enabled = $true; max_production_files = 3; max_test_files = 3; logical_agent = 'csharp-typed-engineer' }
74
+ typescript = @{ direct_mode_enabled = $false; max_production_files = 0; max_test_files = 0; logical_agent = 'typescript-engineer' }
75
+ }
76
+
77
+ # The integral CLR types a JSON integer can deserialize to. A CLR boolean is
78
+ # deliberately absent: Python rejects bool where an int is required.
79
+ $script:INTEGRAL_TYPES = @([int], [long], [short], [byte])
80
+
81
+
82
+ function Get-CodexForcedRootPersona {
83
+ <#
84
+ .SYNOPSIS
85
+ Return the two personas that may be forced as the root of a run.
86
+ .DESCRIPTION
87
+ Read-only accessor for FORCED_ROOT_PERSONAS. Exported so the U6.T
88
+ root_persona enum check reads the same set the resolver enforces instead
89
+ of restating it.
90
+ .OUTPUTS
91
+ System.String[] - the forced root persona names, in declaration order.
92
+ The names are emitted to the pipeline individually, so a caller collects
93
+ them with @(...) in the usual way.
94
+ #>
95
+ [CmdletBinding()]
96
+ [OutputType([string[]])]
97
+ param()
98
+
99
+ return [string[]]@($script:FORCED_ROOT_PERSONAS)
100
+ }
101
+
102
+ function Test-CodexIntegralValue {
103
+ <#
104
+ .SYNOPSIS
105
+ Report whether a value is an integer, rejecting booleans.
106
+ .DESCRIPTION
107
+ Private predicate mirroring the Python guard
108
+ `isinstance(value, bool) or not isinstance(value, int)`. Python's bool is
109
+ a subclass of int, so the reference rejects it explicitly; this port
110
+ compares the exact CLR type so no boolean satisfies the test.
111
+ .PARAMETER Value
112
+ The candidate value. May be $null.
113
+ .OUTPUTS
114
+ System.Boolean - $true only for a non-boolean integral value.
115
+ #>
116
+ [CmdletBinding()]
117
+ [OutputType([bool])]
118
+ param(
119
+ [Parameter(Mandatory = $true)]
120
+ [AllowNull()]
121
+ [object] $Value
122
+ )
123
+
124
+ if ($null -eq $Value -or $Value -is [bool]) { return $false }
125
+ foreach ($integralType in $script:INTEGRAL_TYPES) {
126
+ if ($Value.GetType() -eq $integralType) { return $true }
127
+ }
128
+ return $false
129
+ }
130
+
131
+ function Get-CodexNormalizedLanguage {
132
+ <#
133
+ .SYNOPSIS
134
+ Return unique, lowercased language names in stable ordinal order.
135
+ .DESCRIPTION
136
+ Private helper mirroring _normalize_languages. Every element must be a
137
+ non-blank string; anything else throws the ValueError-equivalent. The
138
+ result is deduplicated and ordinally sorted so the resolved receipt is
139
+ order-independent, matching Python's sorted(set(...)).
140
+ .PARAMETER Language
141
+ The raw language collection. A null value is treated as empty.
142
+ .OUTPUTS
143
+ System.String[] - the normalized language names. Declared additionally as
144
+ System.Object[] because the array is emitted through the unary-comma form
145
+ that stops PowerShell unrolling it.
146
+ #>
147
+ [CmdletBinding()]
148
+ [OutputType([string[]], [object[]])]
149
+ param(
150
+ [Parameter(Mandatory = $true)]
151
+ [AllowNull()]
152
+ [object] $Language
153
+ )
154
+
155
+ $normalized = [System.Collections.Generic.HashSet[string]]::new()
156
+
157
+ # Reject the whole collection on the first malformed member so a partially
158
+ # valid language list can never route a delegation.
159
+ foreach ($item in @($Language)) {
160
+ if (-not ($item -is [string]) -or [string]::IsNullOrWhiteSpace([string]$item)) {
161
+ throw [System.ArgumentException]::new('languages must contain non-empty strings.')
162
+ }
163
+ [void]$normalized.Add(([string]$item).Trim().ToLowerInvariant())
164
+ }
165
+
166
+ # Ordinal ordering matches Python's sorted() over the same names; PowerShell's
167
+ # culture-aware Sort-Object would not be guaranteed to. The unary comma stops
168
+ # PowerShell from unrolling the array on return, which would otherwise turn a
169
+ # single-language result into a bare string and a zero-language result into
170
+ # nothing at all.
171
+ $sorted = [string[]]@($normalized)
172
+ [Array]::Sort($sorted, [System.StringComparer]::Ordinal)
173
+ return , $sorted
174
+ }
175
+
176
+ function Get-CodexEscalationReceipt {
177
+ <#
178
+ .SYNOPSIS
179
+ Build a large-route orchestrator receipt for one escalation reason.
180
+ .DESCRIPTION
181
+ Private pure builder mirroring _orchestrator_receipt. Every escalation returns
182
+ the same shape and differs only in routing_reason and in whether a
183
+ language budget was known at the point of escalation.
184
+ .PARAMETER ExecutionContext
185
+ The validated execution context.
186
+ .PARAMETER Language
187
+ The normalized language names.
188
+ .PARAMETER ProductionFileCount
189
+ The validated production file count.
190
+ .PARAMETER TestFileCount
191
+ The validated test file count.
192
+ .PARAMETER CrossCutting
193
+ The validated cross-cutting indicator.
194
+ .PARAMETER Reason
195
+ The escalation reason recorded in routing_reason.
196
+ .PARAMETER Budget
197
+ The language budget when one was resolved, otherwise $null. When absent,
198
+ both max file counts are reported as null.
199
+ .OUTPUTS
200
+ System.Collections.Hashtable - the twelve-key topology receipt.
201
+ #>
202
+ [CmdletBinding()]
203
+ [OutputType([hashtable])]
204
+ param(
205
+ [Parameter(Mandatory = $true)][string] $ExecutionContext,
206
+ [Parameter(Mandatory = $true)][AllowEmptyCollection()][string[]] $Language,
207
+ [Parameter(Mandatory = $true)][int] $ProductionFileCount,
208
+ [Parameter(Mandatory = $true)][int] $TestFileCount,
209
+ [Parameter(Mandatory = $true)][bool] $CrossCutting,
210
+ [Parameter(Mandatory = $true)][string] $Reason,
211
+ [Parameter(Mandatory = $false)][AllowNull()][hashtable] $Budget = $null
212
+ )
213
+
214
+ return @{
215
+ execution_context = $ExecutionContext
216
+ languages = $Language
217
+ production_file_count = $ProductionFileCount
218
+ test_file_count = $TestFileCount
219
+ cross_cutting = $CrossCutting
220
+ root_persona = $null
221
+ route = 'large'
222
+ topology = 'orchestrator'
223
+ logical_agent = $script:ORCHESTRATOR_LOGICAL_AGENT
224
+ routing_reason = $Reason
225
+ max_production_files = $(if ($null -ne $Budget) { $Budget['max_production_files'] } else { $null })
226
+ max_test_files = $(if ($null -ne $Budget) { $Budget['max_test_files'] } else { $null })
227
+ }
228
+ }
229
+
230
+ function Resolve-CodexTopology {
231
+ <#
232
+ .SYNOPSIS
233
+ Resolve the initial Codex implementation agent from scope data.
234
+ .DESCRIPTION
235
+ Faithful PowerShell port of resolve_codex_topology
236
+ (scripts/dev_tools/resolve_codex_topology.py). Validates the execution
237
+ context, the language collection, the two file counts, and the
238
+ cross-cutting indicator, then applies the forced-root-persona branch or
239
+ the fixed escalation precedence documented in the module header.
240
+ .PARAMETER Language
241
+ The languages the change touches. Normalized to unique lowercase names.
242
+ .PARAMETER ProductionFileCount
243
+ The production file count. Must be a non-boolean integer.
244
+ .PARAMETER TestFileCount
245
+ The test file count. Must be a non-boolean integer.
246
+ .PARAMETER ExecutionContext
247
+ One of standalone, epic_preparation_child, epic_execution_child.
248
+ .PARAMETER CrossCutting
249
+ Whether the change is cross-cutting. Must be a boolean.
250
+ .PARAMETER RootPersona
251
+ An optional forced root persona, which requires a standalone context.
252
+ .OUTPUTS
253
+ System.Collections.Hashtable with the twelve resolved keys:
254
+ execution_context, languages, production_file_count, test_file_count,
255
+ cross_cutting, root_persona, route, topology, logical_agent,
256
+ routing_reason, max_production_files, max_test_files.
257
+ #>
258
+ [CmdletBinding()]
259
+ [OutputType([hashtable])]
260
+ param(
261
+ [Parameter(Mandatory = $true)]
262
+ [AllowNull()]
263
+ [object] $Language,
264
+
265
+ [Parameter(Mandatory = $true)]
266
+ [AllowNull()]
267
+ [object] $ProductionFileCount,
268
+
269
+ [Parameter(Mandatory = $true)]
270
+ [AllowNull()]
271
+ [object] $TestFileCount,
272
+
273
+ [Parameter(Mandatory = $true)]
274
+ [AllowNull()]
275
+ [AllowEmptyString()]
276
+ [string] $ExecutionContext,
277
+
278
+ [Parameter(Mandatory = $false)]
279
+ [AllowNull()]
280
+ [object] $CrossCutting = $false,
281
+
282
+ [Parameter(Mandatory = $false)]
283
+ [AllowNull()]
284
+ [object] $RootPersona = $null
285
+ )
286
+
287
+ if ($script:VALID_EXECUTION_CONTEXTS -cnotcontains $ExecutionContext) {
288
+ throw [System.ArgumentException]::new(
289
+ "execution_context must be one of $($script:VALID_EXECUTION_CONTEXTS_PYTHON_TUPLE), found '$ExecutionContext'."
290
+ )
291
+ }
292
+ $context = $ExecutionContext
293
+ $languages = Get-CodexNormalizedLanguage -Language $Language
294
+
295
+ # Counts are validated before any routing so a malformed estimate can never
296
+ # be silently coerced into a route decision.
297
+ if (-not (Test-CodexIntegralValue -Value $ProductionFileCount)) {
298
+ throw [System.ArgumentException]::new('production_file_count must be an integer.')
299
+ }
300
+ if (-not (Test-CodexIntegralValue -Value $TestFileCount)) {
301
+ throw [System.ArgumentException]::new('test_file_count must be an integer.')
302
+ }
303
+ if (-not ($CrossCutting -is [bool])) {
304
+ throw [System.ArgumentException]::new('cross_cutting must be a boolean.')
305
+ }
306
+ $productionCount = [int]$ProductionFileCount
307
+ $testCount = [int]$TestFileCount
308
+ $isCrossCutting = [bool]$CrossCutting
309
+
310
+ # A forced root persona short-circuits all escalation logic and selects
311
+ # itself, but only from a standalone context.
312
+ if ($null -ne $RootPersona) {
313
+ if (-not ($RootPersona -is [string]) -or ($script:FORCED_ROOT_PERSONAS -cnotcontains [string]$RootPersona)) {
314
+ throw [System.ArgumentException]::new("Unsupported Codex root persona: '$RootPersona'.")
315
+ }
316
+ if ($context -cne $script:STANDALONE_CONTEXT) {
317
+ throw [System.ArgumentException]::new('A forced root persona requires standalone context.')
318
+ }
319
+ return @{
320
+ execution_context = $context
321
+ languages = $languages
322
+ production_file_count = $productionCount
323
+ test_file_count = $testCount
324
+ cross_cutting = $isCrossCutting
325
+ root_persona = [string]$RootPersona
326
+ route = 'epic'
327
+ topology = 'epic_persona'
328
+ logical_agent = [string]$RootPersona
329
+ routing_reason = 'forced_root_persona'
330
+ max_production_files = $null
331
+ max_test_files = $null
332
+ }
333
+ }
334
+
335
+ # Escalation precedence. The first matching condition wins, so the order of
336
+ # these guards is part of the contract, not an implementation detail: epic
337
+ # child work escalates before any estimate is trusted, an invalid estimate
338
+ # escalates before language analysis, and the budget checks run last because
339
+ # they require a resolved single-language budget.
340
+ $escalationArgument = @{
341
+ ExecutionContext = $context
342
+ Language = $languages
343
+ ProductionFileCount = $productionCount
344
+ TestFileCount = $testCount
345
+ CrossCutting = $isCrossCutting
346
+ }
347
+
348
+ if ($script:EPIC_CHILD_CONTEXTS -ccontains $context) {
349
+ return Get-CodexEscalationReceipt @escalationArgument -Reason 'epic_child_context'
350
+ }
351
+ if ($productionCount -le 0 -or $testCount -lt 0) {
352
+ return Get-CodexEscalationReceipt @escalationArgument -Reason 'invalid_estimate'
353
+ }
354
+ if ($languages.Count -gt 1) {
355
+ return Get-CodexEscalationReceipt @escalationArgument -Reason 'cross_language'
356
+ }
357
+ if ($languages.Count -ne 1) {
358
+ return Get-CodexEscalationReceipt @escalationArgument -Reason 'unsupported_language'
359
+ }
360
+ if (-not $script:LANGUAGE_BUDGETS.ContainsKey($languages[0])) {
361
+ return Get-CodexEscalationReceipt @escalationArgument -Reason 'unsupported_language'
362
+ }
363
+
364
+ $budget = $script:LANGUAGE_BUDGETS[$languages[0]]
365
+ if ($isCrossCutting) {
366
+ return Get-CodexEscalationReceipt @escalationArgument -Reason 'cross_cutting' -Budget $budget
367
+ }
368
+ if (-not $budget['direct_mode_enabled']) {
369
+ return Get-CodexEscalationReceipt @escalationArgument -Reason 'direct_mode_disabled' -Budget $budget
370
+ }
371
+ if ($productionCount -gt $budget['max_production_files']) {
372
+ return Get-CodexEscalationReceipt @escalationArgument -Reason 'production_budget_exceeded' -Budget $budget
373
+ }
374
+
375
+ return @{
376
+ execution_context = $context
377
+ languages = $languages
378
+ production_file_count = $productionCount
379
+ test_file_count = $testCount
380
+ cross_cutting = $isCrossCutting
381
+ root_persona = $null
382
+ route = 'small'
383
+ topology = 'typed_engineer'
384
+ logical_agent = $budget['logical_agent']
385
+ routing_reason = 'within_language_budget'
386
+ max_production_files = $budget['max_production_files']
387
+ max_test_files = $budget['max_test_files']
388
+ }
389
+ }
390
+
391
+ # The resolver and the forced-root-persona accessor are exported; the validation,
392
+ # normalization, and receipt-construction helpers stay private so no consumer can
393
+ # bypass the single entry point.
394
+ Export-ModuleMember -Function Resolve-CodexTopology, Get-CodexForcedRootPersona
@@ -42,6 +42,8 @@
42
42
  # Issue #275 remediation cycle 1 (fix #3): measure the epic-orchestrate hook set and
43
43
  # its dot-sourced sibling module so no production hook is excluded from coverage.
44
44
  '.claude/hooks/enforce-epic-merge-gate.ps1'
45
+ # Issue #670: CodeCoverage.Path is an explicit per-file allow-list, so the dot-sourced helpers file is listed here.
46
+ '.claude/hooks/enforce-epic-merge-gate-authorization.ps1'
45
47
  '.claude/hooks/enforce-epic-wave-barrier.ps1'
46
48
  '.claude/hooks/enforce-epic-worktree-removal-gate.ps1'
47
49
  '.claude/hooks/enforce-pr-author-skill.ps1'
@@ -105,6 +107,12 @@
105
107
  # interpreter; measured here so the new production modules are not excluded.
106
108
  '.claude/lib/codex-routing/CodexDeployment.psm1'
107
109
  '.claude/lib/codex-routing/CodexTopology.psm1'
110
+ # Issue #697 added the PowerShell routing CLI wrappers and their shared
111
+ # helper so destinations resolve routing without a Python interpreter;
112
+ # measured here so the new production scripts are not excluded.
113
+ '.codex/scripts/codex-routing-cli-common.ps1'
114
+ '.codex/scripts/Resolve-CodexDeployment.ps1'
115
+ '.codex/scripts/Resolve-CodexTopology.ps1'
108
116
  '.claude/lib/orchestrator-state/OrchestratorStateCodexModelReceipts.psm1'
109
117
  '.claude/lib/orchestrator-state/OrchestratorStateCodexTopologyReceipts.psm1'
110
118
  '.claude/lib/orchestrator-state/OrchestratorStateRoutingMatrix.psm1'
@@ -167,6 +175,19 @@
167
175
  '.claude/lib/blast-radius/BlastRadius.psm1'
168
176
  '.claude/lib/blast-radius/BlastRadiusNormalization.psm1'
169
177
  '.claude/lib/blast-radius/BlastRadiusTokenShape.psm1'
178
+ # Issue #643 added the eighth file below. It holds the mechanically-mergeable
179
+ # path exclusion plus the two overlap helpers relocated out of
180
+ # BlastRadius.psm1. CodeCoverage.Path is an explicit per-file allow-list, so
181
+ # the new module must be named here to be measured.
182
+ '.claude/lib/blast-radius/BlastRadiusConflict.psm1'
183
+ # Issue #643 also added the merge library below: the line grammar, the
184
+ # keyed-union merge, and the entry script. The entry script is measured
185
+ # like the two modules because its Pester suite dot-sources it behind a
186
+ # guarded entry-point body, so every line it attributes was executed by a
187
+ # test rather than by a run of the step itself.
188
+ '.claude/lib/project-file-merge/ProjectFileMergeGrammar.psm1'
189
+ '.claude/lib/project-file-merge/ProjectFileMerge.psm1'
190
+ '.claude/lib/project-file-merge/Resolve-MergeableConflict.ps1'
170
191
  # Issue #440 added the two parallel enforcement hooks (the Layer 1 cohort
171
192
  # barrier and the worktree removal gate) and extended the invocation-origin
172
193
  # hook with the parallel-agent family; measured here so no new or changed
@@ -195,6 +216,12 @@
195
216
  '.claude/lib/mermaid/MermaidLineScanner.psm1'
196
217
  '.claude/lib/mermaid/MermaidMarkdownFences.psm1'
197
218
  '.claude/lib/mermaid/MermaidValidation.psm1'
219
+ # Issue #635 added the sanctioned-removal manifest module read by both worktree
220
+ # removal gate hooks. CodeCoverage.Path is an explicit per-file allow-list, so an
221
+ # unregistered production file would sit outside the coverage denominator, which
222
+ # the Coverage Exclusion Policy forbids. Registered here beside the other
223
+ # .claude/lib modules.
224
+ '.claude/lib/cleanup-manifest/CleanupWorktreeManifest.psm1'
198
225
  # Issue #501 fixed the PreToolUse payload transport and shape across the whole
199
226
  # hook surface. CodeCoverage.Path is an explicit per-file allow-list, so the new
200
227
  # shared payload module, the six hooks that were changed but never registered,
@@ -212,10 +239,16 @@
212
239
  # table and the epic and parallel readiness predicates; registered so the new
213
240
  # production file stays in the coverage denominator per the Coverage Exclusion Policy.
214
241
  '.claude/hooks/enforce-orchestration-preimplementation-gate-modes.ps1'
242
+ # Issue #663 added this dot-sourced sibling for the epic-scope command and path legs.
243
+ '.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1'
215
244
  '.claude/hooks/enforce-evidence-locations.ps1'
216
245
  '.claude/hooks/enforce-feature-folder-order.ps1'
217
246
  '.claude/hooks/enforce-checkpoint-monotonic.ps1'
218
247
  '.claude/hooks/enforce-prd-feature-before-planner.ps1'
248
+ # Issue #672 split the prd-feature gate's resolution logic into a dot-sourced
249
+ # sibling; registered so the new production file stays in the coverage
250
+ # denominator per the Coverage Exclusion Policy.
251
+ '.claude/hooks/enforce-prd-feature-before-planner-helpers.ps1'
219
252
  '.claude/hooks/enforce-parallel-cohort-barrier-helpers.ps1'
220
253
  '.claude/hooks/enforce-pr-author-skill-helpers.ps1'
221
254
  # Issue #526 added this out-of-band release-verification module (Layer B of the
@@ -242,6 +275,37 @@
242
275
  'scripts/dev-tools/Invoke-ReleaseReconciliation.ps1'
243
276
  # Issue #552 validates start-time routing attestation coverage for this hook.
244
277
  '.codex/hooks/record-subagent-routing-attestation.ps1'
278
+ # Issue #545 adds the shared command parser as two dot-sourced .ps1 siblings, one
279
+ # pair per runtime. CodeCoverage.Path is an explicit per-file allow-list, so each
280
+ # new production file is registered here; without it the file would sit outside
281
+ # the coverage denominator, which the Coverage Exclusion Policy forbids. Both
282
+ # files define functions only and are dot-sourced by their Pester suites, so line
283
+ # attribution is valid. No extensions/drm-copilot/resources/ path is added,
284
+ # because the list holds zero entries under that prefix and every bundle mirror is
285
+ # guarded by byte identity instead.
286
+ '.claude/hooks/hook-command-scanner.ps1'
287
+ '.claude/hooks/hook-command-invocation.ps1'
288
+ '.codex/hooks/hook-command-scanner.ps1'
289
+ '.codex/hooks/hook-command-invocation.ps1'
290
+ # Issue #545 also modifies four Codex canonical hooks that the [P0-T9] baseline
291
+ # recorded as absent from this list. They are registered here so the files this
292
+ # change edits are all inside the coverage denominator.
293
+ '.codex/hooks/enforce-promotion-mcp-only.ps1'
294
+ '.codex/hooks/enforce-epic-merge-gate.ps1'
295
+ '.codex/hooks/enforce-epic-worktree-removal-gate.ps1'
296
+ '.codex/hooks/validate-bash.ps1'
297
+ # Issue #669 added the worktree-resolution module (target-worktree locator, path
298
+ # normaliser, and four-state call-target derivation). CodeCoverage.Path is an
299
+ # explicit per-file allow-list, so an unregistered production file would sit
300
+ # outside the coverage denominator, which the Coverage Exclusion Policy forbids.
301
+ # Registered here beside the other .claude/lib modules.
302
+ '.claude/lib/worktree-resolution/WorktreeResolution.psm1'
303
+ '.claude/lib/worktree-resolution/WorktreeTargetResolution.psm1'
304
+ # Issue #673 added the identity-resolution module, registered for the same reason.
305
+ '.claude/lib/worktree-resolution/WorktreeItemResolution.psm1'
306
+ # Issue #663 added the epic-scope resolver and its pure readiness predicates.
307
+ '.claude/lib/worktree-resolution/EpicScopeResolution.psm1'
308
+ '.claude/lib/worktree-resolution/EpicScopeReadiness.psm1'
245
309
  )
246
310
  # Optional: don't fail the run on coverage percentage
247
311
  CoveragePercentTarget = 0