@danmoisan/drm-copilot-mcp 1.1.10 → 1.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/mcp-server.js +3517 -1104
- package/package.json +1 -1
- 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-orchestrator.md +15 -2
- package/resources/claude-customizations/.claude/agents/parallel-planner.md +3 -0
- package/resources/claude-customizations/.claude/agents/pr-author.md +15 -0
- 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 +82 -51
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +52 -3
- 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 +163 -15
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +21 -28
- 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 +124 -8
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +171 -15
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.epic-base-branch.ps1 +50 -10
- 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/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 +218 -6
- 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/codex-routing/CodexDeployment.psm1 +1 -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/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 +66 -2
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +323 -16
- package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +14 -1
- package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +4 -0
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +46 -1
- package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +3 -1
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +75 -8
- 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/.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 +7 -0
- package/resources/claude-customizations/pack-manifests/core.json +16 -0
- package/resources/codex-and-agents-customizations/.agents/skills/codex-model-routing/SKILL.md +13 -14
- 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 +242 -3
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +67 -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-helpers.ps1 +163 -15
- 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/.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 +23 -1
- package/resources/config/orchestration-handoff-registry.json +138 -0
- package/resources/config/orchestration-handoff.schema.json +472 -0
- 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 +64 -0
|
@@ -15,10 +15,13 @@
|
|
|
15
15
|
payload-reader migration (issue #501) added the module import, the envelope-anomaly
|
|
16
16
|
mapping, and the entry-point seam. The split is a pure move with no behaviour change.
|
|
17
17
|
|
|
18
|
-
Both helpers read the script-scoped configuration
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
Both helpers read the script-scoped configuration the parent hook declares and call the
|
|
19
|
+
parent hook's injectable read seams, so this file is only ever dot-sourced from that hook
|
|
20
|
+
and never invoked on its own. $script:PrContextArtifactPath is a process-directory-relative
|
|
21
|
+
artifact path and stays one. $script:OrchestratorStateCheckpointPath is different after
|
|
22
|
+
issue #673: the parent declares it null, and Get-PrAuthorBypassReason assigns it the
|
|
23
|
+
absolute checkpoint path of the worktree identity resolution selected, so every later
|
|
24
|
+
reader receives one resolved path rather than deriving its own.
|
|
22
25
|
|
|
23
26
|
.NOTES
|
|
24
27
|
Compatible with PowerShell 7+. No external module dependencies.
|
|
@@ -26,6 +29,97 @@
|
|
|
26
29
|
[CmdletBinding()]
|
|
27
30
|
param()
|
|
28
31
|
|
|
32
|
+
# Shared command-line parser (issue #545). Dot-sourced here as well as from the parent hook
|
|
33
|
+
# so this file keeps working when a test dot-sources it directly.
|
|
34
|
+
. (Join-Path $PSScriptRoot 'hook-command-scanner.ps1')
|
|
35
|
+
. (Join-Path $PSScriptRoot 'hook-command-invocation.ps1')
|
|
36
|
+
# Worktree target resolution (issue #687). The checkpoint this gate validates must belong to
|
|
37
|
+
# the item the call is about. Reading a session-root-relative path made that the item whose
|
|
38
|
+
# worktree happened to occupy the session root, so in a parallel or epic topology the gate
|
|
39
|
+
# could return allow on the strength of a sibling item's state -- a false green, which is
|
|
40
|
+
# worse than a false denial because it reports success for a lifecycle it never checked.
|
|
41
|
+
Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeResolution.psm1') -Force -ErrorAction Stop
|
|
42
|
+
Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeTargetResolution.psm1') -Force -ErrorAction Stop
|
|
43
|
+
# Portable-identity resolution (issue #673). A path signal cannot select a worktree: a merged
|
|
44
|
+
# feature folder exists in every checkout branched from main, so it places a call in many at
|
|
45
|
+
# once. The item is identified by its canonical issue number and its branch instead.
|
|
46
|
+
Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeItemResolution.psm1') -Force -ErrorAction Stop
|
|
47
|
+
# Epic scope (issue #663): the epic integration pull request is gated against the epic checkpoint.
|
|
48
|
+
Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/EpicScopeResolution.psm1') -Force -ErrorAction Stop
|
|
49
|
+
Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/EpicScopeReadiness.psm1') -Force -ErrorAction Stop
|
|
50
|
+
|
|
51
|
+
function Resolve-PrAuthorWorktreeTarget {
|
|
52
|
+
<#
|
|
53
|
+
.SYNOPSIS
|
|
54
|
+
Resolve the call's target worktree. Tests mock this function (resolution seam).
|
|
55
|
+
.DESCRIPTION
|
|
56
|
+
A seam rather than a direct call because the resolver reads real git state: the
|
|
57
|
+
worktree list, each registration's branch, and the filesystem. A test that drove it
|
|
58
|
+
directly would depend on the machine's live worktrees and would pass or fail
|
|
59
|
+
according to what other sessions happen to have checked out, which the repository's
|
|
60
|
+
determinism policy forbids. Mocking here lets a test state the resolution outcome it
|
|
61
|
+
is exercising and nothing else.
|
|
62
|
+
.OUTPUTS
|
|
63
|
+
The target result object from Resolve-WorktreeItemTarget.
|
|
64
|
+
#>
|
|
65
|
+
[CmdletBinding()]
|
|
66
|
+
param(
|
|
67
|
+
[Parameter(Mandatory)]
|
|
68
|
+
[string] $CommandText
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
return (Resolve-WorktreeItemTarget -Text $CommandText -SessionRoot (Get-Location).Path)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function Get-PrAuthorTargetCheckpointResolution {
|
|
75
|
+
<#
|
|
76
|
+
.SYNOPSIS
|
|
77
|
+
Resolve the checkpoint path this call must be validated against.
|
|
78
|
+
.DESCRIPTION
|
|
79
|
+
Resolves the target worktree from the command text by portable identity and maps the
|
|
80
|
+
four resolution states onto a checkpoint path or a deny reason:
|
|
81
|
+
|
|
82
|
+
SessionRoot -> that worktree's own checkpoint, composed absolutely.
|
|
83
|
+
OtherWorktree -> that worktree's own checkpoint, composed the same way.
|
|
84
|
+
NoTarget -> deny, carrying the code Get-WorktreeResolutionNoTargetReasonCode
|
|
85
|
+
returns. pr-author passes --head on every gh pr create, so a call
|
|
86
|
+
reaching here omitted the explicit target; the remedy is to supply
|
|
87
|
+
it, never to fall back to whichever checkpoint occupies the
|
|
88
|
+
session root.
|
|
89
|
+
Ambiguous -> deny, carrying the code
|
|
90
|
+
Get-WorktreeResolutionAmbiguityReasonCode returns.
|
|
91
|
+
|
|
92
|
+
The two resolved states share one branch because the path is now composed the same
|
|
93
|
+
way for both: identity selects the worktree, and the checkpoint is read beneath it.
|
|
94
|
+
A session-root result is not a special case, it is the case where identity happened
|
|
95
|
+
to select the worktree the process is already running in.
|
|
96
|
+
|
|
97
|
+
Returning a deny for the two unresolved states is what keeps the gate honest: it
|
|
98
|
+
refuses to answer rather than answering from unrelated state. Neither reason-code
|
|
99
|
+
literal appears in this file; both come from the accessors named above.
|
|
100
|
+
.OUTPUTS
|
|
101
|
+
System.Collections.Specialized.OrderedDictionary with CheckpointPath and Reason.
|
|
102
|
+
#>
|
|
103
|
+
[CmdletBinding()]
|
|
104
|
+
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
105
|
+
param(
|
|
106
|
+
[Parameter(Mandatory)]
|
|
107
|
+
[string] $CommandText
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
$target = Resolve-PrAuthorWorktreeTarget -CommandText $CommandText
|
|
111
|
+
|
|
112
|
+
switch ($target.Status) {
|
|
113
|
+
{ $_ -in @('SessionRoot', 'OtherWorktree') } {
|
|
114
|
+
return [ordered]@{ CheckpointPath = (Get-WorktreeItemCheckpointPath -WorktreeRoot $target.WorktreeRoot); Reason = $null }
|
|
115
|
+
}
|
|
116
|
+
default {
|
|
117
|
+
$reason = "$($target.ReasonCode): $($target.Detail) The pr-author gate will not validate this call against the session root's checkpoint, because that checkpoint may belong to a different item. Pass --head <branch> on the gh pr create command so the call names its own target."
|
|
118
|
+
return [ordered]@{ CheckpointPath = $null; Reason = $reason }
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
29
123
|
function Test-PrAuthorReceiptVerification {
|
|
30
124
|
<#
|
|
31
125
|
.SYNOPSIS
|
|
@@ -50,6 +144,12 @@ function Test-PrAuthorReceiptVerification {
|
|
|
50
144
|
with Write access to artifacts/ can replace the body file and the receipt together.
|
|
51
145
|
.PARAMETER CommandText
|
|
52
146
|
The Bash command text containing the --body-file argument.
|
|
147
|
+
.PARAMETER CheckpointPath
|
|
148
|
+
The absolute checkpoint path identity resolution selected, passed through to check 6
|
|
149
|
+
so that check reads the resolved worktree's checkpoint rather than deriving its own.
|
|
150
|
+
.PARAMETER EpicScope
|
|
151
|
+
Optional. The epic-scope result Get-PrAuthorBypassReason resolved (issue #663), passed
|
|
152
|
+
through to check 6 so the call is resolved once; $null outside epic scope.
|
|
53
153
|
.OUTPUTS
|
|
54
154
|
System.String or $null
|
|
55
155
|
#>
|
|
@@ -57,7 +157,13 @@ function Test-PrAuthorReceiptVerification {
|
|
|
57
157
|
[OutputType([string])]
|
|
58
158
|
param(
|
|
59
159
|
[Parameter(Mandatory)]
|
|
60
|
-
[string] $CommandText
|
|
160
|
+
[string] $CommandText,
|
|
161
|
+
|
|
162
|
+
[Parameter(Mandatory)]
|
|
163
|
+
[string] $CheckpointPath,
|
|
164
|
+
|
|
165
|
+
[AllowNull()]
|
|
166
|
+
[object] $EpicScope
|
|
61
167
|
)
|
|
62
168
|
|
|
63
169
|
# Check 1: the --body-file argument must match the canonical artifacts/pr_body_<N>.md pattern.
|
|
@@ -128,7 +234,7 @@ function Test-PrAuthorReceiptVerification {
|
|
|
128
234
|
}
|
|
129
235
|
|
|
130
236
|
# Check 6: under epic_mode, gh pr create must carry a matching --base override.
|
|
131
|
-
$epicBaseBranchReason = Test-EpicBaseBranchOverride -CommandText $CommandText
|
|
237
|
+
$epicBaseBranchReason = Test-EpicBaseBranchOverride -CommandText $CommandText -CheckpointPath $CheckpointPath -EpicScope $EpicScope
|
|
132
238
|
if ($epicBaseBranchReason) {
|
|
133
239
|
return $epicBaseBranchReason
|
|
134
240
|
}
|
|
@@ -166,16 +272,43 @@ function Get-PrAuthorBypassReason {
|
|
|
166
272
|
[bool] $ContextExists
|
|
167
273
|
)
|
|
168
274
|
|
|
169
|
-
# Only act on gh pr create or gh pr edit subcommands.
|
|
170
|
-
|
|
171
|
-
|
|
275
|
+
# Only act on gh pr create or gh pr edit subcommands. The test is structural: a segment
|
|
276
|
+
# must INVOKE gh with the subcommand path, so quoted prose that merely mentions the
|
|
277
|
+
# phrase no longer triggers the gate, and a relocating spelling that carries a gh global
|
|
278
|
+
# option between the command word and the subcommand now does.
|
|
279
|
+
$isPrCreate = Test-CommandLineInvocation -CommandText $CommandText -CommandWord 'gh' -SubcommandPath @('pr', 'create')
|
|
280
|
+
$isPrEdit = Test-CommandLineInvocation -CommandText $CommandText -CommandWord 'gh' -SubcommandPath @('pr', 'edit')
|
|
172
281
|
|
|
173
282
|
if (-not $isPrCreate -and -not $isPrEdit) {
|
|
174
283
|
return $null
|
|
175
284
|
}
|
|
176
285
|
|
|
177
|
-
|
|
178
|
-
|
|
286
|
+
# Flag presence comes from the matched segment's tokens. Exact token comparison is what
|
|
287
|
+
# keeps '--body' from matching '--body-file'; the previous negative lookahead expressed
|
|
288
|
+
# the same distinction over raw text.
|
|
289
|
+
$subcommandPath = if ($isPrCreate) { @('pr', 'create') } else { @('pr', 'edit') }
|
|
290
|
+
$hasBodyFile = Test-CommandLineFlag -CommandText $CommandText -CommandWord 'gh' -SubcommandPath $subcommandPath -FlagName '--body-file'
|
|
291
|
+
$hasInlineBody = Test-CommandLineFlag -CommandText $CommandText -CommandWord 'gh' -SubcommandPath $subcommandPath -FlagName '--body'
|
|
292
|
+
|
|
293
|
+
# Raw-scan fallback for a wrapper-led, live-substitution, or unbalanced segment. A
|
|
294
|
+
# wrapper's quoted argument collapses into ONE token, so both flags read absent there and
|
|
295
|
+
# the gh pr edit no-body branch below allowed bash -c "gh pr edit 42 --body 'x'". The
|
|
296
|
+
# --body-file test runs first and wins, so a --body-file carried inside a wrapper is never
|
|
297
|
+
# misread as an inline body. That ordering restores the pre-parser routing, in which one
|
|
298
|
+
# whole-text --body-file match set $hasBodyFile for exactly this input.
|
|
299
|
+
if (-not $hasBodyFile -and -not $hasInlineBody) {
|
|
300
|
+
$comparison = [System.StringComparison]::OrdinalIgnoreCase
|
|
301
|
+
foreach ($segment in @(Read-CommandLineSegment -CommandText $CommandText)) {
|
|
302
|
+
if (-not (Test-CommandLineSegmentRawScan -Segment $segment)) {
|
|
303
|
+
continue
|
|
304
|
+
}
|
|
305
|
+
if ($segment.ScanText.IndexOf('--body-file', $comparison) -ge 0) {
|
|
306
|
+
$hasBodyFile = $true
|
|
307
|
+
} elseif ($segment.ScanText.IndexOf('--body', $comparison) -ge 0) {
|
|
308
|
+
$hasInlineBody = $true
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
179
312
|
|
|
180
313
|
# Case A: gh pr create OR gh pr edit with inline --body (not --body-file). Evaluated before the
|
|
181
314
|
# gh pr edit no-body allow short-circuit so inline-body edits are blocked, not allowed.
|
|
@@ -202,13 +335,36 @@ function Get-PrAuthorBypassReason {
|
|
|
202
335
|
return "PR_CONTEXT_MISSING: ``$script:PrContextArtifactPath`` is absent. Run ``mcp__drm-copilot__collect_pr_context`` before creating or editing the PR body."
|
|
203
336
|
}
|
|
204
337
|
|
|
338
|
+
# Epic scope (issue #663): when the call's --head equals the epic checkpoint's
|
|
339
|
+
# integration_branch, the integration pull request is gated by the epic PR-creation
|
|
340
|
+
# readiness predicate on artifacts/orchestration/epic-orchestrator-state.json, and no
|
|
341
|
+
# per-feature checkpoint is resolved or read. Resolved once; check 6 reuses the result.
|
|
342
|
+
$epicScope = $null
|
|
343
|
+
if ($hasBodyFile -and $ContextExists) {
|
|
344
|
+
$epicScope = Resolve-EpicScopeCheckpoint -Text $CommandText -SessionRoot (Get-Location).Path
|
|
345
|
+
}
|
|
346
|
+
|
|
205
347
|
# Orchestrator-state preflight: runs inside this same PreToolUse hook (so it cannot be
|
|
206
348
|
# bypassed by invoking gh pr create/edit directly) before receipt verification.
|
|
207
|
-
if ($
|
|
208
|
-
$
|
|
349
|
+
if ($null -ne $epicScope -and $epicScope.IsEpicScope) {
|
|
350
|
+
$failure = Get-EpicPrCreationReadinessFailure -Checkpoint $epicScope.Checkpoint -HeadBranch $epicScope.Branch
|
|
351
|
+
if ($failure) {
|
|
352
|
+
return "ORCHESTRATOR_STATE_PREFLIGHT_FAILED: this epic-scope pull request was evaluated against $($epicScope.CheckpointPath), and the failed readiness predicate is '$failure'."
|
|
353
|
+
}
|
|
354
|
+
$script:OrchestratorStateCheckpointPath = $epicScope.CheckpointPath
|
|
355
|
+
} elseif ($hasBodyFile -and $ContextExists) {
|
|
356
|
+
$resolution = Get-PrAuthorTargetCheckpointResolution -CommandText $CommandText
|
|
357
|
+
if ($resolution.Reason) {
|
|
358
|
+
return $resolution.Reason
|
|
359
|
+
}
|
|
360
|
+
# Assigned once here and read by every later consumer, so the preflight, the receipt
|
|
361
|
+
# verifier, and the epic base-branch check cannot disagree about which item is gated.
|
|
362
|
+
$script:OrchestratorStateCheckpointPath = $resolution.CheckpointPath
|
|
363
|
+
$checkpointPath = $script:OrchestratorStateCheckpointPath
|
|
364
|
+
$preflightResult = Invoke-OrchestratorStatePreflight -CheckpointPath $checkpointPath
|
|
209
365
|
if ($preflightResult.HasErrors) {
|
|
210
366
|
$preflightSummary = if ([string]::IsNullOrWhiteSpace($preflightResult.ErrorText)) {
|
|
211
|
-
"checkpoint missing at $
|
|
367
|
+
"checkpoint missing at $checkpointPath"
|
|
212
368
|
} else {
|
|
213
369
|
$preflightResult.ErrorText
|
|
214
370
|
}
|
|
@@ -218,7 +374,7 @@ function Get-PrAuthorBypassReason {
|
|
|
218
374
|
|
|
219
375
|
# Receipt verification: extends, and does not replace, the previously-allowed path.
|
|
220
376
|
if ($hasBodyFile -and $ContextExists) {
|
|
221
|
-
$receiptReason = Test-PrAuthorReceiptVerification -CommandText $CommandText
|
|
377
|
+
$receiptReason = Test-PrAuthorReceiptVerification -CommandText $CommandText -CheckpointPath $script:OrchestratorStateCheckpointPath -EpicScope $epicScope
|
|
222
378
|
if ($receiptReason) {
|
|
223
379
|
return $receiptReason
|
|
224
380
|
}
|
package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.epic-base-branch.ps1
CHANGED
|
@@ -9,25 +9,35 @@
|
|
|
9
9
|
structural extraction only: no logic, decision behavior, or reason-string wording changed.
|
|
10
10
|
#>
|
|
11
11
|
|
|
12
|
+
# Shared command-line parser (issue #545). Dot-sourced here as well as from the parent hook
|
|
13
|
+
# so this file keeps working when a test dot-sources it directly.
|
|
14
|
+
. (Join-Path $PSScriptRoot 'hook-command-scanner.ps1')
|
|
15
|
+
. (Join-Path $PSScriptRoot 'hook-command-invocation.ps1')
|
|
16
|
+
|
|
12
17
|
function Get-PrAuthorCheckpointContent {
|
|
13
18
|
<#
|
|
14
19
|
.SYNOPSIS
|
|
15
20
|
Read the raw JSON text of the per-feature orchestrator checkpoint. Tests mock this
|
|
16
21
|
function (read seam) for the epic-mode base-branch override check.
|
|
17
22
|
.DESCRIPTION
|
|
18
|
-
Returns the raw text content of
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
Returns the raw text content of the checkpoint at the supplied path, or $null when the
|
|
24
|
+
file is absent. This is the injectable boundary for checkpoint content used by
|
|
25
|
+
Test-EpicBaseBranchOverride; no test writes the checkpoint file to disk.
|
|
26
|
+
|
|
27
|
+
The path is mandatory and carries no default (issue #673). A relative default was the
|
|
28
|
+
second of this feature's three bindings: it made check 6 read whichever checkpoint
|
|
29
|
+
occupied the process directory even when target resolution had already selected a
|
|
30
|
+
different worktree, so the epic verdict could come from an unrelated item.
|
|
21
31
|
.PARAMETER CheckpointPath
|
|
22
|
-
The
|
|
32
|
+
The absolute path of the checkpoint belonging to the resolved worktree.
|
|
23
33
|
.OUTPUTS
|
|
24
34
|
System.String or $null
|
|
25
35
|
#>
|
|
26
36
|
[CmdletBinding()]
|
|
27
37
|
[OutputType([string])]
|
|
28
38
|
param(
|
|
29
|
-
[Parameter(Mandatory
|
|
30
|
-
[string] $CheckpointPath
|
|
39
|
+
[Parameter(Mandatory)]
|
|
40
|
+
[string] $CheckpointPath
|
|
31
41
|
)
|
|
32
42
|
|
|
33
43
|
if (-not (Test-Path -LiteralPath $CheckpointPath)) {
|
|
@@ -52,6 +62,13 @@ function Test-EpicBaseBranchOverride {
|
|
|
52
62
|
unchanged.
|
|
53
63
|
.PARAMETER CommandText
|
|
54
64
|
The Bash command text under evaluation.
|
|
65
|
+
.PARAMETER CheckpointPath
|
|
66
|
+
The absolute checkpoint path identity resolution selected, passed straight through to
|
|
67
|
+
the read seam so this check and the orchestrator-state preflight read one file.
|
|
68
|
+
.PARAMETER EpicScope
|
|
69
|
+
Optional. The epic-scope result gate 1 resolved (issue #663). When it reports epic
|
|
70
|
+
scope, the integration pull request must pass --base main and the per-feature
|
|
71
|
+
checkpoint seam is not read; $null keeps the per-feature epic_mode rule unchanged.
|
|
55
72
|
.OUTPUTS
|
|
56
73
|
System.String or $null
|
|
57
74
|
#>
|
|
@@ -59,16 +76,35 @@ function Test-EpicBaseBranchOverride {
|
|
|
59
76
|
[OutputType([string])]
|
|
60
77
|
param(
|
|
61
78
|
[Parameter(Mandatory)]
|
|
62
|
-
[string] $CommandText
|
|
79
|
+
[string] $CommandText,
|
|
80
|
+
|
|
81
|
+
[Parameter(Mandatory)]
|
|
82
|
+
[string] $CheckpointPath,
|
|
83
|
+
|
|
84
|
+
[AllowNull()]
|
|
85
|
+
[object] $EpicScope
|
|
63
86
|
)
|
|
64
87
|
|
|
65
88
|
# The epic-mode base-branch override only constrains gh pr create; gh pr edit does not
|
|
66
89
|
# re-target the base branch, so non-create commands are out of scope for this check.
|
|
67
|
-
|
|
90
|
+
# The test is structural, so a quoted mention of the phrase is out of scope and a
|
|
91
|
+
# relocating spelling carrying a gh global option is in scope (issue #545).
|
|
92
|
+
if (-not (Test-CommandLineInvocation -CommandText $CommandText -CommandWord 'gh' -SubcommandPath @('pr', 'create'))) {
|
|
93
|
+
return $null
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
# Epic scope (issue #663): the epic's own integration pull request targets main. The
|
|
97
|
+
# scope object comes from gate 1, so the epic checkpoint is not read a second time and
|
|
98
|
+
# the per-feature checkpoint is not consulted.
|
|
99
|
+
if ($null -ne $EpicScope -and $EpicScope.IsEpicScope) {
|
|
100
|
+
$epicBaseValue = Get-CommandLineFlagValue -CommandText $CommandText -CommandWord 'gh' -SubcommandPath @('pr', 'create') -FlagName '--base'
|
|
101
|
+
if ($null -eq $epicBaseValue -or $epicBaseValue -cne 'main') {
|
|
102
|
+
return "EPIC_BASE_BRANCH_MISMATCH: this epic-scope gh pr create was evaluated against $($EpicScope.CheckpointPath); the integration pull request must pass --base main."
|
|
103
|
+
}
|
|
68
104
|
return $null
|
|
69
105
|
}
|
|
70
106
|
|
|
71
|
-
$checkpointRaw = Get-PrAuthorCheckpointContent
|
|
107
|
+
$checkpointRaw = Get-PrAuthorCheckpointContent -CheckpointPath $CheckpointPath
|
|
72
108
|
if ([string]::IsNullOrWhiteSpace($checkpointRaw)) {
|
|
73
109
|
return $null
|
|
74
110
|
}
|
|
@@ -96,7 +132,11 @@ function Test-EpicBaseBranchOverride {
|
|
|
96
132
|
return "EPIC_BASE_BRANCH_MISMATCH: checkpoint has epic_mode == true but no ``epic_context.integration_branch`` is recorded; ``gh pr create`` cannot be verified against the required ``--base`` value."
|
|
97
133
|
}
|
|
98
134
|
|
|
99
|
-
|
|
135
|
+
# The --base value is taken from the matched segment's tokens, so a chained segment that
|
|
136
|
+
# happens to carry a --base token contributes nothing here. The comparison stays
|
|
137
|
+
# case-sensitive, as the previous -cnotmatch form was, because a branch name is.
|
|
138
|
+
$baseValue = Get-CommandLineFlagValue -CommandText $CommandText -CommandWord 'gh' -SubcommandPath @('pr', 'create') -FlagName '--base'
|
|
139
|
+
if ($null -eq $baseValue -or $baseValue -cne $integrationBranch) {
|
|
100
140
|
return "EPIC_BASE_BRANCH_MISMATCH: ``gh pr create`` must pass ``--base $integrationBranch`` (``epic_context.integration_branch``) under ``epic_mode``; the command does not carry a matching ``--base`` argument."
|
|
101
141
|
}
|
|
102
142
|
|
|
@@ -46,7 +46,9 @@ param()
|
|
|
46
46
|
|
|
47
47
|
Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force
|
|
48
48
|
$script:PrContextArtifactPath = 'artifacts/pr_context.summary.txt'
|
|
49
|
-
|
|
49
|
+
# Null until the resolution step assigns the absolute path of the resolved worktree's
|
|
50
|
+
# checkpoint (issue #673); a relative default here was the process-directory binding.
|
|
51
|
+
$script:OrchestratorStateCheckpointPath = $null
|
|
50
52
|
|
|
51
53
|
Import-Module (Join-Path $PSScriptRoot '../lib/orchestrator-state/OrchestratorState.psm1') -Force
|
|
52
54
|
|