@danmoisan/drm-copilot-mcp 1.1.16 → 1.1.18
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 +1139 -133
- package/package.json +4 -3
- package/resources/claude-customizations/.claude/agents/feature-review.md +10 -1
- package/resources/claude-customizations/.claude/agents/orchestrator.md +16 -3
- package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +18 -6
- package/resources/claude-customizations/.claude/agents/python-typed-engineer.md +3 -3
- package/resources/claude-customizations/.claude/hooks/{enforce-powershell-batch-budget-route.ps1 → enforce-batch-budget-route.ps1} +12 -10
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-resolution.ps1 +221 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +42 -66
- package/resources/claude-customizations/.claude/hooks/enforce-epic-wave-barrier.ps1 +58 -10
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate-resolution.ps1 +144 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +36 -47
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +179 -27
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +21 -38
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +4 -4
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier.ps1 +58 -10
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-drift-gate.ps1 +59 -9
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +114 -37
- package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +3 -3
- package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +99 -64
- package/resources/claude-customizations/.claude/lib/bash/abandon-parallel-item.sh +176 -0
- package/resources/claude-customizations/.claude/lib/bash/parallel-lane-assertion.sh +4 -2
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusConflict.psm1 +193 -14
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusGlob.psm1 +51 -28
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusScheduling.psm1 +5 -8
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorState.psm1 +6 -13
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1 +375 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateReceipts.psm1 +22 -15
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRemediationAccounting.psm1 +337 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRoutingContract.psm1 +9 -1
- package/resources/claude-customizations/.claude/lib/parallel-drift/Invoke-ParallelDriftDetection.ps1 +336 -0
- package/resources/claude-customizations/.claude/lib/parallel-drift/ParallelDrift.psm1 +467 -0
- package/resources/claude-customizations/.claude/lib/parallel-drift/ParallelDriftHalt.psm1 +388 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/EpicScopeResolution.psm1 +53 -32
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +19 -4
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1 +497 -0
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +119 -3
- package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +21 -0
- package/resources/claude-customizations/.claude/rules/quality-tiers.md +1 -1
- package/resources/claude-customizations/.claude/skills/acceptance-criteria-tracking/SKILL.md +8 -0
- package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +2 -0
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +11 -3
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup-worktrees.sh +11 -2
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_enumerate_lib.sh +169 -5
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_preserve_lib.sh +1 -1
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_report_records_lib.sh +6 -43
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_scan_helper.sh +4 -15
- package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +15 -6
- package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +4 -0
- package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +10 -0
- package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +3 -1
- package/resources/claude-customizations/.claude/skills/invoke-csharp-engineer/SKILL.md +4 -0
- package/resources/claude-customizations/.claude/skills/invoke-powershell-engineer/SKILL.md +4 -0
- package/resources/claude-customizations/.claude/skills/invoke-python-engineer/SKILL.md +6 -3
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +33 -13
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +50 -21
- package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +10 -10
- package/resources/claude-customizations/.claude/skills/python-change-budget-router/SKILL.md +11 -35
- package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +5 -1
- package/resources/claude-customizations/pack-manifests/core.json +10 -0
- package/resources/claude-customizations/pack-manifests/powershell.json +0 -1
- package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +8 -0
- package/resources/codex-and-agents-customizations/.agents/skills/epic-orchestrate/SKILL.md +5 -3
- package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +4 -0
- package/resources/codex-and-agents-customizations/.agents/skills/feature-promotion-lifecycle/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/feature-review/SKILL.md +30 -6
- package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +3 -1
- package/resources/codex-and-agents-customizations/.agents/skills/invoke-powershell-engineer/SKILL.md +0 -1
- package/resources/codex-and-agents-customizations/.agents/skills/invoke-python-engineer/SKILL.md +2 -3
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +30 -7
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-state/SKILL.md +41 -2
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +18 -3
- package/resources/codex-and-agents-customizations/.agents/skills/python-change-budget-router/SKILL.md +10 -34
- package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +2 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c1.toml +1 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c2.toml +1 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c3-elevated.toml +1 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c3.toml +1 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c4.toml +1 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer.toml +1 -0
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c1.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-orchestrator.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c1.toml +3 -3
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c2.toml +3 -3
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3-elevated.toml +3 -3
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3.toml +3 -3
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c4.toml +3 -3
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer.toml +3 -3
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-batch-budget-route.ps1 +139 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +4 -124
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +169 -79
- package/resources/codex-and-agents-customizations/pack-manifests/core.json +1 -0
- package/resources/customizations/.github/agents/python-orchestrator.agent.md +4 -4
- package/resources/customizations/.github/agents/python-typed-engineer.agent.md +7 -8
- package/resources/customizations/.github/prompts/orchestrate-python-work.prompt.md +3 -3
- package/resources/customizations/.github/skills/acceptance-criteria-tracking/SKILL.md +8 -0
- package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +4 -0
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +16 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danmoisan/drm-copilot-mcp",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.18",
|
|
4
4
|
"description": "Stdio MCP server exposing drm-copilot repo-automation tools.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -38,11 +38,12 @@
|
|
|
38
38
|
"prepack": "node prepack.cjs"
|
|
39
39
|
},
|
|
40
40
|
"overrides": {
|
|
41
|
-
"fast-uri": "^3.1.
|
|
41
|
+
"fast-uri": "^3.1.8",
|
|
42
42
|
"hono": "^4.12.27",
|
|
43
43
|
"ip-address": "^10.7.2",
|
|
44
44
|
"qs": "^6.15.2",
|
|
45
|
-
"@hono/node-server": "^2.0.5"
|
|
45
|
+
"@hono/node-server": "^2.0.5",
|
|
46
|
+
"brace-expansion": "^5.0.12"
|
|
46
47
|
},
|
|
47
48
|
"devDependencies": {
|
|
48
49
|
"esbuild": "^0.28.0"
|
|
@@ -9,6 +9,7 @@ tools:
|
|
|
9
9
|
- "Bash(git diff *)"
|
|
10
10
|
- "Bash(git log *)"
|
|
11
11
|
- "Write(/docs/features/active/**)"
|
|
12
|
+
- "mcp__drm-copilot__validate_orchestration_artifacts"
|
|
12
13
|
skills:
|
|
13
14
|
- policy-compliance-order
|
|
14
15
|
- acceptance-criteria-tracking
|
|
@@ -32,7 +33,12 @@ When the active review scope is a selected version folder such as `docs/features
|
|
|
32
33
|
1. `docs/features/active/<feature-or-selected-version>/policy-audit.<timestamp>.md` — policy compliance audit with PASS/PARTIAL/FAIL verdicts and evidence
|
|
33
34
|
2. `docs/features/active/<feature-or-selected-version>/code-review.<timestamp>.md` — code quality review covering best practices
|
|
34
35
|
3. `docs/features/active/<feature-or-selected-version>/feature-audit.<timestamp>.md` — acceptance criteria verification relative to baseline
|
|
35
|
-
4.
|
|
36
|
+
4. For every review with at least one blocking finding, including halt (`HALT_NON_REMEDIABLE`) and wait (`AWAITING_CI`) verdicts: `docs/features/active/<feature-or-selected-version>/remediation-inputs.<timestamp>.md` with explicit remediation-required findings and artifact paths. The file carries these line formats:
|
|
37
|
+
- one line `Review-Verdict: <VERDICT>`, where the verdict is `PASS`, `REMEDIATION_REQUIRED`, `HALT_NON_REMEDIABLE`, or `AWAITING_CI`,
|
|
38
|
+
- inside each blocking finding block, one line `Remediability: <class>`, where the class is `autonomous`, `external_dependency`, `policy_hold`, `awaiting_ci`, or `human_decision_required` (`autonomous` when the line is absent),
|
|
39
|
+
- inside each blocking finding block, one line `Remediability-Evidence: <text>` stating why the class applies.
|
|
40
|
+
|
|
41
|
+
These lines, including the evidence text, never contain the substrings the orchestrator counts when it counts blocking findings, so their presence does not change the blocking count. The verdict follows from the classes: no blocking finding is `PASS`; any `autonomous` finding is `REMEDIATION_REQUIRED`; otherwise any `external_dependency`, `policy_hold`, or `human_decision_required` finding is `HALT_NON_REMEDIABLE`; otherwise `AWAITING_CI`.
|
|
36
42
|
|
|
37
43
|
Timestamp format: `yyyy-MM-ddTHH-mm` (ISO-8601).
|
|
38
44
|
|
|
@@ -44,6 +50,9 @@ Report the required artifact paths in the final response using these tokens:
|
|
|
44
50
|
- `code-review-path: docs/features/active/<feature-or-selected-version>/code-review.<timestamp>.md`
|
|
45
51
|
- `feature-audit-path: docs/features/active/<feature-or-selected-version>/feature-audit.<timestamp>.md`
|
|
46
52
|
- When remediation inputs are produced: `remediation-inputs-path: docs/features/active/<feature-or-selected-version>/remediation-inputs.<timestamp>.md`
|
|
53
|
+
- Optional: `review-status: <VERDICT>` with the same verdict as the remediation inputs (`PASS` when none were produced). The orchestrator treats the remediation inputs as authoritative and this token as a cross-check.
|
|
54
|
+
|
|
55
|
+
Validate each review artifact immediately after writing it with `mcp__drm-copilot__validate_orchestration_artifacts`, passing `artifact_path` set to that artifact: use `artifact_type: "policy-audit"` for `policy-audit.<timestamp>.md`, `artifact_type: "code-review"` for `code-review.<timestamp>.md`, and `artifact_type: "feature-audit"` for `feature-audit.<timestamp>.md`. When the validator reports a failure, fix the artifact in the same review and validate it again. Report an artifact path only after its validation passes.
|
|
47
56
|
|
|
48
57
|
## Context Sources
|
|
49
58
|
|
|
@@ -96,7 +96,9 @@ PR creation and PR body edits must be delegated to `Agent(pr-author)`. The orche
|
|
|
96
96
|
|
|
97
97
|
When the orchestrator runs the remediation loop, it records a top-level `remediation_loop` object in `artifacts/orchestration/orchestrator-state.json`:
|
|
98
98
|
|
|
99
|
-
- `current_cycle` — integer index of the active cycle.
|
|
99
|
+
- `current_cycle` — integer index of the active cycle's record in `cycles[]`; a record position, not an attempt count.
|
|
100
|
+
- `completed_attempts` — non-negative integer equal to the number of cycles whose `candidate_applied` is `true`. This is the shared attempt count for local, CI-failure, merge-conflict, and parallel-drift cycles; the guard halts after three completed attempts.
|
|
101
|
+
- `review_outcomes[]` — one entry per review, including `PASS`, appended in order. Each entry is an object with `verdict` (one of `PASS`, `REMEDIATION_REQUIRED`, `HALT_NON_REMEDIABLE`, `AWAITING_CI`) and `findings[]`, the review's blocking findings only, each an object with `remediability` (one of `autonomous`, `external_dependency`, `policy_hold`, `awaiting_ci`, `human_decision_required`). An optional `inputs_path` names the `remediation-inputs` file.
|
|
100
102
|
- `cycles[]` — an ordered array of cycle records. Each cycle is an object with:
|
|
101
103
|
- `entry_timestamp` — ISO-8601 timestamp when the cycle was entered.
|
|
102
104
|
- `inputs_path` — path to the `remediation-inputs.<entry-ts>.md` that opened the cycle.
|
|
@@ -106,14 +108,25 @@ When the orchestrator runs the remediation loop, it records a top-level `remedia
|
|
|
106
108
|
- `audit_paths` — the reaudit artifact paths produced at cycle exit (`code-review`, `feature-audit`, `policy-audit`).
|
|
107
109
|
- `blocking_count` — the total number of blocking findings across the reaudit artifacts.
|
|
108
110
|
- `exit_condition_met` — boolean; `true` only when the cycle's exit gate is satisfied.
|
|
111
|
+
- `candidate_applied` — boolean; `true` only for a completed attempt (execution `complete` and a non-empty pre-reaudit commit). A cycle without a candidate records `false` and consumes no attempt number.
|
|
112
|
+
- `opened_by_review` — zero-based index of the `review_outcomes[]` entry that opened the cycle.
|
|
113
|
+
|
|
114
|
+
These four fields are recorded by producers. Verdicts follow from the classes of a review's blocking findings: none is `PASS`; any `autonomous` is `REMEDIATION_REQUIRED`; otherwise any `external_dependency`, `policy_hold`, or `human_decision_required` is `HALT_NON_REMEDIABLE`; otherwise `AWAITING_CI`. A `HALT_NON_REMEDIABLE` or `AWAITING_CI` review opens no cycle.
|
|
109
115
|
|
|
110
116
|
Malformed-cycle rules:
|
|
111
117
|
|
|
112
118
|
- `plan_path` must be a non-empty string.
|
|
113
119
|
- `execution_status` may be in `{in_progress, complete, failed}` only when `preflight.final_status == 'clear'`; any other preflight status with one of those execution statuses is malformed (execution recorded before preflight cleared).
|
|
114
120
|
- `exit_condition_met == true` requires `blocking_count == 0`; a non-zero `blocking_count` with `exit_condition_met == true` is malformed.
|
|
121
|
+
- R5: `candidate_applied`, when present, must be a boolean.
|
|
122
|
+
- R6: `candidate_applied == true` requires `execution_status == "complete"`.
|
|
123
|
+
- R7: `completed_attempts`, when present, must be a non-negative integer equal to the number of object cycles whose `candidate_applied` is `true`.
|
|
124
|
+
- R8: `review_outcomes`, when present, must be a list, and each entry must be an object.
|
|
125
|
+
- R9: each outcome's `verdict` must be one of the four verdicts, its `findings` must be a list, and each finding must be an object whose `remediability` is one of the five classes (case-sensitive).
|
|
126
|
+
- R10: each well-formed outcome's `verdict` must equal the verdict derived from its findings' classes.
|
|
127
|
+
- R11: `opened_by_review`, when present, must be a non-negative integer that indexes an object entry of `review_outcomes` whose `verdict` is `REMEDIATION_REQUIRED`.
|
|
115
128
|
|
|
116
|
-
Cycle-aware `next_step` uses the form `remediation.cycle_N.{plan,preflight,execute,reaudit,exit_check}`, where `N` is the cycle
|
|
129
|
+
Cycle-aware `next_step` uses the form `remediation.cycle_N.{plan,preflight,execute,reaudit,exit_check}`, where `N` is `completed_attempts + 1` (the active cycle number) and the sub-step names the current position in the loop; `current_cycle` remains a record position in `cycles[]`.
|
|
117
130
|
|
|
118
131
|
### CI Monitoring and Post-PR Remediation
|
|
119
132
|
|
|
@@ -149,7 +162,7 @@ A new finding discovered during execution triggers a NEW cycle with a follow-up
|
|
|
149
162
|
|
|
150
163
|
### Exit Gate
|
|
151
164
|
|
|
152
|
-
`blocking_count` is the total of FAIL and blocking-PARTIAL findings across the three reaudit artifacts (`code-review`, `feature-audit`, `policy-audit`). Only `blocking_count == 0` sets `exit_condition_met = true`.
|
|
165
|
+
`blocking_count` is the total of FAIL and blocking-PARTIAL findings across the three reaudit artifacts (`code-review`, `feature-audit`, `policy-audit`). Only `blocking_count == 0` sets `exit_condition_met = true`. The exit gate counts only `autonomous` blocking findings when deciding whether to open the next cycle: a non-zero count of `autonomous` blocking findings leaves the gate unmet and opens the next cycle, while remaining blocking findings that are all non-remediable halt (`HALT_NON_REMEDIABLE`) or wait (`AWAITING_CI`) instead of opening a cycle.
|
|
153
166
|
|
|
154
167
|
### Citations
|
|
155
168
|
|
|
@@ -18,7 +18,9 @@ tools:
|
|
|
18
18
|
- "Bash(bash .claude/lib/bash/compute-cohorts.sh*)"
|
|
19
19
|
- "Bash(bash .claude/lib/bash/compute-concurrency-batches.sh*)"
|
|
20
20
|
- "Bash(bash .claude/lib/bash/validate-parallel-manifest.sh*)"
|
|
21
|
+
- "Bash(bash .claude/lib/bash/abandon-parallel-item.sh*)"
|
|
21
22
|
- "Bash(pwsh -NoProfile -File .claude/lib/project-file-merge/Resolve-MergeableConflict.ps1*)"
|
|
23
|
+
- "Bash(pwsh -NoProfile -NonInteractive -File .claude/lib/parallel-drift/Invoke-ParallelDriftDetection.ps1*)"
|
|
22
24
|
- "Bash(dotnet tool restore*)"
|
|
23
25
|
- "Bash(dotnet csharpier check *)"
|
|
24
26
|
- "Bash(dotnet build *)"
|
|
@@ -94,12 +96,16 @@ batching use `compute-cohorts.sh` and `compute-concurrency-batches.sh` under the
|
|
|
94
96
|
entry.
|
|
95
97
|
|
|
96
98
|
The `poetry run` grants remain for the repository-local paths that still need an interpreter. The
|
|
97
|
-
skill's `## Parallel-Level Checkpoint` section
|
|
98
|
-
`mcp__drm-copilot__validate_orchestration_artifacts`,
|
|
99
|
-
|
|
100
|
-
`
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
skill's `## Parallel-Level Checkpoint` section validates through
|
|
100
|
+
`mcp__drm-copilot__validate_orchestration_artifacts`, and since issue #763 radius drift detection runs
|
|
101
|
+
through the destination-runtime PowerShell entry point
|
|
102
|
+
`.claude/lib/parallel-drift/Invoke-ParallelDriftDetection.ps1` and the abandon disposition through the
|
|
103
|
+
bundled entry point `.claude/lib/bash/abandon-parallel-item.sh`, so no skill step names a
|
|
104
|
+
`poetry run` consumer. The two `poetry run python` grants are left unchanged pending a separate
|
|
105
|
+
removal decision. Each grant stays scoped to its own `poetry run python` invocation form — not to
|
|
106
|
+
`poetry run` as a whole — so `pytest`, `black`, `ruff`, and every other `poetry run` subcommand
|
|
107
|
+
remain outside the allowlist. The sibling persona `.claude/agents/parallel-planner.md` records the
|
|
108
|
+
same destination-runtime posture.
|
|
103
109
|
|
|
104
110
|
## Startup Protocol
|
|
105
111
|
|
|
@@ -269,6 +275,12 @@ In `closed` mode, do not report completion until:
|
|
|
269
275
|
`artifact_type: "parallel-orchestrator-state"`).
|
|
270
276
|
4. Acceptance criteria in the AC source files of each item have been checked off by that item's own
|
|
271
277
|
run, per the `acceptance-criteria-tracking` skill.
|
|
278
|
+
A CI-dependent criterion, whose verification requires the result of CI on the pull-request head,
|
|
279
|
+
is checked off and pushed by the item's own run after its S9 success and before its DONE, and
|
|
280
|
+
that run re-runs S9 so its reported `ci_gate.head_sha` is the final head. Before `gh pr merge`,
|
|
281
|
+
confirm that the `headRefOid` value equals that reported `ci_gate.head_sha` and that the item
|
|
282
|
+
reported no pending CI-dependent criteria. You never commit acceptance-criteria check-offs from
|
|
283
|
+
the coordinator root.
|
|
272
284
|
|
|
273
285
|
In `open` mode there is no automatic completion. The run is a standing queue and terminates only
|
|
274
286
|
via `/parallel-close`, which is owned by F6 and is not specified here or shipped by this feature. Do
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: python-typed-engineer
|
|
3
3
|
model: sonnet
|
|
4
|
-
description: Project-scoped worker that implements and verifies Python changes within typed repository boundaries. Applies the Black -> Ruff -> Pyright -> Pytest toolchain, the 3
|
|
4
|
+
description: Project-scoped worker that implements and verifies Python changes within typed repository boundaries. Applies the Black -> Ruff -> Pyright -> Pytest toolchain, the 1-3 production-file direct-mode budget with routing to the orchestrated large path above it, and zero-regression quality gates.
|
|
5
5
|
tools:
|
|
6
6
|
- Read
|
|
7
7
|
- Write
|
|
@@ -37,7 +37,7 @@ Language standards and toolchain are defined in `.claude/rules/python.md` and `.
|
|
|
37
37
|
Follow the phased workflow defined by the preloaded skills:
|
|
38
38
|
|
|
39
39
|
1. **Policy compliance** — apply `policy-compliance-order` to load mandatory repo policies before any change.
|
|
40
|
-
2. **Routing and scope** — apply `python-change-budget-router` to estimate scope
|
|
40
|
+
2. **Routing and scope** — apply `python-change-budget-router` to estimate scope and select direct mode (1-3 production files) or, for more than 3 production files, large-path escalation through `/orchestrate`. The large path has no production-file cap, and test files are not counted toward the routing threshold.
|
|
41
41
|
3. **Plan and baseline** — apply `atomic-plan-contract` for Phase 0 baseline capture and atomic plan structure. Delegate plan authoring to `atomic_planner` when no plan is supplied.
|
|
42
42
|
4. **Implement in batches** — apply the approved plan. After each batch, run targeted Ruff and Pyright on touched files plus targeted Pytest, and confirm per-file coverage.
|
|
43
43
|
5. **Final QA gate** — apply `python-qa-gate` to run the full toolchain, enforce zero-regression deltas against the baseline, and produce the required reporting block before declaring completion.
|
|
@@ -58,7 +58,7 @@ If the marker is missing or malformed, fail closed to `full-feature`.
|
|
|
58
58
|
|
|
59
59
|
Stop implementation and return to the user when:
|
|
60
60
|
|
|
61
|
-
- the scope estimate
|
|
61
|
+
- the scope estimate exceeds 3 production files in direct mode,
|
|
62
62
|
- a file is near or would exceed the 500-line limit,
|
|
63
63
|
- any QA gate delta is non-zero after self-correction,
|
|
64
64
|
- the toolchain cannot be executed in the current environment (mark the change **unverified**),
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
<#
|
|
2
2
|
.SYNOPSIS
|
|
3
|
-
Pure route helpers
|
|
3
|
+
Pure route helpers shared by the batch-budget hooks.
|
|
4
4
|
|
|
5
5
|
.DESCRIPTION
|
|
6
6
|
Defines the functions that read the selected route from orchestrator checkpoint
|
|
7
7
|
text and decide whether that checkpoint selects the uncapped orchestrated large
|
|
8
|
-
path. The file is dot-sourced by
|
|
9
|
-
entry point.
|
|
10
|
-
|
|
8
|
+
path. The file is dot-sourced by the PowerShell and Python batch-budget hooks and
|
|
9
|
+
has no entry point. The Claude and Codex runtime copies of this file are
|
|
10
|
+
byte-identical, and a parity test holds them identical. None of the functions
|
|
11
|
+
reads a file or an environment variable; the checkpoint text is supplied by the
|
|
12
|
+
caller.
|
|
11
13
|
|
|
12
14
|
.NOTES
|
|
13
15
|
Compatible with PowerShell 7+.
|
|
14
16
|
#>
|
|
15
17
|
|
|
16
|
-
function ConvertFrom-
|
|
18
|
+
function ConvertFrom-BatchBudgetCheckpoint {
|
|
17
19
|
<#
|
|
18
20
|
.SYNOPSIS
|
|
19
21
|
Parses orchestrator checkpoint text into a checkpoint object.
|
|
@@ -50,7 +52,7 @@ function ConvertFrom-PowerShellBatchBudgetCheckpoint {
|
|
|
50
52
|
return $null
|
|
51
53
|
}
|
|
52
54
|
|
|
53
|
-
function Get-
|
|
55
|
+
function Get-BatchBudgetSelectedRoute {
|
|
54
56
|
<#
|
|
55
57
|
.SYNOPSIS
|
|
56
58
|
Returns the route selected by an orchestrator checkpoint.
|
|
@@ -69,7 +71,7 @@ function Get-PowerShellBatchBudgetSelectedRoute {
|
|
|
69
71
|
[string] $CheckpointText
|
|
70
72
|
)
|
|
71
73
|
|
|
72
|
-
$checkpoint = ConvertFrom-
|
|
74
|
+
$checkpoint = ConvertFrom-BatchBudgetCheckpoint -CheckpointText $CheckpointText
|
|
73
75
|
if ($null -eq $checkpoint) {
|
|
74
76
|
return ''
|
|
75
77
|
}
|
|
@@ -89,7 +91,7 @@ function Get-PowerShellBatchBudgetSelectedRoute {
|
|
|
89
91
|
return ''
|
|
90
92
|
}
|
|
91
93
|
|
|
92
|
-
function Test-
|
|
94
|
+
function Test-BatchBudgetLargePathRoute {
|
|
93
95
|
<#
|
|
94
96
|
.SYNOPSIS
|
|
95
97
|
Reports whether an orchestrator checkpoint selects the uncapped large path.
|
|
@@ -110,12 +112,12 @@ function Test-PowerShellBatchBudgetLargePathRoute {
|
|
|
110
112
|
)
|
|
111
113
|
|
|
112
114
|
try {
|
|
113
|
-
$checkpoint = ConvertFrom-
|
|
115
|
+
$checkpoint = ConvertFrom-BatchBudgetCheckpoint -CheckpointText $CheckpointText
|
|
114
116
|
if ($null -eq $checkpoint) {
|
|
115
117
|
return $false
|
|
116
118
|
}
|
|
117
119
|
|
|
118
|
-
$route = Get-
|
|
120
|
+
$route = Get-BatchBudgetSelectedRoute -CheckpointText $CheckpointText
|
|
119
121
|
if (-not (@('large', 'remediation', 'preparation') -ccontains $route)) {
|
|
120
122
|
return $false
|
|
121
123
|
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Checkpoint read seams and run-target resolution for the epic merge gate (issue #690).
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Dot-sourced by enforce-epic-merge-gate.ps1 immediately after the authorization sibling.
|
|
7
|
+
Holds:
|
|
8
|
+
|
|
9
|
+
- The import guard for WorktreeRunResolution.psm1. A failed import is recorded in
|
|
10
|
+
$script:EpicMergeGateResolutionImportFailure, and
|
|
11
|
+
Get-EpicMergeGateImportFailureDecision turns it into a deny, so a missing module
|
|
12
|
+
cannot make the gate exit non-zero and fail open.
|
|
13
|
+
- The three checkpoint read seams, relocated from the gate file with their names kept.
|
|
14
|
+
Each takes a mandatory absolute path composed beneath a resolved worktree root.
|
|
15
|
+
- The session worktree root, used for the child branch and for a bare command.
|
|
16
|
+
- The resolution seam Resolve-EpicMergeGateRunTarget, which locates the epic or
|
|
17
|
+
parallel checkpoint that records the command's pull request number.
|
|
18
|
+
- Test-ChildCheckpointPrGateBinding, which binds the command's pull request number to
|
|
19
|
+
the per-feature checkpoint's pr_gate.pr_number when that field is recorded.
|
|
20
|
+
- Get-EpicMergeGateUnresolvedReason, the reason prefix used when neither run branch
|
|
21
|
+
resolves.
|
|
22
|
+
|
|
23
|
+
.NOTES
|
|
24
|
+
PowerShell 7+. Depends on Get-EpicMergeGateBlockDecision from the authorization
|
|
25
|
+
sibling, dot-sourced by the gate before this file. Mirrored byte-identically under
|
|
26
|
+
extensions/drm-copilot/resources/claude-customizations/.
|
|
27
|
+
#>
|
|
28
|
+
|
|
29
|
+
# Import guard (issue #690): a failed import denies instead of failing open.
|
|
30
|
+
$script:EpicMergeGateResolutionImportFailure = $null
|
|
31
|
+
try {
|
|
32
|
+
Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeRunResolution.psm1') -Force -ErrorAction Stop
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
$script:EpicMergeGateResolutionImportFailure = 'WorktreeRunResolution.psm1'
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function Get-EpicMergeGateImportFailureDecision {
|
|
39
|
+
<#
|
|
40
|
+
.SYNOPSIS
|
|
41
|
+
Returns the deny for a failed worktree-resolution import, or $null.
|
|
42
|
+
.OUTPUTS
|
|
43
|
+
System.Collections.Specialized.OrderedDictionary, or $null when the import succeeded.
|
|
44
|
+
#>
|
|
45
|
+
[CmdletBinding()]
|
|
46
|
+
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
47
|
+
param()
|
|
48
|
+
|
|
49
|
+
if (-not $script:EpicMergeGateResolutionImportFailure) {
|
|
50
|
+
return $null
|
|
51
|
+
}
|
|
52
|
+
return Get-EpicMergeGateBlockDecision -Reason (
|
|
53
|
+
"EPIC_MERGE_GATE_BLOCKED: the worktree-resolution module '$($script:EpicMergeGateResolutionImportFailure)' " +
|
|
54
|
+
'failed to import, so the run checkpoint that governs this merge cannot be located; the gate fails closed.')
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function Get-ChildOrchestratorCheckpointContent {
|
|
58
|
+
<#
|
|
59
|
+
.SYNOPSIS
|
|
60
|
+
Read the raw JSON text of the per-feature orchestrator checkpoint. Tests mock
|
|
61
|
+
this function (read seam).
|
|
62
|
+
.PARAMETER Path
|
|
63
|
+
The absolute checkpoint path composed beneath the session worktree root.
|
|
64
|
+
.OUTPUTS
|
|
65
|
+
System.String or $null
|
|
66
|
+
#>
|
|
67
|
+
[CmdletBinding()]
|
|
68
|
+
[OutputType([string])]
|
|
69
|
+
param([Parameter(Mandatory)][ValidatePattern('^([A-Za-z]:[\\/]|/)')][string] $Path)
|
|
70
|
+
|
|
71
|
+
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
|
|
72
|
+
return $null
|
|
73
|
+
}
|
|
74
|
+
return (Get-Content -LiteralPath $Path -Raw)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function Get-EpicOrchestratorCheckpointContent {
|
|
78
|
+
<#
|
|
79
|
+
.SYNOPSIS
|
|
80
|
+
Read the raw JSON text of the epic checkpoint. Tests mock this function
|
|
81
|
+
(read seam).
|
|
82
|
+
.PARAMETER Path
|
|
83
|
+
The absolute checkpoint path composed beneath the resolved worktree root.
|
|
84
|
+
.OUTPUTS
|
|
85
|
+
System.String or $null
|
|
86
|
+
#>
|
|
87
|
+
[CmdletBinding()]
|
|
88
|
+
[OutputType([string])]
|
|
89
|
+
param([Parameter(Mandatory)][ValidatePattern('^([A-Za-z]:[\\/]|/)')][string] $Path)
|
|
90
|
+
|
|
91
|
+
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
|
|
92
|
+
return $null
|
|
93
|
+
}
|
|
94
|
+
return (Get-Content -LiteralPath $Path -Raw)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function Get-ParallelOrchestratorCheckpointContent {
|
|
98
|
+
<#
|
|
99
|
+
.SYNOPSIS
|
|
100
|
+
Read the raw JSON text of the parallel-orchestrator checkpoint. Tests mock
|
|
101
|
+
this function (read seam).
|
|
102
|
+
.PARAMETER Path
|
|
103
|
+
The absolute checkpoint path composed beneath the resolved worktree root.
|
|
104
|
+
.OUTPUTS
|
|
105
|
+
System.String or $null
|
|
106
|
+
#>
|
|
107
|
+
[CmdletBinding()]
|
|
108
|
+
[OutputType([string])]
|
|
109
|
+
param([Parameter(Mandatory)][ValidatePattern('^([A-Za-z]:[\\/]|/)')][string] $Path)
|
|
110
|
+
|
|
111
|
+
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
|
|
112
|
+
return $null
|
|
113
|
+
}
|
|
114
|
+
return (Get-Content -LiteralPath $Path -Raw)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function Get-EpicMergeGateSessionWorktreeRoot {
|
|
118
|
+
<#
|
|
119
|
+
.SYNOPSIS
|
|
120
|
+
Return the worktree root the session runs in (the child branch and bare-command root).
|
|
121
|
+
.OUTPUTS
|
|
122
|
+
System.String
|
|
123
|
+
#>
|
|
124
|
+
[CmdletBinding()]
|
|
125
|
+
[OutputType([string])]
|
|
126
|
+
param()
|
|
127
|
+
|
|
128
|
+
return (Resolve-WorktreeOperandTarget -Path '' -SessionRoot (Get-Location).Path).WorktreeRoot
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function Resolve-EpicMergeGateRunTarget {
|
|
132
|
+
<#
|
|
133
|
+
.SYNOPSIS
|
|
134
|
+
Resolve the worktree whose run checkpoint records a pull request number (issue #690 seam).
|
|
135
|
+
.PARAMETER Kind
|
|
136
|
+
epic or parallel.
|
|
137
|
+
.PARAMETER PrNumber
|
|
138
|
+
The explicit pull request number the command names.
|
|
139
|
+
.OUTPUTS
|
|
140
|
+
System.Management.Automation.PSCustomObject (the worktree-resolution target result).
|
|
141
|
+
#>
|
|
142
|
+
[CmdletBinding()]
|
|
143
|
+
[OutputType([pscustomobject])]
|
|
144
|
+
param(
|
|
145
|
+
[Parameter(Mandatory)][ValidateSet('epic', 'parallel')][string] $Kind,
|
|
146
|
+
[Parameter(Mandatory)][int] $PrNumber
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
return Resolve-WorktreeRunTargetByRecord -Kind $Kind -RecordField pr_number -Value ([string]$PrNumber) -SessionRoot (Get-Location).Path
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function Test-ChildCheckpointPrGateBinding {
|
|
153
|
+
<#
|
|
154
|
+
.SYNOPSIS
|
|
155
|
+
True unless the per-feature checkpoint records a pr_gate.pr_number that differs from the command's.
|
|
156
|
+
.DESCRIPTION
|
|
157
|
+
A bare command, an absent checkpoint, and a checkpoint without pr_gate.pr_number
|
|
158
|
+
keep today's unbound child-branch decision. When both numbers are present they must
|
|
159
|
+
parse as integers and be equal.
|
|
160
|
+
.PARAMETER Checkpoint
|
|
161
|
+
Parsed per-feature checkpoint, or $null.
|
|
162
|
+
.PARAMETER CommandPrNumber
|
|
163
|
+
The explicit pull request number the command names, or $null.
|
|
164
|
+
.OUTPUTS
|
|
165
|
+
System.Boolean
|
|
166
|
+
#>
|
|
167
|
+
[CmdletBinding()]
|
|
168
|
+
[OutputType([bool])]
|
|
169
|
+
param(
|
|
170
|
+
[AllowNull()] $Checkpoint,
|
|
171
|
+
[AllowNull()][Nullable[int]] $CommandPrNumber
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
if ($null -eq $CommandPrNumber -or $null -eq $Checkpoint) {
|
|
175
|
+
return $true
|
|
176
|
+
}
|
|
177
|
+
if (@($Checkpoint.PSObject.Properties.Name) -notcontains 'pr_gate' -or $null -eq $Checkpoint.pr_gate) {
|
|
178
|
+
return $true
|
|
179
|
+
}
|
|
180
|
+
$prGate = $Checkpoint.pr_gate
|
|
181
|
+
if (@($prGate.PSObject.Properties.Name) -notcontains 'pr_number' -or $null -eq $prGate.pr_number) {
|
|
182
|
+
return $true
|
|
183
|
+
}
|
|
184
|
+
$recorded = 0
|
|
185
|
+
if (-not [int]::TryParse([string]$prGate.pr_number, [ref] $recorded)) {
|
|
186
|
+
return $false
|
|
187
|
+
}
|
|
188
|
+
return ($recorded -eq $CommandPrNumber)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function Get-EpicMergeGateUnresolvedReason {
|
|
192
|
+
<#
|
|
193
|
+
.SYNOPSIS
|
|
194
|
+
Returns '<code>: <epic detail>; <parallel detail>' when neither run branch resolved, or $null.
|
|
195
|
+
.DESCRIPTION
|
|
196
|
+
The ambiguity code wins when either target is Ambiguous. Returns $null when either
|
|
197
|
+
target is absent (a bare command) or either target resolved.
|
|
198
|
+
.PARAMETER EpicTarget
|
|
199
|
+
The epic run target, or $null.
|
|
200
|
+
.PARAMETER ParallelTarget
|
|
201
|
+
The parallel run target, or $null.
|
|
202
|
+
.OUTPUTS
|
|
203
|
+
System.String or $null
|
|
204
|
+
#>
|
|
205
|
+
[CmdletBinding()]
|
|
206
|
+
[OutputType([string])]
|
|
207
|
+
param(
|
|
208
|
+
[AllowNull()] $EpicTarget,
|
|
209
|
+
[AllowNull()] $ParallelTarget
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
if ($null -eq $EpicTarget -or $null -eq $ParallelTarget) {
|
|
213
|
+
return $null
|
|
214
|
+
}
|
|
215
|
+
$unresolved = @('NoTarget', 'Ambiguous')
|
|
216
|
+
if ($unresolved -notcontains $EpicTarget.Status -or $unresolved -notcontains $ParallelTarget.Status) {
|
|
217
|
+
return $null
|
|
218
|
+
}
|
|
219
|
+
$code = if ($EpicTarget.Status -eq 'Ambiguous') { $EpicTarget.ReasonCode } elseif ($ParallelTarget.Status -eq 'Ambiguous') { $ParallelTarget.ReasonCode } else { $EpicTarget.ReasonCode }
|
|
220
|
+
return ('{0}: {1}; {2}' -f $code, $EpicTarget.Detail, $ParallelTarget.Detail)
|
|
221
|
+
}
|
|
@@ -38,10 +38,16 @@
|
|
|
38
38
|
policy-level-not-cryptographic posture already accepted for
|
|
39
39
|
enforce-pr-author-skill.ps1's own receipt mechanism. It is not a cryptographic control.
|
|
40
40
|
|
|
41
|
+
Issue #690: with an explicit PR number, the epic and parallel checkpoints are read
|
|
42
|
+
beneath the live worktree whose run checkpoint records that number, and the child
|
|
43
|
+
branch binds the number to pr_gate.pr_number when the per-feature checkpoint records
|
|
44
|
+
it. A bare command reads every checkpoint beneath the session worktree.
|
|
45
|
+
|
|
41
46
|
.NOTES
|
|
42
|
-
Compatible with PowerShell 7+.
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
Compatible with PowerShell 7+. Depends on WorktreeRunResolution.psm1 through the
|
|
48
|
+
dot-sourced enforce-epic-merge-gate-resolution.ps1, whose import guard denies on a
|
|
49
|
+
failed import. Filesystem reads go through injectable wrapper functions so tests can
|
|
50
|
+
mock the boundary without writing temporary files.
|
|
45
51
|
|
|
46
52
|
Honest disclosure: the standalone-merge authorization record is a policy-level,
|
|
47
53
|
auditable declaration and is not a cryptographic or security control. authorized_by is
|
|
@@ -59,64 +65,8 @@ Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -
|
|
|
59
65
|
. (Join-Path $PSScriptRoot 'hook-command-invocation.ps1')
|
|
60
66
|
# Standalone-merge authorization predicates and the two decision-envelope factories (issue #670).
|
|
61
67
|
. (Join-Path $PSScriptRoot 'enforce-epic-merge-gate-authorization.ps1')
|
|
62
|
-
|
|
63
|
-
$
|
|
64
|
-
$script:EpicCheckpointPath = 'artifacts/orchestration/epic-orchestrator-state.json'
|
|
65
|
-
$script:ParallelCheckpointPath = 'artifacts/orchestration/parallel-orchestrator-state.json'
|
|
66
|
-
|
|
67
|
-
function Get-ChildOrchestratorCheckpointContent {
|
|
68
|
-
<#
|
|
69
|
-
.SYNOPSIS
|
|
70
|
-
Read the raw JSON text of the per-feature orchestrator checkpoint. Tests mock
|
|
71
|
-
this function (read seam).
|
|
72
|
-
.OUTPUTS
|
|
73
|
-
System.String or $null
|
|
74
|
-
#>
|
|
75
|
-
[CmdletBinding()]
|
|
76
|
-
[OutputType([string])]
|
|
77
|
-
param()
|
|
78
|
-
|
|
79
|
-
if (-not (Test-Path -LiteralPath $script:ChildCheckpointPath -PathType Leaf)) {
|
|
80
|
-
return $null
|
|
81
|
-
}
|
|
82
|
-
return (Get-Content -LiteralPath $script:ChildCheckpointPath -Raw)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function Get-EpicOrchestratorCheckpointContent {
|
|
86
|
-
<#
|
|
87
|
-
.SYNOPSIS
|
|
88
|
-
Read the raw JSON text of the epic checkpoint. Tests mock this function
|
|
89
|
-
(read seam).
|
|
90
|
-
.OUTPUTS
|
|
91
|
-
System.String or $null
|
|
92
|
-
#>
|
|
93
|
-
[CmdletBinding()]
|
|
94
|
-
[OutputType([string])]
|
|
95
|
-
param()
|
|
96
|
-
|
|
97
|
-
if (-not (Test-Path -LiteralPath $script:EpicCheckpointPath -PathType Leaf)) {
|
|
98
|
-
return $null
|
|
99
|
-
}
|
|
100
|
-
return (Get-Content -LiteralPath $script:EpicCheckpointPath -Raw)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function Get-ParallelOrchestratorCheckpointContent {
|
|
104
|
-
<#
|
|
105
|
-
.SYNOPSIS
|
|
106
|
-
Read the raw JSON text of the parallel-orchestrator checkpoint. Tests mock
|
|
107
|
-
this function (read seam).
|
|
108
|
-
.OUTPUTS
|
|
109
|
-
System.String or $null
|
|
110
|
-
#>
|
|
111
|
-
[CmdletBinding()]
|
|
112
|
-
[OutputType([string])]
|
|
113
|
-
param()
|
|
114
|
-
|
|
115
|
-
if (-not (Test-Path -LiteralPath $script:ParallelCheckpointPath -PathType Leaf)) {
|
|
116
|
-
return $null
|
|
117
|
-
}
|
|
118
|
-
return (Get-Content -LiteralPath $script:ParallelCheckpointPath -Raw)
|
|
119
|
-
}
|
|
68
|
+
# Checkpoint read seams, the import guard, and run-target resolution (issue #690).
|
|
69
|
+
. (Join-Path $PSScriptRoot 'enforce-epic-merge-gate-resolution.ps1')
|
|
120
70
|
|
|
121
71
|
function ConvertFrom-EpicMergeGateJson {
|
|
122
72
|
<#
|
|
@@ -361,6 +311,12 @@ function Invoke-EpicMergeGateDecision {
|
|
|
361
311
|
[string] $ToolInputRaw
|
|
362
312
|
)
|
|
363
313
|
|
|
314
|
+
# A failed worktree-resolution import denies before any other logic (issue #690).
|
|
315
|
+
$importFailure = Get-EpicMergeGateImportFailureDecision
|
|
316
|
+
if ($null -ne $importFailure) {
|
|
317
|
+
return $importFailure
|
|
318
|
+
}
|
|
319
|
+
|
|
364
320
|
$payload = Resolve-ClaudeHookToolInput -Raw $ToolInputRaw
|
|
365
321
|
if (-not $payload.IsValid) {
|
|
366
322
|
return Get-EpicMergeGateBlockDecision -Reason (
|
|
@@ -399,18 +355,35 @@ function Invoke-EpicMergeGateDecision {
|
|
|
399
355
|
}
|
|
400
356
|
|
|
401
357
|
$commandPrNumber = Get-EpicMergeGateCommandPrNumber -CommandText $commandText
|
|
358
|
+
$sessionRoot = Get-EpicMergeGateSessionWorktreeRoot
|
|
402
359
|
|
|
403
|
-
|
|
404
|
-
|
|
360
|
+
# The child merges from its own worktree, so its checkpoint is the session worktree's;
|
|
361
|
+
# a recorded pr_gate.pr_number must match an explicit command PR number (issue #690).
|
|
362
|
+
$childCheckpoint = ConvertFrom-EpicMergeGateJson -Raw (Get-ChildOrchestratorCheckpointContent -Path (Get-WorktreeRunCheckpointPath -Kind item -WorktreeRoot $sessionRoot))
|
|
363
|
+
if ((Test-ChildCheckpointAllowsEpicMerge -Checkpoint $childCheckpoint) -and
|
|
364
|
+
(Test-ChildCheckpointPrGateBinding -Checkpoint $childCheckpoint -CommandPrNumber $commandPrNumber)) {
|
|
405
365
|
return Get-EpicMergeGateAllowDecision
|
|
406
366
|
}
|
|
407
367
|
|
|
408
|
-
|
|
368
|
+
# A bare command targets the current branch's PR, so it reads the session worktree. An
|
|
369
|
+
# explicit PR number locates each run checkpoint that records it (issue #690).
|
|
370
|
+
$epicTarget = $null
|
|
371
|
+
$parallelTarget = $null
|
|
372
|
+
$epicRoot = $sessionRoot
|
|
373
|
+
$parallelRoot = $sessionRoot
|
|
374
|
+
if ($null -ne $commandPrNumber) {
|
|
375
|
+
$epicTarget = Resolve-EpicMergeGateRunTarget -Kind epic -PrNumber $commandPrNumber
|
|
376
|
+
$parallelTarget = Resolve-EpicMergeGateRunTarget -Kind parallel -PrNumber $commandPrNumber
|
|
377
|
+
$epicRoot = if (@('SessionRoot', 'OtherWorktree') -contains $epicTarget.Status) { $epicTarget.WorktreeRoot } else { $null }
|
|
378
|
+
$parallelRoot = if (@('SessionRoot', 'OtherWorktree') -contains $parallelTarget.Status) { $parallelTarget.WorktreeRoot } else { $null }
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
$epicCheckpoint = if ($epicRoot) { ConvertFrom-EpicMergeGateJson -Raw (Get-EpicOrchestratorCheckpointContent -Path (Get-WorktreeRunCheckpointPath -Kind epic -WorktreeRoot $epicRoot)) } else { $null }
|
|
409
382
|
if (Test-EpicCheckpointAllowsMerge -Checkpoint $epicCheckpoint -CommandPrNumber $commandPrNumber) {
|
|
410
383
|
return Get-EpicMergeGateAllowDecision
|
|
411
384
|
}
|
|
412
385
|
|
|
413
|
-
$parallelCheckpoint = ConvertFrom-EpicMergeGateJson -Raw (Get-ParallelOrchestratorCheckpointContent)
|
|
386
|
+
$parallelCheckpoint = if ($parallelRoot) { ConvertFrom-EpicMergeGateJson -Raw (Get-ParallelOrchestratorCheckpointContent -Path (Get-WorktreeRunCheckpointPath -Kind parallel -WorktreeRoot $parallelRoot)) } else { $null }
|
|
414
387
|
if (Test-ParallelCheckpointAllowsMerge -Checkpoint $parallelCheckpoint -CommandPrNumber $commandPrNumber) {
|
|
415
388
|
return Get-EpicMergeGateAllowDecision
|
|
416
389
|
}
|
|
@@ -423,7 +396,10 @@ function Invoke-EpicMergeGateDecision {
|
|
|
423
396
|
if ($standalone.Allowed) {
|
|
424
397
|
return Get-EpicMergeGateAllowDecision
|
|
425
398
|
}
|
|
426
|
-
|
|
399
|
+
# When neither run branch resolved, its reason code and detail precede the standalone reason.
|
|
400
|
+
$unresolved = Get-EpicMergeGateUnresolvedReason -EpicTarget $epicTarget -ParallelTarget $parallelTarget
|
|
401
|
+
$prefix = if ($unresolved) { $unresolved + '; ' } else { '' }
|
|
402
|
+
return Get-EpicMergeGateBlockDecision -Reason ('EPIC_MERGE_GATE_BLOCKED: ' + $prefix + $standalone.ReasonCode + ': ' + $standalone.Message)
|
|
427
403
|
}
|
|
428
404
|
|
|
429
405
|
return Get-EpicMergeGateBlockDecision -Reason 'EPIC_MERGE_GATE_BLOCKED: gh pr merge --merge requires either a per-feature checkpoint with epic_mode == true and step9_status == "passed", an epic checkpoint with epic_merge_pr.ci_gate.conclusion == "success" and a matching pr_number, or a parallel-orchestrator checkpoint with route_id == "parallel" whose target item (matched by pr_number) has merge_status == "ci_green". No checkpoint satisfied this gate.'
|