@danmoisan/drm-copilot-mcp 1.1.11 → 1.1.14
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 +368 -209
- package/package.json +2 -2
- package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +1 -1
- package/resources/claude-customizations/.claude/agents/epic-planner.md +4 -1
- package/resources/claude-customizations/.claude/agents/parallel-planner.md +18 -1
- package/resources/claude-customizations/.claude/agents/pr-author.md +15 -0
- package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +16 -8
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-authorization.ps1 +443 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +33 -37
- package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +120 -5
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +127 -0
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +172 -24
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +14 -27
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +66 -5
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +134 -10
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.epic-base-branch.ps1 +37 -8
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +3 -1
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner-helpers.ps1 +332 -0
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +211 -182
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadius.psm1 +43 -6
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusScheduling.psm1 +486 -0
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusValidation.psm1 +5 -2
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusWriteIntent.psm1 +446 -0
- package/resources/claude-customizations/.claude/lib/codex-routing/CodexDeployment.psm1 +1 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/EpicScopeReadiness.psm1 +176 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/EpicScopeResolution.psm1 +369 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +392 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeResolution.psm1 +500 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeTargetResolution.psm1 +347 -0
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +27 -0
- package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +150 -2
- package/resources/claude-customizations/.claude/rules/plan-acceptance-gates.md +5 -5
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +12 -0
- package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +14 -1
- package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +15 -0
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +4 -1
- package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +32 -17
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +39 -6
- package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +39 -10
- package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +15 -0
- package/resources/claude-customizations/.claude/skills/quota-throttling/SKILL.md +221 -0
- package/resources/claude-customizations/config/blast-radius.json +14 -1
- package/resources/claude-customizations/pack-manifests/core.json +11 -0
- package/resources/codex-and-agents-customizations/.agents/skills/codex-model-routing/SKILL.md +13 -14
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +193 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +15 -5
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-resolution.ps1 +493 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +189 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +172 -24
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +14 -27
- package/resources/codex-and-agents-customizations/.codex/scripts/Resolve-CodexDeployment.ps1 +87 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/Resolve-CodexTopology.ps1 +96 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/codex-routing-cli-common.ps1 +370 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-launch-contract.ps1 +475 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-launch-runtime.ps1 +445 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-persistence-runtime.ps1 +118 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-sandbox-preflight.ps1 +69 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/launch-epic-child-wave.ps1 +471 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/post-codex-worktree-session.ps1 +280 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/resume-epic-child.ps1 +263 -0
- package/resources/codex-and-agents-customizations/.codex-variants/csharp-legacy/agents/csharp-typed-engineer.toml +2 -1
- package/resources/codex-and-agents-customizations/pack-manifests/core.json +20 -1
- package/resources/lib/codex-routing/CodexDeployment.psm1 +315 -0
- package/resources/lib/codex-routing/CodexTopology.psm1 +394 -0
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +37 -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'
|
|
@@ -137,6 +145,9 @@
|
|
|
137
145
|
# table and the epic and parallel readiness predicates; registered so the new
|
|
138
146
|
# production file stays in the coverage denominator per the Coverage Exclusion Policy.
|
|
139
147
|
'.codex/hooks/enforce-orchestration-preimplementation-gate-modes.ps1'
|
|
148
|
+
# Issue #707 added these two dot-sourced siblings for the Codex epic-scope command and path legs.
|
|
149
|
+
'.codex/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1'
|
|
150
|
+
'.codex/hooks/enforce-orchestration-preimplementation-gate-epic-resolution.ps1'
|
|
140
151
|
# Issue #415 remediation cycle 2 (R-COV): the detached-HEAD null-guard fix changed
|
|
141
152
|
# these two Codex PreToolUse hooks. Both were absent from this list, so the changed
|
|
142
153
|
# production surface was outside the coverage denominator. Measured here so the
|
|
@@ -172,6 +183,14 @@
|
|
|
172
183
|
# BlastRadius.psm1. CodeCoverage.Path is an explicit per-file allow-list, so
|
|
173
184
|
# the new module must be named here to be measured.
|
|
174
185
|
'.claude/lib/blast-radius/BlastRadiusConflict.psm1'
|
|
186
|
+
# Issue #722 added the ninth file below, the integration-cost scheduling
|
|
187
|
+
# layer over the unchanged contention relation. CodeCoverage.Path is an
|
|
188
|
+
# explicit per-file allow-list, so the new module is named here to keep
|
|
189
|
+
# it in the coverage denominator.
|
|
190
|
+
'.claude/lib/blast-radius/BlastRadiusScheduling.psm1'
|
|
191
|
+
# Issue #722 also added the tenth file below, the write-intent
|
|
192
|
+
# extraction layer the facade and validation module delegate to.
|
|
193
|
+
'.claude/lib/blast-radius/BlastRadiusWriteIntent.psm1'
|
|
175
194
|
# Issue #643 also added the merge library below: the line grammar, the
|
|
176
195
|
# keyed-union merge, and the entry script. The entry script is measured
|
|
177
196
|
# like the two modules because its Pester suite dot-sources it behind a
|
|
@@ -231,10 +250,16 @@
|
|
|
231
250
|
# table and the epic and parallel readiness predicates; registered so the new
|
|
232
251
|
# production file stays in the coverage denominator per the Coverage Exclusion Policy.
|
|
233
252
|
'.claude/hooks/enforce-orchestration-preimplementation-gate-modes.ps1'
|
|
253
|
+
# Issue #663 added this dot-sourced sibling for the epic-scope command and path legs.
|
|
254
|
+
'.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1'
|
|
234
255
|
'.claude/hooks/enforce-evidence-locations.ps1'
|
|
235
256
|
'.claude/hooks/enforce-feature-folder-order.ps1'
|
|
236
257
|
'.claude/hooks/enforce-checkpoint-monotonic.ps1'
|
|
237
258
|
'.claude/hooks/enforce-prd-feature-before-planner.ps1'
|
|
259
|
+
# Issue #672 split the prd-feature gate's resolution logic into a dot-sourced
|
|
260
|
+
# sibling; registered so the new production file stays in the coverage
|
|
261
|
+
# denominator per the Coverage Exclusion Policy.
|
|
262
|
+
'.claude/hooks/enforce-prd-feature-before-planner-helpers.ps1'
|
|
238
263
|
'.claude/hooks/enforce-parallel-cohort-barrier-helpers.ps1'
|
|
239
264
|
'.claude/hooks/enforce-pr-author-skill-helpers.ps1'
|
|
240
265
|
# Issue #526 added this out-of-band release-verification module (Layer B of the
|
|
@@ -280,6 +305,18 @@
|
|
|
280
305
|
'.codex/hooks/enforce-epic-merge-gate.ps1'
|
|
281
306
|
'.codex/hooks/enforce-epic-worktree-removal-gate.ps1'
|
|
282
307
|
'.codex/hooks/validate-bash.ps1'
|
|
308
|
+
# Issue #669 added the worktree-resolution module (target-worktree locator, path
|
|
309
|
+
# normaliser, and four-state call-target derivation). CodeCoverage.Path is an
|
|
310
|
+
# explicit per-file allow-list, so an unregistered production file would sit
|
|
311
|
+
# outside the coverage denominator, which the Coverage Exclusion Policy forbids.
|
|
312
|
+
# Registered here beside the other .claude/lib modules.
|
|
313
|
+
'.claude/lib/worktree-resolution/WorktreeResolution.psm1'
|
|
314
|
+
'.claude/lib/worktree-resolution/WorktreeTargetResolution.psm1'
|
|
315
|
+
# Issue #673 added the identity-resolution module, registered for the same reason.
|
|
316
|
+
'.claude/lib/worktree-resolution/WorktreeItemResolution.psm1'
|
|
317
|
+
# Issue #663 added the epic-scope resolver and its pure readiness predicates.
|
|
318
|
+
'.claude/lib/worktree-resolution/EpicScopeResolution.psm1'
|
|
319
|
+
'.claude/lib/worktree-resolution/EpicScopeReadiness.psm1'
|
|
283
320
|
)
|
|
284
321
|
# Optional: don't fail the run on coverage percentage
|
|
285
322
|
CoveragePercentTarget = 0
|