@danmoisan/drm-copilot-mcp 1.1.23 → 1.1.25
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 +13347 -7926
- package/package.json +1 -1
- package/resources/claude-customizations/.claude/agents/discovery-capability-sentinel.md +34 -0
- package/resources/claude-customizations/.claude/agents/legacy-parity-analyst.md +101 -43
- package/resources/claude-customizations/.claude/agents/migration-coverage-reviewer.md +107 -43
- package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +14 -0
- package/resources/claude-customizations/.claude/agents/parallel-planner.md +21 -1
- package/resources/claude-customizations/.claude/agents/pr-author.md +17 -1
- package/resources/claude-customizations/.claude/agents/requirements-reconciler.md +117 -42
- package/resources/claude-customizations/.claude/agents/runtime-characterization-analyst.md +103 -43
- package/resources/claude-customizations/.claude/hooks/discovery-capability-probe.ps1 +102 -0
- package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +69 -194
- package/resources/claude-customizations/.claude/hooks/enforce-discovery-planning-gate.ps1 +87 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +6 -3
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +168 -48
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +1 -1
- package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +59 -20
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-command-allowlist.ps1 +44 -3
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +60 -4
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.epic-base-branch.ps1 +4 -2
- package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +59 -20
- package/resources/claude-customizations/.claude/hooks/record-discovery-agent-start.ps1 +132 -0
- package/resources/claude-customizations/.claude/hooks/validate-discovery-artifact-gate.ps1 +56 -212
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-rejection.ps1 +172 -0
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +9 -1
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadius.psm1 +9 -3
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusAggregate.psm1 +154 -0
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusCombination.psm1 +228 -0
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +1 -0
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusNormalization.psm1 +4 -2
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusScheduling.psm1 +63 -16
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusTokenShape.psm1 +56 -1
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusWriteIntent.psm1 +4 -1
- package/resources/claude-customizations/.claude/lib/codex-routing/CodexDeployment.psm1 +5 -1
- package/resources/claude-customizations/.claude/lib/codex-routing/CodexDiscoveryRouting.psm1 +161 -0
- package/resources/claude-customizations/.claude/lib/codex-routing/CodexTopology.psm1 +25 -2
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryAuthority.psd1 +90 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryAuthority.psm1 +493 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryAuthorization.psm1 +479 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryCapability.psm1 +468 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryEncoding.psm1 +156 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryManifest.psm1 +384 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryObservation.psm1 +217 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryPayload.psm1 +276 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryPlanning.psm1 +336 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryRootGrant.psm1 +164 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryStart.psm1 +440 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryStartRecord.psm1 +296 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryStop.psm1 +336 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryToolRouting.psm1 +228 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryValidation.psm1 +188 -341
- package/resources/claude-customizations/.claude/lib/worktree-resolution/HookTargetWorktree.psm1 +391 -0
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +1 -0
- package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +150 -18
- package/resources/claude-customizations/.claude/settings.json +32 -3
- package/resources/claude-customizations/.claude/skills/adjudicate-unspecified-behavior/SKILL.md +135 -0
- package/resources/claude-customizations/.claude/skills/characterize-runtime-behavior/SKILL.md +134 -0
- package/resources/claude-customizations/.claude/skills/discovery-behavior-reconciliation/SKILL.md +28 -47
- package/resources/claude-customizations/.claude/skills/discovery-coverage-ledger/SKILL.md +32 -48
- package/resources/claude-customizations/.claude/skills/discovery-parity-matrix/SKILL.md +28 -45
- package/resources/claude-customizations/.claude/skills/discovery-repo-inventory/SKILL.md +28 -62
- package/resources/claude-customizations/.claude/skills/discovery-runtime-characterization/SKILL.md +28 -45
- package/resources/claude-customizations/.claude/skills/discovery-validate-artifacts/SKILL.md +28 -61
- package/resources/claude-customizations/.claude/skills/discovery-workflow/SKILL.md +55 -139
- package/resources/claude-customizations/.claude/skills/execution-environment/SKILL.md +24 -9
- package/resources/claude-customizations/.claude/skills/extract-feature-contract/SKILL.md +133 -0
- package/resources/claude-customizations/.claude/skills/generate-acceptance-scenarios/SKILL.md +132 -0
- package/resources/claude-customizations/.claude/skills/initialize-discovery-workspace/SKILL.md +131 -0
- package/resources/claude-customizations/.claude/skills/inventory-legacy-system/SKILL.md +131 -0
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +5 -4
- package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +21 -1
- package/resources/claude-customizations/.claude/skills/parallel-close/SKILL.md +11 -1
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +34 -0
- package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +69 -6
- package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +15 -0
- package/resources/claude-customizations/.claude/skills/parallel-run/SKILL.md +6 -0
- package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +21 -4
- package/resources/claude-customizations/.claude/skills/reconcile-parity-matrix/SKILL.md +131 -0
- package/resources/claude-customizations/.claude/skills/review-discovery-coverage/SKILL.md +129 -0
- package/resources/claude-customizations/.claude/skills/validate-discovery-artifacts/SKILL.md +127 -0
- package/resources/claude-customizations/config/blast-radius.json +6 -5
- package/resources/claude-customizations/config/orchestration-routing.json +61 -1
- package/resources/claude-customizations/pack-manifests/core.json +33 -1
- package/resources/codex-and-agents-customizations/.agents/skills/adjudicate-unspecified-behavior/SKILL.md +135 -0
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +7 -0
- package/resources/codex-and-agents-customizations/.agents/skills/characterize-runtime-behavior/SKILL.md +134 -0
- package/resources/codex-and-agents-customizations/.agents/skills/discovery-behavior-reconciliation/SKILL.md +46 -0
- package/resources/codex-and-agents-customizations/.agents/skills/discovery-coverage-ledger/SKILL.md +50 -0
- package/resources/codex-and-agents-customizations/.agents/skills/discovery-parity-matrix/SKILL.md +46 -0
- package/resources/codex-and-agents-customizations/.agents/skills/discovery-repo-inventory/SKILL.md +46 -0
- package/resources/codex-and-agents-customizations/.agents/skills/discovery-runtime-characterization/SKILL.md +46 -0
- package/resources/codex-and-agents-customizations/.agents/skills/discovery-validate-artifacts/SKILL.md +46 -0
- package/resources/codex-and-agents-customizations/.agents/skills/discovery-workflow/SKILL.md +62 -0
- package/resources/codex-and-agents-customizations/.agents/skills/execution-environment/SKILL.md +24 -9
- package/resources/codex-and-agents-customizations/.agents/skills/extract-feature-contract/SKILL.md +133 -0
- package/resources/codex-and-agents-customizations/.agents/skills/generate-acceptance-scenarios/SKILL.md +132 -0
- package/resources/codex-and-agents-customizations/.agents/skills/initialize-discovery-workspace/SKILL.md +131 -0
- package/resources/codex-and-agents-customizations/.agents/skills/inventory-legacy-system/SKILL.md +131 -0
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +8 -0
- package/resources/codex-and-agents-customizations/.agents/skills/reconcile-parity-matrix/SKILL.md +131 -0
- package/resources/codex-and-agents-customizations/.agents/skills/review-discovery-coverage/SKILL.md +129 -0
- package/resources/codex-and-agents-customizations/.agents/skills/validate-discovery-artifacts/SKILL.md +127 -0
- package/resources/codex-and-agents-customizations/.codex/agents/discovery-capability-sentinel.toml +51 -0
- package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c1.toml +148 -0
- package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c2.toml +148 -0
- package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c3-elevated.toml +148 -0
- package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c3.toml +148 -0
- package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c4.toml +148 -0
- package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst.toml +148 -0
- package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c1.toml +154 -0
- package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c2.toml +154 -0
- package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c3-elevated.toml +154 -0
- package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c3.toml +154 -0
- package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c4.toml +154 -0
- package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer.toml +154 -0
- package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c1.toml +164 -0
- package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c2.toml +164 -0
- package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c3-elevated.toml +164 -0
- package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c3.toml +164 -0
- package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c4.toml +164 -0
- package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler.toml +164 -0
- package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c1.toml +150 -0
- package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c2.toml +150 -0
- package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c3-elevated.toml +150 -0
- package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c3.toml +150 -0
- package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c4.toml +150 -0
- package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst.toml +150 -0
- package/resources/codex-and-agents-customizations/.codex/config.toml +33 -3
- package/resources/codex-and-agents-customizations/.codex/hooks/codex-agent-profile-attestation.ps1 +22 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/discovery-capability-probe.ps1 +97 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/discovery-start-attestation.ps1 +171 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-codex-model-routing.ps1 +4 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-discovery-artifact-gate.ps1 +187 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-discovery-planning-gate.ps1 +149 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +239 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +6 -15
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +54 -15
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +54 -15
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-target-worktree.ps1 +369 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/record-subagent-routing-attestation.ps1 +8 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-codex-subagent-routing.ps1 +22 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-discovery-agent-output.ps1 +178 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/Resolve-CodexTopology.ps1 +11 -1
- package/resources/codex-and-agents-customizations/pack-manifests/core.json +63 -1
- package/resources/config/orchestration-routing.json +61 -1
- package/resources/customizations/.github/agents/discovery-capability-sentinel.agent.md +34 -0
- package/resources/customizations/.github/agents/legacy-parity-analyst.agent.md +122 -0
- package/resources/customizations/.github/agents/migration-coverage-reviewer.agent.md +128 -0
- package/resources/customizations/.github/agents/requirements-reconciler.agent.md +138 -0
- package/resources/customizations/.github/agents/runtime-characterization-analyst.agent.md +124 -0
- package/resources/customizations/.github/hooks/discovery-agent-start.ps1 +139 -0
- package/resources/customizations/.github/hooks/discovery-agent-stop.ps1 +123 -0
- package/resources/customizations/.github/hooks/discovery-capability-probe.ps1 +98 -0
- package/resources/customizations/.github/hooks/discovery-planning.ps1 +91 -0
- package/resources/customizations/.github/hooks/discovery-pre-tool.ps1 +123 -0
- package/resources/customizations/.github/hooks/discovery.json +35 -0
- package/resources/customizations/.github/instructions/mermaid.instructions.md +85 -0
- package/resources/customizations/.github/prompts/execute-hard-lock.prompt.md +105 -0
- package/resources/customizations/.github/skills/adjudicate-unspecified-behavior/SKILL.md +135 -0
- package/resources/customizations/.github/skills/characterize-runtime-behavior/SKILL.md +134 -0
- package/resources/customizations/.github/skills/discovery-behavior-reconciliation/SKILL.md +46 -0
- package/resources/customizations/.github/skills/discovery-coverage-ledger/SKILL.md +50 -0
- package/resources/customizations/.github/skills/discovery-parity-matrix/SKILL.md +46 -0
- package/resources/customizations/.github/skills/discovery-repo-inventory/SKILL.md +46 -0
- package/resources/customizations/.github/skills/discovery-runtime-characterization/SKILL.md +46 -0
- package/resources/customizations/.github/skills/discovery-validate-artifacts/SKILL.md +46 -0
- package/resources/customizations/.github/skills/discovery-workflow/SKILL.md +62 -0
- package/resources/customizations/.github/skills/execution-environment/SKILL.md +24 -9
- package/resources/customizations/.github/skills/extract-feature-contract/SKILL.md +133 -0
- package/resources/customizations/.github/skills/generate-acceptance-scenarios/SKILL.md +132 -0
- package/resources/customizations/.github/skills/initialize-discovery-workspace/SKILL.md +131 -0
- package/resources/customizations/.github/skills/inventory-legacy-system/SKILL.md +131 -0
- package/resources/customizations/.github/skills/reconcile-parity-matrix/SKILL.md +131 -0
- package/resources/customizations/.github/skills/review-discovery-coverage/SKILL.md +129 -0
- package/resources/customizations/.github/skills/validate-discovery-artifacts/SKILL.md +127 -0
- package/resources/lib/codex-routing/CodexDeployment.psm1 +5 -1
- package/resources/lib/codex-routing/CodexDiscoveryRouting.psm1 +161 -0
- package/resources/lib/codex-routing/CodexTopology.psm1 +25 -2
- package/resources/lib/discovery-validation/DiscoveryAuthority.psd1 +90 -0
- package/resources/lib/discovery-validation/DiscoveryAuthority.psm1 +493 -0
- package/resources/lib/discovery-validation/DiscoveryAuthorization.psm1 +479 -0
- package/resources/lib/discovery-validation/DiscoveryCapability.psm1 +468 -0
- package/resources/lib/discovery-validation/DiscoveryEncoding.psm1 +156 -0
- package/resources/lib/discovery-validation/DiscoveryManifest.psm1 +384 -0
- package/resources/lib/discovery-validation/DiscoveryObservation.psm1 +217 -0
- package/resources/lib/discovery-validation/DiscoveryPayload.psm1 +276 -0
- package/resources/lib/discovery-validation/DiscoveryPlanning.psm1 +336 -0
- package/resources/lib/discovery-validation/DiscoveryRootGrant.psm1 +164 -0
- package/resources/lib/discovery-validation/DiscoveryStart.psm1 +440 -0
- package/resources/lib/discovery-validation/DiscoveryStartRecord.psm1 +296 -0
- package/resources/lib/discovery-validation/DiscoveryStop.psm1 +336 -0
- package/resources/lib/discovery-validation/DiscoveryToolRouting.psm1 +228 -0
- package/resources/powershell/PoshQC/PoshQC.Parallel.psm1 +452 -0
- package/resources/powershell/PoshQC/PoshQC.Shard.psm1 +106 -8
- package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +12 -0
- package/resources/powershell/PoshQC/PoshQC.psd1 +6 -1
- package/resources/powershell/PoshQC/PoshQC.psm1 +7 -1
- package/resources/powershell/PoshQC/README.md +20 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Epic-scope legs, checkpoint read seams, and target resolution of the preimplementation gate.
|
|
4
4
|
|
|
5
5
|
.DESCRIPTION
|
|
6
|
-
Dot-sourced by enforce-orchestration-preimplementation-gate.ps1. Holds
|
|
6
|
+
Dot-sourced by enforce-orchestration-preimplementation-gate.ps1. Holds five groups of
|
|
7
7
|
functions:
|
|
8
8
|
|
|
9
9
|
- The import guards for the worktree-resolution modules (issue #690). A failed import
|
|
@@ -15,13 +15,20 @@
|
|
|
15
15
|
read binds to the calling process's directory. Get-CheckpointContent and
|
|
16
16
|
Get-OrchestrationModeDenyReason were relocated here from the gate file by issue #690
|
|
17
17
|
to keep the gate inside the 500-line cap.
|
|
18
|
+
- The target-resolution building blocks of issue #1010: the worktree-list wrapper
|
|
19
|
+
Get-OrchestrationGateWorktreeList (the only listing seam), the recording invoker
|
|
20
|
+
Invoke-OrchestrationGateWorktreeList, which records a failed or empty listing in
|
|
21
|
+
$script:OrchestrationGateWorktreeListFailed, and the command-operand parser
|
|
22
|
+
Get-OrchestrationGateCommandOperandPath for git add and git commit.
|
|
18
23
|
- The target resolution of issue #690. Resolve-OrchestrationGateTarget selects the
|
|
19
24
|
worktree whose checkpoint governs the call from portable identity only: the
|
|
20
25
|
integration_branch: value for an epic delegation, the parallel_slug: value for a
|
|
21
|
-
parallel delegation, the
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
parallel delegation, and the canonical issue-number line and branch: label for a
|
|
27
|
+
single-feature delegation. The path and command legs resolve through
|
|
28
|
+
Resolve-HookTargetWorktree (issue #1010) from the file_path or from every git add
|
|
29
|
+
and git commit selector and absolute operand. Read-OrchestrationGateCheckpoint reads
|
|
30
|
+
the checkpoint beneath the resolved root, or returns the deny for an unresolved
|
|
31
|
+
target.
|
|
25
32
|
- The epic-scope decision for the command and path legs (issue #663). A staging
|
|
26
33
|
command or a Write/Edit call is epic scope when the effective worktree's HEAD equals
|
|
27
34
|
the integration branch of the epic checkpoint that Resolve-EpicScopeCheckpoint
|
|
@@ -30,21 +37,24 @@
|
|
|
30
37
|
|
|
31
38
|
.NOTES
|
|
32
39
|
PowerShell 7+. Depends on functions the gate defines or dot-sources before any call:
|
|
33
|
-
Split-OrchestrationCommandLine and ConvertTo-OrchestrationCommandToken (helpers file),
|
|
34
40
|
Get-OrchestrationDelegationCheckpointPath (modes file), the gate's allow and block
|
|
35
|
-
decision constructors, and,
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
decision constructors, and Read-CommandLineSegment, Get-CommandLineGlobalOption, and
|
|
42
|
+
Get-CommandLineInvocation (loaded by the gate's dot-source of
|
|
43
|
+
hook-command-invocation.ps1), which the targets file this file dot-sources (issue #738)
|
|
44
|
+
and Get-OrchestrationGateCommandOperandPath call. Imports
|
|
45
|
+
WorktreeResolution.psm1 (the reason-code accessors), then WorktreeItemResolution.psm1
|
|
46
|
+
and WorktreeRunResolution.psm1 before EpicScopeResolution.psm1, so the nested import
|
|
47
|
+
inside EpicScopeResolution reuses the same module instance, and HookTargetWorktree.psm1
|
|
48
|
+
(Resolve-HookTargetWorktree, issue #1009) last. Mirrored byte-identically under
|
|
41
49
|
extensions/drm-copilot/resources/claude-customizations/.
|
|
42
50
|
#>
|
|
43
51
|
|
|
52
|
+
try { Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeResolution.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'WorktreeResolution.psm1' -ErrorRecord $_ }
|
|
44
53
|
try { Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeItemResolution.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'WorktreeItemResolution.psm1' -ErrorRecord $_ }
|
|
45
54
|
try { Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeRunResolution.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'WorktreeRunResolution.psm1' -ErrorRecord $_ }
|
|
46
55
|
try { Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/EpicScopeResolution.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'EpicScopeResolution.psm1' -ErrorRecord $_ }
|
|
47
56
|
try { Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/EpicScopeReadiness.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'EpicScopeReadiness.psm1' -ErrorRecord $_ }
|
|
57
|
+
try { Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/HookTargetWorktree.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'HookTargetWorktree.psm1' -ErrorRecord $_ }
|
|
48
58
|
# Per-segment target resolution for the epic-scope decision (issue #738).
|
|
49
59
|
. (Join-Path $PSScriptRoot 'enforce-orchestration-preimplementation-gate-targets.ps1')
|
|
50
60
|
|
|
@@ -102,15 +112,137 @@ function Get-OrchestrationModeDenyReason {
|
|
|
102
112
|
'require that checkpoint to satisfy every readiness predicate before implementation begins.')
|
|
103
113
|
}
|
|
104
114
|
|
|
115
|
+
function Get-OrchestrationGateWorktreeList {
|
|
116
|
+
<#
|
|
117
|
+
.SYNOPSIS
|
|
118
|
+
Returns the porcelain worktree listing for the session root (issue #1010 seam).
|
|
119
|
+
.DESCRIPTION
|
|
120
|
+
Runs the listing exactly as the default WorktreeListInvoker of
|
|
121
|
+
Resolve-HookTargetWorktree does and returns an empty string on a non-zero exit. It
|
|
122
|
+
is the only test seam for the listing.
|
|
123
|
+
.OUTPUTS
|
|
124
|
+
System.String
|
|
125
|
+
#>
|
|
126
|
+
[CmdletBinding()]
|
|
127
|
+
[OutputType([string])]
|
|
128
|
+
param([Parameter(Mandatory)][string] $SessionRoot)
|
|
129
|
+
|
|
130
|
+
$porcelain = & git -C $SessionRoot worktree list --porcelain 2>$null
|
|
131
|
+
if ($LASTEXITCODE -ne 0) { return '' }
|
|
132
|
+
return ($porcelain -join "`n")
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function Invoke-OrchestrationGateWorktreeList {
|
|
136
|
+
<#
|
|
137
|
+
.SYNOPSIS
|
|
138
|
+
Recording WorktreeListInvoker: returns the listing and records a listing failure.
|
|
139
|
+
.DESCRIPTION
|
|
140
|
+
Sets $script:OrchestrationGateWorktreeListFailed when the wrapper throws (and then
|
|
141
|
+
rethrows) or when its text holds no worktree entry, so the gate denies instead of
|
|
142
|
+
accepting the resolver's session-root fallback (issue #1010, spec E1).
|
|
143
|
+
.OUTPUTS
|
|
144
|
+
System.String
|
|
145
|
+
#>
|
|
146
|
+
[CmdletBinding()]
|
|
147
|
+
[OutputType([string])]
|
|
148
|
+
param([Parameter(Mandatory)][string] $SessionRoot)
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
$text = [string](Get-OrchestrationGateWorktreeList -SessionRoot $SessionRoot)
|
|
152
|
+
} catch {
|
|
153
|
+
$script:OrchestrationGateWorktreeListFailed = $true
|
|
154
|
+
throw
|
|
155
|
+
}
|
|
156
|
+
if (@(ConvertFrom-HookTargetWorktreePorcelain -Text $text).Count -eq 0) {
|
|
157
|
+
$script:OrchestrationGateWorktreeListFailed = $true
|
|
158
|
+
}
|
|
159
|
+
return $text
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function Get-OrchestrationGateCommandOperandPath {
|
|
163
|
+
<#
|
|
164
|
+
.SYNOPSIS
|
|
165
|
+
Returns the worktree-selecting paths of every git add and git commit invocation.
|
|
166
|
+
.DESCRIPTION
|
|
167
|
+
Scans every Structural match of git add and git commit (every command segment and
|
|
168
|
+
wrapper payload). Each -C value in any git global-option position and each absolute
|
|
169
|
+
operand is returned, distinct and in first-seen order. Relative operands, a -C on a
|
|
170
|
+
git segment invoking neither add nor commit, and Indeterminate matches are ignored.
|
|
171
|
+
For a match whose record is not argument-injected, a subcommand-aware walk (issue
|
|
172
|
+
#1010, F2) also reads the tokens after the subcommand with a per-subcommand option
|
|
173
|
+
model, compared case-sensitively: a value option consumes the next token unless its
|
|
174
|
+
value is inline (--name=value) or attached (-mvalue); a standalone flag consumes
|
|
175
|
+
itself; '--' makes every later token an operand; a redirection and its target are
|
|
176
|
+
skipped; any other dash token, such as the cluster -am, stops the walk. Its literal
|
|
177
|
+
absolute tokens are returned, and a token the walk reads as an option value is not
|
|
178
|
+
returned as an operand.
|
|
179
|
+
.PARAMETER Command
|
|
180
|
+
The full command line.
|
|
181
|
+
.OUTPUTS
|
|
182
|
+
System.String[]
|
|
183
|
+
#>
|
|
184
|
+
[CmdletBinding()]
|
|
185
|
+
[OutputType([string[]])]
|
|
186
|
+
param([Parameter(Mandatory)][AllowEmptyString()][string] $Command)
|
|
187
|
+
|
|
188
|
+
$valueOptions = @{
|
|
189
|
+
commit = @('-m', '--message', '-F', '--file', '--author', '--date', '-C', '--reuse-message', '-c', '--reedit-message', '-t', '--template', '--fixup', '--squash', '--trailer', '--pathspec-from-file')
|
|
190
|
+
add = @('--pathspec-from-file', '--chmod')
|
|
191
|
+
}
|
|
192
|
+
$flagOptions = @{
|
|
193
|
+
commit = @('-a', '--all', '--amend', '--no-edit', '--no-verify', '-n', '-s', '--signoff', '-q', '--quiet', '-v', '--verbose', '--allow-empty', '-o', '--only', '-i', '--include', '--pathspec-file-nul')
|
|
194
|
+
add = @('-A', '--all', '-u', '--update', '-f', '--force', '-N', '--intent-to-add', '-n', '--dry-run', '-v', '--verbose', '--pathspec-file-nul')
|
|
195
|
+
}
|
|
196
|
+
$absolutePattern = '^([A-Za-z]:[\\/]|/)'
|
|
197
|
+
$paths = [System.Collections.Generic.List[string]]::new()
|
|
198
|
+
foreach ($subcommand in @('add', 'commit')) {
|
|
199
|
+
foreach ($match in @(Get-CommandLineInvocation -CommandText $Command -CommandWord 'git' -SubcommandPath @($subcommand))) {
|
|
200
|
+
if ($match.Status -ne 'Structural') { continue }
|
|
201
|
+
$walked = [System.Collections.Generic.List[string]]::new()
|
|
202
|
+
$optionValues = [System.Collections.Generic.List[string]]::new()
|
|
203
|
+
$tokens = if ($match.Segment.ArgumentInjected) { [string[]]@() } else { [string[]]@($match.Segment.Tokens) }
|
|
204
|
+
$afterSeparator = $false
|
|
205
|
+
for ($index = [int]$match.OperandIndex; $index -lt $tokens.Count; $index++) {
|
|
206
|
+
$token = $tokens[$index]
|
|
207
|
+
if (-not $afterSeparator -and $token -match '^(<|>|\d*>|&>)') {
|
|
208
|
+
if ($token -match '^(\d*[<>]{1,2}&?|&>{1,2})$') { $index++ }
|
|
209
|
+
continue
|
|
210
|
+
}
|
|
211
|
+
if ($afterSeparator -or -not $token.StartsWith('-')) { $walked.Add($token); continue }
|
|
212
|
+
if ($token -ceq '--') { $afterSeparator = $true; continue }
|
|
213
|
+
$name = if ($token.IndexOf('=') -gt 0) { $token.Substring(0, $token.IndexOf('=')) } else { $token }
|
|
214
|
+
if ($valueOptions[$subcommand] -ccontains $name) {
|
|
215
|
+
if ($name -ceq $token -and $index + 1 -lt $tokens.Count) { $index++; $optionValues.Add($tokens[$index]) }
|
|
216
|
+
continue
|
|
217
|
+
}
|
|
218
|
+
if ($token -match '^-[^-].' -and $valueOptions[$subcommand] -ccontains $token.Substring(0, 2)) { continue }
|
|
219
|
+
if ($flagOptions[$subcommand] -ccontains $token) { continue }
|
|
220
|
+
break
|
|
221
|
+
}
|
|
222
|
+
$candidates = @(@($match.GlobalOptions) | Where-Object { $_.Name -ceq '-C' -and $_.Value } | ForEach-Object { [string]$_.Value })
|
|
223
|
+
$candidates += @(@($match.Operands) | Where-Object { $_ -match $absolutePattern -and $optionValues -cnotcontains $_ })
|
|
224
|
+
$candidates += @($walked | Where-Object { $_ -match $absolutePattern -and (Test-CommandLineTokenLiteral -Token $_) })
|
|
225
|
+
foreach ($candidate in $candidates) {
|
|
226
|
+
if ($paths -notcontains $candidate) { $paths.Add($candidate) }
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return [string[]]$paths.ToArray()
|
|
231
|
+
}
|
|
232
|
+
|
|
105
233
|
function Resolve-OrchestrationGateTarget {
|
|
106
234
|
<#
|
|
107
235
|
.SYNOPSIS
|
|
108
236
|
Resolves the worktree whose checkpoint governs a gated call (issue #690 seam).
|
|
109
237
|
.DESCRIPTION
|
|
110
238
|
An epic delegation is keyed on integration_branch: (with epic_feature_folder: as a
|
|
111
|
-
cross-check), a parallel delegation on parallel_slug:, a
|
|
112
|
-
|
|
113
|
-
|
|
239
|
+
cross-check), a parallel delegation on parallel_slug:, and a single-feature
|
|
240
|
+
delegation on its canonical issue-number line and branch: label. The path and
|
|
241
|
+
command legs use Resolve-HookTargetWorktree (issue #1010): a path leg passes its
|
|
242
|
+
file_path, a command leg the Get-OrchestrationGateCommandOperandPath result, and the
|
|
243
|
+
recording invoker supplies the worktree listing. A failed or empty listing denies as
|
|
244
|
+
not derivable instead of falling back to the session root; an ambiguous result
|
|
245
|
+
denies as ambiguous; a null root denies as not derivable.
|
|
114
246
|
.OUTPUTS
|
|
115
247
|
System.Management.Automation.PSCustomObject (the worktree-resolution target result).
|
|
116
248
|
#>
|
|
@@ -131,13 +263,29 @@ function Resolve-OrchestrationGateTarget {
|
|
|
131
263
|
}
|
|
132
264
|
return Resolve-WorktreeParallelTarget -ParallelSlug $signal.ParallelSlug -SessionRoot $sessionRoot
|
|
133
265
|
}
|
|
134
|
-
if ($FilePath) {
|
|
135
|
-
return Resolve-
|
|
266
|
+
if (-not $FilePath -and -not $Command) {
|
|
267
|
+
return Resolve-WorktreeItemTarget -Text $Prompt -SessionRoot $sessionRoot
|
|
268
|
+
}
|
|
269
|
+
$script:OrchestrationGateWorktreeListFailed = $false
|
|
270
|
+
$invoker = { param($SessionRoot) Invoke-OrchestrationGateWorktreeList -SessionRoot $SessionRoot }
|
|
271
|
+
$resolved = if ($FilePath) {
|
|
272
|
+
Resolve-HookTargetWorktree -SessionRoot $sessionRoot -FilePath @($FilePath) -WorktreeListInvoker $invoker
|
|
273
|
+
} else {
|
|
274
|
+
Resolve-HookTargetWorktree -SessionRoot $sessionRoot -CommandOperandPath @(Get-OrchestrationGateCommandOperandPath -Command $Command) -WorktreeListInvoker $invoker
|
|
275
|
+
}
|
|
276
|
+
$detail = "resolver source $($resolved.Source): $($resolved.Detail)"
|
|
277
|
+
if ($script:OrchestrationGateWorktreeListFailed) {
|
|
278
|
+
return [pscustomobject]@{ Status = 'NoTarget'; WorktreeRoot = $null; ReasonCode = (Get-WorktreeResolutionNoTargetReasonCode); Detail = "the worktree listing failed or named no worktree; $detail" }
|
|
136
279
|
}
|
|
137
|
-
if ($
|
|
138
|
-
return
|
|
280
|
+
if ($resolved.Ambiguous) {
|
|
281
|
+
return [pscustomobject]@{ Status = 'Ambiguous'; WorktreeRoot = $null; ReasonCode = (Get-WorktreeResolutionAmbiguityReasonCode); Detail = $detail }
|
|
139
282
|
}
|
|
140
|
-
|
|
283
|
+
if ([string]::IsNullOrEmpty([string]$resolved.WorktreeRoot)) {
|
|
284
|
+
return [pscustomobject]@{ Status = 'NoTarget'; WorktreeRoot = $null; ReasonCode = (Get-WorktreeResolutionNoTargetReasonCode); Detail = $detail }
|
|
285
|
+
}
|
|
286
|
+
$isSession = [string]::Equals([string]$resolved.WorktreeRoot, (ConvertTo-HookTargetWorktreeNormalizedPath -Path $sessionRoot), [System.StringComparison]::OrdinalIgnoreCase)
|
|
287
|
+
$status = if ($isSession) { 'SessionRoot' } else { 'OtherWorktree' }
|
|
288
|
+
return [pscustomobject]@{ Status = $status; WorktreeRoot = [string]$resolved.WorktreeRoot; ReasonCode = $null; Detail = $detail }
|
|
141
289
|
}
|
|
142
290
|
|
|
143
291
|
function Read-OrchestrationGateCheckpoint {
|
|
@@ -187,34 +335,6 @@ function Read-OrchestrationGateCheckpoint {
|
|
|
187
335
|
}
|
|
188
336
|
}
|
|
189
337
|
|
|
190
|
-
function Get-OrchestrationEpicScopeSelector {
|
|
191
|
-
<#
|
|
192
|
-
.SYNOPSIS
|
|
193
|
-
Returns the value of a leading `git -C <value>` selector, or $null.
|
|
194
|
-
.DESCRIPTION
|
|
195
|
-
Pure. Reads only the first segment of the command line, split and tokenized with
|
|
196
|
-
the helpers-file parser so quoting is handled exactly as the staging exemption
|
|
197
|
-
handles it. An unbalanced command line has no decidable selector and returns $null.
|
|
198
|
-
.PARAMETER Command
|
|
199
|
-
The full command line.
|
|
200
|
-
.OUTPUTS
|
|
201
|
-
System.String
|
|
202
|
-
#>
|
|
203
|
-
[CmdletBinding()]
|
|
204
|
-
[OutputType([string])]
|
|
205
|
-
param([Parameter(Mandatory)][AllowEmptyString()][string] $Command)
|
|
206
|
-
|
|
207
|
-
$split = Split-OrchestrationCommandLine -CommandText $Command
|
|
208
|
-
if (-not $split.Balanced -or @($split.Segments).Count -eq 0) {
|
|
209
|
-
return $null
|
|
210
|
-
}
|
|
211
|
-
$token = @(ConvertTo-OrchestrationCommandToken -Segment ([string]@($split.Segments)[0]).Trim())
|
|
212
|
-
if ($token.Count -ge 3 -and $token[0] -ceq 'git' -and $token[1] -ceq '-C' -and $token[2]) {
|
|
213
|
-
return $token[2]
|
|
214
|
-
}
|
|
215
|
-
return $null
|
|
216
|
-
}
|
|
217
|
-
|
|
218
338
|
function Get-OrchestrationEpicScopeDecision {
|
|
219
339
|
<#
|
|
220
340
|
.SYNOPSIS
|
|
@@ -239,7 +239,7 @@ function Test-OrchestrationReady {
|
|
|
239
239
|
}
|
|
240
240
|
$lifecycleReady = $false
|
|
241
241
|
if ($Payload.PSObject.Properties.Name -contains 'lifecycle_ready') {
|
|
242
|
-
$lifecycleReady = [bool]$Payload.lifecycle_ready
|
|
242
|
+
$lifecycleReady = ($Payload.lifecycle_ready -is [bool]) -and $Payload.lifecycle_ready
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
if (-not $issueNum -or -not $featureFolder -or -not $routeId -or -not $lifecycleReady) {
|
|
@@ -7,14 +7,28 @@
|
|
|
7
7
|
operation. When the target file is a PowerShell source file (.ps1, .psm1, .psd1),
|
|
8
8
|
it decides whether the change may proceed in direct mode.
|
|
9
9
|
|
|
10
|
-
Direct mode counts distinct production PowerShell paths per Claude Code session
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
The
|
|
17
|
-
|
|
10
|
+
Direct mode counts distinct production PowerShell paths per Claude Code session and
|
|
11
|
+
per resolved target worktree, and repeated edits to the same file are counted once.
|
|
12
|
+
The 4th distinct production path in one worktree is denied with a
|
|
13
|
+
POWERSHELL_LARGE_PATH_REQUIRED reason that instructs the caller to route the change
|
|
14
|
+
through /orchestrate.
|
|
15
|
+
|
|
16
|
+
The edited file is placed with Resolve-HookTargetWorktree from
|
|
17
|
+
.claude/lib/worktree-resolution/HookTargetWorktree.psm1. An absolute path resolves to
|
|
18
|
+
its nearest enclosing registered git worktree; a relative path, a path inside no
|
|
19
|
+
registered worktree, and an ambiguous result use the session root (the checkout that
|
|
20
|
+
holds this hook). The route is read from
|
|
21
|
+
artifacts/orchestration/orchestrator-state.json in the resolved target worktree.
|
|
22
|
+
The running set is kept under the session root, in
|
|
23
|
+
.claude/state/powershell-batch-budget.<session_id>.json when the resolved target worktree
|
|
24
|
+
is the session root and in
|
|
25
|
+
.claude/state/powershell-batch-budget.<session_id>.<worktree key>.json otherwise,
|
|
26
|
+
where the key comes from Get-HookTargetWorktreeKey; no state is written under another
|
|
27
|
+
worktree. A path under the root-level artifacts/ directory of the resolved target worktree
|
|
28
|
+
is gitignored scratch: it is allowed, consumes no slot, and writes no state. A
|
|
29
|
+
nested directory named artifacts elsewhere in the tree still counts.
|
|
30
|
+
|
|
31
|
+
The selected route is the
|
|
18
32
|
route_id value when that key is present, otherwise the path_selected value, and it
|
|
19
33
|
is usable only as a non-blank string. When the selected route is large,
|
|
20
34
|
remediation, or preparation and the checkpoint is not terminal (next_step is not
|
|
@@ -38,9 +52,9 @@
|
|
|
38
52
|
is composed into a file name, so a hostile id cannot escape the state
|
|
39
53
|
directory. Resolving the id never creates the state directory.
|
|
40
54
|
|
|
41
|
-
Candidate paths are contained to the resolved root. A candidate
|
|
42
|
-
outside it is discarded: the decision is 'allow', no slot is consumed,
|
|
43
|
-
state is written. Persisted entries that fail the same containment test are
|
|
55
|
+
Candidate paths are contained to the resolved target worktree root. A candidate
|
|
56
|
+
that resolves outside it is discarded: the decision is 'allow', no slot is consumed,
|
|
57
|
+
and no state is written. Persisted entries that fail the same containment test are
|
|
44
58
|
dropped when state is rehydrated, so a state file carried between worktrees
|
|
45
59
|
cannot spend this worktree's budget.
|
|
46
60
|
|
|
@@ -48,9 +62,12 @@
|
|
|
48
62
|
hookSpecificOutput.permissionDecision = 'deny' and exits 0. Files already counted
|
|
49
63
|
are always allowed through.
|
|
50
64
|
|
|
51
|
-
Known
|
|
52
|
-
|
|
53
|
-
|
|
65
|
+
Known limitations: an 8.3 short-name path, or a path reached through a junction,
|
|
66
|
+
that differs textually from the long path git reports matches no registered
|
|
67
|
+
worktree and falls back to the session root. A stale non-terminal large-path
|
|
68
|
+
checkpoint in a worktree exempts a later direct-mode session editing that worktree;
|
|
69
|
+
orchestrator checkpoint hygiene (issue #673) moves foreign checkpoints aside before
|
|
70
|
+
a new run.
|
|
54
71
|
|
|
55
72
|
.NOTES
|
|
56
73
|
Compatible with PowerShell 7+.
|
|
@@ -61,6 +78,7 @@ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot
|
|
|
61
78
|
|
|
62
79
|
|
|
63
80
|
try { Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'HookPayload.psm1' -ErrorRecord $_ }
|
|
81
|
+
try { Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/HookTargetWorktree.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'HookTargetWorktree.psm1' -ErrorRecord $_ }
|
|
64
82
|
$script:PowerShellBatchBudgetRouteImportFailure = $null; try { . (Join-Path $PSScriptRoot 'enforce-batch-budget-route.ps1') } catch { $script:PowerShellBatchBudgetRouteImportFailure = 'enforce-batch-budget-route.ps1' }
|
|
65
83
|
|
|
66
84
|
function Test-PowerShellBatchBudgetPathInRoot {
|
|
@@ -351,7 +369,8 @@ function Invoke-PowerShellBatchBudgetHook {
|
|
|
351
369
|
[scriptblock] $WriteState = {
|
|
352
370
|
param([string] $Path, [System.Collections.IDictionary] $State)
|
|
353
371
|
$State | ConvertTo-Json -Depth 5 | Set-Content -Path $Path -Encoding UTF8
|
|
354
|
-
}
|
|
372
|
+
},
|
|
373
|
+
[scriptblock] $WorktreeListInvoker
|
|
355
374
|
)
|
|
356
375
|
|
|
357
376
|
$payload = Resolve-ClaudeHookToolInput -Raw $ToolInputRaw
|
|
@@ -373,6 +392,25 @@ function Invoke-PowerShellBatchBudgetHook {
|
|
|
373
392
|
}
|
|
374
393
|
if ($script:PowerShellBatchBudgetRouteImportFailure) { return Get-PowerShellBatchBudgetBlockDecision -Reason "POWERSHELL_LARGE_PATH_REQUIRED: the route helper '$($script:PowerShellBatchBudgetRouteImportFailure)' failed to load, so the batch budget for '$normalized' cannot be evaluated; the gate fails closed." }
|
|
375
394
|
|
|
395
|
+
# The file is evaluated against the registered worktree that holds it. The session root
|
|
396
|
+
# stays the fallback for a relative path, a path in no registered worktree, and an
|
|
397
|
+
# ambiguous result, which keeps today's checkpoint, containment, and state-file name.
|
|
398
|
+
$resolverArguments = @{ SessionRoot = $Root; FilePath = @($filePath) }
|
|
399
|
+
if ($PSBoundParameters.ContainsKey('WorktreeListInvoker')) { $resolverArguments['WorktreeListInvoker'] = $WorktreeListInvoker }
|
|
400
|
+
$resolution = Resolve-HookTargetWorktree @resolverArguments
|
|
401
|
+
$targetRoot = $Root
|
|
402
|
+
$isSessionRoot = $true
|
|
403
|
+
if (-not $resolution.Ambiguous -and $null -ne $resolution.WorktreeRoot -and -not [string]::Equals($resolution.WorktreeRoot, (ConvertTo-HookTargetWorktreeNormalizedPath -Path $Root), [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
404
|
+
$targetRoot = $resolution.WorktreeRoot
|
|
405
|
+
$isSessionRoot = $false
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
# The root-level artifacts/ directory is gitignored scratch, never a production file.
|
|
409
|
+
$relativePath = if ($normalized -match '^(/|[A-Za-z]:/)') { Get-HookTargetWorktreeRelativePath -Path $normalized -WorktreeRoot $targetRoot } else { $normalized }
|
|
410
|
+
if ($null -ne $relativePath -and $relativePath.StartsWith('artifacts/', [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
411
|
+
return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
|
|
412
|
+
}
|
|
413
|
+
|
|
376
414
|
$stateDir = Join-Path -Path $Root -ChildPath '.claude/state'
|
|
377
415
|
|
|
378
416
|
# Resolved before the directory is ensured, because reading the session-id
|
|
@@ -383,11 +421,12 @@ function Invoke-PowerShellBatchBudgetHook {
|
|
|
383
421
|
-SessionIdFilePath (Join-Path -Path $stateDir -ChildPath 'current-session-id') `
|
|
384
422
|
-ReadSessionIdFile $ReadSessionIdFile
|
|
385
423
|
|
|
386
|
-
$
|
|
424
|
+
$stateLeaf = if ($isSessionRoot) { "powershell-batch-budget.$resolvedSessionId.json" } else { "powershell-batch-budget.$resolvedSessionId.$(Get-HookTargetWorktreeKey -WorktreeRoot $targetRoot).json" }
|
|
425
|
+
$stateFile = Join-Path -Path $stateDir -ChildPath $stateLeaf
|
|
387
426
|
|
|
388
427
|
# The checkpoint is read before any state operation, so the large path neither
|
|
389
428
|
# creates the state directory nor reads or writes the state file.
|
|
390
|
-
$checkpointPath = Join-Path -Path $
|
|
429
|
+
$checkpointPath = Join-Path -Path $targetRoot -ChildPath 'artifacts/orchestration/orchestrator-state.json'
|
|
391
430
|
$checkpointText = ''
|
|
392
431
|
try {
|
|
393
432
|
$checkpointText = [string](& $ReadCheckpoint $checkpointPath)
|
|
@@ -399,7 +438,7 @@ function Invoke-PowerShellBatchBudgetHook {
|
|
|
399
438
|
$isLargePath = Test-BatchBudgetLargePathRoute -CheckpointText $checkpointText
|
|
400
439
|
$observedRoute = Get-BatchBudgetSelectedRoute -CheckpointText $checkpointText
|
|
401
440
|
if ($isLargePath) {
|
|
402
|
-
return Invoke-PowerShellBatchBudgetDecision -FilePath $filePath -State (Get-PowerShellBatchBudgetState -ProdCap $ProdCap) -StateFile $stateFile -Root $
|
|
441
|
+
return Invoke-PowerShellBatchBudgetDecision -FilePath $filePath -State (Get-PowerShellBatchBudgetState -ProdCap $ProdCap) -StateFile $stateFile -Root $targetRoot -LargePathRoute
|
|
403
442
|
}
|
|
404
443
|
|
|
405
444
|
if (-not (& $TestPathExists $stateDir)) {
|
|
@@ -411,13 +450,13 @@ function Invoke-PowerShellBatchBudgetHook {
|
|
|
411
450
|
if (& $TestPathExists $stateFile) {
|
|
412
451
|
try {
|
|
413
452
|
$loaded = & $ReadState $stateFile | ConvertFrom-Json -ErrorAction Stop
|
|
414
|
-
$state = ConvertTo-PowerShellBatchBudgetState -InputObject $loaded -ProdCap $ProdCap -Root $
|
|
453
|
+
$state = ConvertTo-PowerShellBatchBudgetState -InputObject $loaded -ProdCap $ProdCap -Root $targetRoot
|
|
415
454
|
} catch {
|
|
416
455
|
Write-Verbose "Ignoring unreadable PowerShell batch-budget state file '$stateFile': $($_.Exception.Message)"
|
|
417
456
|
}
|
|
418
457
|
}
|
|
419
458
|
|
|
420
|
-
$decision = Invoke-PowerShellBatchBudgetDecision -FilePath $filePath -State $state -StateFile $stateFile -Root $
|
|
459
|
+
$decision = Invoke-PowerShellBatchBudgetDecision -FilePath $filePath -State $state -StateFile $stateFile -Root $targetRoot -ObservedRoute $observedRoute
|
|
421
460
|
if ($decision.shouldWriteState) {
|
|
422
461
|
try {
|
|
423
462
|
& $WriteState $stateFile $decision.state
|
package/resources/claude-customizations/.claude/hooks/enforce-pr-author-command-allowlist.ps1
CHANGED
|
@@ -10,9 +10,12 @@
|
|
|
10
10
|
in one of these forms, matched by token position:
|
|
11
11
|
|
|
12
12
|
- git log ... and git rev-parse ... (no git global option)
|
|
13
|
+
- git worktree list --porcelain (exactly these four tokens, issue #1011)
|
|
13
14
|
- gh pr create ... and gh pr edit ... (no gh global option)
|
|
14
15
|
- gh pr view <N> --json closingIssuesReferences,body (exactly these six tokens, issue #989)
|
|
15
16
|
- sha256sum artifacts/pr_body_<N>.md (only as the whole command)
|
|
17
|
+
- sha256sum <item worktree>/artifacts/pr_body_<N>.md (only as the whole command; an absolute
|
|
18
|
+
forward-slash path, issue #1011)
|
|
16
19
|
- date -u [+<format>] (only as the whole command)
|
|
17
20
|
|
|
18
21
|
Segments may be joined only by ';', '&&', '||', '|', or a newline. A record that is
|
|
@@ -42,7 +45,38 @@ $script:PrAuthorAllowlistWrapperWords = @(
|
|
|
42
45
|
'sh', 'bash', 'zsh', 'dash', 'ksh', 'pwsh', 'powershell', 'eval', 'xargs',
|
|
43
46
|
'env', 'timeout', 'time', 'command', 'nohup'
|
|
44
47
|
)
|
|
45
|
-
$script:PrAuthorAllowlistFormText = 'git log, git rev-parse, gh pr create, gh pr edit, gh pr view <N> --json closingIssuesReferences,body, sha256sum artifacts/pr_body_<N>.md, date -u [+<format>]'
|
|
48
|
+
$script:PrAuthorAllowlistFormText = 'git log, git rev-parse, git worktree list --porcelain, gh pr create, gh pr edit, gh pr view <N> --json closingIssuesReferences,body, sha256sum artifacts/pr_body_<N>.md, sha256sum <item worktree>/artifacts/pr_body_<N>.md, date -u [+<format>]'
|
|
49
|
+
|
|
50
|
+
function Test-PrAuthorAbsoluteBodyPathToken {
|
|
51
|
+
<#
|
|
52
|
+
.SYNOPSIS
|
|
53
|
+
Report whether a token is an absolute canonical body path beneath an item worktree (issue #1011).
|
|
54
|
+
.DESCRIPTION
|
|
55
|
+
The token must be rooted at '/' or at a drive letter followed by ':/', use forward slashes
|
|
56
|
+
only, hold at least one segment before 'artifacts', hold no empty, '.', or '..' segment,
|
|
57
|
+
and end in '/artifacts/pr_body_<digits>.md' with the exact case of the relative form.
|
|
58
|
+
Pure string logic: no process, file read, or module import.
|
|
59
|
+
.PARAMETER Value
|
|
60
|
+
The sha256sum argument token, quotes already stripped.
|
|
61
|
+
.OUTPUTS
|
|
62
|
+
System.Boolean
|
|
63
|
+
#>
|
|
64
|
+
[CmdletBinding()]
|
|
65
|
+
[OutputType([bool])]
|
|
66
|
+
param(
|
|
67
|
+
[Parameter(Mandatory)][AllowEmptyString()][string] $Value
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if ($Value.Contains('\') -or $Value -cnotmatch '^(/|[A-Za-z]:/)(?<rest>.+)/artifacts/pr_body_\d+\.md$') {
|
|
71
|
+
return $false
|
|
72
|
+
}
|
|
73
|
+
foreach ($segment in $Matches['rest'].Split('/')) {
|
|
74
|
+
if ($segment -eq '' -or $segment -eq '.' -or $segment -eq '..') {
|
|
75
|
+
return $false
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return $true
|
|
79
|
+
}
|
|
46
80
|
|
|
47
81
|
function Test-PrAuthorAllowlistForm {
|
|
48
82
|
<#
|
|
@@ -67,7 +101,11 @@ function Test-PrAuthorAllowlistForm {
|
|
|
67
101
|
}
|
|
68
102
|
switch -CaseSensitive ($Token[0]) {
|
|
69
103
|
'git' {
|
|
70
|
-
|
|
104
|
+
if ($Token.Count -ge 2 -and @('log', 'rev-parse') -ccontains $Token[1]) {
|
|
105
|
+
return $true
|
|
106
|
+
}
|
|
107
|
+
# The read-only worktree discovery form, matched exactly (issue #1011).
|
|
108
|
+
return ($Token.Count -eq 4 -and $Token[1] -ceq 'worktree' -and $Token[2] -ceq 'list' -and $Token[3] -ceq '--porcelain')
|
|
71
109
|
}
|
|
72
110
|
'gh' {
|
|
73
111
|
if ($Token.Count -ge 3 -and $Token[1] -ceq 'pr' -and @('create', 'edit') -ccontains $Token[2]) {
|
|
@@ -78,7 +116,10 @@ function Test-PrAuthorAllowlistForm {
|
|
|
78
116
|
$Token[4] -ceq '--json' -and $Token[5] -ceq 'closingIssuesReferences,body')
|
|
79
117
|
}
|
|
80
118
|
'sha256sum' {
|
|
81
|
-
|
|
119
|
+
if (-not $IsWholeCommand -or $Token.Count -ne 2) {
|
|
120
|
+
return $false
|
|
121
|
+
}
|
|
122
|
+
return ($Token[1] -cmatch '^artifacts/pr_body_\d+\.md$' -or (Test-PrAuthorAbsoluteBodyPathToken -Value $Token[1]))
|
|
82
123
|
}
|
|
83
124
|
'date' {
|
|
84
125
|
if (-not $IsWholeCommand -or $Token.Count -lt 2 -or $Token[1] -cne '-u') {
|
|
@@ -50,6 +50,8 @@ Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeTarge
|
|
|
50
50
|
# feature folder exists in every checkout branched from main, so it places a call in many at
|
|
51
51
|
# once. The item is identified by its canonical issue number and its branch instead.
|
|
52
52
|
Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeItemResolution.psm1') -Force -ErrorAction Stop
|
|
53
|
+
# Shared target-worktree resolver (issues #1009, #1011): resolves the branch a --head names.
|
|
54
|
+
Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/HookTargetWorktree.psm1') -Force -ErrorAction Stop
|
|
53
55
|
# Epic scope (issue #663): the epic integration pull request is gated against the epic checkpoint.
|
|
54
56
|
Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/EpicScopeResolution.psm1') -Force -ErrorAction Stop
|
|
55
57
|
Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/EpicScopeReadiness.psm1') -Force -ErrorAction Stop
|
|
@@ -70,10 +72,15 @@ function Resolve-PrAuthorWorktreeTarget {
|
|
|
70
72
|
directly would depend on the machine's live worktrees and would pass or fail
|
|
71
73
|
according to what other sessions happen to have checked out, which the repository's
|
|
72
74
|
determinism policy forbids. Mocking here lets a test state the resolution outcome it
|
|
73
|
-
is exercising and nothing else. A
|
|
74
|
-
|
|
75
|
+
is exercising and nothing else. A branch the command names (--head, --branch, or
|
|
76
|
+
branch:) is resolved by the shared resolver Resolve-HookTargetWorktree, given only the
|
|
77
|
+
session root and that branch (issue #1011), unless the command also carries a canonical
|
|
78
|
+
issue number line, which Resolve-WorktreeItemTarget decides so the branch/issue agreement
|
|
79
|
+
check applies. Without a selection, Resolve-WorktreeItemTarget runs, and a NoTarget result
|
|
80
|
+
for a command with no signal passes through Resolve-PrAuthorCurrentBranchTarget, which
|
|
81
|
+
re-resolves the current branch (#989).
|
|
75
82
|
.OUTPUTS
|
|
76
|
-
|
|
83
|
+
A target result object built by New-WorktreeResolutionTargetResult.
|
|
77
84
|
#>
|
|
78
85
|
[CmdletBinding()]
|
|
79
86
|
param(
|
|
@@ -81,10 +88,59 @@ function Resolve-PrAuthorWorktreeTarget {
|
|
|
81
88
|
[string] $CommandText
|
|
82
89
|
)
|
|
83
90
|
|
|
84
|
-
$
|
|
91
|
+
$sessionRoot = (Get-Location).Path
|
|
92
|
+
$branch = Find-WorktreeResolutionBranchSignal -Text $CommandText
|
|
93
|
+
$issueSignal = Find-WorktreeItemIssueSignal -Text $CommandText
|
|
94
|
+
if ($null -ne $branch -and @($issueSignal).Count -eq 0) {
|
|
95
|
+
$shared = Resolve-HookTargetWorktree -SessionRoot $sessionRoot -CommandBranch $branch
|
|
96
|
+
$mapped = ConvertFrom-PrAuthorHookTargetResult -Result $shared -CommandText $CommandText -SessionRoot $sessionRoot
|
|
97
|
+
if ($null -ne $mapped) { return $mapped }
|
|
98
|
+
}
|
|
99
|
+
$target = Resolve-WorktreeItemTarget -Text $CommandText -SessionRoot $sessionRoot
|
|
85
100
|
return (Resolve-PrAuthorCurrentBranchTarget -CommandText $CommandText -Target $target)
|
|
86
101
|
}
|
|
87
102
|
|
|
103
|
+
function ConvertFrom-PrAuthorHookTargetResult {
|
|
104
|
+
<#
|
|
105
|
+
.SYNOPSIS
|
|
106
|
+
Map a Resolve-HookTargetWorktree result onto a pr-author target result, or $null (issue #1011).
|
|
107
|
+
.DESCRIPTION
|
|
108
|
+
Returns $null (no selection) when Result is null, when CommandText carries no branch
|
|
109
|
+
signal, when Result.Source is not CommandOperand, when Result names no root, or when that
|
|
110
|
+
root fails Test-WorktreeResolutionRootMarker (a registration whose directory is gone).
|
|
111
|
+
Only a CommandOperand result selects, being branch-derived; a FilePath, SessionRoot, or
|
|
112
|
+
DelegationIdentity result never selects the worktree (issue #673). An ambiguous result
|
|
113
|
+
maps to Ambiguous. Labelling is delegated to ConvertTo-WorktreeItemResolvedResult, which
|
|
114
|
+
maps a root equal to SessionRoot, or to the worktree enclosing it, to SessionRoot and any
|
|
115
|
+
other root to OtherWorktree.
|
|
116
|
+
.PARAMETER Result
|
|
117
|
+
The Resolve-HookTargetWorktree result.
|
|
118
|
+
.PARAMETER CommandText
|
|
119
|
+
The Bash command text under evaluation.
|
|
120
|
+
.PARAMETER SessionRoot
|
|
121
|
+
The session root the result is compared with.
|
|
122
|
+
.OUTPUTS
|
|
123
|
+
System.Management.Automation.PSCustomObject or $null
|
|
124
|
+
#>
|
|
125
|
+
[CmdletBinding()]
|
|
126
|
+
[OutputType([pscustomobject])]
|
|
127
|
+
param(
|
|
128
|
+
[AllowNull()][object] $Result,
|
|
129
|
+
[Parameter(Mandatory)][AllowEmptyString()][string] $CommandText,
|
|
130
|
+
[Parameter(Mandatory)][string] $SessionRoot
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
$branch = Find-WorktreeResolutionBranchSignal -Text $CommandText
|
|
134
|
+
if ($null -eq $Result -or $null -eq $branch -or $Result.Source -cne 'CommandOperand') { return $null }
|
|
135
|
+
$detail = if ([string]::IsNullOrWhiteSpace([string]$Result.Detail)) { "branch '$branch' resolved by the shared resolver" } else { [string]$Result.Detail }
|
|
136
|
+
if ($Result.Ambiguous) {
|
|
137
|
+
return (New-WorktreeResolutionTargetResult -Status Ambiguous -SessionRoot $SessionRoot -Signal Branch -SignalValue $branch -Candidate @($Result.Candidates) -Detail $detail)
|
|
138
|
+
}
|
|
139
|
+
if ([string]::IsNullOrEmpty([string]$Result.WorktreeRoot)) { return $null }
|
|
140
|
+
if (-not (Test-WorktreeResolutionRootMarker -Path $Result.WorktreeRoot)) { return $null }
|
|
141
|
+
return (ConvertTo-WorktreeItemResolvedResult -WorktreeRoot $Result.WorktreeRoot -SessionRoot $SessionRoot -Branch $branch -Detail $detail)
|
|
142
|
+
}
|
|
143
|
+
|
|
88
144
|
function Get-PrAuthorTargetCheckpointResolution {
|
|
89
145
|
<#
|
|
90
146
|
.SYNOPSIS
|
package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.epic-base-branch.ps1
CHANGED
|
@@ -59,7 +59,9 @@ function Test-EpicBaseBranchOverride {
|
|
|
59
59
|
with reason EPIC_BASE_BRANCH_MISMATCH. When epic_mode is absent or false, when the
|
|
60
60
|
checkpoint is unreadable, or when the command is not gh pr create (the base branch is
|
|
61
61
|
set at create time, not edit time), this check is a no-op and standalone behavior is
|
|
62
|
-
unchanged.
|
|
62
|
+
unchanged. Only the JSON boolean true counts as epic mode: a string or number such as
|
|
63
|
+
"false", "true", or 1 is not epic mode, so the --base requirement does not apply
|
|
64
|
+
(issue #1001).
|
|
63
65
|
.PARAMETER CommandText
|
|
64
66
|
The Bash command text under evaluation.
|
|
65
67
|
.PARAMETER CheckpointPath
|
|
@@ -116,7 +118,7 @@ function Test-EpicBaseBranchOverride {
|
|
|
116
118
|
}
|
|
117
119
|
|
|
118
120
|
$checkpointProps = @($checkpoint.PSObject.Properties.Name)
|
|
119
|
-
if ($checkpointProps -notcontains 'epic_mode' -or -not [bool]$checkpoint.epic_mode) {
|
|
121
|
+
if ($checkpointProps -notcontains 'epic_mode' -or -not (($checkpoint.epic_mode -is [bool]) -and $checkpoint.epic_mode)) {
|
|
120
122
|
return $null
|
|
121
123
|
}
|
|
122
124
|
|