@danmoisan/drm-copilot-mcp 1.1.22 → 1.1.24
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 +18758 -12030
- package/package.json +1 -1
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/MEMORY.md +1 -1
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_test_files_count_against_500_cap.md +4 -4
- package/resources/claude-customizations/.claude/agents/csharp-typed-engineer.md +1 -1
- package/resources/claude-customizations/.claude/agents/discovery-capability-sentinel.md +34 -0
- package/resources/claude-customizations/.claude/agents/feature-review.md +8 -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/orchestrator.md +1 -1
- package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +60 -6
- package/resources/claude-customizations/.claude/agents/parallel-planner.md +21 -1
- package/resources/claude-customizations/.claude/agents/powershell-typed-engineer.md +1 -1
- package/resources/claude-customizations/.claude/agents/pr-author.md +48 -7
- package/resources/claude-customizations/.claude/agents/python-typed-engineer.md +1 -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-green-ci.ps1 +325 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +29 -7
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate-resolution.ps1 +23 -10
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +29 -21
- package/resources/claude-customizations/.claude/hooks/enforce-file-size-limit.ps1 +244 -0
- package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +51 -0
- 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-parallel-worktree-removal-gate.ps1 +22 -11
- 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 +51 -4
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +72 -11
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.current-branch.ps1 +149 -0
- 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-feature-folder.ps1 +146 -0
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-rejection.ps1 +172 -0
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +34 -4
- 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/cleanup-manifest/CleanupWorktreeManifest.psm1 +33 -6
- 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/file-size-policy/FileSizePolicy.psm1 +434 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateFeatureFolderRecord.psm1 +166 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1 +8 -3
- package/resources/claude-customizations/.claude/lib/worktree-resolution/HookTargetWorktree.psm1 +391 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunRecordPath.psm1 +185 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1 +11 -21
- package/resources/claude-customizations/.claude/rules/general-code-change.md +4 -1
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +29 -5
- package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +152 -20
- package/resources/claude-customizations/.claude/rules/powershell.md +1 -1
- package/resources/claude-customizations/.claude/rules/shell.md +1 -1
- package/resources/claude-customizations/.claude/settings.json +36 -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/cleanup-merged-worktrees/SKILL.md +2 -2
- 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 +25 -10
- package/resources/claude-customizations/.claude/skills/extract-feature-contract/SKILL.md +133 -0
- package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +1 -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 +15 -7
- package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +25 -2
- package/resources/claude-customizations/.claude/skills/parallel-close/SKILL.md +11 -1
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +124 -14
- package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +72 -7
- 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 +56 -9
- 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/.claude-variants/csharp-legacy/agents/csharp-typed-engineer.md +1 -1
- 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 +40 -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 +25 -10
- package/resources/codex-and-agents-customizations/.agents/skills/extract-feature-contract/SKILL.md +133 -0
- package/resources/codex-and-agents-customizations/.agents/skills/feature-promotion-lifecycle/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/feature-review/SKILL.md +8 -0
- package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +4 -1
- 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/powershell/SKILL.md +1 -1
- 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/csharp-typed-engineer-c1.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c2.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3-elevated.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c4.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/discovery-capability-sentinel.toml +51 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +8 -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/powershell-typed-engineer-c1.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c2.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3-elevated.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c4.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c1.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c2.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3-elevated.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c4.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer.toml +1 -1
- 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 +39 -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-file-size-limit.ps1 +282 -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/file-size-policy.ps1 +421 -0
- 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/.codex-variants/csharp-legacy/agents/csharp-typed-engineer.toml +1 -1
- package/resources/codex-and-agents-customizations/AGENTS.md +4 -1
- package/resources/codex-and-agents-customizations/pack-manifests/core.json +65 -1
- package/resources/config/orchestration-routing.json +61 -1
- package/resources/customizations/.github/agents/commentary-remediation.agent.md +1 -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/python-execution-only-typed.agent.md +1 -1
- package/resources/customizations/.github/agents/python-typed-engineer.agent.md +1 -1
- 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/general-code-change.instructions.md +5 -2
- package/resources/customizations/.github/instructions/mermaid.instructions.md +85 -0
- package/resources/customizations/.github/instructions/powershell-code-change.instructions.md +1 -1
- package/resources/customizations/.github/prompts/execute-hard-lock.prompt.md +105 -0
- package/resources/customizations/.github/prompts/remediate-comments.prompt.md +1 -1
- 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 +25 -10
- package/resources/customizations/.github/skills/extract-feature-contract/SKILL.md +133 -0
- package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +8 -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
- package/resources/templates/policy_audit/code-review.yyyy-MM-ddTHH-mm.md +4 -4
- package/resources/templates/policy_audit/policy-audit.yyyy-MM-ddTHH-mm.md +4 -4
package/resources/claude-customizations/.claude/lib/worktree-resolution/HookTargetWorktree.psm1
ADDED
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Pure target-worktree resolver for PreToolUse hooks (issue #1009).
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Resolves the registered git worktree that a hook call acts on, from the first present
|
|
7
|
+
source in a strict order: (1) file-tool target paths, (2) command operand paths or a
|
|
8
|
+
branch the caller has already parsed, (3) the identity carried by an Agent delegation
|
|
9
|
+
prompt, and (4) the session root as the final fallback. Registered worktrees come only
|
|
10
|
+
from `git worktree list --porcelain` text returned by the injectable
|
|
11
|
+
WorktreeListInvoker seam, and a path resolves to its nearest enclosing registered
|
|
12
|
+
worktree (the longest normalized root prefix). Paths are compared after forward-slash
|
|
13
|
+
normalization with OrdinalIgnoreCase on every platform.
|
|
14
|
+
|
|
15
|
+
Known limitation: an 8.3 short-name path, or a path reached through a junction, that
|
|
16
|
+
differs textually from the long path git reports matches no registered worktree and
|
|
17
|
+
falls back to the session root.
|
|
18
|
+
|
|
19
|
+
The TARGET_WORKTREE_AMBIGUOUS and TARGET_WORKTREE_NOT_DERIVABLE literals are restated
|
|
20
|
+
inside Resolve-HookTargetWorktree rather than read from WorktreeResolution.psm1,
|
|
21
|
+
because this module imports nothing. A test pins both literals equal to the
|
|
22
|
+
WorktreeResolution.psm1 accessors, following the precedent of
|
|
23
|
+
validate-orchestrator-output-resolution.ps1.
|
|
24
|
+
|
|
25
|
+
.NOTES
|
|
26
|
+
Compatible with PowerShell 7+. No module dependency, no file read, no environment
|
|
27
|
+
read, and no clock read; the only process is the default WorktreeListInvoker body.
|
|
28
|
+
Functions reference no module-scope state, so the Codex twin
|
|
29
|
+
.codex/hooks/hook-target-worktree.ps1 carries byte-identical function text.
|
|
30
|
+
Mirrored byte-identically under extensions/drm-copilot/resources/claude-customizations/.
|
|
31
|
+
CONVENTION: this module fails fast at module scope and imports its siblings with -ErrorAction Stop.
|
|
32
|
+
#>
|
|
33
|
+
|
|
34
|
+
Set-StrictMode -Version Latest
|
|
35
|
+
$ErrorActionPreference = 'Stop'
|
|
36
|
+
|
|
37
|
+
function ConvertTo-HookTargetWorktreeNormalizedPath {
|
|
38
|
+
<#
|
|
39
|
+
.SYNOPSIS
|
|
40
|
+
Normalize a path to forward slashes with repeated and trailing slashes removed.
|
|
41
|
+
.DESCRIPTION
|
|
42
|
+
Backslashes become forward slashes, runs of slashes collapse to one, and a trailing
|
|
43
|
+
slash is trimmed except from a bare root ('/' or a drive root such as 'C:/'). Blank
|
|
44
|
+
input returns an empty string. Case is preserved; callers compare with
|
|
45
|
+
OrdinalIgnoreCase.
|
|
46
|
+
.PARAMETER Path
|
|
47
|
+
The path to normalize.
|
|
48
|
+
#>
|
|
49
|
+
[CmdletBinding()]
|
|
50
|
+
[OutputType([string])]
|
|
51
|
+
param(
|
|
52
|
+
[AllowNull()] [AllowEmptyString()] [string] $Path
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
if ([string]::IsNullOrWhiteSpace($Path)) { return '' }
|
|
56
|
+
$normalized = ($Path.Trim() -replace '\\', '/') -replace '/{2,}', '/'
|
|
57
|
+
if ($normalized.Length -gt 1 -and $normalized.EndsWith('/') -and $normalized -notmatch '^[A-Za-z]:/$') {
|
|
58
|
+
$normalized = $normalized.Substring(0, $normalized.Length - 1)
|
|
59
|
+
}
|
|
60
|
+
return $normalized
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function Get-HookTargetWorktreeRelativePath {
|
|
64
|
+
<#
|
|
65
|
+
.SYNOPSIS
|
|
66
|
+
Return a path relative to a worktree root, or $null when it lies outside the root.
|
|
67
|
+
.DESCRIPTION
|
|
68
|
+
Both values are normalized first. A path equal to the root returns an empty string;
|
|
69
|
+
a path under the root returns its forward-slash remainder; any other path, or a
|
|
70
|
+
blank value, returns $null.
|
|
71
|
+
.PARAMETER Path
|
|
72
|
+
The path to express relative to the root.
|
|
73
|
+
.PARAMETER WorktreeRoot
|
|
74
|
+
The worktree root.
|
|
75
|
+
#>
|
|
76
|
+
[CmdletBinding()]
|
|
77
|
+
[OutputType([string])]
|
|
78
|
+
param(
|
|
79
|
+
[AllowNull()] [AllowEmptyString()] [string] $Path,
|
|
80
|
+
[AllowNull()] [AllowEmptyString()] [string] $WorktreeRoot
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
$normalizedPath = ConvertTo-HookTargetWorktreeNormalizedPath -Path $Path
|
|
84
|
+
$normalizedRoot = ConvertTo-HookTargetWorktreeNormalizedPath -Path $WorktreeRoot
|
|
85
|
+
if ([string]::IsNullOrEmpty($normalizedPath) -or [string]::IsNullOrEmpty($normalizedRoot)) { return $null }
|
|
86
|
+
if ([string]::Equals($normalizedPath, $normalizedRoot, [System.StringComparison]::OrdinalIgnoreCase)) { return '' }
|
|
87
|
+
$prefix = if ($normalizedRoot.EndsWith('/')) { $normalizedRoot } else { $normalizedRoot + '/' }
|
|
88
|
+
if ($normalizedPath.StartsWith($prefix, [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
89
|
+
return $normalizedPath.Substring($prefix.Length)
|
|
90
|
+
}
|
|
91
|
+
return $null
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function Get-HookTargetWorktreeEnclosingRoot {
|
|
95
|
+
<#
|
|
96
|
+
.SYNOPSIS
|
|
97
|
+
Return the nearest root that encloses a path, or $null when none does.
|
|
98
|
+
.DESCRIPTION
|
|
99
|
+
A root encloses a path when the normalized path equals the normalized root or
|
|
100
|
+
starts with the root followed by a slash. When several roots enclose the path, the
|
|
101
|
+
longest one wins, so a worktree nested inside another wins for paths under it.
|
|
102
|
+
.PARAMETER Path
|
|
103
|
+
The path to place.
|
|
104
|
+
.PARAMETER Roots
|
|
105
|
+
The candidate worktree roots.
|
|
106
|
+
#>
|
|
107
|
+
[CmdletBinding()]
|
|
108
|
+
[OutputType([string])]
|
|
109
|
+
param(
|
|
110
|
+
[AllowNull()] [AllowEmptyString()] [string] $Path,
|
|
111
|
+
[AllowNull()] [AllowEmptyCollection()] [string[]] $Roots
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
$best = $null
|
|
115
|
+
foreach ($root in @($Roots)) {
|
|
116
|
+
$normalizedRoot = ConvertTo-HookTargetWorktreeNormalizedPath -Path $root
|
|
117
|
+
if ([string]::IsNullOrEmpty($normalizedRoot)) { continue }
|
|
118
|
+
$relative = Get-HookTargetWorktreeRelativePath -Path $Path -WorktreeRoot $normalizedRoot
|
|
119
|
+
if ($null -ne $relative -and ($null -eq $best -or $normalizedRoot.Length -gt $best.Length)) { $best = $normalizedRoot }
|
|
120
|
+
}
|
|
121
|
+
return $best
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function Get-HookTargetWorktreeKey {
|
|
125
|
+
<#
|
|
126
|
+
.SYNOPSIS
|
|
127
|
+
Return the stable key '<sanitized leaf>-<8 hex>' that names a worktree root.
|
|
128
|
+
.DESCRIPTION
|
|
129
|
+
The hex part is the first four bytes of the SHA-256 of the normalized root with no
|
|
130
|
+
trailing slash, and the leaf is reduced to file-name-safe characters. The algorithm
|
|
131
|
+
equals the worktree-derived batch-budget session id, which prefixes 'worktree-'.
|
|
132
|
+
.PARAMETER WorktreeRoot
|
|
133
|
+
The worktree root to key.
|
|
134
|
+
#>
|
|
135
|
+
[CmdletBinding()]
|
|
136
|
+
[OutputType([string])]
|
|
137
|
+
param(
|
|
138
|
+
[Parameter(Mandatory)] [string] $WorktreeRoot
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
$normalizedRoot = (ConvertTo-HookTargetWorktreeNormalizedPath -Path $WorktreeRoot).TrimEnd('/')
|
|
142
|
+
$leaf = $normalizedRoot.Substring($normalizedRoot.LastIndexOf('/') + 1) -replace '[^A-Za-z0-9._-]', '_'
|
|
143
|
+
$hashBytes = [System.Security.Cryptography.SHA256]::HashData([System.Text.Encoding]::UTF8.GetBytes($normalizedRoot))
|
|
144
|
+
$shortHash = -join (@($hashBytes[0..3]) | ForEach-Object { $_.ToString('x2') })
|
|
145
|
+
return "$leaf-$shortHash"
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function ConvertFrom-HookTargetWorktreePorcelain {
|
|
149
|
+
<#
|
|
150
|
+
.SYNOPSIS
|
|
151
|
+
Parse `git worktree list --porcelain` text into worktree records.
|
|
152
|
+
.DESCRIPTION
|
|
153
|
+
Emits one record per non-bare stanza with Root (normalized), Branch (the short
|
|
154
|
+
branch name, or $null when detached), Bare, and Prunable. A bare stanza is skipped.
|
|
155
|
+
Blank lines separate stanzas, and CRLF line endings are tolerated.
|
|
156
|
+
.PARAMETER Text
|
|
157
|
+
The porcelain text.
|
|
158
|
+
#>
|
|
159
|
+
[CmdletBinding()]
|
|
160
|
+
[OutputType([pscustomobject])]
|
|
161
|
+
param(
|
|
162
|
+
[AllowNull()] [AllowEmptyString()] [string] $Text
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
$records = [System.Collections.Generic.List[object]]::new()
|
|
166
|
+
$stanza = $null
|
|
167
|
+
foreach ($rawLine in @(([string] $Text -split "`r?`n") + '')) {
|
|
168
|
+
$line = $rawLine.Trim()
|
|
169
|
+
if ($line -eq '' -or $line.StartsWith('worktree ')) {
|
|
170
|
+
if ($null -ne $stanza -and -not $stanza.Bare) {
|
|
171
|
+
$records.Add([pscustomobject]@{ Root = $stanza.Root; Branch = $stanza.Branch; Bare = $false; Prunable = $stanza.Prunable })
|
|
172
|
+
}
|
|
173
|
+
$stanza = $null
|
|
174
|
+
if ($line -ne '') {
|
|
175
|
+
$stanza = @{ Root = (ConvertTo-HookTargetWorktreeNormalizedPath -Path $line.Substring(9)); Branch = $null; Bare = $false; Prunable = $false }
|
|
176
|
+
}
|
|
177
|
+
continue
|
|
178
|
+
}
|
|
179
|
+
if ($null -eq $stanza) { continue }
|
|
180
|
+
if ($line -eq 'bare') {
|
|
181
|
+
$stanza.Bare = $true
|
|
182
|
+
} elseif ($line.StartsWith('branch ')) {
|
|
183
|
+
$reference = $line.Substring(7).Trim()
|
|
184
|
+
$stanza.Branch = if ($reference.StartsWith('refs/heads/')) { $reference.Substring(11) } else { $reference }
|
|
185
|
+
} elseif ($line -eq 'detached') {
|
|
186
|
+
$stanza.Branch = $null
|
|
187
|
+
} elseif ($line -eq 'prunable' -or $line.StartsWith('prunable ')) {
|
|
188
|
+
$stanza.Prunable = $true
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return $records.ToArray()
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function Resolve-HookTargetWorktree {
|
|
195
|
+
<#
|
|
196
|
+
.SYNOPSIS
|
|
197
|
+
Resolve the registered worktree that a hook call acts on.
|
|
198
|
+
.DESCRIPTION
|
|
199
|
+
The first present source decides, in the order FilePath, CommandOperand (paths
|
|
200
|
+
and/or branch), DelegationIdentity, SessionRoot; a later source is never consulted.
|
|
201
|
+
Relative paths map to the session root without enumerating worktrees. An absolute
|
|
202
|
+
path maps to its nearest enclosing registered worktree, and a path inside none, or
|
|
203
|
+
an invoker that throws or returns no worktree, falls back to the session root.
|
|
204
|
+
Disagreeing components yield TARGET_WORKTREE_AMBIGUOUS; a present branch or
|
|
205
|
+
identity that names no worktree yields TARGET_WORKTREE_NOT_DERIVABLE and never the
|
|
206
|
+
session root. The result carries WorktreeRoot, Source, Ambiguous, ReasonCode,
|
|
207
|
+
Detail, and Candidates. The invoker runs at most once.
|
|
208
|
+
.PARAMETER SessionRoot
|
|
209
|
+
The absolute session root (the hook's root); the fallback root.
|
|
210
|
+
.PARAMETER FilePath
|
|
211
|
+
File-tool target paths.
|
|
212
|
+
.PARAMETER CommandOperandPath
|
|
213
|
+
Command operand paths parsed by the caller.
|
|
214
|
+
.PARAMETER CommandBranch
|
|
215
|
+
A command branch operand parsed by the caller.
|
|
216
|
+
.PARAMETER DelegationPrompt
|
|
217
|
+
Agent delegation prompt text; its 'branch:' label and canonical issue line are read.
|
|
218
|
+
.PARAMETER CheckpointIssueReader
|
|
219
|
+
Optional { param($WorktreeRoot) <issue number or $null> } used only for an issue identity.
|
|
220
|
+
.PARAMETER WorktreeListInvoker
|
|
221
|
+
{ param($SessionRoot) <porcelain text> }; the default runs git worktree list --porcelain.
|
|
222
|
+
#>
|
|
223
|
+
[CmdletBinding()]
|
|
224
|
+
[OutputType([pscustomobject])]
|
|
225
|
+
param(
|
|
226
|
+
[Parameter(Mandatory)] [string] $SessionRoot,
|
|
227
|
+
[AllowNull()] [AllowEmptyCollection()] [string[]] $FilePath,
|
|
228
|
+
[AllowNull()] [AllowEmptyCollection()] [string[]] $CommandOperandPath,
|
|
229
|
+
[AllowNull()] [AllowEmptyString()] [string] $CommandBranch,
|
|
230
|
+
[AllowNull()] [AllowEmptyString()] [string] $DelegationPrompt,
|
|
231
|
+
[scriptblock] $CheckpointIssueReader,
|
|
232
|
+
[scriptblock] $WorktreeListInvoker = { param($SessionRoot) $porcelain = & git -C $SessionRoot worktree list --porcelain 2>$null; if ($LASTEXITCODE -ne 0) { return '' }; return ($porcelain -join "`n") }
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
$ambiguousCode = 'TARGET_WORKTREE_AMBIGUOUS'
|
|
236
|
+
$notDerivableCode = 'TARGET_WORKTREE_NOT_DERIVABLE'
|
|
237
|
+
$ignoreCase = [System.StringComparison]::OrdinalIgnoreCase
|
|
238
|
+
$absolutePattern = '^(/|[A-Za-z]:/)'
|
|
239
|
+
$session = ConvertTo-HookTargetWorktreeNormalizedPath -Path $SessionRoot
|
|
240
|
+
|
|
241
|
+
# Nested helpers are called by constant name and read this function's variables.
|
|
242
|
+
function Get-HookTargetResult {
|
|
243
|
+
param($Root, [string] $Source, $Code, [string] $Detail = '', [string[]] $Candidates = @())
|
|
244
|
+
[pscustomobject]@{
|
|
245
|
+
WorktreeRoot = $Root
|
|
246
|
+
Source = $Source
|
|
247
|
+
Ambiguous = ($Code -eq $ambiguousCode)
|
|
248
|
+
ReasonCode = $Code
|
|
249
|
+
Detail = $Detail
|
|
250
|
+
Candidates = [string[]] @($Candidates)
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function Add-HookTargetDistinctValue {
|
|
254
|
+
param([System.Collections.Generic.List[string]] $List, [string] $Value)
|
|
255
|
+
foreach ($existing in $List) { if ([string]::Equals($existing, $Value, $ignoreCase)) { return } }
|
|
256
|
+
$List.Add($Value)
|
|
257
|
+
}
|
|
258
|
+
function Get-HookTargetRecordedIssue {
|
|
259
|
+
param([string] $Root)
|
|
260
|
+
try { $value = ([string] (& $CheckpointIssueReader $Root)).Trim().TrimStart('#') } catch { return $null }
|
|
261
|
+
if ($value -notmatch '^\d+$') { return $null }
|
|
262
|
+
$digits = $value.TrimStart('0')
|
|
263
|
+
return $(if ($digits -eq '') { '0' } else { $digits })
|
|
264
|
+
}
|
|
265
|
+
function Resolve-HookTargetPathSource {
|
|
266
|
+
param([string[]] $Paths, [string] $Source)
|
|
267
|
+
$resolved = [System.Collections.Generic.List[string]]::new()
|
|
268
|
+
$details = [System.Collections.Generic.List[string]]::new()
|
|
269
|
+
$fellBack = $false
|
|
270
|
+
foreach ($candidate in $Paths) {
|
|
271
|
+
$normalized = ConvertTo-HookTargetWorktreeNormalizedPath -Path $candidate
|
|
272
|
+
$root = $session
|
|
273
|
+
if ($normalized -notmatch $absolutePattern) {
|
|
274
|
+
$fellBack = $true
|
|
275
|
+
$details.Add("relative path '$normalized' maps to the session root")
|
|
276
|
+
} else {
|
|
277
|
+
$enclosing = Get-HookTargetWorktreeEnclosingRoot -Path $normalized -Roots $roots
|
|
278
|
+
if ($null -eq $enclosing) {
|
|
279
|
+
$fellBack = $true
|
|
280
|
+
$details.Add($(if ($listDetail) { $listDetail } else { "path '$normalized' is inside no registered worktree" }))
|
|
281
|
+
} else {
|
|
282
|
+
$root = $enclosing
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
Add-HookTargetDistinctValue -List $resolved -Value $root
|
|
286
|
+
}
|
|
287
|
+
if ($resolved.Count -gt 1) {
|
|
288
|
+
return (Get-HookTargetResult -Root $null -Source $Source -Code $ambiguousCode -Detail ('the paths resolve to {0} different worktrees' -f $resolved.Count) -Candidates $resolved.ToArray())
|
|
289
|
+
}
|
|
290
|
+
$decidedBy = if ($fellBack) { 'SessionRoot' } else { $Source }
|
|
291
|
+
return (Get-HookTargetResult -Root $resolved[0] -Source $decidedBy -Code $null -Detail ($details -join '; '))
|
|
292
|
+
}
|
|
293
|
+
function Resolve-HookTargetBranchSource {
|
|
294
|
+
param([string] $Branch, [string] $Source)
|
|
295
|
+
$matched = @($records | Where-Object { $null -ne $_.Branch -and [string]::Equals($_.Branch, $Branch, $ignoreCase) } | ForEach-Object { $_.Root })
|
|
296
|
+
if ($matched.Count -eq 0) { return (Get-HookTargetResult -Root $null -Source $Source -Code $notDerivableCode -Detail "branch '$Branch' is checked out in no registered worktree") }
|
|
297
|
+
if ($matched.Count -gt 1) { return (Get-HookTargetResult -Root $null -Source $Source -Code $ambiguousCode -Detail "branch '$Branch' is checked out in $($matched.Count) registered worktrees" -Candidates $matched) }
|
|
298
|
+
return (Get-HookTargetResult -Root $matched[0] -Source $Source -Code $null)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
# Select the deciding source: the first present one in precedence order.
|
|
302
|
+
$filePaths = @($FilePath | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
|
|
303
|
+
$operandPaths = @($CommandOperandPath | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
|
|
304
|
+
$branchOperand = if ([string]::IsNullOrWhiteSpace($CommandBranch)) { '' } else { $CommandBranch.Trim() }
|
|
305
|
+
$branches = [System.Collections.Generic.List[string]]::new()
|
|
306
|
+
$issues = [System.Collections.Generic.List[string]]::new()
|
|
307
|
+
if ($filePaths.Count -gt 0) {
|
|
308
|
+
$source = 'FilePath'
|
|
309
|
+
} elseif ($operandPaths.Count -gt 0 -or $branchOperand) {
|
|
310
|
+
$source = 'CommandOperand'
|
|
311
|
+
} elseif (-not [string]::IsNullOrWhiteSpace($DelegationPrompt)) {
|
|
312
|
+
$source = 'DelegationIdentity'
|
|
313
|
+
foreach ($match in [regex]::Matches($DelegationPrompt, '(?i)\bbranch:\s*(?<name>[A-Za-z0-9][A-Za-z0-9._/-]*)')) {
|
|
314
|
+
Add-HookTargetDistinctValue -List $branches -Value $match.Groups['name'].Value.TrimEnd('.', '/')
|
|
315
|
+
}
|
|
316
|
+
foreach ($match in [regex]::Matches($DelegationPrompt, 'Canonical issue number for this feature is #?(?<number>\d+)')) {
|
|
317
|
+
$digits = $match.Groups['number'].Value.TrimStart('0')
|
|
318
|
+
Add-HookTargetDistinctValue -List $issues -Value $(if ($digits -eq '') { '0' } else { $digits })
|
|
319
|
+
}
|
|
320
|
+
} else {
|
|
321
|
+
return (Get-HookTargetResult -Root $session -Source 'SessionRoot' -Code $null -Detail 'no target source is present; the session root applies')
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
# Enumerate registered worktrees at most once, and only when the deciding source needs them.
|
|
325
|
+
$needsList = switch ($source) {
|
|
326
|
+
'FilePath' { @($filePaths | Where-Object { (ConvertTo-HookTargetWorktreeNormalizedPath -Path $_) -match $absolutePattern }).Count -gt 0 }
|
|
327
|
+
'CommandOperand' { [bool] $branchOperand -or @($operandPaths | Where-Object { (ConvertTo-HookTargetWorktreeNormalizedPath -Path $_) -match $absolutePattern }).Count -gt 0 }
|
|
328
|
+
default { ($branches.Count -eq 1 -and $issues.Count -le 1) -or ($branches.Count -eq 0 -and $issues.Count -eq 1 -and $null -ne $CheckpointIssueReader) }
|
|
329
|
+
}
|
|
330
|
+
$records = @()
|
|
331
|
+
$listDetail = ''
|
|
332
|
+
if ($needsList) {
|
|
333
|
+
try {
|
|
334
|
+
$records = @(ConvertFrom-HookTargetWorktreePorcelain -Text (@(& $WorktreeListInvoker $SessionRoot) -join "`n"))
|
|
335
|
+
if ($records.Count -eq 0) { $listDetail = 'the worktree list returned no registered worktree' }
|
|
336
|
+
} catch {
|
|
337
|
+
$records = @()
|
|
338
|
+
$listDetail = "the worktree list failed: $($_.Exception.Message)"
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
$roots = @($records | ForEach-Object { $_.Root })
|
|
342
|
+
|
|
343
|
+
if ($source -eq 'FilePath') { return (Resolve-HookTargetPathSource -Paths $filePaths -Source 'FilePath') }
|
|
344
|
+
|
|
345
|
+
if ($source -eq 'CommandOperand') {
|
|
346
|
+
$pathResult = if ($operandPaths.Count -gt 0) { Resolve-HookTargetPathSource -Paths $operandPaths -Source 'CommandOperand' } else { $null }
|
|
347
|
+
if (-not $branchOperand) { return $pathResult }
|
|
348
|
+
$branchResult = Resolve-HookTargetBranchSource -Branch $branchOperand -Source 'CommandOperand'
|
|
349
|
+
if ($null -eq $pathResult) { return $branchResult }
|
|
350
|
+
if ($pathResult.Ambiguous) { return $pathResult }
|
|
351
|
+
if ($null -ne $branchResult.ReasonCode) { return $branchResult }
|
|
352
|
+
if (-not [string]::Equals($pathResult.WorktreeRoot, $branchResult.WorktreeRoot, $ignoreCase)) {
|
|
353
|
+
return (Get-HookTargetResult -Root $null -Source 'CommandOperand' -Code $ambiguousCode -Detail "the operand paths and branch '$branchOperand' resolve to different worktrees" -Candidates @($pathResult.WorktreeRoot, $branchResult.WorktreeRoot))
|
|
354
|
+
}
|
|
355
|
+
return $branchResult
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if ($branches.Count -gt 1) { return (Get-HookTargetResult -Root $null -Source $source -Code $ambiguousCode -Detail ('the prompt names {0} different branches: {1}' -f $branches.Count, ($branches -join ', '))) }
|
|
359
|
+
if ($issues.Count -gt 1) { return (Get-HookTargetResult -Root $null -Source $source -Code $ambiguousCode -Detail ('the prompt names {0} different issue numbers: {1}' -f $issues.Count, ($issues -join ', '))) }
|
|
360
|
+
if ($branches.Count -eq 1) {
|
|
361
|
+
$branchResult = Resolve-HookTargetBranchSource -Branch $branches[0] -Source $source
|
|
362
|
+
if ($null -ne $branchResult.ReasonCode -or $issues.Count -eq 0 -or $null -eq $CheckpointIssueReader) { return $branchResult }
|
|
363
|
+
$target = $branchResult.WorktreeRoot
|
|
364
|
+
$recorded = Get-HookTargetRecordedIssue -Root $target
|
|
365
|
+
if ($null -ne $recorded) {
|
|
366
|
+
if ($recorded -eq $issues[0]) { return $branchResult }
|
|
367
|
+
return (Get-HookTargetResult -Root $null -Source $source -Code $ambiguousCode -Detail "branch '$($branches[0])' places the call in a worktree whose checkpoint records issue $recorded, not issue $($issues[0])" -Candidates @($target))
|
|
368
|
+
}
|
|
369
|
+
$elsewhere = @($records | Where-Object { -not [string]::Equals($_.Root, $target, $ignoreCase) -and (Get-HookTargetRecordedIssue -Root $_.Root) -eq $issues[0] } | ForEach-Object { $_.Root })
|
|
370
|
+
if ($elsewhere.Count -gt 0) {
|
|
371
|
+
return (Get-HookTargetResult -Root $null -Source $source -Code $ambiguousCode -Detail "branch '$($branches[0])' and issue $($issues[0]) place the call in different worktrees" -Candidates (@($target) + $elsewhere))
|
|
372
|
+
}
|
|
373
|
+
return $branchResult
|
|
374
|
+
}
|
|
375
|
+
if ($issues.Count -eq 1) {
|
|
376
|
+
if ($null -eq $CheckpointIssueReader) { return (Get-HookTargetResult -Root $null -Source $source -Code $notDerivableCode -Detail "issue $($issues[0]) cannot be placed without a checkpoint issue reader") }
|
|
377
|
+
$matched = @($records | Where-Object { (Get-HookTargetRecordedIssue -Root $_.Root) -eq $issues[0] } | ForEach-Object { $_.Root })
|
|
378
|
+
if ($matched.Count -eq 0) { return (Get-HookTargetResult -Root $null -Source $source -Code $notDerivableCode -Detail "issue $($issues[0]) is recorded by no registered worktree") }
|
|
379
|
+
if ($matched.Count -gt 1) { return (Get-HookTargetResult -Root $null -Source $source -Code $ambiguousCode -Detail "issue $($issues[0]) is recorded by $($matched.Count) registered worktrees" -Candidates $matched) }
|
|
380
|
+
return (Get-HookTargetResult -Root $matched[0] -Source $source -Code $null)
|
|
381
|
+
}
|
|
382
|
+
return (Get-HookTargetResult -Root $null -Source $source -Code $notDerivableCode -Detail 'the prompt carries no branch: label and no canonical issue line')
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
Export-ModuleMember -Function `
|
|
386
|
+
Resolve-HookTargetWorktree, `
|
|
387
|
+
ConvertFrom-HookTargetWorktreePorcelain, `
|
|
388
|
+
Get-HookTargetWorktreeEnclosingRoot, `
|
|
389
|
+
ConvertTo-HookTargetWorktreeNormalizedPath, `
|
|
390
|
+
Get-HookTargetWorktreeRelativePath, `
|
|
391
|
+
Get-HookTargetWorktreeKey
|
package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunRecordPath.psm1
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Shared comparison of a recorded worktree_path with a removal target (issue #995).
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
A run checkpoint records items[].worktree_path or features[].worktree_path either as
|
|
7
|
+
an absolute path or relative to the main checkout. Every site that compares such a
|
|
8
|
+
record to the absolute path a command names calls Test-WorktreeRunRecordedPathMatchesTarget,
|
|
9
|
+
so a relative record is joined to the same base roots everywhere and no site carries
|
|
10
|
+
its own normalise-and-compare.
|
|
11
|
+
|
|
12
|
+
A rooted record is compared to the target with Test-WorktreeRunPathEqual. A relative
|
|
13
|
+
record is joined to each non-blank base root and compared the same way. A relative
|
|
14
|
+
record that is blank, is . or .., has a . or .. segment, or contains : matches
|
|
15
|
+
nothing, so it can never name the main checkout or a path outside it.
|
|
16
|
+
|
|
17
|
+
.NOTES
|
|
18
|
+
PowerShell 7+. Exports Test-WorktreeRunPathEqual, Test-WorktreeRunRecordedPathMatchesTarget,
|
|
19
|
+
Get-WorktreeRunRecordBaseRoot, and Get-WorktreeRunMainCheckoutRoot. The two comparison
|
|
20
|
+
functions and Get-WorktreeRunRecordBaseRoot are pure. Get-WorktreeRunMainCheckoutRoot
|
|
21
|
+
reads the filesystem only through its siblings' seams (Get-WorktreeItemLiveRoot and
|
|
22
|
+
Get-WorktreeResolutionGitEntryKind). Mirrored byte-identically.
|
|
23
|
+
CONVENTION: this module fails fast at module scope and imports its siblings with -ErrorAction Stop.
|
|
24
|
+
#>
|
|
25
|
+
|
|
26
|
+
Set-StrictMode -Version Latest
|
|
27
|
+
$ErrorActionPreference = 'Stop'
|
|
28
|
+
|
|
29
|
+
Import-Module (Join-Path $PSScriptRoot 'WorktreeResolution.psm1') -ErrorAction Stop
|
|
30
|
+
Import-Module (Join-Path $PSScriptRoot 'WorktreeItemResolution.psm1') -ErrorAction Stop
|
|
31
|
+
|
|
32
|
+
# A drive-rooted, slash-rooted, or // (UNC) path; anything else is relative to some directory.
|
|
33
|
+
# The same pattern as $script:AbsolutePathPattern in WorktreeRunResolution.psm1.
|
|
34
|
+
$script:AbsolutePathPattern = '^([A-Za-z]:(/|$)|/)'
|
|
35
|
+
|
|
36
|
+
# The administrative entry that is a directory only in the main checkout; a linked
|
|
37
|
+
# worktree carries a .git file instead.
|
|
38
|
+
$script:GitEntryName = '.git'
|
|
39
|
+
|
|
40
|
+
# Private (moved from WorktreeRunResolution.psm1 for issue #995): compare two worktree
|
|
41
|
+
# paths, insensitive to separator style and trailing slashes, and case-insensitive for
|
|
42
|
+
# drive-letter and UNC (//) paths.
|
|
43
|
+
function Test-WorktreeRunPathEqual {
|
|
44
|
+
<#
|
|
45
|
+
.SYNOPSIS
|
|
46
|
+
Compare two worktree paths for equality.
|
|
47
|
+
.DESCRIPTION
|
|
48
|
+
Pure. Insensitive to separator style and trailing slashes; ordinal for single-slash
|
|
49
|
+
POSIX roots and ordinal-ignore-case for drive-letter and UNC (//) roots. A blank
|
|
50
|
+
side never matches.
|
|
51
|
+
.PARAMETER Left
|
|
52
|
+
One path.
|
|
53
|
+
.PARAMETER Right
|
|
54
|
+
The other path.
|
|
55
|
+
#>
|
|
56
|
+
[CmdletBinding()]
|
|
57
|
+
[OutputType([bool])]
|
|
58
|
+
param(
|
|
59
|
+
[Parameter(Mandatory = $true)] [AllowNull()] [AllowEmptyString()] [string] $Left,
|
|
60
|
+
[Parameter(Mandatory = $true)] [AllowNull()] [AllowEmptyString()] [string] $Right
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
if ([string]::IsNullOrWhiteSpace($Left) -or [string]::IsNullOrWhiteSpace($Right)) { return $false }
|
|
64
|
+
$a = ($Left.Trim() -replace '\\', '/').TrimEnd('/')
|
|
65
|
+
$b = ($Right.Trim() -replace '\\', '/').TrimEnd('/')
|
|
66
|
+
$isCaseInsensitivePath = ($a -match '^([A-Za-z]:|//)') -or ($b -match '^([A-Za-z]:|//)')
|
|
67
|
+
$comparison = if ($isCaseInsensitivePath) { [System.StringComparison]::OrdinalIgnoreCase } else { [System.StringComparison]::Ordinal }
|
|
68
|
+
return [string]::Equals($a, $b, $comparison)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# Private: whether a relative recorded path (already slash-normalised) is safe to join.
|
|
72
|
+
function Test-WorktreeRunRelativeRecordSafe {
|
|
73
|
+
[CmdletBinding()]
|
|
74
|
+
[OutputType([bool])]
|
|
75
|
+
param(
|
|
76
|
+
[Parameter(Mandatory = $true)] [AllowEmptyString()] [string] $Path
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
if ([string]::IsNullOrWhiteSpace($Path) -or $Path.Contains(':')) { return $false }
|
|
80
|
+
foreach ($segment in $Path.TrimEnd('/').Split('/')) {
|
|
81
|
+
if ($segment -eq '.' -or $segment -eq '..') { return $false }
|
|
82
|
+
}
|
|
83
|
+
return ($Path.TrimEnd('/').Length -gt 0)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function Test-WorktreeRunRecordedPathMatchesTarget {
|
|
87
|
+
<#
|
|
88
|
+
.SYNOPSIS
|
|
89
|
+
Report whether a recorded worktree_path names the removal target.
|
|
90
|
+
.DESCRIPTION
|
|
91
|
+
Pure. A rooted record (drive-letter, single-slash, or // after backslash-to-slash
|
|
92
|
+
normalisation) matches when Test-WorktreeRunPathEqual holds against the target. A
|
|
93
|
+
relative record matches when it is safe and Test-WorktreeRunPathEqual holds for
|
|
94
|
+
its join to at least one non-blank base root. A relative record that is blank, is
|
|
95
|
+
. or .., has a . or .. segment, or contains : matches nothing, as does a blank target.
|
|
96
|
+
.PARAMETER RecordedPath
|
|
97
|
+
The worktree_path a checkpoint records, absolute or relative.
|
|
98
|
+
.PARAMETER TargetPath
|
|
99
|
+
The absolute path the command names.
|
|
100
|
+
.PARAMETER BaseRoot
|
|
101
|
+
Optional. The roots a relative record is joined to: the main checkout root and the
|
|
102
|
+
root that holds the checkpoint. With none, a relative record matches nothing.
|
|
103
|
+
#>
|
|
104
|
+
[CmdletBinding()]
|
|
105
|
+
[OutputType([bool])]
|
|
106
|
+
param(
|
|
107
|
+
[Parameter(Mandatory = $true)] [AllowNull()] [AllowEmptyString()] [string] $RecordedPath,
|
|
108
|
+
[Parameter(Mandatory = $true)] [AllowNull()] [AllowEmptyString()] [string] $TargetPath,
|
|
109
|
+
[AllowNull()] [AllowEmptyCollection()] [string[]] $BaseRoot = @()
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
if ([string]::IsNullOrWhiteSpace($RecordedPath) -or [string]::IsNullOrWhiteSpace($TargetPath)) { return $false }
|
|
113
|
+
$recorded = $RecordedPath.Trim() -replace '\\', '/'
|
|
114
|
+
if ($recorded -match $script:AbsolutePathPattern) {
|
|
115
|
+
return (Test-WorktreeRunPathEqual -Left $recorded -Right $TargetPath)
|
|
116
|
+
}
|
|
117
|
+
if (-not (Test-WorktreeRunRelativeRecordSafe -Path $recorded)) { return $false }
|
|
118
|
+
foreach ($root in @($BaseRoot)) {
|
|
119
|
+
if ([string]::IsNullOrWhiteSpace($root)) { continue }
|
|
120
|
+
$joined = (($root.Trim() -replace '\\', '/').TrimEnd('/')) + '/' + $recorded.TrimEnd('/')
|
|
121
|
+
if (Test-WorktreeRunPathEqual -Left $joined -Right $TargetPath) { return $true }
|
|
122
|
+
}
|
|
123
|
+
return $false
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function Get-WorktreeRunRecordBaseRoot {
|
|
127
|
+
<#
|
|
128
|
+
.SYNOPSIS
|
|
129
|
+
Return the base roots a relative recorded worktree_path is joined to.
|
|
130
|
+
.DESCRIPTION
|
|
131
|
+
Pure. The main checkout root and the root that holds the checkpoint a read
|
|
132
|
+
resolved (Read.Target.WorktreeRoot), with blank entries dropped. Every gate
|
|
133
|
+
passes the result to Test-WorktreeRunRecordedPathMatchesTarget as -BaseRoot.
|
|
134
|
+
.PARAMETER MainRoot
|
|
135
|
+
The main checkout root, or $null when none was derived.
|
|
136
|
+
.PARAMETER Read
|
|
137
|
+
A run-checkpoint read result carrying Target.WorktreeRoot, or $null.
|
|
138
|
+
.OUTPUTS
|
|
139
|
+
System.String[]
|
|
140
|
+
#>
|
|
141
|
+
[CmdletBinding()]
|
|
142
|
+
[OutputType([string[]], [object[]])]
|
|
143
|
+
param(
|
|
144
|
+
[AllowNull()][AllowEmptyString()][string] $MainRoot,
|
|
145
|
+
[AllowNull()] $Read
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
$readRoot = $null
|
|
149
|
+
if ($null -ne $Read -and @($Read.PSObject.Properties.Name) -contains 'Target' -and $null -ne $Read.Target -and @($Read.Target.PSObject.Properties.Name) -contains 'WorktreeRoot') {
|
|
150
|
+
$readRoot = [string] $Read.Target.WorktreeRoot
|
|
151
|
+
}
|
|
152
|
+
return , [string[]] @(@($MainRoot, $readRoot) | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function Get-WorktreeRunMainCheckoutRoot {
|
|
156
|
+
<#
|
|
157
|
+
.SYNOPSIS
|
|
158
|
+
Return the main checkout root reachable from a session path, or $null.
|
|
159
|
+
.DESCRIPTION
|
|
160
|
+
The first live root whose .git entry is a directory; a linked worktree's .git entry
|
|
161
|
+
is a file. Reads the filesystem only through Get-WorktreeItemLiveRoot and
|
|
162
|
+
Get-WorktreeResolutionGitEntryKind, the seams a test mocks.
|
|
163
|
+
.PARAMETER SessionRoot
|
|
164
|
+
The calling process's path, used only to find the repository.
|
|
165
|
+
#>
|
|
166
|
+
[CmdletBinding()]
|
|
167
|
+
[OutputType([string])]
|
|
168
|
+
param(
|
|
169
|
+
[Parameter(Mandatory = $true)] [AllowEmptyString()] [string] $SessionRoot
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
$liveRoots = Get-WorktreeItemLiveRoot -SessionRoot $SessionRoot
|
|
173
|
+
foreach ($root in $liveRoots) {
|
|
174
|
+
if ([string]::IsNullOrWhiteSpace($root)) { continue }
|
|
175
|
+
$gitEntry = (($root -replace '\\', '/').TrimEnd('/')) + '/' + $script:GitEntryName
|
|
176
|
+
if ((Get-WorktreeResolutionGitEntryKind -Path $gitEntry) -eq 'Directory') { return $root }
|
|
177
|
+
}
|
|
178
|
+
return $null
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
Export-ModuleMember -Function `
|
|
182
|
+
Test-WorktreeRunPathEqual, `
|
|
183
|
+
Test-WorktreeRunRecordedPathMatchesTarget, `
|
|
184
|
+
Get-WorktreeRunRecordBaseRoot, `
|
|
185
|
+
Get-WorktreeRunMainCheckoutRoot
|
package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1
CHANGED
|
@@ -31,6 +31,7 @@ $ErrorActionPreference = 'Stop'
|
|
|
31
31
|
Import-Module (Join-Path $PSScriptRoot 'WorktreeResolution.psm1') -ErrorAction Stop
|
|
32
32
|
Import-Module (Join-Path $PSScriptRoot 'WorktreeTargetResolution.psm1') -ErrorAction Stop
|
|
33
33
|
Import-Module (Join-Path $PSScriptRoot 'WorktreeItemResolution.psm1') -ErrorAction Stop
|
|
34
|
+
Import-Module (Join-Path $PSScriptRoot 'WorktreeRunRecordPath.psm1') -ErrorAction Stop
|
|
34
35
|
|
|
35
36
|
# The run checkpoint locations relative to a worktree root.
|
|
36
37
|
$script:EpicCheckpointRelativePath = 'artifacts/orchestration/epic-orchestrator-state.json'
|
|
@@ -339,24 +340,6 @@ function Resolve-WorktreeParallelTarget {
|
|
|
339
340
|
return (Resolve-WorktreeRunMatch -Matched $matched.ToArray() -SessionRoot $SessionRoot -Description ("parallel_slug '{0}'" -f $ParallelSlug))
|
|
340
341
|
}
|
|
341
342
|
|
|
342
|
-
# Private: compare two worktree paths, insensitive to separator style and trailing
|
|
343
|
-
# slashes, and case-insensitive for drive-letter and UNC (//) paths.
|
|
344
|
-
function Test-WorktreeRunPathEqual {
|
|
345
|
-
[CmdletBinding()]
|
|
346
|
-
[OutputType([bool])]
|
|
347
|
-
param(
|
|
348
|
-
[Parameter(Mandatory = $true)] [AllowNull()] [AllowEmptyString()] [string] $Left,
|
|
349
|
-
[Parameter(Mandatory = $true)] [AllowNull()] [AllowEmptyString()] [string] $Right
|
|
350
|
-
)
|
|
351
|
-
|
|
352
|
-
if ([string]::IsNullOrWhiteSpace($Left) -or [string]::IsNullOrWhiteSpace($Right)) { return $false }
|
|
353
|
-
$a = ($Left.Trim() -replace '\\', '/').TrimEnd('/')
|
|
354
|
-
$b = ($Right.Trim() -replace '\\', '/').TrimEnd('/')
|
|
355
|
-
$isCaseInsensitivePath = ($a -match '^([A-Za-z]:|//)') -or ($b -match '^([A-Za-z]:|//)')
|
|
356
|
-
$comparison = if ($isCaseInsensitivePath) { [System.StringComparison]::OrdinalIgnoreCase } else { [System.StringComparison]::Ordinal }
|
|
357
|
-
return [string]::Equals($a, $b, $comparison)
|
|
358
|
-
}
|
|
359
|
-
|
|
360
343
|
# Private: compare two pull request numbers as integers.
|
|
361
344
|
function Test-WorktreeRunPrNumberEqual {
|
|
362
345
|
[CmdletBinding()]
|
|
@@ -373,6 +356,8 @@ function Test-WorktreeRunPrNumberEqual {
|
|
|
373
356
|
}
|
|
374
357
|
|
|
375
358
|
# Private: whether a parsed run checkpoint records a pull request number or worktree path.
|
|
359
|
+
# A worktree_path record is compared through the shared matcher, so a relative record is
|
|
360
|
+
# joined to each BaseRoot (issue #995); with no BaseRoot a relative record matches nothing.
|
|
376
361
|
function Test-WorktreeRunCheckpointRecord {
|
|
377
362
|
[CmdletBinding()]
|
|
378
363
|
[OutputType([bool])]
|
|
@@ -380,7 +365,8 @@ function Test-WorktreeRunCheckpointRecord {
|
|
|
380
365
|
[Parameter(Mandatory = $true)] [pscustomobject] $Checkpoint,
|
|
381
366
|
[Parameter(Mandatory = $true)] [ValidateSet('epic', 'parallel')] [string] $Kind,
|
|
382
367
|
[Parameter(Mandatory = $true)] [ValidateSet('pr_number', 'worktree_path')] [string] $RecordField,
|
|
383
|
-
[Parameter(Mandatory = $true)] [string] $Value
|
|
368
|
+
[Parameter(Mandatory = $true)] [string] $Value,
|
|
369
|
+
[AllowNull()] [AllowEmptyCollection()] [string[]] $BaseRoot = @()
|
|
384
370
|
)
|
|
385
371
|
|
|
386
372
|
$values = [System.Collections.Generic.List[object]]::new()
|
|
@@ -397,7 +383,7 @@ function Test-WorktreeRunCheckpointRecord {
|
|
|
397
383
|
if ($RecordField -eq 'pr_number') {
|
|
398
384
|
if (Test-WorktreeRunPrNumberEqual -Recorded $recorded -Expected ([long] $Value)) { return $true }
|
|
399
385
|
}
|
|
400
|
-
elseif (Test-
|
|
386
|
+
elseif (Test-WorktreeRunRecordedPathMatchesTarget -RecordedPath ([string] $recorded) -TargetPath $Value -BaseRoot $BaseRoot) { return $true }
|
|
401
387
|
}
|
|
402
388
|
return $false
|
|
403
389
|
}
|
|
@@ -438,11 +424,15 @@ function Resolve-WorktreeRunTargetByRecord {
|
|
|
438
424
|
'the command names no usable {0}, so the {1} run it belongs to cannot be identified' -f $RecordField, $Kind))
|
|
439
425
|
}
|
|
440
426
|
$needle = $Value.Trim()
|
|
427
|
+
# A relative worktree_path record is joined to the main checkout root and to the root
|
|
428
|
+
# holding the checkpoint (issue #995); the main root is derived once, and only for paths.
|
|
429
|
+
$mainRoot = if ($RecordField -eq 'worktree_path') { Get-WorktreeRunMainCheckoutRoot -SessionRoot $SessionRoot } else { $null }
|
|
441
430
|
$matched = [System.Collections.Generic.List[string]]::new()
|
|
442
431
|
foreach ($root in (Get-WorktreeRunLiveRoot -SessionRoot $SessionRoot)) {
|
|
443
432
|
$checkpoint = Get-WorktreeRunCheckpoint -Kind $Kind -WorktreeRoot $root
|
|
444
433
|
if ($null -eq $checkpoint) { continue }
|
|
445
|
-
|
|
434
|
+
$baseRoot = [string[]] @(@($mainRoot, $root) | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
|
|
435
|
+
if (Test-WorktreeRunCheckpointRecord -Checkpoint $checkpoint -Kind $Kind -RecordField $RecordField -Value $needle -BaseRoot $baseRoot) { $matched.Add($root) }
|
|
446
436
|
}
|
|
447
437
|
return (Resolve-WorktreeRunMatch -Matched $matched.ToArray() -SessionRoot $SessionRoot -Description $description)
|
|
448
438
|
}
|
|
@@ -46,7 +46,10 @@ Mutation testing and golden tests run in pre-merge or nightly pipelines, not the
|
|
|
46
46
|
|
|
47
47
|
## File Size Limit
|
|
48
48
|
|
|
49
|
-
-
|
|
49
|
+
- File size follows three tiers for production code, test code, and reusable script files, counted in logical lines (a trailing line terminator does not add a line):
|
|
50
|
+
- **500 lines (guidance):** look for a logical way to divide the file into multiple files, provided the parts can be separated logically. No gate fails at this tier.
|
|
51
|
+
- **700 lines (advisory):** look for a split more aggressively. Reviewers and `feature-review` report a non-blocking Warning for a file at or above 700 lines. No gate fails at this tier.
|
|
52
|
+
- **Above 800 lines (hard block):** split the file regardless of logical separability. The repository CI check and the PreToolUse hook block a file of 801 lines or more; a file of exactly 800 lines passes. Legacy files above 800 lines are listed in `config/file-size-baseline.json` and may shrink (with the entry tightened) or stay the same size, but may not grow.
|
|
50
53
|
- Exceptions: temporary throwaway scripts created and deleted within an agent session; raw text fixtures for language-processing test data; Markdown documentation files.
|
|
51
54
|
|
|
52
55
|
## Error Handling and Logging
|