@danmoisan/drm-copilot-mcp 0.0.1 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/out/mcp-server.js +98 -21
  2. package/package.json +21 -5
  3. package/resources/claude-customizations/.claude/agent-memory/orchestrator/MEMORY.md +15 -3
  4. package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_branch_base_check_unmerged_pr_deps.md +16 -0
  5. package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_every_change_through_lifecycle.md +15 -0
  6. package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_policy_compliance_not_optional.md +18 -0
  7. package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_potential_to_issue_creates_github_issue.md +13 -0
  8. package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_remediation_plan_em_dash_required.md +13 -0
  9. package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_small_bug_uses_minor_audit.md +13 -0
  10. package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_test_files_count_against_500_cap.md +13 -0
  11. package/resources/claude-customizations/.claude/agents/atomic-executor.md +7 -7
  12. package/resources/claude-customizations/.claude/agents/csharp-typed-engineer.md +4 -5
  13. package/resources/claude-customizations/.claude/agents/feature-review.md +7 -3
  14. package/resources/claude-customizations/.claude/agents/orchestrator.md +22 -3
  15. package/resources/claude-customizations/.claude/agents/powershell-typed-engineer.md +1 -1
  16. package/resources/claude-customizations/.claude/agents/pr-author.md +78 -0
  17. package/resources/claude-customizations/.claude/agents/task-researcher.md +10 -6
  18. package/resources/claude-customizations/.claude/hooks/enforce-checkpoint-monotonic.ps1 +303 -0
  19. package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +300 -0
  20. package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +40 -14
  21. package/resources/claude-customizations/.claude/hooks/enforce-feature-folder-order.ps1 +148 -0
  22. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +210 -0
  23. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +333 -0
  24. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +216 -0
  25. package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +84 -15
  26. package/resources/claude-customizations/.claude/hooks/validate-executor-output.ps1 +1 -1
  27. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage.ps1 +75 -5
  28. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +93 -0
  29. package/resources/claude-customizations/.claude/hooks/validate-pr-author-output.ps1 +136 -0
  30. package/resources/claude-customizations/.claude/hooks/validate-task-researcher-output.ps1 +90 -6
  31. package/resources/claude-customizations/.claude/rules/architecture-boundaries.md +46 -0
  32. package/resources/claude-customizations/.claude/rules/benchmark-baselines.md +35 -0
  33. package/resources/claude-customizations/.claude/rules/ci-workflows.md +36 -0
  34. package/resources/claude-customizations/.claude/rules/csharp.md +62 -16
  35. package/resources/claude-customizations/.claude/rules/general-code-change.md +12 -3
  36. package/resources/claude-customizations/.claude/rules/general-unit-test.md +47 -2
  37. package/resources/claude-customizations/.claude/rules/orchestrator-state.md +39 -0
  38. package/resources/claude-customizations/.claude/rules/powershell.md +5 -5
  39. package/resources/claude-customizations/.claude/rules/python.md +4 -3
  40. package/resources/claude-customizations/.claude/rules/quality-tiers.md +51 -0
  41. package/resources/claude-customizations/.claude/rules/typescript.md +37 -8
  42. package/resources/claude-customizations/.claude/settings.json +59 -12
  43. package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +2 -2
  44. package/resources/claude-customizations/.claude/skills/csharp-qa-gate/SKILL.md +25 -10
  45. package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +0 -1
  46. package/resources/claude-customizations/.claude/skills/execute-hard-lock/SKILL.md +6 -6
  47. package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +8 -8
  48. package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +17 -6
  49. package/resources/claude-customizations/.claude/skills/human-exception-runbook/SKILL.md +52 -0
  50. package/resources/claude-customizations/.claude/skills/human-exception-runbook/example.runbook.md +36 -0
  51. package/resources/claude-customizations/.claude/skills/invoke-csharp-engineer/SKILL.md +4 -4
  52. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +172 -5
  53. package/resources/claude-customizations/.claude/skills/policy-audit-template-usage/SKILL.md +3 -3
  54. package/resources/claude-customizations/.claude/skills/powershell-qa-gate/SKILL.md +4 -4
  55. package/resources/claude-customizations/.claude/skills/pr-base-branch-merge-base/SKILL.md +3 -3
  56. package/resources/claude-customizations/.claude/skills/python-qa-gate/SKILL.md +1 -1
  57. package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +90 -17
  58. package/resources/claude-customizations/.claude/skills/research-issue/SKILL.md +5 -3
  59. package/resources/claude-customizations/.claude-variants/csharp-legacy/agents/csharp-typed-engineer.md +69 -0
  60. package/resources/claude-customizations/.claude-variants/csharp-legacy/rules/csharp.md +96 -0
  61. package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +77 -0
  62. package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/invoke-csharp-engineer/SKILL.md +64 -0
  63. package/resources/claude-customizations/pack-manifests/core.json +65 -0
  64. package/resources/claude-customizations/pack-manifests/csharp-legacy.json +11 -0
  65. package/resources/claude-customizations/pack-manifests/csharp-modern.json +12 -0
  66. package/resources/claude-customizations/pack-manifests/powershell.json +14 -0
  67. package/resources/claude-customizations/pack-manifests/python.json +14 -0
  68. package/resources/claude-customizations/pack-manifests/typescript.json +9 -0
  69. package/resources/claude-dir-customizations/.mcp.json +3 -3
  70. package/resources/codex-and-agents-customizations/.agents/README.md +1 -1
  71. package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +0 -5
  72. package/resources/codex-and-agents-customizations/.agents/skills/architecture-boundaries/SKILL.md +52 -0
  73. package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +16 -8
  74. package/resources/codex-and-agents-customizations/.agents/skills/benchmark-baselines/SKILL.md +44 -0
  75. package/resources/codex-and-agents-customizations/.agents/skills/ci-workflows/SKILL.md +45 -0
  76. package/resources/codex-and-agents-customizations/.agents/skills/commit-message/SKILL.md +3 -11
  77. package/resources/codex-and-agents-customizations/.agents/skills/csharp/SKILL.md +1 -5
  78. package/resources/codex-and-agents-customizations/.agents/skills/csharp-change-budget-router/SKILL.md +1 -6
  79. package/resources/codex-and-agents-customizations/.agents/skills/csharp-orchestration-state-machine/SKILL.md +0 -5
  80. package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +0 -5
  81. package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +0 -5
  82. package/resources/codex-and-agents-customizations/.agents/skills/execute-hard-lock/SKILL.md +8 -17
  83. package/resources/codex-and-agents-customizations/.agents/skills/feature-promotion-lifecycle/SKILL.md +41 -25
  84. package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +1 -6
  85. package/resources/codex-and-agents-customizations/.agents/skills/fill-feature-docs/SKILL.md +0 -5
  86. package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +86 -0
  87. package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +111 -0
  88. package/resources/codex-and-agents-customizations/.agents/skills/human-exception-runbook/SKILL.md +57 -0
  89. package/resources/codex-and-agents-customizations/.agents/skills/human-exception-runbook/example.runbook.md +36 -0
  90. package/resources/codex-and-agents-customizations/.agents/skills/invoke-csharp-engineer/SKILL.md +0 -9
  91. package/resources/codex-and-agents-customizations/.agents/skills/invoke-powershell-engineer/SKILL.md +0 -9
  92. package/resources/codex-and-agents-customizations/.agents/skills/invoke-python-engineer/SKILL.md +0 -9
  93. package/resources/codex-and-agents-customizations/.agents/skills/make-skill-template/SKILL.md +0 -5
  94. package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +206 -18
  95. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-state/SKILL.md +48 -0
  96. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +94 -10
  97. package/resources/codex-and-agents-customizations/.agents/skills/policy-audit-template-usage/SKILL.md +3 -8
  98. package/resources/codex-and-agents-customizations/.agents/skills/policy-compliance-order/SKILL.md +0 -10
  99. package/resources/codex-and-agents-customizations/.agents/skills/powershell/SKILL.md +4 -8
  100. package/resources/codex-and-agents-customizations/.agents/skills/powershell-change-budget-router/SKILL.md +1 -6
  101. package/resources/codex-and-agents-customizations/.agents/skills/powershell-orchestration-state-machine/SKILL.md +0 -5
  102. package/resources/codex-and-agents-customizations/.agents/skills/powershell-qa-gate/SKILL.md +3 -9
  103. package/resources/codex-and-agents-customizations/.agents/skills/pr-author/SKILL.md +1 -9
  104. package/resources/codex-and-agents-customizations/.agents/skills/pr-base-branch-merge-base/SKILL.md +4 -9
  105. package/resources/codex-and-agents-customizations/.agents/skills/pr-context-artifacts/SKILL.md +0 -5
  106. package/resources/codex-and-agents-customizations/.agents/skills/python/SKILL.md +1 -5
  107. package/resources/codex-and-agents-customizations/.agents/skills/python-change-budget-router/SKILL.md +1 -6
  108. package/resources/codex-and-agents-customizations/.agents/skills/python-qa-gate/SKILL.md +0 -5
  109. package/resources/codex-and-agents-customizations/.agents/skills/python-suppressions/SKILL.md +2 -6
  110. package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +57 -0
  111. package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +0 -5
  112. package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/SKILL.md +96 -72
  113. package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/agents/openai.yaml +1 -1
  114. package/resources/codex-and-agents-customizations/.agents/skills/research-issue/SKILL.md +0 -10
  115. package/resources/codex-and-agents-customizations/.agents/skills/review-epic/SKILL.md +0 -5
  116. package/resources/codex-and-agents-customizations/.agents/skills/review-feature/SKILL.md +0 -5
  117. package/resources/codex-and-agents-customizations/.agents/skills/review-staged/SKILL.md +0 -5
  118. package/resources/codex-and-agents-customizations/.agents/skills/self-explanatory-code-commenting/SKILL.md +2 -6
  119. package/resources/codex-and-agents-customizations/.agents/skills/skill-canonical-location-audit/SKILL.md +0 -5
  120. package/resources/codex-and-agents-customizations/.agents/skills/tonality/SKILL.md +86 -0
  121. package/resources/codex-and-agents-customizations/.agents/skills/translate-claude-to-codex/SKILL.md +297 -0
  122. package/resources/codex-and-agents-customizations/.agents/skills/translate-copilot-to-claude/SKILL.md +0 -22
  123. package/resources/codex-and-agents-customizations/.agents/skills/typescript/SKILL.md +1 -5
  124. package/resources/codex-and-agents-customizations/.agents/skills/typescript-suppressions/SKILL.md +2 -6
  125. package/resources/codex-and-agents-customizations/.agents/skills/update-status/SKILL.md +0 -5
  126. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +5 -5
  127. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +91 -63
  128. package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-executor.toml +1 -1
  129. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer.toml +1 -1
  130. package/resources/codex-and-agents-customizations/.codex/config.toml +111 -137
  131. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-checkpoint-monotonic.ps1 +303 -0
  132. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +300 -0
  133. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +148 -0
  134. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-pr-author-skill.ps1 +336 -0
  135. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +84 -18
  136. package/resources/codex-and-agents-customizations/.codex/prompts/orchestrate-work.md +4 -3
  137. package/resources/codex-and-agents-customizations/.codex/scripts/post-codex-worktree-session.ps1 +5 -0
  138. package/resources/codex-and-agents-customizations/.github/workflows/_validate-orchestrator-state.yml +68 -0
  139. package/resources/codex-and-agents-customizations/.github/workflows/validate-orchestrator-state.yml +15 -0
  140. package/resources/config/orchestration-routing.json +76 -0
  141. package/resources/customizations/.github/agents/Powershell DI Unit Test Engineer.agent.md +1 -1
  142. package/resources/customizations/.github/agents/atomic_executor.agent.md +1 -1
  143. package/resources/customizations/.github/agents/atomic_planning.agent.md +10 -10
  144. package/resources/customizations/.github/agents/csharp-orchestrator.agent.md +6 -2
  145. package/resources/customizations/.github/agents/feature-review.agent.md +2 -2
  146. package/resources/customizations/.github/agents/orchestrator.agent.md +6 -2
  147. package/resources/customizations/.github/agents/powershell-atomic-executor.agent.md +4 -4
  148. package/resources/customizations/.github/agents/powershell-atomic-planning.agent.md +10 -10
  149. package/resources/customizations/.github/agents/powershell-orchestrator.agent.md +6 -2
  150. package/resources/customizations/.github/agents/powershell-typed-engineer.agent.md +2 -2
  151. package/resources/customizations/.github/agents/pr-author.agent.md +25 -0
  152. package/resources/customizations/.github/agents/python-orchestrator.agent.md +6 -2
  153. package/resources/customizations/.github/agents/staged-review.agent.md +1 -1
  154. package/resources/customizations/.github/agents/task-researcher.agent.md +4 -4
  155. package/resources/customizations/.github/instructions/powershell-code-change.instructions.md +6 -6
  156. package/resources/customizations/.github/prompts/fillout-prd-feature.prompt.md +1 -1
  157. package/resources/customizations/.github/prompts/generate-commit-message-repo.prompt.md +1 -1
  158. package/resources/customizations/.github/prompts/orchestrate-csharp-work.prompt.md +5 -3
  159. package/resources/customizations/.github/prompts/orchestrate-work.prompt.md +5 -3
  160. package/resources/customizations/.github/prompts/research-issue.prompt.md +4 -3
  161. package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +14 -1
  162. package/resources/customizations/.github/skills/feature-promotion-lifecycle/SKILL.md +11 -7
  163. package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +10 -1
  164. package/resources/customizations/.github/skills/pr-base-branch-merge-base/SKILL.md +2 -2
  165. package/resources/customizations/.github/skills/remediation-handoff-atomic-planner/SKILL.md +5 -0
  166. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +7 -0
  167. package/resources/scripts/dev_tools/_orchestrator_state_human_interaction.py +127 -0
  168. package/resources/scripts/dev_tools/_orchestrator_state_routing.py +216 -0
  169. package/resources/scripts/dev_tools/push_down_claude_customizations.py +276 -61
  170. package/resources/scripts/dev_tools/push_down_claude_filesystem.py +472 -0
  171. package/resources/scripts/dev_tools/push_down_claude_pack_selection.py +401 -0
  172. package/resources/scripts/dev_tools/validate_orchestration_artifacts.py +103 -411
  173. package/resources/scripts/dev_tools/validate_orchestration_review_artifacts.py +107 -0
  174. package/resources/scripts/dev_tools/validate_orchestrator_state.py +505 -0
  175. package/resources/scripts/dev_tools/validate_policy_audit_artifact.py +472 -0
  176. package/resources/templates/push_down_claude_customizations.py +162 -117
  177. package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_repo_root_is_source_of_truth.md +0 -11
  178. package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_vsce_verify_package_location.md +0 -19
  179. package/resources/claude-customizations/.claude/agent-memory/orchestrator/project_extension_location.md +0 -11
  180. package/resources/claude-customizations/.claude/agent-memory/prd-feature/MEMORY.md +0 -1
  181. package/resources/claude-customizations/.claude/agent-memory/prd-feature/project_push_down_pattern.md +0 -13
  182. package/resources/claude-customizations/.claude/agent-memory/task-researcher/MEMORY.md +0 -3
  183. package/resources/claude-customizations/.claude/agent-memory/task-researcher/project_push_down_claude_dir.md +0 -11
