@danmoisan/drm-copilot-mcp 1.1.18 → 1.1.20
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 +1149 -661
- package/package.json +3 -2
- package/resources/claude-customizations/.claude/agents/atomic-executor.md +28 -6
- package/resources/claude-customizations/.claude/agents/atomic-planner.md +20 -2
- package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +17 -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 +9 -5
- package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +35 -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/agents/task-researcher.md +8 -0
- 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-command.ps1 +190 -0
- 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 +78 -77
- 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 +497 -0
- package/resources/claude-customizations/.claude/hooks/premortem-section.ps1 +149 -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 +267 -113
- 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 +77 -33
- 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 +52 -36
- 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/ci-gate/Invoke-MergeConfirmationParser.ps1 +203 -0
- 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 +85 -10
- 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 +52 -16
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +7 -3
- 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/csharp-change-budget-router/SKILL.md +2 -2
- package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +45 -9
- package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +5 -3
- package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
- package/resources/claude-customizations/.claude/skills/execution-environment/SKILL.md +134 -0
- 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 +36 -15
- 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 +108 -21
- 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/powershell-change-budget-router/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +3 -1
- package/resources/claude-customizations/.claude/skills/python-change-budget-router/SKILL.md +1 -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 +16 -8
- package/resources/claude-customizations/.claude/skills/research-issue/SKILL.md +6 -0
- package/resources/claude-customizations/pack-manifests/core.json +27 -0
- package/resources/codex-and-agents-customizations/.agents/skills/README.md +1 -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 +19 -7
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +43 -4
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +18 -6
- package/resources/codex-and-agents-customizations/.agents/skills/csharp/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.agents/skills/csharp-change-budget-router/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 +16 -1
- 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/execution-environment/SKILL.md +134 -0
- 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 +27 -8
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/powershell-change-budget-router/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/python-change-budget-router/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/remediation-handoff-atomic-planner/SKILL.md +7 -3
- package/resources/codex-and-agents-customizations/.agents/skills/research-issue/SKILL.md +6 -0
- 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 +21 -6
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +21 -6
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +21 -6
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +21 -6
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +21 -6
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +21 -6
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +15 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +15 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +15 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +15 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +15 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +15 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planning.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-atomic-planning.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/epic-orchestrator.toml +4 -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 +7 -4
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +7 -4
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +7 -4
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +7 -4
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +7 -4
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +7 -4
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-planning.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-atomic-planning.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c1.toml +6 -0
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c2.toml +6 -0
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c3-elevated.toml +6 -0
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c3.toml +6 -0
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c4.toml +6 -0
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher.toml +6 -0
- 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-command.ps1 +190 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +54 -43
- 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 +497 -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/.codex/prompts/feature-review-remediate.md +6 -4
- package/resources/codex-and-agents-customizations/pack-manifests/core.json +11 -0
- package/resources/customizations/.github/agents/atomic_executor.agent.md +21 -11
- package/resources/customizations/.github/agents/atomic_planning.agent.md +8 -3
- package/resources/customizations/.github/agents/csharp-atomic-executor.agent.md +13 -9
- package/resources/customizations/.github/agents/csharp-atomic-planning.agent.md +2 -2
- package/resources/customizations/.github/agents/csharp-orchestrator.agent.md +2 -2
- package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +5 -5
- package/resources/customizations/.github/agents/orchestrator.agent.md +8 -5
- package/resources/customizations/.github/agents/powershell-atomic-executor.agent.md +13 -9
- package/resources/customizations/.github/agents/powershell-atomic-planning.agent.md +2 -2
- package/resources/customizations/.github/agents/powershell-orchestrator.agent.md +2 -2
- package/resources/customizations/.github/agents/powershell-typed-engineer.agent.md +3 -3
- package/resources/customizations/.github/agents/python-atomic-executor.agent.md +13 -9
- package/resources/customizations/.github/agents/python-atomic-planning.agent.md +2 -2
- package/resources/customizations/.github/agents/python-orchestrator.agent.md +2 -2
- package/resources/customizations/.github/agents/task-researcher.agent.md +6 -0
- 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/prompts/generate-atomic-plan.prompt.md +16 -10
- package/resources/customizations/.github/prompts/orchestrate-csharp-work.prompt.md +2 -2
- package/resources/customizations/.github/prompts/orchestrate-powershell-work.prompt.md +1 -1
- package/resources/customizations/.github/prompts/orchestrate-python-work.prompt.md +1 -1
- package/resources/customizations/.github/prompts/orchestrate-work.prompt.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 +43 -4
- package/resources/customizations/.github/skills/csharp-change-budget-router/SKILL.md +1 -1
- package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
- package/resources/customizations/.github/skills/execution-environment/SKILL.md +134 -0
- package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +1 -1
- package/resources/customizations/.github/skills/powershell-change-budget-router/SKILL.md +1 -1
- package/resources/customizations/.github/skills/remediation-handoff-atomic-planner/SKILL.md +7 -3
- package/resources/feature-templates/bug/plan.yyyy-MM-ddTHH-mm.md +10 -0
- package/resources/feature-templates/feature/plan.yyyy-MM-ddTHH-mm.md +11 -0
- package/resources/feature-templates/refactor/plan.yyyy-MM-ddTHH-mm.md +11 -0
- package/resources/powershell/PoshQC/PoshQC.Coverage.psm1 +314 -0
- package/resources/powershell/PoshQC/PoshQC.CoverageMerge.psm1 +163 -0
- package/resources/powershell/PoshQC/PoshQC.Shard.psm1 +296 -0
- package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +48 -32
- package/resources/powershell/PoshQC/PoshQC.psd1 +7 -1
- package/resources/powershell/PoshQC/PoshQC.psm1 +18 -5
- package/resources/powershell/PoshQC/README.md +21 -3
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +5 -319
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# parallel-mutation-ops.sh: sourceable bash port of the add, close, and requeue
|
|
3
|
+
# functions of the parallel mutation engine (issue #890), so a workspace that
|
|
4
|
+
# received the Claude customization payload can decide an admission, gate a run
|
|
5
|
+
# close, and build the add, close, and requeue mutations[] records without an
|
|
6
|
+
# interpreter. It sources parallel-mutation.sh, which carries the removal
|
|
7
|
+
# functions and the shared message builders.
|
|
8
|
+
#
|
|
9
|
+
# Parity reference (the repository authority; this file reproduces its
|
|
10
|
+
# observable outputs and rejection messages byte for byte):
|
|
11
|
+
# decide_admission scripts/dev_tools/parallel_mutation_protocol.py
|
|
12
|
+
# decide_close scripts/dev_tools/parallel_mutation_protocol.py
|
|
13
|
+
# build_add_entry scripts/dev_tools/_parallel_mutation_entries.py
|
|
14
|
+
# build_close_entry scripts/dev_tools/_parallel_mutation_entries.py
|
|
15
|
+
# build_requeue_entry scripts/dev_tools/_parallel_mutation_entries.py
|
|
16
|
+
# ItemRecord, MutationEntry scripts/dev_tools/_parallel_mutation_models.py
|
|
17
|
+
# rejection messages scripts/dev_tools/_parallel_mutation_errors.py
|
|
18
|
+
#
|
|
19
|
+
# Declared divergences (also listed in both parity lanes):
|
|
20
|
+
# 1. A duplicate key in `close --items` is a usage error (exit 2) raised by
|
|
21
|
+
# the command line; the Python mapping would keep the last record.
|
|
22
|
+
# 2. `at` is a caller-supplied string instead of a `datetime`.
|
|
23
|
+
# 3. Option abbreviations are rejected.
|
|
24
|
+
# 4. `admit` has no rejection branch, in either implementation.
|
|
25
|
+
#
|
|
26
|
+
# Contract. Every public function is pure: no file I/O, no network access, no
|
|
27
|
+
# clock read, and no external utility beyond `sort`. Each sets PM_RESULT to one
|
|
28
|
+
# compact JSON object and returns 0 on success, or sets PM_ERROR to the exact
|
|
29
|
+
# reference message and returns 1 on a rule rejection. Integer arguments are
|
|
30
|
+
# expected in canonical decimal form; the entry point mutate-parallel-run.sh
|
|
31
|
+
# validates them before calling here. Entry validation order follows
|
|
32
|
+
# MutationEntry: the stamped recolor_generation first, then item_key.
|
|
33
|
+
#
|
|
34
|
+
# shellcheck disable=SC2034
|
|
35
|
+
# SC2034 is disabled file-wide because PM_RESULT and PM_ERROR are written here
|
|
36
|
+
# and read by the entry point mutate-parallel-run.sh and by the bats suites,
|
|
37
|
+
# which shellcheck analyses separately.
|
|
38
|
+
|
|
39
|
+
# Resolve this file's own directory so its dependencies source regardless of
|
|
40
|
+
# the caller's working directory.
|
|
41
|
+
PMOPS_LIB_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
|
42
|
+
# shellcheck source=.claude/lib/bash/parallel-mutation.sh
|
|
43
|
+
# shellcheck disable=SC1091
|
|
44
|
+
source "$PMOPS_LIB_DIR/parallel-mutation.sh"
|
|
45
|
+
|
|
46
|
+
pm_decide_admission() {
|
|
47
|
+
# Decide whether a candidate joins the current cohort or is deferred.
|
|
48
|
+
#
|
|
49
|
+
# Args: $1 = candidate key, $2 = space-separated `a:b` conflict edges over
|
|
50
|
+
# all items, $3 = space-separated in-flight keys, $4 = space-separated
|
|
51
|
+
# current-cohort member keys. The candidate is deferred when any edge joins
|
|
52
|
+
# it to a key in the union of $3 and $4, in either endpoint position.
|
|
53
|
+
# Returns 0 with PM_RESULT holding the decision; admission never rejects.
|
|
54
|
+
local candidate="$1" edges="$2" edge first second outcome="admit_current_cohort"
|
|
55
|
+
local recompute=false
|
|
56
|
+
PM_RESULT=""
|
|
57
|
+
PM_ERROR=""
|
|
58
|
+
pcoh_split_words "$3 $4"
|
|
59
|
+
local blocking=" ${PCOH_WORDS[*]} "
|
|
60
|
+
pcoh_split_words "$edges"
|
|
61
|
+
local -a edge_list=("${PCOH_WORDS[@]}")
|
|
62
|
+
# Scan every edge for one joining the candidate to a blocking key; the
|
|
63
|
+
# first such edge settles the decision.
|
|
64
|
+
for edge in "${edge_list[@]}"; do
|
|
65
|
+
first="${edge%%:*}"
|
|
66
|
+
second="${edge#*:}"
|
|
67
|
+
if { [[ $first == "$candidate" ]] && [[ $blocking == *" $second "* ]]; } ||
|
|
68
|
+
{ [[ $second == "$candidate" ]] && [[ $blocking == *" $first "* ]]; }; then
|
|
69
|
+
outcome="defer_and_recolor"
|
|
70
|
+
recompute=true
|
|
71
|
+
break
|
|
72
|
+
fi
|
|
73
|
+
done
|
|
74
|
+
PM_RESULT="{\"candidate\":$candidate,\"outcome\":\"$outcome\",\"triggers_recompute\":$recompute}"
|
|
75
|
+
return 0
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
pm_build_add_entry() {
|
|
79
|
+
# Build the single mutations[] record for an accepted add.
|
|
80
|
+
#
|
|
81
|
+
# Args: $1 = item key, $2 = `true` for a deferred add (stamps g + 1),
|
|
82
|
+
# otherwise `false` (stamps g), $3 = the generation before the add, $4 =
|
|
83
|
+
# the `at` timestamp string. Returns 0 with PM_RESULT holding the record in
|
|
84
|
+
# F3 field order, 1 with PM_ERROR holding the reference contract message.
|
|
85
|
+
local item_key="$1" deferred="$2" generation="$3" at="$4" stamped
|
|
86
|
+
PM_RESULT=""
|
|
87
|
+
PM_ERROR=""
|
|
88
|
+
stamped="$generation"
|
|
89
|
+
if [[ $deferred == true ]]; then
|
|
90
|
+
stamped=$((generation + 1))
|
|
91
|
+
fi
|
|
92
|
+
if ((stamped < 0)); then
|
|
93
|
+
pm_fail "$(pm_entry_contract_message recolor_generation "$stamped" add)"
|
|
94
|
+
return 1
|
|
95
|
+
fi
|
|
96
|
+
if ((item_key <= 0)); then
|
|
97
|
+
pm_fail "$(pm_entry_contract_message item_key "$item_key" add)"
|
|
98
|
+
return 1
|
|
99
|
+
fi
|
|
100
|
+
PM_RESULT="{\"op\":\"add\",\"item_key\":$item_key,\"at\":$(pm_json_string "$at"),\"prior_state\":null,\"new_state\":\"scheduled\",\"disposition\":null,\"recolor_generation\":$stamped}"
|
|
101
|
+
return 0
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
pm_in_flight_list() {
|
|
105
|
+
# Echo a key list as Python renders a list of ints, ascending.
|
|
106
|
+
#
|
|
107
|
+
# Args: $1 = space-separated keys, at least one.
|
|
108
|
+
local ordered key rendered=""
|
|
109
|
+
pcoh_split_words "$1"
|
|
110
|
+
ordered=$(printf '%s\n' "${PCOH_WORDS[@]}" | LC_ALL=C sort -n)
|
|
111
|
+
while IFS= read -r key; do
|
|
112
|
+
[[ -n $key ]] || continue
|
|
113
|
+
[[ -z $rendered ]] || rendered="$rendered, "
|
|
114
|
+
rendered="$rendered$key"
|
|
115
|
+
done <<<"$ordered"
|
|
116
|
+
printf '[%s]' "$rendered"
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
pm_decide_close() {
|
|
120
|
+
# Gate a run close on no item being in flight.
|
|
121
|
+
#
|
|
122
|
+
# Args: $1 = space-separated `<key>:<state>` tokens, one per tracked item.
|
|
123
|
+
# Each token is validated in supplied order as the reference ItemRecord
|
|
124
|
+
# constructor validates it: a non-positive key names nothing the run
|
|
125
|
+
# tracks, and an out-of-enum state is rejected with the item-state members.
|
|
126
|
+
# Returns 0 with PM_RESULT `{"close_permitted":true}`, or 1 with PM_ERROR
|
|
127
|
+
# naming every in-flight key ascending.
|
|
128
|
+
local token key state in_flight=""
|
|
129
|
+
PM_RESULT=""
|
|
130
|
+
PM_ERROR=""
|
|
131
|
+
pcoh_split_words "$1"
|
|
132
|
+
local -a tokens=("${PCOH_WORDS[@]}")
|
|
133
|
+
for token in "${tokens[@]}"; do
|
|
134
|
+
key="${token%%:*}"
|
|
135
|
+
state="${token#*:}"
|
|
136
|
+
if ((key <= 0)); then
|
|
137
|
+
pm_fail "$(pm_unknown_item_message "$key")"
|
|
138
|
+
return 1
|
|
139
|
+
fi
|
|
140
|
+
if ! pc_enum_members_contains "$PC_VALID_ITEM_STATES" "$state"; then
|
|
141
|
+
pm_fail "$PM_REJECTION_PREFIX item $key state $(pc_repr str "$state") is not one of $PC_VALID_ITEM_STATES."
|
|
142
|
+
return 1
|
|
143
|
+
fi
|
|
144
|
+
if [[ $state == "$PM_PINNED_STATE" ]]; then
|
|
145
|
+
in_flight="$in_flight $key"
|
|
146
|
+
fi
|
|
147
|
+
done
|
|
148
|
+
if [[ -n $in_flight ]]; then
|
|
149
|
+
pm_fail "$PM_REJECTION_PREFIX close requires no item in flight; still in flight: $(pm_in_flight_list "$in_flight")."
|
|
150
|
+
return 1
|
|
151
|
+
fi
|
|
152
|
+
PM_RESULT='{"close_permitted":true}'
|
|
153
|
+
return 0
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
pm_build_close_entry() {
|
|
157
|
+
# Build the single run-scoped mutations[] record for an accepted close.
|
|
158
|
+
#
|
|
159
|
+
# Args: $1 = the generation, stamped unchanged, $2 = the `at` timestamp
|
|
160
|
+
# string. Returns 0 with PM_RESULT holding the record, 1 with PM_ERROR when
|
|
161
|
+
# the generation is negative.
|
|
162
|
+
local generation="$1" at="$2"
|
|
163
|
+
PM_RESULT=""
|
|
164
|
+
PM_ERROR=""
|
|
165
|
+
if ((generation < 0)); then
|
|
166
|
+
pm_fail "$(pm_entry_contract_message recolor_generation "$generation" close)"
|
|
167
|
+
return 1
|
|
168
|
+
fi
|
|
169
|
+
PM_RESULT="{\"op\":\"close\",\"item_key\":null,\"at\":$(pm_json_string "$at"),\"prior_state\":null,\"new_state\":null,\"disposition\":null,\"recolor_generation\":$generation}"
|
|
170
|
+
return 0
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
pm_build_requeue_entry() {
|
|
174
|
+
# Build the single mutations[] record for a drift-induced requeue.
|
|
175
|
+
#
|
|
176
|
+
# Args: $1 = item key, $2 = the generation before the requeue (stamps
|
|
177
|
+
# g + 1), $3 = the `at` timestamp string. Returns 0 with PM_RESULT holding
|
|
178
|
+
# the record, 1 with PM_ERROR holding the reference contract message.
|
|
179
|
+
local item_key="$1" generation="$2" at="$3" stamped
|
|
180
|
+
PM_RESULT=""
|
|
181
|
+
PM_ERROR=""
|
|
182
|
+
stamped=$((generation + 1))
|
|
183
|
+
if ((stamped < 0)); then
|
|
184
|
+
pm_fail "$(pm_entry_contract_message recolor_generation "$stamped" requeue)"
|
|
185
|
+
return 1
|
|
186
|
+
fi
|
|
187
|
+
if ((item_key <= 0)); then
|
|
188
|
+
pm_fail "$(pm_entry_contract_message item_key "$item_key" requeue)"
|
|
189
|
+
return 1
|
|
190
|
+
fi
|
|
191
|
+
PM_RESULT="{\"op\":\"requeue\",\"item_key\":$item_key,\"at\":$(pm_json_string "$at"),\"prior_state\":\"$PM_PINNED_STATE\",\"new_state\":\"blocked\",\"disposition\":null,\"recolor_generation\":$stamped}"
|
|
192
|
+
return 0
|
|
193
|
+
}
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# parallel-mutation.sh: sourceable bash port of the three removal functions of
|
|
3
|
+
# the parallel mutation engine, so a workspace that received the Claude
|
|
4
|
+
# customization payload can decide a removal, recolor the unstarted subgraph,
|
|
5
|
+
# and build the removal's mutations[] record without an interpreter.
|
|
6
|
+
#
|
|
7
|
+
# Parity reference (the repository authority; this file reproduces its
|
|
8
|
+
# observable outputs and rejection messages byte for byte):
|
|
9
|
+
# decide_removal scripts/dev_tools/parallel_mutation_protocol.py
|
|
10
|
+
# recolor_unstarted scripts/dev_tools/parallel_mutation_protocol.py
|
|
11
|
+
# build_remove_entry scripts/dev_tools/_parallel_mutation_entries.py
|
|
12
|
+
# MutationEntry scripts/dev_tools/_parallel_mutation_models.py
|
|
13
|
+
# rejection messages scripts/dev_tools/_parallel_mutation_errors.py
|
|
14
|
+
#
|
|
15
|
+
# Declared divergences (also listed in both parity lanes):
|
|
16
|
+
# 1. A `--removal-disposition` value outside `detach abandon` is a usage error
|
|
17
|
+
# (exit 2) raised by the command line before the library runs. A direct
|
|
18
|
+
# library call and the Python `UnknownEnumMemberError` both reject it with
|
|
19
|
+
# the same message, which lists `detach, abandon`
|
|
20
|
+
# (spec D5; aligned under #890).
|
|
21
|
+
# 2. `at` is a caller-supplied string instead of a `datetime`.
|
|
22
|
+
# 3. Option abbreviations are rejected.
|
|
23
|
+
#
|
|
24
|
+
# Contract. Every public function is pure: no file I/O, no network access, no
|
|
25
|
+
# clock read, and no external utility beyond the ones parallel-cohorts.sh
|
|
26
|
+
# already uses. Each sets PM_RESULT to one compact JSON object and returns 0 on
|
|
27
|
+
# success, or sets PM_ERROR to the exact reference message and returns 1 on a
|
|
28
|
+
# rule rejection. Integer arguments are expected in canonical decimal form; the
|
|
29
|
+
# entry point remove-parallel-item.sh validates them before calling here.
|
|
30
|
+
#
|
|
31
|
+
# shellcheck disable=SC2034
|
|
32
|
+
# SC2034 is disabled file-wide because PM_RESULT and PM_ERROR are written here
|
|
33
|
+
# and read by the entry point remove-parallel-item.sh and by the bats suites,
|
|
34
|
+
# which shellcheck analyses separately.
|
|
35
|
+
|
|
36
|
+
# Resolve this file's own directory so its dependencies source regardless of
|
|
37
|
+
# the caller's working directory.
|
|
38
|
+
PM_LIB_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
|
39
|
+
# shellcheck source=.claude/lib/bash/parallel-cohorts.sh
|
|
40
|
+
# shellcheck disable=SC1091
|
|
41
|
+
source "$PM_LIB_DIR/parallel-cohorts.sh"
|
|
42
|
+
|
|
43
|
+
# The compact JSON object produced by the most recent success.
|
|
44
|
+
PM_RESULT=""
|
|
45
|
+
|
|
46
|
+
# The rejection message set by the most recent non-zero return.
|
|
47
|
+
PM_ERROR=""
|
|
48
|
+
|
|
49
|
+
# Literal prefix shared by every engine rejection message.
|
|
50
|
+
PM_REJECTION_PREFIX="Parallel mutation rejected:"
|
|
51
|
+
|
|
52
|
+
# Item states whose items have not started (vertices of the recolored subgraph).
|
|
53
|
+
PM_UNSTARTED_STATES="proposed admitted prepared scheduled"
|
|
54
|
+
|
|
55
|
+
# The single item state that pins an item against recoloring.
|
|
56
|
+
PM_PINNED_STATE="in_flight"
|
|
57
|
+
|
|
58
|
+
# The two accepted dispositions of an in-flight removal, in canonical order.
|
|
59
|
+
PM_VALID_DISPOSITIONS="detach abandon"
|
|
60
|
+
|
|
61
|
+
pm_fail() {
|
|
62
|
+
# Record a rejection message and return 1.
|
|
63
|
+
#
|
|
64
|
+
# Args: $1 = the complete, literal rejection message.
|
|
65
|
+
PM_ERROR="$1"
|
|
66
|
+
return 1
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
pm_json_string() {
|
|
70
|
+
# Echo a JSON string literal for a raw value.
|
|
71
|
+
#
|
|
72
|
+
# Args: $1 = the raw value. Escapes the backslash first so a later escape's
|
|
73
|
+
# own backslash is not doubled, then the quote and the three control
|
|
74
|
+
# characters a caller-supplied token could carry.
|
|
75
|
+
local body="$1"
|
|
76
|
+
body=${body//\\/\\\\}
|
|
77
|
+
body=${body//\"/\\\"}
|
|
78
|
+
body=${body//$'\n'/\\n}
|
|
79
|
+
body=${body//$'\r'/\\r}
|
|
80
|
+
body=${body//$'\t'/\\t}
|
|
81
|
+
printf '"%s"' "$body"
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
pm_json_nullable_string() {
|
|
85
|
+
# Echo a JSON string literal, or null for an empty value.
|
|
86
|
+
#
|
|
87
|
+
# Args: $1 = the raw value; empty means absent (Python None).
|
|
88
|
+
if [[ -z ${1-} ]]; then
|
|
89
|
+
printf 'null'
|
|
90
|
+
else
|
|
91
|
+
pm_json_string "$1"
|
|
92
|
+
fi
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
pm_unknown_item_message() {
|
|
96
|
+
# Echo the UnknownItemError message for one item key.
|
|
97
|
+
#
|
|
98
|
+
# Args: $1 = the offending integer key, rendered as Python repr renders it.
|
|
99
|
+
printf '%s item key %s does not resolve to a tracked items[].issue_num.' \
|
|
100
|
+
"$PM_REJECTION_PREFIX" "$1"
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
pm_entry_contract_message() {
|
|
104
|
+
# Echo the MutationEntryContractError message for one op.
|
|
105
|
+
#
|
|
106
|
+
# Args: $1 = field name, $2 = the already-rendered repr of the value, $3 =
|
|
107
|
+
# the op name, optional and defaulting to `remove` so every removal call
|
|
108
|
+
# site passes two arguments unchanged.
|
|
109
|
+
printf "Parallel mutation entry rejected: %s %s violates the F3 mutations[] contract for op '%s'." \
|
|
110
|
+
"$1" "$2" "${3:-remove}"
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
pm_decide_removal() {
|
|
114
|
+
# Decide the outcome of removing one item, per the FR2 behavior table.
|
|
115
|
+
#
|
|
116
|
+
# Args: $1 = item key, $2 = the item's state, or empty when the key resolves
|
|
117
|
+
# to no tracked item, $3 = disposition, or empty when none was supplied.
|
|
118
|
+
# Returns 0 with PM_RESULT holding the decision, 1 with PM_ERROR holding the
|
|
119
|
+
# reference rejection message.
|
|
120
|
+
local item_key="$1" state="${2-}" disposition="${3-}" prior
|
|
121
|
+
PM_RESULT=""
|
|
122
|
+
PM_ERROR=""
|
|
123
|
+
# An absent record and a non-positive key both name nothing the run tracks;
|
|
124
|
+
# the reference rejects the latter while constructing the item record.
|
|
125
|
+
if [[ -z $state ]] || ((item_key <= 0)); then
|
|
126
|
+
pm_fail "$(pm_unknown_item_message "$item_key")"
|
|
127
|
+
return 1
|
|
128
|
+
fi
|
|
129
|
+
if ! pc_enum_members_contains "$PC_VALID_ITEM_STATES" "$state"; then
|
|
130
|
+
pm_fail "$PM_REJECTION_PREFIX item $item_key state $(pc_repr str "$state") is not one of $PC_VALID_ITEM_STATES."
|
|
131
|
+
return 1
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
# Routing table, one branch per FR2 row. An unstarted item is a vertex of
|
|
135
|
+
# the recolored subgraph, so its removal recomputes and records no
|
|
136
|
+
# disposition even when one was supplied.
|
|
137
|
+
if pc_contains_word "$PM_UNSTARTED_STATES" "$state"; then
|
|
138
|
+
PM_RESULT="{\"item_key\":$item_key,\"prior_state\":$(pm_json_string "$state"),\"new_state\":\"withdrawn\",\"disposition\":null,\"triggers_recompute\":true}"
|
|
139
|
+
return 0
|
|
140
|
+
fi
|
|
141
|
+
if [[ $state == "$PM_PINNED_STATE" ]]; then
|
|
142
|
+
if [[ -z $disposition ]]; then
|
|
143
|
+
pm_fail "$PM_REJECTION_PREFIX removal of in-flight item $item_key requires an explicit disposition, one of detach, abandon; no default is inferred."
|
|
144
|
+
return 1
|
|
145
|
+
fi
|
|
146
|
+
if ! pc_contains_word "$PM_VALID_DISPOSITIONS" "$disposition"; then
|
|
147
|
+
pm_fail "$PM_REJECTION_PREFIX item $item_key disposition $(pc_repr str "$disposition") is not one of ${PM_VALID_DISPOSITIONS// /, }."
|
|
148
|
+
return 1
|
|
149
|
+
fi
|
|
150
|
+
prior="$PM_PINNED_STATE"
|
|
151
|
+
PM_RESULT="{\"item_key\":$item_key,\"prior_state\":\"$prior\",\"new_state\":\"withdrawn\",\"disposition\":\"$disposition\",\"triggers_recompute\":false}"
|
|
152
|
+
return 0
|
|
153
|
+
fi
|
|
154
|
+
if [[ $state == merged ]]; then
|
|
155
|
+
pm_fail "$PM_REJECTION_PREFIX item $item_key is already merged into main and cannot be removed."
|
|
156
|
+
return 1
|
|
157
|
+
fi
|
|
158
|
+
# Only withdrawn and blocked remain; neither is a live removal target.
|
|
159
|
+
pm_fail "$(pm_unknown_item_message "$item_key")"
|
|
160
|
+
return 1
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
pm_smallest_overlap() {
|
|
164
|
+
# Echo the smallest key present in both key lists, or nothing.
|
|
165
|
+
#
|
|
166
|
+
# Args: $1 = space-separated unstarted keys, $2 = space-separated pinned keys.
|
|
167
|
+
local key smallest=""
|
|
168
|
+
pcoh_split_words "$1"
|
|
169
|
+
local -a unstarted_keys=("${PCOH_WORDS[@]}")
|
|
170
|
+
pcoh_split_words "$2"
|
|
171
|
+
local pinned=" ${PCOH_WORDS[*]} "
|
|
172
|
+
for key in "${unstarted_keys[@]}"; do
|
|
173
|
+
[[ $pinned == *" $key "* ]] || continue
|
|
174
|
+
if [[ -z $smallest ]] || ((key < smallest)); then
|
|
175
|
+
smallest="$key"
|
|
176
|
+
fi
|
|
177
|
+
done
|
|
178
|
+
printf '%s' "$smallest"
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
pm_render_assignments() {
|
|
182
|
+
# Echo the cohort_assignments object for a cohort list and an offset.
|
|
183
|
+
#
|
|
184
|
+
# Args: $1 = compact JSON array of arrays from pcoh_compute_cohorts, $2 = the
|
|
185
|
+
# uniform offset. List position is the local color index; keys are emitted
|
|
186
|
+
# in ascending numeric order, matching the reference rendering.
|
|
187
|
+
local cohorts="$1" offset="$2" group key index=0 lines="" ordered rendered=""
|
|
188
|
+
# Strip the outer brackets and the first and last inner bracket, then turn
|
|
189
|
+
# each `],[` boundary into a space so every group is one comma-joined word.
|
|
190
|
+
cohorts=${cohorts#[}
|
|
191
|
+
cohorts=${cohorts%]}
|
|
192
|
+
cohorts=${cohorts#[}
|
|
193
|
+
cohorts=${cohorts%]}
|
|
194
|
+
pcoh_split_words "${cohorts//'],['/ }"
|
|
195
|
+
local -a groups=("${PCOH_WORDS[@]}")
|
|
196
|
+
for group in "${groups[@]}"; do
|
|
197
|
+
pcoh_split_words "${group//,/ }"
|
|
198
|
+
for key in "${PCOH_WORDS[@]}"; do
|
|
199
|
+
lines="$lines$key $((offset + index))"$'\n'
|
|
200
|
+
done
|
|
201
|
+
index=$((index + 1))
|
|
202
|
+
done
|
|
203
|
+
if [[ -n $lines ]]; then
|
|
204
|
+
ordered=$(printf '%s' "$lines" | LC_ALL=C sort -k1,1n)
|
|
205
|
+
while IFS=' ' read -r key index; do
|
|
206
|
+
[[ -n $key ]] || continue
|
|
207
|
+
[[ -z $rendered ]] || rendered="$rendered,"
|
|
208
|
+
rendered="$rendered\"$key\":$index"
|
|
209
|
+
done <<<"$ordered"
|
|
210
|
+
fi
|
|
211
|
+
printf '{%s}' "$rendered"
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
pm_recolor_unstarted() {
|
|
215
|
+
# Recolor the unstarted subgraph, leaving every pinned item untouched.
|
|
216
|
+
#
|
|
217
|
+
# Args: $1 = space-separated unstarted keys, $2 = space-separated `a:b`
|
|
218
|
+
# conflict edges over all items, $3 = space-separated pinned keys, $4 = the
|
|
219
|
+
# generation before this recolor, $5 = current_cohort, $6 =
|
|
220
|
+
# highest_pinned_cohort. Returns 0 with PM_RESULT holding the assignments
|
|
221
|
+
# and the next generation, 1 with PM_ERROR holding the reference message.
|
|
222
|
+
local unstarted="$1" edges="$2" pinned="$3" generation="$4"
|
|
223
|
+
local current_cohort="$5" highest_pinned="$6"
|
|
224
|
+
local overlap edge first second crosses=0 induced="" offset
|
|
225
|
+
PM_RESULT=""
|
|
226
|
+
PM_ERROR=""
|
|
227
|
+
|
|
228
|
+
# Check order is part of the contract: overlap, then the negative base,
|
|
229
|
+
# then the pinned barrier, then the induced coloring.
|
|
230
|
+
overlap=$(pm_smallest_overlap "$unstarted" "$pinned")
|
|
231
|
+
if [[ -n $overlap ]]; then
|
|
232
|
+
pm_fail "$(pm_unknown_item_message "$overlap")"
|
|
233
|
+
return 1
|
|
234
|
+
fi
|
|
235
|
+
if ((current_cohort < 0)); then
|
|
236
|
+
pm_fail "current_cohort must be >= 0 per F3 invariant 12; received $current_cohort."
|
|
237
|
+
return 1
|
|
238
|
+
fi
|
|
239
|
+
|
|
240
|
+
pcoh_split_words "$unstarted"
|
|
241
|
+
local unstarted_set=" ${PCOH_WORDS[*]} "
|
|
242
|
+
pcoh_split_words "$pinned"
|
|
243
|
+
local pinned_set=" ${PCOH_WORDS[*]} "
|
|
244
|
+
pcoh_split_words "$edges"
|
|
245
|
+
local -a edge_list=("${PCOH_WORDS[@]}")
|
|
246
|
+
# Decide the pinned barrier before the induced restriction discards the
|
|
247
|
+
# unstarted-to-pinned edges, then keep only edges with both ends unstarted.
|
|
248
|
+
for edge in "${edge_list[@]}"; do
|
|
249
|
+
first="${edge%%:*}"
|
|
250
|
+
second="${edge#*:}"
|
|
251
|
+
if [[ $unstarted_set == *" $first "* && $pinned_set == *" $second "* ]] ||
|
|
252
|
+
[[ $unstarted_set == *" $second "* && $pinned_set == *" $first "* ]]; then
|
|
253
|
+
crosses=1
|
|
254
|
+
fi
|
|
255
|
+
if [[ $unstarted_set == *" $first "* && $unstarted_set == *" $second "* ]]; then
|
|
256
|
+
induced="$induced $edge"
|
|
257
|
+
fi
|
|
258
|
+
done
|
|
259
|
+
|
|
260
|
+
if ! pcoh_compute_cohorts "$unstarted" "$induced"; then
|
|
261
|
+
pm_fail "$PCOH_ERROR"
|
|
262
|
+
return 1
|
|
263
|
+
fi
|
|
264
|
+
|
|
265
|
+
# A uniform shift keeps the local-to-absolute map injective.
|
|
266
|
+
if ((crosses == 1)); then
|
|
267
|
+
offset=$((highest_pinned + 1))
|
|
268
|
+
else
|
|
269
|
+
offset="$current_cohort"
|
|
270
|
+
fi
|
|
271
|
+
PM_RESULT="{\"cohort_assignments\":$(pm_render_assignments "$PCOH_RESULT" "$offset"),\"generation\":$((generation + 1))}"
|
|
272
|
+
return 0
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
pm_build_remove_entry() {
|
|
276
|
+
# Build the single mutations[] record for an accepted removal.
|
|
277
|
+
#
|
|
278
|
+
# Args: $1 = item key, $2 = prior state, $3 = disposition, or empty for
|
|
279
|
+
# none, $4 = `true` when the removal recomputes, otherwise `false`, $5 = the
|
|
280
|
+
# generation before the removal, $6 = the `at` timestamp string. Returns 0
|
|
281
|
+
# with PM_RESULT holding the record in F3 field order, 1 with PM_ERROR
|
|
282
|
+
# holding the reference contract message.
|
|
283
|
+
local item_key="$1" prior_state="$2" disposition="${3-}" recompute="$4"
|
|
284
|
+
local generation="$5" at="$6" stamped
|
|
285
|
+
PM_RESULT=""
|
|
286
|
+
PM_ERROR=""
|
|
287
|
+
# The stamped generation is computed first; the record validation then
|
|
288
|
+
# runs in field order, so the first violation names the earliest field.
|
|
289
|
+
stamped="$generation"
|
|
290
|
+
if [[ $recompute == true ]]; then
|
|
291
|
+
stamped=$((generation + 1))
|
|
292
|
+
fi
|
|
293
|
+
if ((stamped < 0)); then
|
|
294
|
+
pm_fail "$(pm_entry_contract_message recolor_generation "$stamped")"
|
|
295
|
+
return 1
|
|
296
|
+
fi
|
|
297
|
+
if ((item_key <= 0)); then
|
|
298
|
+
pm_fail "$(pm_entry_contract_message item_key "$item_key")"
|
|
299
|
+
return 1
|
|
300
|
+
fi
|
|
301
|
+
if ! pc_enum_members_contains "$PC_VALID_ITEM_STATES" "$prior_state"; then
|
|
302
|
+
pm_fail "$PM_REJECTION_PREFIX item $item_key state $(pc_repr str "$prior_state") is not one of $PC_VALID_ITEM_STATES."
|
|
303
|
+
return 1
|
|
304
|
+
fi
|
|
305
|
+
# An in-flight removal must record a valid disposition; every other
|
|
306
|
+
# removal must record none.
|
|
307
|
+
if [[ $prior_state == "$PM_PINNED_STATE" ]]; then
|
|
308
|
+
if [[ -z $disposition ]]; then
|
|
309
|
+
pm_fail "$(pm_entry_contract_message disposition None)"
|
|
310
|
+
return 1
|
|
311
|
+
fi
|
|
312
|
+
if ! pc_contains_word "$PM_VALID_DISPOSITIONS" "$disposition"; then
|
|
313
|
+
pm_fail "$(pm_entry_contract_message disposition "$(pc_repr str "$disposition")")"
|
|
314
|
+
return 1
|
|
315
|
+
fi
|
|
316
|
+
elif [[ -n $disposition ]]; then
|
|
317
|
+
pm_fail "$(pm_entry_contract_message disposition "$(pc_repr str "$disposition")")"
|
|
318
|
+
return 1
|
|
319
|
+
fi
|
|
320
|
+
PM_RESULT="{\"op\":\"remove\",\"item_key\":$item_key,\"at\":$(pm_json_string "$at"),\"prior_state\":$(pm_json_string "$prior_state"),\"new_state\":\"withdrawn\",\"disposition\":$(pm_json_nullable_string "$disposition"),\"recolor_generation\":$stamped}"
|
|
321
|
+
return 0
|
|
322
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# remove-parallel-item.sh: destination-portable command-line entry point for
|
|
3
|
+
# the parallel-remove skill's decision, recolor, and audit-record steps. It
|
|
4
|
+
# exists so a workspace that received the Claude customization payload can run
|
|
5
|
+
# those steps with nothing but bash -- no interpreter, no repository checkout.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# bash .claude/lib/bash/remove-parallel-item.sh decide --item <key> \
|
|
9
|
+
# [--state <state>] [--removal-disposition detach|abandon]
|
|
10
|
+
# bash .claude/lib/bash/remove-parallel-item.sh recolor --unstarted "<k> ..." \
|
|
11
|
+
# [--edges "<a>:<b> ..."] --pinned "<k> ..." --generation <g> \
|
|
12
|
+
# --current-cohort <c> --highest-pinned-cohort <h>
|
|
13
|
+
# bash .claude/lib/bash/remove-parallel-item.sh entry --item <key> \
|
|
14
|
+
# --prior-state <state> [--removal-disposition detach|abandon] \
|
|
15
|
+
# --recompute true|false --generation <g> [--at <timestamp>]
|
|
16
|
+
#
|
|
17
|
+
# `decide` without `--state` means the key resolves to no tracked item.
|
|
18
|
+
# `--unstarted`, `--edges`, and `--pinned` accept an empty string. Integer
|
|
19
|
+
# tokens match `-?(0|[1-9][0-9]*)`. `--at` defaults to the current UTC time as
|
|
20
|
+
# yyyy-MM-ddTHH-mm; the clock is read only when `--at` is omitted.
|
|
21
|
+
#
|
|
22
|
+
# Exit-code contract:
|
|
23
|
+
# exit 0 success; one compact JSON object on stdout
|
|
24
|
+
# exit 1 rule rejection; the reference implementation's exact message as
|
|
25
|
+
# one stderr line
|
|
26
|
+
# exit 2 usage error; one stderr line beginning
|
|
27
|
+
# "PARALLEL_MUTATION_ERROR: usage error:"
|
|
28
|
+
# Argument validation runs first, then rule rejection, then output.
|
|
29
|
+
#
|
|
30
|
+
# Parity reference: scripts/dev_tools/parallel_mutation_protocol.py
|
|
31
|
+
# (decide_removal, recolor_unstarted) and
|
|
32
|
+
# scripts/dev_tools/_parallel_mutation_entries.py (build_remove_entry).
|
|
33
|
+
#
|
|
34
|
+
# Declared divergences (also listed in both parity lanes):
|
|
35
|
+
# 1. A `--removal-disposition` value outside `detach abandon` is a usage error
|
|
36
|
+
# (exit 2) raised by the command line before the library runs. A direct
|
|
37
|
+
# library call and the Python `UnknownEnumMemberError` both reject it with
|
|
38
|
+
# the same message, which lists `detach, abandon`
|
|
39
|
+
# (spec D5; aligned under #890).
|
|
40
|
+
# 2. `at` is a caller-supplied string instead of a `datetime`.
|
|
41
|
+
# 3. Option abbreviations are rejected.
|
|
42
|
+
set -euo pipefail
|
|
43
|
+
|
|
44
|
+
# Resolve this script's own directory so the libraries source regardless of
|
|
45
|
+
# cwd. The option-parsing helpers (pmcli_*) are shared with
|
|
46
|
+
# mutate-parallel-run.sh through parallel-mutation-cli.sh (issue #890).
|
|
47
|
+
RPI_SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
|
48
|
+
# shellcheck source=.claude/lib/bash/parallel-mutation.sh
|
|
49
|
+
# shellcheck disable=SC1091
|
|
50
|
+
source "$RPI_SCRIPT_DIR/parallel-mutation.sh"
|
|
51
|
+
# shellcheck source=.claude/lib/bash/parallel-mutation-cli.sh
|
|
52
|
+
# shellcheck disable=SC1091
|
|
53
|
+
source "$RPI_SCRIPT_DIR/parallel-mutation-cli.sh"
|
|
54
|
+
|
|
55
|
+
pc_enforce_c_locale
|
|
56
|
+
|
|
57
|
+
rpi_disposition() {
|
|
58
|
+
# Validate the optional disposition; store it, or empty when absent.
|
|
59
|
+
PMCLI_VALUE="${PMCLI_OPTS["removal-disposition"]-}"
|
|
60
|
+
if [[ -n ${PMCLI_OPTS["removal-disposition"]+set} ]] && ! pc_contains_word "detach abandon" "$PMCLI_VALUE"; then
|
|
61
|
+
pmcli_usage_error "--removal-disposition must be one of detach, abandon; found: $PMCLI_VALUE"
|
|
62
|
+
fi
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
rpi_decide() {
|
|
66
|
+
# Run the decide subcommand.
|
|
67
|
+
local item state="" disposition rc=0
|
|
68
|
+
pmcli_parse_options "item state removal-disposition" "$@"
|
|
69
|
+
pmcli_require item
|
|
70
|
+
pmcli_integer "${PMCLI_OPTS[item]}" "--item"
|
|
71
|
+
item="$PMCLI_VALUE"
|
|
72
|
+
if [[ -n ${PMCLI_OPTS[state]+set} ]]; then
|
|
73
|
+
state="${PMCLI_OPTS[state]}"
|
|
74
|
+
pc_enum_members_contains "$PC_VALID_ITEM_STATES" "$state" ||
|
|
75
|
+
pmcli_usage_error "--state must be one of $PC_VALID_ITEM_STATES; found: $state"
|
|
76
|
+
fi
|
|
77
|
+
rpi_disposition
|
|
78
|
+
disposition="$PMCLI_VALUE"
|
|
79
|
+
pm_decide_removal "$item" "$state" "$disposition" || rc=$?
|
|
80
|
+
pmcli_emit "$rc"
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
rpi_recolor() {
|
|
84
|
+
# Run the recolor subcommand.
|
|
85
|
+
local unstarted edges pinned generation current highest rc=0
|
|
86
|
+
pmcli_parse_options "unstarted edges pinned generation current-cohort highest-pinned-cohort" "$@"
|
|
87
|
+
pmcli_require unstarted pinned generation current-cohort highest-pinned-cohort
|
|
88
|
+
pmcli_integer_list "${PMCLI_OPTS[unstarted]}" "--unstarted"
|
|
89
|
+
unstarted="$PMCLI_VALUE"
|
|
90
|
+
pmcli_edge_list "${PMCLI_OPTS[edges]-}"
|
|
91
|
+
edges="$PMCLI_VALUE"
|
|
92
|
+
pmcli_integer_list "${PMCLI_OPTS[pinned]}" "--pinned"
|
|
93
|
+
pinned="$PMCLI_VALUE"
|
|
94
|
+
pmcli_integer "${PMCLI_OPTS[generation]}" "--generation"
|
|
95
|
+
generation="$PMCLI_VALUE"
|
|
96
|
+
pmcli_integer "${PMCLI_OPTS["current-cohort"]}" "--current-cohort"
|
|
97
|
+
current="$PMCLI_VALUE"
|
|
98
|
+
pmcli_integer "${PMCLI_OPTS["highest-pinned-cohort"]}" "--highest-pinned-cohort"
|
|
99
|
+
highest="$PMCLI_VALUE"
|
|
100
|
+
pm_recolor_unstarted "$unstarted" "$edges" "$pinned" "$generation" "$current" "$highest" || rc=$?
|
|
101
|
+
pmcli_emit "$rc"
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
rpi_entry() {
|
|
105
|
+
# Run the entry subcommand.
|
|
106
|
+
local item prior disposition recompute generation at rc=0
|
|
107
|
+
pmcli_parse_options "item prior-state removal-disposition recompute generation at" "$@"
|
|
108
|
+
pmcli_require item prior-state recompute generation
|
|
109
|
+
pmcli_integer "${PMCLI_OPTS[item]}" "--item"
|
|
110
|
+
item="$PMCLI_VALUE"
|
|
111
|
+
prior="${PMCLI_OPTS["prior-state"]}"
|
|
112
|
+
[[ $prior =~ ^[A-Za-z_]+$ ]] || pmcli_usage_error "--prior-state must match [A-Za-z_]+; found: $prior"
|
|
113
|
+
rpi_disposition
|
|
114
|
+
disposition="$PMCLI_VALUE"
|
|
115
|
+
recompute="${PMCLI_OPTS[recompute]}"
|
|
116
|
+
[[ $recompute == true || $recompute == false ]] ||
|
|
117
|
+
pmcli_usage_error "--recompute must be true or false; found: $recompute"
|
|
118
|
+
pmcli_integer "${PMCLI_OPTS[generation]}" "--generation"
|
|
119
|
+
generation="$PMCLI_VALUE"
|
|
120
|
+
# The clock is read only when --at is omitted.
|
|
121
|
+
pmcli_timestamp
|
|
122
|
+
at="$PMCLI_VALUE"
|
|
123
|
+
pm_build_remove_entry "$item" "$prior" "$disposition" "$recompute" "$generation" "$at" || rc=$?
|
|
124
|
+
pmcli_emit "$rc"
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
rpi_main() {
|
|
128
|
+
# Dispatch on the subcommand.
|
|
129
|
+
(($# > 0)) || pmcli_usage_error "missing subcommand; expected one of decide, recolor, entry"
|
|
130
|
+
local subcommand="$1"
|
|
131
|
+
shift
|
|
132
|
+
case "$subcommand" in
|
|
133
|
+
decide) rpi_decide "$@" ;;
|
|
134
|
+
recolor) rpi_recolor "$@" ;;
|
|
135
|
+
entry) rpi_entry "$@" ;;
|
|
136
|
+
*) pmcli_usage_error "unknown subcommand: $subcommand" ;;
|
|
137
|
+
esac
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
# Guard so the file can be sourced without executing main. main's return code
|
|
141
|
+
# is captured and re-exited explicitly as the final statement.
|
|
142
|
+
if [[ ${BASH_SOURCE[0]} == "${0}" ]]; then
|
|
143
|
+
rpi_rc=0
|
|
144
|
+
rpi_main "$@" || rpi_rc=$?
|
|
145
|
+
exit "$rpi_rc"
|
|
146
|
+
fi
|