@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danmoisan/drm-copilot-mcp",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.18",
|
|
4
4
|
"description": "Stdio MCP server exposing drm-copilot repo-automation tools.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -38,11 +38,12 @@
|
|
|
38
38
|
"prepack": "node prepack.cjs"
|
|
39
39
|
},
|
|
40
40
|
"overrides": {
|
|
41
|
-
"fast-uri": "^3.1.
|
|
41
|
+
"fast-uri": "^3.1.8",
|
|
42
42
|
"hono": "^4.12.27",
|
|
43
43
|
"ip-address": "^10.7.2",
|
|
44
44
|
"qs": "^6.15.2",
|
|
45
|
-
"@hono/node-server": "^2.0.5"
|
|
45
|
+
"@hono/node-server": "^2.0.5",
|
|
46
|
+
"brace-expansion": "^5.0.12"
|
|
46
47
|
},
|
|
47
48
|
"devDependencies": {
|
|
48
49
|
"esbuild": "^0.28.0"
|
|
@@ -9,6 +9,7 @@ tools:
|
|
|
9
9
|
- "Bash(git diff *)"
|
|
10
10
|
- "Bash(git log *)"
|
|
11
11
|
- "Write(/docs/features/active/**)"
|
|
12
|
+
- "mcp__drm-copilot__validate_orchestration_artifacts"
|
|
12
13
|
skills:
|
|
13
14
|
- policy-compliance-order
|
|
14
15
|
- acceptance-criteria-tracking
|
|
@@ -32,7 +33,12 @@ When the active review scope is a selected version folder such as `docs/features
|
|
|
32
33
|
1. `docs/features/active/<feature-or-selected-version>/policy-audit.<timestamp>.md` — policy compliance audit with PASS/PARTIAL/FAIL verdicts and evidence
|
|
33
34
|
2. `docs/features/active/<feature-or-selected-version>/code-review.<timestamp>.md` — code quality review covering best practices
|
|
34
35
|
3. `docs/features/active/<feature-or-selected-version>/feature-audit.<timestamp>.md` — acceptance criteria verification relative to baseline
|
|
35
|
-
4.
|
|
36
|
+
4. For every review with at least one blocking finding, including halt (`HALT_NON_REMEDIABLE`) and wait (`AWAITING_CI`) verdicts: `docs/features/active/<feature-or-selected-version>/remediation-inputs.<timestamp>.md` with explicit remediation-required findings and artifact paths. The file carries these line formats:
|
|
37
|
+
- one line `Review-Verdict: <VERDICT>`, where the verdict is `PASS`, `REMEDIATION_REQUIRED`, `HALT_NON_REMEDIABLE`, or `AWAITING_CI`,
|
|
38
|
+
- inside each blocking finding block, one line `Remediability: <class>`, where the class is `autonomous`, `external_dependency`, `policy_hold`, `awaiting_ci`, or `human_decision_required` (`autonomous` when the line is absent),
|
|
39
|
+
- inside each blocking finding block, one line `Remediability-Evidence: <text>` stating why the class applies.
|
|
40
|
+
|
|
41
|
+
These lines, including the evidence text, never contain the substrings the orchestrator counts when it counts blocking findings, so their presence does not change the blocking count. The verdict follows from the classes: no blocking finding is `PASS`; any `autonomous` finding is `REMEDIATION_REQUIRED`; otherwise any `external_dependency`, `policy_hold`, or `human_decision_required` finding is `HALT_NON_REMEDIABLE`; otherwise `AWAITING_CI`.
|
|
36
42
|
|
|
37
43
|
Timestamp format: `yyyy-MM-ddTHH-mm` (ISO-8601).
|
|
38
44
|
|
|
@@ -44,6 +50,9 @@ Report the required artifact paths in the final response using these tokens:
|
|
|
44
50
|
- `code-review-path: docs/features/active/<feature-or-selected-version>/code-review.<timestamp>.md`
|
|
45
51
|
- `feature-audit-path: docs/features/active/<feature-or-selected-version>/feature-audit.<timestamp>.md`
|
|
46
52
|
- When remediation inputs are produced: `remediation-inputs-path: docs/features/active/<feature-or-selected-version>/remediation-inputs.<timestamp>.md`
|
|
53
|
+
- Optional: `review-status: <VERDICT>` with the same verdict as the remediation inputs (`PASS` when none were produced). The orchestrator treats the remediation inputs as authoritative and this token as a cross-check.
|
|
54
|
+
|
|
55
|
+
Validate each review artifact immediately after writing it with `mcp__drm-copilot__validate_orchestration_artifacts`, passing `artifact_path` set to that artifact: use `artifact_type: "policy-audit"` for `policy-audit.<timestamp>.md`, `artifact_type: "code-review"` for `code-review.<timestamp>.md`, and `artifact_type: "feature-audit"` for `feature-audit.<timestamp>.md`. When the validator reports a failure, fix the artifact in the same review and validate it again. Report an artifact path only after its validation passes.
|
|
47
56
|
|
|
48
57
|
## Context Sources
|
|
49
58
|
|
|
@@ -96,7 +96,9 @@ PR creation and PR body edits must be delegated to `Agent(pr-author)`. The orche
|
|
|
96
96
|
|
|
97
97
|
When the orchestrator runs the remediation loop, it records a top-level `remediation_loop` object in `artifacts/orchestration/orchestrator-state.json`:
|
|
98
98
|
|
|
99
|
-
- `current_cycle` — integer index of the active cycle.
|
|
99
|
+
- `current_cycle` — integer index of the active cycle's record in `cycles[]`; a record position, not an attempt count.
|
|
100
|
+
- `completed_attempts` — non-negative integer equal to the number of cycles whose `candidate_applied` is `true`. This is the shared attempt count for local, CI-failure, merge-conflict, and parallel-drift cycles; the guard halts after three completed attempts.
|
|
101
|
+
- `review_outcomes[]` — one entry per review, including `PASS`, appended in order. Each entry is an object with `verdict` (one of `PASS`, `REMEDIATION_REQUIRED`, `HALT_NON_REMEDIABLE`, `AWAITING_CI`) and `findings[]`, the review's blocking findings only, each an object with `remediability` (one of `autonomous`, `external_dependency`, `policy_hold`, `awaiting_ci`, `human_decision_required`). An optional `inputs_path` names the `remediation-inputs` file.
|
|
100
102
|
- `cycles[]` — an ordered array of cycle records. Each cycle is an object with:
|
|
101
103
|
- `entry_timestamp` — ISO-8601 timestamp when the cycle was entered.
|
|
102
104
|
- `inputs_path` — path to the `remediation-inputs.<entry-ts>.md` that opened the cycle.
|
|
@@ -106,14 +108,25 @@ When the orchestrator runs the remediation loop, it records a top-level `remedia
|
|
|
106
108
|
- `audit_paths` — the reaudit artifact paths produced at cycle exit (`code-review`, `feature-audit`, `policy-audit`).
|
|
107
109
|
- `blocking_count` — the total number of blocking findings across the reaudit artifacts.
|
|
108
110
|
- `exit_condition_met` — boolean; `true` only when the cycle's exit gate is satisfied.
|
|
111
|
+
- `candidate_applied` — boolean; `true` only for a completed attempt (execution `complete` and a non-empty pre-reaudit commit). A cycle without a candidate records `false` and consumes no attempt number.
|
|
112
|
+
- `opened_by_review` — zero-based index of the `review_outcomes[]` entry that opened the cycle.
|
|
113
|
+
|
|
114
|
+
These four fields are recorded by producers. Verdicts follow from the classes of a review's blocking findings: none is `PASS`; any `autonomous` is `REMEDIATION_REQUIRED`; otherwise any `external_dependency`, `policy_hold`, or `human_decision_required` is `HALT_NON_REMEDIABLE`; otherwise `AWAITING_CI`. A `HALT_NON_REMEDIABLE` or `AWAITING_CI` review opens no cycle.
|
|
109
115
|
|
|
110
116
|
Malformed-cycle rules:
|
|
111
117
|
|
|
112
118
|
- `plan_path` must be a non-empty string.
|
|
113
119
|
- `execution_status` may be in `{in_progress, complete, failed}` only when `preflight.final_status == 'clear'`; any other preflight status with one of those execution statuses is malformed (execution recorded before preflight cleared).
|
|
114
120
|
- `exit_condition_met == true` requires `blocking_count == 0`; a non-zero `blocking_count` with `exit_condition_met == true` is malformed.
|
|
121
|
+
- R5: `candidate_applied`, when present, must be a boolean.
|
|
122
|
+
- R6: `candidate_applied == true` requires `execution_status == "complete"`.
|
|
123
|
+
- R7: `completed_attempts`, when present, must be a non-negative integer equal to the number of object cycles whose `candidate_applied` is `true`.
|
|
124
|
+
- R8: `review_outcomes`, when present, must be a list, and each entry must be an object.
|
|
125
|
+
- R9: each outcome's `verdict` must be one of the four verdicts, its `findings` must be a list, and each finding must be an object whose `remediability` is one of the five classes (case-sensitive).
|
|
126
|
+
- R10: each well-formed outcome's `verdict` must equal the verdict derived from its findings' classes.
|
|
127
|
+
- R11: `opened_by_review`, when present, must be a non-negative integer that indexes an object entry of `review_outcomes` whose `verdict` is `REMEDIATION_REQUIRED`.
|
|
115
128
|
|
|
116
|
-
Cycle-aware `next_step` uses the form `remediation.cycle_N.{plan,preflight,execute,reaudit,exit_check}`, where `N` is the cycle
|
|
129
|
+
Cycle-aware `next_step` uses the form `remediation.cycle_N.{plan,preflight,execute,reaudit,exit_check}`, where `N` is `completed_attempts + 1` (the active cycle number) and the sub-step names the current position in the loop; `current_cycle` remains a record position in `cycles[]`.
|
|
117
130
|
|
|
118
131
|
### CI Monitoring and Post-PR Remediation
|
|
119
132
|
|
|
@@ -149,7 +162,7 @@ A new finding discovered during execution triggers a NEW cycle with a follow-up
|
|
|
149
162
|
|
|
150
163
|
### Exit Gate
|
|
151
164
|
|
|
152
|
-
`blocking_count` is the total of FAIL and blocking-PARTIAL findings across the three reaudit artifacts (`code-review`, `feature-audit`, `policy-audit`). Only `blocking_count == 0` sets `exit_condition_met = true`.
|
|
165
|
+
`blocking_count` is the total of FAIL and blocking-PARTIAL findings across the three reaudit artifacts (`code-review`, `feature-audit`, `policy-audit`). Only `blocking_count == 0` sets `exit_condition_met = true`. The exit gate counts only `autonomous` blocking findings when deciding whether to open the next cycle: a non-zero count of `autonomous` blocking findings leaves the gate unmet and opens the next cycle, while remaining blocking findings that are all non-remediable halt (`HALT_NON_REMEDIABLE`) or wait (`AWAITING_CI`) instead of opening a cycle.
|
|
153
166
|
|
|
154
167
|
### Citations
|
|
155
168
|
|
|
@@ -275,6 +275,12 @@ In `closed` mode, do not report completion until:
|
|
|
275
275
|
`artifact_type: "parallel-orchestrator-state"`).
|
|
276
276
|
4. Acceptance criteria in the AC source files of each item have been checked off by that item's own
|
|
277
277
|
run, per the `acceptance-criteria-tracking` skill.
|
|
278
|
+
A CI-dependent criterion, whose verification requires the result of CI on the pull-request head,
|
|
279
|
+
is checked off and pushed by the item's own run after its S9 success and before its DONE, and
|
|
280
|
+
that run re-runs S9 so its reported `ci_gate.head_sha` is the final head. Before `gh pr merge`,
|
|
281
|
+
confirm that the `headRefOid` value equals that reported `ci_gate.head_sha` and that the item
|
|
282
|
+
reported no pending CI-dependent criteria. You never commit acceptance-criteria check-offs from
|
|
283
|
+
the coordinator root.
|
|
278
284
|
|
|
279
285
|
In `open` mode there is no automatic completion. The run is a standing queue and terminates only
|
|
280
286
|
via `/parallel-close`, which is owned by F6 and is not specified here or shipped by this feature. Do
|
|
@@ -82,8 +82,10 @@ pla_parse_edges() {
|
|
|
82
82
|
PLA_EDGES=()
|
|
83
83
|
local -a tokens=()
|
|
84
84
|
# read -ra rather than an unquoted expansion, so a token carrying a glob
|
|
85
|
-
# character is never subjected to pathname expansion.
|
|
86
|
-
read -
|
|
85
|
+
# character is never subjected to pathname expansion. Newline, CR, VT, and FF
|
|
86
|
+
# become spaces first: read consumes one newline-terminated record and splits
|
|
87
|
+
# it on IFS only, so the Python str.split() whitespace set would otherwise differ.
|
|
88
|
+
read -ra tokens <<<"${text//[$'\n\r\v\f']/ }"
|
|
87
89
|
for token in "${tokens[@]}"; do
|
|
88
90
|
# Partition on the FIRST colon, matching str.partition: a token with two
|
|
89
91
|
# colons yields a second endpoint that still carries one, which the lexis
|
package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorState.psm1
CHANGED
|
@@ -93,17 +93,10 @@ $script:STEP_SPECIFIC_EXTRA_STATUS = @{
|
|
|
93
93
|
step9_status = @('passed', 'failed_remediation_required', 'blocked_ci_loop_limit')
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
#
|
|
97
|
-
|
|
98
|
-
$script:
|
|
99
|
-
|
|
100
|
-
'spawn_agent_unavailable',
|
|
101
|
-
'delegation_launch_failed',
|
|
102
|
-
'delegate_no_receipt',
|
|
103
|
-
'delegate_contract_incomplete',
|
|
104
|
-
'validator_failed',
|
|
105
|
-
'user_requested_stop'
|
|
106
|
-
)
|
|
96
|
+
# blocked_reason vocabulary; mirrors scripts/dev_tools/_orchestrator_state_blocked_reason.py.
|
|
97
|
+
$script:MECHANICAL_BLOCKED_REASONS = @('spawn_agent_unavailable', 'delegation_launch_failed', 'delegate_no_receipt', 'delegate_contract_incomplete', 'validator_failed', 'user_requested_stop')
|
|
98
|
+
$script:NON_MECHANICAL_BLOCKED_REASONS = @('premise_falsified', 'external_dependency', 'policy_hold', 'awaiting_ci', 'human_decision_required')
|
|
99
|
+
$script:VALID_BLOCKED_REASONS = @('none') + $script:MECHANICAL_BLOCKED_REASONS + $script:NON_MECHANICAL_BLOCKED_REASONS
|
|
107
100
|
|
|
108
101
|
# The upstream steps that must not be pending/blocked before the first PR creation
|
|
109
102
|
# of a branch. Pinned to PR_CREATION_READY_STEP_KEYS in
|
|
@@ -290,7 +283,7 @@ function Get-OrchestratorStateBasePresenceError {
|
|
|
290
283
|
# A present, non-null blocked_reason must be a member of the allowed vocabulary.
|
|
291
284
|
$blocked = Get-OrchestratorStateField -State $State -Name 'blocked_reason'
|
|
292
285
|
if ($blocked.Present -and $null -ne $blocked.Value -and
|
|
293
|
-
($script:VALID_BLOCKED_REASONS -
|
|
286
|
+
($script:VALID_BLOCKED_REASONS -cnotcontains [string]$blocked.Value)) {
|
|
294
287
|
$errors.Add("Checkpoint has invalid blocked_reason: $($blocked.Value)")
|
|
295
288
|
}
|
|
296
289
|
|
|
@@ -333,7 +326,7 @@ function Get-OrchestratorStatePrCreationReadinessError {
|
|
|
333
326
|
# blocked_reason must read as clear: absent, null, or the literal 'none'. Any
|
|
334
327
|
# other recorded reason means the branch is not ready for PR creation.
|
|
335
328
|
$blocked = Get-OrchestratorStateField -State $State -Name 'blocked_reason'
|
|
336
|
-
if ($blocked.Present -and $null -ne $blocked.Value -and ([string]$blocked.Value -
|
|
329
|
+
if ($blocked.Present -and $null -ne $blocked.Value -and ([string]$blocked.Value -cne 'none')) {
|
|
337
330
|
$errors.Add('Checkpoint PR-creation readiness validation failed: blocked_reason is not `none`.')
|
|
338
331
|
}
|
|
339
332
|
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Issue-adoption record validation and receipt waivers (inventory row C6.15).
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Destination-runtime PowerShell port of `resolve_issue_adoption` in
|
|
7
|
+
`scripts/dev_tools/_orchestrator_state_issue_adoption.py`. The optional
|
|
8
|
+
checkpoint object `issue_adoption` records that the orchestration adopted a
|
|
9
|
+
GitHub issue that existed before orchestration started. A valid record
|
|
10
|
+
waives the successful-receipt requirement for `potential_to_issue` and,
|
|
11
|
+
optionally, for the checkpoint's promotion-entry tool.
|
|
12
|
+
|
|
13
|
+
Invariants:
|
|
14
|
+
- When the `issue_adoption` member is absent, no error is produced and no
|
|
15
|
+
tool is waived, so existing checkpoints validate exactly as before.
|
|
16
|
+
- Errors accumulate in the fixed rule order of the feature specification.
|
|
17
|
+
- Fail-closed: WaivedTools is empty whenever Errors is non-empty.
|
|
18
|
+
- Every string comparison is ordinal and case-sensitive.
|
|
19
|
+
- Messages interpolate only non-blank `waived_tools` entries and the route id.
|
|
20
|
+
|
|
21
|
+
Every function is pure: it reads no file, starts no process, and never mutates
|
|
22
|
+
its input.
|
|
23
|
+
CONVENTION: this module fails fast at module scope and imports its siblings with -ErrorAction Stop.
|
|
24
|
+
#>
|
|
25
|
+
|
|
26
|
+
Set-StrictMode -Version Latest
|
|
27
|
+
$ErrorActionPreference = 'Stop'
|
|
28
|
+
|
|
29
|
+
# Import the shared checkpoint-value primitives, resolved relative to this
|
|
30
|
+
# module's directory so the import travels with the pushed-down pack.
|
|
31
|
+
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'OrchestratorStateCheckpointValue.psm1') -Force -ErrorAction Stop
|
|
32
|
+
|
|
33
|
+
$script:ISSUE_ADOPTION_KEY = 'issue_adoption'
|
|
34
|
+
$script:POTENTIAL_TO_ISSUE_TOOL = 'potential_to_issue'
|
|
35
|
+
$script:FEATURE_PROMOTION_ENTRY_TOOL = 'new_potential_entry'
|
|
36
|
+
$script:BUG_PROMOTION_ENTRY_TOOL = 'new_potential_bug_entry'
|
|
37
|
+
$script:ORIGIN_VALUES = [string[]]@('transferred', 'filed_before_orchestration', 'epic_decomposition')
|
|
38
|
+
$script:VERIFIED_VIA_VALUES = [string[]]@('gh_issue_view', 'gh_api_get', 'github_mcp_issue_read')
|
|
39
|
+
|
|
40
|
+
# The closed set of tools an adoption record may waive: the issue-creation tool
|
|
41
|
+
# and the two promotion-type-specific promotion-entry tools.
|
|
42
|
+
$script:WAIVABLE_TOOLS = [string[]]@($script:POTENTIAL_TO_ISSUE_TOOL, $script:FEATURE_PROMOTION_ENTRY_TOOL, $script:BUG_PROMOTION_ENTRY_TOOL)
|
|
43
|
+
$script:PROMOTION_ENTRY_TOOLS = [string[]]@($script:FEATURE_PROMOTION_ENTRY_TOOL, $script:BUG_PROMOTION_ENTRY_TOOL)
|
|
44
|
+
$script:POTENTIAL_RECORD_PREFIX = 'docs/features/potential/'
|
|
45
|
+
$script:POTENTIAL_RECORD_SUFFIX = '.md'
|
|
46
|
+
$script:ISSUE_NUM_PATTERN = '\A[1-9][0-9]*\z'
|
|
47
|
+
|
|
48
|
+
$script:ERROR_NOT_OBJECT = 'Checkpoint issue_adoption must be an object when present.'
|
|
49
|
+
$script:ERROR_ISSUE_NUM_FORMAT = 'Checkpoint issue_adoption.issue_num must be a string of decimal digits without a leading zero.'
|
|
50
|
+
$script:ERROR_ISSUE_NUM_MISMATCH = 'Checkpoint issue_adoption.issue_num must equal the checkpoint issue-num.'
|
|
51
|
+
$script:ERROR_ISSUE_URL = 'Checkpoint issue_adoption.issue_url must end with /issues/ followed by issue_num.'
|
|
52
|
+
$script:ERROR_ORIGIN = 'Checkpoint issue_adoption.origin must be one of transferred, filed_before_orchestration, epic_decomposition.'
|
|
53
|
+
$script:ERROR_VERIFIED_VIA = 'Checkpoint issue_adoption.verified_via must be one of gh_issue_view, gh_api_get, github_mcp_issue_read.'
|
|
54
|
+
$script:ERROR_VERIFIED_AT = 'Checkpoint issue_adoption.verified_at must be present.'
|
|
55
|
+
$script:ERROR_EVIDENCE = 'Checkpoint issue_adoption.evidence must be a non-empty string.'
|
|
56
|
+
$script:ERROR_WAIVED_TOOLS_SHAPE = 'Checkpoint issue_adoption.waived_tools must be a non-empty list of tool names.'
|
|
57
|
+
$script:ERROR_WAIVED_TOOLS_INCLUDE = 'Checkpoint issue_adoption.waived_tools must include potential_to_issue.'
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
function Test-AdoptionNonBlankString {
|
|
61
|
+
<#
|
|
62
|
+
.SYNOPSIS
|
|
63
|
+
Report whether a value is a string with non-whitespace content.
|
|
64
|
+
.PARAMETER Value
|
|
65
|
+
The deserialized JSON value to inspect. May be $null.
|
|
66
|
+
.OUTPUTS
|
|
67
|
+
System.Boolean
|
|
68
|
+
#>
|
|
69
|
+
[CmdletBinding()]
|
|
70
|
+
[OutputType([bool])]
|
|
71
|
+
param(
|
|
72
|
+
[Parameter(Mandatory = $true)]
|
|
73
|
+
[AllowNull()]
|
|
74
|
+
[object] $Value
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
return (($Value -is [string]) -and -not [string]::IsNullOrWhiteSpace([string]$Value))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function Get-AdoptionIssueIdentityError {
|
|
81
|
+
<#
|
|
82
|
+
.SYNOPSIS
|
|
83
|
+
Validate issue_num and issue_url (rules 2 and 3).
|
|
84
|
+
.PARAMETER Adoption
|
|
85
|
+
The issue_adoption object.
|
|
86
|
+
.PARAMETER State
|
|
87
|
+
The parsed checkpoint object, read for its hyphenated issue-num key.
|
|
88
|
+
.OUTPUTS
|
|
89
|
+
System.String[] - zero or more error strings.
|
|
90
|
+
#>
|
|
91
|
+
[CmdletBinding()]
|
|
92
|
+
[OutputType([string[]])]
|
|
93
|
+
param(
|
|
94
|
+
[Parameter(Mandatory = $true)]
|
|
95
|
+
[psobject] $Adoption,
|
|
96
|
+
|
|
97
|
+
[Parameter(Mandatory = $true)]
|
|
98
|
+
[psobject] $State
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
$errors = [System.Collections.Generic.List[string]]::new()
|
|
102
|
+
$rawIssueNum = (Get-CheckpointObjectMember -Owner $Adoption -Name 'issue_num').Value
|
|
103
|
+
$issueNum = $null
|
|
104
|
+
if (($rawIssueNum -is [string]) -and ([string]$rawIssueNum -cmatch $script:ISSUE_NUM_PATTERN)) {
|
|
105
|
+
$issueNum = [string]$rawIssueNum
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if ($null -ceq $issueNum) {
|
|
109
|
+
$errors.Add($script:ERROR_ISSUE_NUM_FORMAT)
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
# The equality check applies only when the checkpoint records its own
|
|
113
|
+
# issue number as a string; other shapes are validated elsewhere.
|
|
114
|
+
$checkpointIssueNum = (Get-CheckpointObjectMember -Owner $State -Name 'issue-num').Value
|
|
115
|
+
if (($checkpointIssueNum -is [string]) -and -not [string]::Equals([string]$checkpointIssueNum, $issueNum, [System.StringComparison]::Ordinal)) {
|
|
116
|
+
$errors.Add($script:ERROR_ISSUE_NUM_MISMATCH)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
$issueUrl = (Get-CheckpointObjectMember -Owner $Adoption -Name 'issue_url').Value
|
|
121
|
+
$urlIsValid = ($null -cne $issueNum) -and ($issueUrl -is [string]) -and ([string]$issueUrl).EndsWith("/issues/$issueNum", [System.StringComparison]::Ordinal)
|
|
122
|
+
if (-not $urlIsValid) {
|
|
123
|
+
$errors.Add($script:ERROR_ISSUE_URL)
|
|
124
|
+
}
|
|
125
|
+
return $errors.ToArray()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function Get-AdoptionProvenanceError {
|
|
129
|
+
<#
|
|
130
|
+
.SYNOPSIS
|
|
131
|
+
Validate origin, verified_via, verified_at, and evidence (rules 4 to 7).
|
|
132
|
+
.PARAMETER Adoption
|
|
133
|
+
The issue_adoption object.
|
|
134
|
+
.OUTPUTS
|
|
135
|
+
System.String[] - zero or more error strings.
|
|
136
|
+
#>
|
|
137
|
+
[CmdletBinding()]
|
|
138
|
+
[OutputType([string[]])]
|
|
139
|
+
param(
|
|
140
|
+
[Parameter(Mandatory = $true)]
|
|
141
|
+
[psobject] $Adoption
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
$errors = [System.Collections.Generic.List[string]]::new()
|
|
145
|
+
|
|
146
|
+
$origin = (Get-CheckpointObjectMember -Owner $Adoption -Name 'origin').Value
|
|
147
|
+
if (-not (($origin -is [string]) -and ($script:ORIGIN_VALUES -ccontains [string]$origin))) {
|
|
148
|
+
$errors.Add($script:ERROR_ORIGIN)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
$verifiedVia = (Get-CheckpointObjectMember -Owner $Adoption -Name 'verified_via').Value
|
|
152
|
+
if (-not (($verifiedVia -is [string]) -and ($script:VERIFIED_VIA_VALUES -ccontains [string]$verifiedVia))) {
|
|
153
|
+
$errors.Add($script:ERROR_VERIFIED_VIA)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
# Presence-only: any non-null value other than a blank string passes,
|
|
157
|
+
# including a System.DateTime produced by ConvertFrom-Json.
|
|
158
|
+
$verifiedAt = (Get-CheckpointObjectMember -Owner $Adoption -Name 'verified_at').Value
|
|
159
|
+
if (($null -ceq $verifiedAt) -or (($verifiedAt -is [string]) -and [string]::IsNullOrWhiteSpace([string]$verifiedAt))) {
|
|
160
|
+
$errors.Add($script:ERROR_VERIFIED_AT)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (-not (Test-AdoptionNonBlankString -Value (Get-CheckpointObjectMember -Owner $Adoption -Name 'evidence').Value)) {
|
|
164
|
+
$errors.Add($script:ERROR_EVIDENCE)
|
|
165
|
+
}
|
|
166
|
+
return $errors.ToArray()
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function Get-AdoptionWaivedToolList {
|
|
170
|
+
<#
|
|
171
|
+
.SYNOPSIS
|
|
172
|
+
Return the waived tools when they form a non-empty list of non-blank strings.
|
|
173
|
+
.PARAMETER Value
|
|
174
|
+
The deserialized waived_tools value. May be $null.
|
|
175
|
+
.OUTPUTS
|
|
176
|
+
System.Collections.Hashtable with keys Ok (bool) and Value (string[]).
|
|
177
|
+
#>
|
|
178
|
+
[CmdletBinding()]
|
|
179
|
+
[OutputType([hashtable])]
|
|
180
|
+
param(
|
|
181
|
+
[Parameter(Mandatory = $true)]
|
|
182
|
+
[AllowNull()]
|
|
183
|
+
[object] $Value
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
if (-not (Test-CheckpointListValue -Value $Value)) {
|
|
187
|
+
return @{ Ok = $false; Value = [string[]]@() }
|
|
188
|
+
}
|
|
189
|
+
$items = @($Value)
|
|
190
|
+
if ($items.Count -ceq 0) {
|
|
191
|
+
return @{ Ok = $false; Value = [string[]]@() }
|
|
192
|
+
}
|
|
193
|
+
foreach ($item in $items) {
|
|
194
|
+
if (-not (Test-AdoptionNonBlankString -Value $item)) {
|
|
195
|
+
return @{ Ok = $false; Value = [string[]]@() }
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return @{ Ok = $true; Value = [string[]]$items }
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function Get-AdoptionWaivedToolError {
|
|
202
|
+
<#
|
|
203
|
+
.SYNOPSIS
|
|
204
|
+
Validate each waived tool entry and the mandatory issue-creation waiver (rule 8).
|
|
205
|
+
.PARAMETER WaivedTool
|
|
206
|
+
The well-formed waived tool list, in checkpoint order.
|
|
207
|
+
.PARAMETER RouteId
|
|
208
|
+
The selected route id, interpolated into route errors.
|
|
209
|
+
.PARAMETER RequiredMcpTool
|
|
210
|
+
The route's required MCP tools after promotion-type resolution.
|
|
211
|
+
.PARAMETER SuccessfulTool
|
|
212
|
+
Tools that hold a successful MCP receipt.
|
|
213
|
+
.OUTPUTS
|
|
214
|
+
System.String[] - zero or more error strings.
|
|
215
|
+
#>
|
|
216
|
+
[CmdletBinding()]
|
|
217
|
+
[OutputType([string[]])]
|
|
218
|
+
param(
|
|
219
|
+
[Parameter(Mandatory = $true)]
|
|
220
|
+
[string[]] $WaivedTool,
|
|
221
|
+
|
|
222
|
+
[Parameter(Mandatory = $true)]
|
|
223
|
+
[string] $RouteId,
|
|
224
|
+
|
|
225
|
+
[Parameter(Mandatory = $true)]
|
|
226
|
+
[AllowEmptyCollection()]
|
|
227
|
+
[string[]] $RequiredMcpTool,
|
|
228
|
+
|
|
229
|
+
[Parameter(Mandatory = $true)]
|
|
230
|
+
[AllowEmptyCollection()]
|
|
231
|
+
[string[]] $SuccessfulTool
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
$errors = [System.Collections.Generic.List[string]]::new()
|
|
235
|
+
$seen = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::Ordinal)
|
|
236
|
+
foreach ($tool in $WaivedTool) {
|
|
237
|
+
# Each entry receives only the first rule it violates, in rule order.
|
|
238
|
+
if ($seen.Contains($tool)) {
|
|
239
|
+
$errors.Add("Checkpoint issue_adoption.waived_tools lists a tool more than once: $tool.")
|
|
240
|
+
}
|
|
241
|
+
elseif ($script:WAIVABLE_TOOLS -cnotcontains $tool) {
|
|
242
|
+
$errors.Add("Checkpoint issue_adoption.waived_tools names a tool that cannot be waived: $tool.")
|
|
243
|
+
}
|
|
244
|
+
elseif ($RequiredMcpTool -cnotcontains $tool) {
|
|
245
|
+
$errors.Add("Checkpoint issue_adoption.waived_tools names a tool that is not required by route ${RouteId}: $tool.")
|
|
246
|
+
}
|
|
247
|
+
elseif ($SuccessfulTool -ccontains $tool) {
|
|
248
|
+
$errors.Add("Checkpoint issue_adoption.waived_tools names a tool that has a successful MCP receipt: $tool.")
|
|
249
|
+
}
|
|
250
|
+
[void]$seen.Add($tool)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (-not $seen.Contains($script:POTENTIAL_TO_ISSUE_TOOL)) {
|
|
254
|
+
$errors.Add($script:ERROR_WAIVED_TOOLS_INCLUDE)
|
|
255
|
+
}
|
|
256
|
+
return $errors.ToArray()
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function Get-AdoptionPotentialRecordError {
|
|
260
|
+
<#
|
|
261
|
+
.SYNOPSIS
|
|
262
|
+
Require a potential record when a promotion-entry tool is waived (rule 9).
|
|
263
|
+
.PARAMETER WaivedTool
|
|
264
|
+
The well-formed waived tool list, in checkpoint order.
|
|
265
|
+
.PARAMETER PotentialRecord
|
|
266
|
+
The deserialized potential_record value. May be $null.
|
|
267
|
+
.OUTPUTS
|
|
268
|
+
System.String[] - zero or more error strings.
|
|
269
|
+
#>
|
|
270
|
+
[CmdletBinding()]
|
|
271
|
+
[OutputType([string[]])]
|
|
272
|
+
param(
|
|
273
|
+
[Parameter(Mandatory = $true)]
|
|
274
|
+
[string[]] $WaivedTool,
|
|
275
|
+
|
|
276
|
+
[Parameter(Mandatory = $true)]
|
|
277
|
+
[AllowNull()]
|
|
278
|
+
[object] $PotentialRecord
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
$recordIsValid = $false
|
|
282
|
+
if ($PotentialRecord -is [string]) {
|
|
283
|
+
$record = [string]$PotentialRecord
|
|
284
|
+
$recordIsValid = $record.StartsWith($script:POTENTIAL_RECORD_PREFIX, [System.StringComparison]::Ordinal) -and $record.EndsWith($script:POTENTIAL_RECORD_SUFFIX, [System.StringComparison]::Ordinal)
|
|
285
|
+
}
|
|
286
|
+
if ($recordIsValid) { return [string[]]@() }
|
|
287
|
+
|
|
288
|
+
$errors = [System.Collections.Generic.List[string]]::new()
|
|
289
|
+
$reported = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::Ordinal)
|
|
290
|
+
foreach ($tool in $WaivedTool) {
|
|
291
|
+
if (($script:PROMOTION_ENTRY_TOOLS -ccontains $tool) -and -not $reported.Contains($tool)) {
|
|
292
|
+
$errors.Add("Checkpoint issue_adoption.potential_record must name a markdown file under docs/features/potential/ when waiving $tool.")
|
|
293
|
+
[void]$reported.Add($tool)
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return $errors.ToArray()
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function Get-OrchestratorStateIssueAdoptionResult {
|
|
300
|
+
<#
|
|
301
|
+
.SYNOPSIS
|
|
302
|
+
Validate the checkpoint's issue_adoption record and resolve its waivers.
|
|
303
|
+
.DESCRIPTION
|
|
304
|
+
Public entry mirroring resolve_issue_adoption. Absent key: no errors and
|
|
305
|
+
no waivers. A present value that is not an object (null included) yields
|
|
306
|
+
only the not-an-object error. Otherwise rules 2 to 9 accumulate errors in
|
|
307
|
+
order, and the waived-tool set is populated only when no error was found.
|
|
308
|
+
.PARAMETER State
|
|
309
|
+
The parsed checkpoint object.
|
|
310
|
+
.PARAMETER RouteId
|
|
311
|
+
The selected route id, interpolated into route errors.
|
|
312
|
+
.PARAMETER RequiredMcpTool
|
|
313
|
+
The route's required MCP tools after promotion-type resolution.
|
|
314
|
+
.PARAMETER SuccessfulTool
|
|
315
|
+
Tools that hold a successful MCP receipt in the checkpoint.
|
|
316
|
+
.OUTPUTS
|
|
317
|
+
System.Collections.Hashtable with keys Errors (string[]) and WaivedTools (string[]).
|
|
318
|
+
#>
|
|
319
|
+
[CmdletBinding()]
|
|
320
|
+
[OutputType([hashtable])]
|
|
321
|
+
param(
|
|
322
|
+
[Parameter(Mandatory = $true)]
|
|
323
|
+
[psobject] $State,
|
|
324
|
+
|
|
325
|
+
[Parameter(Mandatory = $true)]
|
|
326
|
+
[string] $RouteId,
|
|
327
|
+
|
|
328
|
+
[Parameter(Mandatory = $true)]
|
|
329
|
+
[AllowEmptyCollection()]
|
|
330
|
+
[string[]] $RequiredMcpTool,
|
|
331
|
+
|
|
332
|
+
[Parameter(Mandatory = $true)]
|
|
333
|
+
[AllowEmptyCollection()]
|
|
334
|
+
[string[]] $SuccessfulTool
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
$member = Get-CheckpointObjectMember -Owner $State -Name $script:ISSUE_ADOPTION_KEY
|
|
338
|
+
if (-not $member.Present) {
|
|
339
|
+
return @{ Errors = [string[]]@(); WaivedTools = [string[]]@() }
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
$adoption = $member.Value
|
|
343
|
+
if (-not (Test-CheckpointObjectValue -Value $adoption)) {
|
|
344
|
+
return @{ Errors = [string[]]@($script:ERROR_NOT_OBJECT); WaivedTools = [string[]]@() }
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
$errors = [System.Collections.Generic.List[string]]::new()
|
|
348
|
+
$errors.AddRange([string[]]@(Get-AdoptionIssueIdentityError -Adoption $adoption -State $State))
|
|
349
|
+
$errors.AddRange([string[]]@(Get-AdoptionProvenanceError -Adoption $adoption))
|
|
350
|
+
|
|
351
|
+
$waived = Get-AdoptionWaivedToolList -Value (Get-CheckpointObjectMember -Owner $adoption -Name 'waived_tools').Value
|
|
352
|
+
if (-not $waived.Ok) {
|
|
353
|
+
# A malformed list stops rule 8 and skips rule 9 entirely.
|
|
354
|
+
$errors.Add($script:ERROR_WAIVED_TOOLS_SHAPE)
|
|
355
|
+
return @{ Errors = $errors.ToArray(); WaivedTools = [string[]]@() }
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
$errors.AddRange([string[]]@(Get-AdoptionWaivedToolError -WaivedTool $waived.Value -RouteId $RouteId -RequiredMcpTool $RequiredMcpTool -SuccessfulTool $SuccessfulTool))
|
|
359
|
+
$potentialRecord = (Get-CheckpointObjectMember -Owner $adoption -Name 'potential_record').Value
|
|
360
|
+
$errors.AddRange([string[]]@(Get-AdoptionPotentialRecordError -WaivedTool $waived.Value -PotentialRecord $potentialRecord))
|
|
361
|
+
|
|
362
|
+
if ($errors.Count -gt 0) {
|
|
363
|
+
return @{ Errors = $errors.ToArray(); WaivedTools = [string[]]@() }
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
# The waived-tool set is the distinct entries in first-occurrence order.
|
|
367
|
+
$distinct = [System.Collections.Generic.List[string]]::new()
|
|
368
|
+
foreach ($tool in $waived.Value) {
|
|
369
|
+
if (-not $distinct.Contains($tool)) { $distinct.Add($tool) }
|
|
370
|
+
}
|
|
371
|
+
return @{ Errors = [string[]]@(); WaivedTools = $distinct.ToArray() }
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
# Only the family entry point is exported; the rule helpers stay private.
|
|
375
|
+
Export-ModuleMember -Function Get-OrchestratorStateIssueAdoptionResult
|
|
@@ -37,6 +37,7 @@ $ErrorActionPreference = 'Stop'
|
|
|
37
37
|
# module's directory so the import travels with the pushed-down pack regardless
|
|
38
38
|
# of the consumer repository's working directory.
|
|
39
39
|
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'OrchestratorStateCheckpointValue.psm1') -Force -ErrorAction Stop
|
|
40
|
+
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'OrchestratorStateRemediationAccounting.psm1') -Force -ErrorAction Stop
|
|
40
41
|
|
|
41
42
|
# The eight keys every list-form delegation receipt must carry. Pinned to
|
|
42
43
|
# REQUIRED_RECEIPT_KEYS in scripts/dev_tools/validate_orchestrator_state.py.
|
|
@@ -290,12 +291,15 @@ function Get-RemediationCycleError {
|
|
|
290
291
|
function Get-OrchestratorStateRemediationLoopError {
|
|
291
292
|
<#
|
|
292
293
|
.SYNOPSIS
|
|
293
|
-
Return the remediation_loop errors (inventory rows U6.R1-U6.R4).
|
|
294
|
+
Return the remediation_loop errors (inventory rows U6.R1-U6.R4, R5-R11).
|
|
294
295
|
.DESCRIPTION
|
|
295
296
|
Public entry mirroring _validate_remediation_loop. A non-object
|
|
296
|
-
remediation_loop
|
|
297
|
-
|
|
298
|
-
|
|
297
|
+
remediation_loop deliberately yields ZERO errors, matching the Python
|
|
298
|
+
tolerance rather than fabricating a structural error. When cycles is a
|
|
299
|
+
list each cycle is validated independently (U6.R1-U6.R4); the issue #484
|
|
300
|
+
review-outcome and accounting invariants R5-R11 then run through
|
|
301
|
+
Get-OrchestratorStateRemediationAccountingError with the cycle list, or
|
|
302
|
+
$null when cycles is absent or not a list.
|
|
299
303
|
.PARAMETER Value
|
|
300
304
|
The raw deserialized value of the checkpoint's remediation_loop key.
|
|
301
305
|
.OUTPUTS
|
|
@@ -313,21 +317,24 @@ function Get-OrchestratorStateRemediationLoopError {
|
|
|
313
317
|
|
|
314
318
|
if (-not (Test-CheckpointObjectValue -Value $Value)) { return $errors.ToArray() }
|
|
315
319
|
$cycles = (Get-CheckpointObjectMember -Owner $Value -Name $script:REMEDIATION_CYCLES_KEY).Value
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
320
|
+
$cycleList = $null
|
|
321
|
+
if (Test-CheckpointListValue -Value $cycles) {
|
|
322
|
+
$cycleList = $cycles
|
|
323
|
+
# Validate each cycle independently so a malformed entry does not mask the
|
|
324
|
+
# errors of the cycles that follow it.
|
|
325
|
+
$index = 0
|
|
326
|
+
foreach ($cycle in @($cycles)) {
|
|
327
|
+
if (-not (Test-CheckpointObjectValue -Value $cycle)) {
|
|
328
|
+
$errors.Add("Checkpoint remediation cycle #$index must be an object.")
|
|
329
|
+
$index++
|
|
330
|
+
continue
|
|
331
|
+
}
|
|
332
|
+
$errors.AddRange([string[]]@(Get-RemediationCycleError -Index $index -Cycle $cycle))
|
|
324
333
|
$index++
|
|
325
|
-
continue
|
|
326
334
|
}
|
|
327
|
-
$errors.AddRange([string[]]@(Get-RemediationCycleError -Index $index -Cycle $cycle))
|
|
328
|
-
$index++
|
|
329
335
|
}
|
|
330
336
|
|
|
337
|
+
$errors.AddRange([string[]]@(Get-OrchestratorStateRemediationAccountingError -RemediationLoop $Value -Cycle $cycleList))
|
|
331
338
|
return $errors.ToArray()
|
|
332
339
|
}
|
|
333
340
|
|