@@ -0,0 +1,505 @@
1
+ """Validate orchestration checkpoint state artifacts.
2
+
3
+ Purpose:
4
+ Hold the orchestrator-state validation logic and receipt-namespace rules so
5
+ the stable CLI entrypoint can remain small while preserving the existing
6
+ validator contract.
7
+
8
+ Usage:
9
+ Import ``validate_orchestrator_state_text`` from
10
+ ``scripts.dev_tools.validate_orchestration_artifacts`` or directly from this
11
+ module when a caller needs checkpoint validation.
12
+
13
+ Flow:
14
+ 1. Parse the checkpoint JSON payload.
15
+ 2. Validate required top-level keys and status values.
16
+ 3. Validate either the legacy list-based receipts, top-level promotion
17
+ receipts, or the additive ``delegation_receipts.promotion.*``
18
+ namespace.
19
+
20
+ Invariants / Constraints:
21
+ - The validator accepts the legacy list, top-level promotion receipts, and
22
+ the additive promotion namespace forms for receipt evidence.
23
+ - Unsupported namespace keys are rejected.
24
+ - The validator returns error strings and never mutates the checkpoint.
25
+
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import json
31
+ from typing import Any, cast
32
+
33
+ from dev_tools._orchestrator_state_human_interaction import (
34
+ HUMAN_INTERACTION_KEY,
35
+ _validate_human_interaction,
36
+ )
37
+ from dev_tools._orchestrator_state_routing import validate_routing_contract
38
+
39
+ REQUIRED_STATE_KEYS = (
40
+ "objective",
41
+ "change_budget_estimate",
42
+ "path_selected",
43
+ "promotion-type",
44
+ "short-name",
45
+ "relativeFile",
46
+ "long-name",
47
+ "issue-num",
48
+ "feature-folder",
49
+ "work-mode",
50
+ "plan-path",
51
+ "completed_steps",
52
+ "next_step",
53
+ "last_updated",
54
+ "step5_status",
55
+ "step6_status",
56
+ "step7_status",
57
+ "step8_status",
58
+ "step9_status",
59
+ "step10_status",
60
+ "delegation_receipts",
61
+ "blocked_reason",
62
+ )
63
+ VALID_STEP_STATUS = {
64
+ "not-applicable",
65
+ "pending",
66
+ "delegated",
67
+ "verified",
68
+ "blocked",
69
+ "not_started",
70
+ "in_progress",
71
+ "completed",
72
+ }
73
+ VALID_BLOCKED_REASONS = {
74
+ "none",
75
+ "spawn_agent_unavailable",
76
+ "delegation_launch_failed",
77
+ "delegate_no_receipt",
78
+ "delegate_contract_incomplete",
79
+ "validator_failed",
80
+ "user_requested_stop",
81
+ }
82
+ REQUIRED_RECEIPT_KEYS = (
83
+ "step",
84
+ "agent_name",
85
+ "agent_id",
86
+ "skill_source",
87
+ "started_at",
88
+ "completed_at",
89
+ "result_signal",
90
+ "artifact_paths",
91
+ )
92
+ PROMOTION_RECEIPT_NAMESPACE_KEY = "promotion"
93
+ PROMOTION_RECEIPT_KEYS = (
94
+ "potential_entry",
95
+ "issue",
96
+ "feature_folder",
97
+ )
98
+ ISSUE_232 = "232"
99
+ ISSUE_232_BRANCH = "feature/harden-orchestrate-skill-232"
100
+ PR_GATE_KEYS = ("pr_number", "pr_url", "head_branch", "head_sha")
101
+ CI_GATE_KEYS = ("conclusion", "head_sha", "verified_at")
102
+ REMEDIATION_LOOP_KEY = "remediation_loop"
103
+ REMEDIATION_CYCLES_KEY = "cycles"
104
+ # Execution statuses that may only be recorded once a cycle's preflight gate has
105
+ # cleared; recording any of these before preflight clears is a malformed cycle.
106
+ EXECUTION_STATUSES_REQUIRING_CLEAR_PREFLIGHT = {
107
+ "in_progress",
108
+ "complete",
109
+ "failed",
110
+ }
111
+ PREFLIGHT_CLEARED_STATUS = "clear"
112
+
113
+
114
+ def _validate_remediation_cycle(index: int, cycle: dict[str, Any]) -> list[str]:
115
+ """Validate the three invariants for one remediation cycle.
116
+
117
+ Purpose:
118
+ Enforce the orchestrator-state remediation-cycle invariants documented
119
+ in `.claude/rules/orchestrator-state.md` for a single cycle object:
120
+ non-empty `plan_path`, execution only after a cleared preflight, and a
121
+ satisfied exit gate only with zero blocking findings.
122
+
123
+ Args:
124
+ index (int): Zero-based position of this cycle within the
125
+ `remediation_loop.cycles` array, used for error context.
126
+ cycle (dict[str, Any]): The raw cycle object extracted from the
127
+ checkpoint JSON.
128
+
129
+ Returns:
130
+ list[str]: One error string per violated invariant; an empty list when
131
+ the cycle satisfies all three invariants.
132
+
133
+ Raises:
134
+ None.
135
+
136
+ Side Effects:
137
+ None.
138
+ """
139
+
140
+ errors: list[str] = []
141
+
142
+ # Invariant 1: plan_path must be a non-empty, non-whitespace string.
143
+ plan_path = cycle.get("plan_path")
144
+ if not isinstance(plan_path, str) or not plan_path.strip():
145
+ errors.append(
146
+ f"Checkpoint remediation cycle #{index} plan_path must be a "
147
+ "non-empty string."
148
+ )
149
+
150
+ # Invariant 2: an execution status in the blocked set requires that the
151
+ # cycle's preflight gate reports exactly the cleared status.
152
+ execution_status = cycle.get("execution_status")
153
+ if execution_status in EXECUTION_STATUSES_REQUIRING_CLEAR_PREFLIGHT:
154
+ preflight = cycle.get("preflight")
155
+ # Read the nested preflight final status defensively; a missing or
156
+ # non-object preflight cannot satisfy the cleared requirement.
157
+ preflight_status: object = (
158
+ cast("dict[str, Any]", preflight).get("final_status")
159
+ if isinstance(preflight, dict)
160
+ else None
161
+ )
162
+ if preflight_status != PREFLIGHT_CLEARED_STATUS:
163
+ errors.append(
164
+ f"Checkpoint remediation cycle #{index} execution_status is "
165
+ f"{execution_status} but preflight.final_status is not 'clear'."
166
+ )
167
+
168
+ # Invariant 3: a satisfied exit gate requires zero blocking findings.
169
+ if cycle.get("exit_condition_met") is True and cycle.get("blocking_count") != 0:
170
+ errors.append(
171
+ f"Checkpoint remediation cycle #{index} exit_condition_met is true "
172
+ "but blocking_count is not 0."
173
+ )
174
+
175
+ return errors
176
+
177
+
178
+ def _validate_remediation_loop(remediation_loop: object) -> list[str]:
179
+ errors: list[str] = []
180
+
181
+ # A non-object remediation_loop carries no cycles to validate; treat it as
182
+ # nothing to enforce rather than fabricating a structural error here.
183
+ if not isinstance(remediation_loop, dict):
184
+ return errors
185
+ loop_map = cast("dict[str, Any]", remediation_loop)
186
+
187
+ cycles = loop_map.get(REMEDIATION_CYCLES_KEY)
188
+ if not isinstance(cycles, list):
189
+ return errors
190
+ cycle_list = cast("list[object]", cycles)
191
+
192
+ # Validate each cycle independently so callers receive a complete error
193
+ # list instead of stopping at the first malformed cycle.
194
+ for index, cycle in enumerate(cycle_list):
195
+ if not isinstance(cycle, dict):
196
+ errors.append(f"Checkpoint remediation cycle #{index} must be an object.")
197
+ continue
198
+ errors.extend(_validate_remediation_cycle(index, cast("dict[str, Any]", cycle)))
199
+
200
+ return errors
201
+
202
+
203
+ def _missing_object_keys(value: object, keys: tuple[str, ...]) -> list[str]:
204
+ if not isinstance(value, dict):
205
+ return list(keys)
206
+ value_map = cast("dict[str, object]", value)
207
+ missing: list[str] = []
208
+ for key in keys:
209
+ item = value_map.get(key)
210
+ if item is None or (isinstance(item, str) and not item.strip()):
211
+ missing.append(key)
212
+ return missing
213
+
214
+
215
+ def _validate_completion_pr_gate(state: dict[str, Any]) -> list[str]:
216
+ pr_gate = state.get("pr_gate")
217
+ missing = _missing_object_keys(pr_gate, PR_GATE_KEYS)
218
+ if not isinstance(pr_gate, dict):
219
+ return [
220
+ "Checkpoint completion validation failed: pr_gate must be an object "
221
+ f"with keys: {', '.join(PR_GATE_KEYS)}."
222
+ ]
223
+ errors: list[str] = []
224
+ if missing:
225
+ errors.append(
226
+ "Checkpoint completion validation failed: pr_gate missing required "
227
+ f"fields: {', '.join(missing)}."
228
+ )
229
+ if state.get("issue-num") == ISSUE_232:
230
+ head_branch = cast("dict[str, object]", pr_gate).get("head_branch")
231
+ if head_branch != ISSUE_232_BRANCH:
232
+ errors.append(
233
+ "Checkpoint completion validation failed: pr_gate.head_branch "
234
+ f"must be {ISSUE_232_BRANCH} for Issue #232."
235
+ )
236
+ return errors
237
+
238
+
239
+ def _validate_completion_ci_gate(state: dict[str, Any]) -> list[str]:
240
+ ci_gate = state.get("ci_gate")
241
+ missing = _missing_object_keys(ci_gate, CI_GATE_KEYS)
242
+ if not isinstance(ci_gate, dict):
243
+ return [
244
+ "Checkpoint completion validation failed: ci_gate must be an object "
245
+ f"with keys: {', '.join(CI_GATE_KEYS)}."
246
+ ]
247
+ errors: list[str] = []
248
+ if missing:
249
+ errors.append(
250
+ "Checkpoint completion validation failed: ci_gate missing required "
251
+ f"fields: {', '.join(missing)}."
252
+ )
253
+ ci_map = cast("dict[str, object]", ci_gate)
254
+ if ci_map.get("conclusion") != "success":
255
+ errors.append(
256
+ "Checkpoint completion validation failed: ci_gate.conclusion must be "
257
+ "success."
258
+ )
259
+ pr_gate = state.get("pr_gate")
260
+ if isinstance(pr_gate, dict):
261
+ pr_map = cast("dict[str, object]", pr_gate)
262
+ pr_head_sha = pr_map.get("head_sha")
263
+ else:
264
+ pr_head_sha = None
265
+ if pr_head_sha is not None and ci_map.get("head_sha") != pr_head_sha:
266
+ errors.append(
267
+ "Checkpoint completion validation failed: ci_gate.head_sha must match "
268
+ "pr_gate.head_sha."
269
+ )
270
+ return errors
271
+
272
+
273
+ def _validate_issue_232_promotion_receipts(state: dict[str, Any]) -> list[str]:
274
+ if state.get("issue-num") != ISSUE_232:
275
+ return []
276
+ receipts = state.get("delegation_receipts")
277
+ promotion_source = "promotion_receipts"
278
+ promotion: object = state.get("promotion_receipts")
279
+ if isinstance(receipts, dict):
280
+ receipts_map = cast("dict[str, object]", receipts)
281
+ namespaced_promotion = receipts_map.get(PROMOTION_RECEIPT_NAMESPACE_KEY)
282
+ if namespaced_promotion is not None:
283
+ promotion_source = "delegation_receipts.promotion"
284
+ promotion = namespaced_promotion
285
+ missing = _missing_object_keys(promotion, PROMOTION_RECEIPT_KEYS)
286
+ return (
287
+ [
288
+ "Checkpoint completion validation failed: "
289
+ f"{promotion_source} missing required Issue #232 "
290
+ f"receipt keys: {', '.join(missing)}."
291
+ ]
292
+ if missing
293
+ else []
294
+ )
295
+
296
+
297
+ def _validate_list_delegation_receipts(receipts: list[object]) -> list[str]:
298
+ """Validate the legacy list-based delegation receipt payload.
299
+
300
+ Purpose:
301
+ Preserve compatibility with older checkpoints that store delegation
302
+ receipts as a list of receipt objects.
303
+
304
+ Args:
305
+ receipts (list[object]): Raw receipt payload extracted from the
306
+ checkpoint JSON.
307
+
308
+ Returns:
309
+ list[str]: Validation errors for any malformed receipt objects.
310
+
311
+ Raises:
312
+ None.
313
+
314
+ Side Effects:
315
+ None.
316
+ """
317
+
318
+ errors: list[str] = []
319
+
320
+ # Validate each legacy receipt independently so callers receive a complete
321
+ # error list instead of stopping at the first malformed item.
322
+ for index, receipt in enumerate(receipts):
323
+ if not isinstance(receipt, dict):
324
+ errors.append(f"Checkpoint delegation receipt #{index} must be an object.")
325
+ continue
326
+ for key in REQUIRED_RECEIPT_KEYS:
327
+ if key not in receipt:
328
+ errors.append(
329
+ f"Checkpoint delegation receipt #{index} missing key: {key}"
330
+ )
331
+ artifact_paths = cast("dict[str, Any]", receipt).get("artifact_paths")
332
+ if artifact_paths is not None and not isinstance(artifact_paths, list):
333
+ errors.append(
334
+ "Checkpoint delegation receipt "
335
+ f"#{index} artifact_paths must be a list."
336
+ )
337
+
338
+ return errors
339
+
340
+
341
+ def _validate_namespaced_delegation_receipts(receipts: dict[str, Any]) -> list[str]:
342
+ """Validate the additive object namespace form of delegation receipts.
343
+
344
+ Purpose:
345
+ Enforce the reviewed ``delegation_receipts.promotion.*`` contract while
346
+ keeping the raw receipt payloads opaque to the validator.
347
+
348
+ Args:
349
+ receipts (dict[str, Any]): Object-form receipt payload extracted from
350
+ the checkpoint JSON.
351
+
352
+ Returns:
353
+ list[str]: Validation errors for unsupported object-shape keys.
354
+
355
+ Raises:
356
+ None.
357
+
358
+ Side Effects:
359
+ None.
360
+ """
361
+
362
+ errors: list[str] = []
363
+ unsupported_keys = sorted(
364
+ key for key in receipts if key != PROMOTION_RECEIPT_NAMESPACE_KEY
365
+ )
366
+ for key in unsupported_keys:
367
+ errors.append(
368
+ f"Checkpoint delegation_receipts object contains unsupported key: {key}"
369
+ )
370
+
371
+ promotion_receipts = receipts.get(PROMOTION_RECEIPT_NAMESPACE_KEY)
372
+ if promotion_receipts is None:
373
+ return errors
374
+ if not isinstance(promotion_receipts, dict):
375
+ errors.append(
376
+ "Checkpoint delegation_receipts.promotion must be an object namespace."
377
+ )
378
+ return errors
379
+
380
+ promotion_receipt_map = cast("dict[str, Any]", promotion_receipts)
381
+ unsupported_promotion_keys = sorted(
382
+ key for key in promotion_receipt_map if key not in PROMOTION_RECEIPT_KEYS
383
+ )
384
+
385
+ # Reject unknown nested keys while leaving the documented raw receipt values
386
+ # untouched and unnormalized.
387
+ for key in unsupported_promotion_keys:
388
+ errors.append(
389
+ "Checkpoint delegation_receipts.promotion contains unsupported key: "
390
+ f"{key}"
391
+ )
392
+
393
+ return errors
394
+
395
+
396
+ def validate_orchestrator_state_text(
397
+ text: str, *, require_complete: bool = False
398
+ ) -> list[str]:
399
+ """Validate checkpoint schema and completion-state fields.
400
+
401
+ Purpose:
402
+ Enforce the repository contract for orchestrator-state artifacts before
403
+ resume or review workflows rely on the checkpoint contents.
404
+
405
+ Args:
406
+ text (str): Raw checkpoint JSON text.
407
+ require_complete (bool): When True, require all tracked lifecycle states
408
+ to be completion-safe.
409
+
410
+ Returns:
411
+ list[str]: Validation errors for malformed or incomplete checkpoint
412
+ state.
413
+
414
+ Raises:
415
+ None.
416
+
417
+ Side Effects:
418
+ None.
419
+ """
420
+
421
+ errors: list[str] = []
422
+ try:
423
+ state = json.loads(text)
424
+ except json.JSONDecodeError as exc:
425
+ return [f"Checkpoint is not valid JSON: {exc}"]
426
+
427
+ if not isinstance(state, dict):
428
+ return ["Checkpoint root must be a JSON object."]
429
+ state_map = cast("dict[str, Any]", state)
430
+
431
+ # Require the canonical top-level fields before evaluating deeper state and
432
+ # receipt invariants.
433
+ for key in REQUIRED_STATE_KEYS:
434
+ if key not in state_map:
435
+ errors.append(f"Checkpoint missing required key: {key}")
436
+
437
+ for key in (
438
+ "step5_status",
439
+ "step6_status",
440
+ "step7_status",
441
+ "step8_status",
442
+ "step9_status",
443
+ "step10_status",
444
+ ):
445
+ value = state_map.get(key)
446
+ if value is not None and value not in VALID_STEP_STATUS:
447
+ errors.append(f"Checkpoint has invalid {key}: {value}")
448
+
449
+ blocked_reason = state_map.get("blocked_reason")
450
+ if blocked_reason is not None and blocked_reason not in VALID_BLOCKED_REASONS:
451
+ errors.append(f"Checkpoint has invalid blocked_reason: {blocked_reason}")
452
+
453
+ receipts = state_map.get("delegation_receipts")
454
+ if receipts is not None:
455
+ if isinstance(receipts, list):
456
+ errors.extend(
457
+ _validate_list_delegation_receipts(cast("list[object]", receipts))
458
+ )
459
+ elif isinstance(receipts, dict):
460
+ errors.extend(
461
+ _validate_namespaced_delegation_receipts(
462
+ cast("dict[str, Any]", receipts)
463
+ )
464
+ )
465
+ else:
466
+ errors.append(
467
+ "Checkpoint delegation_receipts must be a list or object namespace."
468
+ )
469
+
470
+ # Apply the additive remediation-cycle invariants only when the checkpoint
471
+ # carries a remediation_loop; absent the key, behavior is unchanged.
472
+ if REMEDIATION_LOOP_KEY in state_map:
473
+ errors.extend(_validate_remediation_loop(state_map.get(REMEDIATION_LOOP_KEY)))
474
+
475
+ # Apply the additive human_interaction invariants only when the checkpoint
476
+ # carries a human_interaction key; absent the key, behavior is unchanged.
477
+ if HUMAN_INTERACTION_KEY in state_map:
478
+ errors.extend(_validate_human_interaction(state_map.get(HUMAN_INTERACTION_KEY)))
479
+
480
+ if require_complete:
481
+ # Enforce completion-safe lifecycle states only when the caller opts into
482
+ # the stricter completion gate.
483
+ for key in (
484
+ "step5_status",
485
+ "step6_status",
486
+ "step7_status",
487
+ "step8_status",
488
+ "step9_status",
489
+ "step10_status",
490
+ ):
491
+ value = state_map.get(key)
492
+ if value in {"pending", "blocked"}:
493
+ errors.append(
494
+ f"Checkpoint completion validation failed: {key} is {value}."
495
+ )
496
+ if state_map.get("blocked_reason") not in {None, "none"}:
497
+ errors.append(
498
+ "Checkpoint completion validation failed: blocked_reason is not `none`."
499
+ )
500
+ errors.extend(_validate_completion_pr_gate(state_map))
501
+ errors.extend(_validate_completion_ci_gate(state_map))
502
+ errors.extend(_validate_issue_232_promotion_receipts(state_map))
503
+ errors.extend(validate_routing_contract(state_map))
504
+
505
+ return errors