@danmoisan/drm-copilot-mcp 1.1.17 → 1.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/mcp-server.js +500 -106
- package/package.json +4 -3
- package/resources/claude-customizations/.claude/agents/feature-review.md +10 -1
- package/resources/claude-customizations/.claude/agents/orchestrator.md +16 -3
- package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +6 -0
- package/resources/claude-customizations/.claude/lib/bash/parallel-lane-assertion.sh +4 -2
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorState.psm1 +6 -13
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1 +375 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateReceipts.psm1 +22 -15
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRemediationAccounting.psm1 +337 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRoutingContract.psm1 +9 -1
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +119 -3
- package/resources/claude-customizations/.claude/rules/quality-tiers.md +1 -1
- package/resources/claude-customizations/.claude/skills/acceptance-criteria-tracking/SKILL.md +8 -0
- package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +2 -0
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +11 -3
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup-worktrees.sh +11 -2
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_enumerate_lib.sh +169 -5
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_preserve_lib.sh +1 -1
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_report_records_lib.sh +6 -43
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_scan_helper.sh +4 -15
- package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +13 -6
- package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +4 -0
- package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +10 -0
- package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +3 -1
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +30 -12
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +18 -3
- package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +5 -1
- package/resources/claude-customizations/pack-manifests/core.json +2 -0
- package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +8 -0
- package/resources/codex-and-agents-customizations/.agents/skills/epic-orchestrate/SKILL.md +5 -3
- package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +4 -0
- package/resources/codex-and-agents-customizations/.agents/skills/feature-promotion-lifecycle/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/feature-review/SKILL.md +30 -6
- package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +3 -1
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +30 -7
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-state/SKILL.md +41 -2
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +18 -3
- package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +2 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c1.toml +1 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c2.toml +1 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c3-elevated.toml +1 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c3.toml +1 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c4.toml +1 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer.toml +1 -0
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c1.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/customizations/.github/skills/acceptance-criteria-tracking/SKILL.md +8 -0
- package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +4 -0
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +4 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Portable remediation-loop review-outcome and accounting checks (R5-R11).
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Destination-runtime PowerShell port of the issue #484 invariants the
|
|
7
|
+
authoritative Python validator applies to a checkpoint's remediation_loop:
|
|
8
|
+
the review-outcome rules R8a, R8b, R9a-R9d, and R10, and the accounting
|
|
9
|
+
rules R5, R6, R7a, R7b, and R11, matching the error-string templates in
|
|
10
|
+
`scripts/dev_tools/_orchestrator_state_remediation_loop.py` and the rules
|
|
11
|
+
document `.claude/rules/orchestrator-state.md`.
|
|
12
|
+
|
|
13
|
+
The receipts module calls the exported entry point after its unchanged
|
|
14
|
+
per-cycle checks, passing the cycle list or $null when cycles is absent or
|
|
15
|
+
not a list, so the new checks also run for a loop with review outcomes and
|
|
16
|
+
no cycles.
|
|
17
|
+
|
|
18
|
+
Every comparison against the verdict and class vocabulary is case-sensitive
|
|
19
|
+
(-ccontains, -cnotcontains, -ceq). An integer is a JSON number deserialized
|
|
20
|
+
as [int] or [long]; a boolean is never an integer. Values interpolated into
|
|
21
|
+
a message ({v}) render through ConvertTo-PythonDisplayText so the text
|
|
22
|
+
matches Python's str().
|
|
23
|
+
|
|
24
|
+
Every function is pure: it reads no file, starts no process, and never
|
|
25
|
+
mutates its input. Each check returns a string array, empty when valid.
|
|
26
|
+
CONVENTION: this module fails fast at module scope and imports its siblings with -ErrorAction Stop.
|
|
27
|
+
#>
|
|
28
|
+
|
|
29
|
+
Set-StrictMode -Version Latest
|
|
30
|
+
$ErrorActionPreference = 'Stop'
|
|
31
|
+
|
|
32
|
+
# Import the shared checkpoint-value primitives, resolved relative to this
|
|
33
|
+
# module's directory so the import travels with the pushed-down pack.
|
|
34
|
+
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'OrchestratorStateCheckpointValue.psm1') -Force -ErrorAction Stop
|
|
35
|
+
|
|
36
|
+
# Loop and cycle keys read by the accounting rules.
|
|
37
|
+
$script:REVIEW_OUTCOMES_KEY = 'review_outcomes'
|
|
38
|
+
$script:COMPLETED_ATTEMPTS_KEY = 'completed_attempts'
|
|
39
|
+
$script:CANDIDATE_APPLIED_KEY = 'candidate_applied'
|
|
40
|
+
$script:OPENED_BY_REVIEW_KEY = 'opened_by_review'
|
|
41
|
+
$script:COMPLETED_EXECUTION_STATUS = 'complete'
|
|
42
|
+
|
|
43
|
+
# Review verdicts and remediability classes in spec table order, and the two
|
|
44
|
+
# derived class partitions. Pinned to the Python constants of the same names.
|
|
45
|
+
$script:REVIEW_VERDICTS = @('PASS', 'REMEDIATION_REQUIRED', 'HALT_NON_REMEDIABLE', 'AWAITING_CI')
|
|
46
|
+
$script:REMEDIABILITY_CLASSES = @('autonomous', 'external_dependency', 'policy_hold', 'awaiting_ci', 'human_decision_required')
|
|
47
|
+
$script:NON_REMEDIABLE_CLASSES = @('external_dependency', 'policy_hold', 'awaiting_ci', 'human_decision_required')
|
|
48
|
+
$script:HALT_CLASSES = @('external_dependency', 'policy_hold', 'human_decision_required')
|
|
49
|
+
$script:REMEDIABLE_CLASS = 'autonomous'
|
|
50
|
+
$script:REMEDIATION_REQUIRED_VERDICT = 'REMEDIATION_REQUIRED'
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
function Test-RemediationStrictInteger {
|
|
54
|
+
<#
|
|
55
|
+
.SYNOPSIS
|
|
56
|
+
Report whether a deserialized JSON value is an integer and not a boolean.
|
|
57
|
+
.DESCRIPTION
|
|
58
|
+
Private predicate mirroring Python's isinstance(value, int) and not
|
|
59
|
+
isinstance(value, bool). ConvertFrom-Json materializes a JSON integer as
|
|
60
|
+
[int] or [long] and a JSON boolean as [bool].
|
|
61
|
+
.PARAMETER Value
|
|
62
|
+
The deserialized JSON value to classify. May be $null.
|
|
63
|
+
.OUTPUTS
|
|
64
|
+
System.Boolean - $true only for an [int] or [long] value.
|
|
65
|
+
#>
|
|
66
|
+
[CmdletBinding()]
|
|
67
|
+
[OutputType([bool])]
|
|
68
|
+
param(
|
|
69
|
+
[Parameter(Mandatory = $true)]
|
|
70
|
+
[AllowNull()]
|
|
71
|
+
[object] $Value
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
return (($Value -is [int]) -or ($Value -is [long])) -and -not ($Value -is [bool])
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function Get-RemediationReviewVerdict {
|
|
78
|
+
<#
|
|
79
|
+
.SYNOPSIS
|
|
80
|
+
Derive a review verdict from its findings' remediability classes.
|
|
81
|
+
.DESCRIPTION
|
|
82
|
+
Private helper mirroring derive_review_verdict. Evaluates, in order: no
|
|
83
|
+
classes yields PASS; any autonomous class yields REMEDIATION_REQUIRED;
|
|
84
|
+
any member of HALT_CLASSES yields HALT_NON_REMEDIABLE; otherwise
|
|
85
|
+
AWAITING_CI. A member outside REMEDIABILITY_CLASSES (compared
|
|
86
|
+
case-sensitively) raises a terminating error whose message begins
|
|
87
|
+
'invalid remediability: '.
|
|
88
|
+
.PARAMETER Remediability
|
|
89
|
+
The remediability class of every blocking finding; may be empty and may
|
|
90
|
+
repeat a class.
|
|
91
|
+
.OUTPUTS
|
|
92
|
+
System.String - one of the four review verdicts.
|
|
93
|
+
#>
|
|
94
|
+
[CmdletBinding()]
|
|
95
|
+
[OutputType([string])]
|
|
96
|
+
param(
|
|
97
|
+
[Parameter(Mandatory = $true)]
|
|
98
|
+
[AllowEmptyCollection()]
|
|
99
|
+
[string[]] $Remediability
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
foreach ($value in $Remediability) {
|
|
103
|
+
if ($script:REMEDIABILITY_CLASSES -cnotcontains $value) {
|
|
104
|
+
throw "invalid remediability: $value"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if ($Remediability.Count -eq 0) { return 'PASS' }
|
|
109
|
+
if ($Remediability -ccontains $script:REMEDIABLE_CLASS) { return 'REMEDIATION_REQUIRED' }
|
|
110
|
+
foreach ($value in $Remediability) {
|
|
111
|
+
if ($script:HALT_CLASSES -ccontains $value) { return 'HALT_NON_REMEDIABLE' }
|
|
112
|
+
}
|
|
113
|
+
return 'AWAITING_CI'
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function Get-RemediationReviewOutcomeError {
|
|
117
|
+
<#
|
|
118
|
+
.SYNOPSIS
|
|
119
|
+
Return one review outcome's errors (R9a-R9d, then R10).
|
|
120
|
+
.DESCRIPTION
|
|
121
|
+
Private helper mirroring _validate_review_outcome. Checks the verdict
|
|
122
|
+
(string check before membership), the findings list, and each finding's
|
|
123
|
+
remediability (string check before membership); R10 runs only when none
|
|
124
|
+
of those checks produced an error for the outcome.
|
|
125
|
+
.PARAMETER Index
|
|
126
|
+
The outcome's zero-based position, used for error context.
|
|
127
|
+
.PARAMETER Outcome
|
|
128
|
+
The deserialized outcome object.
|
|
129
|
+
.OUTPUTS
|
|
130
|
+
System.String[] - zero or more error strings.
|
|
131
|
+
#>
|
|
132
|
+
[CmdletBinding()]
|
|
133
|
+
[OutputType([string[]])]
|
|
134
|
+
param(
|
|
135
|
+
[Parameter(Mandatory = $true)]
|
|
136
|
+
[int] $Index,
|
|
137
|
+
|
|
138
|
+
[Parameter(Mandatory = $true)]
|
|
139
|
+
[psobject] $Outcome
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
$errors = [System.Collections.Generic.List[string]]::new()
|
|
143
|
+
$prefix = "Checkpoint remediation review outcome #$Index"
|
|
144
|
+
|
|
145
|
+
$verdict = (Get-CheckpointObjectMember -Owner $Outcome -Name 'verdict').Value
|
|
146
|
+
if (-not ($verdict -is [string]) -or ($script:REVIEW_VERDICTS -cnotcontains [string]$verdict)) {
|
|
147
|
+
$rendered = ConvertTo-PythonDisplayText -Value $verdict
|
|
148
|
+
$errors.Add("$prefix verdict must be one of PASS, REMEDIATION_REQUIRED, HALT_NON_REMEDIABLE, AWAITING_CI; got: $rendered")
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
$classes = [System.Collections.Generic.List[string]]::new()
|
|
152
|
+
$findings = (Get-CheckpointObjectMember -Owner $Outcome -Name 'findings').Value
|
|
153
|
+
if (-not (Test-CheckpointListValue -Value $findings)) {
|
|
154
|
+
$errors.Add("$prefix findings must be a list.")
|
|
155
|
+
} else {
|
|
156
|
+
$findingIndex = 0
|
|
157
|
+
foreach ($finding in @($findings)) {
|
|
158
|
+
if (-not (Test-CheckpointObjectValue -Value $finding)) {
|
|
159
|
+
$errors.Add("$prefix finding #$findingIndex must be an object.")
|
|
160
|
+
} else {
|
|
161
|
+
$remediability = (Get-CheckpointObjectMember -Owner $finding -Name 'remediability').Value
|
|
162
|
+
if (-not ($remediability -is [string]) -or ($script:REMEDIABILITY_CLASSES -cnotcontains [string]$remediability)) {
|
|
163
|
+
$rendered = ConvertTo-PythonDisplayText -Value $remediability
|
|
164
|
+
$errors.Add("$prefix finding #$findingIndex remediability must be one of autonomous, external_dependency, policy_hold, awaiting_ci, human_decision_required; got: $rendered")
|
|
165
|
+
} else {
|
|
166
|
+
$classes.Add([string]$remediability)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
$findingIndex++
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
# R10 compares only a well-formed outcome; any shape error above suppresses it.
|
|
174
|
+
if ($errors.Count -eq 0) {
|
|
175
|
+
$expected = Get-RemediationReviewVerdict -Remediability $classes.ToArray()
|
|
176
|
+
if (-not ([string]$verdict -ceq $expected)) {
|
|
177
|
+
$errors.Add("$prefix verdict $verdict does not match its findings (expected $expected).")
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return $errors.ToArray()
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function Get-RemediationReviewOutcomeListError {
|
|
185
|
+
<#
|
|
186
|
+
.SYNOPSIS
|
|
187
|
+
Return the review-outcome errors (R8a, R8b, then each outcome).
|
|
188
|
+
.DESCRIPTION
|
|
189
|
+
Private helper mirroring _validate_review_outcomes. An absent
|
|
190
|
+
review_outcomes key yields no errors; a present non-list value yields R8a
|
|
191
|
+
alone; otherwise each non-object outcome yields R8b and is skipped.
|
|
192
|
+
.PARAMETER RemediationLoop
|
|
193
|
+
The deserialized remediation_loop object.
|
|
194
|
+
.OUTPUTS
|
|
195
|
+
System.String[] - zero or more error strings.
|
|
196
|
+
#>
|
|
197
|
+
[CmdletBinding()]
|
|
198
|
+
[OutputType([string[]])]
|
|
199
|
+
param(
|
|
200
|
+
[Parameter(Mandatory = $true)]
|
|
201
|
+
[psobject] $RemediationLoop
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
$errors = [System.Collections.Generic.List[string]]::new()
|
|
205
|
+
$member = Get-CheckpointObjectMember -Owner $RemediationLoop -Name $script:REVIEW_OUTCOMES_KEY
|
|
206
|
+
if (-not $member.Present) { return $errors.ToArray() }
|
|
207
|
+
if (-not (Test-CheckpointListValue -Value $member.Value)) {
|
|
208
|
+
$errors.Add('Checkpoint remediation_loop review_outcomes must be a list.')
|
|
209
|
+
return $errors.ToArray()
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
$index = 0
|
|
213
|
+
foreach ($outcome in @($member.Value)) {
|
|
214
|
+
if (-not (Test-CheckpointObjectValue -Value $outcome)) {
|
|
215
|
+
$errors.Add("Checkpoint remediation review outcome #$index must be an object.")
|
|
216
|
+
} else {
|
|
217
|
+
$errors.AddRange([string[]]@(Get-RemediationReviewOutcomeError -Index $index -Outcome $outcome))
|
|
218
|
+
}
|
|
219
|
+
$index++
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return $errors.ToArray()
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function Test-RemediationOpenedByReview {
|
|
226
|
+
<#
|
|
227
|
+
.SYNOPSIS
|
|
228
|
+
Report whether an index names a REMEDIATION_REQUIRED outcome object.
|
|
229
|
+
.DESCRIPTION
|
|
230
|
+
Private predicate for R11: the value must be an integer (not a boolean)
|
|
231
|
+
from 0 to one less than the outcome count of a list review_outcomes, and
|
|
232
|
+
the indexed entry must be an object whose verdict is exactly
|
|
233
|
+
REMEDIATION_REQUIRED. A negative index is out of range.
|
|
234
|
+
.PARAMETER OpenedByReview
|
|
235
|
+
The raw opened_by_review value. May be $null.
|
|
236
|
+
.PARAMETER Outcomes
|
|
237
|
+
The raw review_outcomes value. May be $null.
|
|
238
|
+
.OUTPUTS
|
|
239
|
+
System.Boolean - $true only when the reference is valid.
|
|
240
|
+
#>
|
|
241
|
+
[CmdletBinding()]
|
|
242
|
+
[OutputType([bool])]
|
|
243
|
+
param(
|
|
244
|
+
[Parameter(Mandatory = $true)]
|
|
245
|
+
[AllowNull()]
|
|
246
|
+
[object] $OpenedByReview,
|
|
247
|
+
|
|
248
|
+
[Parameter(Mandatory = $true)]
|
|
249
|
+
[AllowNull()]
|
|
250
|
+
[object] $Outcomes
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
if (-not (Test-RemediationStrictInteger -Value $OpenedByReview)) { return $false }
|
|
254
|
+
if (-not (Test-CheckpointListValue -Value $Outcomes)) { return $false }
|
|
255
|
+
$outcomeList = @($Outcomes)
|
|
256
|
+
if ($OpenedByReview -lt 0 -or $OpenedByReview -ge $outcomeList.Count) { return $false }
|
|
257
|
+
$target = $outcomeList[[int]$OpenedByReview]
|
|
258
|
+
if (-not (Test-CheckpointObjectValue -Value $target)) { return $false }
|
|
259
|
+
$verdict = (Get-CheckpointObjectMember -Owner $target -Name 'verdict').Value
|
|
260
|
+
return ($verdict -is [string]) -and ([string]$verdict -ceq $script:REMEDIATION_REQUIRED_VERDICT)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function Get-OrchestratorStateRemediationAccountingError {
|
|
264
|
+
<#
|
|
265
|
+
.SYNOPSIS
|
|
266
|
+
Return the remediation_loop review-outcome and accounting errors (R5-R11).
|
|
267
|
+
.DESCRIPTION
|
|
268
|
+
Public entry mirroring the issue #484 tail of _validate_remediation_loop.
|
|
269
|
+
Returns, in order: the review-outcome errors (R8a; otherwise per outcome
|
|
270
|
+
R8b, R9a, R9b, R9c or R9d per finding, then R10); then, for each object
|
|
271
|
+
cycle in order, R5, R6, and R11; then R7a, otherwise R7b. With none of the
|
|
272
|
+
new keys present it returns nothing.
|
|
273
|
+
.PARAMETER RemediationLoop
|
|
274
|
+
The deserialized remediation_loop object.
|
|
275
|
+
.PARAMETER Cycle
|
|
276
|
+
The loop's cycles list, or $null when cycles is absent or not a list.
|
|
277
|
+
.OUTPUTS
|
|
278
|
+
System.String[] - zero or more error strings.
|
|
279
|
+
#>
|
|
280
|
+
[CmdletBinding()]
|
|
281
|
+
[OutputType([string[]])]
|
|
282
|
+
param(
|
|
283
|
+
[Parameter(Mandatory = $true)]
|
|
284
|
+
[psobject] $RemediationLoop,
|
|
285
|
+
|
|
286
|
+
[Parameter(Mandatory = $true)]
|
|
287
|
+
[AllowNull()]
|
|
288
|
+
[AllowEmptyCollection()]
|
|
289
|
+
[object] $Cycle
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
$errors = [System.Collections.Generic.List[string]]::new()
|
|
293
|
+
$errors.AddRange([string[]]@(Get-RemediationReviewOutcomeListError -RemediationLoop $RemediationLoop))
|
|
294
|
+
|
|
295
|
+
$outcomes = (Get-CheckpointObjectMember -Owner $RemediationLoop -Name $script:REVIEW_OUTCOMES_KEY).Value
|
|
296
|
+
$appliedCount = 0
|
|
297
|
+
$cycleItems = @()
|
|
298
|
+
if ($null -ne $Cycle) { $cycleItems = @($Cycle) }
|
|
299
|
+
$index = 0
|
|
300
|
+
foreach ($item in $cycleItems) {
|
|
301
|
+
if (Test-CheckpointObjectValue -Value $item) {
|
|
302
|
+
$prefix = "Checkpoint remediation cycle #$index"
|
|
303
|
+
$applied = Get-CheckpointObjectMember -Owner $item -Name $script:CANDIDATE_APPLIED_KEY
|
|
304
|
+
if ($applied.Present) {
|
|
305
|
+
if (-not ($applied.Value -is [bool])) {
|
|
306
|
+
$errors.Add("$prefix candidate_applied must be a boolean.")
|
|
307
|
+
} elseif ($applied.Value) {
|
|
308
|
+
$appliedCount++
|
|
309
|
+
$executionStatus = (Get-CheckpointObjectMember -Owner $item -Name 'execution_status').Value
|
|
310
|
+
if (-not ($executionStatus -is [string]) -or -not ([string]$executionStatus -ceq $script:COMPLETED_EXECUTION_STATUS)) {
|
|
311
|
+
$errors.Add("$prefix candidate_applied is true but execution_status is not 'complete'.")
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
$opened = Get-CheckpointObjectMember -Owner $item -Name $script:OPENED_BY_REVIEW_KEY
|
|
316
|
+
if ($opened.Present -and -not (Test-RemediationOpenedByReview -OpenedByReview $opened.Value -Outcomes $outcomes)) {
|
|
317
|
+
$errors.Add("$prefix opened_by_review must reference a review outcome whose verdict is REMEDIATION_REQUIRED.")
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
$index++
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
$attempts = Get-CheckpointObjectMember -Owner $RemediationLoop -Name $script:COMPLETED_ATTEMPTS_KEY
|
|
324
|
+
if ($attempts.Present) {
|
|
325
|
+
if (-not (Test-RemediationStrictInteger -Value $attempts.Value) -or $attempts.Value -lt 0) {
|
|
326
|
+
$errors.Add('Checkpoint remediation_loop completed_attempts must be a non-negative integer.')
|
|
327
|
+
} elseif ($attempts.Value -ne $appliedCount) {
|
|
328
|
+
$errors.Add("Checkpoint remediation_loop completed_attempts is $($attempts.Value) but $appliedCount cycles have candidate_applied true.")
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return $errors.ToArray()
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
# Only the entry point is exported; the verdict helper and the per-outcome and
|
|
336
|
+
# per-reference checks stay private to this module.
|
|
337
|
+
Export-ModuleMember -Function Get-OrchestratorStateRemediationAccountingError
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
.DESCRIPTION
|
|
6
6
|
Destination-runtime PowerShell port of `validate_routing_contract` in
|
|
7
7
|
`scripts/dev_tools/_orchestrator_state_routing.py`, covering parity-inventory
|
|
8
|
-
rows C6.1 through C6.
|
|
8
|
+
rows C6.1 through C6.15. The C6.15 adoption waiver affects only receipt
|
|
9
|
+
presence (C6.9); the declared-list equality checks are unchanged:
|
|
9
10
|
|
|
10
11
|
C6.1 routing matrix carries no routes object (returns)
|
|
11
12
|
C6.2 no route selected (returns)
|
|
@@ -21,6 +22,7 @@
|
|
|
21
22
|
C6.12 lifecycle_operations must be a list when present
|
|
22
23
|
C6.13 per non-object lifecycle operation
|
|
23
24
|
C6.14 per lifecycle operation that did not use the MCP surface
|
|
25
|
+
C6.15 issue_adoption field and waiver rules (OrchestratorStateIssueAdoption.psm1)
|
|
24
26
|
|
|
25
27
|
The routing matrix is read from `OrchestratorStateRoutingMatrix.psm1`, which
|
|
26
28
|
implements deviation PD-1: the constants are pinned and no disk read occurs at
|
|
@@ -56,6 +58,7 @@ $ErrorActionPreference = 'Stop'
|
|
|
56
58
|
# pushed-down pack regardless of the working directory.
|
|
57
59
|
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'OrchestratorStateCheckpointValue.psm1') -Force -ErrorAction Stop
|
|
58
60
|
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'OrchestratorStateRoutingMatrix.psm1') -Force -ErrorAction Stop
|
|
61
|
+
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'OrchestratorStateIssueAdoption.psm1') -Force -ErrorAction Stop
|
|
59
62
|
|
|
60
63
|
# The promotion-entry MCP tools and the promotion type that triggers substitution.
|
|
61
64
|
$script:FEATURE_PROMOTION_ENTRY_TOOL = 'new_potential_entry'
|
|
@@ -410,11 +413,16 @@ function Get-OrchestratorStateRoutingContractError {
|
|
|
410
413
|
}
|
|
411
414
|
|
|
412
415
|
$actualTools = @(Get-CheckpointAcknowledgedName -State $State -ArrayKey 'mcp_call_receipts' -NameKey 'tool' -FlagKey 'ok')
|
|
416
|
+
# C6.15: a valid issue_adoption record waives the receipt requirement for the
|
|
417
|
+
# tools it lists; any adoption error waives nothing.
|
|
418
|
+
$adoption = Get-OrchestratorStateIssueAdoptionResult -State $State -RouteId $routeId -RequiredMcpTool ([string[]]$requiredMcpTools) -SuccessfulTool ([string[]]$actualTools)
|
|
413
419
|
foreach ($tool in $requiredMcpTools) {
|
|
420
|
+
if (@($adoption.WaivedTools) -ccontains $tool) { continue }
|
|
414
421
|
if ($actualTools -cnotcontains $tool) {
|
|
415
422
|
$errors.Add("Checkpoint missing successful MCP receipt: $tool.")
|
|
416
423
|
}
|
|
417
424
|
}
|
|
425
|
+
$errors.AddRange([string[]]@($adoption.Errors))
|
|
418
426
|
|
|
419
427
|
# C6.10 to C6.14: the two empty-list fields and the lifecycle operations.
|
|
420
428
|
foreach ($key in $script:COMPLETION_EMPTY_LIST_KEYS) {
|
|
@@ -24,7 +24,7 @@ description: Checkpoint invariants for the orchestration state artifact and the
|
|
|
24
24
|
|
|
25
25
|
# Orchestrator-State Remediation-Cycle and Human-Interaction Invariants
|
|
26
26
|
|
|
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
|
|
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 the invariants that must hold for each remediation cycle and for the remediation-loop review outcomes and attempt count, plus three invariants for the `human_interaction` block, so that resume and review workflows do not depend on a structurally invalid checkpoint.
|
|
28
28
|
|
|
29
29
|
## Foreign Schema Warning (do not copy verbatim)
|
|
30
30
|
|
|
@@ -34,7 +34,7 @@ This prohibition is specific to the disqualified foreign schema identified by th
|
|
|
34
34
|
|
|
35
35
|
## Scope and Backward Compatibility
|
|
36
36
|
|
|
37
|
-
These invariants apply only when the checkpoint contains a top-level `remediation_loop
|
|
37
|
+
These invariants apply only when the checkpoint contains a top-level `remediation_loop`; the per-cycle invariants apply when it has a `cycles` array, and the review-outcome and attempt-count invariants apply when their keys are present. A checkpoint with no `remediation_loop` (the existing step-based checkpoint shape) is unaffected: it validates exactly as before and produces no new errors. The invariants are additive.
|
|
38
38
|
|
|
39
39
|
## Invariants (per remediation cycle)
|
|
40
40
|
|
|
@@ -44,6 +44,45 @@ These invariants apply only when the checkpoint contains a top-level `remediatio
|
|
|
44
44
|
|
|
45
45
|
3. **Exit gate requires zero blocking findings.** When a cycle's `exit_condition_met == true`, its `blocking_count` must be `0`. A non-zero `blocking_count` with `exit_condition_met == true` is a malformed cycle (the exit gate was marked satisfied while blocking findings remained).
|
|
46
46
|
|
|
47
|
+
4. **`candidate_applied` is a boolean (R5).** When a cycle object carries `candidate_applied`, its value must be a JSON boolean, and any other value is a malformed cycle.
|
|
48
|
+
|
|
49
|
+
5. **A completed attempt requires completed execution (R6).** A cycle whose `candidate_applied == true` must have `execution_status == "complete"`, and `candidate_applied == true` with any other execution status is a malformed cycle.
|
|
50
|
+
|
|
51
|
+
6. **`opened_by_review` references a remediation review (R11).** When a cycle object carries `opened_by_review`, its value must be a non-negative integer (not a boolean) that indexes an object entry of the `remediation_loop.review_outcomes` list whose `verdict` is `REMEDIATION_REQUIRED`, and a value that is not such an integer, is out of range, or references an outcome with any other verdict is a malformed cycle.
|
|
52
|
+
|
|
53
|
+
7. **`completed_attempts` counts completed attempts (R7).** When `remediation_loop.completed_attempts` is present, it must be a non-negative integer (not a boolean) equal to the number of object cycles whose `candidate_applied` is `true` (zero when `cycles` is absent or not a list), and any other value is a malformed loop.
|
|
54
|
+
|
|
55
|
+
## Invariants (remediation_loop.review_outcomes)
|
|
56
|
+
|
|
57
|
+
These invariants apply only when `remediation_loop` contains a `review_outcomes` key. The orchestrator appends one entry per review, including a `PASS` review, in the shape `{"verdict": <verdict>, "findings": [{"remediability": <class>}, ...]}`, where `findings` lists the review's blocking findings only. A halt or wait verdict records no remediation cycle.
|
|
58
|
+
|
|
59
|
+
Review verdicts (exact, case-sensitive):
|
|
60
|
+
|
|
61
|
+
| Verdict | Condition over the classes of the review's blocking findings |
|
|
62
|
+
|---|---|
|
|
63
|
+
| `PASS` | No blocking findings. |
|
|
64
|
+
| `REMEDIATION_REQUIRED` | At least one `autonomous` finding. |
|
|
65
|
+
| `HALT_NON_REMEDIABLE` | No `autonomous` finding, and at least one `external_dependency`, `policy_hold`, or `human_decision_required` finding. |
|
|
66
|
+
| `AWAITING_CI` | At least one finding, and every finding is `awaiting_ci`. |
|
|
67
|
+
|
|
68
|
+
Remediability classes (exact, case-sensitive) and the `blocked_reason` member (#523) that a halt or wait records for each class:
|
|
69
|
+
|
|
70
|
+
| Class | Meaning | `blocked_reason` member |
|
|
71
|
+
|---|---|---|
|
|
72
|
+
| `autonomous` | Repository remediation can resolve the finding without a human decision. | none (remediation loop) |
|
|
73
|
+
| `external_dependency` | A system, service, runtime, or published artifact outside the repository is unavailable or mismatched. | `external_dependency` |
|
|
74
|
+
| `policy_hold` | Proceeding requires a policy decision, exception, or authorization that the orchestrator may not grant. | `policy_hold` |
|
|
75
|
+
| `awaiting_ci` | The finding resolves when a CI result that has not completed becomes available. | `awaiting_ci` |
|
|
76
|
+
| `human_decision_required` | A human must choose between alternatives or approve a direction. | `human_decision_required` |
|
|
77
|
+
|
|
78
|
+
Derivation order. The verdict is a pure function of the findings' classes, evaluated in this order: no findings yields `PASS`; any `autonomous` finding yields `REMEDIATION_REQUIRED`; any `external_dependency`, `policy_hold`, or `human_decision_required` finding yields `HALT_NON_REMEDIABLE`; otherwise the verdict is `AWAITING_CI`.
|
|
79
|
+
|
|
80
|
+
1. **`review_outcomes` is a list of objects (R8).** `review_outcomes` must be a list, and each entry must be an object. A non-list value or a non-object entry is malformed.
|
|
81
|
+
|
|
82
|
+
2. **Verdict and class membership (R9).** Each outcome's `verdict` must be a string among the four verdicts, its `findings` must be a list (a missing key is not a list), and each finding must be an object whose `remediability` is a string among the five classes. Comparisons are case-sensitive, so a case variant such as `Pass` or `Autonomous` is malformed.
|
|
83
|
+
|
|
84
|
+
3. **Verdict matches its findings (R10).** When an outcome passes the membership checks, its `verdict` must equal the verdict that the derivation order yields for its findings' classes. A mismatch is a malformed outcome.
|
|
85
|
+
|
|
47
86
|
## Human-Interaction Scope and Backward Compatibility
|
|
48
87
|
|
|
49
88
|
These invariants apply only when the checkpoint contains a top-level `human_interaction` block. A checkpoint with no `human_interaction` key (the existing checkpoint shape) is unaffected: it validates exactly as before and produces no new errors. The invariants are additive and support the autonomous-execution mandate documented in `.claude/skills/orchestrate/SKILL.md`.
|
|
@@ -56,6 +95,41 @@ These invariants apply only when the checkpoint contains a top-level `human_inte
|
|
|
56
95
|
|
|
57
96
|
3. **Exception requires `runbook_path`.** A requirement whose `response == "exception"` must carry a non-empty `runbook_path` string. A missing, non-string, or empty/whitespace-only `runbook_path` on an `exception` requirement is a malformed requirement.
|
|
58
97
|
|
|
98
|
+
## Blocked-Reason Vocabulary
|
|
99
|
+
|
|
100
|
+
The checkpoint key `blocked_reason` accepts exactly the twelve members below. The authoritative definition is `scripts/dev_tools/_orchestrator_state_blocked_reason.py`; the TypeScript module `extensions/drm-copilot/src/lib/validate/orchestrator-state-blocked-reason.ts` and the grouped arrays in `.claude/lib/orchestrator-state/OrchestratorState.psm1` mirror it.
|
|
101
|
+
|
|
102
|
+
| Member | Partition | Definition |
|
|
103
|
+
|---|---|---|
|
|
104
|
+
| `none` | not blocked | The run is not blocked. JSON `null` is equivalent for classification. |
|
|
105
|
+
| `spawn_agent_unavailable` | mechanical | Existing member; meaning unchanged. |
|
|
106
|
+
| `delegation_launch_failed` | mechanical | Existing member; meaning unchanged. |
|
|
107
|
+
| `delegate_no_receipt` | mechanical | Existing member; meaning unchanged. |
|
|
108
|
+
| `delegate_contract_incomplete` | mechanical | Existing member; meaning unchanged. |
|
|
109
|
+
| `validator_failed` | mechanical | Existing member; meaning unchanged. |
|
|
110
|
+
| `user_requested_stop` | mechanical | Existing member; meaning unchanged. |
|
|
111
|
+
| `premise_falsified` | non-mechanical | Every delegation and validator succeeded, but evidence gathered during execution falsified the premise on which the plan was built, so continuing would implement an invalid plan. |
|
|
112
|
+
| `external_dependency` | non-mechanical | The run cannot proceed because a system, service, runtime, or artifact outside the repository's control is unavailable or mismatched, and no in-repository remediation can resolve it. |
|
|
113
|
+
| `policy_hold` | non-mechanical | The run is stopped because proceeding requires a policy decision, exception, or authorization that the orchestrator is not permitted to grant autonomously. |
|
|
114
|
+
| `awaiting_ci` | non-mechanical | The run is waiting for a CI result that has not yet completed, and no remediation is warranted until that result is available. |
|
|
115
|
+
| `human_decision_required` | non-mechanical | The run requires a human to choose between alternatives or approve a direction before it can continue. |
|
|
116
|
+
|
|
117
|
+
Partition definitions:
|
|
118
|
+
|
|
119
|
+
- **Not blocked:** `none`, JSON `null`, or key absent at a gate. Plain validation still reports an absent key through the required-key check.
|
|
120
|
+
- **Mechanical:** the orchestration process itself did not complete a step: a tool or delegation failed, a delegate returned no or an incomplete receipt, a validator failed, or the operator stopped execution. Membership is defined by the published constant, not by the wording.
|
|
121
|
+
- **Non-mechanical:** every process step could proceed, but the work cannot continue for a reason that no retry or remediation cycle of the process resolves.
|
|
122
|
+
|
|
123
|
+
The classification is recoverable from `blocked_reason` alone by looking the value up in the published partition (`classify_blocked_reason` in Python, `classifyBlockedReason` in TypeScript).
|
|
124
|
+
|
|
125
|
+
Enforcement parity: the Python, PowerShell, and TypeScript validators enforce this vocabulary identically, with case-sensitive comparison. A value outside the vocabulary, including a case variant of any member, is rejected with `Checkpoint has invalid blocked_reason: <value>`. Every non-`none` member, including the five non-mechanical members, blocks completion and PR-creation readiness with the existing messages.
|
|
126
|
+
|
|
127
|
+
Relationship to `human_interaction.requirements[].response == "halt"`: a human-decision halt may appear in both places. `blocked_reason: "human_decision_required"` classifies the run's halt, and a `human_interaction.requirements[]` entry with `response: "halt"` describes the specific requirement. There is no cross-field rule between them: either may appear without the other, and neither is validated against the other.
|
|
128
|
+
|
|
129
|
+
Producer guidance for `premise_falsified`: set it when every delegation and validator succeeded but evidence gathered during execution falsified the plan's premise. The evidence path is still recorded in free-form checkpoint keys; `blocked_reason` carries the classification only.
|
|
130
|
+
|
|
131
|
+
Extension point for #484: #484 consumes `external_dependency`, `policy_hold`, `awaiting_ci`, and `human_decision_required` without renaming, removing, or re-partitioning them; any further halt class is a separate contract change that appends the literal to the non-mechanical partition in all three runtimes, updates the partition oracle and corpus, and updates this section.
|
|
132
|
+
|
|
59
133
|
## Complexity-Assessment Scope and Backward Compatibility
|
|
60
134
|
|
|
61
135
|
These invariants apply only when the checkpoint contains a top-level `complexity_assessments` array. A checkpoint with no `complexity_assessments` key (the existing checkpoint shape) is unaffected: it validates exactly as before and produces no new errors. The invariants are additive and support the two-axis model-selection mechanism documented in `.claude/skills/orchestrate/SKILL.md` (`## Model Selection`). Enforcement is the Python validator, not an imported schema.
|
|
@@ -92,7 +166,11 @@ The session `model_budget.fable_policy` switch is a three-way enum `disabled | a
|
|
|
92
166
|
|
|
93
167
|
## Require-Model-Routing Mode Scope and Backward Compatibility
|
|
94
168
|
|
|
95
|
-
The complexity-assessment and model-routing-receipt invariants above are key-gated: they run only when their key is present, so a checkpoint that omits both arrays passes at every stage. The `require_model_routing` mode adds an existence gate that closes that gap without changing the default behavior. It is an opt-in keyword on `validate_orchestrator_state_text(..., require_model_routing=False)` (CLI flag `--require-model-routing
|
|
169
|
+
The complexity-assessment and model-routing-receipt invariants above are key-gated: they run only when their key is present, so a checkpoint that omits both arrays passes at every stage. The `require_model_routing` mode adds an existence gate that closes that gap without changing the default behavior. It is an opt-in keyword on `validate_orchestrator_state_text(..., require_model_routing=False)` (CLI flag `--require-model-routing`, accepted by the dispatcher `orchestrator-state` subcommand and by the bare-module CLI; MCP parameter `require_model_routing`), defaulting off. Plain, `require_complete`, and `require_pr_creation_ready` calls are unaffected and produce byte-identical results.
|
|
170
|
+
|
|
171
|
+
## Bare-Module CLI Contract
|
|
172
|
+
|
|
173
|
+
The validator module runs directly as `python -m scripts.dev_tools.validate_orchestrator_state <path> [--require-complete] [--require-model-routing] [--require-pr-creation-ready] [--require-codex-model-routing] [--require-codex-topology]`. The dispatcher `python -m scripts.dev_tools.validate_orchestration_artifacts orchestrator-state` stays available and accepts the same flags. Exit code 0 means the checkpoint passed and the success line `orchestrator-state validation passed: <path>` is written to stdout. Exit code 1 means validation errors, written one per stderr line. Exit code 2 means the path is missing, unreadable, or not UTF-8, with one stderr diagnostic and no traceback; argparse usage errors also exit 2.
|
|
96
174
|
|
|
97
175
|
## Invariants (require_model_routing mode)
|
|
98
176
|
|
|
@@ -146,6 +224,43 @@ not a cryptographic or security control.
|
|
|
146
224
|
|
|
147
225
|
Field checks 3 through 9 run in the fixed order `pr_url`, `issue_num`, `branch_name`, `authorized_by`, `authorized_at`, `basis`, `run_slug`, `session_id`; the first failure wins and the gate denies with `STANDALONE_MERGE_AUTHORIZATION_MALFORMED` naming that field.
|
|
148
226
|
|
|
227
|
+
## Issue-Adoption Scope and Backward Compatibility
|
|
228
|
+
|
|
229
|
+
These invariants apply only when a checkpoint contains a top-level `issue_adoption` key. The key records that the orchestration adopted a GitHub issue that already existed before orchestration started (transferred, filed by hand, or created by epic decomposition), so the issue was never created through `potential_to_issue`. The key is presence-gated: a checkpoint without it validates byte-identically to before and produces no new errors. It is evaluated only by the routing-contract completion check (`validate_routing_contract` and its TypeScript and PowerShell ports). It is not a member of `REQUIRED_STATE_KEYS`, it is not read by plain (non-completion) validation, and it is not read by the PR-creation-readiness gate. No JSON Schema file is authored, imported, or read for it.
|
|
230
|
+
|
|
231
|
+
Honest disclosure: the validators perform no network I/O. They cannot confirm that the issue exists, so `evidence` and `verified_via` are an auditable declaration of the read-only verification the orchestrator performed, not proof of it. This matches the `standalone_merge_authorizations` precedent above: the record is a policy-level declaration, not a security control.
|
|
232
|
+
|
|
233
|
+
## Invariants (issue_adoption object)
|
|
234
|
+
|
|
235
|
+
The object has the shape `{ issue_num, issue_url, origin, verified_via, verified_at, evidence, waived_tools, potential_record? }`.
|
|
236
|
+
|
|
237
|
+
| Field | Rule |
|
|
238
|
+
| --- | --- |
|
|
239
|
+
| `issue_num` | A string of decimal digits without a leading zero; equal to the checkpoint `issue-num` when that is a string. |
|
|
240
|
+
| `issue_url` | A string ending with `/issues/` followed by `issue_num`. |
|
|
241
|
+
| `origin` | One of `transferred`, `filed_before_orchestration`, `epic_decomposition`. |
|
|
242
|
+
| `verified_via` | One of `gh_issue_view`, `gh_api_get`, `github_mcp_issue_read`. |
|
|
243
|
+
| `verified_at` | Present, not `null`, and not a blank string. |
|
|
244
|
+
| `evidence` | A string with non-whitespace content. |
|
|
245
|
+
| `waived_tools` | A non-empty list of non-blank tool names that includes `potential_to_issue`. |
|
|
246
|
+
| `potential_record` | Required when waiving a promotion-entry tool: a path under `docs/features/potential/` ending in `.md`. |
|
|
247
|
+
|
|
248
|
+
The rules run in this order, and their errors accumulate:
|
|
249
|
+
|
|
250
|
+
1. A present value that is not an object (including `null`) yields `Checkpoint issue_adoption must be an object when present.` and evaluation stops.
|
|
251
|
+
2. `issue_num` must be a string of decimal digits without a leading zero (a JSON number is invalid). Only when it is valid and the checkpoint `issue-num` is a different string, `issue_num` must equal the checkpoint issue-num.
|
|
252
|
+
3. `issue_url` must end with `/issues/` followed by a valid `issue_num`.
|
|
253
|
+
4. `origin` must be one of the three origin values.
|
|
254
|
+
5. `verified_via` must be one of the three verification values.
|
|
255
|
+
6. `verified_at` must be present: absent, `null`, and blank strings are rejected; any other value passes.
|
|
256
|
+
7. `evidence` must be a non-empty string.
|
|
257
|
+
8. `waived_tools` must be a non-empty list of non-blank strings; otherwise no further rule-8 or rule-9 check runs. Each entry, in list order, receives the first applicable error of: listed more than once; not in the closed waivable set; not required by the selected route after promotion-type resolution; already holding a successful MCP receipt. The list must include `potential_to_issue`.
|
|
258
|
+
9. Only when the rule-8 shape check passed: each distinct waived promotion-entry tool (`new_potential_entry` or `new_potential_bug_entry`) requires a valid `potential_record`.
|
|
259
|
+
|
|
260
|
+
The closed waivable set is `potential_to_issue`, `new_potential_entry`, and `new_potential_bug_entry`. `new_active_feature_folder`, `collect_pr_context`, `validate_orchestration_artifacts`, and every other tool can never be waived. All comparisons are ordinal and case-sensitive.
|
|
261
|
+
|
|
262
|
+
Fail-closed behavior: any adoption error waives nothing, so each missing receipt is still reported as `Checkpoint missing successful MCP receipt: <tool>.`. Adoption errors are placed after the receipt-loop errors and before the `local_execution_overrides` errors. The waiver affects only receipt presence: the declared `required_mcp_tools` equality check with the routing matrix is unchanged, so a checkpoint must still declare every route tool it waives.
|
|
263
|
+
|
|
149
264
|
## Enforcement
|
|
150
265
|
|
|
151
266
|
- `scripts/dev_tools/validate_orchestrator_state.py` appends one error per violated invariant when a `remediation_loop` is present, using the existing validator message style (literal, checkpoint-context prefixed). The validator returns a list of error strings and does not mutate its input.
|
|
@@ -157,3 +272,4 @@ Field checks 3 through 9 run in the fixed order `pr_url`, `issue_num`, `branch_n
|
|
|
157
272
|
- The standalone-merge authorization invariants are enforced by the `PreToolUse` merge gate (`.claude/hooks/enforce-epic-merge-gate.ps1` with its dot-sourced helpers file, and `.codex/hooks/enforce-epic-merge-gate.ps1`) at merge time. The Python checkpoint validator
|
|
158
273
|
does not currently validate standalone_merge_authorizations
|
|
159
274
|
entries; adding that check is recorded as follow-up FU-3.
|
|
275
|
+
- The `issue_adoption` invariants are enforced at completion by the routing-contract check in all three runtimes: `scripts/dev_tools/_orchestrator_state_issue_adoption.py` (Python authority), `extensions/drm-copilot/src/lib/validate/orchestrator-state-issue-adoption.ts`, and `.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1`, with byte-identical error strings pinned by the shared corpus under `tests/fixtures/orchestrator_state_issue_adoption/`.
|
|
@@ -6,7 +6,7 @@ description: Module rigor tier system and uniform coverage thresholds.
|
|
|
6
6
|
|
|
7
7
|
# Module Rigor Tiers
|
|
8
8
|
|
|
9
|
-
This rule defines the T1–T4 module rigor tier system used by all CI gates in this repository. The tier system source of truth
|
|
9
|
+
This rule defines the T1–T4 module rigor tier system used by all CI gates in this repository. The tier definitions and gate matrix in this document are the tier system's source of truth; the file `quality-tiers.yml` at the repository root maps every project to a tier. Adding a project without a tier classification fails CI.
|
|
10
10
|
|
|
11
11
|
## Tiers
|
|
12
12
|
|
package/resources/claude-customizations/.claude/skills/acceptance-criteria-tracking/SKILL.md
CHANGED
|
@@ -70,6 +70,14 @@ During plan execution, after completing a task whose work satisfies an acceptanc
|
|
|
70
70
|
|
|
71
71
|
Timing: Check off AC items as soon as the corresponding plan task passes verification — do not defer all AC updates to the end of plan execution.
|
|
72
72
|
|
|
73
|
+
### CI-Dependent Criteria
|
|
74
|
+
|
|
75
|
+
A CI-dependent criterion is one whose verification requires the result of CI on the PR head. It cannot be verified before the pull request exists, so it stays unchecked and is listed as pending-CI in the AC verification artifact until CI has run.
|
|
76
|
+
|
|
77
|
+
- Owner: the item's own orchestrator run owns the check-off and performs it at S9; this is the one case in which an orchestrator run checks off AC. A parent or coordinating session never commits the check-off from its own root.
|
|
78
|
+
- Timing: after the CI green gate (S9) records `ci_gate.conclusion` as `success`, and before the run reports DONE.
|
|
79
|
+
- Push and re-verify: the check-off is committed in the item's own worktree and pushed to the PR branch, and the CI green gate is re-run against the new head SHA so that `ci_gate.head_sha` equals the final PR head before DONE.
|
|
80
|
+
|
|
73
81
|
### When Reviewers Check Off AC
|
|
74
82
|
|
|
75
83
|
During feature review (feature-audit phase):
|
|
@@ -212,6 +212,8 @@ Rules:
|
|
|
212
212
|
- **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.
|
|
213
213
|
- **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.
|
|
214
214
|
- **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.
|
|
215
|
+
- **Name the runner and job for a Pester acceptance criterion.** CI runs Pester in two jobs of `.github/workflows/_poshqc.yml`: `poshqc / PowerShell QC` runs every suite with coverage on `windows-latest`, and `poshqc / PowerShell hook suites (Linux)` runs only `tests/scripts/claude-hooks` and `tests/scripts/codex-hooks` on `ubuntu-latest`, with coverage disabled. A Pester acceptance criterion may cite `ubuntu-latest` only for suites under `tests/scripts/claude-hooks` or `tests/scripts/codex-hooks`, never for coverage, and must name the job it relies on (`poshqc / PowerShell QC` or `poshqc / PowerShell hook suites (Linux)`).
|
|
216
|
+
- **Do not assert a fixed-string search literal that contains a backslash.** Git for Windows grep 3.0 reads a doubled backslash in a `grep -F` pattern as one backslash, so the same check returns a different count locally than on a CI runner and cannot pass in both places. Assert a backslash-free substring of the literal, a named test, or a hash or byte-identity check instead.
|
|
215
217
|
|
|
216
218
|
## Plan-Path Continuity Contract (Mandatory)
|
|
217
219
|
|
|
@@ -128,9 +128,17 @@ The script emits pipe-delimited, `LC_ALL=C`-ordered records, one per line:
|
|
|
128
128
|
per-file outcome is reported by the companion `ACTION|preserve-stage|...` record.
|
|
129
129
|
- `ORPHAN_DIR|<path>|<size>` — a directory under a worktree-tracking root that carries
|
|
130
130
|
no `.git` pointer file and no `git worktree list` entry. `<size>` is best-effort and
|
|
131
|
-
may be the literal `unknown`. The
|
|
132
|
-
|
|
133
|
-
|
|
131
|
+
may be the literal `unknown`. The scanned roots are the default pair
|
|
132
|
+
`<main>/.claude/worktrees` and `<main>-wt`, or the `CLEANUP_WT_ORPHAN_ROOTS` entries
|
|
133
|
+
when that variable is set (the override replaces the default pair), plus the parent
|
|
134
|
+
directory of every non-main registered worktree, which is always added. A derived
|
|
135
|
+
parent is skipped when it is the main worktree or one of its ancestors, or is equal to
|
|
136
|
+
or inside a registered worktree. `CLEANUP_WT_ORPHAN_ROOTS` entries are separated by a
|
|
137
|
+
semicolon, a newline, or a colon; a colon that follows a single drive letter and
|
|
138
|
+
precedes `/` or `\` is part of the path, and empty or relative entries are dropped.
|
|
139
|
+
The record is advisory: it reports the directory, and nothing in apply mode acts on
|
|
140
|
+
it. For the disposition, see the Dirty Worktree Triage Procedure's step 7, which
|
|
141
|
+
governs how an orphaned directory is handled.
|
|
134
142
|
- `STALE_REF|<refname>` — a `refs/remotes/<name>/*` ref whose `<name>` is not a
|
|
135
143
|
configured remote, named in full ref form. Advisory only; no ref is ever pruned by
|
|
136
144
|
this tool.
|
|
@@ -139,8 +139,17 @@ Environment overrides:
|
|
|
139
139
|
nonexistent value is treated as missing (falls back to
|
|
140
140
|
the bundled cleanup_worktrees_scan_helper.sh). This is
|
|
141
141
|
the filesystem-scan test-stub seam.
|
|
142
|
-
CLEANUP_WT_ORPHAN_ROOTS
|
|
143
|
-
|
|
142
|
+
CLEANUP_WT_ORPHAN_ROOTS Worktree-tracking roots scanned for ORPHAN_DIR and
|
|
143
|
+
WARN|registration-lost. Entries are separated by a
|
|
144
|
+
semicolon, a newline, or a colon; a colon that follows
|
|
145
|
+
a single drive letter and precedes / or \ is part of
|
|
146
|
+
the path (C:/x). Empty and relative entries are
|
|
147
|
+
dropped. When set, it replaces the default pair
|
|
148
|
+
<main>/.claude/worktrees and <main>-wt. The parent of
|
|
149
|
+
every non-main registered worktree is always added,
|
|
150
|
+
except a parent that is the main worktree or its
|
|
151
|
+
ancestor, or is equal to or inside a registered
|
|
152
|
+
worktree.
|
|
144
153
|
CLEANUP_WT_STUB_SCENARIO Scenario directory consumed by the checked-in git and
|
|
145
154
|
scan stubs (tests only).
|
|
146
155
|
CLEANUP_WT_CONSOLIDATION_PATH Override the derived consolidation worktree path
|