@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
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
.DESCRIPTION
|
|
6
6
|
Invoked by the Claude Code PreToolUse hook on the "Bash" matcher before any Bash
|
|
7
7
|
command runs. Regex-matches gh pr merge with a --merge flag against
|
|
8
|
-
the envelope's tool_input.command and, when matched, allows the merge only when one of
|
|
8
|
+
the envelope's tool_input.command and, when matched, allows the merge only when one of four
|
|
9
9
|
checkpoint-only conditions holds:
|
|
10
10
|
|
|
11
11
|
1. Child-feature path: artifacts/orchestration/orchestrator-state.json exists,
|
|
@@ -19,12 +19,19 @@
|
|
|
19
19
|
entry whose merge_status == "ci_green". A parallel run always names an explicit PR
|
|
20
20
|
number (each item merges from its own isolated worktree), so a bare command with no
|
|
21
21
|
PR number cannot satisfy this branch.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
4. Standalone path (issue #670), evaluated last: the command names an explicit PR
|
|
23
|
+
number and one of the three checkpoints above carries a
|
|
24
|
+
standalone_merge_authorizations record whose pr_number equals it, whose fields are
|
|
25
|
+
well formed, and whose session_id equals the live envelope's session_id. The
|
|
26
|
+
dot-sourced helpers file implements this branch.
|
|
27
|
+
|
|
28
|
+
Otherwise the command is denied with reason EPIC_MERGE_GATE_BLOCKED; a denial from
|
|
29
|
+
condition 4 also carries a standalone reason code after that token. A missing or
|
|
30
|
+
unreadable checkpoint in any branch fails closed (denies). A standalone (non-epic,
|
|
31
|
+
non-parallel) run never satisfies conditions 1-3, so its only route to a merge is an
|
|
32
|
+
explicit, PR-specific, session-bound record under condition 4; a bare command with no
|
|
33
|
+
PR number never satisfies condition 4, and a blanket flag is rejected rather than read.
|
|
34
|
+
Trigger scope: --squash is out of scope for this gate and is allowed on both runtimes.
|
|
28
35
|
|
|
29
36
|
Design decision: this gate trusts the on-disk checkpoint rather than shelling out live
|
|
30
37
|
to gh pr view for a real-time head-SHA check, matching the same non-adversarial,
|
|
@@ -35,6 +42,12 @@
|
|
|
35
42
|
Compatible with PowerShell 7+. No external module dependencies. Filesystem reads go
|
|
36
43
|
through injectable wrapper functions so tests can mock the boundary without writing
|
|
37
44
|
temporary files.
|
|
45
|
+
|
|
46
|
+
Honest disclosure: the standalone-merge authorization record is a policy-level,
|
|
47
|
+
auditable declaration and is not a cryptographic or security control. authorized_by is
|
|
48
|
+
a declaration the hook does not verify. The record is not tamper-proof against a writer
|
|
49
|
+
inside the authorizing session. The session_id cross-check rejects stale or copied
|
|
50
|
+
records from other sessions; it does not stop same-session forgery.
|
|
38
51
|
#>
|
|
39
52
|
[CmdletBinding()]
|
|
40
53
|
param()
|
|
@@ -44,6 +57,8 @@ Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -
|
|
|
44
57
|
# Invoke-EpicMergeGateDecision and by Get-EpicMergeGateCommandPrNumber.
|
|
45
58
|
. (Join-Path $PSScriptRoot 'hook-command-scanner.ps1')
|
|
46
59
|
. (Join-Path $PSScriptRoot 'hook-command-invocation.ps1')
|
|
60
|
+
# Standalone-merge authorization predicates and the two decision-envelope factories (issue #670).
|
|
61
|
+
. (Join-Path $PSScriptRoot 'enforce-epic-merge-gate-authorization.ps1')
|
|
47
62
|
|
|
48
63
|
$script:ChildCheckpointPath = 'artifacts/orchestration/orchestrator-state.json'
|
|
49
64
|
$script:EpicCheckpointPath = 'artifacts/orchestration/epic-orchestrator-state.json'
|
|
@@ -324,36 +339,6 @@ function Test-ParallelCheckpointAllowsMerge {
|
|
|
324
339
|
return $false
|
|
325
340
|
}
|
|
326
341
|
|
|
327
|
-
function Get-EpicMergeGateAllowDecision {
|
|
328
|
-
[CmdletBinding()]
|
|
329
|
-
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
330
|
-
param()
|
|
331
|
-
|
|
332
|
-
return [ordered]@{
|
|
333
|
-
hookSpecificOutput = [ordered]@{
|
|
334
|
-
hookEventName = 'PreToolUse'
|
|
335
|
-
permissionDecision = 'allow'
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
function Get-EpicMergeGateBlockDecision {
|
|
341
|
-
[CmdletBinding()]
|
|
342
|
-
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
343
|
-
param(
|
|
344
|
-
[Parameter(Mandatory)]
|
|
345
|
-
[string] $Reason
|
|
346
|
-
)
|
|
347
|
-
|
|
348
|
-
return [ordered]@{
|
|
349
|
-
hookSpecificOutput = [ordered]@{
|
|
350
|
-
hookEventName = 'PreToolUse'
|
|
351
|
-
permissionDecision = 'deny'
|
|
352
|
-
permissionDecisionReason = $Reason
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
342
|
function Invoke-EpicMergeGateDecision {
|
|
358
343
|
<#
|
|
359
344
|
.SYNOPSIS
|
|
@@ -430,6 +415,17 @@ function Invoke-EpicMergeGateDecision {
|
|
|
430
415
|
return Get-EpicMergeGateAllowDecision
|
|
431
416
|
}
|
|
432
417
|
|
|
418
|
+
# Branch 4, evaluated last and only for an explicit PR number: a PR-specific standalone
|
|
419
|
+
# authorization record bound to the live session. Its deny keeps the gate token first.
|
|
420
|
+
if ($null -ne $commandPrNumber) {
|
|
421
|
+
$envelopeSessionId = [string](Get-ClaudeHookEnvelopeValue -Envelope $payload.Envelope -Name 'session_id')
|
|
422
|
+
$standalone = Test-StandaloneCheckpointAllowsMerge -Checkpoints @($childCheckpoint, $epicCheckpoint, $parallelCheckpoint) -CommandPrNumber $commandPrNumber -EnvelopeSessionId $envelopeSessionId
|
|
423
|
+
if ($standalone.Allowed) {
|
|
424
|
+
return Get-EpicMergeGateAllowDecision
|
|
425
|
+
}
|
|
426
|
+
return Get-EpicMergeGateBlockDecision -Reason ('EPIC_MERGE_GATE_BLOCKED: ' + $standalone.ReasonCode + ': ' + $standalone.Message)
|
|
427
|
+
}
|
|
428
|
+
|
|
433
429
|
return Get-EpicMergeGateBlockDecision -Reason 'EPIC_MERGE_GATE_BLOCKED: gh pr merge --merge requires either a per-feature checkpoint with epic_mode == true and step9_status == "passed", an epic checkpoint with epic_merge_pr.ci_gate.conclusion == "success" and a matching pr_number, or a parallel-orchestrator checkpoint with route_id == "parallel" whose target item (matched by pr_number) has merge_status == "ci_green". No checkpoint satisfied this gate.'
|
|
434
430
|
}
|
|
435
431
|
|
|
@@ -5,8 +5,15 @@
|
|
|
5
5
|
|
|
6
6
|
.DESCRIPTION
|
|
7
7
|
Invoked by the Claude Code PreToolUse hook on the Agent (Task) tool. Reads
|
|
8
|
-
tool input JSON from the
|
|
9
|
-
|
|
8
|
+
tool input JSON from the envelope's nested tool_input, resolves which item the
|
|
9
|
+
delegation is about, and reads that item's orchestrator checkpoint.
|
|
10
|
+
|
|
11
|
+
The item is identified by portable identity only (issue #673): the canonical
|
|
12
|
+
issue-number line in the delegation prompt, and a branch label. A feature-folder
|
|
13
|
+
path never selects the worktree, because a merged folder exists in every checkout
|
|
14
|
+
branched from main. A delegation the gate cannot identify is denied with a named
|
|
15
|
+
reason code rather than checked against whichever checkpoint occupies the calling
|
|
16
|
+
process's directory, which is how a sibling item's state used to produce an allow.
|
|
10
17
|
|
|
11
18
|
The hook enforces presence only: it cannot read the delegate's chosen
|
|
12
19
|
`model` (no `model` field is exposed in the tool input), so it verifies that
|
|
@@ -24,7 +31,14 @@
|
|
|
24
31
|
empty or absent tool input, and malformed tool-input JSON.
|
|
25
32
|
|
|
26
33
|
The checkpoint read goes through Get-ModelRoutingCheckpoint so tests can
|
|
27
|
-
inject a synthetic checkpoint without touching disk.
|
|
34
|
+
inject a synthetic checkpoint without touching disk. Its path is mandatory and
|
|
35
|
+
carries no default: the caller supplies the absolute path of the resolved
|
|
36
|
+
worktree's checkpoint, so the gate cannot fall back to a relative location.
|
|
37
|
+
|
|
38
|
+
The delegation-identity contract this gate depends on is stated in
|
|
39
|
+
.claude/skills/orchestrate/SKILL.md under `## Issue Number Consistency`: every
|
|
40
|
+
delegation prompt to a receipt-gated subagent type carries the canonical issue
|
|
41
|
+
number line and a `branch:` label naming the item's branch.
|
|
28
42
|
|
|
29
43
|
.NOTES
|
|
30
44
|
Compatible with PowerShell 7+. Read-only presence-gating deterrent.
|
|
@@ -34,6 +48,11 @@ param()
|
|
|
34
48
|
|
|
35
49
|
|
|
36
50
|
Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force
|
|
51
|
+
# Portable-identity resolution (issue #673). Unguarded and fail-closed on purpose: a gate
|
|
52
|
+
# that cannot load its resolver must not degrade into the cwd-relative read it replaces.
|
|
53
|
+
Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeItemResolution.psm1') -Force -ErrorAction Stop
|
|
54
|
+
# Epic scope (issue #663): a delegation for the epic integration branch is gated against the epic checkpoint.
|
|
55
|
+
Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/EpicScopeResolution.psm1') -Force -ErrorAction Stop
|
|
37
56
|
function Get-ModelRoutingCheckpoint {
|
|
38
57
|
<#
|
|
39
58
|
.SYNOPSIS
|
|
@@ -43,7 +62,8 @@ function Get-ModelRoutingCheckpoint {
|
|
|
43
62
|
[CmdletBinding()]
|
|
44
63
|
[OutputType([object])]
|
|
45
64
|
param(
|
|
46
|
-
[
|
|
65
|
+
[Parameter(Mandatory)]
|
|
66
|
+
[string] $CheckpointPath
|
|
47
67
|
)
|
|
48
68
|
|
|
49
69
|
if (-not (Test-Path -LiteralPath $CheckpointPath -PathType Leaf)) {
|
|
@@ -117,6 +137,66 @@ function Test-ModelRoutingReceiptPresent {
|
|
|
117
137
|
return $false
|
|
118
138
|
}
|
|
119
139
|
|
|
140
|
+
function Resolve-ModelRoutingWorktreeTarget {
|
|
141
|
+
<#
|
|
142
|
+
.SYNOPSIS
|
|
143
|
+
Resolve the delegation's target worktree. Tests mock this function (resolution seam).
|
|
144
|
+
.DESCRIPTION
|
|
145
|
+
A seam rather than a direct call because the resolver reads real git state: the
|
|
146
|
+
worktree list, each registration's branch, and the filesystem. A test that drove it
|
|
147
|
+
directly would depend on whichever worktrees exist on the machine running it.
|
|
148
|
+
.PARAMETER PromptText
|
|
149
|
+
The delegation prompt, which is the only field scanned for identity.
|
|
150
|
+
.OUTPUTS
|
|
151
|
+
The target result object from Resolve-WorktreeItemTarget.
|
|
152
|
+
#>
|
|
153
|
+
[CmdletBinding()]
|
|
154
|
+
[OutputType([pscustomobject])]
|
|
155
|
+
param(
|
|
156
|
+
[Parameter(Mandatory)]
|
|
157
|
+
[AllowEmptyString()]
|
|
158
|
+
[string] $PromptText
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
return (Resolve-WorktreeItemTarget -Text $PromptText -SessionRoot (Get-Location).Path)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function Get-ModelRoutingTargetCheckpointResolution {
|
|
165
|
+
<#
|
|
166
|
+
.SYNOPSIS
|
|
167
|
+
Resolve the checkpoint path this delegation must be checked against.
|
|
168
|
+
.DESCRIPTION
|
|
169
|
+
Maps the four resolution states onto a checkpoint path or a deny reason. The two
|
|
170
|
+
resolved states share one branch because the path is composed the same way for
|
|
171
|
+
both: identity selects the worktree, and the checkpoint is read beneath it. The two
|
|
172
|
+
unresolved states deny, carrying the code the worktree-resolution accessors supply;
|
|
173
|
+
neither literal appears in this file.
|
|
174
|
+
.PARAMETER PromptText
|
|
175
|
+
The delegation prompt to resolve identity from.
|
|
176
|
+
.OUTPUTS
|
|
177
|
+
System.Collections.Specialized.OrderedDictionary with CheckpointPath and Reason.
|
|
178
|
+
#>
|
|
179
|
+
[CmdletBinding()]
|
|
180
|
+
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
181
|
+
param(
|
|
182
|
+
[Parameter(Mandatory)]
|
|
183
|
+
[AllowEmptyString()]
|
|
184
|
+
[string] $PromptText
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
$target = Resolve-ModelRoutingWorktreeTarget -PromptText $PromptText
|
|
188
|
+
|
|
189
|
+
switch ($target.Status) {
|
|
190
|
+
{ $_ -in @('SessionRoot', 'OtherWorktree') } {
|
|
191
|
+
return [ordered]@{ CheckpointPath = (Get-WorktreeItemCheckpointPath -WorktreeRoot $target.WorktreeRoot); Reason = $null }
|
|
192
|
+
}
|
|
193
|
+
default {
|
|
194
|
+
$reason = "$($target.ReasonCode): $($target.Detail) The model-routing gate will not check this delegation against a checkpoint that may belong to a different item. Put the line 'Canonical issue number for this feature is <N>.' and a 'branch: <item branch>' label in the delegation prompt so the gate can identify the item."
|
|
195
|
+
return [ordered]@{ CheckpointPath = $null; Reason = $reason }
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
120
200
|
function Invoke-ModelRoutingReceiptDecision {
|
|
121
201
|
<#
|
|
122
202
|
.SYNOPSIS
|
|
@@ -154,7 +234,42 @@ function Invoke-ModelRoutingReceiptDecision {
|
|
|
154
234
|
return $allow
|
|
155
235
|
}
|
|
156
236
|
|
|
157
|
-
|
|
237
|
+
# Identity resolution sits after the scope filter and before the checkpoint read, so a
|
|
238
|
+
# delegation outside the gated set is still allowed however unresolvable its target is,
|
|
239
|
+
# and a gated delegation the gate cannot identify is refused rather than answered from
|
|
240
|
+
# unrelated state. An absent prompt is passed as an empty string, which resolves to no
|
|
241
|
+
# identity and therefore to the same deny.
|
|
242
|
+
$prompt = [string](Get-ClaudeHookToolInputString -ToolInput $envelope.Value -Name 'prompt')
|
|
243
|
+
|
|
244
|
+
# Decision: a prompt whose branch label equals the epic checkpoint's integration_branch is
|
|
245
|
+
# epic scope (issue #663), and the receipt is looked up in the epic checkpoint without
|
|
246
|
+
# running per-feature identity resolution. Any other prompt falls through unchanged.
|
|
247
|
+
$epicScope = Resolve-EpicScopeCheckpoint -Text $prompt -SessionRoot (Get-Location).Path
|
|
248
|
+
if ($epicScope.IsEpicScope) {
|
|
249
|
+
if (Test-ModelRoutingReceiptPresent -Checkpoint $epicScope.Checkpoint -Subagent $subagent) {
|
|
250
|
+
return $allow
|
|
251
|
+
}
|
|
252
|
+
return [ordered]@{
|
|
253
|
+
hookSpecificOutput = [ordered]@{
|
|
254
|
+
hookEventName = 'PreToolUse'
|
|
255
|
+
permissionDecision = 'deny'
|
|
256
|
+
permissionDecisionReason = "MODEL_ROUTING_RECEIPT_BLOCKED: cannot delegate to '$subagent' in epic scope before a model_routing_receipts entry for it is recorded in $($epicScope.CheckpointPath); the failed readiness predicate is 'model_routing_receipts'."
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
$resolution = Get-ModelRoutingTargetCheckpointResolution -PromptText $prompt
|
|
262
|
+
if ($resolution.Reason) {
|
|
263
|
+
return [ordered]@{
|
|
264
|
+
hookSpecificOutput = [ordered]@{
|
|
265
|
+
hookEventName = 'PreToolUse'
|
|
266
|
+
permissionDecision = 'deny'
|
|
267
|
+
permissionDecisionReason = $resolution.Reason
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
$checkpoint = Get-ModelRoutingCheckpoint -CheckpointPath $resolution.CheckpointPath
|
|
158
273
|
if (Test-ModelRoutingReceiptPresent -Checkpoint $checkpoint -Subagent $subagent) {
|
|
159
274
|
return $allow
|
|
160
275
|
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Epic-scope command and path legs of the preimplementation gate (issue #663).
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Dot-sourced by enforce-orchestration-preimplementation-gate.ps1. Holds two groups of
|
|
7
|
+
functions:
|
|
8
|
+
|
|
9
|
+
- The two per-mode read seams of issue #554 (Get-EpicCheckpointContent and
|
|
10
|
+
Get-ParallelCheckpointContent), relocated verbatim from the gate file by issue #663
|
|
11
|
+
so the gate stays inside the 500-line cap. Their names and behaviour are unchanged,
|
|
12
|
+
so tests that mock or shadow them by name are unaffected.
|
|
13
|
+
- The epic-scope decision for the command and path legs. A staging command or a
|
|
14
|
+
Write/Edit call is epic scope when the effective worktree's HEAD (the -C selector
|
|
15
|
+
worktree when present, otherwise the session root) equals the integration_branch of
|
|
16
|
+
artifacts/orchestration/epic-orchestrator-state.json. In epic scope the call is
|
|
17
|
+
decided by the epic command-leg readiness predicate, and under decision D2 an
|
|
18
|
+
implementation-classified operand is allowed only while a merge is in progress in
|
|
19
|
+
that worktree. Outside epic scope the decision function returns $null and the gate's
|
|
20
|
+
single-feature path runs unchanged.
|
|
21
|
+
|
|
22
|
+
.NOTES
|
|
23
|
+
PowerShell 7+. Depends on functions the gate defines or dot-sources before any call:
|
|
24
|
+
Split-OrchestrationCommandLine and ConvertTo-OrchestrationCommandToken (helpers file),
|
|
25
|
+
Get-OrchestrationDelegationCheckpointPath (modes file), and the gate's allow and block
|
|
26
|
+
decision constructors. Mirrored byte-identically under
|
|
27
|
+
extensions/drm-copilot/resources/claude-customizations/.
|
|
28
|
+
#>
|
|
29
|
+
|
|
30
|
+
Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/EpicScopeResolution.psm1') -Force -ErrorAction Stop
|
|
31
|
+
Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/EpicScopeReadiness.psm1') -Force -ErrorAction Stop
|
|
32
|
+
|
|
33
|
+
# The two per-mode read seams (issue #554). Each takes its path from the fixed mode
|
|
34
|
+
# table and never from a delegation's own text; an absent file returns an empty
|
|
35
|
+
# string, which the readiness predicate then treats as a deny.
|
|
36
|
+
function Get-EpicCheckpointContent {
|
|
37
|
+
[CmdletBinding()]
|
|
38
|
+
[OutputType([string])]
|
|
39
|
+
param()
|
|
40
|
+
|
|
41
|
+
$path = Get-OrchestrationDelegationCheckpointPath -Mode 'epic'
|
|
42
|
+
if (-not (Test-Path -LiteralPath $path)) {
|
|
43
|
+
return ''
|
|
44
|
+
}
|
|
45
|
+
return Get-Content -Raw -LiteralPath $path
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function Get-ParallelCheckpointContent {
|
|
49
|
+
[CmdletBinding()]
|
|
50
|
+
[OutputType([string])]
|
|
51
|
+
param()
|
|
52
|
+
|
|
53
|
+
$path = Get-OrchestrationDelegationCheckpointPath -Mode 'parallel'
|
|
54
|
+
if (-not (Test-Path -LiteralPath $path)) {
|
|
55
|
+
return ''
|
|
56
|
+
}
|
|
57
|
+
return Get-Content -Raw -LiteralPath $path
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function Get-OrchestrationEpicScopeSelector {
|
|
61
|
+
<#
|
|
62
|
+
.SYNOPSIS
|
|
63
|
+
Returns the value of a leading `git -C <value>` selector, or $null.
|
|
64
|
+
.DESCRIPTION
|
|
65
|
+
Pure. Reads only the first segment of the command line, split and tokenized with
|
|
66
|
+
the helpers-file parser so quoting is handled exactly as the staging exemption
|
|
67
|
+
handles it. An unbalanced command line has no decidable selector and returns $null.
|
|
68
|
+
.PARAMETER Command
|
|
69
|
+
The full command line.
|
|
70
|
+
.OUTPUTS
|
|
71
|
+
System.String
|
|
72
|
+
#>
|
|
73
|
+
[CmdletBinding()]
|
|
74
|
+
[OutputType([string])]
|
|
75
|
+
param([Parameter(Mandatory)][AllowEmptyString()][string] $Command)
|
|
76
|
+
|
|
77
|
+
$split = Split-OrchestrationCommandLine -CommandText $Command
|
|
78
|
+
if (-not $split.Balanced -or @($split.Segments).Count -eq 0) {
|
|
79
|
+
return $null
|
|
80
|
+
}
|
|
81
|
+
$token = @(ConvertTo-OrchestrationCommandToken -Segment ([string]@($split.Segments)[0]).Trim())
|
|
82
|
+
if ($token.Count -ge 3 -and $token[0] -ceq 'git' -and $token[1] -ceq '-C' -and $token[2]) {
|
|
83
|
+
return $token[2]
|
|
84
|
+
}
|
|
85
|
+
return $null
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function Get-OrchestrationEpicScopeDecision {
|
|
89
|
+
<#
|
|
90
|
+
.SYNOPSIS
|
|
91
|
+
Returns the epic-scope decision for an implementation-classified command or path, or $null.
|
|
92
|
+
.DESCRIPTION
|
|
93
|
+
Resolves epic scope with worktree-HEAD matching and the command's -C selector. When
|
|
94
|
+
the call is not epic scope, returns $null so the caller's single-feature path runs
|
|
95
|
+
unchanged. In epic scope, returns an allow decision when the epic command-leg
|
|
96
|
+
readiness predicate passes, and otherwise a deny naming the epic checkpoint and the
|
|
97
|
+
failed conjunct.
|
|
98
|
+
.PARAMETER Command
|
|
99
|
+
The Bash command line; empty for a path leg.
|
|
100
|
+
.PARAMETER FilePath
|
|
101
|
+
The Write/Edit file path; empty for a command leg.
|
|
102
|
+
.OUTPUTS
|
|
103
|
+
System.Collections.Specialized.OrderedDictionary, or $null outside epic scope.
|
|
104
|
+
#>
|
|
105
|
+
[CmdletBinding()]
|
|
106
|
+
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
107
|
+
param(
|
|
108
|
+
[AllowNull()][AllowEmptyString()][string] $Command = '',
|
|
109
|
+
[AllowNull()][AllowEmptyString()][string] $FilePath = ''
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# A call carrying neither a command nor a path has no leg to decide.
|
|
113
|
+
if (-not $Command -and -not $FilePath) {
|
|
114
|
+
return $null
|
|
115
|
+
}
|
|
116
|
+
$selector = if ($Command) { Get-OrchestrationEpicScopeSelector -Command $Command } else { $null }
|
|
117
|
+
$scope = Resolve-EpicScopeCheckpoint -Text ([string]$Command) -SessionRoot (Get-Location).Path -WorktreeSelector $selector -MatchWorktreeHead
|
|
118
|
+
if (-not $scope.IsEpicScope) {
|
|
119
|
+
return $null
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
$failure = Get-EpicCommandLegReadinessFailure -Checkpoint $scope.Checkpoint -MergeInProgress $scope.MergeInProgress
|
|
123
|
+
if (-not $failure) {
|
|
124
|
+
return Get-OrchestrationPreimplementationGateAllowDecision
|
|
125
|
+
}
|
|
126
|
+
return Get-OrchestrationPreimplementationGateBlockDecision -Reason ("PREIMPLEMENTATION_GATE_BLOCKED: this epic-scope operation was evaluated against $($scope.CheckpointPath), and the failed readiness predicate is '$failure'. Implementation operations in epic scope require that checkpoint to satisfy every readiness predicate, and a production path may be staged or edited only while a merge is in progress.")
|
|
127
|
+
}
|