@danmoisan/drm-copilot-mcp 1.1.16 → 1.1.17

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 (58) hide show
  1. package/out/mcp-server.js +639 -27
  2. package/package.json +1 -1
  3. package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +12 -6
  4. package/resources/claude-customizations/.claude/agents/python-typed-engineer.md +3 -3
  5. package/resources/claude-customizations/.claude/hooks/{enforce-powershell-batch-budget-route.ps1 → enforce-batch-budget-route.ps1} +12 -10
  6. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-resolution.ps1 +221 -0
  7. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +42 -66
  8. package/resources/claude-customizations/.claude/hooks/enforce-epic-wave-barrier.ps1 +58 -10
  9. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate-resolution.ps1 +144 -0
  10. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +36 -47
  11. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +179 -27
  12. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +21 -38
  13. package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +4 -4
  14. package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier.ps1 +58 -10
  15. package/resources/claude-customizations/.claude/hooks/enforce-parallel-drift-gate.ps1 +59 -9
  16. package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +114 -37
  17. package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +3 -3
  18. package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +99 -64
  19. package/resources/claude-customizations/.claude/lib/bash/abandon-parallel-item.sh +176 -0
  20. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusConflict.psm1 +193 -14
  21. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusGlob.psm1 +51 -28
  22. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusScheduling.psm1 +5 -8
  23. package/resources/claude-customizations/.claude/lib/parallel-drift/Invoke-ParallelDriftDetection.ps1 +336 -0
  24. package/resources/claude-customizations/.claude/lib/parallel-drift/ParallelDrift.psm1 +467 -0
  25. package/resources/claude-customizations/.claude/lib/parallel-drift/ParallelDriftHalt.psm1 +388 -0
  26. package/resources/claude-customizations/.claude/lib/worktree-resolution/EpicScopeResolution.psm1 +53 -32
  27. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +19 -4
  28. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1 +497 -0
  29. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +21 -0
  30. package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +2 -0
  31. package/resources/claude-customizations/.claude/skills/invoke-csharp-engineer/SKILL.md +4 -0
  32. package/resources/claude-customizations/.claude/skills/invoke-powershell-engineer/SKILL.md +4 -0
  33. package/resources/claude-customizations/.claude/skills/invoke-python-engineer/SKILL.md +6 -3
  34. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +3 -1
  35. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +32 -18
  36. package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +10 -10
  37. package/resources/claude-customizations/.claude/skills/python-change-budget-router/SKILL.md +11 -35
  38. package/resources/claude-customizations/pack-manifests/core.json +8 -0
  39. package/resources/claude-customizations/pack-manifests/powershell.json +0 -1
  40. package/resources/codex-and-agents-customizations/.agents/skills/invoke-powershell-engineer/SKILL.md +0 -1
  41. package/resources/codex-and-agents-customizations/.agents/skills/invoke-python-engineer/SKILL.md +2 -3
  42. package/resources/codex-and-agents-customizations/.agents/skills/python-change-budget-router/SKILL.md +10 -34
  43. package/resources/codex-and-agents-customizations/.codex/agents/python-orchestrator.toml +2 -2
  44. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c1.toml +3 -3
  45. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c2.toml +3 -3
  46. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3-elevated.toml +3 -3
  47. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3.toml +3 -3
  48. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c4.toml +3 -3
  49. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer.toml +3 -3
  50. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  51. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-batch-budget-route.ps1 +139 -0
  52. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +4 -124
  53. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +169 -79
  54. package/resources/codex-and-agents-customizations/pack-manifests/core.json +1 -0
  55. package/resources/customizations/.github/agents/python-orchestrator.agent.md +4 -4
  56. package/resources/customizations/.github/agents/python-typed-engineer.agent.md +7 -8
  57. package/resources/customizations/.github/prompts/orchestrate-python-work.prompt.md +3 -3
  58. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +12 -1
