@danmoisan/drm-copilot-mcp 1.1.4 → 1.1.6
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 +335 -24
- package/package.json +1 -1
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +163 -18
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +477 -0
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +120 -12
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadius.psm1 +11 -0
- package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +1 -0
- package/resources/claude-customizations/.claude/rules/plan-acceptance-gates.md +131 -2
- package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +37 -0
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +136 -4
- package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +3 -1
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +18 -9
- package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +8 -1
- package/resources/claude-customizations/.claude/skills/pr-context-artifacts/SKILL.md +22 -0
- package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +10 -0
- package/resources/claude-customizations/config/orchestration-routing.json +1 -0
- package/resources/claude-customizations/pack-manifests/core.json +1 -0
- package/resources/codex-and-agents-customizations/.agents/skills/codex-model-routing/SKILL.md +10 -0
- package/resources/codex-and-agents-customizations/.agents/skills/pr-context-artifacts/SKILL.md +22 -0
- package/resources/codex-and-agents-customizations/.codex/agents/commit-steward-c1.toml +22 -0
- package/resources/codex-and-agents-customizations/.codex/agents/commit-steward-c2.toml +22 -0
- package/resources/codex-and-agents-customizations/.codex/agents/commit-steward-c3-elevated.toml +22 -0
- package/resources/codex-and-agents-customizations/.codex/agents/commit-steward-c3.toml +22 -0
- package/resources/codex-and-agents-customizations/.codex/agents/commit-steward-c4.toml +22 -0
- package/resources/codex-and-agents-customizations/.codex/agents/commit-steward.toml +2 -0
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c1.toml +7 -0
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +7 -0
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +7 -0
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +7 -0
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +7 -0
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +7 -0
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +477 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +125 -12
- package/resources/codex-and-agents-customizations/pack-manifests/core.json +8 -1
- package/resources/config/orchestration-routing.json +1 -0
- package/resources/customizations/.github/skills/pr-context-artifacts/SKILL.md +22 -0
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +10 -2
|
@@ -409,3 +409,4 @@ an unclassified key or a key present in only one copy fails loudly and names its
|
|
|
409
409
|
- Enforcement is therefore Python validator logic, plus the TypeScript parity port, plus this prose file. It is NEVER an imported JSON Schema. No schema file is read at validation time.
|
|
410
410
|
- The `parallel` route entry lives in `config/orchestration-routing.json` with `requires_pr_gate: false` (there is no run-level pull request to gate; each child's own route checkpoint enforces its per-item pull-request gate) and is mirrored byte-for-byte in `extensions/drm-copilot/resources/config/orchestration-routing.json`.
|
|
411
411
|
- The `PreToolUse` merge gate `.claude/hooks/enforce-epic-merge-gate.ps1` carries a parallel allow-branch that authorizes a per-item `gh pr merge --merge` from the parallel-orchestrator checkpoint when `route_id == "parallel"`, the target item's `merge_status == "ci_green"`, and the command's PR number matches that item's `pr_number`; any other case fails closed with `EPIC_MERGE_GATE_BLOCKED`.
|
|
412
|
+
- The `PreToolUse` worktree-removal gate `.claude/hooks/enforce-epic-worktree-removal-gate.ps1` likewise carries a parallel allow-branch that authorizes a per-item worktree removal from the parallel-orchestrator checkpoint when `route_id == "parallel"` and the `items[]` entry whose `worktree_path` matches the normalized removal target has `merge_status` in `{merged, worktree_removed}`; any other case — neither checkpoint present, either checkpoint unparseable, `route_id` absent or not `"parallel"`, no matching `worktree_path`, or a matched entry whose `merge_status` is outside that set or absent — fails closed with `EPIC_WORKTREE_REMOVAL_BLOCKED`. Removal is keyed on the worktree path rather than on `pr_number`, because the command names a path. This gate and the sibling gate `.claude/hooks/enforce-parallel-worktree-removal-gate.ps1`, which owns `PARALLEL_WORKTREE_REMOVAL_BLOCKED`, both fire on the same command, and `PreToolUse` denials are conjunctive, so both must allow for a removal to proceed.
|
|
@@ -36,10 +36,18 @@ Every finding string begins with the square-bracketed `P#-T#` identifier of the
|
|
|
36
36
|
| **G4** | A `--cov` value supplied space-separated (`--cov <value>`) rather than with `=`. The ambiguous form can bind the following positional argument. Independent of resolvability, so it is reported for every value. | **Warning** |
|
|
37
37
|
| **G5** | A checkable search literal that is absent from the tracked tree **and** not quoted in the plan document outside the command span it was read from. | **Warning** (see below) |
|
|
38
38
|
| **G6** | A checkable search literal absent from every single line of a tracked file but present in that file's sliding-window join of adjacent lines. A line-oriented search returns zero matches. | **Warning** |
|
|
39
|
+
| **G7** | A write-mode command — one matching a write-mode register entry, that is a tool that rewrites tracked source and exits 0 after rewriting — whose attributed task text carries none of that entry's observation markers. The exit code alone cannot distinguish a clean run from a repairing one, so the acceptance condition holds either way. | **Warning** |
|
|
40
|
+
| **G8** | A `git diff` invocation carrying no non-flag ref operand and neither `--cached` nor `--staged`. It compares the worktree against the index, which is ambient state: the comparison passes vacuously once the change is committed. Exonerated when the attributed task text carries a second `git diff` or a `git status` span. | **Warning** |
|
|
41
|
+
| **G8b** | A `git diff` invocation carrying a non-flag ref operand together with `--name-only` or `--name-status`, whose attributed task text carries neither a `git add` span nor a `git status --porcelain` span. A name-listing diff enumerates tracked changes only, so a path the plan creates is invisible to it. | **Warning** |
|
|
42
|
+
| **G9** | A command carrying a `--cov` token, carrying no token beginning `--cov-report=term` and no token beginning `--cov-fail-under`, whose project `addopts` value also carries no `--cov-report=term`. No coverage table is printed, so a coverage number the acceptance condition demands can never be read. | **Warning** |
|
|
43
|
+
|
|
44
|
+
G1 through G9 are the complete shipped set. G1 through G6 were added by issue #486 and G7 through G9 by issue #519; the parenthetical in this file's title names the original set only.
|
|
39
45
|
|
|
40
46
|
G1 through G4 form a cascade over each `--cov` value: the value is decided once, so a value G1 rejects is never additionally reported by G2 or G3. G4 is evaluated independently of the cascade because the ambiguous form is a defect whatever the value resolves to. G6 is evaluated before G5, because cross-line presence falsifies G5's tree-absence claim.
|
|
41
47
|
|
|
42
|
-
|
|
48
|
+
G8b is a distinct rule from G8 and not a sub-case of it: G8 reports a diff with no ref operand, G8b reports one that has a ref operand but cannot observe an untracked path. A single invocation can satisfy only one of the two, because the presence of a ref operand decides between them.
|
|
49
|
+
|
|
50
|
+
G1 and G4 are context-free and run on every invocation. G2, G3, G5, and G6 require a repository seam; with no context supplied they do not run, and the Blocking list is byte-identical to the pre-change output for the same text. G7, G8, and G8b are likewise context-free and run on every invocation. G9 requires the repository seam, because it reads the project `addopts` value through it, so with no context supplied G9 does not run. All four of the rules added by issue #519 ship in the Warning channel, so none of them can alter the Blocking list at all.
|
|
43
51
|
|
|
44
52
|
### Attribution window
|
|
45
53
|
|
|
@@ -55,7 +63,7 @@ A repository seam that raises, or that reports a non-zero exit, causes G2, G3, G
|
|
|
55
63
|
|
|
56
64
|
The shipped G5 severity was not chosen by argument. It was fixed by a pre-declared rule applied to a measurement over the committed plan corpus: Blocking if and only if the total G5 finding count is greater than zero **and** the recorded false-positive count is zero; otherwise Warning.
|
|
57
65
|
|
|
58
|
-
The measurement is recorded in `docs/features/
|
|
66
|
+
The measurement is recorded in `docs/features/completed/2026-08-17-reject-unfalsifiable-acceptance-gates-in-atomic-plans-486/evidence/qa-gates/g5-corpus-measurement.2026-08-20T12-02.md`. That feature has since been completed and its folder moved out of the active tree, so the citation names the completed tree; the path this file previously carried, under `docs/features/active/`, no longer resolves. It scanned 166 plan files, evaluated 100 candidate literals, and produced a total G5 finding count of 0. A zero false-positive count over zero findings measures nothing, so the first conjunct failed and **G5 ships as a Warning**.
|
|
59
67
|
|
|
60
68
|
The zero count is a property of the corpus, not a defect in the measurement. Every committed plan is a tracked file, so a fixed-string search for a literal quoted inside a committed plan always finds at least that plan itself, and the tree-absence condition holds for no committed candidate. The measurement artifact records the four checks that established this (non-vacuous enumeration, a working repository seam, a self-hit on every sampled lookup, and predicate-order equivalence with the shipped rule).
|
|
61
69
|
|
|
@@ -75,6 +83,127 @@ Rejecting such a plan would block a correct plan on evidence about a state the p
|
|
|
75
83
|
|
|
76
84
|
The window size is fixed at four adjacent non-blank lines. Blank lines are removed before windowing, and one window is emitted per start position, so the boundary is exact: two lines further apart than the window size never appear in the same join. The size is recorded here rather than left implicit so that a later feature can revise it against measured wrap-depth data instead of re-deriving it.
|
|
77
85
|
|
|
86
|
+
### The shared measurement behind G7, G8, G8b, and G9
|
|
87
|
+
|
|
88
|
+
The four severities below were fixed by one corpus measurement recorded in `docs/features/active/2026-08-23-plan-acceptance-gates-miss-unobservable-and-ambient-state-gates-519/evidence/qa-gates/corpus-measurement.2026-08-24T00-00.md`. The pre-declared decision rule was written and committed **before** the driver existed and before any count was taken, so the ordering is verifiable from git history rather than only asserted in prose. Its form matches the G5 precedent: the shipped severity is Blocking if and only if the total finding count for that rule is greater than zero **and** the recorded false-positive count for that rule is zero; otherwise Warning.
|
|
89
|
+
|
|
90
|
+
The false-positive definition the rule consumes is likewise pre-declared: a finding is a false positive when the acceptance condition it reports is in fact falsifiable — when the plan states an observation sufficient to distinguish a passing run from a failing one, by a mechanism the rule's predicate does not recognise. A finding is a true positive when the acceptance condition it reports genuinely cannot fail, or can only be satisfied vacuously.
|
|
91
|
+
|
|
92
|
+
The measurement scanned 194 plan files under `docs/features`, covering the `active`, `completed`, and `archive` trees. Unlike the G5 measurement, every rule here found findings, so each false-positive count is a count over an examined population rather than over an empty one. The vacuity declaration therefore applies to no rule of this set.
|
|
93
|
+
|
|
94
|
+
| Rule | Corpus files | Candidates | Findings | True positives | False positives | Decision | Shipped |
|
|
95
|
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
96
|
+
| G7 | 194 | 519 | 466 | 444 | 22 | second conjunct fails | **Warning** |
|
|
97
|
+
| G8 | 194 | 237 | 82 | 75 | 7 | second conjunct fails | **Warning** |
|
|
98
|
+
| G8b | 194 | 47 | 19 | 19 | 0 | pre-declared unconditional clause | **Warning** |
|
|
99
|
+
| G9 | 194 | 273 | 8 | 4 | 4 | second conjunct fails | **Warning** |
|
|
100
|
+
|
|
101
|
+
### G7 — ships as a Warning because the measurement recorded 22 false positives
|
|
102
|
+
|
|
103
|
+
G7's finding count of 466 satisfies the first conjunct, so the rule was not decided by a vacuous measurement. It fails the second conjunct: 22 of the 466 findings are false positives, in two classes named in full in the measurement artifact.
|
|
104
|
+
|
|
105
|
+
**Class 1, two findings — read-only argv shape.** The `prettier-write` register entry matches the argv shape `npm run format` and declares no exclusion for a check flag, so a check-mode invocation written as `npm run format -- --check` is matched as if it wrote. That command does not write and exits non-zero on drift, so its acceptance condition is falsifiable by the exit code alone.
|
|
106
|
+
|
|
107
|
+
**Class 2, twenty findings — the task observes the tree rather than the tool's stdout.** The attributed task text carries a `git status --porcelain` or `git status` span, so the plan distinguishes a clean run from a repairing one by comparing the tree before and after. G7's marker set recognises tool-output observation only, so it does not see this mechanism. Class 2 is the larger of the two and is the reason the rule cannot ship Blocking on this measurement: a plan that observes the tree has stated a real acceptance condition, and rejecting it would block a correct plan.
|
|
108
|
+
|
|
109
|
+
Both classes are addressable by a later feature — Class 1 by adding a check-flag exclusion to the `prettier-write` entry, Class 2 by admitting a tree-observation span as an alternative to a marker — but neither was changed here, because narrowing a predicate after reading its measurement would invalidate the measurement that decided its severity. A later feature that narrows either class must re-take the measurement the same way.
|
|
110
|
+
|
|
111
|
+
Counts, and every one of the 22 false positives named by plan path, task identifier, and offending span: `docs/features/active/2026-08-23-plan-acceptance-gates-miss-unobservable-and-ambient-state-gates-519/evidence/qa-gates/corpus-measurement.2026-08-24T00-00.md`.
|
|
112
|
+
|
|
113
|
+
### G8 — ships as a Warning because the measurement recorded 7 false positives
|
|
114
|
+
|
|
115
|
+
G8's finding count of 82 satisfies the first conjunct. It fails the second: 7 findings are false positives, in two classes.
|
|
116
|
+
|
|
117
|
+
**Class 1, six findings — `--no-index`.** `git diff --no-index` compares two named paths on disk. It does not compare the worktree against the index, so G8's stated claim that the comparison passes vacuously once the change is committed is false for this form.
|
|
118
|
+
|
|
119
|
+
**Class 2, one finding — the unmerged-path filter.** `--diff-filter=U` selects conflicted paths during a merge or rebase. In that state the worktree-against-index comparison is the correct one and does not become vacuous on commit, because the conflict is precisely what is not yet committed.
|
|
120
|
+
|
|
121
|
+
Counts, and every one of the 7 false positives named by plan path, task identifier, and offending span: `docs/features/active/2026-08-23-plan-acceptance-gates-miss-unobservable-and-ambient-state-gates-519/evidence/qa-gates/corpus-measurement.2026-08-24T00-00.md`.
|
|
122
|
+
|
|
123
|
+
### G8b — ships as a Warning unconditionally, by the pre-declared rule
|
|
124
|
+
|
|
125
|
+
G8b is exempt from the two-conjunct rule and **cannot reach the Blocking channel by any measured outcome**. That exemption was declared before the counts were taken, and it holds even though G8b is the only rule of the four whose measured false-positive count is zero.
|
|
126
|
+
|
|
127
|
+
The reason is that G8b carries the highest false-positive surface of the set, and a false-positive count taken over one corpus does not bound the false-positive surface of a predicate. Promoting a rule on the strength of a clean run over one corpus would convert an accident of that corpus into a gate. The 19 findings were nonetheless examined individually and all 19 were retained as true positives, across three sub-classes: a placeholder ref operand, which resolves to a real ref at run time and still cannot make the diff see an untracked path; an artifact-field label inside the span, which is cosmetic and leaves the underlying acceptance condition as the finding describes it; and a pathspec written without the `--` separator, where the predicate reads the pathspec as a ref operand. That last sub-class is a true positive for G8b and is simultaneously recorded in the measurement as a **G8 false negative**: the same span should also have been reported by G8, and was not.
|
|
128
|
+
|
|
129
|
+
Counts, the zero false-positive record, and the three sub-classes with their plan paths and offending spans: `docs/features/active/2026-08-23-plan-acceptance-gates-miss-unobservable-and-ambient-state-gates-519/evidence/qa-gates/corpus-measurement.2026-08-24T00-00.md`.
|
|
130
|
+
|
|
131
|
+
### G9 — ships as a Warning because the measurement recorded 4 false positives
|
|
132
|
+
|
|
133
|
+
G9's finding count of 8 satisfies the first conjunct. It fails the second: 4 of the 8 findings are false positives, and all four share one cause — the offending span is not a command the plan states as an acceptance condition. Two are prose quotations of a flag or flag pair used to describe something the task declares out of scope; one is a test-data literal inside a sentence asserting a helper's return values; and one is a truncated restatement of a command whose full form on the task line does supply a terminal reporter. In every one of the four, the acceptance condition the task actually states is decided by a pass count, an exit code, or an artifact field, not by a coverage number.
|
|
134
|
+
|
|
135
|
+
The remaining 4 findings are true positives, and each is the exact defect the rule was written for: the acceptance condition demands a numeric coverage percentage, the command supplies no terminal reporter, the project `addopts` supplies only an LCOV reporter, and the number the acceptance demands is therefore never printed.
|
|
136
|
+
|
|
137
|
+
Counts, and every one of the 4 false positives named by plan path, task identifier, offending span, and the reason its acceptance condition is falsifiable: `docs/features/active/2026-08-23-plan-acceptance-gates-miss-unobservable-and-ambient-state-gates-519/evidence/qa-gates/corpus-measurement.2026-08-24T00-00.md`.
|
|
138
|
+
|
|
139
|
+
## The Write-Mode Register
|
|
140
|
+
|
|
141
|
+
G7 reads a fixed register of six entries. Each entry is an argv predicate plus a set of observation markers matched case-sensitively as substrings of the owning task's attributed text. The register is data, not code, so the same six entries are transcribed into the TypeScript twin without porting behaviour.
|
|
142
|
+
|
|
143
|
+
### Membership criterion
|
|
144
|
+
|
|
145
|
+
**A tool belongs in the write-mode register when it rewrites tracked source and still exits 0 after rewriting.** That conjunction is the whole criterion. The exit code of such a tool is the same on a clean run and on a repairing run, so a plan that states the invocation as an acceptance condition and records nothing but the exit code has asserted nothing the tool can fail. The register's markers name the literals the tool's success-case output prints, which is the observation that separates the two runs.
|
|
146
|
+
|
|
147
|
+
The six entries are `black-write`, `ruff-fix`, `prettier-write`, `poshqc-format`, `run_poshqc_analyze_autofix`, and `poshqc-suite`. The fifth is named for the MCP tool `run_poshqc_analyze_autofix`, whose argv predicate is an argv word ending with that name.
|
|
148
|
+
|
|
149
|
+
### Executable-position constraint
|
|
150
|
+
|
|
151
|
+
A word satisfies an entry's argv shape only when its index lies within the leading four-word scan window and the word immediately preceding it does not begin with a hyphen. A tool name supplied as the operand of a search flag is therefore never read as an invocation, which mirrors the extractor's existing rule that a grep-family name appearing as an operand is not the executable. Without this constraint a task that searches a policy file for a register member's name would report a finding against its own search command — including a task that searches this file.
|
|
152
|
+
|
|
153
|
+
### Exclusions, with the reason for each
|
|
154
|
+
|
|
155
|
+
Two tools that do write are deliberately **not** register members.
|
|
156
|
+
|
|
157
|
+
- **`git add` is excluded.** A plan stages in order to make a later diff complete, so its acceptance concerns the diff, not the staging. The staging itself carries no acceptance condition to make unfalsifiable, and G8b already reads a `git add` span as an exonerating companion rather than as an offence.
|
|
158
|
+
- **`npm ci` is excluded.** Its only write target is git-ignored, so it rewrites no tracked source and fails the membership criterion's first half. A plan can still state a real observation of it — an installed-package count, or the existence of a resolved binary — but it is not a case G7 needs to report.
|
|
159
|
+
|
|
160
|
+
### Two writers that are not register members and are not exclusions
|
|
161
|
+
|
|
162
|
+
Two further tools write files without being register members, and they are recorded here so a later maintainer does not read their absence as an oversight.
|
|
163
|
+
|
|
164
|
+
- **The Python test runner** writes coverage output under the artifacts tree, for example the LCOV file the project `addopts` value names. It rewrites no tracked source, and its exit code already distinguishes a passing run from a failing one, so it fails the membership criterion on both halves.
|
|
165
|
+
- **The PoshQC test tool** likewise writes result and coverage files under the artifacts tree without rewriting tracked source, and likewise reports pass and fail through its own result. The three PoshQC entries that *are* members — format, analyze-autofix, and the suite — are members because they rewrite tracked PowerShell source in place.
|
|
166
|
+
|
|
167
|
+
A tool that writes only under the artifacts tree is therefore outside the register by construction. The register is about tracked source, not about writing in general.
|
|
168
|
+
|
|
169
|
+
### Known false-negative class — the single-token tool-name span
|
|
170
|
+
|
|
171
|
+
The command extractor drops any command span shorter than two shell words. That two-word minimum-argv floor predates these rules, is pinned by an existing test, and was deliberately left unchanged: relaxing it would newly admit a single-word coverage-argument span into the G1 and G4 scan, which would change existing output, and changing existing output is exactly what this addition forbids itself.
|
|
172
|
+
|
|
173
|
+
The consequence is a false-negative class. **A tool invoked as a bare single-token name is never extracted, so G7 can never report it, however unobservable its acceptance condition is.** This is the form plans commonly use for MCP tools, including the PoshQC formatter, the PoshQC analyzer autofix tool, and the PoshQC composite suite tool — three of the six register entries. Those three entries are reachable only by a span whose first word is the tool name and whose second word is an operand, for example the tool name followed by a path.
|
|
174
|
+
|
|
175
|
+
This is a stated limitation, not a promise deferred, and it is recorded here for the same reason the placeholder guard's false-negative class is recorded above: it is the cost side of a trade, and a later feature must weigh it rather than rediscover it. The limitation is pinned by a test asserting that a single-token tool-name span produces zero findings from every one of the four rules, so the boundary cannot move silently.
|
|
176
|
+
|
|
177
|
+
A later feature may revisit the floor, but only together with a plan for the coverage-rule output change that relaxing it would cause. The two cannot be separated: the floor is one value read by one extractor feeding every rule, so any relaxation reaches G1 and G4 as well as G7.
|
|
178
|
+
|
|
179
|
+
## Deliberately Uncovered Sub-Classes
|
|
180
|
+
|
|
181
|
+
Issue #519 measured five classes of unfalsifiable acceptance condition on a single plan. G7 and G9 cover two decidable slices of the first, G8 and G8b cover the third. The remainder is recorded here so a reviewer knows what these rules do **not** reach and that human attention is still required for it.
|
|
182
|
+
|
|
183
|
+
### The general unobservable-success-output class, beyond what G7 and G9 reach
|
|
184
|
+
|
|
185
|
+
The class is: an acceptance condition that asserts over output the command does not emit on a successful run. G9 covers the slice where a coverage command prints no table at all, and G7 covers the slice where a write-mode tool's exit code is identical on a clean and a repairing run. Neither reaches the general case.
|
|
186
|
+
|
|
187
|
+
Three measured instances outside their reach illustrate why. A gate requiring a formatter's `reformatted` line to be recorded verbatim is unsatisfiable on a clean run, because that line is printed only when a file was rewritten. A gate demanding a zero-diagnostic count from an analyzer that returns only an ok flag and a one-sentence summary names a value that has no source. A gate demanding separate line and branch percentages from a coverage run that prints one combined `Cover` column reads two numbers that are never printed.
|
|
188
|
+
|
|
189
|
+
**Deciding these requires knowing each tool's success-case output, which is not derivable from the plan text, the rules, or the tool documentation.** That is what makes the class invisible to review: four careful review cycles on the measured plan missed it entirely, and it surfaced only when the reviewer ran the tools and observed what they actually printed. A rule would need a per-tool output model, which is a different mechanism from a register of argv predicates. The class is therefore addressed by an authoring requirement in `.claude/skills/atomic-plan-contract/SKILL.md` — that a plan author observe a command's success-case output before asserting over that output — and not by a rule.
|
|
190
|
+
|
|
191
|
+
### The task-ordering class
|
|
192
|
+
|
|
193
|
+
The class is: an acceptance condition that is unsatisfiable because of where its task sits in the plan rather than because of what the command does. A gate that runs a test path containing deliberately-failing cases added by an earlier task, before the later task that makes them pass, cannot exit 0. A baseline captured *after* a write-mode formatter has already repaired pre-existing drift becomes either a blanket waiver or makes the later gate unsatisfiable.
|
|
194
|
+
|
|
195
|
+
**No rule covers this class.** Detecting it requires intra-plan dependency reasoning across phases: the validator would have to model which task changes which file, and which later assertion depends on that change. That is a different analysis from the per-command predicates these rules apply. It is recorded here so a reviewer knows the class still needs human attention.
|
|
196
|
+
|
|
197
|
+
### The executor-choice heuristic — rejected, and closed rather than deferred
|
|
198
|
+
|
|
199
|
+
The proposal was a rule that flags selection vocabulary in acceptance text — "any", "a suitable", "the known", "choose" — on the reasoning that an executor free to pick the evidence it is judged against cannot fail. The underlying concern is real and was measured: two conditions on the examined plan asked the executor to identify "the known-genuine pair" and to choose a survivor list.
|
|
200
|
+
|
|
201
|
+
**The rule is rejected.** The vocabulary it would scan for is ordinary plan prose used in roles that carry no selection semantics, and the research recorded a corpus instance in which the word "any" appears inside a *prohibition* rather than a selection — a case the scan would report and the author would be right to ignore. A keyword scan over prose is not statically decidable for the property the rule would claim to detect. This file's own guidance is to weigh a new rule on its authoring-time false-positive rate rather than on how many committed plans it would have flagged, and on that test the proposal fails.
|
|
202
|
+
|
|
203
|
+
The concern is addressed as authoring guidance in `.claude/skills/atomic-plan-contract/SKILL.md` instead, where a false positive costs an author a moment's judgment rather than a blocked plan.
|
|
204
|
+
|
|
205
|
+
**This is closed, not deferred.** Reviving it requires new evidence about its false-positive rate on plan prose, not a restatement of the original proposal.
|
|
206
|
+
|
|
78
207
|
## Checkable-Literal Definition and the Placeholder Guard
|
|
79
208
|
|
|
80
209
|
G5 and G6 apply only to a *checkable* literal. The specification defines a checkable literal by two conditions: the command carries the fixed-string flag `-F`, or the pattern contains none of the regular-expression metacharacters `. * [ ] ^ $ \ ( ) { } | + ?`. That condition is conservative in POSIX BRE, POSIX ERE, PCRE, and the Rust regex dialect simultaneously, so no dialect-selection logic is required.
|
|
@@ -139,6 +139,20 @@ For command-bearing tasks in approved plans (especially Phase 2 final-QC tasks):
|
|
|
139
139
|
|
|
140
140
|
Any regression test task expected to fail must be tagged with `[expect-fail]` and include an auditable evidence artifact per `evidence-and-timestamp-conventions`.
|
|
141
141
|
|
|
142
|
+
## Planner Adversarial Self-Review (Mandatory)
|
|
143
|
+
|
|
144
|
+
Before any plan handoff, `atomic-planner` MUST complete one explicit adversarial self-review pass over every fact, assumption, and line or file citation the plan relies on. The pass is required on initial authoring and on every revision-delta round. A revision round is not exempt because it changed only part of the plan: the citations the revision touched describe the tree as it stands after the revision, and no earlier pass observed that state.
|
|
145
|
+
|
|
146
|
+
Rules:
|
|
147
|
+
|
|
148
|
+
- **Re-derive every citation in this pass.** Any line, file, test, or assertion that the planner's own edit touched, added, or removed in the current authoring or revision pass MUST be re-derived directly against current repository state in that same pass. The prohibited source is a citation carried forward from an earlier round, including one the planner itself verified in a prior round: that earlier verification observed the tree before the intervening edits, so it is evidence about a superseded state rather than about the state the plan now asserts.
|
|
149
|
+
- **Re-check the sibling region.** The self-review MUST re-check the sibling lines, tests, and assertions that sit in the same file or region as any edited citation. The failure mechanism is sibling invalidation: a fix to one line can invalidate an assumption baked into a sibling line or test that a prior round's citation did not cover, so a pass that verifies only the edited line leaves the invalidated sibling unreported and it surfaces as a defect on a later round.
|
|
150
|
+
|
|
151
|
+
Declaration requirement. Every plan handoff MUST carry exactly one of these two signal lines, written in the directive-line form already used elsewhere in this contract:
|
|
152
|
+
|
|
153
|
+
- `SELF-REVIEW: RE-DERIVED THIS PASS` — the adversarial self-review pass completed in this pass. This signal MUST be followed by an enumeration of the citations re-derived in that pass, one entry per citation, each naming the file and the line, test, or identifier that was re-derived. A signal carrying no enumeration is not a completed declaration.
|
|
154
|
+
- `SELF-REVIEW: BLOCKED` — the pass could not be completed. This signal halts the handoff. It does not permit a self-approved plan: the planner reports the blocking reason and waits for the caller rather than proceeding to hand off an unverified plan.
|
|
155
|
+
|
|
142
156
|
## Preflight Validation (Planner ↔ Executor)
|
|
143
157
|
|
|
144
158
|
When validating or handing off plans for execution:
|
|
@@ -149,6 +163,20 @@ When validating or handing off plans for execution:
|
|
|
149
163
|
- If revisions are required, provide a precise plan delta and repeat validation until all clear.
|
|
150
164
|
- If the required planner ↔ executor handoff cannot be started or completed, stop and report blocked state; do not self-approve the plan.
|
|
151
165
|
|
|
166
|
+
Review depth and reporting rules:
|
|
167
|
+
|
|
168
|
+
- **Review the entire plan in one pass.** Under `DIRECTIVE: PREFLIGHT VALIDATION ONLY`, `atomic-executor` MUST continue checking every remaining phase, task, and prose region after finding an initial defect. Stopping at the first defect is prohibited: the unchecked remainder holds defects that the same pass could have reported, and each one that is left unreported becomes an additional round.
|
|
169
|
+
- **Enumerate every defect found.** `PREFLIGHT: REVISIONS REQUIRED` output MUST list every defect found in that pass, not only the first. The failure mechanism is round inflation: a single-defect report causes the next round to rediscover a defect the same pass could have reported, so the round count rises without the review having covered more of the plan.
|
|
170
|
+
- **Check the delta against its own rule.** Before returning either signal, `atomic-executor` MUST check its proposed fix or delta text against every rule the plan enforces, including that delta's own prose against the same violation class it is remediating. Worked example: the delta prose of a tonality-compliance fix must not itself contain the hyperbole or humor that `.claude/rules/tonality.md` prohibits, because a delta that violates the rule it is written to enforce reintroduces the finding it closes.
|
|
171
|
+
- **Two-round target.** The quality bar is a target of at most two preflight rounds per plan. Exhaustive first-pass review is the mechanism that holds the round count to that target: a pass that reports every defect it can find leaves at most a revision round and a confirming round, whereas a pass that reports one defect at a time cannot reach the target however correct each individual report is.
|
|
172
|
+
|
|
173
|
+
Convergence signal. Every preflight return, whether it carries `PREFLIGHT: ALL CLEAR` or `PREFLIGHT: REVISIONS REQUIRED`, MUST additionally carry exactly one of these two forward-looking lines:
|
|
174
|
+
|
|
175
|
+
- `CONVERGENCE: NO FURTHER ROUNDS EXPECTED` — the reviewer expects the plan to clear without a further round.
|
|
176
|
+
- `CONVERGENCE: FURTHER ROUNDS LIKELY` — the reviewer expects at least one further round, and states why.
|
|
177
|
+
|
|
178
|
+
The convergence line is a required signal rather than free prose. It is a second required line accompanying the preflight signal, not a third value of the signal set that the `Require one of the exact signals:` bullet above enumerates: that bullet's two-value set is unchanged, and every return carries one value from it together with one convergence line.
|
|
179
|
+
|
|
152
180
|
## Validator Gate (Mandatory)
|
|
153
181
|
|
|
154
182
|
Before a plan can be treated as approved:
|
|
@@ -159,6 +187,8 @@ Before a plan can be treated as approved:
|
|
|
159
187
|
|
|
160
188
|
The same validator call also applies the acceptance-gate rules G1 through G6 defined in `.claude/rules/plan-acceptance-gates.md`. Those rules report acceptance conditions that cannot fail — a coverage argument that collects no data, or a search for a literal that returns zero matches whatever the executor does. They run automatically on the existing `plan` route with no additional flag. Blocking findings appear in the validator's error output and fail the gate; Warnings are surfaced without failing it, prefixed with `PLAN GATE WARNING: ` on the CLI and carried on the optional `warnings` field of the MCP result. Read that rule file before authoring acceptance conditions.
|
|
161
189
|
|
|
190
|
+
The same call additionally applies the rules G7, G8, G8b, and G9, which report a write-mode command observed only by its exit code, an unanchored `git diff`, a name-listing diff with no companion span, and a coverage command that prints no table. All four ship in the Warning channel, so they surface without failing the gate. The complete shipped set is therefore G1 through G9.
|
|
191
|
+
|
|
162
192
|
## Wrap-Tolerant Assertion Authoring (Mandatory)
|
|
163
193
|
|
|
164
194
|
An acceptance condition must be able to fail. A condition whose command returns the same result whatever the executor does verifies nothing, however precise it reads. Author every acceptance condition in a wrap-tolerant form: one that survives line wrapping and shell quoting in the file it asserts against.
|
|
@@ -171,6 +201,13 @@ Rules:
|
|
|
171
201
|
- **Dotted coverage-argument form.** Coverage assertions must name an importable dotted module, for example `--cov=scripts.dev_tools.plan_gate_discrimination`. The filesystem-path spellings `--cov=scripts/dev_tools/module.py` and `--cov=scripts/dev_tools/module` collect no data, so a coverage threshold asserted against them cannot fail. Rules G1 through G3 report those spellings.
|
|
172
202
|
- **Use the `=` form, not the space-separated form.** `--cov <value>` can bind the following positional argument. Rule G4 reports it.
|
|
173
203
|
- **Quote what the task will create.** When an asserted literal does not yet exist in the tree, quote the exact literal in the plan prose outside the command span. The gate reads that quotation as the executor's instruction and exonerates the assertion; a paraphrase does not.
|
|
204
|
+
- **Record an observation beyond the exit code for a write-mode command.** A formatter or a fixing linter rewrites tracked source and still exits 0 after rewriting, so its exit code is identical on a clean run and on a repairing one. State, in the task text, the literal its success-case output prints — for example the summary line a formatter prints when it changed nothing — or state a before-and-after tree observation. Rule G7 in `.claude/rules/plan-acceptance-gates.md` reports a write-mode command whose task text carries neither.
|
|
205
|
+
- **Anchor every `git diff` to a ref.** A `git diff` with no ref operand and no `--cached` compares the worktree against the index, which is ambient state: it passes vacuously once the change is committed, so the assertion cannot fail for the executor who commits before running it. Supply an explicit ref operand, usually the base branch. Rule G8 reports the unanchored form.
|
|
206
|
+
- **Pair a name-listing diff with a staging or porcelain-status companion.** An anchored `git diff --name-only` or `--name-status` enumerates tracked changes only, so it can never report a file the task creates, and an assertion that it lists newly created files always sees an empty list. Add a `git add` span or a `git status --porcelain` span in the same task. Rule G8b reports a name-listing diff with neither companion. The two mechanisms are complementary and each alone is wrong in one state: the anchored diff is blind to untracked files, and porcelain status goes empty once the change is committed.
|
|
207
|
+
- **Pass a terminal reporter to every coverage command.** The project `addopts` value supplies an LCOV reporter only, so a coverage command that does not pass `--cov-report=term-missing` prints no coverage table at all, and a numeric percentage the acceptance condition demands can never be read from it. Rule G9 reports a coverage command that supplies no terminal reporter and no `--cov-fail-under` threshold.
|
|
208
|
+
- **Observe a command's success-case output before asserting over that output (mandatory).** Run the command, or read a recorded run of it, and confirm the value you intend to assert is actually printed on a *successful* run. Do not infer it from the tool's documentation, from the plan, or from what the tool prints on failure. This requirement exists because the class of defect it prevents is not detectable by reading: an assertion on a line the tool prints only when it changed something is unsatisfiable on a clean run; a demand for a zero-diagnostic count from a tool that returns only an ok flag names a value with no source; a demand for separate line and branch percentages from a coverage run that prints one combined column reads two numbers that are never printed. G7 and G9 cover two decidable slices of this class. The remainder is covered by this requirement and by nothing else, so a reviewer cannot rely on the gate to catch it.
|
|
209
|
+
- **Fix the evidence in the plan; never leave the executor to select it.** An executor free to choose the evidence it is judged against cannot fail. Do not write an acceptance condition that asks the executor to identify "the known-genuine pair", to choose a survivor list, or to pick any suitable instance. Name the instances, or state the mechanical derivation that produces them, so a third party re-running it obtains the same set. This is authoring guidance and not a rule: a validator rule that scanned acceptance text for selection vocabulary was proposed and rejected, because that vocabulary is ordinary plan prose used in roles carrying no selection semantics and a keyword scan over prose is not statically decidable for the property it would claim to detect. The judgment is the author's, and it is not automated. `.claude/rules/plan-acceptance-gates.md` records the rejection and the reason for it.
|
|
210
|
+
- **Check that the task-ordering does not make the condition unsatisfiable.** No rule covers this. A gate that runs a test path containing deliberately-failing cases added by an earlier task, before the later task that makes them pass, cannot exit 0. A baseline captured after a write-mode formatter has already repaired pre-existing drift becomes either a blanket waiver or makes a later gate unsatisfiable. Read each acceptance condition against the state the plan will actually be in when its task runs.
|
|
174
211
|
|
|
175
212
|
## Plan-Path Continuity Contract (Mandatory)
|
|
176
213
|
|
|
@@ -3,11 +3,23 @@ name: cleanup-merged-worktrees
|
|
|
3
3
|
description: 'Detect, consolidate, and delete git worktrees/branches that are fully merged into main; use after an epic or feature''s PRs have merged and stale drm-copilot-wt-* branches/worktrees remain, driving the detect -> report -> consolidate -> pr-author handoff -> post-merge deletion workflow.'
|
|
4
4
|
allowed-tools:
|
|
5
5
|
- Read
|
|
6
|
+
- Grep
|
|
7
|
+
- Glob
|
|
8
|
+
- Agent
|
|
6
9
|
- "Bash(bash scripts/bash/cleanup-worktrees.sh *)"
|
|
7
10
|
- "Bash(git fetch *)"
|
|
8
11
|
- "Bash(git merge-base *)"
|
|
9
12
|
- "Bash(git push *)"
|
|
10
13
|
- "Bash(git rev-parse *)"
|
|
14
|
+
- "Bash(git status *)"
|
|
15
|
+
- "Bash(git log *)"
|
|
16
|
+
- "Bash(git show *)"
|
|
17
|
+
- "Bash(git diff *)"
|
|
18
|
+
- "Bash(git branch -r*)"
|
|
19
|
+
- "Bash(git worktree list*)"
|
|
20
|
+
- "Bash(gh issue view *)"
|
|
21
|
+
- mcp__drm-copilot__new_potential_bug_entry
|
|
22
|
+
- mcp__drm-copilot__potential_to_issue
|
|
11
23
|
---
|
|
12
24
|
|
|
13
25
|
# Cleanup Merged Worktrees
|
|
@@ -35,8 +47,12 @@ and is out of the script's scope.
|
|
|
35
47
|
which carry unmerged or unique work (`NOT_MERGED`, `HAS_UNIQUE_RESIDUALS`).
|
|
36
48
|
- When stranded documentation/agent-memory commits were appended to a worktree branch
|
|
37
49
|
after its feature content already merged and must be preserved before deletion.
|
|
50
|
+
- When a worktree is reported `BLOCKED-DIRTY`, or its branch is classified `NOT_MERGED`
|
|
51
|
+
or `HAS_UNIQUE_RESIDUALS`, and the uncommitted or unmerged content it holds must be
|
|
52
|
+
triaged into disposable versus must-preserve before the worktree can ever be deleted.
|
|
38
53
|
- Do not use this skill to manage remote branches; its scope is local branches and
|
|
39
|
-
local worktree registrations only
|
|
54
|
+
local worktree registrations only, except for the explicitly confirmed origin-branch
|
|
55
|
+
offer in the Dirty Worktree Triage Procedure's final step.
|
|
40
56
|
|
|
41
57
|
## Report Line Contract
|
|
42
58
|
|
|
@@ -99,7 +115,9 @@ The script emits pipe-delimited, `LC_ALL=C`-ordered records, one per line:
|
|
|
99
115
|
(without force; a dirty worktree is reported via `DIRTY|` lines and skipped), then
|
|
100
116
|
deletes branches with `git branch -D`. The now-merged `documentationandmemories`
|
|
101
117
|
branch and its worktree become `MERGED_CLEAN` instances and are cleaned up by the same
|
|
102
|
-
mechanics.
|
|
118
|
+
mechanics. Any worktree left standing afterward — reported `BLOCKED-DIRTY`, or whose
|
|
119
|
+
branch classified `NOT_MERGED` or `HAS_UNIQUE_RESIDUALS` — is not abandoned; it moves
|
|
120
|
+
to the Dirty Worktree Triage Procedure below.
|
|
103
121
|
|
|
104
122
|
## Nothing to Consolidate (Short Path)
|
|
105
123
|
|
|
@@ -108,6 +126,108 @@ with an empty cherry-pick-candidate list, skip steps 3-5 entirely: proceed direc
|
|
|
108
126
|
the report to `bash scripts/bash/cleanup-worktrees.sh --apply`. Cleanup completes in a
|
|
109
127
|
single session with no PR.
|
|
110
128
|
|
|
129
|
+
## Dirty Worktree Triage Procedure
|
|
130
|
+
|
|
131
|
+
**Trigger.** A worktree reported `ACTION|worktree-remove|<path>|BLOCKED-DIRTY` (with
|
|
132
|
+
accompanying `DIRTY|<path>|<status-porcelain-line>` records), or a branch classified
|
|
133
|
+
`NOT_MERGED` or `HAS_UNIQUE_RESIDUALS`, carries uncommitted or unmerged content the
|
|
134
|
+
script correctly refuses to discard. That refusal is correct and this procedure never
|
|
135
|
+
overrides it — a dirty worktree is never force-removed. This procedure is the systematic
|
|
136
|
+
follow-up: deciding, per worktree, whether that content is disposable or must be
|
|
137
|
+
preserved before the worktree can ever be deleted.
|
|
138
|
+
|
|
139
|
+
Steps 1-7 are read-only investigation. Run them per worktree, or fan out one
|
|
140
|
+
`Agent(general-purpose)` investigation per worktree (or small batch) concurrently per
|
|
141
|
+
step 8, each returning a `SAFE_TO_DELETE` / `PRESERVE` verdict with justification citing
|
|
142
|
+
specific files or commit SHAs, before step 9 acts on any finding.
|
|
143
|
+
|
|
144
|
+
1. **Re-verify current state before analyzing.** Worktrees can be actively in use by
|
|
145
|
+
another concurrent session. Re-run `git status --porcelain` in the worktree and
|
|
146
|
+
re-check the branch's merge status fresh — do not reuse the original scan's
|
|
147
|
+
snapshot. If the worktree's `.git`/index/HEAD mtimes show activity in the last few
|
|
148
|
+
minutes, treat it as possibly live and pause rather than analyze it as abandoned.
|
|
149
|
+
|
|
150
|
+
2. **Check committed-but-unmerged commits, not only the working tree.** Run
|
|
151
|
+
`git log main..<branch> --oneline`. Some worktrees carry real commits that never
|
|
152
|
+
merged, separate from uncommitted working-tree changes. Both need the classification
|
|
153
|
+
in step 5.
|
|
154
|
+
|
|
155
|
+
3. **Check for equivalent content already on `main`, by topic, not only by path.** For
|
|
156
|
+
every dirty, untracked, or unmerged file, check `git show main:<path>` at the same
|
|
157
|
+
path, and also grep broadly across the relevant shared namespace (for example
|
|
158
|
+
`.claude/agent-memory/**` for lesson files, `docs/features/**` for feature docs)
|
|
159
|
+
since the same fact is often re-recorded under a different filename on `main`.
|
|
160
|
+
|
|
161
|
+
4. **For feature-folder doc snapshots** (`issue.md`, `plan.md`, `spec.md`,
|
|
162
|
+
`research/*`), check whether the feature is fully closed on `main` — acceptance
|
|
163
|
+
criteria all checked, code-review/feature-audit/policy-audit artifacts present, an
|
|
164
|
+
evidence trail present. An earlier draft of an already-closed feature is almost
|
|
165
|
+
always fully superseded; diff it against the closed feature's final artifacts to
|
|
166
|
+
confirm rather than assume.
|
|
167
|
+
|
|
168
|
+
5. **Classify any content that is not obviously superseded** into exactly one of:
|
|
169
|
+
- `DEAD_ONE_OFF` — real, but tied to an already-executed, closed plan with no reuse
|
|
170
|
+
elsewhere (check whether the same pattern appears in shared `.claude/skills/**`
|
|
171
|
+
templates or in other feature plans). Low value; safe to discard even though it is
|
|
172
|
+
not technically duplicated.
|
|
173
|
+
- `ALREADY_SOLVED_ELSEWHERE` — the underlying problem it documents is fixed a
|
|
174
|
+
different way on `main` (check `main`'s current code/config/script, not only its
|
|
175
|
+
memory files — a memory file can describe a bug that no longer exists).
|
|
176
|
+
- `STALE_OR_CONTRADICTED` — `main`'s current version of the same lesson has since
|
|
177
|
+
been corrected to state something different or opposite. This is not merely
|
|
178
|
+
redundant; it is actively wrong, and discarding is the right call.
|
|
179
|
+
- `GENUINELY_NEW` / `STILL_RELEVANT` — not found anywhere else, or it corrects
|
|
180
|
+
something `main` currently gets wrong, or it documents unresolved scope on a
|
|
181
|
+
still-open issue (verify open/closed with `gh issue view <n>`; never assume). Must
|
|
182
|
+
be preserved before the worktree is deleted.
|
|
183
|
+
|
|
184
|
+
6. **Handle non-memory dirty content on its own terms.** Some worktrees carry stale
|
|
185
|
+
build artifacts (a modified `.csproj`/`packages.config`/`app.config` from a build run
|
|
186
|
+
in that worktree) rather than documentation. Diff a representative sample against
|
|
187
|
+
`main` (`git diff main -- <path>`) to characterize the change before deciding it is
|
|
188
|
+
disposable.
|
|
189
|
+
|
|
190
|
+
7. **Recognize orphaned non-worktree directories.** A path can still exist on disk
|
|
191
|
+
under a worktree-tracking folder after `git worktree remove` partially ran or
|
|
192
|
+
failed, with no `.git` file inside and no entry in `git worktree list`. These are no
|
|
193
|
+
longer worktrees — flag them for plain filesystem removal, not `git worktree
|
|
194
|
+
remove`, which will misfire or no-op on them. Filesystem removal of an orphaned
|
|
195
|
+
directory is a destructive action outside this skill's pre-approved tool surface; it
|
|
196
|
+
requires explicit user confirmation each time, the same as any other irreversible
|
|
197
|
+
delete.
|
|
198
|
+
|
|
199
|
+
8. **Parallelize the triage.** Steps 1-7 are pure read-only investigation. Fan out one
|
|
200
|
+
`Agent(general-purpose)` investigation per worktree (or a small batch) concurrently,
|
|
201
|
+
each following steps 1-7 and returning a structured `SAFE_TO_DELETE` / `PRESERVE`
|
|
202
|
+
verdict with justification. This scales far better than triaging serially.
|
|
203
|
+
|
|
204
|
+
9. **Route `PRESERVE` findings through the existing consolidation flow** (the
|
|
205
|
+
`documentationandmemories` branch/PR mechanism in steps 3-4 of the End-to-End
|
|
206
|
+
Workflow above) before that worktree's dirty content is discarded. If a finding
|
|
207
|
+
describes unresolved product scope rather than a process lesson, promote it to a
|
|
208
|
+
real follow-up issue instead of folding it into the docs/memory PR: file it with
|
|
209
|
+
`mcp__drm-copilot__new_potential_bug_entry` and promote with
|
|
210
|
+
`mcp__drm-copilot__potential_to_issue` per
|
|
211
|
+
`.claude/skills/feature-promotion-lifecycle/SKILL.md`. For a `SAFE_TO_DELETE`
|
|
212
|
+
verdict, discard the content as a distinct, individually confirmed manual action —
|
|
213
|
+
clear the dirty working tree, or delete a disposable `NOT_MERGED`/
|
|
214
|
+
`HAS_UNIQUE_RESIDUALS` branch directly. This is never automated: the script's
|
|
215
|
+
classification ladder and apply-mode allowlist are never changed to accept these
|
|
216
|
+
states, so a `--apply` run never deletes them on its own, before or after triage. If
|
|
217
|
+
discarding the working-tree content changes the branch's classification (for example
|
|
218
|
+
to content-neutral against `main`), a follow-up report/apply pass then picks it up
|
|
219
|
+
through the normal deterministic path.
|
|
220
|
+
|
|
221
|
+
10. **After local branch deletion, check origin too.** This skill is local-only by
|
|
222
|
+
design (see "When to Use This Skill"), which leaves stale branches on the remote for
|
|
223
|
+
anything already merged. After `--apply` finishes, diff the deleted-local-branch
|
|
224
|
+
list against `git branch -r` (post-prune) to find remote branches whose local
|
|
225
|
+
counterpart is gone, and offer to delete the remainder on origin. Because this
|
|
226
|
+
mutates shared, visible remote state, each deletion requires explicit user
|
|
227
|
+
confirmation — never delete an origin branch as an automatic consequence of local
|
|
228
|
+
cleanup, and never rely on this skill's general `Bash(git push *)` allowance to
|
|
229
|
+
perform it silently.
|
|
230
|
+
|
|
111
231
|
## Prohibited Shortcuts
|
|
112
232
|
|
|
113
233
|
- Never invoke `gh pr create` or `gh pr edit --body*` from this skill or the scripts. PR
|
|
@@ -116,10 +236,19 @@ single session with no PR.
|
|
|
116
236
|
- Never pass a force flag to `git worktree remove`. A dirty worktree blocks deletion and
|
|
117
237
|
is reported for manual handling; it is never force-removed.
|
|
118
238
|
- Never execute `git worktree prune`. Prunable registrations are report-only.
|
|
119
|
-
- Never act on `NOT_MERGED`, `HAS_UNIQUE_RESIDUALS`, or `PROTECTED_CURRENT` candidates
|
|
120
|
-
the
|
|
239
|
+
- Never act on `NOT_MERGED`, `HAS_UNIQUE_RESIDUALS`, or `PROTECTED_CURRENT` candidates
|
|
240
|
+
through the script or its apply-mode allowlist; `--apply` never mutates them, and the
|
|
241
|
+
caller's worktree and branch, and the main worktree, are never mutated under any
|
|
242
|
+
disposition. The Dirty Worktree Triage Procedure's `SAFE_TO_DELETE` verdict authorizes
|
|
243
|
+
only a distinct, individually confirmed manual action outside that automated path for
|
|
244
|
+
`NOT_MERGED`/`HAS_UNIQUE_RESIDUALS` — never a change to the classification ladder
|
|
245
|
+
itself, and never for `PROTECTED_CURRENT`.
|
|
121
246
|
- Never use commit-message text matching as a classification input, and never
|
|
122
247
|
auto-resolve cherry-pick conflicts.
|
|
248
|
+
- Never delete an origin branch, or run plain filesystem removal on an orphaned
|
|
249
|
+
worktree-tracking directory, without explicit per-item user confirmation — both are
|
|
250
|
+
outside this skill's pre-approved tool surface regardless of how the triage verdict
|
|
251
|
+
came out.
|
|
123
252
|
|
|
124
253
|
## Cross-References
|
|
125
254
|
|
|
@@ -130,3 +259,6 @@ single session with no PR.
|
|
|
130
259
|
- `.claude/rules/shell.md` — the bash toolchain (shfmt/shellcheck/bats/kcov), the
|
|
131
260
|
500-line cap, the no-temp-files test policy, and the `CLEANUP_WT_GIT_BIN` seam
|
|
132
261
|
convention.
|
|
262
|
+
- `.claude/skills/feature-promotion-lifecycle/SKILL.md` — the potential-entry-to-issue
|
|
263
|
+
promotion path used by the Dirty Worktree Triage Procedure's step 9 for `PRESERVE`
|
|
264
|
+
findings that describe unresolved product scope.
|
|
@@ -64,7 +64,9 @@ re-derivation is mandatory and is not an optimization to skip when the checkpoin
|
|
|
64
64
|
required parsed `config/blast-radius.json` mapping, which push-down publishes into the
|
|
65
65
|
destination workspace. `conflicts(a, b, config)` in `scripts/dev_tools/compute_blast_radius.py`
|
|
66
66
|
(defined in `scripts/dev_tools/_blast_radius_conflicts.py`) remains the repository authority and
|
|
67
|
-
the parity reference.
|
|
67
|
+
the parity reference. Read the verdict from the conflict key of the returned hashtable.
|
|
68
|
+
The hashtable itself is always truthy, so a bare boolean test on the result treats every pair as
|
|
69
|
+
conflicting. Map each conflicting pair onto an `(int, int)` conflict edge
|
|
68
70
|
of `items[].issue_num` values, normalized so `a < b`. Do not reimplement the relation and do not
|
|
69
71
|
compute edges over the unstarted subset only: an in-flight conflict is precisely what the
|
|
70
72
|
admission decision turns on.
|
|
@@ -387,15 +387,21 @@ checkout, never from inside a child worktree — issues `git worktree remove <wo
|
|
|
387
387
|
success it records `merge_status: worktree_removed` and `worktree_removed_at`, then regenerates
|
|
388
388
|
`docs/features/parallel/<slug>/parallel-status.md`.
|
|
389
389
|
|
|
390
|
-
Mechanical gating of this command for parallel worktrees is
|
|
391
|
-
`.claude/hooks/enforce-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
`
|
|
397
|
-
|
|
398
|
-
|
|
390
|
+
Mechanical gating of this command for parallel worktrees is delivered. Both halves have landed.
|
|
391
|
+
`.claude/hooks/enforce-parallel-worktree-removal-gate.ps1` exists and is registered in
|
|
392
|
+
`.claude/settings.json` on the `Bash` matcher; it authorizes a removal from the
|
|
393
|
+
parallel-orchestrator checkpoint and owns the block reason
|
|
394
|
+
`PARALLEL_WORKTREE_REMOVAL_BLOCKED`. `.claude/hooks/enforce-epic-worktree-removal-gate.ps1` is a
|
|
395
|
+
project-wide `PreToolUse` Bash-matcher hook registered alongside it whose block reason is
|
|
396
|
+
`EPIC_WORKTREE_REMOVAL_BLOCKED`; it now carries a second, parallel allow-branch, so it authorizes a
|
|
397
|
+
removal either from a matching epic checkpoint `features[]` record or from a parallel-orchestrator
|
|
398
|
+
checkpoint whose `route_id` is `parallel` and whose matching `items[].worktree_path` record has
|
|
399
|
+
`merge_status` in `{merged, worktree_removed}`. An unreadable checkpoint, an absent record, or a
|
|
400
|
+
non-authorizing `merge_status` still denies on both branches. The coordination matters because
|
|
401
|
+
`PreToolUse` denials are conjunctive: a new allow-hook alone could not have overridden the epic
|
|
402
|
+
gate's independent deny, which is why the epic gate itself had to gain the parallel branch.
|
|
403
|
+
The parallel-orchestrator-surface feature (F7) shipped no hook file and made no
|
|
404
|
+
`.claude/settings.json` change of its own.
|
|
399
405
|
|
|
400
406
|
## Documentation Maintenance Boundaries
|
|
401
407
|
|
|
@@ -736,6 +742,9 @@ against `items[].worktree_path`. Removal is allowed only when that item's `merge
|
|
|
736
742
|
or `worktree_removed`; anything else — including an unreadable checkpoint or no matching record —
|
|
737
743
|
denies with a reason prefixed `PARALLEL_WORKTREE_REMOVAL_BLOCKED`. Commands that are not
|
|
738
744
|
`git worktree remove` always allow. This is the mechanical counterpart to `## Worktree Cleanup`.
|
|
745
|
+
`.claude/hooks/enforce-epic-worktree-removal-gate.ps1` fires on the same command and now carries a
|
|
746
|
+
matching parallel allow-branch keyed on the same checkpoint, so both gates must allow for a removal
|
|
747
|
+
to proceed.
|
|
739
748
|
|
|
740
749
|
**Invocation-origin extension.** `.claude/hooks/enforce-epic-invocation-origin.ps1` was extended
|
|
741
750
|
additively so `$script:GatedSubagentTypes` lists `epic-planner`, `epic-orchestrator`,
|
|
@@ -214,7 +214,9 @@ as-is and never reimplemented here:
|
|
|
214
214
|
`compute_blast_radius.py`. The signature takes three arguments; the third is the parsed
|
|
215
215
|
`config/blast-radius.json`. Reasons come from the fixed vocabulary
|
|
216
216
|
`{path_overlap, module_overlap, shared_surface_overlap, contract_dependency}`, and the relation
|
|
217
|
-
fails closed.
|
|
217
|
+
fails closed. Read the verdict from the conflict field of the returned ConflictResult.
|
|
218
|
+
The result's boolean projection now agrees with that field, so `if conflicts(a, b, config):`
|
|
219
|
+
yields the verdict rather than the unconditional truth a bare object test gave before issue #576.
|
|
218
220
|
|
|
219
221
|
**The F1a corrections (issue #452, merged PR #453) are load-bearing.** Derivation now reaches
|
|
220
222
|
separator-free repository-root shared surfaces from plan and spec text, admitting such a token only
|
|
@@ -303,6 +305,11 @@ The library returns the partition; the planner supplies the record fields.
|
|
|
303
305
|
item is `prepared` and radius-validated. Derive the conflict edge set by applying
|
|
304
306
|
`Test-BlastRadiusConflict` to every unordered pair of `declared` radii, then pass the pairs as
|
|
305
307
|
`--edges "<a>:<b> ..."` and the item keys as `--keys "<k1> <k2> ..."`.
|
|
308
|
+
Read the verdict from the conflict key of the returned hashtable.
|
|
309
|
+
The hashtable itself is always truthy, so a bare boolean test on the result treats every pair as
|
|
310
|
+
conflicting and serializes the whole run. This is the sibling hazard to the `@(...)` warning
|
|
311
|
+
above for `Test-BlastRadius`: that function writes an `IList`-shaped pipeline result whose
|
|
312
|
+
emptiness is falsy, while this one returns a hashtable whose emptiness is not expressible at all.
|
|
306
313
|
2. Immediately after the conflict-edge set is derived and before anything consumes it, run the
|
|
307
314
|
lane-assertion diagnostic:
|
|
308
315
|
`poetry run python -m scripts.dev_tools.parallel_lane_assertion --manifest docs/features/parallel/<slug>/parallel.md --edges "<a>:<b> ..."`
|
|
@@ -28,3 +28,25 @@ If the artifacts are missing or stale relative to the current branch state, re-g
|
|
|
28
28
|
- Do not infer the refresh base from the repository default branch unless merge-base resolution fails for all candidates.
|
|
29
29
|
- Treat an already-fresh artifact pair as authoritative; do not refresh solely because no explicit `PRBaseBranch` input was provided.
|
|
30
30
|
|
|
31
|
+
### Freshness Cross-Check
|
|
32
|
+
|
|
33
|
+
Both artifacts open with a `Context generated` section carrying the generation timestamp and a
|
|
34
|
+
`Head SHA:` line. Decide freshness from those two values in two steps, and from nothing else.
|
|
35
|
+
|
|
36
|
+
1. **Pair identity.** The generated-context timestamp must be byte-identical in the summary and in
|
|
37
|
+
the appendix. A mismatch proves the two files came from different invocations — a summary
|
|
38
|
+
refreshed while a stale appendix persists, or the reverse — so the pair does not describe one
|
|
39
|
+
run and must be regenerated.
|
|
40
|
+
2. **Head binding.** The head SHA recorded in both files must equal the current head of the branch
|
|
41
|
+
under review. A mismatch proves the pair predates the current head, so it describes a different
|
|
42
|
+
diff than the one being reviewed and must be regenerated.
|
|
43
|
+
|
|
44
|
+
File existence and file modification time are not freshness signals. A file left at the expected
|
|
45
|
+
path by a previous invocation satisfies an existence check, and a stale file that was copied or
|
|
46
|
+
touched satisfies a modification-time check. Both operands of the cross-check above are read from
|
|
47
|
+
the artifacts themselves and from git, so the check is deterministic and does not depend on a wall
|
|
48
|
+
clock.
|
|
49
|
+
|
|
50
|
+
When the head SHA renders the `(unknown)` token, the collected context carried no head SHA. Head
|
|
51
|
+
binding cannot be established in that case, so treat the pair as unverified and regenerate it.
|
|
52
|
+
|
package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md
CHANGED
|
@@ -81,6 +81,10 @@ Timestamp rule:
|
|
|
81
81
|
|
|
82
82
|
A cycle with fewer than five artifacts is malformed. A cycle that uses the same timestamp value for both its `remediation/<ts>/` and `audit/<ts>/` folders is malformed unless entry and exit genuinely ran within the same minute — the two folders remain distinct either way, since one is named `remediation/` and the other `audit/`.
|
|
83
83
|
|
|
84
|
+
### Cycle-Document Sweep Scope
|
|
85
|
+
|
|
86
|
+
A comprehensive or final sweep in a remediation cycle covers that cycle's own plan and audit documents — `remediation-plan.md`, `code-review.md`, `feature-audit.md`, and `policy-audit.md` — in addition to production and test code. The failure mechanism a code-only sweep leaves open is self-referential rule violation: a policy-compliance fix whose own descriptive text violates the policy it enforces is written into one of those four documents rather than into code, so a sweep scoped to code only reports no finding and the violation ships with the cycle.
|
|
87
|
+
|
|
84
88
|
## Plan Shape
|
|
85
89
|
|
|
86
90
|
`remediation/<entry-ts>/remediation-plan.md` MUST conform to `.claude/skills/atomic-plan-contract/SKILL.md`. In particular:
|
|
@@ -102,6 +106,12 @@ After the plan is authored, `atomic-executor` runs preflight under the directive
|
|
|
102
106
|
|
|
103
107
|
The orchestrator records the preflight outcome in `remediation_loop.cycles[current_cycle].preflight` with `iterations` (counter) and `final_status` (`clear|changes_requested|pending`).
|
|
104
108
|
|
|
109
|
+
The exhaustive-pass, defect-enumeration, and delta-self-check rules that govern how `atomic-executor` conducts preflight are defined in the `## Preflight Validation (Planner ↔ Executor)` section of `.claude/skills/atomic-plan-contract/SKILL.md` and are not restated here.
|
|
110
|
+
|
|
111
|
+
Alongside `iterations` and `final_status`, the orchestrator also records in `remediation_loop.cycles[current_cycle].preflight` the convergence line `atomic-executor` returned on that round, which is one of `CONVERGENCE: NO FURTHER ROUNDS EXPECTED` or `CONVERGENCE: FURTHER ROUNDS LIKELY`. This convergence field extends the field set already recorded at `remediation_loop.cycles[current_cycle].preflight` rather than replacing it: `iterations` and `final_status` continue to be recorded exactly as stated above, and the convergence field is written in addition to them.
|
|
112
|
+
|
|
113
|
+
Iteration ceiling. When a cycle's `iterations` would exceed 2, the orchestrator records `final_status: "blocked_preflight_iteration_limit"`, halts the preflight sub-loop, and escalates to the caller, rather than continuing the sub-loop indefinitely. `blocked_preflight_iteration_limit` is a fourth `final_status` value extending the `clear|changes_requested|pending` enumeration stated above. This ceiling bounds the repeat-until-clear behavior stated above it: the sub-loop still repeats until `PREFLIGHT: ALL CLEAR` is returned, and the ceiling supplies the terminating condition for the case where that signal is not reached within two iterations.
|
|
114
|
+
|
|
105
115
|
## Execution and Reaudit
|
|
106
116
|
|
|
107
117
|
When preflight is clear, `atomic-executor` executes the plan task-by-task. The executor invokes workers (`python-typed-engineer`, `typescript-engineer`, `csharp-typed-engineer`, `powershell-typed-engineer`) internally as needed. The orchestrator does not call workers.
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
".claude/hooks/enforce-model-routing-receipt.ps1",
|
|
35
35
|
".claude/hooks/enforce-orchestration-preimplementation-gate.ps1",
|
|
36
36
|
".claude/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1",
|
|
37
|
+
".claude/hooks/enforce-orchestration-preimplementation-gate-modes.ps1",
|
|
37
38
|
".claude/hooks/enforce-parallel-abandon-gate.ps1",
|
|
38
39
|
".claude/hooks/enforce-parallel-cohort-barrier.ps1",
|
|
39
40
|
".claude/hooks/enforce-parallel-cohort-barrier-helpers.ps1",
|