@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
|
@@ -8,17 +8,29 @@
|
|
|
8
8
|
under one of the two tracked research roots
|
|
9
9
|
(docs/features/<feature>/research/ for feature-associated research, or
|
|
10
10
|
docs/research/ for one-off research), matches the documented filename
|
|
11
|
-
convention, and the file exists on disk.
|
|
11
|
+
convention, and the file exists on disk. Feature-associated research under
|
|
12
|
+
docs/features/ must also carry a complete `## Pre-Mortem` section
|
|
13
|
+
(Test-PreMortemSection in premortem-section.ps1, issue #964); one-off
|
|
14
|
+
research under docs/research/ is exempt.
|
|
12
15
|
|
|
13
16
|
.NOTES
|
|
14
|
-
Reads the
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
Reads the SubagentStop envelope JSON from stdin through the shared
|
|
18
|
+
envelope helper (subagentstop-envelope.ps1) and validates its
|
|
19
|
+
last_assistant_message field, the researcher's last assistant message.
|
|
20
|
+
Exits 0 to allow termination. A block writes the reason to stderr and
|
|
21
|
+
exits 2 through the shared envelope helper; an invalid envelope blocks
|
|
22
|
+
with a payload anomaly reason. A hook dependency that fails to load
|
|
23
|
+
blocks with exit 2 and the validate-task-researcher-output: prefix.
|
|
24
|
+
Filesystem reads go through Test-ResearchFile and Get-ResearchFileContent
|
|
25
|
+
so tests can mock the boundary without writing temporary files.
|
|
26
|
+
An unhandled error during validation blocks with exit 2 and a reason (issue #928).
|
|
18
27
|
#>
|
|
19
28
|
|
|
20
29
|
[CmdletBinding()]
|
|
21
30
|
param()
|
|
31
|
+
$script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
|
|
32
|
+
try { . (Join-Path $PSScriptRoot 'subagentstop-envelope.ps1') } catch { Add-HookDependencyFailure -Name 'subagentstop-envelope.ps1' -ErrorRecord $_ }
|
|
33
|
+
try { . (Join-Path $PSScriptRoot 'premortem-section.ps1') } catch { Add-HookDependencyFailure -Name 'premortem-section.ps1' -ErrorRecord $_ }
|
|
22
34
|
|
|
23
35
|
Set-StrictMode -Version Latest
|
|
24
36
|
$ErrorActionPreference = 'Stop'
|
|
@@ -34,6 +46,16 @@ function Test-ResearchFile {
|
|
|
34
46
|
return [bool](Test-Path -LiteralPath $Path -PathType Leaf)
|
|
35
47
|
}
|
|
36
48
|
|
|
49
|
+
function Get-ResearchFileContent {
|
|
50
|
+
# File seam: returns the research file text, or $null when no file exists at the path.
|
|
51
|
+
[CmdletBinding()]
|
|
52
|
+
[OutputType([string])]
|
|
53
|
+
param([Parameter(Mandatory = $true)] [string] $Path)
|
|
54
|
+
|
|
55
|
+
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { return $null }
|
|
56
|
+
return Get-Content -LiteralPath $Path -Raw -ErrorAction Stop
|
|
57
|
+
}
|
|
58
|
+
|
|
37
59
|
function Get-ResearchPathFromOutput {
|
|
38
60
|
[CmdletBinding()]
|
|
39
61
|
[OutputType([string])]
|
|
@@ -94,7 +116,7 @@ function Test-IsValidResearchFileName {
|
|
|
94
116
|
$fileName = [System.IO.Path]::GetFileName($normalized)
|
|
95
117
|
return [regex]::IsMatch(
|
|
96
118
|
$fileName,
|
|
97
|
-
'
|
|
119
|
+
'^(?:(?:\d{4}-\d{2}-\d{2}(?:T\d{2}-?\d{2})?|\d{8})-[A-Za-z0-9][A-Za-z0-9-]*[-.]research|research(?:-addendum)?\.\d{4}-\d{2}-\d{2}(?:T\d{2}-\d{2})?)\.md$'
|
|
98
120
|
)
|
|
99
121
|
}
|
|
100
122
|
|
|
@@ -192,7 +214,7 @@ function Test-NumericDerivationEvidence {
|
|
|
192
214
|
if (-not $match.Success) { return @{ Ok = $false; Message = "task-researcher hook: numeric derivation evidence is missing $label." } }
|
|
193
215
|
$values[$label] = $match.Groups['value'].Value.Trim()
|
|
194
216
|
}
|
|
195
|
-
if ($values['Exhaustive Search Scope'] -notmatch '(?i)\b(entire|all|complete)\b.*\b(repository|repo|source tree|tree)\b') {
|
|
217
|
+
if ($values['Exhaustive Search Scope'] -notmatch '(?i)\b(entire|whole|all|complete)\b.*\b(repository|repo|worktree|source tree|tree)\b') {
|
|
196
218
|
return @{ Ok = $false; Message = 'task-researcher hook: numeric derivation evidence does not declare an exhaustive repository search scope.' }
|
|
197
219
|
}
|
|
198
220
|
$primaryStrategy = $values['Primary Search Strategy or Query Expression']
|
|
@@ -227,29 +249,18 @@ function Invoke-TaskResearcherOutputValidation {
|
|
|
227
249
|
[CmdletBinding()]
|
|
228
250
|
[OutputType([hashtable])]
|
|
229
251
|
param(
|
|
230
|
-
[string] $
|
|
252
|
+
[AllowNull()] [AllowEmptyString()] [string] $InputText
|
|
231
253
|
)
|
|
232
|
-
|
|
233
|
-
if (
|
|
234
|
-
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
$payload = $RawPayload | ConvertFrom-Json -ErrorAction Stop
|
|
239
|
-
}
|
|
240
|
-
catch {
|
|
241
|
-
return @{ Ok = $false; Message = "task-researcher hook: failed to parse CLAUDE_HOOK_INPUT as JSON: $($_.Exception.Message)" }
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
$agentOutput = $null
|
|
245
|
-
if ($payload.PSObject.Properties.Name -contains 'output') {
|
|
246
|
-
$agentOutput = $payload.output
|
|
247
|
-
}
|
|
248
|
-
if ([string]::IsNullOrWhiteSpace($agentOutput)) {
|
|
254
|
+
$dependencyDecision = Get-HookDependencyFailureDecision -HookEvent SubagentStop -ReasonPrefix 'validate-task-researcher-output:'
|
|
255
|
+
if ($null -ne $dependencyDecision) { return $dependencyDecision }
|
|
256
|
+
$envelope = if ($PSBoundParameters.ContainsKey('InputText')) { Read-SubagentStopEnvelope -InputText $InputText } else { Read-SubagentStopEnvelope }
|
|
257
|
+
if (-not $envelope.IsValid) { return @{ Ok = $false; Message = "task-researcher hook: payload anomaly - $(Get-SubagentStopEnvelopeAnomalyReason -Anomaly $envelope.Anomaly)" } }
|
|
258
|
+
$assistantMessage = [string]$envelope.AssistantText
|
|
259
|
+
if ([string]::IsNullOrWhiteSpace($assistantMessage)) {
|
|
249
260
|
return @{ Ok = $false; Message = 'task-researcher hook: agent output is empty; researcher must return research-path before termination.' }
|
|
250
261
|
}
|
|
251
262
|
|
|
252
|
-
$researchPath = Get-ResearchPathFromOutput -AgentOutput $
|
|
263
|
+
$researchPath = Get-ResearchPathFromOutput -AgentOutput $assistantMessage
|
|
253
264
|
if ($null -eq $researchPath) {
|
|
254
265
|
return @{ Ok = $false; Message = 'task-researcher hook: agent output does not advertise a research-path. Researcher must report `research-path: <path>` pointing to docs/features/<feature>/research/ (feature-associated) or docs/research/ (one-off).' }
|
|
255
266
|
}
|
|
@@ -266,16 +277,22 @@ function Invoke-TaskResearcherOutputValidation {
|
|
|
266
277
|
return @{ Ok = $false; Message = "task-researcher hook: researcher advertised research-path '$researchPath' but no file exists at that location." }
|
|
267
278
|
}
|
|
268
279
|
|
|
269
|
-
$feasibilityResult = Test-AutomationFeasibilitySection -ResearchFilePath $researchPath -AgentOutput $
|
|
280
|
+
$feasibilityResult = Test-AutomationFeasibilitySection -ResearchFilePath $researchPath -AgentOutput $assistantMessage
|
|
270
281
|
if (-not $feasibilityResult.Ok) {
|
|
271
282
|
return @{ Ok = $false; Message = $feasibilityResult.Message }
|
|
272
283
|
}
|
|
273
284
|
|
|
274
|
-
|
|
275
|
-
|
|
285
|
+
try { $researchContent = Get-ResearchFileContent -Path $researchPath } catch { return @{ Ok = $false; Message = "task-researcher hook: failed to read research-path '$researchPath': $($_.Exception.Message)" } }
|
|
286
|
+
if ($null -ne $researchContent) {
|
|
287
|
+
$numericEvidenceResult = Test-NumericDerivationEvidence -Content $researchContent
|
|
276
288
|
if (-not $numericEvidenceResult.Ok) {
|
|
277
289
|
return $numericEvidenceResult
|
|
278
290
|
}
|
|
291
|
+
# Issue #964: feature research carries a Pre-Mortem; one-off research under docs/research/ is exempt.
|
|
292
|
+
if ((($researchPath -replace '\\', '/') -replace '^\./', '').StartsWith('docs/features/', [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
293
|
+
$preMortem = Test-PreMortemSection -Content ([string]$researchContent)
|
|
294
|
+
if (-not $preMortem.Ok) { return @{ Ok = $false; Message = "task-researcher hook: feature research '$researchPath' $($preMortem.Message)" } }
|
|
295
|
+
}
|
|
279
296
|
}
|
|
280
297
|
|
|
281
298
|
return @{ Ok = $true; Message = $null }
|
|
@@ -284,11 +301,10 @@ function Invoke-TaskResearcherOutputValidation {
|
|
|
284
301
|
if ($MyInvocation.InvocationName -eq '.') {
|
|
285
302
|
return
|
|
286
303
|
}
|
|
287
|
-
|
|
288
|
-
$
|
|
289
|
-
if (-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
293
|
-
|
|
304
|
+
if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('validate-task-researcher-output: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
|
|
305
|
+
$dependencyDecision = Get-HookDependencyFailureDecision -HookEvent SubagentStop -ReasonPrefix 'validate-task-researcher-output:'
|
|
306
|
+
if ($null -ne $dependencyDecision) { [Console]::Error.WriteLine($dependencyDecision.Reason); exit $dependencyDecision.ExitCode }
|
|
307
|
+
try { $result = Invoke-TaskResearcherOutputValidation }
|
|
308
|
+
catch { Exit-SubagentStopBlock -Message ('validate-task-researcher-output: validator error: ' + ([string]$_.Exception.Message -split "`r?`n")[0] + '; the gate fails closed.' + "`n" + 'This condition is not repairable by the subagent. Report it to the operator and halt.') }
|
|
309
|
+
if (-not $result.Ok) { Exit-SubagentStopBlock -Message $result.Message }
|
|
294
310
|
exit 0
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# mutate-parallel-run.sh: destination-portable command-line entry point for the
|
|
3
|
+
# decision and audit-record steps of the parallel-add, parallel-close, and
|
|
4
|
+
# parallel-orchestrate skills (issue #890). It exists so a workspace that
|
|
5
|
+
# received the Claude customization payload can run those steps with nothing
|
|
6
|
+
# but bash -- no interpreter, no repository checkout.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# bash .claude/lib/bash/mutate-parallel-run.sh admit --candidate <key> \
|
|
10
|
+
# [--edges "<a>:<b> ..."] --in-flight "<k> ..." \
|
|
11
|
+
# --current-cohort-members "<k> ..."
|
|
12
|
+
# bash .claude/lib/bash/mutate-parallel-run.sh recolor --unstarted "<k> ..." \
|
|
13
|
+
# [--edges "<a>:<b> ..."] --pinned "<k> ..." --generation <g> \
|
|
14
|
+
# --current-cohort <c> --highest-pinned-cohort <h>
|
|
15
|
+
# bash .claude/lib/bash/mutate-parallel-run.sh add-entry --item <key> \
|
|
16
|
+
# --deferred true|false --generation <g> [--at <timestamp>]
|
|
17
|
+
# bash .claude/lib/bash/mutate-parallel-run.sh close --items "<k>:<state> ..."
|
|
18
|
+
# bash .claude/lib/bash/mutate-parallel-run.sh close-entry --generation <g> \
|
|
19
|
+
# [--at <timestamp>]
|
|
20
|
+
# bash .claude/lib/bash/mutate-parallel-run.sh requeue-entry --item <key> \
|
|
21
|
+
# --generation <g> [--at <timestamp>]
|
|
22
|
+
#
|
|
23
|
+
# `--edges` is optional; `--in-flight`, `--current-cohort-members`,
|
|
24
|
+
# `--unstarted`, `--pinned`, and `--items` accept an empty string. Integer
|
|
25
|
+
# tokens match `-?(0|[1-9][0-9]*)`. An `--items` token is an integer, a colon,
|
|
26
|
+
# and a `[A-Za-z_]+` state; a duplicate key is a usage error. `--at` defaults to
|
|
27
|
+
# the current UTC time as yyyy-MM-ddTHH-mm; the clock is read only when `--at`
|
|
28
|
+
# is omitted.
|
|
29
|
+
#
|
|
30
|
+
# Exit-code contract (identical to remove-parallel-item.sh):
|
|
31
|
+
# exit 0 success; one compact JSON object on stdout
|
|
32
|
+
# exit 1 rule rejection; the reference implementation's exact message as
|
|
33
|
+
# one stderr line
|
|
34
|
+
# exit 2 usage error; one stderr line beginning
|
|
35
|
+
# "PARALLEL_MUTATION_ERROR: usage error:"
|
|
36
|
+
# Argument validation runs first, then rule rejection, then output.
|
|
37
|
+
#
|
|
38
|
+
# Parity reference: scripts/dev_tools/parallel_mutation_protocol.py
|
|
39
|
+
# (decide_admission, recolor_unstarted, decide_close) and
|
|
40
|
+
# scripts/dev_tools/_parallel_mutation_entries.py (build_add_entry,
|
|
41
|
+
# build_close_entry, build_requeue_entry).
|
|
42
|
+
#
|
|
43
|
+
# Declared divergences (also listed in both parity lanes):
|
|
44
|
+
# 1. A duplicate key in `close --items` is a usage error (exit 2); the Python
|
|
45
|
+
# mapping would silently keep the last record for that key.
|
|
46
|
+
# 2. `at` is a caller-supplied string instead of a `datetime`.
|
|
47
|
+
# 3. Option abbreviations are rejected.
|
|
48
|
+
# 4. `admit` has no rejection branch, in either implementation.
|
|
49
|
+
set -euo pipefail
|
|
50
|
+
|
|
51
|
+
# Resolve this script's own directory so the libraries source regardless of cwd.
|
|
52
|
+
MPR_SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
|
53
|
+
# shellcheck source=.claude/lib/bash/parallel-mutation-ops.sh
|
|
54
|
+
# shellcheck disable=SC1091
|
|
55
|
+
source "$MPR_SCRIPT_DIR/parallel-mutation-ops.sh"
|
|
56
|
+
# shellcheck source=.claude/lib/bash/parallel-mutation-cli.sh
|
|
57
|
+
# shellcheck disable=SC1091
|
|
58
|
+
source "$MPR_SCRIPT_DIR/parallel-mutation-cli.sh"
|
|
59
|
+
|
|
60
|
+
pc_enforce_c_locale
|
|
61
|
+
|
|
62
|
+
mpr_admit() {
|
|
63
|
+
# Run the admit subcommand.
|
|
64
|
+
local candidate edges in_flight members rc=0
|
|
65
|
+
pmcli_parse_options "candidate edges in-flight current-cohort-members" "$@"
|
|
66
|
+
pmcli_require candidate in-flight current-cohort-members
|
|
67
|
+
pmcli_integer "${PMCLI_OPTS[candidate]}" "--candidate"
|
|
68
|
+
candidate="$PMCLI_VALUE"
|
|
69
|
+
pmcli_edge_list "${PMCLI_OPTS[edges]-}"
|
|
70
|
+
edges="$PMCLI_VALUE"
|
|
71
|
+
pmcli_integer_list "${PMCLI_OPTS["in-flight"]}" "--in-flight"
|
|
72
|
+
in_flight="$PMCLI_VALUE"
|
|
73
|
+
pmcli_integer_list "${PMCLI_OPTS["current-cohort-members"]}" "--current-cohort-members"
|
|
74
|
+
members="$PMCLI_VALUE"
|
|
75
|
+
pm_decide_admission "$candidate" "$edges" "$in_flight" "$members" || rc=$?
|
|
76
|
+
pmcli_emit "$rc"
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
mpr_recolor() {
|
|
80
|
+
# Run the recolor subcommand; same options as remove-parallel-item.sh.
|
|
81
|
+
local unstarted edges pinned generation current highest rc=0
|
|
82
|
+
pmcli_parse_options "unstarted edges pinned generation current-cohort highest-pinned-cohort" "$@"
|
|
83
|
+
pmcli_require unstarted pinned generation current-cohort highest-pinned-cohort
|
|
84
|
+
pmcli_integer_list "${PMCLI_OPTS[unstarted]}" "--unstarted"
|
|
85
|
+
unstarted="$PMCLI_VALUE"
|
|
86
|
+
pmcli_edge_list "${PMCLI_OPTS[edges]-}"
|
|
87
|
+
edges="$PMCLI_VALUE"
|
|
88
|
+
pmcli_integer_list "${PMCLI_OPTS[pinned]}" "--pinned"
|
|
89
|
+
pinned="$PMCLI_VALUE"
|
|
90
|
+
pmcli_integer "${PMCLI_OPTS[generation]}" "--generation"
|
|
91
|
+
generation="$PMCLI_VALUE"
|
|
92
|
+
pmcli_integer "${PMCLI_OPTS["current-cohort"]}" "--current-cohort"
|
|
93
|
+
current="$PMCLI_VALUE"
|
|
94
|
+
pmcli_integer "${PMCLI_OPTS["highest-pinned-cohort"]}" "--highest-pinned-cohort"
|
|
95
|
+
highest="$PMCLI_VALUE"
|
|
96
|
+
pm_recolor_unstarted "$unstarted" "$edges" "$pinned" "$generation" "$current" "$highest" || rc=$?
|
|
97
|
+
pmcli_emit "$rc"
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
mpr_add_entry() {
|
|
101
|
+
# Run the add-entry subcommand.
|
|
102
|
+
local item deferred generation at rc=0
|
|
103
|
+
pmcli_parse_options "item deferred generation at" "$@"
|
|
104
|
+
pmcli_require item deferred generation
|
|
105
|
+
pmcli_integer "${PMCLI_OPTS[item]}" "--item"
|
|
106
|
+
item="$PMCLI_VALUE"
|
|
107
|
+
deferred="${PMCLI_OPTS[deferred]}"
|
|
108
|
+
[[ $deferred == true || $deferred == false ]] ||
|
|
109
|
+
pmcli_usage_error "--deferred must be true or false; found: $deferred"
|
|
110
|
+
pmcli_integer "${PMCLI_OPTS[generation]}" "--generation"
|
|
111
|
+
generation="$PMCLI_VALUE"
|
|
112
|
+
pmcli_timestamp
|
|
113
|
+
at="$PMCLI_VALUE"
|
|
114
|
+
pm_build_add_entry "$item" "$deferred" "$generation" "$at" || rc=$?
|
|
115
|
+
pmcli_emit "$rc"
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
mpr_close() {
|
|
119
|
+
# Run the close subcommand over every `<key>:<state>` token.
|
|
120
|
+
local token key state canonical="" seen=" " rc=0
|
|
121
|
+
pmcli_parse_options "items" "$@"
|
|
122
|
+
pmcli_require items
|
|
123
|
+
pcoh_split_words "${PMCLI_OPTS[items]}"
|
|
124
|
+
local -a tokens=("${PCOH_WORDS[@]}")
|
|
125
|
+
for token in "${tokens[@]}"; do
|
|
126
|
+
[[ $token =~ ^(-?(0|[1-9][0-9]*)):([A-Za-z_]+)$ ]] ||
|
|
127
|
+
pmcli_usage_error "--items token must be <key>:<state>; found: $token"
|
|
128
|
+
state="${BASH_REMATCH[3]}"
|
|
129
|
+
key=$((BASH_REMATCH[1]))
|
|
130
|
+
# A duplicate key would let one record silently replace another, so it
|
|
131
|
+
# is rejected before the library runs (declared divergence 1).
|
|
132
|
+
[[ $seen != *" $key "* ]] || pmcli_usage_error "--items key given more than once: $key"
|
|
133
|
+
seen="$seen$key "
|
|
134
|
+
canonical="$canonical $key:$state"
|
|
135
|
+
done
|
|
136
|
+
pm_decide_close "${canonical# }" || rc=$?
|
|
137
|
+
pmcli_emit "$rc"
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
mpr_close_entry() {
|
|
141
|
+
# Run the close-entry subcommand.
|
|
142
|
+
local generation at rc=0
|
|
143
|
+
pmcli_parse_options "generation at" "$@"
|
|
144
|
+
pmcli_require generation
|
|
145
|
+
pmcli_integer "${PMCLI_OPTS[generation]}" "--generation"
|
|
146
|
+
generation="$PMCLI_VALUE"
|
|
147
|
+
pmcli_timestamp
|
|
148
|
+
at="$PMCLI_VALUE"
|
|
149
|
+
pm_build_close_entry "$generation" "$at" || rc=$?
|
|
150
|
+
pmcli_emit "$rc"
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
mpr_requeue_entry() {
|
|
154
|
+
# Run the requeue-entry subcommand.
|
|
155
|
+
local item generation at rc=0
|
|
156
|
+
pmcli_parse_options "item generation at" "$@"
|
|
157
|
+
pmcli_require item generation
|
|
158
|
+
pmcli_integer "${PMCLI_OPTS[item]}" "--item"
|
|
159
|
+
item="$PMCLI_VALUE"
|
|
160
|
+
pmcli_integer "${PMCLI_OPTS[generation]}" "--generation"
|
|
161
|
+
generation="$PMCLI_VALUE"
|
|
162
|
+
pmcli_timestamp
|
|
163
|
+
at="$PMCLI_VALUE"
|
|
164
|
+
pm_build_requeue_entry "$item" "$generation" "$at" || rc=$?
|
|
165
|
+
pmcli_emit "$rc"
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
mpr_main() {
|
|
169
|
+
# Dispatch on the subcommand.
|
|
170
|
+
(($# > 0)) ||
|
|
171
|
+
pmcli_usage_error "missing subcommand; expected one of admit, recolor, add-entry, close, close-entry, requeue-entry"
|
|
172
|
+
local subcommand="$1"
|
|
173
|
+
shift
|
|
174
|
+
case "$subcommand" in
|
|
175
|
+
admit) mpr_admit "$@" ;;
|
|
176
|
+
recolor) mpr_recolor "$@" ;;
|
|
177
|
+
add-entry) mpr_add_entry "$@" ;;
|
|
178
|
+
close) mpr_close "$@" ;;
|
|
179
|
+
close-entry) mpr_close_entry "$@" ;;
|
|
180
|
+
requeue-entry) mpr_requeue_entry "$@" ;;
|
|
181
|
+
*) pmcli_usage_error "unknown subcommand: $subcommand" ;;
|
|
182
|
+
esac
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
# Guard so the file can be sourced without executing main. main's return code
|
|
186
|
+
# is captured and re-exited explicitly as the final statement.
|
|
187
|
+
if [[ ${BASH_SOURCE[0]} == "${0}" ]]; then
|
|
188
|
+
mpr_rc=0
|
|
189
|
+
mpr_main "$@" || mpr_rc=$?
|
|
190
|
+
exit "$mpr_rc"
|
|
191
|
+
fi
|
|
@@ -52,14 +52,19 @@ PCOH_RESULT=""
|
|
|
52
52
|
PCOH_WORDS=()
|
|
53
53
|
|
|
54
54
|
pcoh_split_words() {
|
|
55
|
-
# Populate PCOH_WORDS by splitting a
|
|
55
|
+
# Populate PCOH_WORDS by splitting a whitespace-separated string.
|
|
56
56
|
#
|
|
57
57
|
# Args: $1 = the string to split. An empty or whitespace-only string yields
|
|
58
58
|
# an empty array, which is the empty-graph and empty-cohort case. `read -ra`
|
|
59
59
|
# is used rather than an unquoted expansion so a token is never subjected to
|
|
60
|
-
# pathname expansion.
|
|
60
|
+
# pathname expansion. Newline, CR, VT, and FF are converted to spaces first:
|
|
61
|
+
# `read` consumes one newline-terminated record and splits it on IFS only, so
|
|
62
|
+
# without the conversion every token after the first newline is dropped and
|
|
63
|
+
# CR, VT, and FF are not separators. With it, the separator set is space, tab,
|
|
64
|
+
# LF, CR, VT, and FF, which matches Python str.split() for ASCII.
|
|
65
|
+
local text="${1-}"
|
|
61
66
|
PCOH_WORDS=()
|
|
62
|
-
read -ra PCOH_WORDS <<<"${
|
|
67
|
+
read -ra PCOH_WORDS <<<"${text//[$'\n\r\v\f']/ }"
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
pcoh_fail() {
|
|
@@ -123,9 +128,14 @@ pcoh_build_adjacency() {
|
|
|
123
128
|
PCOH_ADJACENT=()
|
|
124
129
|
pcoh_split_words "$keys"
|
|
125
130
|
local -a key_list=("${PCOH_WORDS[@]}")
|
|
131
|
+
# Rebuild the membership haystack from the split tokens. The endpoint checks
|
|
132
|
+
# below match a space-delimited word, so they would miss a key that the caller
|
|
133
|
+
# separated with a newline, tab, CR, VT, or FF instead of a single space.
|
|
134
|
+
keys=""
|
|
126
135
|
# Seed every declared key so an isolated vertex survives into the coloring
|
|
127
136
|
# step; inferring vertices from the edge list alone would drop it.
|
|
128
137
|
for key in "${key_list[@]}"; do
|
|
138
|
+
keys="$keys $key"
|
|
129
139
|
PCOH_DEGREE["$key"]=0
|
|
130
140
|
PCOH_NEIGHBORS["$key"]=""
|
|
131
141
|
done
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# parallel-mutation-cli.sh: sourceable command-line helpers shared by the two
|
|
3
|
+
# parallel mutation entry points, remove-parallel-item.sh (issue #791) and
|
|
4
|
+
# mutate-parallel-run.sh (issue #890). It holds option parsing, required-option
|
|
5
|
+
# checks, integer, key-list, and edge-list validation, the `--at` timestamp, and
|
|
6
|
+
# the result printer, so both entry points apply one grammar and one exit-code
|
|
7
|
+
# contract.
|
|
8
|
+
#
|
|
9
|
+
# Grammar. Every option takes exactly one value. An unknown name, an
|
|
10
|
+
# abbreviation, a `--name=value` form, a repeated option, or a missing value is
|
|
11
|
+
# a usage error; a value beginning with `--` counts as missing. Integer tokens
|
|
12
|
+
# match `-?(0|[1-9][0-9]*)`. `--at` matches `[0-9A-Za-z:.+-]+` and defaults to
|
|
13
|
+
# the current UTC minute as yyyy-MM-ddTHH-mm.
|
|
14
|
+
#
|
|
15
|
+
# Exit-code contract carried by these helpers:
|
|
16
|
+
# exit 2 usage error; one stderr line beginning
|
|
17
|
+
# "PARALLEL_MUTATION_ERROR: usage error:" (pmcli_usage_error)
|
|
18
|
+
# exit 1 rule rejection; PM_ERROR printed as one stderr line (pmcli_emit)
|
|
19
|
+
# exit 0 success; PM_RESULT printed as one stdout line (pmcli_emit)
|
|
20
|
+
#
|
|
21
|
+
# Dependencies. pmcli_emit reads PM_RESULT and PM_ERROR, which
|
|
22
|
+
# parallel-mutation.sh owns; an entry point sources that library before calling
|
|
23
|
+
# it. The only external command is `date`, run solely when `--at` is omitted.
|
|
24
|
+
#
|
|
25
|
+
# shellcheck disable=SC2034
|
|
26
|
+
# SC2034 is disabled file-wide because PMCLI_OPTS and PMCLI_VALUE are written
|
|
27
|
+
# here and read by the two entry points, which shellcheck analyses separately.
|
|
28
|
+
|
|
29
|
+
# Resolve this file's own directory so its dependencies source regardless of
|
|
30
|
+
# the caller's working directory.
|
|
31
|
+
PMCLI_LIB_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
|
32
|
+
# shellcheck source=.claude/lib/bash/parallel-cohorts.sh
|
|
33
|
+
# shellcheck disable=SC1091
|
|
34
|
+
source "$PMCLI_LIB_DIR/parallel-cohorts.sh"
|
|
35
|
+
|
|
36
|
+
# Parsed option values, keyed by option name without the leading dashes.
|
|
37
|
+
declare -gA PMCLI_OPTS=()
|
|
38
|
+
|
|
39
|
+
# The canonical value stored by the most recent validator.
|
|
40
|
+
PMCLI_VALUE=""
|
|
41
|
+
|
|
42
|
+
pmcli_usage_error() {
|
|
43
|
+
# Print one usage-error line on stderr and exit 2.
|
|
44
|
+
#
|
|
45
|
+
# Args: $1 = the specific reason.
|
|
46
|
+
printf 'PARALLEL_MUTATION_ERROR: usage error: %s\n' "$1" >&2
|
|
47
|
+
exit 2
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
pmcli_parse_options() {
|
|
51
|
+
# Parse `--name value` pairs into PMCLI_OPTS against an allowed-name list.
|
|
52
|
+
#
|
|
53
|
+
# Args: $1 = space-separated allowed option names, then the arguments.
|
|
54
|
+
local allowed="$1" name
|
|
55
|
+
shift
|
|
56
|
+
PMCLI_OPTS=()
|
|
57
|
+
while (($# > 0)); do
|
|
58
|
+
name="${1#--}"
|
|
59
|
+
if [[ $1 != --* ]] || ! pc_contains_word "$allowed" "$name"; then
|
|
60
|
+
pmcli_usage_error "unknown option: $1"
|
|
61
|
+
fi
|
|
62
|
+
if [[ -n ${PMCLI_OPTS["$name"]+set} ]]; then
|
|
63
|
+
pmcli_usage_error "option given more than once: $1"
|
|
64
|
+
fi
|
|
65
|
+
if (($# < 2)) || [[ $2 == --* ]]; then
|
|
66
|
+
pmcli_usage_error "option $1 requires a value"
|
|
67
|
+
fi
|
|
68
|
+
PMCLI_OPTS["$name"]="$2"
|
|
69
|
+
shift 2
|
|
70
|
+
done
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
pmcli_require() {
|
|
74
|
+
# Exit with a usage error unless every named option was supplied.
|
|
75
|
+
#
|
|
76
|
+
# Args: the option names that are required.
|
|
77
|
+
local name
|
|
78
|
+
for name in "$@"; do
|
|
79
|
+
[[ -n ${PMCLI_OPTS["$name"]+set} ]] || pmcli_usage_error "missing required option: --$name"
|
|
80
|
+
done
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
pmcli_integer() {
|
|
84
|
+
# Validate one integer token and store it in canonical form in PMCLI_VALUE.
|
|
85
|
+
#
|
|
86
|
+
# Args: $1 = the token, $2 = a label naming where it came from. The
|
|
87
|
+
# validators store their result in a global instead of echoing it, because
|
|
88
|
+
# a usage error raised inside a command substitution would exit only the
|
|
89
|
+
# subshell.
|
|
90
|
+
if [[ ! $1 =~ ^-?(0|[1-9][0-9]*)$ ]]; then
|
|
91
|
+
pmcli_usage_error "$2 must be a decimal integer matching -?(0|[1-9][0-9]*); found: $1"
|
|
92
|
+
fi
|
|
93
|
+
PMCLI_VALUE=$(($1))
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
pmcli_integer_list() {
|
|
97
|
+
# Validate a whitespace-separated key list; store it space separated.
|
|
98
|
+
#
|
|
99
|
+
# Args: $1 = the list, $2 = a label naming the option.
|
|
100
|
+
local token canonical=""
|
|
101
|
+
pcoh_split_words "$1"
|
|
102
|
+
local -a tokens=("${PCOH_WORDS[@]}")
|
|
103
|
+
for token in "${tokens[@]}"; do
|
|
104
|
+
pmcli_integer "$token" "$2 key"
|
|
105
|
+
canonical="$canonical $PMCLI_VALUE"
|
|
106
|
+
done
|
|
107
|
+
PMCLI_VALUE="${canonical# }"
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
pmcli_edge_list() {
|
|
111
|
+
# Validate a whitespace-separated `a:b` edge list; store it canonical.
|
|
112
|
+
#
|
|
113
|
+
# Args: $1 = the edge list.
|
|
114
|
+
local token first canonical=""
|
|
115
|
+
pcoh_split_words "$1"
|
|
116
|
+
local -a tokens=("${PCOH_WORDS[@]}")
|
|
117
|
+
for token in "${tokens[@]}"; do
|
|
118
|
+
if [[ $token != *:* || $token == *:*:* ]]; then
|
|
119
|
+
pmcli_usage_error "edge must be <a>:<b>; found: $token"
|
|
120
|
+
fi
|
|
121
|
+
pmcli_integer "${token%%:*}" "edge endpoint"
|
|
122
|
+
first="$PMCLI_VALUE"
|
|
123
|
+
pmcli_integer "${token#*:}" "edge endpoint"
|
|
124
|
+
canonical="$canonical $first:$PMCLI_VALUE"
|
|
125
|
+
done
|
|
126
|
+
PMCLI_VALUE="${canonical# }"
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
pmcli_timestamp() {
|
|
130
|
+
# Store the validated `--at` value, or the current UTC minute when omitted.
|
|
131
|
+
if [[ -n ${PMCLI_OPTS[at]+set} ]]; then
|
|
132
|
+
PMCLI_VALUE="${PMCLI_OPTS[at]}"
|
|
133
|
+
[[ $PMCLI_VALUE =~ ^[0-9A-Za-z:.+-]+$ ]] ||
|
|
134
|
+
pmcli_usage_error "--at must match [0-9A-Za-z:.+-]+; found: $PMCLI_VALUE"
|
|
135
|
+
else
|
|
136
|
+
# The only clock read of either entry point, taken only when --at is
|
|
137
|
+
# omitted.
|
|
138
|
+
PMCLI_VALUE=$(date -u +%Y-%m-%dT%H-%M)
|
|
139
|
+
fi
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
pmcli_emit() {
|
|
143
|
+
# Print the library outcome and return the matching exit code.
|
|
144
|
+
#
|
|
145
|
+
# Args: $1 = the library function's return code.
|
|
146
|
+
if (($1 != 0)); then
|
|
147
|
+
printf '%s\n' "$PM_ERROR" >&2
|
|
148
|
+
return 1
|
|
149
|
+
fi
|
|
150
|
+
printf '%s\n' "$PM_RESULT"
|
|
151
|
+
return 0
|
|
152
|
+
}
|