@@ -0,0 +1,497 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Portable-identity resolution of the epic or parallel run a gated call belongs to (issue #690).
4
+
5
+ .DESCRIPTION
6
+ Selects the worktree whose run checkpoint governs a call from portable identity
7
+ alone: the integration_branch:, epic_feature_folder:, and parallel_slug: literals of
8
+ a kickoff prompt, or a pull request number or worktree path a command names. A
9
+ checkpoint path is always composed beneath a resolved root, so no read binds to the
10
+ calling process's directory.
11
+
12
+ A live worktree matches an epic run when its epic checkpoint records route_id epic
13
+ and the integration branch; several matches are narrowed to the worktree that has
14
+ that branch checked out, because git checks a branch out in at most one worktree. A
15
+ parallel run has no branch, so several matches are ambiguous. A record match keeps
16
+ the live worktrees whose run checkpoint records the pull request number or worktree
17
+ path. There is no session-root-first shortcut: a stale copy at the session root never
18
+ wins over the worktree that owns the run.
19
+
20
+ .NOTES
21
+ PowerShell 7+. Re-uses its siblings for enumeration, the ascent, normalisation, the
22
+ join, the result constructor, the SessionRoot/OtherWorktree labelling, and both
23
+ reason-code accessors, re-implementing none. Its only filesystem read is the
24
+ checkpoint-text seam. Mirrored byte-identically.
25
+ CONVENTION: this module fails fast at module scope and imports its siblings with -ErrorAction Stop.
26
+ #>
27
+
28
+ Set-StrictMode -Version Latest
29
+ $ErrorActionPreference = 'Stop'
30
+
31
+ Import-Module (Join-Path $PSScriptRoot 'WorktreeResolution.psm1') -ErrorAction Stop
32
+ Import-Module (Join-Path $PSScriptRoot 'WorktreeTargetResolution.psm1') -ErrorAction Stop
33
+ Import-Module (Join-Path $PSScriptRoot 'WorktreeItemResolution.psm1') -ErrorAction Stop
34
+
35
+ # The run checkpoint locations relative to a worktree root.
36
+ $script:EpicCheckpointRelativePath = 'artifacts/orchestration/epic-orchestrator-state.json'
37
+ $script:ParallelCheckpointRelativePath = 'artifacts/orchestration/parallel-orchestrator-state.json'
38
+
39
+ # The route_id each run checkpoint must record.
40
+ $script:EpicRouteId = 'epic'
41
+ $script:ParallelRouteId = 'parallel'
42
+
43
+ # Where a stale checkpoint copy is moved so it stops competing with the owning worktree.
44
+ $script:HandoffRemedy = 'artifacts/orchestration/handoff/'
45
+
46
+ # The kickoff literals, matched case-sensitively and never as the tail of a longer word.
47
+ $script:IntegrationBranchPattern = '(?<![A-Za-z0-9_])integration_branch:[ \t]*(?<value>\S+)'
48
+ $script:EpicSlugPattern = '(?<![A-Za-z0-9_])epic_feature_folder:[ \t]*(?<value>\S+)'
49
+ $script:ParallelSlugPattern = '(?<![A-Za-z0-9_])parallel_slug:[ \t]*(?<value>\S+)'
50
+
51
+ # A drive-rooted or slash-rooted path; anything else is relative to some directory.
52
+ $script:AbsolutePathPattern = '^([A-Za-z]:(/|$)|/)'
53
+
54
+ # Private: the single value a pattern names in a text, or $null when it names none or
55
+ # more than one distinct value (a prompt that names two runs identifies neither).
56
+ function Get-WorktreeRunSignalValue {
57
+ [CmdletBinding()]
58
+ [OutputType([string])]
59
+ param(
60
+ [Parameter(Mandatory = $true)] [AllowEmptyString()] [string] $Text,
61
+ [Parameter(Mandatory = $true)] [string] $Pattern
62
+ )
63
+
64
+ $values = [System.Collections.Generic.List[string]]::new()
65
+ foreach ($match in [regex]::Matches($Text, $Pattern)) {
66
+ $value = $match.Groups['value'].Value
67
+ # The kickoff sentence ends the value with a full stop; one is removed.
68
+ if ($value.EndsWith('.')) { $value = $value.Substring(0, $value.Length - 1) }
69
+ if ($value.Length -gt 0 -and -not $values.Contains($value)) { $values.Add($value) }
70
+ }
71
+ if ($values.Count -eq 1) { return $values[0] }
72
+ return $null
73
+ }
74
+
75
+ function Find-WorktreeRunIdentitySignal {
76
+ <#
77
+ .SYNOPSIS
78
+ Read the run identity literals from a prompt.
79
+ .DESCRIPTION
80
+ Pure. Returns an object with IntegrationBranch, EpicSlug, and ParallelSlug, each
81
+ the single distinct value the prompt names for that literal, or $null.
82
+ .PARAMETER Text
83
+ The prompt text to scan.
84
+ #>
85
+ [CmdletBinding()]
86
+ [OutputType([pscustomobject])]
87
+ param(
88
+ [Parameter(Mandatory = $true)] [AllowNull()] [AllowEmptyString()] [string] $Text
89
+ )
90
+
91
+ $source = [string] $Text
92
+ return [pscustomobject]@{
93
+ IntegrationBranch = Get-WorktreeRunSignalValue -Text $source -Pattern $script:IntegrationBranchPattern
94
+ EpicSlug = Get-WorktreeRunSignalValue -Text $source -Pattern $script:EpicSlugPattern
95
+ ParallelSlug = Get-WorktreeRunSignalValue -Text $source -Pattern $script:ParallelSlugPattern
96
+ }
97
+ }
98
+
99
+ function Get-WorktreeRunCheckpointText {
100
+ <#
101
+ .SYNOPSIS
102
+ Return the raw text of a file, or $null when it is absent or unreadable; an unreadable file also writes a diagnostic to stderr.
103
+ .DESCRIPTION
104
+ The module's only filesystem read, isolated as the seam a test mocks.
105
+ .PARAMETER Path
106
+ The absolute path to read.
107
+ #>
108
+ [CmdletBinding()]
109
+ [OutputType([string])]
110
+ param(
111
+ [Parameter(Mandatory = $true)] [AllowNull()] [AllowEmptyString()] [string] $Path
112
+ )
113
+
114
+ if ([string]::IsNullOrWhiteSpace($Path)) { return $null }
115
+ if (-not (Test-Path -LiteralPath $Path -PathType Leaf -ErrorAction SilentlyContinue)) { return $null }
116
+ # An unreadable file is reported on stderr and treated as absent, so resolution stays fail-closed and the hook's stdout stays JSON-only.
117
+ try { return [System.IO.File]::ReadAllText($Path) }
118
+ catch { [Console]::Error.WriteLine(("WORKTREE_RUN_CHECKPOINT_UNREADABLE: '{0}': {1}" -f $Path, $_.Exception.Message)); return $null }
119
+ }
120
+
121
+ function Get-WorktreeRunCheckpointPath {
122
+ <#
123
+ .SYNOPSIS
124
+ Compose the absolute path of a run or item checkpoint beneath a worktree root.
125
+ .DESCRIPTION
126
+ The single composition of an absolute checkpoint path for the gates. Throws when
127
+ the root is not absolute, through the shared join.
128
+ .PARAMETER Kind
129
+ epic, parallel, or item.
130
+ .PARAMETER WorktreeRoot
131
+ The absolute worktree root, in either separator style.
132
+ #>
133
+ [CmdletBinding()]
134
+ [OutputType([string])]
135
+ param(
136
+ [Parameter(Mandatory = $true)] [ValidateSet('epic', 'parallel', 'item')] [string] $Kind,
137
+ [Parameter(Mandatory = $true)] [string] $WorktreeRoot
138
+ )
139
+
140
+ if ($Kind -eq 'item') { return (Get-WorktreeItemCheckpointPath -WorktreeRoot $WorktreeRoot) }
141
+ $relative = if ($Kind -eq 'epic') { $script:EpicCheckpointRelativePath } else { $script:ParallelCheckpointRelativePath }
142
+ return (Join-WorktreeResolutionPath -WorktreeRoot $WorktreeRoot -RepoRelativePath $relative)
143
+ }
144
+
145
+ # Private: parse checkpoint text into an object, or $null for blank, unparseable, or
146
+ # non-object text. -NoEnumerate keeps a one-element JSON array from reading as an object.
147
+ function ConvertFrom-WorktreeRunCheckpointText {
148
+ [CmdletBinding()]
149
+ [OutputType([pscustomobject])]
150
+ param(
151
+ [Parameter(Mandatory = $true)] [AllowNull()] [AllowEmptyString()] [string] $Text
152
+ )
153
+
154
+ if ([string]::IsNullOrWhiteSpace($Text)) { return $null }
155
+ $parsed = $null
156
+ try { $parsed = ConvertFrom-Json -InputObject $Text -NoEnumerate } catch { return $null }
157
+ # The full type name is tested, because a wrapped array also satisfies [pscustomobject].
158
+ if ($null -eq $parsed -or $parsed -isnot [System.Management.Automation.PSCustomObject]) { return $null }
159
+ return $parsed
160
+ }
161
+
162
+ # Private: a named property of a parsed object, or $null when absent (strict mode would
163
+ # otherwise throw on a missing member).
164
+ function Get-WorktreeRunPropertyValue {
165
+ [CmdletBinding()]
166
+ [OutputType([object])]
167
+ param(
168
+ [Parameter(Mandatory = $true)] [AllowNull()] [object] $InputObject,
169
+ [Parameter(Mandatory = $true)] [string] $Name
170
+ )
171
+
172
+ if ($null -eq $InputObject -or $InputObject -isnot [System.Management.Automation.PSCustomObject]) { return $null }
173
+ if ($InputObject.PSObject.Properties.Name -notcontains $Name) { return $null }
174
+ return $InputObject.$Name
175
+ }
176
+
177
+ # Private: read and parse a live root's run checkpoint of one kind, keeping it only when
178
+ # it records the kind's route_id.
179
+ function Get-WorktreeRunCheckpoint {
180
+ [CmdletBinding()]
181
+ [OutputType([pscustomobject])]
182
+ param(
183
+ [Parameter(Mandatory = $true)] [ValidateSet('epic', 'parallel')] [string] $Kind,
184
+ [Parameter(Mandatory = $true)] [string] $WorktreeRoot
185
+ )
186
+
187
+ $text = Get-WorktreeRunCheckpointText -Path (Get-WorktreeRunCheckpointPath -Kind $Kind -WorktreeRoot $WorktreeRoot)
188
+ $checkpoint = ConvertFrom-WorktreeRunCheckpointText -Text $text
189
+ $routeId = if ($Kind -eq 'epic') { $script:EpicRouteId } else { $script:ParallelRouteId }
190
+ if ([string] (Get-WorktreeRunPropertyValue -InputObject $checkpoint -Name 'route_id') -cne $routeId) { return $null }
191
+ return $checkpoint
192
+ }
193
+
194
+ # Private: the live roots reachable from the session path, assigned before wrapping
195
+ # because the seam returns its array as one object.
196
+ function Get-WorktreeRunLiveRoot {
197
+ [CmdletBinding()]
198
+ [OutputType([string[]], [object[]])]
199
+ param(
200
+ [Parameter(Mandatory = $true)] [string] $SessionRoot,
201
+ [string] $Branch
202
+ )
203
+
204
+ $liveRoots = if ([string]::IsNullOrWhiteSpace($Branch)) {
205
+ Get-WorktreeItemLiveRoot -SessionRoot $SessionRoot
206
+ }
207
+ else {
208
+ Get-WorktreeItemLiveRoot -SessionRoot $SessionRoot -Branch $Branch
209
+ }
210
+ return , [string[]] @($liveRoots | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
211
+ }
212
+
213
+ # Private: the zero/one/many decision shared by the parallel and record resolvers.
214
+ function Resolve-WorktreeRunMatch {
215
+ [CmdletBinding()]
216
+ [OutputType([pscustomobject])]
217
+ param(
218
+ [Parameter(Mandatory = $true)] [AllowEmptyCollection()] [string[]] $Matched,
219
+ [Parameter(Mandatory = $true)] [string] $SessionRoot,
220
+ [Parameter(Mandatory = $true)] [string] $Description
221
+ )
222
+
223
+ if ($Matched.Count -eq 0) {
224
+ return (New-WorktreeResolutionTargetResult -Status 'NoTarget' -SessionRoot $SessionRoot -Detail (
225
+ 'no live worktree''s run checkpoint records {0}' -f $Description))
226
+ }
227
+ if ($Matched.Count -eq 1) {
228
+ return (ConvertTo-WorktreeItemResolvedResult -WorktreeRoot $Matched[0] -SessionRoot $SessionRoot -Detail (
229
+ '{0} resolves to the live worktree whose run checkpoint records it' -f $Description))
230
+ }
231
+ $detail = ('{0} is recorded by the run checkpoints of {1} live worktrees; move the stale copy to {2}' -f
232
+ $Description, $Matched.Count, $script:HandoffRemedy)
233
+ return (New-WorktreeResolutionTargetResult -Status 'Ambiguous' -SessionRoot $SessionRoot -Candidate $Matched -Detail $detail)
234
+ }
235
+
236
+ function Resolve-WorktreeEpicTarget {
237
+ <#
238
+ .SYNOPSIS
239
+ Resolve the live worktree whose epic checkpoint governs an integration branch.
240
+ .DESCRIPTION
241
+ Keeps each live root whose epic checkpoint records route_id epic and the branch.
242
+ Zero matches resolve NoTarget; one resolves; several are narrowed to the root that
243
+ has the branch checked out, and anything other than one survivor is Ambiguous. A
244
+ supplied slug that a match's epic_feature_folder disagrees with is Ambiguous.
245
+ .PARAMETER IntegrationBranch
246
+ The integration branch the call names.
247
+ .PARAMETER EpicSlug
248
+ Optional. The epic_feature_folder the call names, used as a cross-check only.
249
+ .PARAMETER SessionRoot
250
+ The calling process's path, used only to find the repository and label a result.
251
+ #>
252
+ [CmdletBinding()]
253
+ [OutputType([pscustomobject])]
254
+ param(
255
+ [AllowNull()] [AllowEmptyString()] [string] $IntegrationBranch,
256
+ [AllowNull()] [AllowEmptyString()] [string] $EpicSlug,
257
+ [Parameter(Mandatory = $true)] [string] $SessionRoot
258
+ )
259
+
260
+ if ([string]::IsNullOrWhiteSpace($IntegrationBranch)) {
261
+ return (New-WorktreeResolutionTargetResult -Status 'NoTarget' -SessionRoot $SessionRoot -Detail (
262
+ 'the call carries no integration_branch: value, so the epic run it belongs to cannot be identified'))
263
+ }
264
+
265
+ $kept = [System.Collections.Generic.List[string]]::new()
266
+ $slugs = [System.Collections.Generic.List[string]]::new()
267
+ foreach ($root in (Get-WorktreeRunLiveRoot -SessionRoot $SessionRoot)) {
268
+ $checkpoint = Get-WorktreeRunCheckpoint -Kind 'epic' -WorktreeRoot $root
269
+ if ($null -eq $checkpoint) { continue }
270
+ if ([string] (Get-WorktreeRunPropertyValue -InputObject $checkpoint -Name 'integration_branch') -cne $IntegrationBranch) { continue }
271
+ $kept.Add($root)
272
+ $recordedSlug = Get-WorktreeRunPropertyValue -InputObject $checkpoint -Name 'epic_feature_folder'
273
+ if ($null -ne $recordedSlug) { $slugs.Add([string] $recordedSlug) }
274
+ }
275
+
276
+ # A slug the call names that a matching checkpoint contradicts is not guessed past.
277
+ if (-not [string]::IsNullOrWhiteSpace($EpicSlug)) {
278
+ $disagreeing = @($slugs | Where-Object { $_ -cne $EpicSlug })
279
+ if ($disagreeing.Count -gt 0) {
280
+ return (New-WorktreeResolutionTargetResult -Status 'Ambiguous' -SessionRoot $SessionRoot `
281
+ -Signal 'Branch' -SignalValue $IntegrationBranch -Candidate $kept.ToArray() -Detail (
282
+ "the call names epic_feature_folder '{0}', but an epic checkpoint recording integration_branch '{1}' records epic_feature_folder '{2}'" -f
283
+ $EpicSlug, $IntegrationBranch, $disagreeing[0]))
284
+ }
285
+ }
286
+
287
+ if ($kept.Count -eq 0) {
288
+ return (New-WorktreeResolutionTargetResult -Status 'NoTarget' -SessionRoot $SessionRoot -Detail (
289
+ "no live worktree's epic checkpoint records integration_branch '{0}'" -f $IntegrationBranch))
290
+ }
291
+ if ($kept.Count -eq 1) {
292
+ return (ConvertTo-WorktreeItemResolvedResult -WorktreeRoot $kept[0] -SessionRoot $SessionRoot -Branch $IntegrationBranch -Detail (
293
+ "integration_branch '{0}' resolves to the live worktree whose epic checkpoint records it" -f $IntegrationBranch))
294
+ }
295
+
296
+ # Several copies: the owning worktree is the one with the integration branch checked out.
297
+ $branchRoots = Get-WorktreeRunLiveRoot -SessionRoot $SessionRoot -Branch $IntegrationBranch
298
+ $checkedOut = @(foreach ($root in $branchRoots) { ConvertTo-WorktreeResolutionNormalizedPath -Path $root })
299
+ $survivors = @($kept | Where-Object { $checkedOut -contains (ConvertTo-WorktreeResolutionNormalizedPath -Path $_) })
300
+ if ($survivors.Count -eq 1) {
301
+ return (ConvertTo-WorktreeItemResolvedResult -WorktreeRoot $survivors[0] -SessionRoot $SessionRoot -Branch $IntegrationBranch -Detail (
302
+ "integration_branch '{0}' resolves to the live worktree that has it checked out" -f $IntegrationBranch))
303
+ }
304
+ $detail = ("the epic checkpoints of {0} live worktrees record integration_branch '{1}', and {2} of them have it checked out; move the stale copy to {3}" -f
305
+ $kept.Count, $IntegrationBranch, $survivors.Count, $script:HandoffRemedy)
306
+ return (New-WorktreeResolutionTargetResult -Status 'Ambiguous' -SessionRoot $SessionRoot `
307
+ -Signal 'Branch' -SignalValue $IntegrationBranch -Candidate $kept.ToArray() -Detail $detail)
308
+ }
309
+
310
+ function Resolve-WorktreeParallelTarget {
311
+ <#
312
+ .SYNOPSIS
313
+ Resolve the live worktree whose parallel checkpoint records a parallel slug.
314
+ .DESCRIPTION
315
+ Keeps each live root whose parallel checkpoint records route_id parallel and the
316
+ slug. Zero matches resolve NoTarget, one resolves, and several are Ambiguous.
317
+ .PARAMETER ParallelSlug
318
+ The parallel_slug the call names.
319
+ .PARAMETER SessionRoot
320
+ The calling process's path, used only to find the repository and label a result.
321
+ #>
322
+ [CmdletBinding()]
323
+ [OutputType([pscustomobject])]
324
+ param(
325
+ [AllowNull()] [AllowEmptyString()] [string] $ParallelSlug,
326
+ [Parameter(Mandatory = $true)] [string] $SessionRoot
327
+ )
328
+
329
+ if ([string]::IsNullOrWhiteSpace($ParallelSlug)) {
330
+ return (New-WorktreeResolutionTargetResult -Status 'NoTarget' -SessionRoot $SessionRoot -Detail (
331
+ 'the call carries no parallel_slug: value, so the parallel run it belongs to cannot be identified'))
332
+ }
333
+ $matched = [System.Collections.Generic.List[string]]::new()
334
+ foreach ($root in (Get-WorktreeRunLiveRoot -SessionRoot $SessionRoot)) {
335
+ $checkpoint = Get-WorktreeRunCheckpoint -Kind 'parallel' -WorktreeRoot $root
336
+ if ($null -eq $checkpoint) { continue }
337
+ if ([string] (Get-WorktreeRunPropertyValue -InputObject $checkpoint -Name 'parallel_slug') -ceq $ParallelSlug) { $matched.Add($root) }
338
+ }
339
+ return (Resolve-WorktreeRunMatch -Matched $matched.ToArray() -SessionRoot $SessionRoot -Description ("parallel_slug '{0}'" -f $ParallelSlug))
340
+ }
341
+
342
+ # Private: compare two worktree paths, insensitive to separator style and trailing
343
+ # slashes, and case-insensitive only for drive-letter paths.
344
+ function Test-WorktreeRunPathEqual {
345
+ [CmdletBinding()]
346
+ [OutputType([bool])]
347
+ param(
348
+ [Parameter(Mandatory = $true)] [AllowNull()] [AllowEmptyString()] [string] $Left,
349
+ [Parameter(Mandatory = $true)] [AllowNull()] [AllowEmptyString()] [string] $Right
350
+ )
351
+
352
+ if ([string]::IsNullOrWhiteSpace($Left) -or [string]::IsNullOrWhiteSpace($Right)) { return $false }
353
+ $a = ($Left.Trim() -replace '\\', '/').TrimEnd('/')
354
+ $b = ($Right.Trim() -replace '\\', '/').TrimEnd('/')
355
+ $isDrivePath = ($a -match '^[A-Za-z]:') -or ($b -match '^[A-Za-z]:')
356
+ $comparison = if ($isDrivePath) { [System.StringComparison]::OrdinalIgnoreCase } else { [System.StringComparison]::Ordinal }
357
+ return [string]::Equals($a, $b, $comparison)
358
+ }
359
+
360
+ # Private: compare two pull request numbers as integers.
361
+ function Test-WorktreeRunPrNumberEqual {
362
+ [CmdletBinding()]
363
+ [OutputType([bool])]
364
+ param(
365
+ [Parameter(Mandatory = $true)] [AllowNull()] [object] $Recorded,
366
+ [Parameter(Mandatory = $true)] [long] $Expected
367
+ )
368
+
369
+ if ($null -eq $Recorded) { return $false }
370
+ $number = [long] 0
371
+ if (-not [long]::TryParse(([string] $Recorded).Trim(), [ref] $number)) { return $false }
372
+ return ($number -eq $Expected)
373
+ }
374
+
375
+ # Private: whether a parsed run checkpoint records a pull request number or worktree path.
376
+ function Test-WorktreeRunCheckpointRecord {
377
+ [CmdletBinding()]
378
+ [OutputType([bool])]
379
+ param(
380
+ [Parameter(Mandatory = $true)] [pscustomobject] $Checkpoint,
381
+ [Parameter(Mandatory = $true)] [ValidateSet('epic', 'parallel')] [string] $Kind,
382
+ [Parameter(Mandatory = $true)] [ValidateSet('pr_number', 'worktree_path')] [string] $RecordField,
383
+ [Parameter(Mandatory = $true)] [string] $Value
384
+ )
385
+
386
+ $values = [System.Collections.Generic.List[object]]::new()
387
+ $arrayName = if ($Kind -eq 'epic') { 'features' } else { 'items' }
388
+ foreach ($record in @(Get-WorktreeRunPropertyValue -InputObject $Checkpoint -Name $arrayName)) {
389
+ $values.Add((Get-WorktreeRunPropertyValue -InputObject $record -Name $RecordField))
390
+ }
391
+ if ($Kind -eq 'epic' -and $RecordField -eq 'pr_number') {
392
+ $mergePr = Get-WorktreeRunPropertyValue -InputObject $Checkpoint -Name 'epic_merge_pr'
393
+ $values.Add((Get-WorktreeRunPropertyValue -InputObject $mergePr -Name 'pr_number'))
394
+ }
395
+
396
+ foreach ($recorded in $values) {
397
+ if ($RecordField -eq 'pr_number') {
398
+ if (Test-WorktreeRunPrNumberEqual -Recorded $recorded -Expected ([long] $Value)) { return $true }
399
+ }
400
+ elseif (Test-WorktreeRunPathEqual -Left ([string] $recorded) -Right $Value) { return $true }
401
+ }
402
+ return $false
403
+ }
404
+
405
+ function Resolve-WorktreeRunTargetByRecord {
406
+ <#
407
+ .SYNOPSIS
408
+ Resolve the live worktree whose run checkpoint records a pull request number or worktree path.
409
+ .DESCRIPTION
410
+ Epic checkpoints are searched in epic_merge_pr.pr_number and features[].pr_number, or
411
+ features[].worktree_path; parallel checkpoints in items[].pr_number or
412
+ items[].worktree_path. Zero matches resolve NoTarget, one resolves, and several are
413
+ Ambiguous. A blank value, or a pr_number that is not all digits or does not fit a 64-bit integer, is NoTarget.
414
+ .PARAMETER Kind
415
+ epic or parallel.
416
+ .PARAMETER RecordField
417
+ pr_number or worktree_path.
418
+ .PARAMETER Value
419
+ The pull request number or worktree path the command names.
420
+ .PARAMETER SessionRoot
421
+ The calling process's path, used only to find the repository and label a result.
422
+ #>
423
+ [CmdletBinding()]
424
+ [OutputType([pscustomobject])]
425
+ param(
426
+ [Parameter(Mandatory = $true)] [ValidateSet('epic', 'parallel')] [string] $Kind,
427
+ [Parameter(Mandatory = $true)] [ValidateSet('pr_number', 'worktree_path')] [string] $RecordField,
428
+ [AllowNull()] [AllowEmptyString()] [string] $Value,
429
+ [Parameter(Mandatory = $true)] [string] $SessionRoot
430
+ )
431
+
432
+ $description = "{0} '{1}' in the {2} checkpoint" -f $RecordField, $Value, $Kind
433
+ $isBlank = [string]::IsNullOrWhiteSpace($Value)
434
+ $parsedNumber = [long] 0
435
+ $isNumber = -not $isBlank -and $Value.Trim() -match '^\d+$' -and [long]::TryParse($Value.Trim(), [ref] $parsedNumber)
436
+ if ($isBlank -or ($RecordField -eq 'pr_number' -and -not $isNumber)) {
437
+ return (New-WorktreeResolutionTargetResult -Status 'NoTarget' -SessionRoot $SessionRoot -Detail (
438
+ 'the command names no usable {0}, so the {1} run it belongs to cannot be identified' -f $RecordField, $Kind))
439
+ }
440
+ $needle = $Value.Trim()
441
+ $matched = [System.Collections.Generic.List[string]]::new()
442
+ foreach ($root in (Get-WorktreeRunLiveRoot -SessionRoot $SessionRoot)) {
443
+ $checkpoint = Get-WorktreeRunCheckpoint -Kind $Kind -WorktreeRoot $root
444
+ if ($null -eq $checkpoint) { continue }
445
+ if (Test-WorktreeRunCheckpointRecord -Checkpoint $checkpoint -Kind $Kind -RecordField $RecordField -Value $needle) { $matched.Add($root) }
446
+ }
447
+ return (Resolve-WorktreeRunMatch -Matched $matched.ToArray() -SessionRoot $SessionRoot -Description $description)
448
+ }
449
+
450
+ function Resolve-WorktreeOperandTarget {
451
+ <#
452
+ .SYNOPSIS
453
+ Place a file_path or git -C operand in the worktree that contains it.
454
+ .DESCRIPTION
455
+ Ascends from the operand to its worktree root. A blank operand, and an operand
456
+ inside no worktree, resolve to the session worktree; a relative operand is joined
457
+ to the session root first.
458
+ .PARAMETER Path
459
+ The operand path, absolute or relative, or blank.
460
+ .PARAMETER SessionRoot
461
+ The calling process's path.
462
+ #>
463
+ [CmdletBinding()]
464
+ [OutputType([pscustomobject])]
465
+ param(
466
+ [AllowNull()] [AllowEmptyString()] [string] $Path,
467
+ [Parameter(Mandatory = $true)] [string] $SessionRoot
468
+ )
469
+
470
+ $sessionWorktree = Find-WorktreeResolutionRoot -Path $SessionRoot
471
+ if ($null -eq $sessionWorktree) { $sessionWorktree = ConvertTo-WorktreeResolutionNormalizedPath -Path $SessionRoot }
472
+ if ([string]::IsNullOrWhiteSpace($Path)) {
473
+ return (ConvertTo-WorktreeItemResolvedResult -WorktreeRoot $sessionWorktree -SessionRoot $SessionRoot -Detail (
474
+ 'the call names no operand, so it acts on the session worktree'))
475
+ }
476
+
477
+ $operand = ConvertTo-WorktreeResolutionNormalizedPath -Path $Path
478
+ if ($operand -notmatch $script:AbsolutePathPattern) {
479
+ $operand = Join-WorktreeResolutionPath -WorktreeRoot $SessionRoot -RepoRelativePath $operand
480
+ }
481
+ $found = Find-WorktreeResolutionRoot -Path $operand
482
+ if ($null -eq $found) {
483
+ return (ConvertTo-WorktreeItemResolvedResult -WorktreeRoot $sessionWorktree -SessionRoot $SessionRoot -Detail (
484
+ "the operand '{0}' lies in no worktree, so the call is evaluated against the session worktree" -f $operand))
485
+ }
486
+ return (ConvertTo-WorktreeItemResolvedResult -WorktreeRoot $found -SessionRoot $SessionRoot -Detail (
487
+ "the operand '{0}' lies in the worktree '{1}'" -f $operand, $found))
488
+ }
489
+
490
+ Export-ModuleMember -Function `
491
+ Find-WorktreeRunIdentitySignal, `
492
+ Get-WorktreeRunCheckpointText, `
493
+ Get-WorktreeRunCheckpointPath, `
494
+ Resolve-WorktreeEpicTarget, `
495
+ Resolve-WorktreeParallelTarget, `
496
+ Resolve-WorktreeRunTargetByRecord, `
497
+ Resolve-WorktreeOperandTarget
@@ -611,6 +611,27 @@ in `tests/scripts/dev_tools/test_blast_radius_config_parity.py`, mirrored in
611
611
  union of both copies' top-level keys is exhaustively covered by the three declared classes, and
612
612
  an unclassified key or a key present in only one copy fails loudly and names itself.
613
613
 
614
+ **A destination records its own entries in `config/blast-radius.local.json` (issue #508).** The
615
+ push-down regenerates a destination's `config/blast-radius.json` on every push, so an entry
616
+ hand-added to that file does not survive the next push. The supported extension point is the
617
+ destination-owned overlay `config/blast-radius.local.json`. No payload contains it, and the
618
+ push-down never writes or publishes it: both implementations list it in `EXCLUDED_RELATIVE_PATHS`.
619
+ At push time the overlay is composed onto the freshly generated base document (the derived document
620
+ in TypeScript, the published document in Python), and the composed result is written to
621
+ `config/blast-radius.json`, so consumers continue to read one file. Per-key semantics: the
622
+ string-list keys `shared_surfaces`, `shared_surface_globs`, `mandate_reads`, `mergeable_paths`, and
623
+ `path_roots` take an ordered union (base entries first, then overlay-only entries, duplicates
624
+ removed); an overlay module absent from the base is added and an overlay module with the same name
625
+ replaces that module's glob list, with module names emitted in ordinal order; `conflict_tolerance`
626
+ merges recursively (nested lists by union, nested objects per member, nested scalars overlay-wins);
627
+ the scalars `over_breadth_fraction` and `write_intent_extraction` take the overlay value; overlay-only
628
+ keys are appended after the base keys. An overlay `version` must equal the base `version`, and the
629
+ forbidden-glob guard (`**`, `docs/**`, `tests/**`) applies to the composed module map, including
630
+ overlay-authored modules. A malformed overlay, a `version` mismatch, or a forbidden glob raises an
631
+ error before any write, so `config/blast-radius.json` keeps its prior bytes. With no overlay
632
+ present, the written file is byte-identical to the base document. The overlay cannot remove a
633
+ shipped surface or a derived module.
634
+
614
635
  ## Enforcement
615
636
 
616
637
  - `scripts/dev_tools/validate_parallel_orchestrator_state.py`, with the helper modules `scripts/dev_tools/_parallel_state_common.py`, `scripts/dev_tools/_parallel_state_structures.py`, and `scripts/dev_tools/_parallel_state_records.py`, appends one error per violated orchestrator invariant. The completion-gate invariants 20 and 21 run only when the caller passes `require_complete=True`.
@@ -123,6 +123,8 @@ checkpoint write, and on CI-green (S9 step 6) merges its own PR into the integra
123
123
  recording `epic_merge: { merge_commit_sha, target_branch, merged_at }`. Standalone (non-epic)
124
124
  orchestration is unchanged: `epic_mode` absent or `false` makes S9 step 6 a no-op.
125
125
 
126
+ The run gates `enforce-orchestration-preimplementation-gate.ps1` and `enforce-epic-wave-barrier.ps1` locate the epic checkpoint by the `integration_branch:` value of this line. They select the live worktree whose `artifacts/orchestration/epic-orchestrator-state.json` records `route_id` `epic` and that integration branch, prefer the worktree that has the branch checked out when more than one records it, and deny the delegation with `TARGET_WORKTREE_NOT_DERIVABLE` or `TARGET_WORKTREE_AMBIGUOUS` when none or more than one remains. A stale copy of the epic checkpoint is moved to `artifacts/orchestration/handoff/` rather than left at a worktree root. The child run's own delegations to implementation agents carry the canonical issue-number line and `branch:` label defined in `.claude/skills/orchestrate/SKILL.md` `## Issue Number Consistency`.
127
+
126
128
  ## Bounded Child Return Contract
127
129
 
128
130
  A child `orchestrator`'s final report is consumed as a fixed eight-field shape and nothing else:
@@ -42,6 +42,10 @@ The worker must return the following reporting block:
42
42
  4. Diffs (patch-style or full-file replacements).
43
43
  5. QA Gate Results (CSharpier, .NET Analyzers, Nullable Analysis, xUnit, and coverage deltas, or clearly marked **unverified**).
44
44
 
45
+ ## Delegation Identity Lines
46
+
47
+ Every delegation prompt this skill sends to `csharp-typed-engineer` carries the canonical issue-number line (`Canonical issue number for this feature is <issue_num>. All artifact content, file paths, and cross-references must use this number.`) and a `branch: <name>` label naming the branch checked out in the item's worktree, written as the first `branch:` occurrence in the prompt. `enforce-orchestration-preimplementation-gate.ps1` resolves the item's worktree from these two lines and denies a delegation that carries neither with `TARGET_WORKTREE_NOT_DERIVABLE`.
48
+
45
49
  ## Worker Routing
46
50
 
47
51
  - Worker: `csharp-typed-engineer`
@@ -41,6 +41,10 @@ The worker must return the `powershell-qa-gate` reporting block:
41
41
  4. Diffs (patch-style or full-file replacements).
42
42
  5. QA Gate Results (PSScriptAnalyzer, Pester, and coverage deltas, or clearly marked **unverified**).
43
43
 
44
+ ## Delegation Identity Lines
45
+
46
+ Every delegation prompt this skill sends to `powershell-typed-engineer` carries the canonical issue-number line (`Canonical issue number for this feature is <issue_num>. All artifact content, file paths, and cross-references must use this number.`) and a `branch: <name>` label naming the branch checked out in the item's worktree, written as the first `branch:` occurrence in the prompt. `enforce-orchestration-preimplementation-gate.ps1` resolves the item's worktree from these two lines and denies a delegation that carries neither with `TARGET_WORKTREE_NOT_DERIVABLE`.
47
+
44
48
  ## Worker Routing
45
49
 
46
50
  - Worker: `powershell-typed-engineer`
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: invoke-python-engineer
3
- description: Invoke the python-typed-engineer worker to design, implement, and verify Python changes within typed repository boundaries. Applies Black -> Ruff -> Pyright -> Pytest toolchain, the 3-production + 3-test per-batch budget, and zero-regression quality gates.
3
+ description: Invoke the python-typed-engineer worker to design, implement, and verify Python changes within typed repository boundaries. Applies Black -> Ruff -> Pyright -> Pytest toolchain, the 1-3 production-file direct-mode budget with routing to the orchestrated large path above it, and zero-regression quality gates.
4
4
  ---
5
5
 
6
6
  # Implement Python Skill
@@ -15,7 +15,7 @@ Use this skill when:
15
15
  - Estimated scope fits the small path (1-3 production files plus corresponding tests).
16
16
  - The toolchain (Black, Ruff, Pyright, Pytest) can be run in the current environment, or the user has explicitly authorized an unverified plan-only response.
17
17
 
18
- If the estimated scope exceeds the small-path budget, this skill defers to the orchestrated flow via `python-change-budget-router` instead of proceeding directly.
18
+ If the estimated scope is more than 3 production files, this skill defers to the orchestrated large path (`/orchestrate`) via `python-change-budget-router` instead of proceeding directly. The large path has no production-file cap, and test files are not counted toward the routing threshold.
19
19
 
20
20
  ## Inputs
21
21
 
@@ -23,7 +23,6 @@ If the estimated scope exceeds the small-path budget, this skill defers to the o
23
23
  - Files or entrypoints in scope.
24
24
  - Constraints, including public APIs that must be preserved.
25
25
  - Optional approved plan. If none is supplied, the worker delegates plan authoring to `atomic_planner` before any edits.
26
- - Optional budget override in the form `budget: prod=<N>, test=<M>` subject to repo policy compliance.
27
26
 
28
27
  ## Output Paths
29
28
 
@@ -42,6 +41,10 @@ The worker must return the `python-qa-gate` reporting block:
42
41
  4. Diffs (patch-style or full-file replacements).
43
42
  5. QA Gate Results (Ruff, Pyright, Pytest, and coverage deltas, or clearly marked **unverified**).
44
43
 
44
+ ## Delegation Identity Lines
45
+
46
+ Every delegation prompt this skill sends to `python-typed-engineer` carries the canonical issue-number line (`Canonical issue number for this feature is <issue_num>. All artifact content, file paths, and cross-references must use this number.`) and a `branch: <name>` label naming the branch checked out in the item's worktree, written as the first `branch:` occurrence in the prompt. `enforce-orchestration-preimplementation-gate.ps1` resolves the item's worktree from these two lines and denies a delegation that carries neither with `TARGET_WORKTREE_NOT_DERIVABLE`.
47
+
45
48
  ## Worker Routing
46
49
 
47
50
  - Worker: `python-typed-engineer`
@@ -260,7 +260,9 @@ Every delegation prompt to a receipt-gated subagent type — `atomic-planner`, `
260
260
 
261
261
  > `Canonical issue number for this feature is <issue_num>. All artifact content, file paths, and cross-references must use this number.`
262
262
 
263
- Each such prompt also carries a `branch: <name>` label naming the branch checked out in the item's worktree, written as the first `branch:` occurrence in the prompt. Before promotion assigns an issue number, the branch label alone is required. Two gates, `enforce-model-routing-receipt.ps1` and `enforce-prd-feature-before-planner.ps1`, identify the item from these two lines: they deny a gated delegation they cannot identify rather than checking it against whichever checkpoint occupies the calling session's root, and they never use a feature-folder path to choose the worktree, because a merged feature folder exists in every checkout branched from main.
263
+ Each such prompt also carries a `branch: <name>` label naming the branch checked out in the item's worktree, written as the first `branch:` occurrence in the prompt. Before promotion assigns an issue number, the branch label alone is required. Three gates, `enforce-model-routing-receipt.ps1`, `enforce-prd-feature-before-planner.ps1`, and `enforce-orchestration-preimplementation-gate.ps1`, identify the item from these two lines: they deny a gated delegation they cannot identify rather than checking it against whichever checkpoint occupies the calling session's root, and they never use a feature-folder path to choose the worktree, because a merged feature folder exists in every checkout branched from main.
264
+
265
+ The same two lines are required on every delegation to an implementation agent (`python-typed-engineer`, `powershell-typed-engineer`, `typescript-engineer`, `csharp-typed-engineer`, and `atomic-executor`) and on every `Agent(orchestrator)` delegation that carries no epic-mode, parallel-mode, or preparation-mode kickoff marker. `enforce-orchestration-preimplementation-gate.ps1` resolves such a delegation's worktree from these lines and denies it with `TARGET_WORKTREE_NOT_DERIVABLE` when neither line is present, or with `TARGET_WORKTREE_AMBIGUOUS` when the lines disagree or match more than one live worktree.
264
266
 
265
267
  If a subagent artifact references a different issue number, the orchestrator rejects it, requests correction, and records the discrepancy under `artifact_errors` in the checkpoint.
266
268