@danmoisan/drm-copilot-mcp 1.1.10 → 1.1.12
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 +3517 -1104
- package/package.json +1 -1
- package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +1 -1
- package/resources/claude-customizations/.claude/agents/epic-planner.md +4 -1
- package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +15 -2
- package/resources/claude-customizations/.claude/agents/parallel-planner.md +3 -0
- package/resources/claude-customizations/.claude/agents/pr-author.md +15 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-authorization.ps1 +443 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +82 -51
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +52 -3
- package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +120 -5
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +127 -0
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +163 -15
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +21 -28
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +103 -6
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +124 -8
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +171 -15
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.epic-base-branch.ps1 +50 -10
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +3 -1
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner-helpers.ps1 +332 -0
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +211 -182
- package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +35 -7
- package/resources/claude-customizations/.claude/hooks/hook-command-invocation.ps1 +483 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-scanner.ps1 +483 -0
- package/resources/claude-customizations/.claude/hooks/validate-bash.ps1 +218 -6
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadius.psm1 +18 -75
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusConflict.psm1 +290 -0
- package/resources/claude-customizations/.claude/lib/cleanup-manifest/CleanupWorktreeManifest.psm1 +415 -0
- package/resources/claude-customizations/.claude/lib/codex-routing/CodexDeployment.psm1 +1 -0
- package/resources/claude-customizations/.claude/lib/project-file-merge/ProjectFileMerge.psm1 +355 -0
- package/resources/claude-customizations/.claude/lib/project-file-merge/ProjectFileMergeGrammar.psm1 +318 -0
- package/resources/claude-customizations/.claude/lib/project-file-merge/Resolve-MergeableConflict.ps1 +229 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/EpicScopeReadiness.psm1 +176 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/EpicScopeResolution.psm1 +369 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +392 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeResolution.psm1 +500 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeTargetResolution.psm1 +347 -0
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +27 -0
- package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +66 -2
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +323 -16
- package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +14 -1
- package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +4 -0
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +46 -1
- package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +3 -1
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +75 -8
- package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +6 -0
- package/resources/claude-customizations/.claude/skills/powershell-orchestration-state-machine/SKILL.md +29 -1
- package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +15 -0
- package/resources/claude-customizations/.claude/skills/quota-throttling/SKILL.md +221 -0
- package/resources/claude-customizations/config/blast-radius.json +7 -0
- package/resources/claude-customizations/pack-manifests/core.json +16 -0
- package/resources/codex-and-agents-customizations/.agents/skills/codex-model-routing/SKILL.md +13 -14
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +41 -0
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-state/SKILL.md +41 -0
- package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/SKILL.md +26 -0
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +242 -3
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +67 -12
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-worktree-removal-gate.ps1 +34 -8
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +163 -15
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +6 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +34 -7
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation.ps1 +483 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-scanner.ps1 +483 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-bash.ps1 +130 -2
- package/resources/codex-and-agents-customizations/.codex/scripts/Resolve-CodexDeployment.ps1 +87 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/Resolve-CodexTopology.ps1 +96 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/codex-routing-cli-common.ps1 +370 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-launch-contract.ps1 +475 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-launch-runtime.ps1 +445 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-persistence-runtime.ps1 +118 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-sandbox-preflight.ps1 +69 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/launch-epic-child-wave.ps1 +471 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/post-codex-worktree-session.ps1 +280 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/resume-epic-child.ps1 +263 -0
- package/resources/codex-and-agents-customizations/.codex-variants/csharp-legacy/agents/csharp-typed-engineer.toml +2 -1
- package/resources/codex-and-agents-customizations/pack-manifests/core.json +23 -1
- package/resources/config/orchestration-handoff-registry.json +138 -0
- package/resources/config/orchestration-handoff.schema.json +472 -0
- package/resources/lib/codex-routing/CodexDeployment.psm1 +315 -0
- package/resources/lib/codex-routing/CodexTopology.psm1 +394 -0
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +64 -0
|
@@ -17,6 +17,7 @@ allowed-tools:
|
|
|
17
17
|
- "Bash(git diff *)"
|
|
18
18
|
- "Bash(git branch -r*)"
|
|
19
19
|
- "Bash(git worktree list*)"
|
|
20
|
+
- "Bash(git worktree remove *)"
|
|
20
21
|
- "Bash(gh issue view *)"
|
|
21
22
|
- mcp__drm-copilot__new_potential_bug_entry
|
|
22
23
|
- mcp__drm-copilot__potential_to_issue
|
|
@@ -64,11 +65,83 @@ The script emits pipe-delimited, `LC_ALL=C`-ordered records, one per line:
|
|
|
64
65
|
in `EQUIVALENT | CONTENT_ON_MAIN | EMPTY | UNIQUE | CONFLICT`. A `UNIQUE` COMMIT record
|
|
65
66
|
is a cherry-pick candidate for editorial triage.
|
|
66
67
|
- `WORKTREE|<path>|<branch-or-DETACHED>|<flags>` — worktree registrations.
|
|
68
|
+
- `WORKTREE|<path>|DETACHED|<state>|<flags>` — a detached-HEAD worktree registration,
|
|
69
|
+
classified on its own HEAD SHA. This five-field record replaces the four-field record
|
|
70
|
+
above for a detached registration; it is not emitted in addition to it. `state` is one
|
|
71
|
+
of `MERGED_CLEAN | MERGED_CONTENT_NEUTRAL | MERGED_EQUIVALENT | NOT_MERGED |
|
|
72
|
+
HAS_UNIQUE_RESIDUALS | PROTECTED_CURRENT | ANCESTRY_ERROR`, where `ANCESTRY_ERROR` is
|
|
73
|
+
the fail-closed verdict for a hard git failure at any rung of the classification. The
|
|
74
|
+
fifth field carries the porcelain flag set unchanged, so the `locked` and `prunable`
|
|
75
|
+
markers are preserved in the record.
|
|
67
76
|
- `WARN|main-divergence|<local-sha>|<origin-sha>` — local `main` differs from
|
|
68
77
|
`origin/main` (advisory; classification still runs).
|
|
69
78
|
- `DIRTY|<worktree-path>|<status-porcelain-line>` — a dirty worktree that blocked
|
|
70
|
-
removal.
|
|
79
|
+
removal. This record is apply mode only, and its three-field shape is unchanged by the
|
|
80
|
+
dirt classifier: the classifier adds the two records below rather than altering or
|
|
81
|
+
replacing this one.
|
|
82
|
+
- `DIRTFILE|<worktree-path>|<verdict>|<detail>|<xy>|<file-path>` — one record per
|
|
83
|
+
`git status --porcelain` entry in a dirty worktree, emitted by report mode in porcelain
|
|
84
|
+
order immediately after that worktree's `WORKTREE|` record. `verdict` is exactly one of
|
|
85
|
+
`DISPOSABLE_BUILD_ARTIFACT | DISPOSABLE_SESSION_ARTIFACT | CONTENT_ON_MAIN |
|
|
86
|
+
CONTENT_IN_HISTORY | STAGED_TREE_IS_COMMIT | UNIQUE`, and no other verdict token is ever
|
|
87
|
+
produced. `detail` carries the commit SHA that justifies the verdict for
|
|
88
|
+
`STAGED_TREE_IS_COMMIT` and `CONTENT_IN_HISTORY`, and is empty for the other four
|
|
89
|
+
verdicts. `xy` is the porcelain two-character status field, preserved verbatim including
|
|
90
|
+
its space. The file path is the last field, so a path containing a pipe character cannot
|
|
91
|
+
shift any later field. A classification read that fails maps its entry to `UNIQUE`: that
|
|
92
|
+
is the fail-closed direction, because an entry the tool could not classify is treated as
|
|
93
|
+
content that must be preserved. `CONTENT_ON_MAIN` is emitted for a tracked entry
|
|
94
|
+
only when `main` contains the path, so an entry whose content exists only as a staged
|
|
95
|
+
blob is reported `UNIQUE` rather than as content that is already on `main`. An entry
|
|
96
|
+
whose porcelain status shows content in both the index and the working tree is likewise
|
|
97
|
+
reported `UNIQUE`, because the rungs that could otherwise resolve it compare working-tree
|
|
98
|
+
content only and would leave the differing staged blob unaccounted for.
|
|
99
|
+
`DISPOSABLE_SESSION_ARTIFACT` matches three fixed
|
|
100
|
+
repository paths under `artifacts/`, is repository-agnostic, and cannot fire in a checkout
|
|
101
|
+
that gitignores `artifacts/` — which drm-copilot does at `.gitignore:6` — because the
|
|
102
|
+
status read never carries `--ignored`; the verdict is retained for consumer checkouts
|
|
103
|
+
where those paths are not ignored, and it must not be made reachable by adding
|
|
104
|
+
`--ignored`.
|
|
105
|
+
- `DIRTSUM|<worktree-path>|<aggregate>|<detail>` — exactly one record per dirty worktree,
|
|
106
|
+
emitted by report mode immediately after that worktree's `DIRTFILE|` records.
|
|
107
|
+
`aggregate` is `ALL_DISPOSABLE` if and only if the worktree has at least one status
|
|
108
|
+
entry and none of them is `UNIQUE`, and `HAS_UNIQUE` otherwise. `detail` carries the
|
|
109
|
+
first non-empty per-entry detail. A worktree with zero status entries emits neither this
|
|
110
|
+
record nor any `DIRTFILE|` record. Both records are read-only: an `ALL_DISPOSABLE`
|
|
111
|
+
aggregate unlocks nothing on its own, and clearing happens only when the operator
|
|
112
|
+
supplies `--clear-disposable` in apply mode.
|
|
113
|
+
|
|
114
|
+
Report-mode exit status. When a candidate worktree's `git status --porcelain` read
|
|
115
|
+
fails, report mode emits no `DIRTFILE|` and no `DIRTSUM|` record for that worktree and
|
|
116
|
+
returns git's non-zero exit code. A checkout containing such a worktree therefore exits
|
|
117
|
+
non-zero from report mode where the same checkout previously exited 0 and produced a
|
|
118
|
+
complete report. This is deliberate. A worktree whose status read failed produces no dirt
|
|
119
|
+
records at all, so a report that also exited 0 would be indistinguishable from a report
|
|
120
|
+
about a clean worktree, and an operator would make a deletion decision on silently
|
|
121
|
+
incomplete data. The non-zero exit is the only channel that carries the incompleteness to
|
|
122
|
+
a wrapping script. See the analogous apply-mode note in the End-to-End Workflow, where a
|
|
123
|
+
blocked detached removal sets a non-zero exit status for the same reason.
|
|
71
124
|
- `ACTION|<verb>|<target>|<result>` — apply-mode action results.
|
|
125
|
+
- `PRESERVE|<worktree-path>|<source-path>|<verdict>` — a manifest `preserved_files[]`
|
|
126
|
+
finding staged onto the consolidation branch; `verdict` in `DEAD_ONE_OFF |
|
|
127
|
+
ALREADY_SOLVED_ELSEWHERE | STALE_OR_CONTRADICTED | GENUINELY_NEW | STILL_RELEVANT`. The
|
|
128
|
+
per-file outcome is reported by the companion `ACTION|preserve-stage|...` record.
|
|
129
|
+
- `ORPHAN_DIR|<path>|<size>` — a directory under a worktree-tracking root that carries
|
|
130
|
+
no `.git` pointer file and no `git worktree list` entry. `<size>` is best-effort and
|
|
131
|
+
may be the literal `unknown`. The record is advisory: it reports the directory, and
|
|
132
|
+
nothing in apply mode acts on it. For the disposition, see the Dirty Worktree Triage
|
|
133
|
+
Procedure's step 7, which governs how an orphaned directory is handled.
|
|
134
|
+
- `STALE_REF|<refname>` — a `refs/remotes/<name>/*` ref whose `<name>` is not a
|
|
135
|
+
configured remote, named in full ref form. Advisory only; no ref is ever pruned by
|
|
136
|
+
this tool.
|
|
137
|
+
- `CHILD_OF|<branch>|<ancestor>` — `branch` is a git ancestor of `ancestor`, and both
|
|
138
|
+
resolved exactly `NOT_MERGED` through their own full ladders. It is emitted alongside,
|
|
139
|
+
never instead of, the branch's own `BRANCH|<branch>|NOT_MERGED` line, and names the
|
|
140
|
+
containment relationship so an operator can see that the branch's work is not lost
|
|
141
|
+
when the named ancestor is retained.
|
|
142
|
+
- `WARN|registration-lost|<path>` — a worktree directory whose `.git` pointer file names
|
|
143
|
+
a gitdir target that no longer exists. Git commands run inside such a directory fail
|
|
144
|
+
in confusing ways. Advisory only.
|
|
72
145
|
|
|
73
146
|
## End-to-End Workflow
|
|
74
147
|
|
|
@@ -101,11 +174,31 @@ The script emits pipe-delimited, `LC_ALL=C`-ordered records, one per line:
|
|
|
101
174
|
(`artifacts/orchestration/orchestrator-state.json`) with `--require-pr-creation-ready`
|
|
102
175
|
and record the `pr_author_preflight` result; delegation is prohibited when that
|
|
103
176
|
validation fails. Then delegate PR creation to `Agent(pr-author)` per
|
|
104
|
-
`.claude/skills/pr-author/SKILL.md`,
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
177
|
+
`.claude/skills/pr-author/SKILL.md`, which owns the body-file and receipt contract;
|
|
178
|
+
defer to that skill for it rather than restating any value from it here. For `<N>`,
|
|
179
|
+
use the GitHub issue number the run executes under when the run has one. When the run
|
|
180
|
+
has none, `<N>` is an arbitrary run-scoped identifier chosen by the pr-author agent.
|
|
181
|
+
The delegation prompt MUST carry a `branch: <name>` label naming the consolidation branch,
|
|
182
|
+
and the canonical issue number line when the run has an issue number.
|
|
183
|
+
`enforce-model-routing-receipt.ps1` identifies the item from those two lines and denies a
|
|
184
|
+
gated delegation it cannot identify. A cleanup run with no issue number is therefore
|
|
185
|
+
identified by the branch label alone, which is why the label is required and the issue line
|
|
186
|
+
is not.
|
|
187
|
+
It is not a pull-request number, and the only requirement on it is that the body-file
|
|
188
|
+
path, the receipt's `number` field, and the body bytes all agree. This skill never
|
|
189
|
+
authors or creates the PR itself.
|
|
190
|
+
|
|
191
|
+
5. **Wait for merge and verify git-natively.** The consolidation PR's merge is
|
|
192
|
+
**human-performed**: the operator merges it outside the agent session; this skill
|
|
193
|
+
never issues the merge itself. This is why: `gh pr merge` is absent from this skill's
|
|
194
|
+
`allowed-tools`; the project permission allow-list (`permissions.allow` in
|
|
195
|
+
`.claude/settings.json`) carries no `gh` entry; and `.claude/hooks/enforce-epic-merge-gate.ps1`
|
|
196
|
+
would deny the command with `EPIC_MERGE_GATE_BLOCKED` if it were attempted. At this
|
|
197
|
+
boundary the agent reports the consolidation pull request's URL or number to the
|
|
198
|
+
operator and stops. The ruleset on `main` sets `strict_required_status_checks_policy`,
|
|
199
|
+
so the branch must be up to date with `main` before the merge becomes available to the
|
|
200
|
+
operator; the wait for the merge is unbounded within a session. After the consolidation
|
|
201
|
+
PR merges, verify it with `git fetch` followed by
|
|
109
202
|
`git merge-base --is-ancestor documentationandmemories main`. Exit 0 confirms every
|
|
110
203
|
consolidated commit is now reachable from `main`; that is the only state that unlocks
|
|
111
204
|
deletion of branches whose unique content was consolidated.
|
|
@@ -119,6 +212,31 @@ The script emits pipe-delimited, `LC_ALL=C`-ordered records, one per line:
|
|
|
119
212
|
branch classified `NOT_MERGED` or `HAS_UNIQUE_RESIDUALS` — is not abandoned; it moves
|
|
120
213
|
to the Dirty Worktree Triage Procedure below.
|
|
121
214
|
|
|
215
|
+
A consolidation branch whose tip equals `main`, which is the state of
|
|
216
|
+
`documentationandmemories` between its creation off `main` and its first commit, is
|
|
217
|
+
reported `NOT_ANCESTOR` by a tip-equality pre-check that runs before any network fetch
|
|
218
|
+
and is therefore not delete-eligible; the post-merge cleanup described above is
|
|
219
|
+
unaffected, because a merged consolidation branch's tip differs from `main`, and an
|
|
220
|
+
empty or unresolvable tip on either side is reported `ANCESTRY_ERROR` rather than
|
|
221
|
+
treated as equality.
|
|
222
|
+
|
|
223
|
+
Detached-HEAD worktrees are handled on the same terms. Apply mode classifies each on
|
|
224
|
+
its own HEAD SHA, emits the five-field `WORKTREE|<path>|DETACHED|<state>|<flags>`
|
|
225
|
+
record from that verdict, and acts only on the delete-eligible states `MERGED_CLEAN`,
|
|
226
|
+
`MERGED_CONTENT_NEUTRAL`, and `MERGED_EQUIVALENT`. A delete-eligible detached worktree
|
|
227
|
+
is re-verified in the same process immediately before the destructive action and is
|
|
228
|
+
then removed without force. A locked one is skipped with the result token
|
|
229
|
+
`ACTION|worktree-remove|<path>|BLOCKED-LOCKED`; the locked test reads the porcelain
|
|
230
|
+
flags already in hand and precedes every git invocation, so no `git worktree remove`
|
|
231
|
+
command is issued for it. A prunable one is report-only: it produces its registration
|
|
232
|
+
record and no further line, with no removal and no prune. A dirty one reaches the same
|
|
233
|
+
non-forced removal path as a branch-backed worktree and is reported `BLOCKED-DIRTY`
|
|
234
|
+
with its accompanying `DIRTY|` lines.
|
|
235
|
+
In apply mode a blocked detached removal — `BLOCKED-DIRTY`, `BLOCKED-LOCKED`, or
|
|
236
|
+
`BLOCKED-REVERIFY` — sets a non-zero exit status, so a checkout holding dirty or locked
|
|
237
|
+
detached worktrees exits non-zero from `--apply` where the same checkout previously
|
|
238
|
+
exited 0.
|
|
239
|
+
|
|
122
240
|
## Nothing to Consolidate (Short Path)
|
|
123
241
|
|
|
124
242
|
When report mode classifies every candidate as `MERGED_CLEAN` or `MERGED_EQUIVALENT`
|
|
@@ -134,7 +252,12 @@ accompanying `DIRTY|<path>|<status-porcelain-line>` records), or a branch classi
|
|
|
134
252
|
script correctly refuses to discard. That refusal is correct and this procedure never
|
|
135
253
|
overrides it — a dirty worktree is never force-removed. This procedure is the systematic
|
|
136
254
|
follow-up: deciding, per worktree, whether that content is disposable or must be
|
|
137
|
-
preserved before the worktree can ever be deleted.
|
|
255
|
+
preserved before the worktree can ever be deleted. Report mode now precedes this
|
|
256
|
+
procedure with a machine-readable first pass: one `DIRTFILE|` record per status entry
|
|
257
|
+
giving that entry's verdict, and one `DIRTSUM|` record per worktree giving the aggregate
|
|
258
|
+
`ALL_DISPOSABLE` or `HAS_UNIQUE`. Steps 1-9 below apply to the worktrees carrying at
|
|
259
|
+
least one `UNIQUE` verdict, which are exactly the ones the classifier could not establish
|
|
260
|
+
as disposable and which therefore still require human editorial judgement.
|
|
138
261
|
|
|
139
262
|
Steps 1-7 are read-only investigation. Run them per worktree, or fan out one
|
|
140
263
|
`Agent(general-purpose)` investigation per worktree (or small batch) concurrently per
|
|
@@ -185,7 +308,12 @@ specific files or commit SHAs, before step 9 acts on any finding.
|
|
|
185
308
|
build artifacts (a modified `.csproj`/`packages.config`/`app.config` from a build run
|
|
186
309
|
in that worktree) rather than documentation. Diff a representative sample against
|
|
187
310
|
`main` (`git diff main -- <path>`) to characterize the change before deciding it is
|
|
188
|
-
disposable.
|
|
311
|
+
disposable. The classifier scopes this class narrowly: it labels a project file
|
|
312
|
+
`DISPOSABLE_BUILD_ARTIFACT` only when every changed line in that entry's diff is an
|
|
313
|
+
analyzer `HintPath` rewrite. A project file whose diff touches anything else — a
|
|
314
|
+
`Compile Include` registration, a package reference, a target or property change — is
|
|
315
|
+
reported `UNIQUE` and is not disposable, so this step's manual characterization is
|
|
316
|
+
still required for it.
|
|
189
317
|
|
|
190
318
|
7. **Recognize orphaned non-worktree directories.** A path can still exist on disk
|
|
191
319
|
under a worktree-tracking folder after `git worktree remove` partially ran or
|
|
@@ -210,13 +338,32 @@ specific files or commit SHAs, before step 9 acts on any finding.
|
|
|
210
338
|
`mcp__drm-copilot__potential_to_issue` per
|
|
211
339
|
`.claude/skills/feature-promotion-lifecycle/SKILL.md`. For a `SAFE_TO_DELETE`
|
|
212
340
|
verdict, discard the content as a distinct, individually confirmed manual action —
|
|
213
|
-
clear the dirty working tree,
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
341
|
+
clear the dirty working tree, delete a disposable `NOT_MERGED`/`HAS_UNIQUE_RESIDUALS`
|
|
342
|
+
branch directly, or remove the worktree itself through a
|
|
343
|
+
manifest-authorized removal. A manifest-authorized removal is a single
|
|
344
|
+
`git worktree remove <path>` covering one worktree, issued as its own Bash tool call,
|
|
345
|
+
one call per worktree, and it is authorized only when the Sanctioned Removal Manifest
|
|
346
|
+
below carries a record for that exact path whose `removal_disposition` is
|
|
347
|
+
`SAFE_TO_DELETE` and whose `branch_state` is `NOT_MERGED` or `HAS_UNIQUE_RESIDUALS`.
|
|
348
|
+
Never pass a force flag to that command: a dirty worktree blocks deletion and is
|
|
349
|
+
reported for manual handling, and it is never force-removed. This is never automated:
|
|
350
|
+
the script's classification ladder and apply-mode allowlist are never changed to
|
|
351
|
+
accept these states, so a `--apply` run never deletes them on its own, before or
|
|
352
|
+
after triage. If discarding the working-tree content changes the branch's
|
|
353
|
+
classification (for example to content-neutral against `main`), a follow-up
|
|
354
|
+
report/apply pass then picks it up through the normal deterministic path.
|
|
355
|
+
|
|
356
|
+
Distinguish two different discards. The sentence above governs the editorial discard
|
|
357
|
+
of `UNIQUE` content — content a human judged safe to lose — and that discard stays
|
|
358
|
+
manual and individually confirmed. Separately, dirt the classifier established as
|
|
359
|
+
disposable, meaning a worktree whose `DIRTSUM|` aggregate is `ALL_DISPOSABLE`, can be
|
|
360
|
+
cleared by the tool itself under the opt-in `--clear-disposable` flag in apply mode.
|
|
361
|
+
That path never touches a `UNIQUE` verdict: one `UNIQUE` entry refuses the clear for
|
|
362
|
+
the whole worktree, and a classification read that errors produces a fail-closed
|
|
363
|
+
`UNIQUE` with the same effect. It is also not force-removal — it clears the working
|
|
364
|
+
tree and retries the same unforced `git worktree remove` after a fresh in-process
|
|
365
|
+
re-verification. The classification ladder and the apply-mode allowlist remain
|
|
366
|
+
unchanged by it.
|
|
220
367
|
|
|
221
368
|
10. **After local branch deletion, check origin too.** This skill is local-only by
|
|
222
369
|
design (see "When to Use This Skill"), which leaves stale branches on the remote for
|
|
@@ -228,13 +375,148 @@ specific files or commit SHAs, before step 9 acts on any finding.
|
|
|
228
375
|
cleanup, and never rely on this skill's general `Bash(git push *)` allowance to
|
|
229
376
|
perform it silently.
|
|
230
377
|
|
|
378
|
+
## Sanctioned Removal Manifest
|
|
379
|
+
|
|
380
|
+
**Write the manifest before step 9 of the Dirty Worktree Triage Procedure acts on any
|
|
381
|
+
`SAFE_TO_DELETE` verdict.** The run records its triage verdicts in
|
|
382
|
+
`artifacts/orchestration/cleanup-worktrees-manifest.json`. The two PreToolUse gates
|
|
383
|
+
`.claude/hooks/enforce-epic-worktree-removal-gate.ps1` and
|
|
384
|
+
`.claude/hooks/enforce-parallel-worktree-removal-gate.ps1` read that document and admit a
|
|
385
|
+
`git worktree remove <path>` command only when a record in it covers that exact path. A removal
|
|
386
|
+
issued with no covering record is denied with each gate's existing, unchanged reason code.
|
|
387
|
+
|
|
388
|
+
The document is UTF-8 JSON with `snake_case` keys. It carries six top-level fields. Its `removals`
|
|
389
|
+
and `preserved_files` entries are sibling arrays and are never nested: removal records are keyed by
|
|
390
|
+
worktree path, preserve records by the pair of worktree path and file path, and the gates must
|
|
391
|
+
never traverse preserve data.
|
|
392
|
+
|
|
393
|
+
### Top-level fields
|
|
394
|
+
|
|
395
|
+
- `tool` — string, required, exactly `cleanup-merged-worktrees`. The self-identifying
|
|
396
|
+
discriminator. Absent, non-string, or any other value and no record authorizes anything.
|
|
397
|
+
- `schema_version` — integer, required, exactly `1`. Absent, non-integer, or any other value fails
|
|
398
|
+
closed; a forward version is not accepted by silence.
|
|
399
|
+
- `generated_at` — string, required, an ISO-8601 UTC timestamp recording when the run wrote the
|
|
400
|
+
document. Absent, unparseable, in the future, or older than the 24-hour freshness bound fails
|
|
401
|
+
closed.
|
|
402
|
+
- `run_id` — string, required, non-empty. An opaque per-run identifier for audit correlation.
|
|
403
|
+
- `removals` — array, required, of removal records. It may be empty; absent, non-array, or empty
|
|
404
|
+
authorizes no removal.
|
|
405
|
+
- `preserved_files` — array, required, of preserve records. It may be empty. Neither gate ever
|
|
406
|
+
reads it, and its absence or malformation must not affect any gate decision.
|
|
407
|
+
|
|
408
|
+
### `removals[]` record fields
|
|
409
|
+
|
|
410
|
+
- `worktree_path` — string, required, non-empty. The absolute path of the worktree to remove. A
|
|
411
|
+
record with it absent or empty is skipped.
|
|
412
|
+
- `branch` — string or `null`, key required. The worktree's branch, or `null` for a detached
|
|
413
|
+
worktree. An absent key means the record does not authorize; `null` is a valid, meaningful value.
|
|
414
|
+
- `branch_state` — string, required. The classification the report emitted for that branch: one of
|
|
415
|
+
`MERGED_CLEAN`, `MERGED_CONTENT_NEUTRAL`, `MERGED_EQUIVALENT`, `NOT_MERGED`,
|
|
416
|
+
`HAS_UNIQUE_RESIDUALS`, or `PROTECTED_CURRENT`. Only `NOT_MERGED` and `HAS_UNIQUE_RESIDUALS`
|
|
417
|
+
authorize a removal; every other member, and any value outside the vocabulary, does not.
|
|
418
|
+
- `removal_disposition` — string, required. The triage disposition that authorizes removal. The
|
|
419
|
+
allowed set is exactly the single member `SAFE_TO_DELETE`. This key is deliberately distinct from
|
|
420
|
+
the `merge_status` key the orchestration checkpoints carry.
|
|
421
|
+
- `verdict` — string, required. The step-5 content classification that produced the disposition:
|
|
422
|
+
one of `DEAD_ONE_OFF`, `ALREADY_SOLVED_ELSEWHERE`, `STALE_OR_CONTRADICTED`, `GENUINELY_NEW`, or
|
|
423
|
+
`STILL_RELEVANT`. Absent, out of vocabulary, or preserve-implying and the record does not
|
|
424
|
+
authorize.
|
|
425
|
+
- `evidence` — string, required, non-empty. The justification step 9 requires, citing specific
|
|
426
|
+
files or commit SHAs. Absent, empty, or whitespace-only and the record does not authorize.
|
|
427
|
+
|
|
428
|
+
### `preserved_files[]` record fields
|
|
429
|
+
|
|
430
|
+
These records serve the consolidation consumer and are never read by either gate.
|
|
431
|
+
|
|
432
|
+
- `worktree_path` — string, required, non-empty. The worktree holding the file, and where the
|
|
433
|
+
consumer reads that worktree's `MEMORY.md`.
|
|
434
|
+
- `source_path` — string, required, repo-relative within that worktree. The file to preserve.
|
|
435
|
+
- `change_class` — string, required, either `untracked` or `modified`, recording whether staging
|
|
436
|
+
adds a new file or carries a working-tree modification.
|
|
437
|
+
- `disposition` — string, required, exactly `PRESERVE`.
|
|
438
|
+
- `verdict` — string, required, a member of the step-5 verdict vocabulary listed above.
|
|
439
|
+
- `target_path` — string, required, repo-relative on the consolidation branch. Required because the
|
|
440
|
+
same relative path may already exist on `main` with different content and a lesson file may be
|
|
441
|
+
re-namespaced; the consumer must not guess it.
|
|
442
|
+
- `memory_index_line` — string or `null`, key required. The source worktree's `MEMORY.md` index
|
|
443
|
+
line, or `null` when the file is not a memory entry.
|
|
444
|
+
- `line_ending` — string, required, one of `crlf`, `lf`, or `absent`. Advisory only: it records the
|
|
445
|
+
target file's existing convention, and the consumer re-derives and compares rather than trusting
|
|
446
|
+
it.
|
|
447
|
+
- `host_token_scan` — object, required, carrying a `result` member and a `pattern_set_id` member.
|
|
448
|
+
It records the scan outcome and the identifier of the pattern set used, never the patterns
|
|
449
|
+
themselves.
|
|
450
|
+
- `evidence` — string, required, non-empty.
|
|
451
|
+
|
|
452
|
+
### Example
|
|
453
|
+
|
|
454
|
+
```json
|
|
455
|
+
{
|
|
456
|
+
"tool": "cleanup-merged-worktrees",
|
|
457
|
+
"schema_version": 1,
|
|
458
|
+
"generated_at": "2026-09-07T03:40:00Z",
|
|
459
|
+
"run_id": "cleanup-2026-09-07T03-40-00Z-a47a5e33",
|
|
460
|
+
"removals": [
|
|
461
|
+
{
|
|
462
|
+
"worktree_path": "C:/Users/DanMoisan/repos/drm-copilot/.claude/worktrees/agent-0f1c2d",
|
|
463
|
+
"branch": "drm-copilot-wt-2026-08-14T09-02",
|
|
464
|
+
"branch_state": "HAS_UNIQUE_RESIDUALS",
|
|
465
|
+
"removal_disposition": "SAFE_TO_DELETE",
|
|
466
|
+
"verdict": "ALREADY_SOLVED_ELSEWHERE",
|
|
467
|
+
"evidence": "Unique residual commit 3f9a1c2 records the cleanup-worktrees ancestry error; main already fixes it at scripts/bash/cleanup_worktrees_lib.sh:214-231 under issue #612."
|
|
468
|
+
}
|
|
469
|
+
],
|
|
470
|
+
"preserved_files": [
|
|
471
|
+
{
|
|
472
|
+
"worktree_path": "C:/Users/DanMoisan/repos/drm-copilot/.claude/worktrees/agent-91ee43",
|
|
473
|
+
"source_path": ".claude/agent-memory/general-purpose/hook-payload-anomaly.md",
|
|
474
|
+
"change_class": "untracked",
|
|
475
|
+
"disposition": "PRESERVE",
|
|
476
|
+
"verdict": "GENUINELY_NEW",
|
|
477
|
+
"target_path": ".claude/agent-memory/general-purpose/hook-payload-anomaly-envelope.md",
|
|
478
|
+
"memory_index_line": "- [Hook payload anomaly envelope](hook-payload-anomaly-envelope.md) - the deny path a malformed envelope takes",
|
|
479
|
+
"line_ending": "crlf",
|
|
480
|
+
"host_token_scan": {
|
|
481
|
+
"result": "clean",
|
|
482
|
+
"pattern_set_id": "child-f-host-tokens-v1"
|
|
483
|
+
},
|
|
484
|
+
"evidence": "No equivalent file on main under .claude/agent-memory/**; grep for 'payload anomaly' returns only this worktree."
|
|
485
|
+
}
|
|
486
|
+
]
|
|
487
|
+
}
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
### Accepted residual
|
|
491
|
+
|
|
492
|
+
Both gates read the command text of the `Bash` tool call they are given. A removal routed
|
|
493
|
+
indirectly — for example by writing the command into a file and invoking that file with `bash
|
|
494
|
+
<file>` — presents command text the gates do not match, so the manifest requirement does not reach
|
|
495
|
+
it. This design does not close that indirection, and the requirement is recorded here as an
|
|
496
|
+
accepted residual rather than left implicit.
|
|
497
|
+
|
|
498
|
+
The manifest requirement is a policy-level integrity check, on the same terms
|
|
499
|
+
`.claude/hooks/enforce-pr-author-skill.ps1` records for its own receipt mechanism: it prevents
|
|
500
|
+
accidental bypass and requires a deliberate, documented act to circumvent. It is not a
|
|
501
|
+
cryptographic or security boundary, and it must not be described as tamper-proof. Routing a
|
|
502
|
+
removal through such an indirection in order to avoid the manifest requirement is a deliberate act
|
|
503
|
+
and is prohibited by this skill, which is the term the residual rests on.
|
|
504
|
+
|
|
231
505
|
## Prohibited Shortcuts
|
|
232
506
|
|
|
233
507
|
- Never invoke `gh pr create` or `gh pr edit --body*` from this skill or the scripts. PR
|
|
234
508
|
authoring is `Agent(pr-author)`'s exclusive responsibility and is enforced by the
|
|
235
509
|
`enforce-pr-author-skill.ps1` PreToolUse hook.
|
|
236
510
|
- Never pass a force flag to `git worktree remove`. A dirty worktree blocks deletion and
|
|
237
|
-
is reported for manual handling; it is never force-removed.
|
|
511
|
+
is reported for manual handling; it is never force-removed. The `--clear-disposable`
|
|
512
|
+
flag is not an exception to this rule and is not force-removal: it clears the working
|
|
513
|
+
tree first and then retries the same unforced `git worktree remove`, so the removal it
|
|
514
|
+
retries is the identical unforced call that was blocked. It runs only when every
|
|
515
|
+
per-file verdict for that worktree is non-`UNIQUE`, only in apply mode, only when the
|
|
516
|
+
operator explicitly requests it, and only after a fresh in-process re-verification that
|
|
517
|
+
the branch is still delete-eligible. A single `UNIQUE` verdict refuses the clear for the
|
|
518
|
+
whole worktree, including the fail-closed `UNIQUE` assigned when a classification read
|
|
519
|
+
errors.
|
|
238
520
|
- Never execute `git worktree prune`. Prunable registrations are report-only.
|
|
239
521
|
- Never act on `NOT_MERGED`, `HAS_UNIQUE_RESIDUALS`, or `PROTECTED_CURRENT` candidates
|
|
240
522
|
through the script or its apply-mode allowlist; `--apply` never mutates them, and the
|
|
@@ -243,12 +525,32 @@ specific files or commit SHAs, before step 9 acts on any finding.
|
|
|
243
525
|
only a distinct, individually confirmed manual action outside that automated path for
|
|
244
526
|
`NOT_MERGED`/`HAS_UNIQUE_RESIDUALS` — never a change to the classification ladder
|
|
245
527
|
itself, and never for `PROTECTED_CURRENT`.
|
|
528
|
+
- Never delete the base branch `main` and never remove a worktree checked out on it.
|
|
529
|
+
`compute_protected` protects `main` by name in every checkout topology, so it
|
|
530
|
+
classifies `PROTECTED_CURRENT` even when no protected worktree has it checked out, and
|
|
531
|
+
`delete_candidate` refuses a deletion request for it with
|
|
532
|
+
`ACTION|delete|main|BLOCKED-PROTECTED-BASE` before any re-verification, worktree
|
|
533
|
+
removal, or branch deletion runs.
|
|
246
534
|
- Never use commit-message text matching as a classification input, and never
|
|
247
535
|
auto-resolve cherry-pick conflicts.
|
|
248
536
|
- Never delete an origin branch, or run plain filesystem removal on an orphaned
|
|
249
537
|
worktree-tracking directory, without explicit per-item user confirmation — both are
|
|
250
538
|
outside this skill's pre-approved tool surface regardless of how the triage verdict
|
|
251
539
|
came out.
|
|
540
|
+
- Never issue the consolidation merge command, and never write or edit an orchestration
|
|
541
|
+
checkpoint in order to satisfy `.claude/hooks/enforce-epic-merge-gate.ps1`. This
|
|
542
|
+
forecloses the specific evasion of writing an
|
|
543
|
+
`artifacts/orchestration/orchestrator-state.json` whose `epic_mode` and `step9_status`
|
|
544
|
+
fields the gate's child-feature accept shape would honour for any pull-request number.
|
|
545
|
+
- Never widen the definition of disposable dirt. The session-artifact list is a fixed
|
|
546
|
+
in-script array of exactly three paths with no configuration override, no environment
|
|
547
|
+
variable, and no per-repository extension point; adding a path to it is a change to the
|
|
548
|
+
classifier, not a setting. The build-artifact rule requires both conditions together —
|
|
549
|
+
the `*.csproj` path pattern and the content confinement that every changed line in the
|
|
550
|
+
entry's diff is an analyzer `HintPath` rewrite — and a project file whose diff touches
|
|
551
|
+
anything else is `UNIQUE`. Ignored files are never cleared, because they are never
|
|
552
|
+
classified: `git status --porcelain` is read without `--ignored`, so an ignored file
|
|
553
|
+
never becomes an entry, and `git clean` is never given `-x`, `-X`, or `-ff`.
|
|
252
554
|
|
|
253
555
|
## Cross-References
|
|
254
556
|
|
|
@@ -262,3 +564,8 @@ specific files or commit SHAs, before step 9 acts on any finding.
|
|
|
262
564
|
- `.claude/skills/feature-promotion-lifecycle/SKILL.md` — the potential-entry-to-issue
|
|
263
565
|
promotion path used by the Dirty Worktree Triage Procedure's step 9 for `PRESERVE`
|
|
264
566
|
findings that describe unresolved product scope.
|
|
567
|
+
- `.claude/hooks/enforce-epic-merge-gate.ps1` — the PreToolUse gate on the consolidation
|
|
568
|
+
merge command, backed by orchestration checkpoints. A cleanup run satisfies none of its
|
|
569
|
+
three checkpoint shapes: it is neither a per-feature orchestration, nor an epic
|
|
570
|
+
integration, nor a parallel run, so it writes none of the three checkpoints the gate
|
|
571
|
+
reads.
|
|
@@ -170,6 +170,13 @@ frontmatter default — `opus` for these workers — which suppresses a `fable`
|
|
|
170
170
|
MUST NOT hard-code `model=opus` in a way that overrides the resolved routing model, mirroring
|
|
171
171
|
step 5 of `## Model Selection` in `.claude/skills/orchestrate/SKILL.md`.
|
|
172
172
|
|
|
173
|
+
Every such spawn of `Agent(pr-author)` also carries the canonical issue number line and a
|
|
174
|
+
`branch: <name>` label naming the branch checked out in the worktree the pull request is opened
|
|
175
|
+
from, per `## Issue Number Consistency` in `.claude/skills/orchestrate/SKILL.md`.
|
|
176
|
+
`enforce-model-routing-receipt.ps1` identifies the item from those two lines and denies a gated
|
|
177
|
+
delegation it cannot identify, so a spawn that omits both is refused rather than validated against
|
|
178
|
+
whichever checkpoint occupies the calling session's root.
|
|
179
|
+
|
|
173
180
|
`route` is never an input to model selection; `route` remains file-count driven and governs only
|
|
174
181
|
agents, skills, and MCP tools. A skill whose frontmatter `context` field holds the value `fork`
|
|
175
182
|
inherits the parent model and ignores a model override, so model selection applies to agent
|
|
@@ -281,7 +288,9 @@ checkpoint JSON remains the durable, machine-authoritative source.
|
|
|
281
288
|
`epic_feature_folder`, `epic_manifest_path` (which points at
|
|
282
289
|
`docs/features/epics/<epic-slug>/epic.md`), `epic_status_doc_path`, `integration_branch`,
|
|
283
290
|
`completed_steps`, `next_step`, `last_updated`, `current_wave`, `waves[]`, `features[]`,
|
|
284
|
-
`epic_merge_pr`,
|
|
291
|
+
`epic_merge_pr`, `epic_issue_num` (the epic-level issue `epic-planner` promoted),
|
|
292
|
+
`model_routing_receipts[]` (the routing receipt for each delegation `epic-orchestrator` itself
|
|
293
|
+
spawns), and the three receipt arrays (`delegation_receipts[]`, `skill_receipts[]`,
|
|
285
294
|
`mcp_call_receipts[]`). The
|
|
286
295
|
`merge_status` enum is: `not_started`, `worktree_created`, `pr_open`, `ci_green`,
|
|
287
296
|
`merge_conflict`, `blocked_conflict_loop_limit`, `merged`, `worktree_removed`. The optional
|
|
@@ -298,6 +307,10 @@ Validate the checkpoint through the
|
|
|
298
307
|
that same call at the completion gate. The validation is implemented in
|
|
299
308
|
`scripts/dev_tools/validate_epic_orchestrator_state.py`.
|
|
300
309
|
|
|
310
|
+
**Checkpoint hygiene (issue #673).** The coordinating session never holds a per-feature checkpoint at its own root. Before the first child delegation of a run it moves any `artifacts/orchestration/orchestrator-state.json` at its root to `artifacts/orchestration/handoff/orchestrator-state.issue-<issue-num>.<yyyy-MM-ddTHH-mm>.json`, and writes none there for the rest of the run, because each item's checkpoint lives in that item's worktree. A gated call the coordinator issues on an item's behalf is resolved by the item's issue number and branch, never by the coordinator root.
|
|
311
|
+
|
|
312
|
+
**Integration-PR checkpoint shape (issue #663).** The integration-to-`main` pull request and its `Agent(pr-author)` delegation are gated against `artifacts/orchestration/epic-orchestrator-state.json`, and no per-feature checkpoint is written for them. Before `epic-orchestrator` opens that pull request, the epic checkpoint carries `route_id: "epic"`, `integration_branch`, `epic_issue_num`, and a `features[]` array in which every entry's `merge_status` is `merged` or `worktree_removed`. Before the `Agent(pr-author)` delegation, `epic-orchestrator` records the `pr-author` routing receipt in that file's `model_routing_receipts[]`. The pr-author gate, the epic base-branch check, the model-routing gate, and the preimplementation gate treat a call as epic scope when its branch signal (or, for a staging command or file edit, the worktree HEAD) equals `integration_branch`; the pull request base must then be `main`.
|
|
313
|
+
|
|
301
314
|
## Completion Requirements
|
|
302
315
|
|
|
303
316
|
`epic-orchestrator` must not report completion until:
|
|
@@ -66,6 +66,10 @@ At manifest-authoring time child issues do not exist yet, so `issue_num` values
|
|
|
66
66
|
placeholders and back-filled from each child's promotion receipt as preparation completes. The
|
|
67
67
|
manifest is committed in final, resolved form before the kickoff artifact is written.
|
|
68
68
|
|
|
69
|
+
## Epic-Level Issue Promotion
|
|
70
|
+
|
|
71
|
+
Issue #663. Before the first preparation-mode child delegation, `epic-planner` promotes one epic-level GitHub issue for the epic itself. It authors a potential entry under `docs/features/potential/` describing the epic and promotes it through `mcp__drm-copilot__potential_to_issue` with `promotion_type: epic`; `gh issue create` is not used. It records the resulting number as `epic_issue_num` in the epic planning checkpoint (`artifacts/orchestration/epic-planner-state.json`) and in the kickoff prompt artifact, and `epic-orchestrator` copies it into the epic checkpoint `artifacts/orchestration/epic-orchestrator-state.json` at its first checkpoint write. The integration-to-`main` pull request uses `epic_issue_num` as its canonical issue number.
|
|
72
|
+
|
|
69
73
|
## Complexity Assessment
|
|
70
74
|
|
|
71
75
|
Assess each child feature's complexity band (`C1`-`C4`) using the `model_policy` scale and
|
|
@@ -33,6 +33,49 @@ On every invocation, the main session must:
|
|
|
33
33
|
1. Read `artifacts/orchestration/orchestrator-state.json` to check for existing state.
|
|
34
34
|
2. If a valid checkpoint exists with a matching objective, resume from the recorded `next_step`.
|
|
35
35
|
3. If no checkpoint exists or the objective is new, begin the orchestration lifecycle from the start.
|
|
36
|
+
4. **Checkpoint hygiene (issue #673).** When the checkpoint at `artifacts/orchestration/orchestrator-state.json` records an `issue-num` other than the item this invocation is about, move it to `artifacts/orchestration/handoff/orchestrator-state.issue-<issue-num>.<yyyy-MM-ddTHH-mm>.json` before writing a new checkpoint, rather than overwriting it or leaving it in place. Apply the same move when this session hands an item whose checkpoint it holds to another session or worktree. A checkpoint left at a session root is read by gates that resolve against that root, so a foreign one there is state a gate can answer from.
|
|
37
|
+
|
|
38
|
+
## Prepared-State Portable Handoff Intake
|
|
39
|
+
|
|
40
|
+
Treat a portable prepared-state checkpoint as a request to resume the recorded
|
|
41
|
+
lifecycle, not as a new orchestration intake. Before any destination launch:
|
|
42
|
+
|
|
43
|
+
1. Validate the envelope through the published, workspace-explicit handoff
|
|
44
|
+
authority. Require exact repository, workspace, branch lineage, issue,
|
|
45
|
+
feature folder, work mode, scheduler, capability, and transition bindings.
|
|
46
|
+
Supply the complete caller-controlled independent expected context on every
|
|
47
|
+
call to `resolve_orchestration_topology`, `resolve_provider_routing`, and
|
|
48
|
+
`transition_prepared_orchestration`: `expected_repository_id`,
|
|
49
|
+
`expected_workspace_root`, `expected_branch`, `expected_source_head_sha`,
|
|
50
|
+
`allowed_head_relationship`, `expected_issue_number`,
|
|
51
|
+
`expected_feature_folder`, `expected_work_mode`, `expected_plan_path`, and
|
|
52
|
+
`expected_plan_sha256`. Derive each value from the destination checkout and
|
|
53
|
+
the caller's own record. A call that omits a value, or that copies one from
|
|
54
|
+
the envelope under validation, is rejected before any service invocation.
|
|
55
|
+
2. Prove the plan using only the independently expected repository-relative
|
|
56
|
+
path and raw-byte SHA-256 the caller supplied in `expected_plan_path` and
|
|
57
|
+
`expected_plan_sha256`. Do not rediscover, rename, or substitute a plan, and
|
|
58
|
+
do not read the plan identity from the envelope.
|
|
59
|
+
3. Use `transition_prepared_orchestration` as the only authority that may
|
|
60
|
+
materialize a provider-native destination checkpoint. Dry run validates and
|
|
61
|
+
projects state without source archival or canonical-checkpoint replacement.
|
|
62
|
+
4. Preserve the envelope's completed phases and reject replay before mutation.
|
|
63
|
+
Promotion, research, feature-document authoring, atomic planning, and
|
|
64
|
+
preflight remain complete when the source recorded them as complete.
|
|
65
|
+
5. Keep source-provider model, reasoning, profile, topology, launch, worktree,
|
|
66
|
+
and receipt evidence opaque. Create destination routing evidence only for
|
|
67
|
+
the first new delegation after materialization.
|
|
68
|
+
|
|
69
|
+
When Claude is the destination, resolve its model through the existing Claude
|
|
70
|
+
model-routing procedure and launch with the existing `Agent(...)` and Claude
|
|
71
|
+
worktree mechanisms. Do not translate Codex deployment receipts into Claude
|
|
72
|
+
launch evidence. When Claude is the source, preserve Claude launch and
|
|
73
|
+
worktree evidence in the portable source expression and allow the Codex
|
|
74
|
+
destination to resolve its own topology and model. An ordinary handoff whose
|
|
75
|
+
recorded transition is `atomic_execution` resumes the exact approved plan at
|
|
76
|
+
that transition without invoking a parent scheduler. Any validation,
|
|
77
|
+
authority, plan, replay, or materialization failure returns the deterministic
|
|
78
|
+
blocked result before launch.
|
|
36
79
|
|
|
37
80
|
### Model-choice reconciliation on resume
|
|
38
81
|
|
|
@@ -213,10 +256,12 @@ A bounded loop consisting of five steps. The loop variable `remediation_pass` st
|
|
|
213
256
|
|
|
214
257
|
The canonical issue number is derived once from the active feature folder name: extract the trailing integer from the folder base name (e.g., `2026-04-26-push-down-claude-customizations-162` yields `162`). Record as `issue_num` in the checkpoint.
|
|
215
258
|
|
|
216
|
-
Every delegation prompt to `atomic-planner`, `atomic-executor`,
|
|
259
|
+
Every delegation prompt to a receipt-gated subagent type — `atomic-planner`, `atomic-executor`, `feature-review`, `task-researcher`, `prd-feature`, and `pr-author` — must include the line:
|
|
217
260
|
|
|
218
261
|
> `Canonical issue number for this feature is <issue_num>. All artifact content, file paths, and cross-references must use this number.`
|
|
219
262
|
|
|
263
|
+
Each such prompt also carries a `branch: <name>` label naming the branch checked out in the item's worktree, written as the first `branch:` occurrence in the prompt. Before promotion assigns an issue number, the branch label alone is required. Two gates, `enforce-model-routing-receipt.ps1` and `enforce-prd-feature-before-planner.ps1`, identify the item from these two lines: they deny a gated delegation they cannot identify rather than checking it against whichever checkpoint occupies the calling session's root, and they never use a feature-folder path to choose the worktree, because a merged feature folder exists in every checkout branched from main.
|
|
264
|
+
|
|
220
265
|
If a subagent artifact references a different issue number, the orchestrator rejects it, requests correction, and records the discrepancy under `artifact_errors` in the checkpoint.
|
|
221
266
|
|
|
222
267
|
## Step S9 — CI Green Gate
|
|
@@ -66,7 +66,9 @@ re-derivation is mandatory and is not an optimization to skip when the checkpoin
|
|
|
66
66
|
(Join-Path $repoRoot '.claude/lib/blast-radius/BlastRadius.psm1') -Force -ErrorAction Stop`).
|
|
67
67
|
Its two radius arguments are the two items' radius hashtables, not strings, and the third
|
|
68
68
|
argument is the required parsed `config/blast-radius.json` mapping, which push-down publishes
|
|
69
|
-
into the destination workspace.
|
|
69
|
+
into the destination workspace. That mapping's optional `mergeable_paths` list is read by
|
|
70
|
+
`Test-BlastRadiusConflict`, which contributes no `path_overlap` edge for a path matching it
|
|
71
|
+
while the path stays in the declared radius. `conflicts(a, b, config)` in
|
|
70
72
|
`scripts/dev_tools/compute_blast_radius.py` (defined in
|
|
71
73
|
`scripts/dev_tools/_blast_radius_conflicts.py`) remains the repository authority and the parity
|
|
72
74
|
reference. Read the verdict from `$result['conflict']`; do not test the returned hashtable
|