@danmoisan/drm-copilot-mcp 1.1.2 → 1.1.4
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 +100 -3
- package/package.json +1 -1
- package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +8 -10
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +130 -33
- package/resources/claude-customizations/.claude/rules/benchmark-baselines.md +7 -0
- package/resources/claude-customizations/.claude/rules/ci-workflows.md +6 -0
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +24 -0
- package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +21 -0
- package/resources/claude-customizations/.claude/rules/plan-acceptance-gates.md +12 -0
- package/resources/claude-customizations/.claude/settings.json +4 -9
- package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +27 -10
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +10 -0
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +22 -0
package/out/mcp-server.js
CHANGED
|
@@ -17261,6 +17261,7 @@ function toMcpToolResult(result) {
|
|
|
17261
17261
|
...result.bundledSourcePath === void 0 ? {} : { bundled_source_path: result.bundledSourcePath },
|
|
17262
17262
|
...result.destinationPath === void 0 ? {} : { destination_path: result.destinationPath },
|
|
17263
17263
|
...result.renderedTree === void 0 ? {} : { rendered_tree: result.renderedTree },
|
|
17264
|
+
...result.targetRepository === void 0 ? {} : { target_repository: result.targetRepository },
|
|
17264
17265
|
...result.warnings === void 0 ? {} : { warnings: result.warnings }
|
|
17265
17266
|
};
|
|
17266
17267
|
}
|
|
@@ -31723,12 +31724,17 @@ function splitCombinedOutput(combined) {
|
|
|
31723
31724
|
var RealGhClient = class {
|
|
31724
31725
|
ghPath;
|
|
31725
31726
|
runner;
|
|
31727
|
+
repo;
|
|
31726
31728
|
/**
|
|
31727
31729
|
* Construct a client, resolving and validating the `gh` executable.
|
|
31728
31730
|
*
|
|
31729
31731
|
* @param options Optional injected `runner` (defaults to
|
|
31730
|
-
* {@link SpawnSyncGhCommandRunner})
|
|
31731
|
-
* {@link defaultGhPathLookup}) so tests never touch the real PATH
|
|
31732
|
+
* {@link SpawnSyncGhCommandRunner}), `ghPathLookup` (defaults to
|
|
31733
|
+
* {@link defaultGhPathLookup}) so tests never touch the real PATH, and
|
|
31734
|
+
* `repo` — an explicit `owner/name` target repository. When `repo` is
|
|
31735
|
+
* supplied, every repository-scoped invocation names it explicitly so the
|
|
31736
|
+
* process working directory cannot influence repository selection; when it
|
|
31737
|
+
* is omitted the argument vectors are unchanged.
|
|
31732
31738
|
* @throws Error With {@link GH_NOT_FOUND_MESSAGE} when `gh` cannot be resolved.
|
|
31733
31739
|
*/
|
|
31734
31740
|
constructor(options) {
|
|
@@ -31739,6 +31745,16 @@ var RealGhClient = class {
|
|
|
31739
31745
|
}
|
|
31740
31746
|
this.ghPath = resolved;
|
|
31741
31747
|
this.runner = options?.runner ?? new SpawnSyncGhCommandRunner();
|
|
31748
|
+
this.repo = options?.repo;
|
|
31749
|
+
}
|
|
31750
|
+
/**
|
|
31751
|
+
* Repository selector fragment spliced after the subcommand words.
|
|
31752
|
+
*
|
|
31753
|
+
* @returns `["--repo", "<owner/name>"]` when bound, otherwise an empty vector
|
|
31754
|
+
* so an unbound client keeps its pre-change argument vectors exactly.
|
|
31755
|
+
*/
|
|
31756
|
+
repoSelector() {
|
|
31757
|
+
return this.repo === void 0 ? [] : ["--repo", this.repo];
|
|
31742
31758
|
}
|
|
31743
31759
|
/**
|
|
31744
31760
|
* Run a `gh` invocation and build a {@link GhResult} from combined output.
|
|
@@ -31771,6 +31787,7 @@ var RealGhClient = class {
|
|
|
31771
31787
|
const args = [
|
|
31772
31788
|
"issue",
|
|
31773
31789
|
"create",
|
|
31790
|
+
...this.repoSelector(),
|
|
31774
31791
|
"--title",
|
|
31775
31792
|
title,
|
|
31776
31793
|
"--body-file",
|
|
@@ -31790,6 +31807,7 @@ var RealGhClient = class {
|
|
|
31790
31807
|
const args = [
|
|
31791
31808
|
"label",
|
|
31792
31809
|
"create",
|
|
31810
|
+
...this.repoSelector(),
|
|
31793
31811
|
label,
|
|
31794
31812
|
"--color",
|
|
31795
31813
|
FEATURE_LABEL_COLOR,
|
|
@@ -31808,6 +31826,7 @@ var RealGhClient = class {
|
|
|
31808
31826
|
const args = [
|
|
31809
31827
|
"issue",
|
|
31810
31828
|
"view",
|
|
31829
|
+
...this.repoSelector(),
|
|
31811
31830
|
issueNumber,
|
|
31812
31831
|
"--json",
|
|
31813
31832
|
"number,title,url,author,updatedAt"
|
|
@@ -32241,6 +32260,78 @@ function promotePotential(options) {
|
|
|
32241
32260
|
return { exitCode: 0, messages, destination: destPath };
|
|
32242
32261
|
}
|
|
32243
32262
|
|
|
32263
|
+
// ../../extensions/drm-copilot/src/lib/potential-to-issue/repo-slug.ts
|
|
32264
|
+
var REPO_SLUG_UNRESOLVED_PREFIX = "Unable to resolve the target repository from workspace root";
|
|
32265
|
+
var REPO_VIEW_ARGS = [
|
|
32266
|
+
"repo",
|
|
32267
|
+
"view",
|
|
32268
|
+
"--json",
|
|
32269
|
+
"nameWithOwner"
|
|
32270
|
+
];
|
|
32271
|
+
var NAME_WITH_OWNER_FIELD = "nameWithOwner";
|
|
32272
|
+
function defaultGhProgramName() {
|
|
32273
|
+
return "gh";
|
|
32274
|
+
}
|
|
32275
|
+
function unresolved(workspaceRoot, reason) {
|
|
32276
|
+
return new Error(
|
|
32277
|
+
`${REPO_SLUG_UNRESOLVED_PREFIX} ${workspaceRoot}: ${reason}`
|
|
32278
|
+
);
|
|
32279
|
+
}
|
|
32280
|
+
function extractSlug(stdout, workspaceRoot) {
|
|
32281
|
+
let payload;
|
|
32282
|
+
try {
|
|
32283
|
+
payload = JSON.parse(stdout);
|
|
32284
|
+
} catch (error2) {
|
|
32285
|
+
throw unresolved(
|
|
32286
|
+
workspaceRoot,
|
|
32287
|
+
`the resolution command produced unparseable output (${String(error2)})`
|
|
32288
|
+
);
|
|
32289
|
+
}
|
|
32290
|
+
if (typeof payload !== "object" || payload === null) {
|
|
32291
|
+
throw unresolved(
|
|
32292
|
+
workspaceRoot,
|
|
32293
|
+
"the resolution output is parseable but is not an object"
|
|
32294
|
+
);
|
|
32295
|
+
}
|
|
32296
|
+
if (!(NAME_WITH_OWNER_FIELD in payload)) {
|
|
32297
|
+
throw unresolved(
|
|
32298
|
+
workspaceRoot,
|
|
32299
|
+
`the resolution output carries no ${NAME_WITH_OWNER_FIELD} field`
|
|
32300
|
+
);
|
|
32301
|
+
}
|
|
32302
|
+
const slug = payload[NAME_WITH_OWNER_FIELD];
|
|
32303
|
+
if (typeof slug !== "string") {
|
|
32304
|
+
throw unresolved(
|
|
32305
|
+
workspaceRoot,
|
|
32306
|
+
`the ${NAME_WITH_OWNER_FIELD} field is not a string`
|
|
32307
|
+
);
|
|
32308
|
+
}
|
|
32309
|
+
return slug;
|
|
32310
|
+
}
|
|
32311
|
+
function resolveRepoSlug(input) {
|
|
32312
|
+
const { runner, workspaceRoot } = input;
|
|
32313
|
+
const ghPath = (input.ghPathLookup ?? defaultGhProgramName)();
|
|
32314
|
+
const result = runner.run([ghPath, ...REPO_VIEW_ARGS], {
|
|
32315
|
+
cwd: workspaceRoot,
|
|
32316
|
+
allowError: true
|
|
32317
|
+
});
|
|
32318
|
+
if (result.code !== 0) {
|
|
32319
|
+
const detail = result.stderr.trim();
|
|
32320
|
+
throw unresolved(
|
|
32321
|
+
workspaceRoot,
|
|
32322
|
+
`the resolution command exited ${String(result.code)}${detail === "" ? "" : `: ${detail}`}`
|
|
32323
|
+
);
|
|
32324
|
+
}
|
|
32325
|
+
const stdout = result.stdout.trim();
|
|
32326
|
+
if (stdout === "") {
|
|
32327
|
+
throw unresolved(
|
|
32328
|
+
workspaceRoot,
|
|
32329
|
+
"the resolution command produced empty output"
|
|
32330
|
+
);
|
|
32331
|
+
}
|
|
32332
|
+
return extractSlug(stdout, workspaceRoot);
|
|
32333
|
+
}
|
|
32334
|
+
|
|
32244
32335
|
// ../../extensions/drm-copilot/src/lib/potential-to-issue/potential-to-issue-service-call.ts
|
|
32245
32336
|
var CommandRunnerGhAdapter = class {
|
|
32246
32337
|
constructor(runner) {
|
|
@@ -32265,7 +32356,12 @@ var CommandRunnerGhAdapter = class {
|
|
|
32265
32356
|
}
|
|
32266
32357
|
};
|
|
32267
32358
|
function potentialToIssueServiceCall(input) {
|
|
32268
|
-
const
|
|
32359
|
+
const resolveSlug = input.repoSlugResolver ?? ((workspaceRoot) => resolveRepoSlug({ runner: input.runner, workspaceRoot }));
|
|
32360
|
+
const targetRepository = resolveSlug(input.workspaceRoot);
|
|
32361
|
+
const ghClient = input.gh ?? new RealGhClient({
|
|
32362
|
+
runner: new CommandRunnerGhAdapter(input.runner),
|
|
32363
|
+
repo: targetRepository
|
|
32364
|
+
});
|
|
32269
32365
|
const fileSystem = input.fileSystem ?? new RealPotentialFileSystem();
|
|
32270
32366
|
const outcome = promotePotential({
|
|
32271
32367
|
potentialPath: input.potentialPath,
|
|
@@ -32292,6 +32388,7 @@ ${outcome.messages.join("\n")}` : "";
|
|
|
32292
32388
|
tool: "potential_to_issue",
|
|
32293
32389
|
workspaceRoot: input.workspaceRoot,
|
|
32294
32390
|
summary,
|
|
32391
|
+
targetRepository,
|
|
32295
32392
|
...outcome.destination === void 0 ? {} : { destinationPath: normalizeGeneratedPath(outcome.destination) },
|
|
32296
32393
|
...issueUrl === null ? {} : { artifacts: [issueUrl] }
|
|
32297
32394
|
};
|
package/package.json
CHANGED
|
@@ -19,10 +19,7 @@ tools:
|
|
|
19
19
|
skills:
|
|
20
20
|
- policy-compliance-order
|
|
21
21
|
- epic-orchestrate
|
|
22
|
-
- feature-promotion-lifecycle
|
|
23
|
-
- atomic-plan-contract
|
|
24
22
|
- acceptance-criteria-tracking
|
|
25
|
-
- evidence-and-timestamp-conventions
|
|
26
23
|
memory: project
|
|
27
24
|
hooks:
|
|
28
25
|
SubagentStop:
|
|
@@ -54,15 +51,13 @@ maintenance. This agent frames the *who* and *when*; the skill documents the *ho
|
|
|
54
51
|
|
|
55
52
|
On every invocation:
|
|
56
53
|
|
|
57
|
-
1. Read `
|
|
58
|
-
2. Read applicable `.claude/rules/` files for languages in scope.
|
|
59
|
-
3. Read `artifacts/orchestration/epic-orchestrator-state.json` to check for existing epic
|
|
54
|
+
1. Read `artifacts/orchestration/epic-orchestrator-state.json` to check for existing epic
|
|
60
55
|
checkpoint state.
|
|
61
|
-
|
|
56
|
+
2. If a valid epic checkpoint exists with a matching `epic_feature_folder`, resume from the
|
|
62
57
|
recorded `next_step` (re-deriving durable ground truth via `git worktree list --porcelain`,
|
|
63
58
|
`git branch`, and `gh pr view --json state,mergedAt,headRefOid` per the `epic-orchestrate`
|
|
64
59
|
skill's resume procedure, not from in-memory notifications alone).
|
|
65
|
-
|
|
60
|
+
3. If no checkpoint exists or the objective is new, begin from manifest parsing
|
|
66
61
|
(`docs/features/epics/<epic-slug>/epic.md`).
|
|
67
62
|
|
|
68
63
|
## Invocation Origin
|
|
@@ -104,7 +99,8 @@ You delegate exclusively through two channels:
|
|
|
104
99
|
|
|
105
100
|
- `Agent(orchestrator)` — one delegation per child feature in the manifest, carrying the epic-mode
|
|
106
101
|
kickoff line and, for dependent features, the upstream-context citation lines (both defined in
|
|
107
|
-
|
|
102
|
+
`.claude/skills/epic-orchestrate/SKILL.md` under `## Merge-on-Green Kickoff Parameter` and
|
|
103
|
+
`## Context Handoff to Dependent Features`).
|
|
108
104
|
Each child `orchestrator` runs its own full small/large route (including its own delegation to
|
|
109
105
|
`atomic-planner`, `atomic-executor`, `feature-review`, and, on CI-green in epic mode, the
|
|
110
106
|
merge-on-green S9 step 6 extension) inside its own isolated worktree
|
|
@@ -133,7 +129,9 @@ at your own `SubagentStop` time.
|
|
|
133
129
|
## Checkpoint Persistence
|
|
134
130
|
|
|
135
131
|
Update `artifacts/orchestration/epic-orchestrator-state.json` after every completed step, per the
|
|
136
|
-
full schema
|
|
132
|
+
full schema enforced by `validate_epic_orchestrator_state_text`, implemented in
|
|
133
|
+
`scripts/dev_tools/validate_epic_orchestrator_state.py`: `objective`, `route_id: "epic"`,
|
|
134
|
+
`epic_feature_folder`,
|
|
137
135
|
`epic_manifest_path`, `epic_status_doc_path`, `integration_branch`, `completed_steps`, `next_step`,
|
|
138
136
|
`last_updated`, `current_wave`, `waves[]`, `features[]` (including `merge_status` and the four
|
|
139
137
|
lifecycle timestamps), `epic_merge_pr`, and the three receipt arrays
|
package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1
CHANGED
|
@@ -14,13 +14,31 @@
|
|
|
14
14
|
Feature folder resolution order:
|
|
15
15
|
1. Scan the prompt text for any path matching
|
|
16
16
|
docs/features/active/<token>, accepting both forward-slash and
|
|
17
|
-
backslash separators.
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
backslash separators. Truncate every match to two segments past the
|
|
18
|
+
docs/features/active/ prefix -- that is, to exactly four path segments:
|
|
19
|
+
docs, features, active, and the feature-folder name. Truncation is
|
|
20
|
+
depth-insensitive, so the feature folder itself, a spec.md path, a
|
|
21
|
+
research/ artifact path, and an evidence/ artifact path all resolve to
|
|
22
|
+
the same folder. A match that truncates to fewer than four segments is
|
|
23
|
+
rejected. Candidates are deduplicated preserving first-occurrence
|
|
24
|
+
order.
|
|
25
|
+
2. Select among the distinct candidates: one candidate is used directly;
|
|
26
|
+
otherwise the candidate equal to the checkpoint's feature-folder field
|
|
27
|
+
is preferred, because the checkpoint is the orchestrator's own record of
|
|
28
|
+
which feature is in flight; otherwise the earliest-occurring candidate
|
|
29
|
+
in the prompt wins, because the orchestrator names the active feature
|
|
30
|
+
folder before citing artifacts inside it.
|
|
31
|
+
3. If no candidate was found in the prompt, read the feature-folder field
|
|
20
32
|
from artifacts/orchestration/orchestrator-state.json.
|
|
21
|
-
|
|
33
|
+
4. If neither yields a folder, block with a reason instructing the caller
|
|
22
34
|
to reference a feature folder explicitly.
|
|
23
35
|
|
|
36
|
+
Known limitation: resolution stops at the feature-folder segment, so it does
|
|
37
|
+
not descend into a version folder (v1/, v2/). No versioned folder exists
|
|
38
|
+
under docs/features/active/ today, and issue.md sits at the feature root in
|
|
39
|
+
every case, so the limitation is inert; it is recorded here rather than coded
|
|
40
|
+
around.
|
|
41
|
+
|
|
24
42
|
Once the folder is resolved, the hook reads the persisted work-mode marker
|
|
25
43
|
(`- Work Mode: minor-audit|full-feature|full-bug|full`) from that folder's
|
|
26
44
|
issue.md, per the mode contract in
|
|
@@ -30,11 +48,16 @@
|
|
|
30
48
|
- full-bug -> spec.md only is required.
|
|
31
49
|
- minor-audit -> neither is required; issue.md carries the acceptance
|
|
32
50
|
criteria for this mode.
|
|
33
|
-
- marker absent, unreadable, or unrecognized ->
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
51
|
+
- marker absent, unreadable, or unrecognized -> deny on a distinct
|
|
52
|
+
decision path that names the resolved folder and the issue.md path it
|
|
53
|
+
probed and states adding or correcting the marker as the remedy. That
|
|
54
|
+
path does not run the required-file probe and names neither prerequisite
|
|
55
|
+
document, because when the mode is unknown no prerequisite set is
|
|
56
|
+
knowable: a set containing user-story.md cannot be satisfied by full-bug
|
|
57
|
+
or minor-audit work without violating the lifecycle contract, and the
|
|
58
|
+
empty set would fail open. The delegation is still denied, so the gate
|
|
59
|
+
remains fail-closed. The legacy `full` marker normalizes to
|
|
60
|
+
full-feature's requirement set.
|
|
38
61
|
|
|
39
62
|
If any required file is missing, the script emits a PreToolUse JSON
|
|
40
63
|
response with hookSpecificOutput.permissionDecision='deny' and a reason
|
|
@@ -137,9 +160,14 @@ function Get-PrdFeatureRequiredFile {
|
|
|
137
160
|
allowed.
|
|
138
161
|
.DESCRIPTION
|
|
139
162
|
full-feature requires spec.md and user-story.md; full-bug requires
|
|
140
|
-
spec.md only; minor-audit requires neither.
|
|
141
|
-
|
|
142
|
-
|
|
163
|
+
spec.md only; minor-audit requires neither.
|
|
164
|
+
|
|
165
|
+
The default arm returns spec.md alone. It is not reached from the
|
|
166
|
+
decision path for an undeterminable mode, which denies on its own branch
|
|
167
|
+
without probing at all; the arm exists so a direct caller passing a $null
|
|
168
|
+
or unrecognized mode never receives a permissive empty set, and it must
|
|
169
|
+
not name user-story.md, because that document is required to be ABSENT
|
|
170
|
+
for full-bug and minor-audit work.
|
|
143
171
|
#>
|
|
144
172
|
[CmdletBinding()]
|
|
145
173
|
[OutputType([string[]])]
|
|
@@ -154,7 +182,7 @@ function Get-PrdFeatureRequiredFile {
|
|
|
154
182
|
'full-feature' { return [string[]]@('spec.md', 'user-story.md') }
|
|
155
183
|
'full-bug' { return [string[]]@('spec.md') }
|
|
156
184
|
'minor-audit' { return [string[]]@() }
|
|
157
|
-
default { return [string[]]@('spec.md'
|
|
185
|
+
default { return [string[]]@('spec.md') }
|
|
158
186
|
}
|
|
159
187
|
}
|
|
160
188
|
|
|
@@ -191,9 +219,22 @@ function Get-PrdFeatureCheckpointFolder {
|
|
|
191
219
|
function Find-PrdFeatureFolderFromPrompt {
|
|
192
220
|
<#
|
|
193
221
|
.SYNOPSIS
|
|
194
|
-
Scans a prompt string for docs/features/active/<...> path tokens
|
|
195
|
-
|
|
196
|
-
$null when no
|
|
222
|
+
Scans a prompt string for docs/features/active/<...> path tokens,
|
|
223
|
+
truncates each to exactly four path segments, and returns the selected
|
|
224
|
+
feature folder. Returns $null when no token truncates to four segments.
|
|
225
|
+
.DESCRIPTION
|
|
226
|
+
Truncation to four segments -- docs, features, active, and the
|
|
227
|
+
feature-folder name -- is two segments past the docs/features/active/
|
|
228
|
+
prefix, so the depth at which an artifact is cited cannot change which
|
|
229
|
+
folder is resolved. Candidates are deduplicated preserving
|
|
230
|
+
first-occurrence order; selection among two or more distinct candidates
|
|
231
|
+
prefers the checkpoint's feature-folder field and otherwise takes the
|
|
232
|
+
earliest occurrence in the prompt.
|
|
233
|
+
|
|
234
|
+
The return value is a repo-relative path normalized to forward slashes,
|
|
235
|
+
or $null. The function reads no file except through the existing
|
|
236
|
+
checkpoint seam, and it is deterministic for a given prompt and
|
|
237
|
+
checkpoint value.
|
|
197
238
|
#>
|
|
198
239
|
[CmdletBinding()]
|
|
199
240
|
[OutputType([string])]
|
|
@@ -214,22 +255,56 @@ function Find-PrdFeatureFolderFromPrompt {
|
|
|
214
255
|
return $null
|
|
215
256
|
}
|
|
216
257
|
|
|
217
|
-
|
|
258
|
+
# Deduplicate preserving FIRST-OCCURRENCE order. A [hashtable] must not be
|
|
259
|
+
# used here: PowerShell hashtable key enumeration order is unspecified, so a
|
|
260
|
+
# first-occurrence selection rule fed by a hashtable is not deterministic.
|
|
261
|
+
[System.Collections.Generic.List[string]] $candidates = [System.Collections.Generic.List[string]]::new()
|
|
218
262
|
foreach ($m in $matchList) {
|
|
219
263
|
$normalized = ($m.Value -replace '\\', '/').TrimEnd('/')
|
|
220
|
-
|
|
264
|
+
|
|
265
|
+
# Truncate to exactly two segments past the docs/features/active/ prefix,
|
|
266
|
+
# that is, to the four segments docs, features, active, and the feature
|
|
267
|
+
# folder name. Truncation is depth-insensitive, so a folder path, a
|
|
268
|
+
# spec.md path, a research/ artifact path, and an evidence/ artifact path
|
|
269
|
+
# all reduce to the same value. A '.' component is a path no-op and is
|
|
270
|
+
# discarded first, so a degenerate token such as docs/features/active/.
|
|
271
|
+
# yields three segments and is rejected rather than resolved.
|
|
272
|
+
$segments = @($normalized -split '/' | Where-Object { $_ -ne '' -and $_ -ne '.' })
|
|
273
|
+
if ($segments.Count -lt 4) {
|
|
274
|
+
continue
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
$truncated = ($segments[0..3] -join '/')
|
|
278
|
+
if (-not $candidates.Contains($truncated)) {
|
|
279
|
+
$candidates.Add($truncated)
|
|
280
|
+
}
|
|
221
281
|
}
|
|
222
282
|
|
|
223
|
-
|
|
224
|
-
|
|
283
|
+
if ($candidates.Count -eq 0) {
|
|
284
|
+
return $null
|
|
285
|
+
}
|
|
225
286
|
|
|
226
|
-
#
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
return $
|
|
287
|
+
# One distinct candidate is used directly, so the common case never consults
|
|
288
|
+
# the checkpoint.
|
|
289
|
+
if ($candidates.Count -eq 1) {
|
|
290
|
+
return $candidates[0]
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
# More than one distinct feature folder was cited. Prefer the folder the
|
|
294
|
+
# orchestrator itself records as in flight: the checkpoint is the
|
|
295
|
+
# authoritative disambiguator, and it reuses a seam this hook already owns.
|
|
296
|
+
$checkpointFolder = Get-PrdFeatureCheckpointFolder
|
|
297
|
+
if ($checkpointFolder) {
|
|
298
|
+
$checkpointNormalized = ($checkpointFolder -replace '\\', '/').TrimEnd('/')
|
|
299
|
+
if ($candidates.Contains($checkpointNormalized)) {
|
|
300
|
+
return $checkpointNormalized
|
|
301
|
+
}
|
|
230
302
|
}
|
|
231
303
|
|
|
232
|
-
|
|
304
|
+
# Tiebreak of last resort: the orchestrator supplies the active feature folder
|
|
305
|
+
# among its delegation inputs and names it before citing artifacts inside it,
|
|
306
|
+
# so a cross-reference to another feature appears later in the prompt.
|
|
307
|
+
return $candidates[0]
|
|
233
308
|
}
|
|
234
309
|
|
|
235
310
|
function Get-PrdFeatureMissingFile {
|
|
@@ -308,10 +383,32 @@ function Invoke-PrdFeatureBeforePlannerDecision {
|
|
|
308
383
|
|
|
309
384
|
# Derive the prerequisite set from the persisted work-mode marker rather
|
|
310
385
|
# than a fixed spec.md/user-story.md pair. A marker that cannot be read or
|
|
311
|
-
# recognized must fail closed
|
|
386
|
+
# recognized must fail closed, not fail open: it denies on its own branch
|
|
387
|
+
# below, naming no prerequisite set and probing for no required file.
|
|
312
388
|
$issueContent = Get-PrdFeatureIssueContent -FeatureFolder $folderNormalized
|
|
313
389
|
$workMode = Resolve-PrdFeatureWorkMode -IssueContent $issueContent
|
|
314
|
-
|
|
390
|
+
|
|
391
|
+
# An indeterminate mode is its own decision path, and it deliberately does NOT
|
|
392
|
+
# run the required-file probe. When the mode is unknown no prerequisite set is
|
|
393
|
+
# knowable, so any set the gate named would be wrong for at least one mode:
|
|
394
|
+
# a set containing user-story.md is unsatisfiable for full-bug and minor-audit
|
|
395
|
+
# without violating the lifecycle contract, and the empty set fails open. The
|
|
396
|
+
# only remedy true in all three modes is repairing the marker, so that is what
|
|
397
|
+
# the reason states. This still DENIES, so the gate remains fail-closed.
|
|
398
|
+
if (-not $workMode) {
|
|
399
|
+
return [ordered]@{
|
|
400
|
+
hookSpecificOutput = [ordered]@{
|
|
401
|
+
hookEventName = 'PreToolUse'
|
|
402
|
+
permissionDecision = 'deny'
|
|
403
|
+
permissionDecisionReason = "PRD_FEATURE_BLOCKED: resolved feature folder '$folderNormalized', " +
|
|
404
|
+
"but its work mode could not be determined from '$folderNormalized/issue.md' " +
|
|
405
|
+
'(the ''- Work Mode:'' marker is absent, unreadable, or unrecognized). ' +
|
|
406
|
+
'Confirm that is the intended feature folder, then add or correct the ' +
|
|
407
|
+
'''- Work Mode:'' marker in that file so the prerequisite set can be derived.'
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
315
412
|
# Force array wrapping: PowerShell unravels a zero-element array return down
|
|
316
413
|
# the pipeline to $null, which would otherwise fail the Mandatory
|
|
317
414
|
# -RequiredFile parameter on Get-PrdFeatureMissingFile for minor-audit mode.
|
|
@@ -322,13 +419,13 @@ function Invoke-PrdFeatureBeforePlannerDecision {
|
|
|
322
419
|
return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
|
|
323
420
|
}
|
|
324
421
|
|
|
422
|
+
# Lead with the resolved folder, not with the remedy: a reader who sees a
|
|
423
|
+
# folder they did not intend diagnoses a path problem immediately instead of
|
|
424
|
+
# re-running a step that has already completed correctly.
|
|
325
425
|
$list = ($missing -join ', ')
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
else {
|
|
330
|
-
$reason = "PRD_FEATURE_BLOCKED: cannot delegate to atomic-planner before prd-feature outputs are present in '$folderNormalized'. Missing: $list. Work mode could not be determined from '$folderNormalized/issue.md' (marker absent, unreadable, or unrecognized); failing closed to the strictest prerequisite set (spec.md, user-story.md). Invoke the prd-feature subagent first."
|
|
331
|
-
}
|
|
426
|
+
$reason = "PRD_FEATURE_BLOCKED: resolved feature folder '$folderNormalized' is missing: " +
|
|
427
|
+
"$list (work mode: $workMode). Confirm that is the intended feature folder, then " +
|
|
428
|
+
'invoke the prd-feature subagent to produce the missing output(s).'
|
|
332
429
|
|
|
333
430
|
return [ordered]@{
|
|
334
431
|
hookSpecificOutput = [ordered]@{
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "scripts/benchmarks/**"
|
|
4
|
+
- "**/baseline*.json"
|
|
5
|
+
description: Runner-environment parity and provenance rules for committed benchmark baselines.
|
|
6
|
+
---
|
|
7
|
+
|
|
1
8
|
# Benchmark Baseline Provenance
|
|
2
9
|
|
|
3
10
|
This rule governs performance baselines used by benchmark regression gates. It exists because a baseline captured on a developer workstation was compared against a `windows-latest` runner, producing deterministic latency regressions that the benchmark gate could not survive (issue #26, PR #30).
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- ".github/workflows/**"
|
|
4
|
+
description: Exit-code rules for GitHub Actions workflow steps that run pwsh.
|
|
5
|
+
---
|
|
6
|
+
|
|
1
7
|
# CI Workflow Authoring
|
|
2
8
|
|
|
3
9
|
This rule governs GitHub Actions workflow steps that run PowerShell (`pwsh`). It exists because a `pwsh` step that intentionally invoked a failing nested command left `$LASTEXITCODE == 1` after its verification logic had already succeeded, leaking a failure to GitHub Actions even though the step's intent was satisfied (issue #26, PR #30).
|
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "artifacts/orchestration/*orchestrator-state.json"
|
|
4
|
+
- "artifacts/orchestration/*planner-state.json"
|
|
5
|
+
- "scripts/dev_tools/*orchestrator_state*"
|
|
6
|
+
- "extensions/drm-copilot/src/lib/validate/orchestrator-state-*"
|
|
7
|
+
- "scripts/dev_tools/compute_complexity_floor.py"
|
|
8
|
+
- "scripts/dev_tools/resolve_delegation_model.py"
|
|
9
|
+
- ".claude/hooks/validate-orchestrator-output.ps1"
|
|
10
|
+
- ".claude/hooks/enforce-model-routing-receipt.ps1"
|
|
11
|
+
- "config/orchestration-routing.json"
|
|
12
|
+
- ".claude/agents/orchestrator.md"
|
|
13
|
+
- ".claude/agents/epic-orchestrator.md"
|
|
14
|
+
- ".claude/agents/parallel-orchestrator.md"
|
|
15
|
+
- ".claude/agents/epic-planner.md"
|
|
16
|
+
- ".claude/agents/parallel-planner.md"
|
|
17
|
+
- ".claude/skills/orchestrate/SKILL.md"
|
|
18
|
+
- ".claude/skills/epic-orchestrate/SKILL.md"
|
|
19
|
+
- ".claude/skills/parallel-orchestrate/SKILL.md"
|
|
20
|
+
- ".claude/skills/epic-plan/SKILL.md"
|
|
21
|
+
- ".claude/skills/parallel-plan/SKILL.md"
|
|
22
|
+
description: Checkpoint invariants for the orchestration state artifact and the surfaces that write or validate it.
|
|
23
|
+
---
|
|
24
|
+
|
|
1
25
|
# Orchestrator-State Remediation-Cycle and Human-Interaction Invariants
|
|
2
26
|
|
|
3
27
|
This rule governs remediation-cycle records and the optional `human_interaction` block in the orchestrator-state checkpoint at `artifacts/orchestration/orchestrator-state.json`. It documents three invariants that must hold for each remediation cycle, plus three invariants for the `human_interaction` block, so that resume and review workflows do not depend on a structurally invalid checkpoint.
|
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "artifacts/orchestration/parallel-*"
|
|
4
|
+
- "docs/features/parallel/**"
|
|
5
|
+
- "scripts/dev_tools/*parallel*"
|
|
6
|
+
- "extensions/drm-copilot/src/lib/validate/parallel-*"
|
|
7
|
+
- "scripts/dev_tools/*blast_radius*"
|
|
8
|
+
- "config/blast-radius.json"
|
|
9
|
+
- "**/config/blast-radius.json"
|
|
10
|
+
- "extensions/drm-copilot/src/lib/push-down/claude-blast-radius-derive-core.ts"
|
|
11
|
+
- ".claude/lib/blast-radius/**"
|
|
12
|
+
- ".claude/lib/bash/parallel-yaml-scan.sh"
|
|
13
|
+
- "scripts/dev_tools/validate_orchestration_artifacts.py"
|
|
14
|
+
- "extensions/drm-copilot/src/lib/validate/orchestration-artifacts.ts"
|
|
15
|
+
- ".claude/hooks/enforce-epic-merge-gate.ps1"
|
|
16
|
+
- ".claude/agents/parallel-orchestrator.md"
|
|
17
|
+
- ".claude/agents/parallel-planner.md"
|
|
18
|
+
- ".claude/skills/parallel-*/SKILL.md"
|
|
19
|
+
description: Artifact invariants and blast-radius contention doctrine for the parallel orchestration surface.
|
|
20
|
+
---
|
|
21
|
+
|
|
1
22
|
# Parallel Orchestration Artifact Invariants
|
|
2
23
|
|
|
3
24
|
This rule governs the three artifacts of the `parallel` orchestration surface: the parallel-run manifest at `docs/features/parallel/<slug>/parallel.md`, the parallel-orchestrator checkpoint at `artifacts/orchestration/parallel-orchestrator-state.json`, and the parallel-planner checkpoint at `artifacts/orchestration/parallel-planner-state.json`. It records the invariants those artifacts must satisfy as numbered prose so that downstream features consume a fixed schema and add behavior only.
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "scripts/dev_tools/plan_gate_*"
|
|
4
|
+
- "scripts/dev_tools/validate_orchestration_artifacts.py"
|
|
5
|
+
- "extensions/drm-copilot/src/lib/validate/plan-gate-*"
|
|
6
|
+
- "extensions/drm-copilot/src/lib/validate/orchestration-artifacts.ts"
|
|
7
|
+
- "docs/features/**/plan.*.md"
|
|
8
|
+
- "docs/features/**/remediation-plan.*.md"
|
|
9
|
+
- ".claude/skills/atomic-plan-contract/SKILL.md"
|
|
10
|
+
description: Acceptance-gate rules G1 through G6 applied to the shell commands an atomic plan states as acceptance conditions.
|
|
11
|
+
---
|
|
12
|
+
|
|
1
13
|
# Atomic-Plan Acceptance Gates (G1 through G6)
|
|
2
14
|
|
|
3
15
|
This rule governs the acceptance-gate rules the plan validator applies to the shell commands an atomic plan states as acceptance conditions. It exists because a plan can state an acceptance condition that cannot fail: a coverage argument that collects no data, or a search for a literal that returns zero matches whatever the executor does. Such a condition reads as a verification step and gates nothing (issue #486).
|
|
@@ -9,9 +9,8 @@
|
|
|
9
9
|
"Bash(bash .claude/lib/bash/compute-concurrency-batches.sh*)",
|
|
10
10
|
"Bash(bash .claude/lib/bash/validate-parallel-manifest.sh*)",
|
|
11
11
|
"Read",
|
|
12
|
-
"Edit(
|
|
13
|
-
"
|
|
14
|
-
"Write(/artifacts/**)",
|
|
12
|
+
"Edit(docs/**)",
|
|
13
|
+
"Edit(artifacts/**)",
|
|
15
14
|
"mcp__drm-copilot__run_poshqc_format",
|
|
16
15
|
"mcp__drm-copilot__run_poshqc_analyze",
|
|
17
16
|
"mcp__drm-copilot__run_poshqc_test",
|
|
@@ -64,17 +63,13 @@
|
|
|
64
63
|
"Skill(execute-hard-lock *)",
|
|
65
64
|
"Skill(identify-session-id *)",
|
|
66
65
|
"Skill(show-my-agent-tree *)",
|
|
67
|
-
"Skill(mermaid-diagram *)"
|
|
68
|
-
"Edit(/.claude/skills/execute-hard-lock/**)",
|
|
69
|
-
"Edit(/.claude/skills/feature-review-workflow/**)",
|
|
70
|
-
"Edit(/.claude/skills/csharp-qa-gate/**)"
|
|
66
|
+
"Skill(mermaid-diagram *)"
|
|
71
67
|
],
|
|
72
68
|
"deny": [
|
|
73
69
|
"Read(./.env)",
|
|
74
70
|
"Read(./.env.*)",
|
|
75
71
|
"Read(./secrets/**)",
|
|
76
|
-
"Edit(./secrets/**)"
|
|
77
|
-
"Write(./secrets/**)"
|
|
72
|
+
"Edit(./secrets/**)"
|
|
78
73
|
],
|
|
79
74
|
"additionalDirectories": [
|
|
80
75
|
"c:\\Users\\DanMoisan\\repos\\drm-copilot\\.claude\\skills\\execute-hard-lock"
|
|
@@ -19,14 +19,6 @@ checkpoint handling, wave computation, integration-branch lifecycle, wave barrie
|
|
|
19
19
|
merge-conflict handling, worktree cleanup, and documentation-maintenance procedures so the
|
|
20
20
|
procedure is not re-derived ad hoc on each epic run.
|
|
21
21
|
|
|
22
|
-
## Prerequisites
|
|
23
|
-
|
|
24
|
-
Before proceeding, `epic-orchestrator` must:
|
|
25
|
-
|
|
26
|
-
1. Read `CLAUDE.md` for repository tone policy and architectural context.
|
|
27
|
-
2. Read applicable `.claude/rules/` files for the languages in scope.
|
|
28
|
-
3. Read the policy files listed in the compliance reading order section of `CLAUDE.md`.
|
|
29
|
-
|
|
30
22
|
## Epic Dependency Manifest
|
|
31
23
|
|
|
32
24
|
The epic manifest is the YAML frontmatter of the single epic home
|
|
@@ -123,7 +115,7 @@ function of the DAG.
|
|
|
123
115
|
When `epic-orchestrator` delegates a child feature to `Agent(orchestrator)`, the prompt includes
|
|
124
116
|
the literal epic-mode kickoff line:
|
|
125
117
|
|
|
126
|
-
> `Epic mode: true. epic_feature_folder: <epic-slug>. integration_branch: epic/<epic-slug>-integration. epic_checkpoint_path: artifacts/orchestration/epic-orchestrator-state.json. PR base branch MUST be <integration_branch>, not main; pass --base <integration_branch> to gh pr create.`
|
|
118
|
+
> `Epic mode: true. epic_feature_folder: <epic-slug>. integration_branch: epic/<epic-slug>-integration. epic_checkpoint_path: artifacts/orchestration/epic-orchestrator-state.json. PR base branch MUST be <integration_branch>, not main; pass --base <integration_branch> to gh pr create. Your final report MUST be exactly the bounded return shape (issue_num, feature_folder, merge_status, pr_number, merge_commit_sha, blocked_reason, branch_name, worktree_path) and nothing else; any additional narrative is discarded because the parent re-derives authoritative state regardless.`
|
|
127
119
|
|
|
128
120
|
The child's own `orchestrator`, on reading this line, records `epic_mode: true` and
|
|
129
121
|
`epic_context: { epic_feature_folder, integration_branch, epic_checkpoint_path }` at its first
|
|
@@ -131,6 +123,31 @@ checkpoint write, and on CI-green (S9 step 6) merges its own PR into the integra
|
|
|
131
123
|
recording `epic_merge: { merge_commit_sha, target_branch, merged_at }`. Standalone (non-epic)
|
|
132
124
|
orchestration is unchanged: `epic_mode` absent or `false` makes S9 step 6 a no-op.
|
|
133
125
|
|
|
126
|
+
## Bounded Child Return Contract
|
|
127
|
+
|
|
128
|
+
A child `orchestrator`'s final report is consumed as a fixed eight-field shape and nothing else:
|
|
129
|
+
|
|
130
|
+
- `issue_num` — the child's GitHub issue number.
|
|
131
|
+
- `feature_folder` — the child's feature-folder path.
|
|
132
|
+
- `merge_status` — the child's terminal merge-status enum value.
|
|
133
|
+
- `pr_number` — the child's pull-request number, or null when none was opened.
|
|
134
|
+
- `merge_commit_sha` — the merge commit, or null when the child did not merge.
|
|
135
|
+
- `blocked_reason` — a short reason string when the child is blocked, otherwise null.
|
|
136
|
+
- `branch_name` — the child's feature branch.
|
|
137
|
+
- `worktree_path` — the child's isolated worktree path.
|
|
138
|
+
|
|
139
|
+
Content beyond these eight fields is **discarded**. A child that returns a longer narrative is not
|
|
140
|
+
in error; the excess is simply not read into the parent's context, which is what keeps the parent's
|
|
141
|
+
footprint flat as the child count grows.
|
|
142
|
+
|
|
143
|
+
Discarding is safe because the parent re-derives authoritative state regardless, from
|
|
144
|
+
`git worktree list --porcelain`, `git branch`, and
|
|
145
|
+
`gh pr view --json state,mergedAt,headRefOid`. `branch_name` and `worktree_path` are carried in the
|
|
146
|
+
shape only to spare the parent a re-parse of porcelain output per child before
|
|
147
|
+
`git worktree remove`; they are not authoritative and are re-derived like every other field. The
|
|
148
|
+
governing argument is the cache doctrine already recorded in
|
|
149
|
+
`.claude/rules/parallel-orchestration.md`, which is cited here rather than restated.
|
|
150
|
+
|
|
134
151
|
## Model Selection
|
|
135
152
|
|
|
136
153
|
When `epic-orchestrator` delegates a child feature to `Agent(orchestrator)`, the prompt appends the
|
|
@@ -265,7 +282,7 @@ checkpoint JSON remains the durable, machine-authoritative source.
|
|
|
265
282
|
`docs/features/epics/<epic-slug>/epic.md`), `epic_status_doc_path`, `integration_branch`,
|
|
266
283
|
`completed_steps`, `next_step`, `last_updated`, `current_wave`, `waves[]`, `features[]`,
|
|
267
284
|
`epic_merge_pr`, and the three receipt arrays (`delegation_receipts[]`, `skill_receipts[]`,
|
|
268
|
-
`mcp_call_receipts[]`)
|
|
285
|
+
`mcp_call_receipts[]`). The
|
|
269
286
|
`merge_status` enum is: `not_started`, `worktree_created`, `pr_open`, `ci_green`,
|
|
270
287
|
`merge_conflict`, `blocked_conflict_loop_limit`, `merged`, `worktree_removed`. The optional
|
|
271
288
|
`intent` object (projection of the `epic.md` intent block) is validated presence-gated.
|
|
@@ -96,6 +96,16 @@ A delegation prompt carrying the literal marker `Preparation mode: true` (issued
|
|
|
96
96
|
- **Terminal checkpoint.** Stop with `completed_steps` containing `S3_promotion` and `S4_atomic_planning`, `next_step: "S5_atomic_execution"`, out-of-scope step statuses set to `not-applicable`, and `blocked_reason: "none"`. Do NOT assert completion (`next_step: "complete"`, `S12_complete`, or a `completed` step8/9/10 status): the run has no PR or CI evidence, and the route's `requires_ci_gate: false` exempts it from `ci_gate` at the completion validator instead.
|
|
97
97
|
- **Commit.** Commit the prepared feature folder and plan to the current branch (the worktree branch created off the epic integration branch) before stopping, and report the `plan-path` and preflight status in the final output.
|
|
98
98
|
|
|
99
|
+
## Epic Mode Bounded Return
|
|
100
|
+
|
|
101
|
+
Under `Epic mode: true`, your final report must be exactly this eight-field shape and nothing else:
|
|
102
|
+
`issue_num`, `feature_folder`, `merge_status`, `pr_number`, `merge_commit_sha`, `blocked_reason`,
|
|
103
|
+
`branch_name`, `worktree_path`.
|
|
104
|
+
|
|
105
|
+
Content beyond those fields is discarded unread, so spend no effort on it. The parent
|
|
106
|
+
`epic-orchestrator` re-derives authoritative state from git and `gh` regardless, per
|
|
107
|
+
`## Bounded Child Return Contract` in `.claude/skills/epic-orchestrate/SKILL.md`.
|
|
108
|
+
|
|
99
109
|
## Model Selection
|
|
100
110
|
|
|
101
111
|
Model selection is a second axis, strictly separate from `route`. `route` (`small | large | remediation | preparation | epic`) is deterministic — file-count driven for `small`/`large`, marker-driven for `preparation` (the `Preparation mode: true` kickoff line) and `epic`; it governs `required_agents`, `required_skills`, and `required_mcp_tools` only. `route` is NOT an input to model selection anywhere. The sole feature-level input to the delegation model tier is a judgment-based `complexity_band` (`C1 | C2 | C3 | C4`). The authoritative values live in the `model_policy` block of `config/orchestration-routing.json`.
|
|
@@ -210,6 +210,28 @@
|
|
|
210
210
|
'.claude/hooks/enforce-prd-feature-before-planner.ps1'
|
|
211
211
|
'.claude/hooks/enforce-parallel-cohort-barrier-helpers.ps1'
|
|
212
212
|
'.claude/hooks/enforce-pr-author-skill-helpers.ps1'
|
|
213
|
+
# Issue #526 added this out-of-band release-verification module (Layer B of the
|
|
214
|
+
# missed-npm-publish defence). CodeCoverage.Path is an explicit per-file
|
|
215
|
+
# allow-list, so the new production file is registered here; without it the file
|
|
216
|
+
# would sit outside the coverage denominator, which the Coverage Exclusion Policy
|
|
217
|
+
# forbids. Its Pester suite dot-sources the file (guarded entry-point body), so
|
|
218
|
+
# line attribution is valid.
|
|
219
|
+
'scripts/dev-tools/Invoke-ReleaseVerification.ps1'
|
|
220
|
+
# Issue #526 split the pure helpers of the verification module into this sibling
|
|
221
|
+
# file, because the parent stood one line under the 500-line cap. CodeCoverage.Path
|
|
222
|
+
# is an explicit per-file allow-list, so the new production file must be registered
|
|
223
|
+
# here to stay inside the coverage denominator; without it the relocated lines would
|
|
224
|
+
# leave the denominator entirely, which the Coverage Exclusion Policy forbids. Its
|
|
225
|
+
# Pester suite dot-sources the file, and the file declares no entry-point block, so
|
|
226
|
+
# line attribution is valid.
|
|
227
|
+
'scripts/dev-tools/Invoke-ReleaseVerificationHelpers.ps1'
|
|
228
|
+
# Issue #526 added this release-reconciliation module (Layer C of the
|
|
229
|
+
# missed-npm-publish defence). CodeCoverage.Path is an explicit per-file
|
|
230
|
+
# allow-list, so the new production file is registered here; without it the file
|
|
231
|
+
# would sit outside the coverage denominator, which the Coverage Exclusion Policy
|
|
232
|
+
# forbids. Its Pester suite dot-sources the file (guarded entry-point body), so
|
|
233
|
+
# line attribution is valid.
|
|
234
|
+
'scripts/dev-tools/Invoke-ReleaseReconciliation.ps1'
|
|
213
235
|
)
|
|
214
236
|
# Optional: don't fail the run on coverage percentage
|
|
215
237
|
CoveragePercentTarget = 0
|