@danmoisan/drm-copilot-mcp 1.1.9 → 1.1.11
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 +3526 -1037
- package/package.json +1 -1
- package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +15 -2
- package/resources/claude-customizations/.claude/agents/parallel-planner.md +3 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +49 -14
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +52 -3
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +103 -6
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +58 -3
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +37 -5
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.epic-base-branch.ps1 +13 -2
- package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +35 -7
- package/resources/claude-customizations/.claude/hooks/hook-command-invocation.ps1 +483 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-scanner.ps1 +483 -0
- package/resources/claude-customizations/.claude/hooks/validate-bash.ps1 +254 -5
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadius.psm1 +18 -75
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusConflict.psm1 +290 -0
- package/resources/claude-customizations/.claude/lib/cleanup-manifest/CleanupWorktreeManifest.psm1 +415 -0
- package/resources/claude-customizations/.claude/lib/project-file-merge/ProjectFileMerge.psm1 +355 -0
- package/resources/claude-customizations/.claude/lib/project-file-merge/ProjectFileMergeGrammar.psm1 +318 -0
- package/resources/claude-customizations/.claude/lib/project-file-merge/Resolve-MergeableConflict.ps1 +229 -0
- package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +66 -2
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +311 -16
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +42 -0
- package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +3 -1
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +36 -2
- package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +6 -0
- package/resources/claude-customizations/.claude/skills/powershell-orchestration-state-machine/SKILL.md +29 -1
- package/resources/claude-customizations/config/blast-radius.json +7 -0
- package/resources/claude-customizations/pack-manifests/core.json +7 -0
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +41 -0
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-state/SKILL.md +41 -0
- package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/SKILL.md +26 -0
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +49 -3
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +57 -12
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-worktree-removal-gate.ps1 +34 -8
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +6 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +34 -7
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation.ps1 +483 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-scanner.ps1 +483 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-bash.ps1 +130 -2
- package/resources/codex-and-agents-customizations/pack-manifests/core.json +6 -1
- package/resources/config/orchestration-handoff-registry.json +138 -0
- package/resources/config/orchestration-handoff.schema.json +472 -0
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +38 -0
package/resources/claude-customizations/.claude/lib/project-file-merge/Resolve-MergeableConflict.ps1
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Resolve mechanically-mergeable project-file conflicts in a worktree.
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Entry point for the parent-side project-file merge step of issue #643. It
|
|
7
|
+
lists the conflicted paths of a worktree left by an in-progress merge,
|
|
8
|
+
refuses the whole set unless every path belongs to the mergeable class, and
|
|
9
|
+
otherwise rewrites each conflicted file as the keyed union of the two sides.
|
|
10
|
+
|
|
11
|
+
The script stages nothing and creates no revision: the caller owns those
|
|
12
|
+
steps, so a resolution this script produces is always inspectable before it
|
|
13
|
+
becomes history. It writes one compressed JSON object to stdout and exits 0
|
|
14
|
+
for both verdicts; the verdict is that object's result field. A non-zero exit
|
|
15
|
+
means an unexpected error, whose message is on stderr.
|
|
16
|
+
|
|
17
|
+
.PARAMETER Worktree
|
|
18
|
+
Absolute or relative path to the worktree holding the conflicted merge.
|
|
19
|
+
|
|
20
|
+
.PARAMETER ConfigPath
|
|
21
|
+
Truth table to read mergeable_paths from. Defaults to the worktree's own
|
|
22
|
+
config/blast-radius.json.
|
|
23
|
+
#>
|
|
24
|
+
param(
|
|
25
|
+
[string] $Worktree,
|
|
26
|
+
[string] $ConfigPath
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
Set-StrictMode -Version Latest
|
|
30
|
+
$ErrorActionPreference = 'Stop'
|
|
31
|
+
|
|
32
|
+
# The merge module is imported before the grammar module it nests. Reversing the
|
|
33
|
+
# two would leave the grammar unavailable here: the nested -Force import inside
|
|
34
|
+
# ProjectFileMerge.psm1 removes an already-loaded grammar module and re-imports it
|
|
35
|
+
# into that module's own scope, taking its commands out of this scope.
|
|
36
|
+
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'ProjectFileMerge.psm1') -Force -ErrorAction Stop
|
|
37
|
+
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'ProjectFileMergeGrammar.psm1') -Force -ErrorAction Stop
|
|
38
|
+
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath '../blast-radius/BlastRadiusConflict.psm1') -Force -ErrorAction Stop
|
|
39
|
+
|
|
40
|
+
# The three bytes a UTF-8 byte-order mark occupies, kept as a constant so the
|
|
41
|
+
# reader and the writer agree on one prefix.
|
|
42
|
+
$script:ByteOrderMark = [byte[]] @(0xEF, 0xBB, 0xBF)
|
|
43
|
+
|
|
44
|
+
# Splits a text into lines that each retain their own terminator, which is what
|
|
45
|
+
# lets a CRLF file survive a merge unchanged.
|
|
46
|
+
$script:LinePattern = '[^\r\n]*(?:\r\n|\n|\r)'
|
|
47
|
+
|
|
48
|
+
function Invoke-GitExe {
|
|
49
|
+
<#
|
|
50
|
+
.SYNOPSIS
|
|
51
|
+
The single executable seam of this script.
|
|
52
|
+
.DESCRIPTION
|
|
53
|
+
Every git invocation goes through here, so a test mocks one function
|
|
54
|
+
rather than an executable. A non-zero exit throws with the output.
|
|
55
|
+
#>
|
|
56
|
+
[CmdletBinding()]
|
|
57
|
+
# The unary comma wraps the result so the pipeline does not unroll a
|
|
58
|
+
# one-element collection, which makes the emitted object an Object[]
|
|
59
|
+
# carrying strings rather than a bare string[]. Both are declared.
|
|
60
|
+
[OutputType([string[]], [System.Object[]])]
|
|
61
|
+
param([Parameter(Mandatory = $true)][AllowEmptyCollection()][string[]] $GitArgs)
|
|
62
|
+
|
|
63
|
+
$output = & git @GitArgs 2>&1
|
|
64
|
+
# A failed git call is never a resolvable state, so it stops the run.
|
|
65
|
+
if ($LASTEXITCODE -ne 0) {
|
|
66
|
+
throw ('git {0} exited {1}: {2}' -f ($GitArgs -join ' '), $LASTEXITCODE, ($output -join ' '))
|
|
67
|
+
}
|
|
68
|
+
return , @($output | ForEach-Object { [string] $_ })
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function Read-ConflictedFile {
|
|
72
|
+
<#
|
|
73
|
+
.SYNOPSIS
|
|
74
|
+
Read a conflicted file as bytes, a mark flag, and terminator-bearing lines.
|
|
75
|
+
.DESCRIPTION
|
|
76
|
+
Decoding is strict UTF-8. A file that does not decode returns $null, which
|
|
77
|
+
the caller turns into an escalation rather than a lossy rewrite.
|
|
78
|
+
#>
|
|
79
|
+
[CmdletBinding()]
|
|
80
|
+
[OutputType([pscustomobject])]
|
|
81
|
+
param([Parameter(Mandatory = $true)][string] $Path)
|
|
82
|
+
|
|
83
|
+
$bytes = [System.IO.File]::ReadAllBytes($Path)
|
|
84
|
+
$hasBom = $bytes.Length -ge 3 -and $bytes[0] -eq $script:ByteOrderMark[0] -and $bytes[1] -eq $script:ByteOrderMark[1] -and $bytes[2] -eq $script:ByteOrderMark[2]
|
|
85
|
+
try {
|
|
86
|
+
$text = [System.Text.UTF8Encoding]::new($false, $true).GetString($bytes)
|
|
87
|
+
} catch [System.Text.DecoderFallbackException] {
|
|
88
|
+
return $null
|
|
89
|
+
}
|
|
90
|
+
# The decoded mark is metadata, not content; the writer restores it.
|
|
91
|
+
if ($hasBom) { $text = $text.Substring(1) }
|
|
92
|
+
|
|
93
|
+
$line = [System.Collections.Generic.List[string]]::new()
|
|
94
|
+
foreach ($match in [regex]::Matches($text, $script:LinePattern)) { $line.Add($match.Value) }
|
|
95
|
+
# A final line with no terminator is not matched above and is added here.
|
|
96
|
+
$consumed = ($line -join '').Length
|
|
97
|
+
if ($consumed -lt $text.Length) { $line.Add($text.Substring($consumed)) }
|
|
98
|
+
|
|
99
|
+
return [pscustomobject]@{ Bytes = $bytes; HasBom = $hasBom; Line = $line.ToArray() }
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function Write-MergedFile {
|
|
103
|
+
<#
|
|
104
|
+
.SYNOPSIS
|
|
105
|
+
Write merged lines back over the conflicted file.
|
|
106
|
+
.DESCRIPTION
|
|
107
|
+
The byte array is assembled before the ShouldProcess guard, so -WhatIf
|
|
108
|
+
exercises the whole assembly and suppresses only the write itself.
|
|
109
|
+
#>
|
|
110
|
+
[CmdletBinding(SupportsShouldProcess = $true)]
|
|
111
|
+
[OutputType([void])]
|
|
112
|
+
param(
|
|
113
|
+
[Parameter(Mandatory = $true)][string] $Path,
|
|
114
|
+
[Parameter(Mandatory = $true)][AllowEmptyCollection()][AllowEmptyString()][string[]] $Line,
|
|
115
|
+
[Parameter(Mandatory = $true)][bool] $HasBom
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
$body = [System.Text.UTF8Encoding]::new($false).GetBytes($Line -join '')
|
|
119
|
+
$payload = [System.Collections.Generic.List[byte]]::new()
|
|
120
|
+
# A file that carried a mark keeps it, because dropping it is a byte change
|
|
121
|
+
# no reviewer asked for.
|
|
122
|
+
if ($HasBom) { $payload.AddRange($script:ByteOrderMark) }
|
|
123
|
+
$payload.AddRange($body)
|
|
124
|
+
if ($PSCmdlet.ShouldProcess($Path, 'Write the merged project file')) {
|
|
125
|
+
[System.IO.File]::WriteAllBytes($Path, $payload.ToArray())
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function ConvertTo-ResolutionResult {
|
|
130
|
+
<#
|
|
131
|
+
.SYNOPSIS
|
|
132
|
+
Build the single result record this script emits.
|
|
133
|
+
.DESCRIPTION
|
|
134
|
+
The three keys are the whole published contract, so they are written in
|
|
135
|
+
one place rather than at each return site.
|
|
136
|
+
#>
|
|
137
|
+
[CmdletBinding()]
|
|
138
|
+
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
139
|
+
param(
|
|
140
|
+
[Parameter(Mandatory = $true)][string] $ResultName,
|
|
141
|
+
[AllowEmptyCollection()][object[]] $Resolved = @(),
|
|
142
|
+
[AllowEmptyCollection()][AllowEmptyString()][string[]] $EscalatePath = @()
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
return [ordered]@{ result = $ResultName; resolved = $Resolved; escalate_paths = $EscalatePath }
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function Invoke-MergeableConflictResolution {
|
|
149
|
+
<#
|
|
150
|
+
.SYNOPSIS
|
|
151
|
+
Resolve every conflicted path of a worktree, or escalate the whole set.
|
|
152
|
+
.DESCRIPTION
|
|
153
|
+
The set is all-or-nothing. One non-mergeable path escalates before any
|
|
154
|
+
file is read, and one unresolvable file escalates before any file is
|
|
155
|
+
written, so a partially merged worktree is not a reachable state.
|
|
156
|
+
.PARAMETER Worktree
|
|
157
|
+
The worktree holding the conflicted merge.
|
|
158
|
+
.PARAMETER ConfigPath
|
|
159
|
+
Truth table carrying mergeable_paths; the worktree default is used when
|
|
160
|
+
this is blank.
|
|
161
|
+
.OUTPUTS
|
|
162
|
+
An ordered record carrying result, resolved, and escalate_paths.
|
|
163
|
+
#>
|
|
164
|
+
[CmdletBinding()]
|
|
165
|
+
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
166
|
+
param(
|
|
167
|
+
[Parameter(Mandatory = $true)][string] $Worktree,
|
|
168
|
+
[AllowNull()][AllowEmptyString()][string] $ConfigPath
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
if ([string]::IsNullOrWhiteSpace($ConfigPath)) { $ConfigPath = Join-Path -Path $Worktree -ChildPath 'config/blast-radius.json' }
|
|
172
|
+
$config = Get-Content -LiteralPath $ConfigPath -Raw | ConvertFrom-Json
|
|
173
|
+
$mergeable = @(Get-ConfigMergeablePath -Config $config)
|
|
174
|
+
|
|
175
|
+
# The listing is bound before it is filtered: Invoke-GitExe returns one array
|
|
176
|
+
# object, so piping it directly would hand the whole array to the filter.
|
|
177
|
+
$listed = Invoke-GitExe -GitArgs @('-C', $Worktree, 'diff', '--name-only', '--diff-filter=U')
|
|
178
|
+
$conflicted = @($listed | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
|
|
179
|
+
$outside = @($conflicted | Where-Object { -not (Test-MergeablePath -Entry $_ -MergeablePath $mergeable) })
|
|
180
|
+
# Classification happens first so a non-mergeable set costs no file read.
|
|
181
|
+
if ($outside.Count -gt 0) { return (ConvertTo-ResolutionResult -ResultName 'escalate' -EscalatePath $outside) }
|
|
182
|
+
|
|
183
|
+
$pending = [System.Collections.Generic.List[object]]::new()
|
|
184
|
+
foreach ($path in $conflicted) {
|
|
185
|
+
$kind = Get-ProjectFileKind -Path $path
|
|
186
|
+
if ($null -eq $kind) { return (ConvertTo-ResolutionResult -ResultName 'escalate' -EscalatePath @($path)) }
|
|
187
|
+
$file = Read-ConflictedFile -Path (Join-Path -Path $Worktree -ChildPath $path)
|
|
188
|
+
if ($null -eq $file) { return (ConvertTo-ResolutionResult -ResultName 'escalate' -EscalatePath @($path)) }
|
|
189
|
+
|
|
190
|
+
$outcome = Merge-ConflictedText -Line $file.Line -Kind $kind
|
|
191
|
+
if ($outcome.Escalate) { return (ConvertTo-ResolutionResult -ResultName 'escalate' -EscalatePath @($path)) }
|
|
192
|
+
|
|
193
|
+
# The three stages are the only authority on what the merge must keep.
|
|
194
|
+
# Each is bound directly, because Invoke-GitExe already returns one array
|
|
195
|
+
# and an array subexpression around it would nest that array inside another.
|
|
196
|
+
$ours = Invoke-GitExe -GitArgs @('-C', $Worktree, 'show', ":2:$path")
|
|
197
|
+
$theirs = Invoke-GitExe -GitArgs @('-C', $Worktree, 'show', ":3:$path")
|
|
198
|
+
$base = Invoke-GitExe -GitArgs @('-C', $Worktree, 'show', ":1:$path")
|
|
199
|
+
$isKept = Test-NeverDropPostCondition -MergedLine $outcome.Lines -OursLine $ours -TheirsLine $theirs -BaseLine $base -Kind $kind
|
|
200
|
+
if (-not $isKept) { return (ConvertTo-ResolutionResult -ResultName 'escalate' -EscalatePath @($path)) }
|
|
201
|
+
|
|
202
|
+
$pending.Add([pscustomobject]@{
|
|
203
|
+
Path = $path
|
|
204
|
+
HasBom = $file.HasBom
|
|
205
|
+
Line = $outcome.Lines
|
|
206
|
+
Record = [ordered]@{
|
|
207
|
+
path = $path
|
|
208
|
+
entries_added_from_ours = @($outcome.EntriesAddedFromOurs)
|
|
209
|
+
entries_added_from_theirs = @($outcome.EntriesAddedFromTheirs)
|
|
210
|
+
version_resolutions = @($outcome.VersionResolutions)
|
|
211
|
+
}
|
|
212
|
+
})
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
# Every path resolved, so the worktree can be rewritten in one pass.
|
|
216
|
+
foreach ($item in $pending) {
|
|
217
|
+
Write-MergedFile -Path (Join-Path -Path $Worktree -ChildPath $item.Path) -Line $item.Line -HasBom $item.HasBom
|
|
218
|
+
}
|
|
219
|
+
return (ConvertTo-ResolutionResult -ResultName 'resolved' -Resolved @($pending | ForEach-Object { $_.Record }))
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
# Dot-sourcing loads the functions for a test host; any other invocation runs the
|
|
223
|
+
# step. The guard is the precedent set by Invoke-ReleaseReconciliation.ps1.
|
|
224
|
+
if ($MyInvocation.InvocationName -ne '.') {
|
|
225
|
+
if ([string]::IsNullOrWhiteSpace($Worktree)) { throw 'Worktree is required.' }
|
|
226
|
+
$result = Invoke-MergeableConflictResolution -Worktree $Worktree -ConfigPath $ConfigPath
|
|
227
|
+
Write-Output (ConvertTo-Json -InputObject $result -Depth 6 -Compress)
|
|
228
|
+
exit 0
|
|
229
|
+
}
|
|
@@ -260,6 +260,61 @@ wildcard occupies or truncates the feature-folder segment, a contract token carr
|
|
|
260
260
|
letter, and a token containing a placeholder or interpolation marker. `artifacts/` is not a known
|
|
261
261
|
top-level segment, so a bare `artifacts/**` subtree claim no longer satisfies the shape rules.
|
|
262
262
|
|
|
263
|
+
### Mechanically-mergeable path class (issue #643)
|
|
264
|
+
|
|
265
|
+
Some contended paths are not a design collision. A `.csproj` item list, a `packages.config` package
|
|
266
|
+
list, and an `app.config` binding-redirect list are additive registries: two items that each append a
|
|
267
|
+
distinct entry produce a textual conflict that a deterministic union resolves without judgement.
|
|
268
|
+
Serializing those items buys nothing.
|
|
269
|
+
|
|
270
|
+
`config/blast-radius.json` carries an optional `mergeable_paths` list of exact entries and `**`
|
|
271
|
+
globs. That list is the mechanically-mergeable path class. Its published default is the five-entry
|
|
272
|
+
list `**/*.csproj`, `**/packages.config`, `**/app.config`, `**/*.vbproj`, `**/*.props`.
|
|
273
|
+
|
|
274
|
+
A matching path **stays in the declared `paths`**. The exclusion is applied at exactly one place per
|
|
275
|
+
runtime — inside `conflicts` (`scripts/dev_tools/_blast_radius_conflicts.py`) and inside
|
|
276
|
+
`Test-BlastRadiusConflict` (`.claude/lib/blast-radius/BlastRadius.psm1`), on both radii's `paths`
|
|
277
|
+
immediately before the smallest-path-overlap computation. Derivation, normalization,
|
|
278
|
+
observed-radius construction, V1, V2, V3, and `detect_escaped_paths` are therefore unchanged: the
|
|
279
|
+
class narrows one comparison and nothing else. The key is optional and fail-closed: a truth table
|
|
280
|
+
that omits it excludes nothing and reproduces pre-change behaviour exactly.
|
|
281
|
+
|
|
282
|
+
The matcher is local to this comparison and does not extend the glob engine. A declared path matches
|
|
283
|
+
the class when, against some configured entry, any of the following holds: the two strings are
|
|
284
|
+
ordinally equal; or the declared path is concrete (carries no wildcard) and the configured entry is
|
|
285
|
+
a glob that contains it; or the declared path is concrete and, for a configured entry beginning with
|
|
286
|
+
`**/`, the same two-step test succeeds against the entry with that prefix removed, which is what
|
|
287
|
+
makes a root-level `Directory.Build.props` match `**/*.props`. A declared path that is itself a glob
|
|
288
|
+
is mergeable only when it equals a configured entry verbatim, because a glob can expand over
|
|
289
|
+
non-mergeable files. `_glob_to_regex_text` (`scripts/dev_tools/_blast_radius_glob.py`) and its
|
|
290
|
+
PowerShell mirror `BlastRadiusGlob.psm1` are unchanged by this class; if either is ever changed, both
|
|
291
|
+
it and this matcher must be re-examined together, since the containment step depends on the glob
|
|
292
|
+
engine's semantics.
|
|
293
|
+
|
|
294
|
+
Three constraints bound the mergeable-path class:
|
|
295
|
+
|
|
296
|
+
1. **The planner remains obliged to enumerate a genuine write explicitly.** The class describes how a
|
|
297
|
+
contention is scored, not what an item may touch. An item that will write a `.csproj` still
|
|
298
|
+
declares that exact path in its radius.
|
|
299
|
+
2. **The path stays in the declared radius, so the audits still see it.** Nothing is removed from any
|
|
300
|
+
stored record. Over-breadth, shared-surface, and manifest audits read the same `paths` they read
|
|
301
|
+
before; only the pairwise overlap set is narrowed.
|
|
302
|
+
3. **`detect_escaped_paths` remains the backstop at execution time.** A mergeable classification is a
|
|
303
|
+
scheduling decision taken from declared text; `detect_escaped_paths` still compares the declared
|
|
304
|
+
radius against the paths a diff actually touched, so an item that wrote outside its radius is
|
|
305
|
+
caught against observed evidence regardless of this class.
|
|
306
|
+
|
|
307
|
+
An item record of the parallel-orchestrator checkpoint MAY carry an optional
|
|
308
|
+
`mergeable_conflicts_resolved` list recording the mergeable conflicts an item resolved mechanically.
|
|
309
|
+
The field is additive and tolerated-not-validated: no validator in Python, TypeScript, or bash reads
|
|
310
|
+
it, and it follows the `expected_conflict_components` diagnostic precedent — a record written for a
|
|
311
|
+
human reader and for post-hoc audit, never a scheduling input. Each entry carries `path`,
|
|
312
|
+
`resolved_at`, `merged_against`, `merge_commit_sha`, `entries_added_from_ours`,
|
|
313
|
+
`entries_added_from_theirs`, and `version_resolutions`. Entry keys inside those three lists use
|
|
314
|
+
`<ItemType>:<Include>` for MSBuild items, `package:<id>` for `packages.config` packages, and
|
|
315
|
+
`bindingRedirect:<name>` for `app.config` binding redirects. Invariant 9 (the six `blast_radius`
|
|
316
|
+
keys) and invariant 15 (the four edge reasons) are unchanged by this field.
|
|
317
|
+
|
|
263
318
|
### Placeholder-shape rejection (issue #502)
|
|
264
319
|
|
|
265
320
|
The fourth shape is a token containing any member of the marker set
|
|
@@ -353,6 +408,15 @@ reading the file is not told something false, and because
|
|
|
353
408
|
`tests/scripts/dev_tools/test_blast_radius_config.py` calls `load_module_globs` on it and that
|
|
354
409
|
helper raises on an absent key. Nothing schedules on it.
|
|
355
410
|
|
|
411
|
+
**The .NET manifest family is a structure signal, not a module source (issue #643).** `.csproj`,
|
|
412
|
+
`.fsproj`, `.vbproj`, `.sln`, and `.slnx` are classified in
|
|
413
|
+
`extensions/drm-copilot/src/lib/push-down/claude-blast-radius-derive-manifests.ts` as
|
|
414
|
+
structure-observing manifests: each one marks a project root and suppresses the
|
|
415
|
+
top-level-directory fallback, and none of them contributes a module. A .NET solution layout
|
|
416
|
+
therefore derives exactly `{ "config": ["config/**"] }` from `PAYLOAD_MODULES` alone. The reason is
|
|
417
|
+
the granularity criterion: a per-assembly module would fire for the majority of work items in that
|
|
418
|
+
solution, so it discriminates nothing and belongs in neither the module map nor the surface list.
|
|
419
|
+
|
|
356
420
|
**`PAYLOAD_MODULES` carries `config` only.** `claude-runtime` was removed from it by the same
|
|
357
421
|
granularity criterion that removed it from this repository's own map. The criterion transfers
|
|
358
422
|
without modification: every agent in the runtime is instructed to read the policy rules and process
|
|
@@ -366,8 +430,8 @@ non-vacuous input.
|
|
|
366
430
|
subset, not a copy of the self-hosted sets.** They were authored narrow when the bundled copy was
|
|
367
431
|
created and were never a copy that fell behind, so the correct gate is portable-set equality against
|
|
368
432
|
a declared constant plus a subset relation against the self-hosted list — never byte-equality with
|
|
369
|
-
the self-hosted file. Only `version`, `over_breadth_fraction`, and `
|
|
370
|
-
across the two copies.
|
|
433
|
+
the self-hosted file. Only `version`, `over_breadth_fraction`, `mandate_reads`, and `mergeable_paths`
|
|
434
|
+
are byte-equal across the two copies.
|
|
371
435
|
|
|
372
436
|
The reason the two key groups take different relations is an asymmetry between surfaces and modules.
|
|
373
437
|
An over-matching MODULE glob costs concurrency on every pair of items it touches, because a module
|