@danmoisan/drm-copilot-mcp 1.1.18 → 1.1.19
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 +989 -662
- package/package.json +3 -2
- package/resources/claude-customizations/.claude/agents/atomic-executor.md +11 -0
- package/resources/claude-customizations/.claude/agents/atomic-planner.md +10 -0
- package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +12 -4
- package/resources/claude-customizations/.claude/agents/epic-planner.md +5 -4
- package/resources/claude-customizations/.claude/agents/feature-review.md +6 -4
- package/resources/claude-customizations/.claude/agents/orchestrator.md +5 -4
- package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +31 -13
- package/resources/claude-customizations/.claude/agents/parallel-planner.md +9 -6
- package/resources/claude-customizations/.claude/agents/pr-author.md +15 -2
- package/resources/claude-customizations/.claude/agents/prd-feature.md +1 -1
- package/resources/claude-customizations/.claude/hooks/check-powershell-test-purity.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/check-python-test-purity.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +111 -46
- package/resources/claude-customizations/.claude/hooks/enforce-completion-helpers.ps1 +105 -0
- package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +8 -1
- package/resources/claude-customizations/.claude/hooks/enforce-epic-invocation-origin.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-resolution.ps1 +116 -59
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +55 -23
- package/resources/claude-customizations/.claude/hooks/enforce-epic-wave-barrier.ps1 +74 -75
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate-resolution.ps1 +127 -35
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +61 -57
- package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-feature-folder-order.ps1 +129 -39
- package/resources/claude-customizations/.claude/hooks/enforce-mermaid-validation.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +9 -3
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +39 -51
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +17 -12
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +9 -3
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier.ps1 +65 -50
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-drift-gate.ps1 +49 -42
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +54 -72
- package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +8 -2
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-command-allowlist.ps1 +279 -0
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +129 -75
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.artifact-root.ps1 +233 -0
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.docs-only.ps1 +224 -0
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +69 -24
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner-helpers.ps1 +29 -18
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +10 -4
- package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +14 -5
- package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +8 -2
- package/resources/claude-customizations/.claude/hooks/feature-folder-resolution.ps1 +373 -0
- package/resources/claude-customizations/.claude/hooks/feature-review-coverage-thresholds.ps1 +85 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-heredoc.ps1 +119 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-invocation-operands.ps1 +198 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-invocation.ps1 +254 -255
- package/resources/claude-customizations/.claude/hooks/hook-command-payload-powershell.ps1 +201 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-payload.ps1 +484 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-scanner.ps1 +27 -110
- package/resources/claude-customizations/.claude/hooks/hook-dependency-guard.ps1 +114 -0
- package/resources/claude-customizations/.claude/hooks/hook-docs-only-diff.ps1 +493 -0
- package/resources/claude-customizations/.claude/hooks/subagentstop-envelope.ps1 +192 -0
- package/resources/claude-customizations/.claude/hooks/validate-bash.ps1 +9 -3
- package/resources/claude-customizations/.claude/hooks/validate-discovery-artifact-gate.ps1 +40 -42
- package/resources/claude-customizations/.claude/hooks/validate-executor-output.ps1 +66 -38
- package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-metrics.ps1 +180 -0
- package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-rules.ps1 +195 -0
- package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage.ps1 +141 -395
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-resolution.ps1 +311 -0
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-routing.ps1 +170 -0
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +115 -190
- package/resources/claude-customizations/.claude/hooks/validate-planner-output.ps1 +64 -32
- package/resources/claude-customizations/.claude/hooks/validate-pr-author-output.ps1 +39 -44
- package/resources/claude-customizations/.claude/hooks/validate-prd-feature-output.ps1 +43 -14
- package/resources/claude-customizations/.claude/hooks/validate-required-artifact-output.ps1 +66 -36
- package/resources/claude-customizations/.claude/hooks/validate-task-researcher-output.ps1 +31 -34
- package/resources/claude-customizations/.claude/lib/bash/mutate-parallel-run.sh +191 -0
- package/resources/claude-customizations/.claude/lib/bash/parallel-cohorts.sh +13 -3
- package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-cli.sh +152 -0
- package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-ops.sh +193 -0
- package/resources/claude-customizations/.claude/lib/bash/parallel-mutation.sh +322 -0
- package/resources/claude-customizations/.claude/lib/bash/remove-parallel-item.sh +146 -0
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +23 -33
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusTokenShape.psm1 +86 -6
- package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-CiGateParser.ps1 +91 -19
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateEpicWaveBarrier.psm1 +346 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1 +24 -3
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +79 -2
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1 +3 -3
- package/resources/claude-customizations/.claude/rules/architecture-boundaries.md +5 -5
- package/resources/claude-customizations/.claude/rules/csharp.md +2 -2
- package/resources/claude-customizations/.claude/rules/general-code-change.md +1 -1
- package/resources/claude-customizations/.claude/rules/general-unit-test.md +3 -2
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +37 -4
- package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +50 -5
- package/resources/claude-customizations/.claude/rules/plan-acceptance-gates.md +47 -8
- package/resources/claude-customizations/.claude/rules/quality-tiers.md +11 -7
- package/resources/claude-customizations/.claude/rules/shell.md +10 -8
- package/resources/claude-customizations/.claude/rules/typescript.md +1 -1
- package/resources/claude-customizations/.claude/settings.json +3 -4
- package/resources/claude-customizations/.claude/skills/acceptance-criteria-tracking/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +5 -1
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_detached_lib.sh +1 -1
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_enumerate_lib.sh +2 -0
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_scan_helper.sh +2 -0
- package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +33 -6
- package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
- package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +4 -3
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +14 -6
- package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +52 -14
- package/resources/claude-customizations/.claude/skills/parallel-close/SKILL.md +22 -9
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +79 -16
- package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +63 -7
- package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +35 -12
- package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +3 -1
- package/resources/claude-customizations/.claude/skills/quota-throttling/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +2 -2
- package/resources/claude-customizations/pack-manifests/core.json +23 -0
- package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/architecture-boundaries/SKILL.md +5 -5
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-executor/SKILL.md +11 -0
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +4 -0
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +10 -0
- package/resources/codex-and-agents-customizations/.agents/skills/csharp/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.agents/skills/epic-orchestrate/SKILL.md +8 -0
- package/resources/codex-and-agents-customizations/.agents/skills/epic-plan/SKILL.md +17 -1
- package/resources/codex-and-agents-customizations/.agents/skills/epic-run/SKILL.md +2 -1
- package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
- package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +13 -5
- package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +3 -2
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +15 -6
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +11 -7
- package/resources/codex-and-agents-customizations/.agents/skills/translate-claude-to-codex/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c1.toml +11 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +11 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +11 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +11 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +11 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +11 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +10 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +10 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +10 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +10 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +10 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +10 -0
- package/resources/codex-and-agents-customizations/.codex/agents/epic-orchestrator.toml +2 -1
- package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +6 -6
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c1.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/codex-web-setup.sh +318 -273
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/check-powershell-test-purity.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/check-python-test-purity.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/codex-epic-child-launch-attestation.ps1 +1 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-codex-model-routing.ps1 +8 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +97 -44
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-helpers.ps1 +105 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-child-worktree-binding.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +37 -5
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +6 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-root-invocation.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-wave-barrier.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-worktree-removal-gate.ps1 +54 -41
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-evidence-locations.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +35 -14
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +24 -11
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +8 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +13 -4
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +8 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/feature-folder-resolution.ps1 +373 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/feature-review-coverage-thresholds.ps1 +85 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-heredoc.ps1 +119 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation-operands.ps1 +198 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation.ps1 +254 -255
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload-powershell.ps1 +201 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload.ps1 +484 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-scanner.ps1 +27 -110
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-dependency-guard.ps1 +114 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-docs-only-diff.ps1 +493 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-bash.ps1 +8 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-codex-subagent-routing.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-feature-review-coverage.ps1 +24 -8
- package/resources/codex-and-agents-customizations/pack-manifests/core.json +9 -0
- package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +2 -2
- package/resources/customizations/.github/instructions/csharp-code-change.instructions.md +4 -4
- package/resources/customizations/.github/instructions/csharp-unit-test.instructions.md +2 -2
- package/resources/customizations/.github/skills/acceptance-criteria-tracking/SKILL.md +1 -1
- package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +4 -0
- package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
- package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +1 -1
- package/resources/powershell/PoshQC/PoshQC.Coverage.psm1 +314 -0
- package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +25 -28
- package/resources/powershell/PoshQC/PoshQC.psm1 +1 -0
- package/resources/powershell/PoshQC/README.md +8 -2
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +2 -319
|
@@ -13,24 +13,29 @@
|
|
|
13
13
|
conflict_edges[], and position is read from the current cohort coloring.
|
|
14
14
|
|
|
15
15
|
Resolution and decision procedure:
|
|
16
|
-
1. Resolve the
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
1. Resolve the worktree whose parallel checkpoint records the prompt's
|
|
17
|
+
parallel_slug: value (issue #690) and deny an unresolved or ambiguous target.
|
|
18
|
+
2. Collect the cited feature-folder candidates with the shared resolver
|
|
19
|
+
(feature-folder-resolution.ps1, issue #565): every docs/features/active/<token>
|
|
20
|
+
path is truncated to its feature-folder segment, so a nested research/ or
|
|
21
|
+
evidence/ citation names its own folder. No candidate denies.
|
|
22
|
+
3. Read artifacts/orchestration/parallel-orchestrator-state.json beneath that
|
|
23
|
+
worktree and select the target among the candidates with
|
|
24
|
+
Select-FeatureFolderTarget. The canonical issue-number line, when it names exactly
|
|
25
|
+
one number, breaks a tie among the cited run items; two or more remaining
|
|
26
|
+
candidates otherwise deny as ambiguous. Neither string length nor prompt position
|
|
27
|
+
takes part in the selection. Then locate the items[] record whose feature_folder
|
|
28
|
+
resolves to the selected basename.
|
|
29
|
+
4. Project the cohort coloring to the cohorts[] rows whose generation equals the
|
|
25
30
|
top-level recolor_generation, and read the target item's cohort index from that
|
|
26
31
|
projection.
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
5. Collect every conflict_edges[] neighbor of the target item.
|
|
33
|
+
6. Deny with reason PARALLEL_COHORT_BARRIER_BLOCKED unless every neighbor that sits
|
|
29
34
|
in a strictly prior current-generation cohort has merge_status in
|
|
30
35
|
{merged, worktree_removed}. ci_green does NOT satisfy the barrier: an item whose
|
|
31
36
|
CI is green has not merged, so its worktree is still live and its contention is
|
|
32
37
|
unresolved. Same-cohort and later-cohort neighbors do not block Layer 1.
|
|
33
|
-
|
|
38
|
+
7. A missing or unparseable checkpoint, an unresolved feature-folder token, a
|
|
34
39
|
missing items[] record, a target with no current-generation cohort assignment, a
|
|
35
40
|
missing neighbor record, and a missing merge_status all deny (fail-closed).
|
|
36
41
|
|
|
@@ -43,24 +48,33 @@
|
|
|
43
48
|
SubagentStop time.
|
|
44
49
|
|
|
45
50
|
.NOTES
|
|
46
|
-
Compatible with PowerShell 7+. Depends on
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
Compatible with PowerShell 7+. Depends on WorktreeItemResolution.psm1 and
|
|
52
|
+
WorktreeRunResolution.psm1 (issue #690) and the pure sibling
|
|
53
|
+
feature-folder-resolution.ps1 (issue #565), each loaded inside a guard: the first
|
|
54
|
+
failure is recorded and the decision denies naming the dependency. Filesystem reads go
|
|
55
|
+
through an injectable wrapper function so tests can mock the boundary without writing
|
|
56
|
+
temporary files.
|
|
50
57
|
#>
|
|
51
58
|
[CmdletBinding()]
|
|
52
59
|
param()
|
|
60
|
+
$script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
|
|
53
61
|
|
|
54
62
|
|
|
55
|
-
Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force
|
|
63
|
+
try { Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'HookPayload.psm1' -ErrorRecord $_ }
|
|
56
64
|
|
|
57
|
-
|
|
65
|
+
try { Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeItemResolution.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'WorktreeItemResolution.psm1' -ErrorRecord $_ }
|
|
66
|
+
try { Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeRunResolution.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'WorktreeRunResolution.psm1' -ErrorRecord $_ }
|
|
58
67
|
$script:ParallelCohortBarrierResolutionImportFailure = $null
|
|
68
|
+
|
|
69
|
+
# Shared feature-folder resolution (issue #565). Guarded so a failed dot-source denies
|
|
70
|
+
# rather than failing open; an earlier recorded failure is kept.
|
|
59
71
|
try {
|
|
60
|
-
|
|
72
|
+
. (Join-Path $PSScriptRoot 'feature-folder-resolution.ps1')
|
|
61
73
|
}
|
|
62
74
|
catch {
|
|
63
|
-
$script:ParallelCohortBarrierResolutionImportFailure
|
|
75
|
+
if (-not $script:ParallelCohortBarrierResolutionImportFailure) {
|
|
76
|
+
$script:ParallelCohortBarrierResolutionImportFailure = 'feature-folder-resolution.ps1'
|
|
77
|
+
}
|
|
64
78
|
}
|
|
65
79
|
|
|
66
80
|
$script:AllowedMergeStatuses = @('merged', 'worktree_removed')
|
|
@@ -68,7 +82,7 @@ $script:ParallelModeMarker = 'Parallel mode: true'
|
|
|
68
82
|
|
|
69
83
|
# Dot-source the record-resolution and barrier helpers. Guarded so dot-sourcing this
|
|
70
84
|
# hook in tests loads the helpers too (issue #501 headroom split).
|
|
71
|
-
. (Join-Path $PSScriptRoot 'enforce-parallel-cohort-barrier-helpers.ps1')
|
|
85
|
+
try { . (Join-Path $PSScriptRoot 'enforce-parallel-cohort-barrier-helpers.ps1') } catch { Add-HookDependencyFailure -Name 'enforce-parallel-cohort-barrier-helpers.ps1' -ErrorRecord $_ }
|
|
72
86
|
|
|
73
87
|
function Get-ParallelCohortBarrierCheckpointContent {
|
|
74
88
|
<#
|
|
@@ -147,43 +161,27 @@ function Get-ParallelCohortBarrierFolderBasename {
|
|
|
147
161
|
function Find-ParallelCohortBarrierFeatureFolderFromPrompt {
|
|
148
162
|
<#
|
|
149
163
|
.SYNOPSIS
|
|
150
|
-
|
|
151
|
-
|
|
164
|
+
Returns the distinct feature-folder basenames cited in a prompt, in first-occurrence
|
|
165
|
+
order. Returns no output when no folder is cited.
|
|
152
166
|
.DESCRIPTION
|
|
153
|
-
|
|
154
|
-
path
|
|
155
|
-
|
|
167
|
+
Delegates to Find-FeatureFolderCandidate in the shared resolver (issue #565): each
|
|
168
|
+
docs/features/active/<token> path, in either separator style, is truncated to its
|
|
169
|
+
feature-folder segment, so a nested research/ or evidence/ citation names its own
|
|
170
|
+
folder. Selection among several candidates is made by Select-FeatureFolderTarget.
|
|
156
171
|
.PARAMETER Prompt
|
|
157
172
|
The delegation prompt text under evaluation.
|
|
158
173
|
.OUTPUTS
|
|
159
|
-
System.String
|
|
174
|
+
System.String[]
|
|
160
175
|
#>
|
|
161
176
|
[CmdletBinding()]
|
|
162
|
-
[OutputType([string])]
|
|
177
|
+
[OutputType([string[]])]
|
|
163
178
|
param(
|
|
164
179
|
[Parameter(Mandatory)]
|
|
165
180
|
[AllowEmptyString()]
|
|
166
181
|
[string] $Prompt
|
|
167
182
|
)
|
|
168
183
|
|
|
169
|
-
|
|
170
|
-
return $null
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
$pattern = 'docs[\\/]+features[\\/]+active[\\/]+[^\s"''`]+'
|
|
174
|
-
$matchList = [regex]::Matches($Prompt, $pattern)
|
|
175
|
-
if ($matchList.Count -eq 0) {
|
|
176
|
-
return $null
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
$unique = @{}
|
|
180
|
-
foreach ($m in $matchList) {
|
|
181
|
-
$normalized = ($m.Value -replace '\\', '/').TrimEnd('/')
|
|
182
|
-
$unique[$normalized] = $true
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
$candidates = @(@($unique.Keys) | Sort-Object -Property Length -Descending)
|
|
186
|
-
return (Get-ParallelCohortBarrierFolderBasename -FolderPath $candidates[0])
|
|
184
|
+
return [string[]]@(Find-FeatureFolderCandidate -Text $Prompt)
|
|
187
185
|
}
|
|
188
186
|
|
|
189
187
|
function Get-ParallelCohortBarrierAllowDecision {
|
|
@@ -230,11 +228,13 @@ function Invoke-ParallelCohortBarrierDecision {
|
|
|
230
228
|
param(
|
|
231
229
|
[string] $ToolInputRaw
|
|
232
230
|
)
|
|
231
|
+
$dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'PARALLEL_COHORT_BARRIER_BLOCKED:'
|
|
232
|
+
if ($null -ne $dependencyDecision) { return $dependencyDecision }
|
|
233
233
|
|
|
234
|
-
# A failed
|
|
234
|
+
# A failed dependency import denies before any other logic (issue #565).
|
|
235
235
|
if ($script:ParallelCohortBarrierResolutionImportFailure) {
|
|
236
236
|
return Get-ParallelCohortBarrierBlockDecision -Reason (
|
|
237
|
-
"PARALLEL_COHORT_BARRIER_BLOCKED: the
|
|
237
|
+
"PARALLEL_COHORT_BARRIER_BLOCKED: the dependency '$($script:ParallelCohortBarrierResolutionImportFailure)' " +
|
|
238
238
|
'failed to import, so the parallel checkpoint that governs this delegation cannot be located; the gate fails closed.')
|
|
239
239
|
}
|
|
240
240
|
|
|
@@ -262,8 +262,8 @@ function Invoke-ParallelCohortBarrierDecision {
|
|
|
262
262
|
return Get-ParallelCohortBarrierBlockDecision -Reason "PARALLEL_COHORT_BARRIER_BLOCKED: $($target.ReasonCode): $($target.Detail)"
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
-
$
|
|
266
|
-
if (-
|
|
265
|
+
$candidates = @(Find-ParallelCohortBarrierFeatureFolderFromPrompt -Prompt $prompt)
|
|
266
|
+
if ($candidates.Count -eq 0) {
|
|
267
267
|
return Get-ParallelCohortBarrierBlockDecision -Reason 'PARALLEL_COHORT_BARRIER_BLOCKED: a parallel-mode orchestrator delegation must reference the target item feature folder path (docs/features/active/<folder>) in the prompt so its conflict edges and cohort position can be verified.'
|
|
268
268
|
}
|
|
269
269
|
|
|
@@ -277,6 +277,18 @@ function Invoke-ParallelCohortBarrierDecision {
|
|
|
277
277
|
}
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
+
# The target is selected among the cited candidates (issue #565). The canonical
|
|
281
|
+
# issue-number line breaks a tie only when it names exactly one number.
|
|
282
|
+
$records = if ($null -ne $checkpoint -and @($checkpoint.PSObject.Properties.Name) -contains 'items') { @($checkpoint.items) } else { @() }
|
|
283
|
+
$selectionArguments = @{ Records = $records; Candidate = $candidates }
|
|
284
|
+
$declared = Find-WorktreeItemIssueSignal -Text $prompt
|
|
285
|
+
if (@($declared).Count -eq 1) { $selectionArguments['DeclaredIssueNumber'] = [int]@($declared)[0] }
|
|
286
|
+
$selection = Select-FeatureFolderTarget @selectionArguments
|
|
287
|
+
if ($selection.Status -eq 'Ambiguous') {
|
|
288
|
+
return Get-ParallelCohortBarrierBlockDecision -Reason "PARALLEL_COHORT_BARRIER_BLOCKED: $($selection.Detail)."
|
|
289
|
+
}
|
|
290
|
+
$featureFolder = $selection.Basename
|
|
291
|
+
|
|
280
292
|
$itemRecord = Find-ParallelCohortBarrierItemRecord -Checkpoint $checkpoint -FeatureFolder $featureFolder
|
|
281
293
|
if (Test-ParallelCohortBarrierClear -Checkpoint $checkpoint -ItemRecord $itemRecord) {
|
|
282
294
|
return Get-ParallelCohortBarrierAllowDecision
|
|
@@ -327,5 +339,8 @@ function Invoke-ParallelCohortBarrierEntryPoint {
|
|
|
327
339
|
if ($MyInvocation.InvocationName -eq '.') {
|
|
328
340
|
return
|
|
329
341
|
}
|
|
342
|
+
if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('PARALLEL_COHORT_BARRIER_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
|
|
343
|
+
$dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'PARALLEL_COHORT_BARRIER_BLOCKED:'
|
|
344
|
+
if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
|
|
330
345
|
|
|
331
346
|
exit (Invoke-ParallelCohortBarrierEntryPoint)
|
|
@@ -8,13 +8,14 @@
|
|
|
8
8
|
parallel-mode kickoff marker defined in the "Parallel-Mode Kickoff Parameter" section
|
|
9
9
|
of .claude/skills/parallel-orchestrate/SKILL.md, matched byte-for-byte (ordinal).
|
|
10
10
|
|
|
11
|
-
Decision procedure:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
Decision procedure: collect the cited feature-folder candidates with the shared resolver
|
|
12
|
+
(feature-folder-resolution.ps1, issue #565), which truncates every docs/features/active/
|
|
13
|
+
path to its feature-folder segment; read the parallel checkpoint and select the target
|
|
14
|
+
among the candidates with Select-FeatureFolderTarget, using the canonical issue-number
|
|
15
|
+
line as a tie-break when it names exactly one number and denying an unresolved tie as
|
|
16
|
+
ambiguous; locate the items[] record whose feature_folder basename matches the selected
|
|
17
|
+
basename, which is also the folder the finding probe inspects; derive which item keys
|
|
18
|
+
have an unresolved latest drift event; deny with PARALLEL_DRIFT_GATE_BLOCKED when the resolved item is one of them
|
|
18
19
|
and no synthetic Blocking finding file dated at or after that item's latest drift event has
|
|
19
20
|
been written. Allowed: a non-feature-review target, a prompt without the marker, a resolved
|
|
20
21
|
latest event, and an unresolved event whose finding file is dated at or after it, so the
|
|
@@ -50,8 +51,10 @@
|
|
|
50
51
|
ambiguous target denies after the subagent and marker filter and before the folder check.
|
|
51
52
|
|
|
52
53
|
.NOTES
|
|
53
|
-
PowerShell 7+. Depends on
|
|
54
|
-
|
|
54
|
+
PowerShell 7+. Depends on WorktreeItemResolution.psm1 and WorktreeRunResolution.psm1
|
|
55
|
+
(issue #690) and the pure sibling feature-folder-resolution.ps1 (issue #565), each
|
|
56
|
+
loaded inside a guard: the first failure is recorded and the decision denies naming
|
|
57
|
+
the dependency. Both read
|
|
55
58
|
boundaries -- the checkpoint read and the finding-file existence check -- are injectable
|
|
56
59
|
wrapper functions, so tests mock them without writing temporary files.
|
|
57
60
|
|
|
@@ -65,23 +68,27 @@
|
|
|
65
68
|
#>
|
|
66
69
|
[CmdletBinding()]
|
|
67
70
|
param()
|
|
71
|
+
$script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
|
|
68
72
|
|
|
69
73
|
|
|
70
|
-
Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force
|
|
74
|
+
try { Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'HookPayload.psm1' -ErrorRecord $_ }
|
|
71
75
|
|
|
72
|
-
|
|
76
|
+
try { Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeItemResolution.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'WorktreeItemResolution.psm1' -ErrorRecord $_ }
|
|
77
|
+
try { Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeRunResolution.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'WorktreeRunResolution.psm1' -ErrorRecord $_ }
|
|
73
78
|
$script:ParallelDriftGateResolutionImportFailure = $null
|
|
79
|
+
|
|
80
|
+
# Shared feature-folder resolution (issue #565). Guarded so a failed dot-source denies
|
|
81
|
+
# rather than failing open; an earlier recorded failure is kept.
|
|
74
82
|
try {
|
|
75
|
-
|
|
83
|
+
. (Join-Path $PSScriptRoot 'feature-folder-resolution.ps1')
|
|
76
84
|
}
|
|
77
85
|
catch {
|
|
78
|
-
$script:ParallelDriftGateResolutionImportFailure = '
|
|
86
|
+
if (-not $script:ParallelDriftGateResolutionImportFailure) { $script:ParallelDriftGateResolutionImportFailure = 'feature-folder-resolution.ps1' }
|
|
79
87
|
}
|
|
80
88
|
|
|
81
89
|
# Dot-source the shape-and-derivation helpers. Guarded so a missing file produces a clear error
|
|
82
90
|
# and so dot-sourcing this hook in tests loads the helpers too.
|
|
83
|
-
|
|
84
|
-
. $script:ParallelDriftGateHelpersPath
|
|
91
|
+
try { . (Join-Path $PSScriptRoot 'enforce-parallel-drift-gate-helpers.ps1') } catch { Add-HookDependencyFailure -Name 'enforce-parallel-drift-gate-helpers.ps1' -ErrorRecord $_ }
|
|
85
92
|
|
|
86
93
|
$script:ParallelModeMarker = 'Parallel mode: true'
|
|
87
94
|
$script:ReviewSubagentType = 'feature-review'
|
|
@@ -209,32 +216,16 @@ function Test-ParallelDriftFindingPresent {
|
|
|
209
216
|
function Find-ParallelDriftGateFeatureFolderFromPrompt {
|
|
210
217
|
<#
|
|
211
218
|
.SYNOPSIS
|
|
212
|
-
|
|
213
|
-
|
|
219
|
+
Return the distinct feature-folder basenames cited in a delegation prompt, in
|
|
220
|
+
first-occurrence order, through the shared Find-FeatureFolderCandidate (issue #565).
|
|
221
|
+
A nested research/ or evidence/ citation names its own folder. No output when no
|
|
222
|
+
folder is cited; selection among several is made by Select-FeatureFolderTarget.
|
|
214
223
|
#>
|
|
215
224
|
[CmdletBinding()]
|
|
216
|
-
[OutputType([string])]
|
|
225
|
+
[OutputType([string[]])]
|
|
217
226
|
param([Parameter(Mandatory)][AllowEmptyString()][string] $Prompt)
|
|
218
227
|
|
|
219
|
-
|
|
220
|
-
return $null
|
|
221
|
-
}
|
|
222
|
-
$matchList = [regex]::Matches($Prompt, 'docs[\\/]+features[\\/]+active[\\/]+[^\s"''`]+')
|
|
223
|
-
if ($matchList.Count -eq 0) {
|
|
224
|
-
return $null
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
# Collect distinct normalized tokens so the longest, most specific one can be selected.
|
|
228
|
-
$unique = @{}
|
|
229
|
-
foreach ($found in $matchList) {
|
|
230
|
-
$unique[($found.Value -replace '\\', '/').TrimEnd('/')] = $true
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
$best = @(@($unique.Keys) | Sort-Object -Property Length -Descending)[0]
|
|
234
|
-
if ($best -match '\.md$') {
|
|
235
|
-
$best = $best -replace '/[^/]+\.md$', ''
|
|
236
|
-
}
|
|
237
|
-
return ($best -split '/')[-1]
|
|
228
|
+
return [string[]]@(Find-FeatureFolderCandidate -Text $Prompt)
|
|
238
229
|
}
|
|
239
230
|
|
|
240
231
|
function Find-ParallelDriftGateItemRecord {
|
|
@@ -318,11 +309,13 @@ function Invoke-ParallelDriftGateDecision {
|
|
|
318
309
|
[AllowEmptyString()]
|
|
319
310
|
[string] $ToolInputRaw
|
|
320
311
|
)
|
|
312
|
+
$dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'PARALLEL_DRIFT_GATE_BLOCKED:'
|
|
313
|
+
if ($null -ne $dependencyDecision) { return $dependencyDecision }
|
|
321
314
|
|
|
322
|
-
# A failed
|
|
315
|
+
# A failed dependency import denies before any other logic (issue #565).
|
|
323
316
|
if ($script:ParallelDriftGateResolutionImportFailure) {
|
|
324
317
|
return Get-ParallelDriftGateBlockDecision -Reason (
|
|
325
|
-
"PARALLEL_DRIFT_GATE_BLOCKED: the
|
|
318
|
+
"PARALLEL_DRIFT_GATE_BLOCKED: the dependency '$($script:ParallelDriftGateResolutionImportFailure)' " +
|
|
326
319
|
'failed to import, so the parallel checkpoint that governs this review cannot be located; the gate fails closed.')
|
|
327
320
|
}
|
|
328
321
|
|
|
@@ -352,8 +345,8 @@ function Invoke-ParallelDriftGateDecision {
|
|
|
352
345
|
return Get-ParallelDriftGateBlockDecision -Reason "PARALLEL_DRIFT_GATE_BLOCKED: $($target.ReasonCode): $($target.Detail)"
|
|
353
346
|
}
|
|
354
347
|
|
|
355
|
-
$
|
|
356
|
-
if (-
|
|
348
|
+
$candidates = @(Find-ParallelDriftGateFeatureFolderFromPrompt -Prompt $prompt)
|
|
349
|
+
if ($candidates.Count -eq 0) {
|
|
357
350
|
return Get-ParallelDriftGateBlockDecision -Reason 'PARALLEL_DRIFT_GATE_BLOCKED: a parallel-mode feature-review delegation must reference the target item feature folder in the prompt so its drift state can be verified.'
|
|
358
351
|
}
|
|
359
352
|
|
|
@@ -367,8 +360,19 @@ function Invoke-ParallelDriftGateDecision {
|
|
|
367
360
|
}
|
|
368
361
|
}
|
|
369
362
|
if ($null -eq $checkpoint) {
|
|
370
|
-
return Get-ParallelDriftGateBlockDecision -Reason "PARALLEL_DRIFT_GATE_BLOCKED: the parallel checkpoint 'artifacts/orchestration/parallel-orchestrator-state.json' in worktree '$($target.WorktreeRoot)' is missing or unreadable, so the drift state of '$
|
|
363
|
+
return Get-ParallelDriftGateBlockDecision -Reason "PARALLEL_DRIFT_GATE_BLOCKED: the parallel checkpoint 'artifacts/orchestration/parallel-orchestrator-state.json' in worktree '$($target.WorktreeRoot)' is missing or unreadable, so the drift state of '$($candidates -join ', ')' cannot be verified."
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
# The target is selected among the cited candidates (issue #565). The canonical
|
|
367
|
+
# issue-number line breaks a tie only when it names exactly one number.
|
|
368
|
+
$selectionArguments = @{ Records = @($checkpoint.items | Where-Object { $null -ne $_ }); Candidate = $candidates }
|
|
369
|
+
$declared = Find-WorktreeItemIssueSignal -Text $prompt
|
|
370
|
+
if (@($declared).Count -eq 1) { $selectionArguments['DeclaredIssueNumber'] = [int]@($declared)[0] }
|
|
371
|
+
$selection = Select-FeatureFolderTarget @selectionArguments
|
|
372
|
+
if ($selection.Status -eq 'Ambiguous') {
|
|
373
|
+
return Get-ParallelDriftGateBlockDecision -Reason "PARALLEL_DRIFT_GATE_BLOCKED: $($selection.Detail)."
|
|
371
374
|
}
|
|
375
|
+
$featureFolder = $selection.Basename
|
|
372
376
|
|
|
373
377
|
$item = Find-ParallelDriftGateItemRecord -Checkpoint $checkpoint -FeatureFolder $featureFolder
|
|
374
378
|
if ($null -eq $item -or -not (Test-ParallelDriftGateItemKey -Value $item.issue_num)) {
|
|
@@ -440,5 +444,8 @@ function Invoke-ParallelDriftGateEntryPoint {
|
|
|
440
444
|
if ($MyInvocation.InvocationName -eq '.') {
|
|
441
445
|
return
|
|
442
446
|
}
|
|
447
|
+
if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('PARALLEL_DRIFT_GATE_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
|
|
448
|
+
$dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'PARALLEL_DRIFT_GATE_BLOCKED:'
|
|
449
|
+
if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
|
|
443
450
|
|
|
444
451
|
exit (Invoke-ParallelDriftGateEntryPoint)
|
package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1
CHANGED
|
@@ -32,27 +32,21 @@
|
|
|
32
32
|
#>
|
|
33
33
|
[CmdletBinding()]
|
|
34
34
|
param()
|
|
35
|
+
$script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
|
|
35
36
|
|
|
36
37
|
|
|
37
|
-
Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force
|
|
38
|
+
try { Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'HookPayload.psm1' -ErrorRecord $_ }
|
|
38
39
|
# Sanctioned-removal manifest reader (issue #635), consumed by the manifest branch in
|
|
39
40
|
# Invoke-ParallelWorktreeRemovalGateDecision. Same module the epic gate imports.
|
|
40
|
-
Import-Module (Join-Path $PSScriptRoot '../lib/cleanup-manifest/CleanupWorktreeManifest.psm1') -Force
|
|
41
|
+
try { Import-Module (Join-Path $PSScriptRoot '../lib/cleanup-manifest/CleanupWorktreeManifest.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'CleanupWorktreeManifest.psm1' -ErrorRecord $_ }
|
|
41
42
|
# Shared command-line parser (issue #545), consumed by the scope filter in
|
|
42
|
-
# Invoke-ParallelWorktreeRemovalGateDecision and by
|
|
43
|
+
# Invoke-ParallelWorktreeRemovalGateDecision and by Resolve-CommandLineInvocationTarget.
|
|
43
44
|
# Both call sites are byte-for-byte the calls the epic gate makes, so the duplicated
|
|
44
45
|
# concern now has one implementation.
|
|
45
|
-
. (Join-Path $PSScriptRoot 'hook-command-scanner.ps1')
|
|
46
|
-
. (Join-Path $PSScriptRoot 'hook-command-invocation.ps1')
|
|
46
|
+
try { . (Join-Path $PSScriptRoot 'hook-command-scanner.ps1') } catch { Add-HookDependencyFailure -Name 'hook-command-scanner.ps1' -ErrorRecord $_ }
|
|
47
|
+
try { . (Join-Path $PSScriptRoot 'hook-command-invocation.ps1') } catch { Add-HookDependencyFailure -Name 'hook-command-invocation.ps1' -ErrorRecord $_ }
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
$script:ParallelWorktreeGateResolutionImportFailure = $null
|
|
50
|
-
try {
|
|
51
|
-
Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeRunResolution.psm1') -Force -ErrorAction Stop
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
$script:ParallelWorktreeGateResolutionImportFailure = 'WorktreeRunResolution.psm1'
|
|
55
|
-
}
|
|
49
|
+
try { Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeRunResolution.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'WorktreeRunResolution.psm1' -ErrorRecord $_ }
|
|
56
50
|
|
|
57
51
|
$script:AllowedMergeStatuses = @('merged', 'worktree_removed')
|
|
58
52
|
# Sanctioned-removal manifest location. Recorded here so every hook-read document this
|
|
@@ -164,52 +158,6 @@ function Read-ParallelWorktreeGateRunCheckpoint {
|
|
|
164
158
|
}
|
|
165
159
|
}
|
|
166
160
|
|
|
167
|
-
function Get-ParallelWorktreeRemovalCommandPath {
|
|
168
|
-
<#
|
|
169
|
-
.SYNOPSIS
|
|
170
|
-
Extract the target worktree path argument from a git worktree remove command.
|
|
171
|
-
.DESCRIPTION
|
|
172
|
-
The operand comes from the segment that structurally invokes git worktree remove,
|
|
173
|
-
so a 'cd <path> &&' segment chained before the removal contributes nothing and a
|
|
174
|
-
quoted mention of the phrase resolves to no operand at all. Quotes around the path
|
|
175
|
-
are already stripped by the tokenizer.
|
|
176
|
-
|
|
177
|
-
'--force' is a zero-argument flag, so it never contributes an operand and may be
|
|
178
|
-
written on either side of the target path. Its presence is read structurally through
|
|
179
|
-
Test-CommandLineFlag rather than by searching the raw text, so a '--force' spelling
|
|
180
|
-
that appears inside an unrelated quoted argument cannot change how the operand list
|
|
181
|
-
is read. When no operand resolves, a present '--force' is reported in its place, so
|
|
182
|
-
the checkpoint lookup fails closed on a value that matches no recorded worktree_path
|
|
183
|
-
- the same value the previous raw-text pattern returned for that input.
|
|
184
|
-
|
|
185
|
-
This body is identical to Get-EpicWorktreeRemovalCommandPath in
|
|
186
|
-
enforce-epic-worktree-removal-gate.ps1. The two gates fire on the same command and
|
|
187
|
-
now delegate the shared concern to one parser rather than to two patterns that had
|
|
188
|
-
already diverged from the Codex copy.
|
|
189
|
-
.PARAMETER CommandText
|
|
190
|
-
The Bash command text under evaluation.
|
|
191
|
-
.OUTPUTS
|
|
192
|
-
System.String or $null
|
|
193
|
-
#>
|
|
194
|
-
[CmdletBinding()]
|
|
195
|
-
[OutputType([string])]
|
|
196
|
-
param(
|
|
197
|
-
[Parameter(Mandatory)]
|
|
198
|
-
[string] $CommandText
|
|
199
|
-
)
|
|
200
|
-
|
|
201
|
-
$hasForce = Test-CommandLineFlag -CommandText $CommandText -CommandWord 'git' -SubcommandPath @('worktree', 'remove') -FlagName '--force'
|
|
202
|
-
$operands = @(Get-CommandLineOperand -CommandText $CommandText -CommandWord 'git' -SubcommandPath @('worktree', 'remove'))
|
|
203
|
-
|
|
204
|
-
if ($operands.Count -gt 0) {
|
|
205
|
-
return $operands[0]
|
|
206
|
-
}
|
|
207
|
-
if ($hasForce) {
|
|
208
|
-
return '--force'
|
|
209
|
-
}
|
|
210
|
-
return $null
|
|
211
|
-
}
|
|
212
|
-
|
|
213
161
|
function Find-ParallelWorktreeItemRecord {
|
|
214
162
|
<#
|
|
215
163
|
.SYNOPSIS
|
|
@@ -334,13 +282,8 @@ function Invoke-ParallelWorktreeRemovalGateDecision {
|
|
|
334
282
|
param(
|
|
335
283
|
[string] $ToolInputRaw
|
|
336
284
|
)
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
if ($script:ParallelWorktreeGateResolutionImportFailure) {
|
|
340
|
-
return Get-ParallelWorktreeGateBlockDecision -Reason (
|
|
341
|
-
"PARALLEL_WORKTREE_REMOVAL_BLOCKED: the worktree-resolution module '$($script:ParallelWorktreeGateResolutionImportFailure)' " +
|
|
342
|
-
'failed to import, so the run checkpoint that governs this removal cannot be located; the gate fails closed.')
|
|
343
|
-
}
|
|
285
|
+
$dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'PARALLEL_WORKTREE_REMOVAL_BLOCKED:'
|
|
286
|
+
if ($null -ne $dependencyDecision) { return $dependencyDecision }
|
|
344
287
|
|
|
345
288
|
$payload = Resolve-ClaudeHookToolInput -Raw $ToolInputRaw
|
|
346
289
|
if (-not $payload.IsValid) {
|
|
@@ -362,7 +305,43 @@ function Invoke-ParallelWorktreeRemovalGateDecision {
|
|
|
362
305
|
return Get-ParallelWorktreeGateAllowDecision
|
|
363
306
|
}
|
|
364
307
|
|
|
365
|
-
|
|
308
|
+
# Issue #824: the removal targets are derived structurally from every invocation.
|
|
309
|
+
# A target that cannot be derived denies before any checkpoint is read, and every
|
|
310
|
+
# derived target must be authorized on its own.
|
|
311
|
+
$resolution = Resolve-CommandLineInvocationTarget -CommandText $commandText -CommandWord 'git' -SubcommandPath @('worktree', 'remove')
|
|
312
|
+
if ($resolution.Status -eq 'NoMatch') {
|
|
313
|
+
return Get-ParallelWorktreeGateAllowDecision
|
|
314
|
+
}
|
|
315
|
+
if ($resolution.Status -ne 'Targets') {
|
|
316
|
+
return Get-ParallelWorktreeGateBlockDecision -Reason 'PARALLEL_WORKTREE_REMOVAL_BLOCKED: TARGET_WORKTREE_NOT_DERIVABLE: the git worktree remove target cannot be derived from the command text. No checkpoint can authorize this removal.'
|
|
317
|
+
}
|
|
318
|
+
foreach ($target in @($resolution.Targets)) {
|
|
319
|
+
$denial = Get-ParallelWorktreeRemovalTargetDenial -WorktreePath $target
|
|
320
|
+
if ($null -ne $denial) {
|
|
321
|
+
return $denial
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return Get-ParallelWorktreeGateAllowDecision
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function Get-ParallelWorktreeRemovalTargetDenial {
|
|
328
|
+
<#
|
|
329
|
+
.SYNOPSIS
|
|
330
|
+
Evaluate one derived removal target and return its deny decision, or $null when authorized.
|
|
331
|
+
.DESCRIPTION
|
|
332
|
+
Holds the per-path authorization cascade (parallel checkpoint, epic checkpoint,
|
|
333
|
+
sanctioned-removal manifest) for a single worktree path (issue #824).
|
|
334
|
+
.PARAMETER WorktreePath
|
|
335
|
+
One removal target derived by Resolve-CommandLineInvocationTarget.
|
|
336
|
+
.OUTPUTS
|
|
337
|
+
System.Collections.Specialized.OrderedDictionary or $null
|
|
338
|
+
#>
|
|
339
|
+
[CmdletBinding()]
|
|
340
|
+
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
341
|
+
param(
|
|
342
|
+
[Parameter(Mandatory)]
|
|
343
|
+
[string] $WorktreePath
|
|
344
|
+
)
|
|
366
345
|
|
|
367
346
|
# Each run checkpoint is read beneath the worktree that records this path (issue #690),
|
|
368
347
|
# parallel kind first; an ambiguous target denies before any allow or manifest check.
|
|
@@ -377,7 +356,7 @@ function Invoke-ParallelWorktreeRemovalGateDecision {
|
|
|
377
356
|
|
|
378
357
|
$itemRecord = Find-ParallelWorktreeItemRecord -Checkpoint $checkpoint -WorktreePath $worktreePath
|
|
379
358
|
if (Test-ParallelWorktreeRemovalAllowed -ItemRecord $itemRecord) {
|
|
380
|
-
return
|
|
359
|
+
return $null
|
|
381
360
|
}
|
|
382
361
|
|
|
383
362
|
# Epic-authorization branch (issue #688). It runs after the parallel allow above and
|
|
@@ -394,7 +373,7 @@ function Invoke-ParallelWorktreeRemovalGateDecision {
|
|
|
394
373
|
|
|
395
374
|
$featureRecord = Find-ParallelWorktreeItemRecord -Checkpoint $epicCheckpoint -WorktreePath $worktreePath -RecordArrayName 'features'
|
|
396
375
|
if (Test-ParallelWorktreeRemovalAllowed -ItemRecord $featureRecord) {
|
|
397
|
-
return
|
|
376
|
+
return $null
|
|
398
377
|
}
|
|
399
378
|
|
|
400
379
|
# Sanctioned-removal manifest branch (issue #635). It runs last, so a
|
|
@@ -409,15 +388,15 @@ function Invoke-ParallelWorktreeRemovalGateDecision {
|
|
|
409
388
|
# only; the epic gate covers the features array with its own exclusion.
|
|
410
389
|
if (-not (Test-CleanupManifestCheckpointCoversPath -Checkpoint $checkpoint -RecordArrayName 'items' -WorktreePath $worktreePath) -and
|
|
411
390
|
(Test-CleanupWorktreeManifestAuthorizesRemoval -WorktreePath $worktreePath)) {
|
|
412
|
-
return
|
|
391
|
+
return $null
|
|
413
392
|
}
|
|
414
393
|
|
|
415
394
|
# When neither kind resolved, the deny names the resolution reason first (issue #690).
|
|
416
395
|
$prefix = ''
|
|
417
396
|
if ($parallelRead.Target.Status -eq 'NoTarget' -and $epicRead.Target.Status -eq 'NoTarget') {
|
|
418
|
-
$prefix = "
|
|
397
|
+
$prefix = "$($parallelRead.Target.ReasonCode): $($parallelRead.Target.Detail). "
|
|
419
398
|
}
|
|
420
|
-
return Get-ParallelWorktreeGateBlockDecision -Reason ($prefix + "
|
|
399
|
+
return Get-ParallelWorktreeGateBlockDecision -Reason ('PARALLEL_WORKTREE_REMOVAL_BLOCKED: ' + $prefix + "git worktree remove for '$worktreePath' requires a matching parallel checkpoint items[] record with merge_status in {merged, worktree_removed}. The checkpoint was unreadable, no matching record was found, or merge_status was not yet safe for removal.")
|
|
421
400
|
}
|
|
422
401
|
|
|
423
402
|
function Invoke-ParallelWorktreeRemovalGateEntryPoint {
|
|
@@ -462,6 +441,9 @@ function Invoke-ParallelWorktreeRemovalGateEntryPoint {
|
|
|
462
441
|
if ($MyInvocation.InvocationName -eq '.') {
|
|
463
442
|
return
|
|
464
443
|
}
|
|
444
|
+
if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('PARALLEL_WORKTREE_REMOVAL_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
|
|
445
|
+
$dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'PARALLEL_WORKTREE_REMOVAL_BLOCKED:'
|
|
446
|
+
if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
|
|
465
447
|
|
|
466
448
|
# The entry point returns its [int] exit code as the last pipeline element and the
|
|
467
449
|
# decision JSON before it. `exit (<call>)` would capture BOTH into the exit
|
|
@@ -57,10 +57,11 @@
|
|
|
57
57
|
#>
|
|
58
58
|
[CmdletBinding()]
|
|
59
59
|
param()
|
|
60
|
+
$script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
|
|
60
61
|
|
|
61
62
|
|
|
62
|
-
Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force
|
|
63
|
-
. (Join-Path $PSScriptRoot 'enforce-batch-budget-route.ps1')
|
|
63
|
+
try { Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'HookPayload.psm1' -ErrorRecord $_ }
|
|
64
|
+
try { . (Join-Path $PSScriptRoot 'enforce-batch-budget-route.ps1') } catch { Add-HookDependencyFailure -Name 'enforce-batch-budget-route.ps1' -ErrorRecord $_ }
|
|
64
65
|
|
|
65
66
|
function Test-PowerShellBatchBudgetPathInRoot {
|
|
66
67
|
<#
|
|
@@ -277,6 +278,8 @@ function Invoke-PowerShellBatchBudgetDecision {
|
|
|
277
278
|
[AllowEmptyString()]
|
|
278
279
|
[string] $ObservedRoute = ''
|
|
279
280
|
)
|
|
281
|
+
$dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'POWERSHELL_LARGE_PATH_REQUIRED:'
|
|
282
|
+
if ($null -ne $dependencyDecision) { return $dependencyDecision }
|
|
280
283
|
|
|
281
284
|
$normalized = $FilePath -replace '\\', '/'
|
|
282
285
|
if ($normalized -notmatch '\.(ps1|psm1|psd1)$') {
|
|
@@ -474,6 +477,9 @@ function Invoke-PowerShellBatchBudgetEntryPoint {
|
|
|
474
477
|
if ($MyInvocation.InvocationName -eq '.') {
|
|
475
478
|
return
|
|
476
479
|
}
|
|
480
|
+
if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('POWERSHELL_LARGE_PATH_REQUIRED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
|
|
481
|
+
$dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'POWERSHELL_LARGE_PATH_REQUIRED:'
|
|
482
|
+
if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
|
|
477
483
|
|
|
478
484
|
# The entry point returns its [int] exit code as the last pipeline element and the
|
|
479
485
|
# decision JSON before it. `exit (<call>)` would capture BOTH into the exit
|