@danmoisan/drm-copilot-mcp 1.0.13 → 1.0.15

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 (131) hide show
  1. package/out/mcp-server.js +3160 -458
  2. package/package.json +1 -1
  3. package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +24 -0
  4. package/resources/claude-customizations/.claude/agents/epic-planner.md +121 -0
  5. package/resources/claude-customizations/.claude/agents/orchestrator.md +2 -2
  6. package/resources/claude-customizations/.claude/hooks/enforce-epic-invocation-origin.ps1 +246 -0
  7. package/resources/claude-customizations/.claude/settings.json +9 -2
  8. package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +7 -0
  9. package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +189 -0
  10. package/resources/claude-customizations/.claude/skills/epic-run/SKILL.md +38 -0
  11. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +21 -2
  12. package/resources/claude-customizations/pack-manifests/core.json +4 -0
  13. package/resources/codex-and-agents-customizations/.agents/skills/architecture-boundaries/SKILL.md +0 -1
  14. package/resources/codex-and-agents-customizations/.agents/skills/codex-model-routing/SKILL.md +100 -0
  15. package/resources/codex-and-agents-customizations/.agents/skills/codex-model-routing/agents/openai.yaml +4 -0
  16. package/resources/codex-and-agents-customizations/.agents/skills/epic-orchestrate/SKILL.md +170 -0
  17. package/resources/codex-and-agents-customizations/.agents/skills/epic-plan/SKILL.md +230 -0
  18. package/resources/codex-and-agents-customizations/.agents/skills/epic-run/SKILL.md +39 -0
  19. package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +0 -1
  20. package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +0 -1
  21. package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +89 -9
  22. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +39 -4
  23. package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +0 -1
  24. package/resources/codex-and-agents-customizations/.agents/skills/tonality/SKILL.md +0 -1
  25. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c1.toml +153 -0
  26. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +153 -0
  27. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +153 -0
  28. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +153 -0
  29. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +153 -0
  30. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +2 -0
  31. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +95 -0
  32. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +95 -0
  33. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +95 -0
  34. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +95 -0
  35. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +95 -0
  36. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +2 -0
  37. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c1.toml +99 -0
  38. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c2.toml +99 -0
  39. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3-elevated.toml +99 -0
  40. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3.toml +99 -0
  41. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c4.toml +99 -0
  42. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer.toml +2 -0
  43. package/resources/codex-and-agents-customizations/.codex/agents/epic-orchestrator.toml +96 -0
  44. package/resources/codex-and-agents-customizations/.codex/agents/epic-planner.toml +97 -0
  45. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c1.toml +62 -0
  46. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c2.toml +62 -0
  47. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c3-elevated.toml +62 -0
  48. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c3.toml +62 -0
  49. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c4.toml +62 -0
  50. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer.toml +2 -0
  51. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c1.toml +200 -0
  52. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +200 -0
  53. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +200 -0
  54. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +200 -0
  55. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +200 -0
  56. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +53 -3
  57. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c1.toml +110 -0
  58. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c2.toml +110 -0
  59. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3-elevated.toml +110 -0
  60. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3.toml +110 -0
  61. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c4.toml +110 -0
  62. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer.toml +2 -0
  63. package/resources/codex-and-agents-customizations/.codex/agents/pr-author-c1.toml +28 -0
  64. package/resources/codex-and-agents-customizations/.codex/agents/pr-author-c2.toml +28 -0
  65. package/resources/codex-and-agents-customizations/.codex/agents/pr-author-c3-elevated.toml +28 -0
  66. package/resources/codex-and-agents-customizations/.codex/agents/pr-author-c3.toml +28 -0
  67. package/resources/codex-and-agents-customizations/.codex/agents/pr-author-c4.toml +28 -0
  68. package/resources/codex-and-agents-customizations/.codex/agents/pr-author.toml +2 -0
  69. package/resources/codex-and-agents-customizations/.codex/agents/prd-feature-c1.toml +55 -0
  70. package/resources/codex-and-agents-customizations/.codex/agents/prd-feature-c2.toml +55 -0
  71. package/resources/codex-and-agents-customizations/.codex/agents/prd-feature-c3-elevated.toml +55 -0
  72. package/resources/codex-and-agents-customizations/.codex/agents/prd-feature-c3.toml +55 -0
  73. package/resources/codex-and-agents-customizations/.codex/agents/prd-feature-c4.toml +55 -0
  74. package/resources/codex-and-agents-customizations/.codex/agents/prd-feature.toml +2 -0
  75. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c1.toml +102 -0
  76. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c2.toml +102 -0
  77. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3-elevated.toml +102 -0
  78. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3.toml +102 -0
  79. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c4.toml +102 -0
  80. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer.toml +2 -0
  81. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c1.toml +105 -0
  82. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c2.toml +105 -0
  83. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c3-elevated.toml +105 -0
  84. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c3.toml +105 -0
  85. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c4.toml +105 -0
  86. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher.toml +2 -0
  87. package/resources/codex-and-agents-customizations/.codex/agents/typescript-engineer-c1.toml +50 -0
  88. package/resources/codex-and-agents-customizations/.codex/agents/typescript-engineer-c2.toml +50 -0
  89. package/resources/codex-and-agents-customizations/.codex/agents/typescript-engineer-c3-elevated.toml +50 -0
  90. package/resources/codex-and-agents-customizations/.codex/agents/typescript-engineer-c3.toml +50 -0
  91. package/resources/codex-and-agents-customizations/.codex/agents/typescript-engineer-c4.toml +50 -0
  92. package/resources/codex-and-agents-customizations/.codex/agents/typescript-engineer.toml +2 -0
  93. package/resources/codex-and-agents-customizations/.codex/config.toml +202 -37
  94. package/resources/codex-and-agents-customizations/.codex/hooks/authorize-root-epic-invocation.ps1 +236 -0
  95. package/resources/codex-and-agents-customizations/.codex/hooks/check-powershell-test-purity.ps1 +178 -69
  96. package/resources/codex-and-agents-customizations/.codex/hooks/check-python-test-purity.ps1 +92 -19
  97. package/resources/codex-and-agents-customizations/.codex/hooks/codex-agent-profile-attestation.ps1 +199 -0
  98. package/resources/codex-and-agents-customizations/.codex/hooks/codex-authority-store.ps1 +182 -0
  99. package/resources/codex-and-agents-customizations/.codex/hooks/codex-epic-child-launch-attestation.ps1 +129 -0
  100. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-checkpoint-monotonic.ps1 +148 -31
  101. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-codex-model-routing.ps1 +198 -0
  102. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +28 -19
  103. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-child-worktree-binding.ps1 +325 -0
  104. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +140 -0
  105. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +292 -0
  106. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-root-invocation.ps1 +135 -0
  107. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-wave-barrier.ps1 +295 -0
  108. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-worktree-removal-gate.ps1 +151 -0
  109. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-evidence-locations.ps1 +99 -34
  110. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +143 -27
  111. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +88 -43
  112. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +65 -20
  113. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +88 -42
  114. package/resources/codex-and-agents-customizations/.codex/hooks/record-subagent-routing-attestation.ps1 +414 -0
  115. package/resources/codex-and-agents-customizations/.codex/hooks/validate-bash.ps1 +164 -51
  116. package/resources/codex-and-agents-customizations/.codex/hooks/validate-codex-subagent-routing.ps1 +154 -0
  117. package/resources/codex-and-agents-customizations/.codex/hooks/validate-feature-review-coverage.ps1 +58 -23
  118. package/resources/codex-and-agents-customizations/AGENTS.md +27 -0
  119. package/resources/codex-and-agents-customizations/pack-manifests/core.json +66 -1
  120. package/resources/codex-and-agents-customizations/pack-manifests/csharp-legacy.json +6 -1
  121. package/resources/codex-and-agents-customizations/pack-manifests/csharp-modern.json +6 -1
  122. package/resources/codex-and-agents-customizations/pack-manifests/powershell.json +6 -1
  123. package/resources/codex-and-agents-customizations/pack-manifests/python.json +6 -1
  124. package/resources/codex-and-agents-customizations/pack-manifests/typescript.json +6 -1
  125. package/resources/config/orchestration-routing.json +167 -0
  126. package/resources/powershell/PoshQC/PoshQC.Analyzer.psm1 +1 -1
  127. package/resources/powershell/PoshQC/PoshQC.ScanConfig.psm1 +125 -0
  128. package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +14 -2
  129. package/resources/powershell/PoshQC/PoshQC.psd1 +0 -4
  130. package/resources/powershell/PoshQC/PoshQC.psm1 +26 -6
  131. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +34 -18
package/out/mcp-server.js CHANGED
@@ -8964,13 +8964,13 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
8964
8964
  }
8965
8965
  return propValues;
8966
8966
  });
8967
- const isObject9 = isObject;
8967
+ const isObject14 = isObject;
8968
8968
  const catchall = def.catchall;
8969
8969
  let value;
8970
8970
  inst._zod.parse = (payload, ctx) => {
8971
8971
  value ?? (value = _normalized.value);
8972
8972
  const input = payload.value;
8973
- if (!isObject9(input)) {
8973
+ if (!isObject14(input)) {
8974
8974
  payload.issues.push({
8975
8975
  expected: "object",
8976
8976
  code: "invalid_type",
@@ -9097,7 +9097,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
9097
9097
  return (payload, ctx) => fn(shape, payload, ctx);
9098
9098
  };
9099
9099
  let fastpass;
9100
- const isObject9 = isObject;
9100
+ const isObject14 = isObject;
9101
9101
  const jit = !globalConfig.jitless;
9102
9102
  const allowsEval2 = allowsEval;
9103
9103
  const fastEnabled = jit && allowsEval2.value;
@@ -9106,7 +9106,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
9106
9106
  inst._zod.parse = (payload, ctx) => {
9107
9107
  value ?? (value = _normalized.value);
9108
9108
  const input = payload.value;
9109
- if (!isObject9(input)) {
9109
+ if (!isObject14(input)) {
9110
9110
  payload.issues.push({
9111
9111
  expected: "object",
9112
9112
  code: "invalid_type",
@@ -16096,7 +16096,7 @@ var REPO_AUTOMATION_TOOL_DEFINITIONS = [
16096
16096
  },
16097
16097
  {
16098
16098
  name: "run_poshqc_test",
16099
- description: "Run bundled PoshQC Pester checks against the target workspace using bundled extension resources.",
16099
+ description: "Run bundled PoshQC Pester checks against the target workspace using bundled extension resources. When scan_folders is omitted, the scan set is resolved from config/poshqc-scan.json (test.scanFolders); an explicit scan_folders argument overrides the configuration.",
16100
16100
  inputSchema: {
16101
16101
  type: "object",
16102
16102
  properties: {
@@ -16203,7 +16203,7 @@ var REPO_AUTOMATION_TOOL_DEFINITIONS = [
16203
16203
  },
16204
16204
  {
16205
16205
  name: "validate_orchestration_artifacts",
16206
- description: "Validate an orchestration artifact (plan, policy-audit, code-review, feature-audit, or orchestrator-state) against its structural schema.",
16206
+ description: "Validate an orchestration artifact, including epic planner, kickoff, and execution checkpoints, against its structural schema.",
16207
16207
  inputSchema: {
16208
16208
  type: "object",
16209
16209
  properties: {
@@ -16215,7 +16215,10 @@ var REPO_AUTOMATION_TOOL_DEFINITIONS = [
16215
16215
  "policy-audit",
16216
16216
  "code-review",
16217
16217
  "feature-audit",
16218
- "orchestrator-state"
16218
+ "orchestrator-state",
16219
+ "epic-orchestrator-state",
16220
+ "epic-planner-state",
16221
+ "epic-kickoff"
16219
16222
  ],
16220
16223
  description: "The type of orchestration artifact to validate."
16221
16224
  },
@@ -16225,11 +16228,23 @@ var REPO_AUTOMATION_TOOL_DEFINITIONS = [
16225
16228
  },
16226
16229
  require_complete: {
16227
16230
  type: "boolean",
16228
- description: "When true and artifact_type is 'orchestrator-state', require all phases to be complete."
16231
+ description: "When true and artifact_type is 'orchestrator-state' or 'epic-orchestrator-state', require all phases to be complete."
16229
16232
  },
16230
16233
  require_model_routing: {
16231
16234
  type: "boolean",
16232
16235
  description: "When true and artifact_type is 'orchestrator-state', require a model_routing_receipts entry per delegated agent once a delegation is recorded. The TypeScript side performs the existence check only; the Python validator is authoritative for full per-receipt correctness."
16236
+ },
16237
+ require_codex_model_routing: {
16238
+ type: "boolean",
16239
+ description: "When true for an orchestrator checkpoint, require canonical Codex deployment receipts for delegated agents."
16240
+ },
16241
+ require_codex_topology: {
16242
+ type: "boolean",
16243
+ description: "When true for an orchestrator checkpoint, require canonical Codex topology receipts for delegated agents and epic roots."
16244
+ },
16245
+ require_ready_for_execution: {
16246
+ type: "boolean",
16247
+ description: "When true and artifact_type is 'epic-planner-state', require every child to be prepared and preflight-cleared."
16233
16248
  }
16234
16249
  },
16235
16250
  required: ["artifact_type", "artifact_path"],
@@ -16622,7 +16637,10 @@ var VALID_ARTIFACT_TYPES = /* @__PURE__ */ new Set([
16622
16637
  "policy-audit",
16623
16638
  "code-review",
16624
16639
  "feature-audit",
16625
- "orchestrator-state"
16640
+ "orchestrator-state",
16641
+ "epic-orchestrator-state",
16642
+ "epic-planner-state",
16643
+ "epic-kickoff"
16626
16644
  ]);
16627
16645
  function resolveValidateOrchestrationArtifactsToolInput(rawInput, fallbackWorkspaceRoot) {
16628
16646
  const args = asToolArgumentObject(rawInput);
@@ -16637,6 +16655,9 @@ function resolveValidateOrchestrationArtifactsToolInput(rawInput, fallbackWorksp
16637
16655
  }
16638
16656
  const requireComplete = args["require_complete"];
16639
16657
  const requireModelRouting = args["require_model_routing"];
16658
+ const requireCodexModelRouting = args["require_codex_model_routing"];
16659
+ const requireCodexTopology = args["require_codex_topology"];
16660
+ const requireReadyForExecution = args["require_ready_for_execution"];
16640
16661
  return {
16641
16662
  workspaceRoot: normalizeWorkspaceRoot(
16642
16663
  args["workspace_root"],
@@ -16645,7 +16666,10 @@ function resolveValidateOrchestrationArtifactsToolInput(rawInput, fallbackWorksp
16645
16666
  artifactType,
16646
16667
  artifactPath: normalizeRequiredText(args["artifact_path"], "artifact_path"),
16647
16668
  ...requireComplete === true ? { requireComplete: true } : {},
16648
- ...requireModelRouting === true ? { requireModelRouting: true } : {}
16669
+ ...requireModelRouting === true ? { requireModelRouting: true } : {},
16670
+ ...requireCodexModelRouting === true ? { requireCodexModelRouting: true } : {},
16671
+ ...requireCodexTopology === true ? { requireCodexTopology: true } : {},
16672
+ ...requireReadyForExecution === true ? { requireReadyForExecution: true } : {}
16649
16673
  };
16650
16674
  }
16651
16675
 
@@ -17510,11 +17534,11 @@ function pushDownCustomizations(options) {
17510
17534
  effectiveSource,
17511
17535
  effectiveRoots
17512
17536
  )) {
17513
- const relativePath = relativeToPosix(sourcePath, effectiveSource);
17514
- if (relativePath === null) {
17537
+ const relativePath2 = relativeToPosix(sourcePath, effectiveSource);
17538
+ if (relativePath2 === null) {
17515
17539
  continue;
17516
17540
  }
17517
- const destinationPath = joinPosix2(destinationRoot, relativePath);
17541
+ const destinationPath = joinPosix2(destinationRoot, relativePath2);
17518
17542
  const destinationStatus = fs9.isFile(
17519
17543
  destinationPath
17520
17544
  ) ? "overwritten" : "created";
@@ -17539,7 +17563,7 @@ function pushDownCustomizations(options) {
17539
17563
  fs9.ensureDir(destParent);
17540
17564
  fs9.writeTextFile(destinationPath, rewrittenText);
17541
17565
  fileResults.push({
17542
- relativePath,
17566
+ relativePath: relativePath2,
17543
17567
  destinationStatus,
17544
17568
  rewrittenReferenceCount: rewrittenDelta,
17545
17569
  placeholderRewriteCount: placeholderDelta,
@@ -17774,6 +17798,11 @@ function assertSingleCsharpToolchain(publishedPaths, selectedPackNames) {
17774
17798
  var ARTIFACT_DIRECTORY3 = "artifacts/codex-and-agents-customizations";
17775
17799
  var ROOT_FOLDERS = [".codex", ".agents"];
17776
17800
  var PACK_MANIFEST_SUBDIR = "pack-manifests";
17801
+ var ROUTING_CONFIG_RELATIVE_PATH = "config/orchestration-routing.json";
17802
+ var PUBLISHED_ROOT_FOLDERS = [
17803
+ ...ROOT_FOLDERS,
17804
+ "config"
17805
+ ];
17777
17806
  function passthroughRewrite(text) {
17778
17807
  return [text, 0, 0, []];
17779
17808
  }
@@ -17785,6 +17814,14 @@ function joinPosix4(root, relative2) {
17785
17814
  const normalizedRelative = relative2.replace(/\\/g, "/").replace(/^\/+/, "");
17786
17815
  return normalizedRoot === "" ? normalizedRelative : `${normalizedRoot}/${normalizedRelative}`;
17787
17816
  }
17817
+ function parentPosix(path11) {
17818
+ const normalized = normalizePosix(path11);
17819
+ const separatorIndex = normalized.lastIndexOf("/");
17820
+ if (separatorIndex < 0) {
17821
+ return "";
17822
+ }
17823
+ return separatorIndex === 0 ? "/" : normalized.slice(0, separatorIndex);
17824
+ }
17788
17825
  function relativeToPosix2(path11, root) {
17789
17826
  const normalizedPath = normalizePosix(path11);
17790
17827
  const normalizedRoot = normalizePosix(root);
@@ -17847,6 +17884,47 @@ var CodexFilteringFileSystem = class {
17847
17884
  this.inner.ensureDir(path11);
17848
17885
  }
17849
17886
  };
17887
+ var RoutingConfigFileSystem = class {
17888
+ constructor(inner, options) {
17889
+ this.inner = inner;
17890
+ this.virtualPath = joinPosix4(
17891
+ options.sourceRoot,
17892
+ ROUTING_CONFIG_RELATIVE_PATH
17893
+ );
17894
+ this.virtualRoot = joinPosix4(options.sourceRoot, "config");
17895
+ this.resourcePath = joinPosix4(
17896
+ parentPosix(options.bundleRoot),
17897
+ ROUTING_CONFIG_RELATIVE_PATH
17898
+ );
17899
+ }
17900
+ inner;
17901
+ virtualPath;
17902
+ virtualRoot;
17903
+ resourcePath;
17904
+ listFiles(root) {
17905
+ if (normalizePosix(root) === this.virtualRoot) {
17906
+ return this.inner.isFile(this.resourcePath) ? [this.virtualPath] : [];
17907
+ }
17908
+ return this.inner.listFiles(root);
17909
+ }
17910
+ isDir(path11) {
17911
+ return this.inner.isDir(path11);
17912
+ }
17913
+ isFile(path11) {
17914
+ return normalizePosix(path11) === this.virtualPath ? this.inner.isFile(this.resourcePath) : this.inner.isFile(path11);
17915
+ }
17916
+ readTextFile(path11) {
17917
+ return this.inner.readTextFile(
17918
+ normalizePosix(path11) === this.virtualPath ? this.resourcePath : path11
17919
+ );
17920
+ }
17921
+ writeTextFile(path11, content) {
17922
+ this.inner.writeTextFile(path11, content);
17923
+ }
17924
+ ensureDir(path11) {
17925
+ this.inner.ensureDir(path11);
17926
+ }
17927
+ };
17850
17928
  function resolvePublishedPaths(packs, csharpVariant, bundleRoot, fs9) {
17851
17929
  const manifestPacks = resolveManifestPackNames(packs ?? null, csharpVariant);
17852
17930
  if (manifestPacks === null || manifestPacks.size === 0) {
@@ -17877,14 +17955,18 @@ function pushDownCustomizations3(options) {
17877
17955
  publishedPaths,
17878
17956
  csharpVariant
17879
17957
  });
17958
+ const publishingFs = new RoutingConfigFileSystem(filteringFs, {
17959
+ sourceRoot,
17960
+ bundleRoot
17961
+ });
17880
17962
  void options.memoryMode;
17881
17963
  return pushDownCustomizations({
17882
17964
  repoRoot: options.repoRoot,
17883
17965
  destinationRoot: options.destinationRoot,
17884
- fs: filteringFs,
17966
+ fs: publishingFs,
17885
17967
  sourceRoot,
17886
17968
  ...options.artifactRoot === void 0 ? {} : { artifactRoot: options.artifactRoot },
17887
- rootFolders: ROOT_FOLDERS,
17969
+ rootFolders: PUBLISHED_ROOT_FOLDERS,
17888
17970
  artifactDirectory: ARTIFACT_DIRECTORY3,
17889
17971
  rewriteReferences: passthroughRewrite,
17890
17972
  ...options.clock === void 0 ? {} : { clock: options.clock }
@@ -18155,15 +18237,15 @@ var ExcludingFileSystem = class {
18155
18237
  * @returns True when the file is outside the memory subtree or general-scoped.
18156
18238
  */
18157
18239
  isScopeIncluded(path11) {
18158
- const relativePath = relativeToPosix3(path11, this.repoRoot);
18159
- if (relativePath === null) {
18240
+ const relativePath2 = relativeToPosix3(path11, this.repoRoot);
18241
+ if (relativePath2 === null) {
18160
18242
  return true;
18161
18243
  }
18162
- if (!isUnderAgentMemory(relativePath)) {
18244
+ if (!isUnderAgentMemory(relativePath2)) {
18163
18245
  return true;
18164
18246
  }
18165
18247
  const content = this.readText(path11);
18166
- return isGeneralMemoryFile(relativePath, content);
18248
+ return isGeneralMemoryFile(relativePath2, content);
18167
18249
  }
18168
18250
  /**
18169
18251
  * Return whether a candidate file passes the memory-mode filter.
@@ -18575,12 +18657,12 @@ var RealFileSystem = class {
18575
18657
  return;
18576
18658
  }
18577
18659
  for (const entry of entries) {
18578
- const relativePath = relativePrefix === "" ? entry.name : `${relativePrefix}/${entry.name}`;
18579
- if (matcher.test(relativePath)) {
18580
- matches.push(joinPosix8(normalizedRoot, relativePath));
18660
+ const relativePath2 = relativePrefix === "" ? entry.name : `${relativePrefix}/${entry.name}`;
18661
+ if (matcher.test(relativePath2)) {
18662
+ matches.push(joinPosix8(normalizedRoot, relativePath2));
18581
18663
  }
18582
18664
  if (entry.isDirectory()) {
18583
- walk(nodePath3.join(currentDir, entry.name), relativePath);
18665
+ walk(nodePath3.join(currentDir, entry.name), relativePath2);
18584
18666
  }
18585
18667
  }
18586
18668
  };
@@ -21865,6 +21947,243 @@ var SubprocessRunner = class {
21865
21947
  // ../../extensions/drm-copilot/src/lib/validate/validate-orchestration-service-call.ts
21866
21948
  var path9 = __toESM(require("node:path"));
21867
21949
 
21950
+ // ../../extensions/drm-copilot/src/lib/validate/epic-kickoff-artifact.ts
21951
+ var KICKOFF_HEADING_RE = /^# Epic Kickoff: (?<slug>[a-z0-9][a-z0-9-]*)$/;
21952
+ var EPIC_RUN_RE = /Run `\/epic-run (?<slug>[a-z0-9][a-z0-9-]*)`/;
21953
+ var MANIFEST_RE = /docs\/features\/epics\/[a-z0-9][a-z0-9-]*\/epic\.md/;
21954
+ var INTEGRATION_BRANCH_RE = /epic\/[a-z0-9][a-z0-9-]*-integration/;
21955
+ var RESUME_RE = /(?:Every child|child features)\s+resumes?\s+at atomic execution\s+from\s+(?:its|their)\s+committed plan-path/i;
21956
+ var INTEGRITY_COMMIT_RE = /^(?:-\s*)?planning_commit:\s*`?(?<commit>[0-9a-fA-F]{7,64})`?\s*$/;
21957
+ var FEATURE_HEADERS = [
21958
+ "issue_num",
21959
+ "feature_folder",
21960
+ "wave",
21961
+ "complexity",
21962
+ "plan-path"
21963
+ ];
21964
+ var HASH_HEADERS = /* @__PURE__ */ new Set([
21965
+ "plan-hash",
21966
+ "plan_hash",
21967
+ "git-blob-sha",
21968
+ "git_blob_sha"
21969
+ ]);
21970
+ function splitSections2(text) {
21971
+ const sections = /* @__PURE__ */ new Map();
21972
+ const errors = [];
21973
+ let current;
21974
+ for (const line of text.split("\n").slice(1)) {
21975
+ if (line.startsWith("## ")) {
21976
+ current = line.slice(3).trim();
21977
+ if (sections.has(current)) {
21978
+ errors.push(`Epic kickoff contains duplicate section: ## ${current}`);
21979
+ } else {
21980
+ sections.set(current, []);
21981
+ }
21982
+ continue;
21983
+ }
21984
+ if (current !== void 0) {
21985
+ sections.get(current)?.push(line.replace(/\r$/, ""));
21986
+ }
21987
+ }
21988
+ for (const required2 of ["Invocation Prompt", "Feature Summary"]) {
21989
+ if (!sections.has(required2)) {
21990
+ errors.push(`Epic kickoff is missing required section: ## ${required2}`);
21991
+ }
21992
+ }
21993
+ return { sections, errors };
21994
+ }
21995
+ function parseCells(line) {
21996
+ const stripped = line.trim();
21997
+ if (!stripped.startsWith("|") || !stripped.endsWith("|")) {
21998
+ return void 0;
21999
+ }
22000
+ return stripped.slice(1, -1).split("|").map((cell) => cell.trim().replace(/^`|`$/g, ""));
22001
+ }
22002
+ function isSeparator(cells) {
22003
+ return cells.length > 0 && cells.every((cell) => /^:?-{3,}:?$/.test(cell));
22004
+ }
22005
+ function tableRows(lines, expectedHeaders) {
22006
+ const nonempty = lines.filter((line) => line.trim().length > 0);
22007
+ if (nonempty.length < 2) {
22008
+ return {
22009
+ rows: [],
22010
+ errors: ["Epic kickoff table is missing its header or separator row."]
22011
+ };
22012
+ }
22013
+ const headers = parseCells(nonempty[0] ?? "");
22014
+ const separator = parseCells(nonempty[1] ?? "");
22015
+ const errors = [];
22016
+ if (headers === void 0 || headers.length !== expectedHeaders.length || headers.some((value, index) => value !== expectedHeaders[index])) {
22017
+ errors.push(
22018
+ `Epic kickoff feature table headers must be: ${expectedHeaders.join(" | ")}`
22019
+ );
22020
+ }
22021
+ if (separator === void 0 || separator.length !== expectedHeaders.length || !isSeparator(separator)) {
22022
+ errors.push("Epic kickoff table separator row is invalid.");
22023
+ }
22024
+ const rows = [];
22025
+ for (const line of nonempty.slice(2)) {
22026
+ const cells = parseCells(line);
22027
+ if (cells === void 0 || cells.length !== expectedHeaders.length) {
22028
+ errors.push(`Epic kickoff table row is invalid: ${line}`);
22029
+ } else {
22030
+ rows.push(cells);
22031
+ }
22032
+ }
22033
+ if (rows.length === 0) {
22034
+ errors.push(
22035
+ "Epic kickoff feature table must contain at least one feature row."
22036
+ );
22037
+ }
22038
+ return { rows, errors };
22039
+ }
22040
+ function parseFeatures(lines) {
22041
+ const table = tableRows(lines, FEATURE_HEADERS);
22042
+ const features = [];
22043
+ table.rows.forEach((row, index) => {
22044
+ const issueNum = Number(row[0]);
22045
+ if (!Number.isInteger(issueNum)) {
22046
+ table.errors.push(
22047
+ `Epic kickoff feature row ${index} issue_num must be an integer.`
22048
+ );
22049
+ return;
22050
+ }
22051
+ const wave = Number(row[2]);
22052
+ if (!Number.isInteger(wave)) {
22053
+ table.errors.push(
22054
+ `Epic kickoff feature row ${index} wave must be an integer.`
22055
+ );
22056
+ return;
22057
+ }
22058
+ const complexity = row[3] ?? "";
22059
+ if (!(/* @__PURE__ */ new Set(["C1", "C2", "C3", "C4"])).has(complexity)) {
22060
+ table.errors.push(
22061
+ `Epic kickoff feature row ${index} complexity must be C1-C4.`
22062
+ );
22063
+ }
22064
+ features.push({
22065
+ issueNum,
22066
+ featureFolder: row[1] ?? "",
22067
+ wave,
22068
+ complexity,
22069
+ planPath: row[4] ?? ""
22070
+ });
22071
+ });
22072
+ return { features, errors: table.errors };
22073
+ }
22074
+ function parseIntegrity(lines) {
22075
+ let planningCommit;
22076
+ const planHashes = {};
22077
+ const errors = [];
22078
+ const tableLines = [];
22079
+ for (const line of lines) {
22080
+ if (line.trim().length === 0) {
22081
+ continue;
22082
+ }
22083
+ const match = INTEGRITY_COMMIT_RE.exec(line.trim());
22084
+ if (match?.groups?.["commit"] !== void 0) {
22085
+ if (planningCommit !== void 0) {
22086
+ errors.push(
22087
+ "Epic kickoff integrity has duplicate planning_commit fields."
22088
+ );
22089
+ }
22090
+ planningCommit = match.groups["commit"].toLowerCase();
22091
+ } else if (line.trim().startsWith("|")) {
22092
+ tableLines.push(line);
22093
+ } else {
22094
+ errors.push(`Epic kickoff integrity line is invalid: ${line}`);
22095
+ }
22096
+ }
22097
+ if (tableLines.length > 0) {
22098
+ const header = parseCells(tableLines[0] ?? "");
22099
+ if (header === void 0 || header.length !== 2 || header[0] !== "plan-path" || !HASH_HEADERS.has(header[1] ?? "")) {
22100
+ errors.push(
22101
+ "Epic kickoff integrity table headers must be plan-path and plan-hash."
22102
+ );
22103
+ } else if (tableLines.length < 2) {
22104
+ errors.push("Epic kickoff integrity table is missing its separator row.");
22105
+ } else {
22106
+ const separator = parseCells(tableLines[1] ?? "");
22107
+ if (separator === void 0 || separator.length !== 2 || !isSeparator(separator)) {
22108
+ errors.push("Epic kickoff integrity table separator row is invalid.");
22109
+ }
22110
+ for (const line of tableLines.slice(2)) {
22111
+ const cells = parseCells(line);
22112
+ if (cells === void 0 || cells.length !== 2 || !/^[0-9a-fA-F]{40,64}$/.test(cells[1] ?? "")) {
22113
+ errors.push(`Epic kickoff integrity table row is invalid: ${line}`);
22114
+ continue;
22115
+ }
22116
+ const planPath = cells[0] ?? "";
22117
+ if (planHashes[planPath] !== void 0) {
22118
+ errors.push(
22119
+ `Epic kickoff integrity repeats plan path: '${planPath}'.`
22120
+ );
22121
+ }
22122
+ planHashes[planPath] = (cells[1] ?? "").toLowerCase();
22123
+ }
22124
+ }
22125
+ }
22126
+ return {
22127
+ ...planningCommit === void 0 ? {} : { planningCommit },
22128
+ planHashes,
22129
+ errors
22130
+ };
22131
+ }
22132
+ function parseEpicKickoff(text) {
22133
+ const lines = text.split("\n");
22134
+ if (text.length === 0) {
22135
+ return { errors: ["Epic kickoff is empty."] };
22136
+ }
22137
+ const heading = KICKOFF_HEADING_RE.exec((lines[0] ?? "").replace(/\r$/, ""));
22138
+ if (heading?.groups?.["slug"] === void 0) {
22139
+ return {
22140
+ errors: ["Epic kickoff first line must match '# Epic Kickoff: <slug>'."]
22141
+ };
22142
+ }
22143
+ const sectionResult = splitSections2(text);
22144
+ const invocation = (sectionResult.sections.get("Invocation Prompt") ?? []).join("\n");
22145
+ const invocationSlug = EPIC_RUN_RE.exec(invocation)?.groups?.["slug"];
22146
+ const manifestMatch = MANIFEST_RE.exec(invocation);
22147
+ const branchMatch = INTEGRATION_BRANCH_RE.exec(invocation);
22148
+ const resumeMatch = RESUME_RE.exec(invocation);
22149
+ if (invocationSlug === void 0) {
22150
+ sectionResult.errors.push(
22151
+ "Epic kickoff invocation must contain `Run /epic-run <slug>`."
22152
+ );
22153
+ }
22154
+ if (manifestMatch?.[0] === void 0 || branchMatch?.[0] === void 0 || resumeMatch === null) {
22155
+ sectionResult.errors.push(
22156
+ "Epic kickoff invocation must structurally name the manifest, integration branch, and atomic-execution resume boundary."
22157
+ );
22158
+ }
22159
+ const featureResult = parseFeatures(
22160
+ sectionResult.sections.get("Feature Summary") ?? []
22161
+ );
22162
+ sectionResult.errors.push(...featureResult.errors);
22163
+ const integrity = parseIntegrity(
22164
+ sectionResult.sections.get("Integrity") ?? []
22165
+ );
22166
+ sectionResult.errors.push(...integrity.errors);
22167
+ if (sectionResult.errors.length > 0 || invocationSlug === void 0 || manifestMatch?.[0] === void 0 || branchMatch?.[0] === void 0 || resumeMatch === null) {
22168
+ return { errors: sectionResult.errors };
22169
+ }
22170
+ return {
22171
+ parsed: {
22172
+ slug: heading.groups["slug"],
22173
+ invocationSlug,
22174
+ manifestPath: manifestMatch[0],
22175
+ integrationBranch: branchMatch[0],
22176
+ features: featureResult.features,
22177
+ ...integrity.planningCommit === void 0 ? {} : { planningCommit: integrity.planningCommit },
22178
+ planHashes: integrity.planHashes
22179
+ },
22180
+ errors: []
22181
+ };
22182
+ }
22183
+ function validateEpicKickoffText(text) {
22184
+ return parseEpicKickoff(text).errors;
22185
+ }
22186
+
21868
22187
  // ../../extensions/drm-copilot/src/lib/validate/epic-orchestrator-state-resolution.ts
21869
22188
  var LIFECYCLE_PREFIXES = [
21870
22189
  "docs/features/active/",
@@ -22002,143 +22321,1162 @@ function validateIntentBlock(state) {
22002
22321
  return errors;
22003
22322
  }
22004
22323
 
22005
- // ../../extensions/drm-copilot/src/lib/validate/epic-orchestrator-state-core.ts
22006
- var REQUIRED_BASELINE_KEYS = [
22007
- "objective",
22008
- "completed_steps",
22009
- "next_step",
22010
- "last_updated"
22011
- ];
22012
- var REQUIRED_EPIC_KEYS = [
22013
- "route_id",
22014
- "epic_feature_folder",
22015
- "integration_branch",
22016
- "waves",
22017
- "features"
22324
+ // ../../extensions/drm-copilot/src/lib/validate/epic-orchestrator-state-launch-binding.ts
22325
+ var LAUNCH_ARTIFACT_PARTS = [
22326
+ "artifacts",
22327
+ "orchestration",
22328
+ "epic-child-launches"
22018
22329
  ];
22019
- var EXPECTED_ROUTE_ID = "epic";
22020
- var VALID_MERGE_STATUS = /* @__PURE__ */ new Set([
22021
- "not_started",
22022
- "worktree_created",
22023
- "pr_open",
22024
- "ci_green",
22025
- "merge_conflict",
22026
- "blocked_conflict_loop_limit",
22027
- "merged",
22028
- "worktree_removed"
22029
- ]);
22030
- var MERGED_STATUSES = /* @__PURE__ */ new Set([
22031
- "merged",
22032
- "worktree_removed"
22330
+ var GENERATED_ORCHESTRATOR_AGENTS = /* @__PURE__ */ new Set([
22331
+ "orchestrator-c1",
22332
+ "orchestrator-c2",
22333
+ "orchestrator-c3",
22334
+ "orchestrator-c3-elevated",
22335
+ "orchestrator-c4"
22033
22336
  ]);
22034
22337
  function isObject3(value) {
22035
22338
  return typeof value === "object" && value !== null && !Array.isArray(value);
22036
22339
  }
22037
- function extractFeatures(state) {
22038
- const features = state["features"];
22039
- if (!Array.isArray(features)) {
22040
- return [];
22041
- }
22042
- return features.filter(isObject3);
22340
+ function isNonEmptyString(value) {
22341
+ return typeof value === "string" && value.trim() !== "" && value === value.trim();
22043
22342
  }
22044
- function missingBaselineAndEpicKeys(state) {
22045
- const errors = [];
22046
- for (const key of [...REQUIRED_BASELINE_KEYS, ...REQUIRED_EPIC_KEYS]) {
22047
- if (!(key in state)) {
22048
- errors.push(`Epic checkpoint missing required key: ${key}`);
22343
+ function validPathSegments(value, separator) {
22344
+ return value.split(separator).every((part) => part !== "" && part !== "." && part !== "..");
22345
+ }
22346
+ function isCanonicalWorktreePath(value) {
22347
+ if (!isNonEmptyString(value) || value.includes("\0")) {
22348
+ return false;
22349
+ }
22350
+ if (value.startsWith("/")) {
22351
+ if (value.includes("\\") || value === "/") {
22352
+ return !value.includes("\\");
22049
22353
  }
22354
+ const leadingLength = value.startsWith("//") ? 2 : 1;
22355
+ return validPathSegments(value.slice(leadingLength), "/");
22050
22356
  }
22051
- return errors;
22357
+ if (value.includes("/")) {
22358
+ return false;
22359
+ }
22360
+ if (/^[A-Za-z]:\\/.test(value)) {
22361
+ return value.length === 3 || validPathSegments(value.slice(3), "\\");
22362
+ }
22363
+ if (value.startsWith("\\\\")) {
22364
+ const parts = value.slice(2).split("\\");
22365
+ return parts.length >= 2 && validPathSegments(value.slice(2), "\\");
22366
+ }
22367
+ return false;
22052
22368
  }
22053
- function validateRouteId(state) {
22054
- if ("route_id" in state && state["route_id"] !== EXPECTED_ROUTE_ID) {
22055
- return [
22056
- `Epic checkpoint route_id must be 'epic', found: ${JSON.stringify(state["route_id"])}`
22057
- ];
22369
+ function isLaunchArtifactPath(value) {
22370
+ if (!isNonEmptyString(value) || value.includes("\0")) {
22371
+ return false;
22058
22372
  }
22059
- return [];
22373
+ const normalized = value.replaceAll("\\", "/");
22374
+ const rawParts = normalized.split("/");
22375
+ if (rawParts.some((part) => part === "." || part === "..")) {
22376
+ return false;
22377
+ }
22378
+ const parts = rawParts.filter((part) => part !== "");
22379
+ let markerIndex = -1;
22380
+ for (let index = 0; index <= parts.length - LAUNCH_ARTIFACT_PARTS.length; index += 1) {
22381
+ if (LAUNCH_ARTIFACT_PARTS.every(
22382
+ (part, offset) => parts[index + offset] === part
22383
+ )) {
22384
+ markerIndex = index;
22385
+ break;
22386
+ }
22387
+ }
22388
+ if (markerIndex < 0 || markerIndex + LAUNCH_ARTIFACT_PARTS.length >= parts.length) {
22389
+ return false;
22390
+ }
22391
+ const absolute = normalized.startsWith("/") || /^[A-Za-z]:\//.test(normalized);
22392
+ return markerIndex === 0 || absolute;
22060
22393
  }
22061
- function validateFeatureFolderUniquenessAndDependencies(features) {
22394
+ function featurePrefix(feature) {
22395
+ const folder = feature["feature_folder"];
22396
+ const label = typeof folder === "string" && folder !== "" ? folder : "<unknown>";
22397
+ return `Epic checkpoint feature '${label}' launch binding`;
22398
+ }
22399
+ function validateBranchAndPaths(feature, prefix, seenBranches) {
22062
22400
  const errors = [];
22063
- const seen = /* @__PURE__ */ new Set();
22064
- const duplicates = /* @__PURE__ */ new Set();
22065
- const index = buildFeatureReferenceIndex(features);
22066
- for (const feature of features) {
22067
- const folder = feature["feature_folder"];
22068
- if (typeof folder !== "string" || !folder) {
22069
- continue;
22070
- }
22071
- if (seen.has(folder)) {
22072
- duplicates.add(folder);
22073
- }
22074
- seen.add(folder);
22075
- const dependsOn = feature["depends_on"];
22076
- if (!Array.isArray(dependsOn)) {
22077
- continue;
22078
- }
22079
- for (const dependency of dependsOn) {
22080
- if (resolveFeatureReference(dependency, index) === null) {
22081
- errors.push(
22082
- `Epic checkpoint feature '${folder}' depends_on unresolved feature_folder: ${JSON.stringify(dependency)}`
22083
- );
22084
- }
22085
- }
22401
+ const branch = feature["branch_name"];
22402
+ if (!isNonEmptyString(branch) || seenBranches.has(branch)) {
22403
+ errors.push(`${prefix}.branch_name must be a non-empty unique string.`);
22404
+ } else {
22405
+ seenBranches.add(branch);
22086
22406
  }
22087
- for (const folder of [...duplicates].sort()) {
22407
+ if (!isCanonicalWorktreePath(feature["worktree_path"])) {
22088
22408
  errors.push(
22089
- `Epic checkpoint has duplicate features[].feature_folder: ${folder}`
22409
+ `${prefix}.worktree_path must be a non-empty canonical absolute path.`
22090
22410
  );
22091
22411
  }
22092
- return errors;
22093
- }
22094
- function validateMergeStatusEnum(features) {
22095
- const errors = [];
22096
- for (const feature of features) {
22097
- const folder = feature["feature_folder"] ?? "<unknown>";
22098
- const mergeStatus = feature["merge_status"];
22099
- if (mergeStatus !== void 0 && mergeStatus !== null && !(typeof mergeStatus === "string" && VALID_MERGE_STATUS.has(mergeStatus))) {
22412
+ for (const key of ["launch_receipt_path", "launch_status_path"]) {
22413
+ if (!isLaunchArtifactPath(feature[key])) {
22100
22414
  errors.push(
22101
- `Epic checkpoint feature '${String(folder)}' has invalid merge_status: ${JSON.stringify(mergeStatus)}`
22415
+ `${prefix}.${key} must be under artifacts/orchestration/epic-child-launches/.`
22102
22416
  );
22103
22417
  }
22104
22418
  }
22105
22419
  return errors;
22106
22420
  }
22107
- function validateWaveBarrierOrdering(features) {
22421
+ function validateDelegationReceipt(feature, prefix, seenDelegationIds, requireGeneratedOrchestrator = false) {
22422
+ const value = feature["delegation_receipt"];
22423
+ if (!isObject3(value)) {
22424
+ return { errors: [`${prefix}.delegation_receipt must be an object.`] };
22425
+ }
22108
22426
  const errors = [];
22109
- const byFolder = /* @__PURE__ */ new Map();
22110
- for (const feature of features) {
22111
- const folder = feature["feature_folder"];
22112
- if (typeof folder === "string") {
22113
- byFolder.set(folder, feature);
22114
- }
22427
+ const delegationId = value["delegation_id"];
22428
+ const validId = isNonEmptyString(delegationId) ? delegationId : void 0;
22429
+ if (validId === void 0 || seenDelegationIds.has(validId)) {
22430
+ errors.push(
22431
+ `${prefix}.delegation_receipt.delegation_id must be a non-empty unique string.`
22432
+ );
22433
+ } else {
22434
+ seenDelegationIds.add(validId);
22115
22435
  }
22116
- const index = buildFeatureReferenceIndex(features);
22117
- for (const feature of features) {
22118
- const folder = feature["feature_folder"];
22119
- const dependsOn = feature["depends_on"];
22120
- if (typeof folder !== "string" || !Array.isArray(dependsOn)) {
22121
- continue;
22122
- }
22123
- const worktreeCreatedAt = feature["worktree_created_at"];
22124
- for (const dependency of dependsOn) {
22125
- const resolved = resolveFeatureReference(dependency, index);
22126
- const dependencyFeature = resolved !== null ? byFolder.get(resolved) : void 0;
22127
- if (dependencyFeature === void 0) {
22128
- continue;
22129
- }
22130
- const depMergeStatus = dependencyFeature["merge_status"];
22131
- const depConfirmedAt = dependencyFeature["merge_confirmed_at"];
22132
- const statusViolation = typeof depMergeStatus !== "string" || !MERGED_STATUSES.has(depMergeStatus);
22133
- const timingViolation = typeof depConfirmedAt === "string" && typeof worktreeCreatedAt === "string" && depConfirmedAt > worktreeCreatedAt;
22134
- if (statusViolation || timingViolation) {
22135
- errors.push(
22136
- `EPIC_WAVE_BARRIER_VIOLATION: ${folder} started before dependency ${String(dependency)} merged`
22137
- );
22138
- }
22139
- }
22436
+ if (value["feature_folder"] !== feature["feature_folder"]) {
22437
+ errors.push(
22438
+ `${prefix}.delegation_receipt.feature_folder must match the feature.`
22439
+ );
22140
22440
  }
22141
- return errors;
22441
+ if (!("issue_num" in feature) || !("issue_num" in value) || value["issue_num"] !== feature["issue_num"]) {
22442
+ errors.push(
22443
+ `${prefix}.delegation_receipt.issue_num must match the feature.`
22444
+ );
22445
+ }
22446
+ const agentName = value["agent_name"];
22447
+ const validAgent = isNonEmptyString(agentName) ? agentName : void 0;
22448
+ if (validAgent === void 0) {
22449
+ errors.push(
22450
+ `${prefix}.delegation_receipt.agent_name must be a non-empty string.`
22451
+ );
22452
+ } else if (requireGeneratedOrchestrator && !GENERATED_ORCHESTRATOR_AGENTS.has(validAgent)) {
22453
+ errors.push(
22454
+ `${prefix}.delegation_receipt.agent_name must name a generated orchestrator agent.`
22455
+ );
22456
+ }
22457
+ return {
22458
+ errors,
22459
+ ...validId === void 0 ? {} : { delegationId: validId },
22460
+ ...validAgent === void 0 ? {} : { deploymentAgent: validAgent }
22461
+ };
22462
+ }
22463
+ function validateModelReceipt(feature, prefix, delegation, expectedExecutionContext = "epic_execution_child") {
22464
+ const value = feature["model_routing_receipt"];
22465
+ if (!isObject3(value)) {
22466
+ return [`${prefix}.model_routing_receipt must be an object.`];
22467
+ }
22468
+ const errors = [];
22469
+ if (delegation.delegationId !== void 0 && value["delegation_id"] !== delegation.delegationId) {
22470
+ errors.push(
22471
+ `${prefix}.model_routing_receipt.delegation_id must match delegation_receipt.delegation_id.`
22472
+ );
22473
+ }
22474
+ const modelAgent = value["deployment_agent"];
22475
+ if (!isNonEmptyString(modelAgent)) {
22476
+ errors.push(
22477
+ `${prefix}.model_routing_receipt.deployment_agent must be a non-empty string.`
22478
+ );
22479
+ } else if (delegation.deploymentAgent !== void 0 && modelAgent !== delegation.deploymentAgent) {
22480
+ errors.push(
22481
+ `${prefix}.model_routing_receipt.deployment_agent must match delegation_receipt.agent_name.`
22482
+ );
22483
+ }
22484
+ if (value["execution_context"] !== expectedExecutionContext) {
22485
+ errors.push(
22486
+ `${prefix}.model_routing_receipt.execution_context must be '${expectedExecutionContext}'.`
22487
+ );
22488
+ }
22489
+ return errors;
22490
+ }
22491
+ function validateLaunchBindings(features, context) {
22492
+ const errors = [];
22493
+ const seenBranches = /* @__PURE__ */ new Set();
22494
+ const seenDelegationIds = /* @__PURE__ */ new Set();
22495
+ features.forEach((item, index) => {
22496
+ if (!isObject3(item)) {
22497
+ return;
22498
+ }
22499
+ if (context.skipNotStarted && item["merge_status"] === "not_started") {
22500
+ return;
22501
+ }
22502
+ const prefix = context.planner ? `Epic planner checkpoint features[${index}] launch binding` : featurePrefix(item);
22503
+ errors.push(...validateBranchAndPaths(item, prefix, seenBranches));
22504
+ const delegation = validateDelegationReceipt(
22505
+ item,
22506
+ prefix,
22507
+ seenDelegationIds,
22508
+ context.requireGeneratedOrchestrator
22509
+ );
22510
+ errors.push(...delegation.errors);
22511
+ errors.push(
22512
+ ...validateModelReceipt(
22513
+ item,
22514
+ prefix,
22515
+ delegation,
22516
+ context.expectedExecutionContext
22517
+ )
22518
+ );
22519
+ });
22520
+ return errors;
22521
+ }
22522
+ function validateEpicPlannerChildLaunchBindings(features) {
22523
+ return validateLaunchBindings(features, {
22524
+ expectedExecutionContext: "epic_preparation_child",
22525
+ planner: true,
22526
+ requireGeneratedOrchestrator: true,
22527
+ skipNotStarted: false
22528
+ });
22529
+ }
22530
+ function validateEpicChildLaunchBindings(state, options = {}) {
22531
+ if (options.requireCodexModelRouting !== true && options.requireCodexTopology !== true && options.requireComplete !== true) {
22532
+ return [];
22533
+ }
22534
+ const value = state["features"];
22535
+ if (!Array.isArray(value)) {
22536
+ return [];
22537
+ }
22538
+ return validateLaunchBindings(value, {
22539
+ expectedExecutionContext: "epic_execution_child",
22540
+ planner: false,
22541
+ requireGeneratedOrchestrator: false,
22542
+ skipNotStarted: options.requireComplete !== true
22543
+ });
22544
+ }
22545
+
22546
+ // ../../extensions/drm-copilot/src/lib/validate/orchestrator-state-codex-model-routing.ts
22547
+ var CODEX_MODEL_ROUTING_RECEIPTS_KEY = "codex_model_routing_receipts";
22548
+ var BAND_ORDER = ["C1", "C2", "C3", "C4"];
22549
+ var VALID_EXECUTION_CONTEXTS = /* @__PURE__ */ new Set([
22550
+ "standalone",
22551
+ "epic_preparation_child",
22552
+ "epic_execution_child"
22553
+ ]);
22554
+ var EPIC_EXECUTION_CONTEXTS = /* @__PURE__ */ new Set([
22555
+ "epic_preparation_child",
22556
+ "epic_execution_child"
22557
+ ]);
22558
+ var GENERATED_AGENT_FAMILIES = /* @__PURE__ */ new Set([
22559
+ "orchestrator",
22560
+ "atomic-planner",
22561
+ "atomic-executor",
22562
+ "feature-reviewer",
22563
+ "task-researcher",
22564
+ "prd-feature",
22565
+ "pr-author",
22566
+ "python-typed-engineer",
22567
+ "powershell-typed-engineer",
22568
+ "csharp-typed-engineer",
22569
+ "typescript-engineer"
22570
+ ]);
22571
+ var LOGICAL_AGENT_ALIASES = {
22572
+ "feature-review": "feature-reviewer"
22573
+ };
22574
+ var BASE_PROFILES = {
22575
+ C1: {
22576
+ suffix: "c1",
22577
+ model: "gpt-5.6-luna",
22578
+ model_reasoning_effort: "low"
22579
+ },
22580
+ C2: {
22581
+ suffix: "c2",
22582
+ model: "gpt-5.6-terra",
22583
+ model_reasoning_effort: "medium"
22584
+ },
22585
+ C3: {
22586
+ suffix: "c3",
22587
+ model: "gpt-5.6-terra",
22588
+ model_reasoning_effort: "high"
22589
+ },
22590
+ C4: {
22591
+ suffix: "c4",
22592
+ model: "gpt-5.6-sol",
22593
+ model_reasoning_effort: "max"
22594
+ }
22595
+ };
22596
+ var C3_ELEVATED_PROFILE = {
22597
+ suffix: "c3-elevated",
22598
+ model: "gpt-5.6-sol",
22599
+ model_reasoning_effort: "high"
22600
+ };
22601
+ var FORCED_PERSONA_PROFILES = {
22602
+ "epic-planner": {
22603
+ suffix: "",
22604
+ model: "gpt-5.6-sol",
22605
+ model_reasoning_effort: "ultra"
22606
+ },
22607
+ "epic-orchestrator": {
22608
+ suffix: "",
22609
+ model: "gpt-5.6-sol",
22610
+ model_reasoning_effort: "ultra"
22611
+ }
22612
+ };
22613
+ var REQUIRED_KEYS = [
22614
+ "logical_agent",
22615
+ "deployment_agent",
22616
+ "phase",
22617
+ "complexity_band",
22618
+ "execution_context",
22619
+ "orchestration_complexity_ceiling",
22620
+ "c3_overlay_applied",
22621
+ "c3_overlay_reason",
22622
+ "model",
22623
+ "model_reasoning_effort"
22624
+ ];
22625
+ var RESOLVED_KEYS = [
22626
+ "logical_agent",
22627
+ "deployment_agent",
22628
+ "complexity_band",
22629
+ "execution_context",
22630
+ "orchestration_complexity_ceiling",
22631
+ "c3_overlay_applied",
22632
+ "c3_overlay_reason",
22633
+ "model",
22634
+ "model_reasoning_effort"
22635
+ ];
22636
+ var ModelUnavailableError = class extends Error {
22637
+ constructor(message) {
22638
+ super(message);
22639
+ this.name = "ModelUnavailableError";
22640
+ }
22641
+ };
22642
+ function isObject4(value) {
22643
+ return typeof value === "object" && value !== null && !Array.isArray(value);
22644
+ }
22645
+ function pythonRepr2(value) {
22646
+ if (value === void 0 || value === null) {
22647
+ return "None";
22648
+ }
22649
+ if (typeof value === "string") {
22650
+ const escaped = value.replaceAll("\\", "\\\\").replaceAll("'", "\\'").replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll(" ", "\\t");
22651
+ return `'${escaped}'`;
22652
+ }
22653
+ if (typeof value === "boolean") {
22654
+ return value ? "True" : "False";
22655
+ }
22656
+ if (typeof value === "number") {
22657
+ return String(value);
22658
+ }
22659
+ if (Array.isArray(value)) {
22660
+ return `[${value.map(pythonRepr2).join(", ")}]`;
22661
+ }
22662
+ if (isObject4(value)) {
22663
+ const entries = Object.entries(value).map(
22664
+ ([key, item]) => `${pythonRepr2(key)}: ${pythonRepr2(item)}`
22665
+ );
22666
+ return `{${entries.join(", ")}}`;
22667
+ }
22668
+ return String(value);
22669
+ }
22670
+ function pythonStr(value) {
22671
+ return typeof value === "string" ? value : pythonRepr2(value);
22672
+ }
22673
+ function validateBand(value, fieldName) {
22674
+ if (!BAND_ORDER.includes(value)) {
22675
+ throw new Error(
22676
+ `${fieldName} must be one of ('C1', 'C2', 'C3', 'C4'), found ${pythonRepr2(value)}.`
22677
+ );
22678
+ }
22679
+ return value;
22680
+ }
22681
+ function validateContext(value) {
22682
+ if (!VALID_EXECUTION_CONTEXTS.has(value)) {
22683
+ throw new Error(
22684
+ `execution_context must be one of ('epic_execution_child', 'epic_preparation_child', 'standalone'), found ${pythonRepr2(value)}.`
22685
+ );
22686
+ }
22687
+ return value;
22688
+ }
22689
+ function selectC3OverlayReason(executionContext, orchestrationComplexityCeiling) {
22690
+ const epicContext = EPIC_EXECUTION_CONTEXTS.has(executionContext);
22691
+ const c4Ceiling = orchestrationComplexityCeiling === "C4";
22692
+ if (epicContext && c4Ceiling) {
22693
+ return "epic_context_and_c4_ceiling";
22694
+ }
22695
+ if (epicContext) {
22696
+ return "epic_context";
22697
+ }
22698
+ if (c4Ceiling) {
22699
+ return "c4_orchestration_ceiling";
22700
+ }
22701
+ return null;
22702
+ }
22703
+ function resolveCodexDeployment(logicalAgent, complexityBand, executionContext, orchestrationComplexityCeiling, availableModels) {
22704
+ const band = validateBand(complexityBand, "complexity_band");
22705
+ const ceiling = validateBand(
22706
+ orchestrationComplexityCeiling,
22707
+ "orchestration_complexity_ceiling"
22708
+ );
22709
+ const context = validateContext(executionContext);
22710
+ if (BAND_ORDER.indexOf(band) > BAND_ORDER.indexOf(ceiling)) {
22711
+ throw new Error(
22712
+ `orchestration_complexity_ceiling must be greater than or equal to complexity_band, found ${ceiling} below ${band}.`
22713
+ );
22714
+ }
22715
+ const forcedProfile = FORCED_PERSONA_PROFILES[logicalAgent];
22716
+ let profile;
22717
+ let deploymentAgent;
22718
+ let overlayReason;
22719
+ if (forcedProfile !== void 0) {
22720
+ profile = forcedProfile;
22721
+ deploymentAgent = logicalAgent;
22722
+ overlayReason = null;
22723
+ } else {
22724
+ const deploymentFamily = LOGICAL_AGENT_ALIASES[logicalAgent] ?? logicalAgent;
22725
+ if (!GENERATED_AGENT_FAMILIES.has(deploymentFamily)) {
22726
+ throw new Error(
22727
+ `Unsupported Codex logical agent: ${pythonRepr2(logicalAgent)}.`
22728
+ );
22729
+ }
22730
+ overlayReason = band === "C3" ? selectC3OverlayReason(context, ceiling) : null;
22731
+ profile = overlayReason === null ? BASE_PROFILES[band] : C3_ELEVATED_PROFILE;
22732
+ deploymentAgent = `${deploymentFamily}-${profile.suffix}`;
22733
+ }
22734
+ if (availableModels !== void 0 && !availableModels.has(profile.model)) {
22735
+ throw new ModelUnavailableError(
22736
+ `model_unavailable: required Codex model ${pythonRepr2(profile.model)} is unavailable; silent fallback is prohibited.`
22737
+ );
22738
+ }
22739
+ return {
22740
+ logical_agent: logicalAgent,
22741
+ deployment_agent: deploymentAgent,
22742
+ complexity_band: band,
22743
+ execution_context: context,
22744
+ orchestration_complexity_ceiling: ceiling,
22745
+ c3_overlay_applied: overlayReason !== null,
22746
+ c3_overlay_reason: overlayReason,
22747
+ model: profile.model,
22748
+ model_reasoning_effort: profile.model_reasoning_effort
22749
+ };
22750
+ }
22751
+ function validateReceipt(value, prefix, previousCeiling) {
22752
+ if (!isObject4(value)) {
22753
+ return { errors: [`${prefix} must be an object.`] };
22754
+ }
22755
+ const missing = REQUIRED_KEYS.filter((key) => !(key in value));
22756
+ if (missing.length > 0) {
22757
+ return {
22758
+ errors: [`${prefix} missing required keys: ${missing.join(", ")}.`]
22759
+ };
22760
+ }
22761
+ const errors = [];
22762
+ const phase = value["phase"];
22763
+ if (typeof phase !== "string" || phase.trim() === "") {
22764
+ errors.push(`${prefix}.phase must be a non-empty string.`);
22765
+ }
22766
+ let expected;
22767
+ try {
22768
+ expected = resolveCodexDeployment(
22769
+ pythonStr(value["logical_agent"]),
22770
+ pythonStr(value["complexity_band"]),
22771
+ pythonStr(value["execution_context"]),
22772
+ pythonStr(value["orchestration_complexity_ceiling"])
22773
+ );
22774
+ } catch (error2) {
22775
+ const message = error2 instanceof Error ? error2.message : String(error2);
22776
+ errors.push(`${prefix} has invalid routing inputs: ${message}`);
22777
+ return { errors };
22778
+ }
22779
+ const currentCeiling = expected.orchestration_complexity_ceiling;
22780
+ if (previousCeiling !== void 0 && BAND_ORDER.indexOf(currentCeiling) < BAND_ORDER.indexOf(previousCeiling)) {
22781
+ errors.push(
22782
+ `${prefix}.orchestration_complexity_ceiling must be monotonic; found ${currentCeiling} after ${previousCeiling}.`
22783
+ );
22784
+ } else if (previousCeiling !== void 0) {
22785
+ const transition = value["ceiling_transition"];
22786
+ if (currentCeiling === previousCeiling) {
22787
+ if (transition !== void 0 && transition !== null) {
22788
+ errors.push(
22789
+ `${prefix}.ceiling_transition must be absent unless the ceiling rises.`
22790
+ );
22791
+ }
22792
+ } else if (!isObject4(transition)) {
22793
+ errors.push(
22794
+ `${prefix}.ceiling_transition must record a ceiling increase.`
22795
+ );
22796
+ } else {
22797
+ if (transition["from"] !== previousCeiling || transition["to"] !== currentCeiling) {
22798
+ errors.push(
22799
+ `${prefix}.ceiling_transition must record ${previousCeiling} to ${currentCeiling}.`
22800
+ );
22801
+ }
22802
+ const affected = transition["affected_delegation_ids"];
22803
+ if (!Array.isArray(affected) || affected.length === 0 || affected.some(
22804
+ (item) => typeof item !== "string" || item.trim() === ""
22805
+ ) || new Set(affected).size !== affected.length) {
22806
+ errors.push(
22807
+ `${prefix}.ceiling_transition.affected_delegation_ids must be a non-empty unique string list.`
22808
+ );
22809
+ }
22810
+ }
22811
+ } else if (value["ceiling_transition"] !== void 0 && value["ceiling_transition"] !== null) {
22812
+ errors.push(
22813
+ `${prefix}.ceiling_transition must be absent unless the ceiling rises.`
22814
+ );
22815
+ }
22816
+ for (const key of RESOLVED_KEYS) {
22817
+ if (value[key] !== expected[key]) {
22818
+ errors.push(
22819
+ `${prefix}.${key} must be ${pythonRepr2(expected[key])}, found ${pythonRepr2(value[key])}.`
22820
+ );
22821
+ }
22822
+ }
22823
+ return { errors, resolvedCeiling: currentCeiling };
22824
+ }
22825
+ function validateCodexModelRoutingReceipt(value, prefix = `Checkpoint ${CODEX_MODEL_ROUTING_RECEIPTS_KEY}[0]`) {
22826
+ return validateReceipt(value, prefix).errors;
22827
+ }
22828
+ function validateCodexModelRoutingReceipts(value) {
22829
+ if (!Array.isArray(value)) {
22830
+ return [
22831
+ `Checkpoint ${CODEX_MODEL_ROUTING_RECEIPTS_KEY} must be a list when present.`
22832
+ ];
22833
+ }
22834
+ const errors = [];
22835
+ let previousCeiling;
22836
+ value.forEach((item, index) => {
22837
+ const result = validateReceipt(
22838
+ item,
22839
+ `Checkpoint ${CODEX_MODEL_ROUTING_RECEIPTS_KEY}[${index}]`,
22840
+ previousCeiling
22841
+ );
22842
+ errors.push(...result.errors);
22843
+ previousCeiling = result.resolvedCeiling ?? previousCeiling;
22844
+ });
22845
+ return errors;
22846
+ }
22847
+ function delegatedAgentNames(state) {
22848
+ const result = /* @__PURE__ */ new Set();
22849
+ const receipts = state["delegation_receipts"];
22850
+ if (!Array.isArray(receipts)) {
22851
+ return result;
22852
+ }
22853
+ for (const item of receipts) {
22854
+ if (!isObject4(item)) {
22855
+ continue;
22856
+ }
22857
+ const agentName = item["agent_name"];
22858
+ if (typeof agentName === "string" && agentName.trim() !== "") {
22859
+ result.add(agentName);
22860
+ }
22861
+ }
22862
+ return result;
22863
+ }
22864
+ function validateCodexModelRoutingGate(state) {
22865
+ const delegated = delegatedAgentNames(state);
22866
+ if (delegated.size === 0) {
22867
+ return [];
22868
+ }
22869
+ const value = state[CODEX_MODEL_ROUTING_RECEIPTS_KEY];
22870
+ const errors = validateCodexModelRoutingReceipts(value);
22871
+ if (!Array.isArray(value)) {
22872
+ return errors;
22873
+ }
22874
+ const logicalAgents = /* @__PURE__ */ new Set();
22875
+ const deploymentAgents = /* @__PURE__ */ new Set();
22876
+ for (const item of value) {
22877
+ if (!isObject4(item)) {
22878
+ continue;
22879
+ }
22880
+ const logical = item["logical_agent"];
22881
+ const deployment = item["deployment_agent"];
22882
+ if (typeof logical === "string") {
22883
+ logicalAgents.add(logical);
22884
+ }
22885
+ if (typeof deployment === "string") {
22886
+ deploymentAgents.add(deployment);
22887
+ }
22888
+ }
22889
+ for (const agent of [...delegated].sort()) {
22890
+ if (!logicalAgents.has(agent) && !deploymentAgents.has(agent)) {
22891
+ errors.push(
22892
+ `Checkpoint ${CODEX_MODEL_ROUTING_RECEIPTS_KEY} is missing a receipt for delegated agent: ${agent}.`
22893
+ );
22894
+ }
22895
+ }
22896
+ return errors;
22897
+ }
22898
+ function validateCodexModelRoutingState(state, requireGate = false) {
22899
+ const errors = [];
22900
+ if (CODEX_MODEL_ROUTING_RECEIPTS_KEY in state) {
22901
+ errors.push(
22902
+ ...validateCodexModelRoutingReceipts(
22903
+ state[CODEX_MODEL_ROUTING_RECEIPTS_KEY]
22904
+ )
22905
+ );
22906
+ }
22907
+ if (requireGate) {
22908
+ errors.push(...validateCodexModelRoutingGate(state));
22909
+ }
22910
+ return errors;
22911
+ }
22912
+
22913
+ // ../../extensions/drm-copilot/src/lib/validate/codex-topology-resolver.ts
22914
+ var VALID_EXECUTION_CONTEXTS2 = /* @__PURE__ */ new Set([
22915
+ "standalone",
22916
+ "epic_preparation_child",
22917
+ "epic_execution_child"
22918
+ ]);
22919
+ var EPIC_CHILD_CONTEXTS = /* @__PURE__ */ new Set([
22920
+ "epic_preparation_child",
22921
+ "epic_execution_child"
22922
+ ]);
22923
+ var FORCED_ROOT_PERSONAS = /* @__PURE__ */ new Set([
22924
+ "epic-planner",
22925
+ "epic-orchestrator"
22926
+ ]);
22927
+ var ORCHESTRATOR_LOGICAL_AGENT = "orchestrator";
22928
+ var LANGUAGE_BUDGETS = {
22929
+ python: {
22930
+ direct_mode_enabled: true,
22931
+ max_production_files: 3,
22932
+ max_test_files: 3,
22933
+ logical_agent: "python-typed-engineer"
22934
+ },
22935
+ powershell: {
22936
+ direct_mode_enabled: true,
22937
+ max_production_files: 2,
22938
+ max_test_files: 3,
22939
+ logical_agent: "powershell-typed-engineer"
22940
+ },
22941
+ csharp: {
22942
+ direct_mode_enabled: true,
22943
+ max_production_files: 3,
22944
+ max_test_files: 3,
22945
+ logical_agent: "csharp-typed-engineer"
22946
+ },
22947
+ typescript: {
22948
+ direct_mode_enabled: false,
22949
+ max_production_files: 0,
22950
+ max_test_files: 0,
22951
+ logical_agent: "typescript-engineer"
22952
+ }
22953
+ };
22954
+ function pythonRepr3(value) {
22955
+ if (value === void 0 || value === null) {
22956
+ return "None";
22957
+ }
22958
+ if (typeof value === "string") {
22959
+ return `'${value.replaceAll("\\", "\\\\").replaceAll("'", "\\'")}'`;
22960
+ }
22961
+ if (typeof value === "boolean") {
22962
+ return value ? "True" : "False";
22963
+ }
22964
+ return String(value);
22965
+ }
22966
+ function validateContext2(value) {
22967
+ if (typeof value !== "string" || !VALID_EXECUTION_CONTEXTS2.has(value)) {
22968
+ throw new Error(
22969
+ `execution_context must be one of ('epic_execution_child', 'epic_preparation_child', 'standalone'), found ${pythonRepr3(value)}.`
22970
+ );
22971
+ }
22972
+ return value;
22973
+ }
22974
+ function normalizeLanguages(value) {
22975
+ if (!Array.isArray(value) || value.some(
22976
+ (language) => typeof language !== "string" || language.trim() === ""
22977
+ )) {
22978
+ throw new Error("languages must contain non-empty strings.");
22979
+ }
22980
+ const normalized = new Set(
22981
+ value.map((language) => language.trim().toLowerCase())
22982
+ );
22983
+ return [...normalized].sort();
22984
+ }
22985
+ function requireInteger(value, fieldName) {
22986
+ if (typeof value !== "number" || !Number.isInteger(value)) {
22987
+ throw new Error(`${fieldName} must be an integer.`);
22988
+ }
22989
+ return value;
22990
+ }
22991
+ function requireBoolean(value) {
22992
+ if (typeof value !== "boolean") {
22993
+ throw new Error("cross_cutting must be a boolean.");
22994
+ }
22995
+ return value;
22996
+ }
22997
+ function orchestratorReceipt(context, languages, productionFileCount, testFileCount, crossCutting, reason, budget) {
22998
+ return {
22999
+ execution_context: context,
23000
+ languages,
23001
+ production_file_count: productionFileCount,
23002
+ test_file_count: testFileCount,
23003
+ cross_cutting: crossCutting,
23004
+ root_persona: null,
23005
+ route: "large",
23006
+ topology: "orchestrator",
23007
+ logical_agent: ORCHESTRATOR_LOGICAL_AGENT,
23008
+ routing_reason: reason,
23009
+ max_production_files: budget?.max_production_files ?? null,
23010
+ max_test_files: budget?.max_test_files ?? null
23011
+ };
23012
+ }
23013
+ function resolveCodexTopology(languagesValue, productionFileCountValue, testFileCountValue, executionContextValue, options = {}) {
23014
+ const context = validateContext2(executionContextValue);
23015
+ const languages = normalizeLanguages(languagesValue);
23016
+ const productionFileCount = requireInteger(
23017
+ productionFileCountValue,
23018
+ "production_file_count"
23019
+ );
23020
+ const testFileCount = requireInteger(testFileCountValue, "test_file_count");
23021
+ const crossCutting = requireBoolean(
23022
+ options.crossCutting === void 0 ? false : options.crossCutting
23023
+ );
23024
+ const rootPersona = options.rootPersona ?? null;
23025
+ if (rootPersona !== null) {
23026
+ if (typeof rootPersona !== "string" || !FORCED_ROOT_PERSONAS.has(rootPersona)) {
23027
+ throw new Error(
23028
+ `Unsupported Codex root persona: ${pythonRepr3(rootPersona)}.`
23029
+ );
23030
+ }
23031
+ if (context !== "standalone") {
23032
+ throw new Error("A forced root persona requires standalone context.");
23033
+ }
23034
+ const persona = rootPersona;
23035
+ return {
23036
+ execution_context: context,
23037
+ languages,
23038
+ production_file_count: productionFileCount,
23039
+ test_file_count: testFileCount,
23040
+ cross_cutting: crossCutting,
23041
+ root_persona: persona,
23042
+ route: "epic",
23043
+ topology: "epic_persona",
23044
+ logical_agent: persona,
23045
+ routing_reason: "forced_root_persona",
23046
+ max_production_files: null,
23047
+ max_test_files: null
23048
+ };
23049
+ }
23050
+ const escalate = (reason, budget2) => orchestratorReceipt(
23051
+ context,
23052
+ languages,
23053
+ productionFileCount,
23054
+ testFileCount,
23055
+ crossCutting,
23056
+ reason,
23057
+ budget2
23058
+ );
23059
+ if (EPIC_CHILD_CONTEXTS.has(context)) {
23060
+ return escalate("epic_child_context");
23061
+ }
23062
+ if (productionFileCount <= 0 || testFileCount < 0) {
23063
+ return escalate("invalid_estimate");
23064
+ }
23065
+ if (languages.length > 1) {
23066
+ return escalate("cross_language");
23067
+ }
23068
+ if (languages.length !== 1) {
23069
+ return escalate("unsupported_language");
23070
+ }
23071
+ const budget = LANGUAGE_BUDGETS[languages[0] ?? ""];
23072
+ if (budget === void 0) {
23073
+ return escalate("unsupported_language");
23074
+ }
23075
+ if (crossCutting) {
23076
+ return escalate("cross_cutting", budget);
23077
+ }
23078
+ if (!budget.direct_mode_enabled) {
23079
+ return escalate("direct_mode_disabled", budget);
23080
+ }
23081
+ if (productionFileCount > budget.max_production_files) {
23082
+ return escalate("production_budget_exceeded", budget);
23083
+ }
23084
+ return {
23085
+ execution_context: context,
23086
+ languages,
23087
+ production_file_count: productionFileCount,
23088
+ test_file_count: testFileCount,
23089
+ cross_cutting: crossCutting,
23090
+ root_persona: null,
23091
+ route: "small",
23092
+ topology: "typed_engineer",
23093
+ logical_agent: budget.logical_agent,
23094
+ routing_reason: "within_language_budget",
23095
+ max_production_files: budget.max_production_files,
23096
+ max_test_files: budget.max_test_files
23097
+ };
23098
+ }
23099
+
23100
+ // ../../extensions/drm-copilot/src/lib/validate/orchestrator-state-codex-topology.ts
23101
+ var CODEX_TOPOLOGY_RECEIPTS_KEY = "codex_topology_receipts";
23102
+ var REQUIRED_KEYS2 = [
23103
+ "phase",
23104
+ "execution_context",
23105
+ "languages",
23106
+ "production_file_count",
23107
+ "test_file_count",
23108
+ "cross_cutting",
23109
+ "root_persona",
23110
+ "route",
23111
+ "topology",
23112
+ "logical_agent",
23113
+ "routing_reason",
23114
+ "max_production_files",
23115
+ "max_test_files"
23116
+ ];
23117
+ var RESOLVED_KEYS2 = REQUIRED_KEYS2.filter((key) => key !== "phase");
23118
+ var FORCED_ROOT_PERSONAS2 = /* @__PURE__ */ new Set([
23119
+ "epic-orchestrator",
23120
+ "epic-planner"
23121
+ ]);
23122
+ function isObject5(value) {
23123
+ return typeof value === "object" && value !== null && !Array.isArray(value);
23124
+ }
23125
+ function pythonRepr4(value) {
23126
+ if (value === void 0 || value === null) {
23127
+ return "None";
23128
+ }
23129
+ if (typeof value === "string") {
23130
+ const escaped = value.replaceAll("\\", "\\\\").replaceAll("'", "\\'").replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll(" ", "\\t");
23131
+ return `'${escaped}'`;
23132
+ }
23133
+ if (typeof value === "boolean") {
23134
+ return value ? "True" : "False";
23135
+ }
23136
+ if (Array.isArray(value)) {
23137
+ return `[${value.map(pythonRepr4).join(", ")}]`;
23138
+ }
23139
+ if (isObject5(value)) {
23140
+ const entries = Object.entries(value).map(
23141
+ ([key, item]) => `${pythonRepr4(key)}: ${pythonRepr4(item)}`
23142
+ );
23143
+ return `{${entries.join(", ")}}`;
23144
+ }
23145
+ return String(value);
23146
+ }
23147
+ function valuesEqual(actual, expected) {
23148
+ if (Array.isArray(actual) && Array.isArray(expected)) {
23149
+ return actual.length === expected.length && actual.every((item, index) => item === expected[index]);
23150
+ }
23151
+ return actual === expected;
23152
+ }
23153
+ function validateReceiptInputs(receipt, prefix) {
23154
+ const errors = [];
23155
+ const languages = receipt["languages"];
23156
+ if (!Array.isArray(languages) || languages.some(
23157
+ (language) => typeof language !== "string" || language.trim().length === 0
23158
+ )) {
23159
+ errors.push(`${prefix}.languages must be a list of non-empty strings.`);
23160
+ }
23161
+ for (const key of ["production_file_count", "test_file_count"]) {
23162
+ const value = receipt[key];
23163
+ if (typeof value !== "number" || !Number.isInteger(value)) {
23164
+ errors.push(`${prefix}.${key} must be an integer.`);
23165
+ }
23166
+ }
23167
+ if (typeof receipt["cross_cutting"] !== "boolean") {
23168
+ errors.push(`${prefix}.cross_cutting must be a boolean.`);
23169
+ }
23170
+ if (typeof receipt["execution_context"] !== "string") {
23171
+ errors.push(`${prefix}.execution_context must be a string.`);
23172
+ }
23173
+ const rootPersona = receipt["root_persona"];
23174
+ if (rootPersona !== null && !FORCED_ROOT_PERSONAS2.has(rootPersona)) {
23175
+ errors.push(
23176
+ `${prefix}.root_persona must be null or one of ('epic-orchestrator', 'epic-planner').`
23177
+ );
23178
+ }
23179
+ return errors;
23180
+ }
23181
+ function validateReceipt2(value, prefix) {
23182
+ if (!isObject5(value)) {
23183
+ return [`${prefix} must be an object.`];
23184
+ }
23185
+ const missing = REQUIRED_KEYS2.filter((key) => !(key in value));
23186
+ if (missing.length > 0) {
23187
+ return [`${prefix} missing required keys: ${missing.join(", ")}.`];
23188
+ }
23189
+ const errors = [];
23190
+ const phase = value["phase"];
23191
+ if (typeof phase !== "string" || phase.trim().length === 0) {
23192
+ errors.push(`${prefix}.phase must be a non-empty string.`);
23193
+ }
23194
+ const inputErrors = validateReceiptInputs(value, prefix);
23195
+ errors.push(...inputErrors);
23196
+ if (inputErrors.length > 0) {
23197
+ return errors;
23198
+ }
23199
+ let expected;
23200
+ try {
23201
+ expected = resolveCodexTopology(
23202
+ value["languages"],
23203
+ value["production_file_count"],
23204
+ value["test_file_count"],
23205
+ value["execution_context"],
23206
+ {
23207
+ crossCutting: value["cross_cutting"],
23208
+ rootPersona: value["root_persona"]
23209
+ }
23210
+ );
23211
+ } catch (error2) {
23212
+ const message = error2 instanceof Error ? error2.message : String(error2);
23213
+ errors.push(`${prefix} has invalid routing inputs: ${message}`);
23214
+ return errors;
23215
+ }
23216
+ for (const key of RESOLVED_KEYS2) {
23217
+ if (!valuesEqual(value[key], expected[key])) {
23218
+ errors.push(
23219
+ `${prefix}.${key} must be ${pythonRepr4(expected[key])}, found ${pythonRepr4(value[key])}.`
23220
+ );
23221
+ }
23222
+ }
23223
+ return errors;
23224
+ }
23225
+ function validateCodexTopologyReceipt(value, prefix = `Checkpoint ${CODEX_TOPOLOGY_RECEIPTS_KEY}[0]`) {
23226
+ return validateReceipt2(value, prefix);
23227
+ }
23228
+ function validateCodexTopologyReceipts(value) {
23229
+ if (!Array.isArray(value)) {
23230
+ return [
23231
+ `Checkpoint ${CODEX_TOPOLOGY_RECEIPTS_KEY} must be a list when present.`
23232
+ ];
23233
+ }
23234
+ return value.flatMap(
23235
+ (item, index) => validateReceipt2(
23236
+ item,
23237
+ `Checkpoint ${CODEX_TOPOLOGY_RECEIPTS_KEY}[${index}]`
23238
+ )
23239
+ );
23240
+ }
23241
+ function delegatedAgentNames2(state) {
23242
+ const result = /* @__PURE__ */ new Set();
23243
+ const receipts = state["delegation_receipts"];
23244
+ if (!Array.isArray(receipts)) {
23245
+ return result;
23246
+ }
23247
+ for (const item of receipts) {
23248
+ if (!isObject5(item)) {
23249
+ continue;
23250
+ }
23251
+ const agentName = item["agent_name"];
23252
+ if (typeof agentName === "string" && agentName.trim().length > 0) {
23253
+ result.add(agentName);
23254
+ }
23255
+ }
23256
+ return result;
23257
+ }
23258
+ function validReceipts(value) {
23259
+ return value.filter(
23260
+ (item) => isObject5(item) && validateCodexTopologyReceipts([item]).length === 0
23261
+ );
23262
+ }
23263
+ function modelDeployments(state, logicalAgent) {
23264
+ const result = /* @__PURE__ */ new Set();
23265
+ const receipts = state[CODEX_MODEL_ROUTING_RECEIPTS_KEY];
23266
+ if (!Array.isArray(receipts)) {
23267
+ return result;
23268
+ }
23269
+ for (const item of receipts) {
23270
+ if (!isObject5(item) || validateCodexModelRoutingReceipt(item).length > 0 || item["logical_agent"] !== logicalAgent) {
23271
+ continue;
23272
+ }
23273
+ const deployment = item["deployment_agent"];
23274
+ if (typeof deployment === "string") {
23275
+ result.add(deployment);
23276
+ }
23277
+ }
23278
+ return result;
23279
+ }
23280
+ function validateCodexTopologyGate(state, options = {}) {
23281
+ const delegated = delegatedAgentNames2(state);
23282
+ const requiredRootPersona = options.requiredRootPersona;
23283
+ if (delegated.size === 0 && requiredRootPersona === void 0) {
23284
+ return [];
23285
+ }
23286
+ const value = state[CODEX_TOPOLOGY_RECEIPTS_KEY];
23287
+ const errors = validateCodexTopologyReceipts(value);
23288
+ if (!Array.isArray(value)) {
23289
+ return errors;
23290
+ }
23291
+ const receipts = validReceipts(value);
23292
+ if (requiredRootPersona !== void 0 && !receipts.some((receipt) => receipt.root_persona === requiredRootPersona)) {
23293
+ errors.push(
23294
+ `Checkpoint ${CODEX_TOPOLOGY_RECEIPTS_KEY} is missing the forced root persona receipt for ${requiredRootPersona}.`
23295
+ );
23296
+ }
23297
+ const childReceipts = receipts.filter(
23298
+ (receipt) => receipt.root_persona === null
23299
+ );
23300
+ if (delegated.size > 0 && childReceipts.length === 0) {
23301
+ errors.push(
23302
+ `Checkpoint ${CODEX_TOPOLOGY_RECEIPTS_KEY} is missing a child topology receipt for recorded delegations.`
23303
+ );
23304
+ }
23305
+ for (const receipt of childReceipts) {
23306
+ const selectedRoute = state["path_selected"];
23307
+ if (receipt.execution_context === "standalone" && (selectedRoute === "small" || selectedRoute === "large") && receipt.route !== selectedRoute) {
23308
+ errors.push(
23309
+ `Checkpoint path_selected ${pythonRepr4(selectedRoute)} does not match the resolved Codex topology route ${pythonRepr4(receipt.route)}.`
23310
+ );
23311
+ }
23312
+ const allowedNames = modelDeployments(state, receipt.logical_agent);
23313
+ allowedNames.add(receipt.logical_agent);
23314
+ if ([...delegated].every((agent) => !allowedNames.has(agent))) {
23315
+ errors.push(
23316
+ `Checkpoint delegation_receipts is missing the exact resolved topology agent for ${receipt.logical_agent}: ${[...allowedNames].sort().join(", ")}.`
23317
+ );
23318
+ }
23319
+ }
23320
+ return errors;
23321
+ }
23322
+ function validateCodexTopologyState(state, requireGate = false, options = {}) {
23323
+ const errors = [];
23324
+ if (CODEX_TOPOLOGY_RECEIPTS_KEY in state) {
23325
+ errors.push(
23326
+ ...validateCodexTopologyReceipts(state[CODEX_TOPOLOGY_RECEIPTS_KEY])
23327
+ );
23328
+ }
23329
+ if (requireGate) {
23330
+ errors.push(...validateCodexTopologyGate(state, options));
23331
+ }
23332
+ return errors;
23333
+ }
23334
+
23335
+ // ../../extensions/drm-copilot/src/lib/validate/epic-orchestrator-state-core.ts
23336
+ var REQUIRED_BASELINE_KEYS = [
23337
+ "objective",
23338
+ "completed_steps",
23339
+ "next_step",
23340
+ "last_updated"
23341
+ ];
23342
+ var REQUIRED_EPIC_KEYS = [
23343
+ "route_id",
23344
+ "epic_feature_folder",
23345
+ "integration_branch",
23346
+ "max_parallel_features",
23347
+ "waves",
23348
+ "features"
23349
+ ];
23350
+ var EXPECTED_ROUTE_ID = "epic";
23351
+ var VALID_MERGE_STATUS = /* @__PURE__ */ new Set([
23352
+ "not_started",
23353
+ "worktree_created",
23354
+ "pr_open",
23355
+ "ci_green",
23356
+ "merge_conflict",
23357
+ "blocked_conflict_loop_limit",
23358
+ "merged",
23359
+ "worktree_removed"
23360
+ ]);
23361
+ var MERGED_STATUSES = /* @__PURE__ */ new Set([
23362
+ "merged",
23363
+ "worktree_removed"
23364
+ ]);
23365
+ function isObject6(value) {
23366
+ return typeof value === "object" && value !== null && !Array.isArray(value);
23367
+ }
23368
+ function extractFeatures(state) {
23369
+ const features = state["features"];
23370
+ if (!Array.isArray(features)) {
23371
+ return [];
23372
+ }
23373
+ return features.filter(isObject6);
23374
+ }
23375
+ function missingBaselineAndEpicKeys(state) {
23376
+ const errors = [];
23377
+ for (const key of [...REQUIRED_BASELINE_KEYS, ...REQUIRED_EPIC_KEYS]) {
23378
+ if (!(key in state)) {
23379
+ errors.push(`Epic checkpoint missing required key: ${key}`);
23380
+ }
23381
+ }
23382
+ return errors;
23383
+ }
23384
+ function validateRouteId(state) {
23385
+ const errors = [];
23386
+ if ("route_id" in state && state["route_id"] !== EXPECTED_ROUTE_ID) {
23387
+ errors.push(
23388
+ `Epic checkpoint route_id must be 'epic', found: ${JSON.stringify(state["route_id"])}`
23389
+ );
23390
+ }
23391
+ const parallelism = state["max_parallel_features"];
23392
+ if (typeof parallelism !== "number" || !Number.isInteger(parallelism) || parallelism < 1 || parallelism > 8) {
23393
+ errors.push(
23394
+ "Epic checkpoint max_parallel_features must be an integer from 1 through 8."
23395
+ );
23396
+ }
23397
+ return errors;
23398
+ }
23399
+ function validateFeatureFolderUniquenessAndDependencies(features) {
23400
+ const errors = [];
23401
+ const seen = /* @__PURE__ */ new Set();
23402
+ const duplicates = /* @__PURE__ */ new Set();
23403
+ const index = buildFeatureReferenceIndex(features);
23404
+ for (const feature of features) {
23405
+ const folder = feature["feature_folder"];
23406
+ if (typeof folder !== "string" || !folder) {
23407
+ continue;
23408
+ }
23409
+ if (seen.has(folder)) {
23410
+ duplicates.add(folder);
23411
+ }
23412
+ seen.add(folder);
23413
+ const dependsOn = feature["depends_on"];
23414
+ if (!Array.isArray(dependsOn)) {
23415
+ continue;
23416
+ }
23417
+ for (const dependency of dependsOn) {
23418
+ if (resolveFeatureReference(dependency, index) === null) {
23419
+ errors.push(
23420
+ `Epic checkpoint feature '${folder}' depends_on unresolved feature_folder: ${JSON.stringify(dependency)}`
23421
+ );
23422
+ }
23423
+ }
23424
+ }
23425
+ for (const folder of [...duplicates].sort()) {
23426
+ errors.push(
23427
+ `Epic checkpoint has duplicate features[].feature_folder: ${folder}`
23428
+ );
23429
+ }
23430
+ return errors;
23431
+ }
23432
+ function validateMergeStatusEnum(features) {
23433
+ const errors = [];
23434
+ for (const feature of features) {
23435
+ const folder = feature["feature_folder"] ?? "<unknown>";
23436
+ const mergeStatus = feature["merge_status"];
23437
+ if (mergeStatus !== void 0 && mergeStatus !== null && !(typeof mergeStatus === "string" && VALID_MERGE_STATUS.has(mergeStatus))) {
23438
+ errors.push(
23439
+ `Epic checkpoint feature '${String(folder)}' has invalid merge_status: ${JSON.stringify(mergeStatus)}`
23440
+ );
23441
+ }
23442
+ }
23443
+ return errors;
23444
+ }
23445
+ function validateWaveBarrierOrdering(features) {
23446
+ const errors = [];
23447
+ const byFolder = /* @__PURE__ */ new Map();
23448
+ for (const feature of features) {
23449
+ const folder = feature["feature_folder"];
23450
+ if (typeof folder === "string") {
23451
+ byFolder.set(folder, feature);
23452
+ }
23453
+ }
23454
+ const index = buildFeatureReferenceIndex(features);
23455
+ for (const feature of features) {
23456
+ const folder = feature["feature_folder"];
23457
+ const dependsOn = feature["depends_on"];
23458
+ if (typeof folder !== "string" || !Array.isArray(dependsOn)) {
23459
+ continue;
23460
+ }
23461
+ const worktreeCreatedAt = feature["worktree_created_at"];
23462
+ for (const dependency of dependsOn) {
23463
+ const resolved = resolveFeatureReference(dependency, index);
23464
+ const dependencyFeature = resolved !== null ? byFolder.get(resolved) : void 0;
23465
+ if (dependencyFeature === void 0) {
23466
+ continue;
23467
+ }
23468
+ const depMergeStatus = dependencyFeature["merge_status"];
23469
+ const depConfirmedAt = dependencyFeature["merge_confirmed_at"];
23470
+ const statusViolation = typeof depMergeStatus !== "string" || !MERGED_STATUSES.has(depMergeStatus);
23471
+ const timingViolation = typeof depConfirmedAt === "string" && typeof worktreeCreatedAt === "string" && depConfirmedAt > worktreeCreatedAt;
23472
+ if (statusViolation || timingViolation) {
23473
+ errors.push(
23474
+ `EPIC_WAVE_BARRIER_VIOLATION: ${folder} started before dependency ${String(dependency)} merged`
23475
+ );
23476
+ }
23477
+ }
23478
+ }
23479
+ return errors;
22142
23480
  }
22143
23481
  function validateWavesConsistency(state) {
22144
23482
  const errors = [];
@@ -22156,7 +23494,7 @@ function validateWavesConsistency(state) {
22156
23494
  }
22157
23495
  const index = buildFeatureReferenceIndex(features);
22158
23496
  for (const waveItem of waves) {
22159
- if (!isObject3(waveItem)) {
23497
+ if (!isObject6(waveItem)) {
22160
23498
  continue;
22161
23499
  }
22162
23500
  const waveNumber = waveItem["wave_number"];
@@ -22191,7 +23529,7 @@ function validateCompletion(features, state) {
22191
23529
  }
22192
23530
  }
22193
23531
  const epicMergePr = state["epic_merge_pr"];
22194
- const mergeCommitSha = isObject3(epicMergePr) ? epicMergePr["merge_commit_sha"] : void 0;
23532
+ const mergeCommitSha = isObject6(epicMergePr) ? epicMergePr["merge_commit_sha"] : void 0;
22195
23533
  if (typeof mergeCommitSha !== "string" || !mergeCommitSha.trim()) {
22196
23534
  errors.push(
22197
23535
  "Epic checkpoint completion validation failed: epic_merge_pr.merge_commit_sha is missing or empty."
@@ -22208,7 +23546,7 @@ function validateEpicOrchestratorStateText(text, options = {}) {
22208
23546
  `Epic checkpoint is not valid JSON: ${exc instanceof Error ? exc.message : String(exc)}`
22209
23547
  ];
22210
23548
  }
22211
- if (!isObject3(state)) {
23549
+ if (!isObject6(state)) {
22212
23550
  return ["Epic checkpoint root must be a JSON object."];
22213
23551
  }
22214
23552
  const stateMap = state;
@@ -22225,236 +23563,1305 @@ function validateEpicOrchestratorStateText(text, options = {}) {
22225
23563
  errors.push(...validateWaveBarrierOrdering(features));
22226
23564
  errors.push(...validateWavesConsistency(stateMap));
22227
23565
  errors.push(...validateIntentBlock(stateMap));
23566
+ errors.push(...validateEpicChildLaunchBindings(stateMap, options));
23567
+ if (CODEX_MODEL_ROUTING_RECEIPTS_KEY in stateMap) {
23568
+ errors.push(
23569
+ ...validateCodexModelRoutingReceipts(
23570
+ stateMap[CODEX_MODEL_ROUTING_RECEIPTS_KEY]
23571
+ )
23572
+ );
23573
+ }
22228
23574
  if (options.requireComplete === true) {
22229
23575
  errors.push(...validateCompletion(features, stateMap));
22230
23576
  }
23577
+ if (options.requireCodexModelRouting === true) {
23578
+ errors.push(...validateCodexModelRoutingGate(stateMap));
23579
+ }
23580
+ errors.push(
23581
+ ...validateCodexTopologyState(
23582
+ stateMap,
23583
+ options.requireCodexTopology === true,
23584
+ { requiredRootPersona: "epic-orchestrator" }
23585
+ )
23586
+ );
22231
23587
  return errors;
22232
23588
  }
22233
23589
 
22234
- // ../../extensions/drm-copilot/src/lib/validate/orchestrator-state-completion.ts
22235
- var ISSUE_232 = "232";
22236
- var ISSUE_232_BRANCH = "feature/harden-orchestrate-skill-232";
22237
- var PR_GATE_KEYS = [
22238
- "pr_number",
22239
- "pr_url",
22240
- "head_branch",
22241
- "head_sha"
22242
- ];
22243
- var CI_GATE_KEYS = ["conclusion", "head_sha", "verified_at"];
22244
- var PROMOTION_RECEIPT_NAMESPACE_KEY = "promotion";
22245
- var PROMOTION_RECEIPT_KEYS = [
22246
- "potential_entry",
22247
- "issue",
22248
- "feature_folder"
22249
- ];
22250
- function isObject4(value) {
23590
+ // ../../extensions/drm-copilot/src/lib/validate/epic-wave-computation.ts
23591
+ function pythonRepr5(value) {
23592
+ return `'${value.replaceAll("\\", "\\\\").replaceAll("'", "\\'")}'`;
23593
+ }
23594
+ var EpicWaveCycleError = class extends Error {
23595
+ featureFolder;
23596
+ constructor(featureFolder) {
23597
+ super(
23598
+ `Epic dependency manifest contains a cycle at feature folder ${pythonRepr5(featureFolder)}; wave numbers cannot be computed for a cyclic dependency graph.`
23599
+ );
23600
+ this.name = "EpicWaveCycleError";
23601
+ this.featureFolder = featureFolder;
23602
+ }
23603
+ };
23604
+ function computeWaveNumbers(manifest) {
23605
+ const waveNumbers = /* @__PURE__ */ new Map();
23606
+ const inProgress = /* @__PURE__ */ new Set();
23607
+ function resolve4(featureFolder) {
23608
+ const existing = waveNumbers.get(featureFolder);
23609
+ if (existing !== void 0) {
23610
+ return existing;
23611
+ }
23612
+ if (inProgress.has(featureFolder)) {
23613
+ throw new EpicWaveCycleError(featureFolder);
23614
+ }
23615
+ inProgress.add(featureFolder);
23616
+ let waveNumber;
23617
+ try {
23618
+ const dependencies = manifest.get(featureFolder) ?? [];
23619
+ waveNumber = dependencies.length === 0 ? 0 : 1 + Math.max(...dependencies.map(resolve4));
23620
+ } finally {
23621
+ inProgress.delete(featureFolder);
23622
+ }
23623
+ waveNumbers.set(featureFolder, waveNumber);
23624
+ return waveNumber;
23625
+ }
23626
+ for (const featureFolder of manifest.keys()) {
23627
+ resolve4(featureFolder);
23628
+ }
23629
+ return waveNumbers;
23630
+ }
23631
+
23632
+ // ../../extensions/drm-copilot/src/lib/validate/epic-planner-git-integrity.ts
23633
+ var HASH_RE = /^[0-9a-fA-F]{40,64}$/;
23634
+ var COMMIT_RE = /^[0-9a-fA-F]{7,64}$/;
23635
+ var CommandRunnerGitRepository = class {
23636
+ constructor(workspaceRoot, runner) {
23637
+ this.workspaceRoot = workspaceRoot;
23638
+ this.runner = runner;
23639
+ }
23640
+ workspaceRoot;
23641
+ runner;
23642
+ run(arguments_) {
23643
+ const result = this.runner.run(["git", ...arguments_], {
23644
+ cwd: this.workspaceRoot,
23645
+ allowError: true
23646
+ });
23647
+ return { code: result.code, stdout: result.stdout.trim() };
23648
+ }
23649
+ refExists(ref) {
23650
+ return this.run(["rev-parse", "--verify", `${ref}^{commit}`]).code === 0;
23651
+ }
23652
+ commitExists(commit) {
23653
+ return this.run(["cat-file", "-e", `${commit}^{commit}`]).code === 0;
23654
+ }
23655
+ isAncestor(commit, ref) {
23656
+ return this.run(["merge-base", "--is-ancestor", commit, ref]).code === 0;
23657
+ }
23658
+ lastCommit(path11) {
23659
+ const result = this.run(["log", "-n", "1", "--format=%H", "--", path11]);
23660
+ return result.code === 0 && result.stdout.length > 0 ? result.stdout : void 0;
23661
+ }
23662
+ committedBlobHash(ref, path11) {
23663
+ const result = this.run(["rev-parse", "--verify", `${ref}:${path11}`]);
23664
+ return result.code === 0 && HASH_RE.test(result.stdout) ? result.stdout.toLowerCase() : void 0;
23665
+ }
23666
+ worktreeBlobHash(path11) {
23667
+ const result = this.run(["hash-object", "--", path11]);
23668
+ return result.code === 0 && HASH_RE.test(result.stdout) ? result.stdout.toLowerCase() : void 0;
23669
+ }
23670
+ };
23671
+ function isRecord(value) {
23672
+ return typeof value === "object" && value !== null && !Array.isArray(value);
23673
+ }
23674
+ function optionalIntegrity(state, parsed) {
23675
+ const errors = [];
23676
+ const integrityValue = state["integrity"];
23677
+ let integrity = {};
23678
+ if (integrityValue !== void 0) {
23679
+ if (!isRecord(integrityValue)) {
23680
+ errors.push(
23681
+ "Epic planner checkpoint integrity must be an object when present."
23682
+ );
23683
+ } else {
23684
+ integrity = integrityValue;
23685
+ }
23686
+ }
23687
+ const commits = [
23688
+ state["planning_commit"],
23689
+ integrity["planning_commit"],
23690
+ parsed.planningCommit
23691
+ ].filter((value) => value !== void 0);
23692
+ for (const commit of commits) {
23693
+ if (typeof commit !== "string" || !COMMIT_RE.test(commit)) {
23694
+ errors.push(
23695
+ "Optional planning_commit values must be 7-64 hexadecimal characters."
23696
+ );
23697
+ }
23698
+ }
23699
+ const normalized = commits.filter(
23700
+ (commit) => typeof commit === "string" && COMMIT_RE.test(commit)
23701
+ ).map((commit) => commit.toLowerCase());
23702
+ if (new Set(normalized).size > 1) {
23703
+ errors.push(
23704
+ "Optional planning_commit values in state and kickoff must match."
23705
+ );
23706
+ }
23707
+ const planHashes = { ...parsed.planHashes };
23708
+ const stateHashes = integrity["plan_hashes"] ?? state["plan_hashes"];
23709
+ if (stateHashes !== void 0) {
23710
+ if (!isRecord(stateHashes)) {
23711
+ errors.push("Optional plan_hashes must be an object keyed by plan path.");
23712
+ } else {
23713
+ for (const [path11, value] of Object.entries(stateHashes)) {
23714
+ if (typeof value !== "string" || !HASH_RE.test(value)) {
23715
+ errors.push(
23716
+ "Optional plan_hashes entries must map paths to 40-64 character hexadecimal hashes."
23717
+ );
23718
+ continue;
23719
+ }
23720
+ if (planHashes[path11] !== void 0 && planHashes[path11] !== value.toLowerCase()) {
23721
+ errors.push(
23722
+ `Optional plan hash declarations disagree for '${path11}'.`
23723
+ );
23724
+ }
23725
+ planHashes[path11] = value.toLowerCase();
23726
+ }
23727
+ }
23728
+ }
23729
+ return {
23730
+ ...normalized[0] === void 0 ? {} : { planningCommit: normalized[0] },
23731
+ planHashes,
23732
+ errors
23733
+ };
23734
+ }
23735
+ function validateCommittedFile(git, ref, path11, label) {
23736
+ const committed = git.committedBlobHash(ref, path11);
23737
+ const worktree = git.worktreeBlobHash(path11);
23738
+ if (committed === void 0) {
23739
+ return [
23740
+ `Execution readiness requires ${label} committed at ${ref}: ${path11}`
23741
+ ];
23742
+ }
23743
+ if (worktree === void 0) {
23744
+ return [`Execution readiness could not hash worktree ${label}: ${path11}`];
23745
+ }
23746
+ if (committed !== worktree) {
23747
+ return [
23748
+ `Execution readiness detected worktree drift for ${label}: ${path11}`
23749
+ ];
23750
+ }
23751
+ return [];
23752
+ }
23753
+ function validatePlanningGitIntegrity(state, parsed, plans, git) {
23754
+ const branch = state["integration_branch"];
23755
+ if (typeof branch !== "string" || !git.refExists(branch)) {
23756
+ return [
23757
+ `Execution readiness integration branch does not exist: '${String(branch)}'`
23758
+ ];
23759
+ }
23760
+ const errors = [];
23761
+ const integrity = optionalIntegrity(state, parsed);
23762
+ errors.push(...integrity.errors);
23763
+ if (integrity.planningCommit !== void 0) {
23764
+ if (!git.commitExists(integrity.planningCommit)) {
23765
+ errors.push(
23766
+ `Execution readiness planning commit does not exist: ${integrity.planningCommit}`
23767
+ );
23768
+ } else if (!git.isAncestor(integrity.planningCommit, branch)) {
23769
+ errors.push(
23770
+ `Execution readiness planning commit ${integrity.planningCommit} is not an ancestor of ${branch}.`
23771
+ );
23772
+ }
23773
+ }
23774
+ const features = Array.isArray(state["features"]) ? state["features"].filter(isRecord) : [];
23775
+ plans.forEach((plan, index) => {
23776
+ const derived = git.lastCommit(plan);
23777
+ if (derived === void 0) {
23778
+ errors.push(
23779
+ `Execution readiness could not derive a planning commit for ${plan}.`
23780
+ );
23781
+ return;
23782
+ }
23783
+ if (!git.commitExists(derived) || !git.isAncestor(derived, branch)) {
23784
+ errors.push(
23785
+ `Execution readiness derived plan commit ${derived} is not on ${branch}.`
23786
+ );
23787
+ return;
23788
+ }
23789
+ const candidates = [derived];
23790
+ const feature = features[index] ?? {};
23791
+ const featureCommit = feature["planning_commit"];
23792
+ if (featureCommit !== void 0) {
23793
+ if (typeof featureCommit !== "string" || !COMMIT_RE.test(featureCommit)) {
23794
+ errors.push(
23795
+ `Epic planner checkpoint features[${index}].planning_commit must be hexadecimal.`
23796
+ );
23797
+ } else {
23798
+ candidates.push(featureCommit.toLowerCase());
23799
+ }
23800
+ }
23801
+ if (integrity.planningCommit !== void 0) {
23802
+ candidates.push(integrity.planningCommit);
23803
+ }
23804
+ const worktreeHash = git.worktreeBlobHash(plan);
23805
+ if (worktreeHash === void 0) {
23806
+ errors.push(
23807
+ `Execution readiness could not hash worktree atomic plan: ${plan}`
23808
+ );
23809
+ return;
23810
+ }
23811
+ for (const candidate of [...new Set(candidates)]) {
23812
+ if (!git.commitExists(candidate)) {
23813
+ errors.push(
23814
+ `Execution readiness planning commit does not exist: ${candidate}`
23815
+ );
23816
+ continue;
23817
+ }
23818
+ if (!git.isAncestor(candidate, branch)) {
23819
+ errors.push(
23820
+ `Execution readiness planning commit ${candidate} is not an ancestor of ${branch}.`
23821
+ );
23822
+ continue;
23823
+ }
23824
+ const committedHash = git.committedBlobHash(candidate, plan);
23825
+ if (committedHash === void 0) {
23826
+ errors.push(
23827
+ `Execution readiness planning commit ${candidate} does not contain ${plan}.`
23828
+ );
23829
+ } else if (committedHash !== worktreeHash) {
23830
+ errors.push(
23831
+ `Execution readiness detected committed plan drift: ${plan}`
23832
+ );
23833
+ }
23834
+ }
23835
+ const declared = feature["plan_hash"] ?? integrity.planHashes[plan];
23836
+ if (declared !== void 0) {
23837
+ if (typeof declared !== "string" || !HASH_RE.test(declared)) {
23838
+ errors.push(
23839
+ `Optional plan hash for ${plan} must be 40-64 hexadecimal characters.`
23840
+ );
23841
+ } else if (declared.toLowerCase() !== worktreeHash) {
23842
+ errors.push(
23843
+ `Optional plan hash does not match committed bytes for ${plan}.`
23844
+ );
23845
+ }
23846
+ }
23847
+ });
23848
+ for (const extra of Object.keys(integrity.planHashes).filter((path11) => !plans.includes(path11)).sort()) {
23849
+ errors.push(
23850
+ `Optional plan_hashes contains unknown planner path: '${extra}'.`
23851
+ );
23852
+ }
23853
+ return errors;
23854
+ }
23855
+
23856
+ // ../../extensions/drm-copilot/src/lib/validate/epic-planner-launch-evidence.ts
23857
+ var import_node_crypto = require("node:crypto");
23858
+ var LAUNCH_ROOT = "artifacts/orchestration/epic-child-launches";
23859
+ var SHA256_RE = /^[0-9a-f]{64}$/;
23860
+ function isRecord2(value) {
22251
23861
  return typeof value === "object" && value !== null && !Array.isArray(value);
22252
23862
  }
22253
- function missingObjectKeys(value, keys) {
22254
- if (!isObject4(value)) {
22255
- return [...keys];
23863
+ function isNonEmpty(value) {
23864
+ return typeof value === "string" && value.trim().length > 0 && value === value.trim();
23865
+ }
23866
+ function launchPath(value, context, field) {
23867
+ if (!isNonEmpty(value) || value.includes("\0")) {
23868
+ return {
23869
+ errors: [`${field} must identify a launch artifact in this repository.`]
23870
+ };
23871
+ }
23872
+ let normalized = toPosixPath2(value);
23873
+ const root = toPosixPath2(context.workspaceRoot).replace(/\/+$/, "");
23874
+ const rootPrefix = `${root}/`;
23875
+ if (normalized.toLowerCase().startsWith(rootPrefix.toLowerCase())) {
23876
+ normalized = normalized.slice(rootPrefix.length);
23877
+ }
23878
+ const parts = normalized.split("/");
23879
+ if (normalized.startsWith("/") || /^[A-Za-z]:/.test(normalized) || parts.includes("..") || parts.includes(".") || !normalized.startsWith(`${LAUNCH_ROOT}/`)) {
23880
+ return {
23881
+ errors: [`${field} must identify a launch artifact in this repository.`]
23882
+ };
23883
+ }
23884
+ return { relative: parts.join("/"), errors: [] };
23885
+ }
23886
+ function readJson(relative2, context, label) {
23887
+ const path11 = `${toPosixPath2(context.workspaceRoot).replace(/\/+$/, "")}/${relative2}`;
23888
+ if (!context.fileSystem.isFile(path11)) {
23889
+ return { errors: [`Execution readiness requires ${label}: ${relative2}`] };
23890
+ }
23891
+ let text;
23892
+ try {
23893
+ text = context.fileSystem.readTextFile(path11);
23894
+ } catch (error2) {
23895
+ return {
23896
+ errors: [
23897
+ `Execution readiness could not read ${label} ${relative2}: ${String(error2)}`
23898
+ ]
23899
+ };
23900
+ }
23901
+ let value;
23902
+ try {
23903
+ value = JSON.parse(text);
23904
+ } catch (error2) {
23905
+ return {
23906
+ text,
23907
+ errors: [
23908
+ `Execution readiness ${label} is not valid JSON: ${error2 instanceof Error ? error2.message : String(error2)}`
23909
+ ]
23910
+ };
23911
+ }
23912
+ if (!isRecord2(value)) {
23913
+ return {
23914
+ text,
23915
+ errors: [`Execution readiness ${label} must be a JSON object.`]
23916
+ };
23917
+ }
23918
+ return { value, text, errors: [] };
23919
+ }
23920
+ function expectedFeatureBindings(feature) {
23921
+ const delegation = feature["delegation_receipt"];
23922
+ const model = feature["model_routing_receipt"];
23923
+ return {
23924
+ issue_num: feature["issue_num"],
23925
+ feature_folder: feature["feature_folder"],
23926
+ delegation_id: isRecord2(delegation) ? delegation["delegation_id"] : void 0,
23927
+ deployment_agent: isRecord2(model) ? model["deployment_agent"] : void 0,
23928
+ model: isRecord2(model) ? model["model"] : void 0,
23929
+ model_reasoning_effort: isRecord2(model) ? model["model_reasoning_effort"] : void 0,
23930
+ execution_context: "epic_preparation_child",
23931
+ branch_name: feature["branch_name"],
23932
+ worktree_path: feature["worktree_path"]
23933
+ };
23934
+ }
23935
+ function parseTimestamp(value, prefix, key, errors) {
23936
+ if (!isNonEmpty(value) || !/(?:Z|[+-]\d\d:\d\d)$/.test(value) || Number.isNaN(Date.parse(value))) {
23937
+ errors.push(`${prefix}.${key} must be an ISO-8601 timestamp with offset.`);
23938
+ return void 0;
23939
+ }
23940
+ return new Date(value);
23941
+ }
23942
+ function validateCompletedReceipt(receipt, prefix) {
23943
+ const errors = [];
23944
+ if (receipt["schema_version"] !== 2 || receipt["state"] !== "completed") {
23945
+ errors.push(`${prefix} must be a schema 2 completed launch receipt.`);
23946
+ }
23947
+ if (receipt["exit_code"] !== 0) {
23948
+ errors.push(`${prefix}.exit_code must be 0.`);
23949
+ }
23950
+ if (!isNonEmpty(receipt["codex_session_id"])) {
23951
+ errors.push(`${prefix}.codex_session_id must be a non-empty string.`);
23952
+ }
23953
+ const bound = parseTimestamp(
23954
+ receipt["session_bound_at"],
23955
+ prefix,
23956
+ "session_bound_at",
23957
+ errors
23958
+ );
23959
+ const completed = parseTimestamp(
23960
+ receipt["completed_at"],
23961
+ prefix,
23962
+ "completed_at",
23963
+ errors
23964
+ );
23965
+ if (bound !== void 0 && completed !== void 0 && completed < bound) {
23966
+ errors.push(`${prefix}.completed_at must not precede session_bound_at.`);
23967
+ }
23968
+ return errors;
23969
+ }
23970
+ function validateSpec(receipt, expected, context, prefix) {
23971
+ const specPath = launchPath(
23972
+ receipt["spec_path"],
23973
+ context,
23974
+ `${prefix}.spec_path`
23975
+ );
23976
+ const errors = [...specPath.errors];
23977
+ if (specPath.relative === void 0) {
23978
+ return errors;
23979
+ }
23980
+ const receiptPath = launchPath(
23981
+ receipt["receipt_path"],
23982
+ context,
23983
+ `${prefix}.receipt_path`
23984
+ );
23985
+ if (receiptPath.relative !== void 0 && specPath.relative.slice(0, specPath.relative.lastIndexOf("/")) !== receiptPath.relative.slice(0, receiptPath.relative.lastIndexOf("/"))) {
23986
+ errors.push(`${prefix}.spec_path must share the receipt wave directory.`);
23987
+ }
23988
+ const spec = readJson(
23989
+ specPath.relative,
23990
+ context,
23991
+ `${prefix} launch specification`
23992
+ );
23993
+ errors.push(...spec.errors);
23994
+ const digest = receipt["spec_sha256"];
23995
+ if (!isNonEmpty(digest) || !SHA256_RE.test(digest)) {
23996
+ errors.push(`${prefix}.spec_sha256 must be a lowercase SHA-256 hash.`);
23997
+ } else if (spec.text !== void 0 && (0, import_node_crypto.createHash)("sha256").update(spec.text, "utf8").digest("hex") !== digest) {
23998
+ errors.push(`${prefix}.spec_sha256 does not match spec_path bytes.`);
23999
+ }
24000
+ if (spec.value === void 0) {
24001
+ return errors;
24002
+ }
24003
+ if (spec.value["wave_id"] !== receipt["wave_id"]) {
24004
+ errors.push(`${prefix}.wave_id must match its launch specification.`);
24005
+ }
24006
+ const launches = spec.value["launches"];
24007
+ const matching = Array.isArray(launches) ? launches.filter(
24008
+ (item) => isRecord2(item) && item["launch_id"] === receipt["launch_id"]
24009
+ ) : [];
24010
+ if (matching.length !== 1) {
24011
+ errors.push(
24012
+ `${prefix}.launch_id must identify exactly one specification launch.`
24013
+ );
24014
+ } else {
24015
+ const launch = matching[0];
24016
+ if (launch !== void 0) {
24017
+ for (const [key, value] of Object.entries(expected)) {
24018
+ if (launch[key] !== value) {
24019
+ errors.push(
24020
+ `${prefix} specification launch.${key} must match the feature.`
24021
+ );
24022
+ }
24023
+ }
24024
+ }
24025
+ }
24026
+ return errors;
24027
+ }
24028
+ function validateReceipt3(feature, index, context) {
24029
+ const prefix = `Epic planner checkpoint features[${index}] launch receipt`;
24030
+ const path11 = launchPath(
24031
+ feature["launch_receipt_path"],
24032
+ context,
24033
+ `${prefix} path`
24034
+ );
24035
+ const errors = [...path11.errors];
24036
+ if (path11.relative === void 0) {
24037
+ return { errors };
24038
+ }
24039
+ const result = readJson(path11.relative, context, prefix);
24040
+ errors.push(...result.errors);
24041
+ if (result.value === void 0) {
24042
+ return { path: path11.relative, errors };
24043
+ }
24044
+ const receipt = result.value;
24045
+ const expected = expectedFeatureBindings(feature);
24046
+ for (const [key, value] of Object.entries(expected)) {
24047
+ if (receipt[key] !== value) {
24048
+ errors.push(`${prefix}.${key} must match the feature.`);
24049
+ }
24050
+ }
24051
+ const receiptPath = launchPath(
24052
+ receipt["receipt_path"],
24053
+ context,
24054
+ `${prefix}.receipt_path`
24055
+ );
24056
+ errors.push(...receiptPath.errors);
24057
+ if (receiptPath.relative !== void 0 && receiptPath.relative !== path11.relative) {
24058
+ errors.push(`${prefix}.receipt_path must identify the receipt file.`);
24059
+ }
24060
+ const launchId = receipt["launch_id"];
24061
+ const name = path11.relative.split("/").at(-1);
24062
+ if (!isNonEmpty(launchId) || name !== `${launchId}.receipt.json`) {
24063
+ errors.push(`${prefix}.launch_id must match the receipt filename.`);
24064
+ }
24065
+ const statusPath = launchPath(
24066
+ receipt["status_path"],
24067
+ context,
24068
+ `${prefix}.status_path`
24069
+ );
24070
+ errors.push(...statusPath.errors);
24071
+ const featureStatus = launchPath(
24072
+ feature["launch_status_path"],
24073
+ context,
24074
+ `${prefix}.status_path`
24075
+ );
24076
+ if (statusPath.relative !== void 0 && statusPath.relative !== featureStatus.relative) {
24077
+ errors.push(`${prefix}.status_path must match the feature status file.`);
24078
+ }
24079
+ errors.push(
24080
+ ...validateCompletedReceipt(receipt, prefix),
24081
+ ...validateSpec(receipt, expected, context, prefix)
24082
+ );
24083
+ return { receipt, path: path11.relative, errors };
24084
+ }
24085
+ function validateStatus(feature, index, receipt, receiptPath, status, statusPath, context) {
24086
+ const prefix = `Epic planner checkpoint features[${index}] launch status`;
24087
+ const errors = [];
24088
+ if (status["schema_version"] !== 2 || status["state"] !== "completed") {
24089
+ errors.push(`${prefix} shared wave must be a schema 2 completed status.`);
24090
+ }
24091
+ const failure = status["failure"];
24092
+ if (failure !== void 0 && failure !== null && failure !== "") {
24093
+ errors.push(`${prefix} shared wave must not contain a failure.`);
24094
+ }
24095
+ if (status["wave_id"] !== receipt["wave_id"]) {
24096
+ errors.push(`${prefix}.wave_id must match the launch receipt.`);
24097
+ }
24098
+ const launches = status["launches"];
24099
+ const launchId = receipt["launch_id"];
24100
+ const entry = isRecord2(launches) && typeof launchId === "string" ? launches[launchId] : void 0;
24101
+ if (!isRecord2(entry)) {
24102
+ const launchLabel = typeof launchId === "string" ? `'${launchId}'` : "<invalid>";
24103
+ return [...errors, `${prefix} must contain launch_id ${launchLabel}.`];
24104
+ }
24105
+ if (entry["state"] !== "completed" || entry["exit_code"] !== 0) {
24106
+ errors.push(`${prefix} launch must be completed with exit_code 0.`);
24107
+ }
24108
+ const entryReceipt = launchPath(
24109
+ entry["receipt_path"],
24110
+ context,
24111
+ `${prefix} launch.receipt_path`
24112
+ );
24113
+ errors.push(...entryReceipt.errors);
24114
+ if (entryReceipt.relative !== void 0 && entryReceipt.relative !== receiptPath) {
24115
+ errors.push(
24116
+ `${prefix} launch.receipt_path must match the feature receipt.`
24117
+ );
24118
+ }
24119
+ if (entry["codex_session_id"] !== receipt["codex_session_id"]) {
24120
+ errors.push(`${prefix} launch.codex_session_id must match the receipt.`);
24121
+ }
24122
+ if (entry["completed_at"] !== receipt["completed_at"]) {
24123
+ errors.push(`${prefix} launch.completed_at must match the receipt.`);
24124
+ }
24125
+ if (statusPath.slice(0, statusPath.lastIndexOf("/")) !== receiptPath.slice(0, receiptPath.lastIndexOf("/"))) {
24126
+ errors.push(`${prefix} path must share the receipt wave directory.`);
24127
+ }
24128
+ const resolved = launchPath(feature["launch_status_path"], context, "status");
24129
+ if (resolved.relative !== statusPath) {
24130
+ errors.push(`${prefix} path must match the shared status file.`);
24131
+ }
24132
+ return errors;
24133
+ }
24134
+ function validateEpicPlannerLaunchEvidence(state, context) {
24135
+ const features = state["features"];
24136
+ if (!Array.isArray(features)) {
24137
+ return [];
24138
+ }
24139
+ const errors = [];
24140
+ let statusPath;
24141
+ let status;
24142
+ features.forEach((item, index) => {
24143
+ if (!isRecord2(item)) {
24144
+ return;
24145
+ }
24146
+ const receipt = validateReceipt3(item, index, context);
24147
+ errors.push(...receipt.errors);
24148
+ const featureStatus = launchPath(
24149
+ item["launch_status_path"],
24150
+ context,
24151
+ `Epic planner checkpoint features[${index}] launch status path`
24152
+ );
24153
+ errors.push(...featureStatus.errors);
24154
+ if (featureStatus.relative !== void 0 && statusPath !== void 0 && featureStatus.relative !== statusPath) {
24155
+ errors.push(
24156
+ "Epic planner preparation features must share one launch_status_path."
24157
+ );
24158
+ }
24159
+ if (featureStatus.relative !== void 0 && statusPath === void 0) {
24160
+ statusPath = featureStatus.relative;
24161
+ const result = readJson(
24162
+ statusPath,
24163
+ context,
24164
+ "the shared preparation launch status"
24165
+ );
24166
+ errors.push(...result.errors);
24167
+ status = result.value;
24168
+ }
24169
+ if (receipt.receipt !== void 0 && receipt.path !== void 0 && status !== void 0 && statusPath !== void 0) {
24170
+ errors.push(
24171
+ ...validateStatus(
24172
+ item,
24173
+ index,
24174
+ receipt.receipt,
24175
+ receipt.path,
24176
+ status,
24177
+ statusPath,
24178
+ context
24179
+ )
24180
+ );
24181
+ }
24182
+ });
24183
+ return errors;
24184
+ }
24185
+
24186
+ // ../../extensions/drm-copilot/src/lib/validate/epic-planner-readiness-integrity.ts
24187
+ var ALL_CLEAR = "PREFLIGHT: ALL CLEAR";
24188
+ var SLUG_RE = /^[a-z0-9][a-z0-9-]*$/;
24189
+ function join9(root, relative2) {
24190
+ return `${toPosixPath2(root).replace(/\/+$/, "")}/${relative2.replace(/^\/+/, "")}`;
24191
+ }
24192
+ function relativePath(value, field) {
24193
+ if (typeof value !== "string" || value.trim().length === 0) {
24194
+ return {
24195
+ errors: [`${field} must be a non-empty repository-relative path.`]
24196
+ };
22256
24197
  }
22257
- const missing = [];
22258
- for (const key of keys) {
22259
- const item = value[key];
22260
- if (item === void 0 || typeof item === "string" && item.trim() === "") {
22261
- missing.push(key);
22262
- }
24198
+ if (value.includes("\\")) {
24199
+ return { errors: [`${field} must use forward slashes.`] };
22263
24200
  }
22264
- return missing;
24201
+ const parts = value.split("/");
24202
+ if (value.startsWith("/") || /^[A-Za-z]:/.test(value) || parts.includes("..") || parts.includes(".")) {
24203
+ return { errors: [`${field} must stay within the workspace root.`] };
24204
+ }
24205
+ return { path: parts.join("/"), errors: [] };
22265
24206
  }
22266
- function validateCompletionPrGate(state) {
22267
- const prGate = state["pr_gate"];
22268
- const missing = missingObjectKeys(prGate, PR_GATE_KEYS);
22269
- if (!isObject4(prGate)) {
22270
- return [
22271
- `Checkpoint completion validation failed: pr_gate must be an object with keys: ${PR_GATE_KEYS.join(", ")}.`
22272
- ];
24207
+ function readRequired(context, relative2, label) {
24208
+ const path11 = join9(context.workspaceRoot, relative2);
24209
+ if (!context.fileSystem.isFile(path11)) {
24210
+ return { errors: [`Execution readiness requires ${label}: ${relative2}`] };
24211
+ }
24212
+ try {
24213
+ return { text: context.fileSystem.readTextFile(path11), errors: [] };
24214
+ } catch (error2) {
24215
+ return {
24216
+ errors: [
24217
+ `Execution readiness could not read ${label} ${relative2}: ${String(error2)}`
24218
+ ]
24219
+ };
22273
24220
  }
24221
+ }
24222
+ function validateArtifactSource(stateText, context) {
24223
+ const expected = "artifacts/orchestration/epic-planner-state.json";
24224
+ const root = toPosixPath2(context.workspaceRoot).replace(/\/+$/, "");
24225
+ const artifact = toPosixPath2(context.artifactPath);
24226
+ if (!artifact.startsWith(`${root}/`)) {
24227
+ return ["Epic planner artifact path must stay within the workspace root."];
24228
+ }
24229
+ const supplied = artifact.slice(root.length + 1);
22274
24230
  const errors = [];
22275
- if (missing.length > 0) {
24231
+ if (supplied !== expected) {
22276
24232
  errors.push(
22277
- `Checkpoint completion validation failed: pr_gate missing required fields: ${missing.join(", ")}.`
24233
+ `Execution-ready epic planner artifact path must be '${expected}'.`
22278
24234
  );
22279
24235
  }
22280
- if (state["issue-num"] === ISSUE_232) {
22281
- if (prGate["head_branch"] !== ISSUE_232_BRANCH) {
22282
- errors.push(
22283
- `Checkpoint completion validation failed: pr_gate.head_branch must be ${ISSUE_232_BRANCH} for Issue #232.`
22284
- );
22285
- }
24236
+ const source = readRequired(context, supplied, "the epic planner checkpoint");
24237
+ errors.push(...source.errors);
24238
+ if (source.text !== void 0 && source.text !== stateText) {
24239
+ errors.push(
24240
+ "Supplied epic planner text does not match artifact_path bytes."
24241
+ );
22286
24242
  }
22287
24243
  return errors;
22288
24244
  }
22289
- function validateCompletionCiGate(state) {
22290
- const ciGate = state["ci_gate"];
22291
- const missing = missingObjectKeys(ciGate, CI_GATE_KEYS);
22292
- if (!isObject4(ciGate)) {
22293
- return [
22294
- `Checkpoint completion validation failed: ci_gate must be an object with keys: ${CI_GATE_KEYS.join(", ")}.`
22295
- ];
24245
+ function validateFeatureFiles(feature, index, context) {
24246
+ const prefix = `Epic planner checkpoint features[${index}]`;
24247
+ const folderResult = relativePath(
24248
+ feature["feature_folder"],
24249
+ `${prefix}.feature_folder`
24250
+ );
24251
+ const planResult = relativePath(feature["plan_path"], `${prefix}.plan_path`);
24252
+ const researchResult = relativePath(
24253
+ feature["research_path"],
24254
+ `${prefix}.research_path`
24255
+ );
24256
+ const errors = [
24257
+ ...folderResult.errors,
24258
+ ...planResult.errors,
24259
+ ...researchResult.errors
24260
+ ];
24261
+ const folder = folderResult.path;
24262
+ const plan = planResult.path;
24263
+ const researchPath = researchResult.path;
24264
+ if (folder === void 0 || plan === void 0 || researchPath === void 0) {
24265
+ return { ...plan === void 0 ? {} : { plan }, errors };
24266
+ }
24267
+ if (!folder.startsWith("docs/features/active/") && !folder.startsWith("docs/features/completed/")) {
24268
+ errors.push(
24269
+ `${prefix}.feature_folder must be under docs/features/active or completed.`
24270
+ );
24271
+ }
24272
+ const folderPath = join9(context.workspaceRoot, folder);
24273
+ if (!context.fileSystem.isDirectory(folderPath)) {
24274
+ errors.push(`Execution readiness requires feature folder: ${folder}`);
24275
+ return { plan, errors };
24276
+ }
24277
+ if (!plan.startsWith(`${folder}/`)) {
24278
+ errors.push(`${prefix}.plan_path must be inside its feature_folder.`);
24279
+ }
24280
+ for (const name of ["issue.md", "spec.md", "user-story.md"]) {
24281
+ errors.push(
24282
+ ...readRequired(context, `${folder}/${name}`, `${prefix} ${name}`).errors
24283
+ );
24284
+ }
24285
+ errors.push(...readRequired(context, plan, `${prefix} atomic plan`).errors);
24286
+ if (!researchPath.startsWith("artifacts/research/") && !researchPath.startsWith(`${folder}/`)) {
24287
+ errors.push(
24288
+ `${prefix}.research_path must be under artifacts/research or its feature_folder.`
24289
+ );
24290
+ }
24291
+ errors.push(
24292
+ ...readRequired(context, researchPath, `${prefix} research evidence`).errors
24293
+ );
24294
+ const evidenceValue = feature["preflight_evidence_path"];
24295
+ const evidencePaths = [];
24296
+ if (evidenceValue !== void 0) {
24297
+ const evidence = relativePath(
24298
+ evidenceValue,
24299
+ `${prefix}.preflight_evidence_path`
24300
+ );
24301
+ errors.push(...evidence.errors);
24302
+ if (evidence.path !== void 0) {
24303
+ if (!evidence.path.startsWith(`${folder}/`)) {
24304
+ errors.push(
24305
+ `${prefix}.preflight_evidence_path must be inside its feature_folder.`
24306
+ );
24307
+ }
24308
+ evidencePaths.push(join9(context.workspaceRoot, evidence.path));
24309
+ }
24310
+ } else {
24311
+ evidencePaths.push(
24312
+ ...context.fileSystem.glob(folderPath, "**/*preflight*.md")
24313
+ );
24314
+ }
24315
+ let allClear = false;
24316
+ for (const path11 of evidencePaths) {
24317
+ if (!context.fileSystem.isFile(path11)) {
24318
+ continue;
24319
+ }
24320
+ try {
24321
+ allClear = context.fileSystem.readTextFile(path11).includes(ALL_CLEAR);
24322
+ } catch {
24323
+ continue;
24324
+ }
24325
+ if (allClear) {
24326
+ break;
24327
+ }
24328
+ }
24329
+ if (!allClear) {
24330
+ errors.push(
24331
+ `Execution readiness requires preflight evidence containing '${ALL_CLEAR}' under ${folder}.`
24332
+ );
22296
24333
  }
24334
+ return { plan, errors };
24335
+ }
24336
+ function isRecord3(value) {
24337
+ return typeof value === "object" && value !== null && !Array.isArray(value);
24338
+ }
24339
+ function validateKickoffAgainstState(parsed, state, label) {
22297
24340
  const errors = [];
22298
- if (missing.length > 0) {
24341
+ const slug = state["epic_feature_folder"];
24342
+ if (parsed.slug !== slug || parsed.invocationSlug !== slug) {
22299
24343
  errors.push(
22300
- `Checkpoint completion validation failed: ci_gate missing required fields: ${missing.join(", ")}.`
24344
+ `${label} kickoff slug and /epic-run slug must match state '${String(slug)}'.`
22301
24345
  );
22302
24346
  }
22303
- if (ciGate["conclusion"] !== "success") {
24347
+ if (parsed.manifestPath !== state["epic_manifest_path"]) {
22304
24348
  errors.push(
22305
- "Checkpoint completion validation failed: ci_gate.conclusion must be success."
24349
+ `${label} kickoff manifest reference must match epic_manifest_path.`
22306
24350
  );
22307
24351
  }
22308
- const prGate = state["pr_gate"];
22309
- const prHeadSha = isObject4(prGate) ? prGate["head_sha"] : null;
22310
- if (prHeadSha !== null && prHeadSha !== void 0 && ciGate["head_sha"] !== prHeadSha) {
24352
+ if (parsed.integrationBranch !== state["integration_branch"]) {
22311
24353
  errors.push(
22312
- "Checkpoint completion validation failed: ci_gate.head_sha must match pr_gate.head_sha."
24354
+ `${label} kickoff integration branch must match planner state.`
24355
+ );
24356
+ }
24357
+ const expected = Array.isArray(state["features"]) ? state["features"].filter(isRecord3).map((feature) => [
24358
+ feature["issue_num"],
24359
+ feature["feature_folder"],
24360
+ feature["wave"],
24361
+ feature["complexity_band"],
24362
+ feature["plan_path"]
24363
+ ]) : [];
24364
+ const actual = parsed.features.map((feature) => [
24365
+ feature.issueNum,
24366
+ feature.featureFolder,
24367
+ feature.wave,
24368
+ feature.complexity,
24369
+ feature.planPath
24370
+ ]);
24371
+ if (JSON.stringify(actual) !== JSON.stringify(expected)) {
24372
+ errors.push(
24373
+ `${label} kickoff feature table must exactly match planner state order and values.`
22313
24374
  );
22314
24375
  }
22315
24376
  return errors;
22316
24377
  }
22317
- function validateIssue232PromotionReceipts(state) {
22318
- if (state["issue-num"] !== ISSUE_232) {
22319
- return [];
24378
+ function validateEpicReadinessIntegrity(state, stateText, context) {
24379
+ const errors = validateArtifactSource(stateText, context);
24380
+ const slug = state["epic_feature_folder"];
24381
+ if (typeof slug !== "string" || !SLUG_RE.test(slug)) {
24382
+ return [
24383
+ ...errors,
24384
+ "Epic planner checkpoint epic_feature_folder must be a slug."
24385
+ ];
22320
24386
  }
22321
- const receipts = state["delegation_receipts"];
22322
- let promotionSource = "promotion_receipts";
22323
- let promotion = state["promotion_receipts"];
22324
- if (isObject4(receipts)) {
22325
- const namespacedPromotion = receipts[PROMOTION_RECEIPT_NAMESPACE_KEY];
22326
- if (namespacedPromotion !== void 0 && namespacedPromotion !== null) {
22327
- promotionSource = "delegation_receipts.promotion";
22328
- promotion = namespacedPromotion;
24387
+ const manifestPath = `docs/features/epics/${slug}/epic.md`;
24388
+ if (state["epic_manifest_path"] !== manifestPath) {
24389
+ errors.push(
24390
+ `Execution-ready epic_manifest_path must be '${manifestPath}'.`
24391
+ );
24392
+ }
24393
+ const branch = `epic/${slug}-integration`;
24394
+ if (state["integration_branch"] !== branch) {
24395
+ errors.push(`Execution-ready integration_branch must be '${branch}'.`);
24396
+ }
24397
+ const manifest = readRequired(context, manifestPath, "the epic manifest");
24398
+ errors.push(...manifest.errors);
24399
+ const ignoredResult = relativePath(
24400
+ state["kickoff_prompt_path"],
24401
+ "Epic planner checkpoint kickoff_prompt_path"
24402
+ );
24403
+ errors.push(...ignoredResult.errors);
24404
+ const durablePath = `docs/features/epics/${slug}/epic-kickoff.md`;
24405
+ const durable = readRequired(
24406
+ context,
24407
+ durablePath,
24408
+ "the committed epic kickoff"
24409
+ );
24410
+ errors.push(...durable.errors);
24411
+ const ignored = ignoredResult.path === void 0 ? { errors: [] } : readRequired(context, ignoredResult.path, "the ignored epic kickoff");
24412
+ errors.push(...ignored.errors);
24413
+ let parsed;
24414
+ if (durable.text !== void 0) {
24415
+ const result = parseEpicKickoff(durable.text);
24416
+ errors.push(...result.errors);
24417
+ parsed = result.parsed;
24418
+ if (parsed !== void 0) {
24419
+ errors.push(...validateKickoffAgainstState(parsed, state, "Committed"));
24420
+ }
24421
+ }
24422
+ if (ignored.text !== void 0) {
24423
+ const result = parseEpicKickoff(ignored.text);
24424
+ errors.push(...result.errors);
24425
+ if (result.parsed !== void 0) {
24426
+ errors.push(
24427
+ ...validateKickoffAgainstState(result.parsed, state, "Ignored")
24428
+ );
24429
+ }
24430
+ if (durable.text !== void 0 && ignored.text !== durable.text) {
24431
+ errors.push("Committed and ignored epic kickoff bytes must match.");
22329
24432
  }
22330
24433
  }
22331
- const missing = missingObjectKeys(promotion, PROMOTION_RECEIPT_KEYS);
22332
- if (missing.length > 0) {
22333
- return [
22334
- `Checkpoint completion validation failed: ${promotionSource} missing required Issue #232 receipt keys: ${missing.join(", ")}.`
22335
- ];
24434
+ const plans = [];
24435
+ if (Array.isArray(state["features"])) {
24436
+ state["features"].forEach((feature, index) => {
24437
+ if (!isRecord3(feature)) {
24438
+ return;
24439
+ }
24440
+ const result = validateFeatureFiles(feature, index, context);
24441
+ errors.push(...result.errors);
24442
+ if (result.plan !== void 0) {
24443
+ plans.push(result.plan);
24444
+ }
24445
+ });
22336
24446
  }
22337
- return [];
24447
+ errors.push(...validateEpicPlannerLaunchEvidence(state, context));
24448
+ if (parsed !== void 0) {
24449
+ errors.push(
24450
+ ...validatePlanningGitIntegrity(state, parsed, plans, context.git),
24451
+ ...validateCommittedFile(
24452
+ context.git,
24453
+ String(state["integration_branch"]),
24454
+ durablePath,
24455
+ "durable kickoff"
24456
+ )
24457
+ );
24458
+ if (manifest.text !== void 0) {
24459
+ errors.push(
24460
+ ...validateCommittedFile(
24461
+ context.git,
24462
+ String(state["integration_branch"]),
24463
+ manifestPath,
24464
+ "epic manifest"
24465
+ )
24466
+ );
24467
+ }
24468
+ }
24469
+ return errors;
22338
24470
  }
22339
24471
 
22340
- // ../../extensions/drm-copilot/src/lib/validate/orchestrator-state-human-interaction.ts
22341
- var HUMAN_INTERACTION_KEY = "human_interaction";
22342
- var HUMAN_INTERACTION_REQUIREMENTS_KEY = "requirements";
22343
- var HUMAN_INTERACTION_RESPONSE_ENUM = /* @__PURE__ */ new Set([
22344
- "scope_change",
22345
- "exception",
22346
- "halt"
22347
- ]);
22348
- var HUMAN_INTERACTION_EXCEPTION_RESPONSE = "exception";
22349
- function isObject5(value) {
24472
+ // ../../extensions/drm-copilot/src/lib/validate/epic-planner-state-core.ts
24473
+ var REQUIRED_KEYS3 = [
24474
+ "objective",
24475
+ "epic_feature_folder",
24476
+ "epic_manifest_path",
24477
+ "integration_branch",
24478
+ "max_parallel_features",
24479
+ "epic_worthiness",
24480
+ "features",
24481
+ "kickoff_prompt_path",
24482
+ "completed_steps",
24483
+ "next_step",
24484
+ "last_updated"
24485
+ ];
24486
+ var REQUIRED_FEATURE_KEYS = [
24487
+ "issue_num",
24488
+ "feature_folder",
24489
+ "depends_on",
24490
+ "wave",
24491
+ "complexity_band",
24492
+ "preparation_status",
24493
+ "research_path",
24494
+ "plan_path",
24495
+ "preflight_status"
24496
+ ];
24497
+ var COMPLEXITY_BANDS = /* @__PURE__ */ new Set(["C1", "C2", "C3", "C4"]);
24498
+ var COMPLEXITY_BAND_REPR = "('C1', 'C2', 'C3', 'C4')";
24499
+ var READY_NEXT_STEP = "EPIC_EXECUTION_READY";
24500
+ var NON_EPIC_NEXT_STEP = "NON_EPIC_RECOMMENDED";
24501
+ function isObject7(value) {
22350
24502
  return typeof value === "object" && value !== null && !Array.isArray(value);
22351
24503
  }
22352
- function pythonRepr2(value) {
22353
- if (value === null || value === void 0) {
24504
+ function pythonRepr6(value) {
24505
+ if (value === void 0 || value === null) {
22354
24506
  return "None";
22355
24507
  }
22356
- if (value === true) {
22357
- return "True";
24508
+ if (typeof value === "string") {
24509
+ return `'${value.replaceAll("\\", "\\\\").replaceAll("'", "\\'")}'`;
22358
24510
  }
22359
- if (value === false) {
22360
- return "False";
24511
+ if (typeof value === "boolean") {
24512
+ return value ? "True" : "False";
24513
+ }
24514
+ if (Array.isArray(value)) {
24515
+ return `[${value.map(pythonRepr6).join(", ")}]`;
22361
24516
  }
22362
24517
  return String(value);
22363
24518
  }
22364
- function validateHumanInteraction(humanInteraction) {
22365
- const errors = [];
22366
- if (!isObject5(humanInteraction)) {
22367
- errors.push("Checkpoint human_interaction must be an object when present.");
24519
+ function validateExpectedTopologyFields(value, prefix, expected) {
24520
+ const errors = validateCodexTopologyReceipt(value, prefix);
24521
+ if (!isObject7(value)) {
22368
24522
  return errors;
22369
24523
  }
22370
- const requirements = humanInteraction[HUMAN_INTERACTION_REQUIREMENTS_KEY];
22371
- if (!Array.isArray(requirements)) {
22372
- errors.push("Checkpoint human_interaction.requirements must be a list.");
22373
- return errors;
24524
+ for (const [key, expectedValue] of Object.entries(expected)) {
24525
+ if (value[key] !== expectedValue) {
24526
+ const expectedRepresentation = expectedValue === null ? "None" : `'${String(expectedValue)}'`;
24527
+ errors.push(`${prefix}.${key} must be ${expectedRepresentation}.`);
24528
+ }
22374
24529
  }
22375
- requirements.forEach((requirement, index) => {
22376
- if (!isObject5(requirement)) {
24530
+ return errors;
24531
+ }
24532
+ function validatePlannerTopologyReceipt(value) {
24533
+ return validateExpectedTopologyFields(
24534
+ value,
24535
+ "Epic planner topology_receipt",
24536
+ {
24537
+ execution_context: "standalone",
24538
+ root_persona: "epic-planner",
24539
+ route: "epic",
24540
+ topology: "epic_persona",
24541
+ logical_agent: "epic-planner"
24542
+ }
24543
+ );
24544
+ }
24545
+ function validateChildTopologyReceipt(value, prefix) {
24546
+ return validateExpectedTopologyFields(value, prefix, {
24547
+ execution_context: "epic_preparation_child",
24548
+ root_persona: null,
24549
+ route: "large",
24550
+ topology: "orchestrator",
24551
+ logical_agent: "orchestrator"
24552
+ });
24553
+ }
24554
+ function validateWorthiness(value) {
24555
+ if (!isObject7(value)) {
24556
+ return {
24557
+ errors: ["Epic planner checkpoint epic_worthiness must be an object."],
24558
+ verdict: null
24559
+ };
24560
+ }
24561
+ const errors = [];
24562
+ const verdict = value["verdict"];
24563
+ if (verdict !== "epic" && verdict !== "non_epic") {
24564
+ errors.push(
24565
+ "Epic planner checkpoint epic_worthiness.verdict must be 'epic' or 'non_epic'."
24566
+ );
24567
+ }
24568
+ const rationale = value["rationale"];
24569
+ if (typeof rationale !== "string" || !rationale.trim()) {
24570
+ errors.push(
24571
+ "Epic planner checkpoint epic_worthiness.rationale must be non-empty."
24572
+ );
24573
+ }
24574
+ return {
24575
+ errors,
24576
+ verdict: typeof verdict === "string" ? verdict : null
24577
+ };
24578
+ }
24579
+ function extractFeatures2(value) {
24580
+ if (!Array.isArray(value)) {
24581
+ return {
24582
+ errors: ["Epic planner checkpoint features must be a list."],
24583
+ features: []
24584
+ };
24585
+ }
24586
+ const errors = [];
24587
+ const features = [];
24588
+ value.forEach((item, index) => {
24589
+ const prefix = `Epic planner checkpoint features[${index}]`;
24590
+ if (!isObject7(item)) {
24591
+ errors.push(`${prefix} must be an object.`);
24592
+ return;
24593
+ }
24594
+ features.push(item);
24595
+ const missing = REQUIRED_FEATURE_KEYS.filter((key) => !(key in item));
24596
+ if (missing.length > 0) {
24597
+ errors.push(`${prefix} missing required keys: ${missing.join(", ")}.`);
24598
+ return;
24599
+ }
24600
+ if (!Array.isArray(item["depends_on"])) {
24601
+ errors.push(`${prefix}.depends_on must be a list.`);
24602
+ }
24603
+ const wave = item["wave"];
24604
+ if (typeof wave !== "number" || !Number.isInteger(wave) || wave < 0) {
24605
+ errors.push(`${prefix}.wave must be a non-negative integer.`);
24606
+ }
24607
+ const complexityBand = item["complexity_band"];
24608
+ if (typeof complexityBand !== "string" || !COMPLEXITY_BANDS.has(complexityBand)) {
22377
24609
  errors.push(
22378
- `Checkpoint human_interaction.requirements #${index} must be an object.`
24610
+ `${prefix}.complexity_band must be one of ${COMPLEXITY_BAND_REPR}.`
22379
24611
  );
24612
+ }
24613
+ });
24614
+ return { errors, features };
24615
+ }
24616
+ function validateDependencyWaves(features) {
24617
+ const errors = [];
24618
+ const seenFolders = /* @__PURE__ */ new Set();
24619
+ const seenIssues = /* @__PURE__ */ new Set();
24620
+ features.forEach((feature, index2) => {
24621
+ const prefix = `Epic planner checkpoint features[${index2}]`;
24622
+ const folder = feature["feature_folder"];
24623
+ if (typeof folder === "string" && folder.length > 0) {
24624
+ if (seenFolders.has(folder)) {
24625
+ errors.push(
24626
+ `${prefix}.feature_folder must be unique: ${pythonRepr6(folder)}.`
24627
+ );
24628
+ }
24629
+ seenFolders.add(folder);
24630
+ }
24631
+ const issueNum = feature["issue_num"];
24632
+ if (issueNum !== void 0 && issueNum !== null) {
24633
+ if (seenIssues.has(issueNum)) {
24634
+ errors.push(
24635
+ `${prefix}.issue_num must be unique: ${pythonRepr6(issueNum)}.`
24636
+ );
24637
+ }
24638
+ seenIssues.add(issueNum);
24639
+ }
24640
+ });
24641
+ const index = buildFeatureReferenceIndex(features);
24642
+ const manifest = /* @__PURE__ */ new Map();
24643
+ features.forEach((feature, featureIndex) => {
24644
+ const folder = feature["feature_folder"];
24645
+ const dependencies = feature["depends_on"];
24646
+ if (typeof folder !== "string" || folder.length === 0 || !Array.isArray(dependencies)) {
22380
24647
  return;
22381
24648
  }
22382
- const response = requirement["response"];
22383
- if (typeof response !== "string" || !HUMAN_INTERACTION_RESPONSE_ENUM.has(response)) {
24649
+ const resolvedEdges = [];
24650
+ for (const dependency of dependencies) {
24651
+ const resolved = resolveFeatureReference(dependency, index);
24652
+ if (resolved === null) {
24653
+ errors.push(
24654
+ `Epic planner checkpoint features[${featureIndex}].depends_on contains unresolved reference: ${pythonRepr6(dependency)}.`
24655
+ );
24656
+ } else {
24657
+ resolvedEdges.push(resolved);
24658
+ }
24659
+ }
24660
+ manifest.set(folder, resolvedEdges);
24661
+ });
24662
+ let expectedWaves;
24663
+ try {
24664
+ expectedWaves = computeWaveNumbers(manifest);
24665
+ } catch (error2) {
24666
+ if (error2 instanceof EpicWaveCycleError) {
24667
+ errors.push(error2.message);
24668
+ return errors;
24669
+ }
24670
+ throw error2;
24671
+ }
24672
+ features.forEach((feature, index2) => {
24673
+ const folder = feature["feature_folder"];
24674
+ if (typeof folder !== "string" || !expectedWaves.has(folder)) {
24675
+ return;
24676
+ }
24677
+ const expected = expectedWaves.get(folder);
24678
+ if (feature["wave"] !== expected) {
22384
24679
  errors.push(
22385
- `Checkpoint human_interaction.requirements #${index} response must be one of scope_change, exception, halt; got: ` + pythonRepr2(response)
24680
+ `Epic planner checkpoint features[${index2}].wave must be ${expected} from the dependency graph, found ${pythonRepr6(feature["wave"])}.`
22386
24681
  );
22387
- return;
22388
24682
  }
22389
- if (response === HUMAN_INTERACTION_EXCEPTION_RESPONSE) {
22390
- const runbookPath = requirement["runbook_path"];
22391
- if (typeof runbookPath !== "string" || runbookPath.trim() === "") {
24683
+ });
24684
+ return errors;
24685
+ }
24686
+ function validateReadyFeatures(features) {
24687
+ const errors = [];
24688
+ if (features.length < 2) {
24689
+ errors.push(
24690
+ "Execution-ready epic planning requires at least two features."
24691
+ );
24692
+ }
24693
+ features.forEach((feature, index) => {
24694
+ const prefix = `Epic planner checkpoint features[${index}]`;
24695
+ const issueNum = feature["issue_num"];
24696
+ if (typeof issueNum !== "number" || !Number.isInteger(issueNum) || issueNum <= 0) {
24697
+ errors.push(`${prefix}.issue_num must be a positive integer.`);
24698
+ }
24699
+ for (const key of [
24700
+ "feature_folder",
24701
+ "research_path",
24702
+ "plan_path"
24703
+ ]) {
24704
+ const value = feature[key];
24705
+ if (typeof value !== "string" || !value.trim()) {
24706
+ errors.push(`${prefix}.${key} must be a non-empty string.`);
24707
+ }
24708
+ }
24709
+ if (feature["preparation_status"] !== "prepared") {
24710
+ errors.push(`${prefix}.preparation_status must be 'prepared'.`);
24711
+ }
24712
+ if (feature["preflight_status"] !== "PREFLIGHT: ALL CLEAR") {
24713
+ errors.push(`${prefix}.preflight_status must be 'PREFLIGHT: ALL CLEAR'.`);
24714
+ }
24715
+ const receipt = feature["model_routing_receipt"];
24716
+ errors.push(
24717
+ ...validateCodexModelRoutingReceipt(
24718
+ receipt,
24719
+ `${prefix}.model_routing_receipt`
24720
+ )
24721
+ );
24722
+ if (isObject7(receipt) && receipt["logical_agent"] !== "orchestrator") {
24723
+ errors.push(
24724
+ `${prefix}.model_routing_receipt.logical_agent must be 'orchestrator'.`
24725
+ );
24726
+ }
24727
+ if (isObject7(receipt)) {
24728
+ if (receipt["complexity_band"] !== feature["complexity_band"]) {
22392
24729
  errors.push(
22393
- `Checkpoint human_interaction.requirements #${index} response is exception but runbook_path is missing or empty.`
24730
+ `${prefix}.model_routing_receipt.complexity_band must match feature complexity_band ${pythonRepr6(feature["complexity_band"])}.`
24731
+ );
24732
+ }
24733
+ if (receipt["execution_context"] !== "epic_preparation_child") {
24734
+ errors.push(
24735
+ `${prefix}.model_routing_receipt.execution_context must be 'epic_preparation_child'.`
22394
24736
  );
22395
24737
  }
22396
24738
  }
24739
+ errors.push(
24740
+ ...validateChildTopologyReceipt(
24741
+ feature["topology_receipt"],
24742
+ `${prefix}.topology_receipt`
24743
+ )
24744
+ );
22397
24745
  });
22398
24746
  return errors;
22399
24747
  }
22400
-
22401
- // ../../extensions/drm-copilot/src/lib/validate/orchestrator-state-remediation.ts
22402
- var REMEDIATION_LOOP_KEY = "remediation_loop";
22403
- var REMEDIATION_CYCLES_KEY = "cycles";
22404
- var EXECUTION_STATUSES_REQUIRING_CLEAR_PREFLIGHT = /* @__PURE__ */ new Set(["in_progress", "complete", "failed"]);
22405
- var PREFLIGHT_CLEARED_STATUS = "clear";
22406
- function isObject6(value) {
22407
- return typeof value === "object" && value !== null && !Array.isArray(value);
22408
- }
22409
- function validateRemediationCycle(index, cycle) {
22410
- const errors = [];
22411
- const planPath = cycle["plan_path"];
22412
- if (typeof planPath !== "string" || planPath.trim() === "") {
24748
+ function validateEpicPlannerStateText(text, options = {}) {
24749
+ let value;
24750
+ try {
24751
+ value = JSON.parse(text);
24752
+ } catch (error2) {
24753
+ return [
24754
+ `Epic planner checkpoint is not valid JSON: ${error2 instanceof Error ? error2.message : String(error2)}`
24755
+ ];
24756
+ }
24757
+ if (!isObject7(value)) {
24758
+ return ["Epic planner checkpoint root must be a JSON object."];
24759
+ }
24760
+ const errors = REQUIRED_KEYS3.filter((key) => !(key in value)).map(
24761
+ (key) => `Epic planner checkpoint missing required key: ${key}`
24762
+ );
24763
+ const worthiness = validateWorthiness(value["epic_worthiness"]);
24764
+ errors.push(...worthiness.errors);
24765
+ const featureResult = extractFeatures2(value["features"]);
24766
+ errors.push(...featureResult.errors);
24767
+ errors.push(...validateDependencyWaves(featureResult.features));
24768
+ const maxParallel = value["max_parallel_features"];
24769
+ if (typeof maxParallel !== "number" || !Number.isInteger(maxParallel) || maxParallel < 1 || maxParallel > 8) {
22413
24770
  errors.push(
22414
- `Checkpoint remediation cycle #${index} plan_path must be a non-empty string.`
24771
+ "Epic planner checkpoint max_parallel_features must be an integer from 1 through 8."
22415
24772
  );
22416
24773
  }
22417
- const executionStatus = cycle["execution_status"];
22418
- if (typeof executionStatus === "string" && EXECUTION_STATUSES_REQUIRING_CLEAR_PREFLIGHT.has(executionStatus)) {
22419
- const preflight = cycle["preflight"];
22420
- const preflightStatus = isObject6(preflight) ? preflight["final_status"] : void 0;
22421
- if (preflightStatus !== PREFLIGHT_CLEARED_STATUS) {
24774
+ if (worthiness.verdict === "non_epic" && value["next_step"] !== NON_EPIC_NEXT_STEP) {
24775
+ errors.push(
24776
+ `Non-epic planner checkpoint next_step must be '${NON_EPIC_NEXT_STEP}'.`
24777
+ );
24778
+ }
24779
+ if (options.requireReadyForExecution === true) {
24780
+ if (worthiness.verdict !== "epic") {
22422
24781
  errors.push(
22423
- `Checkpoint remediation cycle #${index} execution_status is ${executionStatus} but preflight.final_status is not 'clear'.`
24782
+ "Execution readiness requires epic_worthiness.verdict 'epic'."
22424
24783
  );
22425
24784
  }
22426
- }
22427
- if (cycle["exit_condition_met"] === true && cycle["blocking_count"] !== 0) {
24785
+ if (value["next_step"] !== READY_NEXT_STEP) {
24786
+ errors.push(
24787
+ `Execution-ready planner checkpoint next_step must be '${READY_NEXT_STEP}'.`
24788
+ );
24789
+ }
24790
+ errors.push(...validateReadyFeatures(featureResult.features));
22428
24791
  errors.push(
22429
- `Checkpoint remediation cycle #${index} exit_condition_met is true but blocking_count is not 0.`
24792
+ ...validateEpicPlannerChildLaunchBindings(featureResult.features)
22430
24793
  );
22431
- }
22432
- return errors;
22433
- }
22434
- function validateRemediationLoop(remediationLoop) {
22435
- const errors = [];
22436
- if (!isObject6(remediationLoop)) {
22437
- return errors;
22438
- }
22439
- const cycles = remediationLoop[REMEDIATION_CYCLES_KEY];
22440
- if (!Array.isArray(cycles)) {
22441
- return errors;
22442
- }
22443
- cycles.forEach((cycle, index) => {
22444
- if (!isObject6(cycle)) {
22445
- errors.push(`Checkpoint remediation cycle #${index} must be an object.`);
22446
- return;
24794
+ errors.push(...validatePlannerTopologyReceipt(value["topology_receipt"]));
24795
+ const expectedKickoff = `artifacts/orchestration/epic-kickoff-${String(
24796
+ value["epic_feature_folder"]
24797
+ )}.md`;
24798
+ if (value["kickoff_prompt_path"] !== expectedKickoff) {
24799
+ errors.push(
24800
+ `Execution-ready planner checkpoint kickoff_prompt_path must be '${expectedKickoff}'.`
24801
+ );
22447
24802
  }
22448
- errors.push(...validateRemediationCycle(index, cycle));
22449
- });
24803
+ if (options.readinessContext === void 0) {
24804
+ errors.push(
24805
+ "Execution-ready planner validation requires repository context."
24806
+ );
24807
+ } else {
24808
+ errors.push(
24809
+ ...validateEpicReadinessIntegrity(
24810
+ value,
24811
+ text,
24812
+ options.readinessContext
24813
+ )
24814
+ );
24815
+ }
24816
+ }
22450
24817
  return errors;
22451
24818
  }
22452
24819
 
22453
24820
  // ../../extensions/drm-copilot/src/lib/validate/orchestrator-state-routing.ts
22454
24821
  var ROUTING_MATRIX_RELATIVE_PATH = "config/orchestration-routing.json";
22455
- function isObject7(value) {
24822
+ var MANDATORY_ROUTE_PHASES = {
24823
+ small: ["S3_promotion", "S4_atomic_planning"],
24824
+ preparation: ["S3_promotion", "S4_atomic_planning"]
24825
+ };
24826
+ function isObject8(value) {
22456
24827
  return typeof value === "object" && value !== null && !Array.isArray(value);
22457
24828
  }
24829
+ function selectedRouteId(state) {
24830
+ const value = state["route_id"] !== void 0 ? state["route_id"] : state["path_selected"];
24831
+ return typeof value === "string" && value.trim() !== "" ? value : null;
24832
+ }
24833
+ function routeRequiresPrGate(state, options = {}) {
24834
+ const routeId = selectedRouteId(state);
24835
+ const matrix = options.routingMatrix;
24836
+ if (routeId === null || !isObject8(matrix) || !isObject8(matrix["routes"])) {
24837
+ return false;
24838
+ }
24839
+ const route = matrix["routes"][routeId];
24840
+ return isObject8(route) && route["requires_pr_gate"] === true;
24841
+ }
24842
+ function routeRequiresCiGate(state, options = {}) {
24843
+ const routeId = selectedRouteId(state);
24844
+ const matrix = options.routingMatrix;
24845
+ if (routeId === null || !isObject8(matrix) || !isObject8(matrix["routes"])) {
24846
+ return true;
24847
+ }
24848
+ const route = matrix["routes"][routeId];
24849
+ return !isObject8(route) || route["requires_ci_gate"] !== false;
24850
+ }
24851
+ function validatePhaseCompleteness(state) {
24852
+ const routeId = selectedRouteId(state);
24853
+ if (routeId === null) {
24854
+ return [];
24855
+ }
24856
+ const mandatory = MANDATORY_ROUTE_PHASES[routeId];
24857
+ if (mandatory === void 0) {
24858
+ return [];
24859
+ }
24860
+ const completed = new Set(stringList(state["completed_steps"]) ?? []);
24861
+ return mandatory.filter((phase) => !completed.has(phase)).map(
24862
+ (phase) => `Checkpoint completion validation failed: route ${routeId} is missing mandatory phase ${phase}.`
24863
+ );
24864
+ }
22458
24865
  function loadRoutingMatrix(fs9, root) {
22459
24866
  const path11 = `${toPosixPath2(root).replace(/\/+$/, "")}/${ROUTING_MATRIX_RELATIVE_PATH}`;
22460
24867
  return JSON.parse(fs9.readTextFile(path11));
@@ -22489,7 +24896,7 @@ function listReceipts(receipts) {
22489
24896
  if (!Array.isArray(receipts)) {
22490
24897
  return [];
22491
24898
  }
22492
- return receipts.filter(isObject7);
24899
+ return receipts.filter(isObject8);
22493
24900
  }
22494
24901
  function receiptAgents(state) {
22495
24902
  const agents = /* @__PURE__ */ new Set();
@@ -22508,7 +24915,7 @@ function receiptSkills(state) {
22508
24915
  return skills;
22509
24916
  }
22510
24917
  for (const receipt of receipts) {
22511
- if (!isObject7(receipt)) {
24918
+ if (!isObject8(receipt)) {
22512
24919
  continue;
22513
24920
  }
22514
24921
  const skill = receipt["skill"];
@@ -22527,7 +24934,7 @@ function mcpTools(state) {
22527
24934
  return tools;
22528
24935
  }
22529
24936
  for (const receipt of receipts) {
22530
- if (!isObject7(receipt)) {
24937
+ if (!isObject8(receipt)) {
22531
24938
  continue;
22532
24939
  }
22533
24940
  const tool = receipt["tool"];
@@ -22539,101 +24946,402 @@ function mcpTools(state) {
22539
24946
  }
22540
24947
  return tools;
22541
24948
  }
22542
- function validateEmptyListField(state, key) {
22543
- const value = state[key];
22544
- if (!Array.isArray(value)) {
22545
- return [`Checkpoint ${key} must be an empty list at completion.`];
24949
+ function validateEmptyListField(state, key) {
24950
+ const value = state[key];
24951
+ if (!Array.isArray(value)) {
24952
+ return [`Checkpoint ${key} must be an empty list at completion.`];
24953
+ }
24954
+ if (value.length > 0) {
24955
+ return [`Checkpoint ${key} must be empty at completion.`];
24956
+ }
24957
+ return [];
24958
+ }
24959
+ function validateLifecycleOperations(state) {
24960
+ const operations = state["lifecycle_operations"];
24961
+ if (operations === void 0 || operations === null) {
24962
+ return [];
24963
+ }
24964
+ if (!Array.isArray(operations)) {
24965
+ return ["Checkpoint lifecycle_operations must be a list when present."];
24966
+ }
24967
+ const errors = [];
24968
+ operations.forEach((operation, index) => {
24969
+ if (!isObject8(operation)) {
24970
+ errors.push(
24971
+ `Checkpoint lifecycle_operations #${index} must be an object.`
24972
+ );
24973
+ return;
24974
+ }
24975
+ if (operation["surface"] !== "mcp") {
24976
+ errors.push(
24977
+ `Checkpoint lifecycle_operations #${index} did not use MCP surface.`
24978
+ );
24979
+ }
24980
+ });
24981
+ return errors;
24982
+ }
24983
+ function validateRoutingContract(state, options = {}) {
24984
+ const matrix = options.routingMatrix;
24985
+ if (!isObject8(matrix)) {
24986
+ return ["Routing matrix missing routes object."];
24987
+ }
24988
+ const rawRoutes = matrix["routes"];
24989
+ if (!isObject8(rawRoutes)) {
24990
+ return ["Routing matrix missing routes object."];
24991
+ }
24992
+ const routeId = selectedRouteId(state);
24993
+ if (routeId === null) {
24994
+ return ["Checkpoint route_id or path_selected must select a route."];
24995
+ }
24996
+ const rawRoute = rawRoutes[routeId];
24997
+ if (!isObject8(rawRoute)) {
24998
+ return [
24999
+ `Checkpoint selected route has no routing-matrix entry: ${routeId}.`
25000
+ ];
25001
+ }
25002
+ const errors = [];
25003
+ const requiredAgents = routeList(rawRoute, "required_agents");
25004
+ const requiredSkills = routeList(rawRoute, "required_skills");
25005
+ const requiredMcpTools = routeList(rawRoute, "required_mcp_tools");
25006
+ if (stateList(state, "required_agents", requiredAgents) === null) {
25007
+ errors.push(
25008
+ `Checkpoint required_agents must match routing matrix for route ${routeId}.`
25009
+ );
25010
+ }
25011
+ if (stateList(state, "required_skills", requiredSkills) === null) {
25012
+ errors.push(
25013
+ `Checkpoint required_skills must match routing matrix for route ${routeId}.`
25014
+ );
25015
+ }
25016
+ if (stateList(state, "required_mcp_tools", requiredMcpTools) === null) {
25017
+ errors.push(
25018
+ `Checkpoint required_mcp_tools must match routing matrix for route ${routeId}.`
25019
+ );
25020
+ }
25021
+ const actualAgents = receiptAgents(state);
25022
+ for (const agent of requiredAgents) {
25023
+ if (!actualAgents.has(agent)) {
25024
+ errors.push(`Checkpoint missing required agent receipt: ${agent}.`);
25025
+ }
25026
+ }
25027
+ const actualSkills = receiptSkills(state);
25028
+ for (const skill of requiredSkills) {
25029
+ if (!actualSkills.has(skill)) {
25030
+ errors.push(`Checkpoint missing required skill receipt: ${skill}.`);
25031
+ }
25032
+ }
25033
+ const actualTools = mcpTools(state);
25034
+ for (const tool of requiredMcpTools) {
25035
+ if (!actualTools.has(tool)) {
25036
+ errors.push(`Checkpoint missing successful MCP receipt: ${tool}.`);
25037
+ }
25038
+ }
25039
+ errors.push(...validateEmptyListField(state, "local_execution_overrides"));
25040
+ errors.push(...validateEmptyListField(state, "delegation_bypasses"));
25041
+ errors.push(...validateLifecycleOperations(state));
25042
+ return errors;
25043
+ }
25044
+
25045
+ // ../../extensions/drm-copilot/src/lib/validate/orchestrator-state-completion.ts
25046
+ var PR_GATE_KEYS = [
25047
+ "pr_number",
25048
+ "pr_url",
25049
+ "head_branch",
25050
+ "head_sha"
25051
+ ];
25052
+ var CI_GATE_KEYS = ["conclusion", "head_sha", "verified_at"];
25053
+ var PROMOTION_RECEIPT_NAMESPACE_KEY = "promotion";
25054
+ var PROMOTION_RECEIPT_KEYS = [
25055
+ "potential_entry",
25056
+ "issue",
25057
+ "feature_folder"
25058
+ ];
25059
+ function isObject9(value) {
25060
+ return typeof value === "object" && value !== null && !Array.isArray(value);
25061
+ }
25062
+ function missingObjectKeys(value, keys) {
25063
+ if (!isObject9(value)) {
25064
+ return [...keys];
25065
+ }
25066
+ const missing = [];
25067
+ for (const key of keys) {
25068
+ const item = value[key];
25069
+ if (item === void 0 || typeof item === "string" && item.trim() === "") {
25070
+ missing.push(key);
25071
+ }
25072
+ }
25073
+ return missing;
25074
+ }
25075
+ function validateCompletionPrGate(state, options = {}) {
25076
+ if (!routeRequiresPrGate(state, options)) {
25077
+ return [];
25078
+ }
25079
+ const prGate = state["pr_gate"];
25080
+ const missing = missingObjectKeys(prGate, PR_GATE_KEYS);
25081
+ if (!isObject9(prGate)) {
25082
+ return [
25083
+ `Checkpoint completion validation failed: pr_gate must be an object with keys: ${PR_GATE_KEYS.join(", ")}.`
25084
+ ];
25085
+ }
25086
+ const errors = [];
25087
+ if (missing.length > 0) {
25088
+ errors.push(
25089
+ `Checkpoint completion validation failed: pr_gate missing required fields: ${missing.join(", ")}.`
25090
+ );
25091
+ }
25092
+ return errors;
25093
+ }
25094
+ function validateCompletionCiGate(state) {
25095
+ const ciGate = state["ci_gate"];
25096
+ const missing = missingObjectKeys(ciGate, CI_GATE_KEYS);
25097
+ if (!isObject9(ciGate)) {
25098
+ return [
25099
+ `Checkpoint completion validation failed: ci_gate must be an object with keys: ${CI_GATE_KEYS.join(", ")}.`
25100
+ ];
25101
+ }
25102
+ const errors = [];
25103
+ if (missing.length > 0) {
25104
+ errors.push(
25105
+ `Checkpoint completion validation failed: ci_gate missing required fields: ${missing.join(", ")}.`
25106
+ );
25107
+ }
25108
+ if (ciGate["conclusion"] !== "success") {
25109
+ errors.push(
25110
+ "Checkpoint completion validation failed: ci_gate.conclusion must be success."
25111
+ );
25112
+ }
25113
+ const prGate = state["pr_gate"];
25114
+ const prHeadSha = isObject9(prGate) ? prGate["head_sha"] : null;
25115
+ if (prHeadSha !== null && prHeadSha !== void 0 && ciGate["head_sha"] !== prHeadSha) {
25116
+ errors.push(
25117
+ "Checkpoint completion validation failed: ci_gate.head_sha must match pr_gate.head_sha."
25118
+ );
25119
+ }
25120
+ return errors;
25121
+ }
25122
+
25123
+ // ../../extensions/drm-copilot/src/lib/validate/orchestrator-state-human-interaction.ts
25124
+ var HUMAN_INTERACTION_KEY = "human_interaction";
25125
+ var HUMAN_INTERACTION_REQUIREMENTS_KEY = "requirements";
25126
+ var HUMAN_INTERACTION_RESPONSE_ENUM = /* @__PURE__ */ new Set([
25127
+ "scope_change",
25128
+ "exception",
25129
+ "halt"
25130
+ ]);
25131
+ var HUMAN_INTERACTION_EXCEPTION_RESPONSE = "exception";
25132
+ function isObject10(value) {
25133
+ return typeof value === "object" && value !== null && !Array.isArray(value);
25134
+ }
25135
+ function pythonRepr7(value) {
25136
+ if (value === null || value === void 0) {
25137
+ return "None";
22546
25138
  }
22547
- if (value.length > 0) {
22548
- return [`Checkpoint ${key} must be empty at completion.`];
25139
+ if (value === true) {
25140
+ return "True";
22549
25141
  }
22550
- return [];
25142
+ if (value === false) {
25143
+ return "False";
25144
+ }
25145
+ return String(value);
22551
25146
  }
22552
- function validateLifecycleOperations(state) {
22553
- const operations = state["lifecycle_operations"];
22554
- if (operations === void 0 || operations === null) {
22555
- return [];
25147
+ function validateHumanInteraction(humanInteraction) {
25148
+ const errors = [];
25149
+ if (!isObject10(humanInteraction)) {
25150
+ errors.push("Checkpoint human_interaction must be an object when present.");
25151
+ return errors;
22556
25152
  }
22557
- if (!Array.isArray(operations)) {
22558
- return ["Checkpoint lifecycle_operations must be a list when present."];
25153
+ const requirements = humanInteraction[HUMAN_INTERACTION_REQUIREMENTS_KEY];
25154
+ if (!Array.isArray(requirements)) {
25155
+ errors.push("Checkpoint human_interaction.requirements must be a list.");
25156
+ return errors;
22559
25157
  }
22560
- const errors = [];
22561
- operations.forEach((operation, index) => {
22562
- if (!isObject7(operation)) {
25158
+ requirements.forEach((requirement, index) => {
25159
+ if (!isObject10(requirement)) {
22563
25160
  errors.push(
22564
- `Checkpoint lifecycle_operations #${index} must be an object.`
25161
+ `Checkpoint human_interaction.requirements #${index} must be an object.`
22565
25162
  );
22566
25163
  return;
22567
25164
  }
22568
- if (operation["surface"] !== "mcp") {
25165
+ const response = requirement["response"];
25166
+ if (typeof response !== "string" || !HUMAN_INTERACTION_RESPONSE_ENUM.has(response)) {
22569
25167
  errors.push(
22570
- `Checkpoint lifecycle_operations #${index} did not use MCP surface.`
25168
+ `Checkpoint human_interaction.requirements #${index} response must be one of scope_change, exception, halt; got: ` + pythonRepr7(response)
22571
25169
  );
25170
+ return;
25171
+ }
25172
+ if (response === HUMAN_INTERACTION_EXCEPTION_RESPONSE) {
25173
+ const runbookPath = requirement["runbook_path"];
25174
+ if (typeof runbookPath !== "string" || runbookPath.trim() === "") {
25175
+ errors.push(
25176
+ `Checkpoint human_interaction.requirements #${index} response is exception but runbook_path is missing or empty.`
25177
+ );
25178
+ }
22572
25179
  }
22573
25180
  });
22574
25181
  return errors;
22575
25182
  }
22576
- function validateRoutingContract(state, options = {}) {
22577
- const matrix = options.routingMatrix;
22578
- if (!isObject7(matrix)) {
22579
- return ["Routing matrix missing routes object."];
22580
- }
22581
- const rawRoutes = matrix["routes"];
22582
- if (!isObject7(rawRoutes)) {
22583
- return ["Routing matrix missing routes object."];
22584
- }
22585
- const routeIdValue = state["route_id"] !== void 0 ? state["route_id"] : state["path_selected"];
22586
- if (typeof routeIdValue !== "string" || routeIdValue.trim() === "") {
22587
- return ["Checkpoint route_id or path_selected must select a route."];
22588
- }
22589
- const routeId = routeIdValue;
22590
- const rawRoute = rawRoutes[routeId];
22591
- if (!isObject7(rawRoute)) {
22592
- return [
22593
- `Checkpoint selected route has no routing-matrix entry: ${routeId}.`
22594
- ];
22595
- }
25183
+
25184
+ // ../../extensions/drm-copilot/src/lib/validate/orchestrator-state-remediation.ts
25185
+ var REMEDIATION_LOOP_KEY = "remediation_loop";
25186
+ var REMEDIATION_CYCLES_KEY = "cycles";
25187
+ var EXECUTION_STATUSES_REQUIRING_CLEAR_PREFLIGHT = /* @__PURE__ */ new Set(["in_progress", "complete", "failed"]);
25188
+ var PREFLIGHT_CLEARED_STATUS = "clear";
25189
+ function isObject11(value) {
25190
+ return typeof value === "object" && value !== null && !Array.isArray(value);
25191
+ }
25192
+ function validateRemediationCycle(index, cycle) {
22596
25193
  const errors = [];
22597
- const requiredAgents = routeList(rawRoute, "required_agents");
22598
- const requiredSkills = routeList(rawRoute, "required_skills");
22599
- const requiredMcpTools = routeList(rawRoute, "required_mcp_tools");
22600
- if (stateList(state, "required_agents", requiredAgents) === null) {
25194
+ const planPath = cycle["plan_path"];
25195
+ if (typeof planPath !== "string" || planPath.trim() === "") {
22601
25196
  errors.push(
22602
- `Checkpoint required_agents must match routing matrix for route ${routeId}.`
25197
+ `Checkpoint remediation cycle #${index} plan_path must be a non-empty string.`
22603
25198
  );
22604
25199
  }
22605
- if (stateList(state, "required_skills", requiredSkills) === null) {
25200
+ const executionStatus = cycle["execution_status"];
25201
+ if (typeof executionStatus === "string" && EXECUTION_STATUSES_REQUIRING_CLEAR_PREFLIGHT.has(executionStatus)) {
25202
+ const preflight = cycle["preflight"];
25203
+ const preflightStatus = isObject11(preflight) ? preflight["final_status"] : void 0;
25204
+ if (preflightStatus !== PREFLIGHT_CLEARED_STATUS) {
25205
+ errors.push(
25206
+ `Checkpoint remediation cycle #${index} execution_status is ${executionStatus} but preflight.final_status is not 'clear'.`
25207
+ );
25208
+ }
25209
+ }
25210
+ if (cycle["exit_condition_met"] === true && cycle["blocking_count"] !== 0) {
22606
25211
  errors.push(
22607
- `Checkpoint required_skills must match routing matrix for route ${routeId}.`
25212
+ `Checkpoint remediation cycle #${index} exit_condition_met is true but blocking_count is not 0.`
22608
25213
  );
22609
25214
  }
22610
- if (stateList(state, "required_mcp_tools", requiredMcpTools) === null) {
25215
+ return errors;
25216
+ }
25217
+ function validateRemediationLoop(remediationLoop) {
25218
+ const errors = [];
25219
+ if (!isObject11(remediationLoop)) {
25220
+ return errors;
25221
+ }
25222
+ const cycles = remediationLoop[REMEDIATION_CYCLES_KEY];
25223
+ if (!Array.isArray(cycles)) {
25224
+ return errors;
25225
+ }
25226
+ cycles.forEach((cycle, index) => {
25227
+ if (!isObject11(cycle)) {
25228
+ errors.push(`Checkpoint remediation cycle #${index} must be an object.`);
25229
+ return;
25230
+ }
25231
+ errors.push(...validateRemediationCycle(index, cycle));
25232
+ });
25233
+ return errors;
25234
+ }
25235
+
25236
+ // ../../extensions/drm-copilot/src/lib/validate/orchestrator-state-preparation-terminal.ts
25237
+ var EXPECTED_NEXT_STEP = "S5_atomic_execution";
25238
+ var OUT_OF_SCOPE_STEP_KEYS = [
25239
+ "step5_status",
25240
+ "step6_status",
25241
+ "step7_status",
25242
+ "step8_status",
25243
+ "step9_status",
25244
+ "step10_status"
25245
+ ];
25246
+ function pythonRepr8(value) {
25247
+ if (value === void 0 || value === null) {
25248
+ return "None";
25249
+ }
25250
+ if (typeof value === "string") {
25251
+ const escaped = value.replaceAll("\\", "\\\\").replaceAll("'", "\\'").replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll(" ", "\\t");
25252
+ return `'${escaped}'`;
25253
+ }
25254
+ if (typeof value === "boolean") {
25255
+ return value ? "True" : "False";
25256
+ }
25257
+ if (typeof value === "number") {
25258
+ return String(value);
25259
+ }
25260
+ if (Array.isArray(value)) {
25261
+ return `[${value.map(pythonRepr8).join(", ")}]`;
25262
+ }
25263
+ if (typeof value === "object") {
25264
+ const entries = Object.entries(value).map(
25265
+ ([key, item]) => `${pythonRepr8(key)}: ${pythonRepr8(item)}`
25266
+ );
25267
+ return `{${entries.join(", ")}}`;
25268
+ }
25269
+ return String(value);
25270
+ }
25271
+ function validatePreparationTerminalContract(state) {
25272
+ const routeId = state["route_id"] !== void 0 ? state["route_id"] : state["path_selected"];
25273
+ if (routeId !== "preparation") {
25274
+ return [];
25275
+ }
25276
+ const errors = [];
25277
+ if (state["next_step"] !== EXPECTED_NEXT_STEP) {
22611
25278
  errors.push(
22612
- `Checkpoint required_mcp_tools must match routing matrix for route ${routeId}.`
25279
+ `Preparation checkpoint next_step must be ${pythonRepr8(EXPECTED_NEXT_STEP)}, found ${pythonRepr8(state["next_step"])}.`
22613
25280
  );
22614
25281
  }
22615
- const actualAgents = receiptAgents(state);
22616
- for (const agent of requiredAgents) {
22617
- if (!actualAgents.has(agent)) {
22618
- errors.push(`Checkpoint missing required agent receipt: ${agent}.`);
25282
+ for (const key of OUT_OF_SCOPE_STEP_KEYS) {
25283
+ if (state[key] !== "not-applicable") {
25284
+ errors.push(
25285
+ `Preparation checkpoint ${key} must be 'not-applicable', found ${pythonRepr8(state[key])}.`
25286
+ );
22619
25287
  }
22620
25288
  }
22621
- const actualSkills = receiptSkills(state);
22622
- for (const skill of requiredSkills) {
22623
- if (!actualSkills.has(skill)) {
22624
- errors.push(`Checkpoint missing required skill receipt: ${skill}.`);
25289
+ return errors;
25290
+ }
25291
+
25292
+ // ../../extensions/drm-copilot/src/lib/validate/orchestrator-state-model-routing-existence.ts
25293
+ var DELEGATING_AGENTS = /* @__PURE__ */ new Set([
25294
+ "atomic-planner",
25295
+ "atomic-executor",
25296
+ "feature-review",
25297
+ "task-researcher",
25298
+ "prd-feature",
25299
+ "pr-author"
25300
+ ]);
25301
+ function isObject12(value) {
25302
+ return typeof value === "object" && value !== null && !Array.isArray(value);
25303
+ }
25304
+ function delegatedAgents(state) {
25305
+ const agents = /* @__PURE__ */ new Set();
25306
+ const receipts = state["delegation_receipts"];
25307
+ if (Array.isArray(receipts)) {
25308
+ for (const receipt of receipts) {
25309
+ if (!isObject12(receipt)) {
25310
+ continue;
25311
+ }
25312
+ const agentName = receipt["agent_name"];
25313
+ if (typeof agentName === "string" && agentName.trim().length > 0) {
25314
+ agents.add(agentName);
25315
+ }
22625
25316
  }
22626
25317
  }
22627
- const actualTools = mcpTools(state);
22628
- for (const tool of requiredMcpTools) {
22629
- if (!actualTools.has(tool)) {
22630
- errors.push(`Checkpoint missing successful MCP receipt: ${tool}.`);
25318
+ const nextStep = state["next_step"];
25319
+ if (typeof nextStep === "string" && DELEGATING_AGENTS.has(nextStep)) {
25320
+ agents.add(nextStep);
25321
+ }
25322
+ return agents;
25323
+ }
25324
+ function validateModelRoutingExistence(state) {
25325
+ const delegated = delegatedAgents(state);
25326
+ if (delegated.size === 0) {
25327
+ return [];
25328
+ }
25329
+ const receiptAgents2 = /* @__PURE__ */ new Set();
25330
+ const receipts = state["model_routing_receipts"];
25331
+ if (Array.isArray(receipts)) {
25332
+ for (const receipt of receipts) {
25333
+ if (!isObject12(receipt)) {
25334
+ continue;
25335
+ }
25336
+ const agent = receipt["agent"];
25337
+ if (typeof agent === "string" && agent.trim().length > 0) {
25338
+ receiptAgents2.add(agent);
25339
+ }
22631
25340
  }
22632
25341
  }
22633
- errors.push(...validateEmptyListField(state, "local_execution_overrides"));
22634
- errors.push(...validateEmptyListField(state, "delegation_bypasses"));
22635
- errors.push(...validateLifecycleOperations(state));
22636
- return errors;
25342
+ return [...delegated].filter((agent) => !receiptAgents2.has(agent)).sort().map(
25343
+ (agent) => `Checkpoint model_routing_receipts is missing a receipt for delegated agent: ${agent}.`
25344
+ );
22637
25345
  }
22638
25346
 
22639
25347
  // ../../extensions/drm-copilot/src/lib/validate/orchestrator-state-core.ts
@@ -22698,21 +25406,13 @@ var STEP_STATUS_KEYS = [
22698
25406
  "step9_status",
22699
25407
  "step10_status"
22700
25408
  ];
22701
- var DELEGATING_AGENTS = /* @__PURE__ */ new Set([
22702
- "atomic-planner",
22703
- "atomic-executor",
22704
- "feature-review",
22705
- "task-researcher",
22706
- "prd-feature",
22707
- "pr-author"
22708
- ]);
22709
- function isObject8(value) {
25409
+ function isObject13(value) {
22710
25410
  return typeof value === "object" && value !== null && !Array.isArray(value);
22711
25411
  }
22712
25412
  function validateListDelegationReceipts(receipts) {
22713
25413
  const errors = [];
22714
25414
  receipts.forEach((receipt, index) => {
22715
- if (!isObject8(receipt)) {
25415
+ if (!isObject13(receipt)) {
22716
25416
  errors.push(`Checkpoint delegation receipt #${index} must be an object.`);
22717
25417
  return;
22718
25418
  }
@@ -22744,7 +25444,7 @@ function validateNamespacedDelegationReceipts(receipts) {
22744
25444
  if (promotionReceipts === void 0 || promotionReceipts === null) {
22745
25445
  return errors;
22746
25446
  }
22747
- if (!isObject8(promotionReceipts)) {
25447
+ if (!isObject13(promotionReceipts)) {
22748
25448
  errors.push(
22749
25449
  "Checkpoint delegation_receipts.promotion must be an object namespace."
22750
25450
  );
@@ -22769,53 +25469,6 @@ function resolveRoutingMatrix(options) {
22769
25469
  }
22770
25470
  return void 0;
22771
25471
  }
22772
- function delegatedAgents(stateMap) {
22773
- const agents = /* @__PURE__ */ new Set();
22774
- const receipts = stateMap["delegation_receipts"];
22775
- if (Array.isArray(receipts)) {
22776
- receipts.forEach((receipt) => {
22777
- if (!isObject8(receipt)) {
22778
- return;
22779
- }
22780
- const agentName = receipt["agent_name"];
22781
- if (typeof agentName === "string" && agentName.trim() !== "") {
22782
- agents.add(agentName);
22783
- }
22784
- });
22785
- }
22786
- const nextStep = stateMap["next_step"];
22787
- if (typeof nextStep === "string" && DELEGATING_AGENTS.has(nextStep)) {
22788
- agents.add(nextStep);
22789
- }
22790
- return agents;
22791
- }
22792
- function validateModelRoutingExistence(stateMap) {
22793
- const errors = [];
22794
- const delegated = delegatedAgents(stateMap);
22795
- if (delegated.size === 0) {
22796
- return errors;
22797
- }
22798
- const receiptAgents2 = /* @__PURE__ */ new Set();
22799
- const receipts = stateMap["model_routing_receipts"];
22800
- if (Array.isArray(receipts)) {
22801
- receipts.forEach((receipt) => {
22802
- if (!isObject8(receipt)) {
22803
- return;
22804
- }
22805
- const agent = receipt["agent"];
22806
- if (typeof agent === "string" && agent.trim() !== "") {
22807
- receiptAgents2.add(agent);
22808
- }
22809
- });
22810
- }
22811
- const missing = [...delegated].filter((a) => !receiptAgents2.has(a)).sort();
22812
- for (const agent of missing) {
22813
- errors.push(
22814
- `Checkpoint model_routing_receipts is missing a receipt for delegated agent: ${agent}.`
22815
- );
22816
- }
22817
- return errors;
22818
- }
22819
25472
  function validateOrchestratorStateText(text, options = {}) {
22820
25473
  const errors = [];
22821
25474
  let state;
@@ -22826,7 +25479,7 @@ function validateOrchestratorStateText(text, options = {}) {
22826
25479
  `Checkpoint is not valid JSON: ${exc instanceof Error ? exc.message : String(exc)}`
22827
25480
  ];
22828
25481
  }
22829
- if (!isObject8(state)) {
25482
+ if (!isObject13(state)) {
22830
25483
  return ["Checkpoint root must be a JSON object."];
22831
25484
  }
22832
25485
  const stateMap = state;
@@ -22851,7 +25504,7 @@ function validateOrchestratorStateText(text, options = {}) {
22851
25504
  if (receipts !== void 0 && receipts !== null) {
22852
25505
  if (Array.isArray(receipts)) {
22853
25506
  errors.push(...validateListDelegationReceipts(receipts));
22854
- } else if (isObject8(receipts)) {
25507
+ } else if (isObject13(receipts)) {
22855
25508
  errors.push(...validateNamespacedDelegationReceipts(receipts));
22856
25509
  } else {
22857
25510
  errors.push(
@@ -22880,18 +25533,34 @@ function validateOrchestratorStateText(text, options = {}) {
22880
25533
  "Checkpoint completion validation failed: blocked_reason is not `none`."
22881
25534
  );
22882
25535
  }
22883
- errors.push(...validateCompletionPrGate(stateMap));
22884
- errors.push(...validateCompletionCiGate(stateMap));
22885
- errors.push(...validateIssue232PromotionReceipts(stateMap));
25536
+ const routingMatrix = resolveRoutingMatrix(options);
25537
+ errors.push(...validateCompletionPrGate(stateMap, { routingMatrix }));
25538
+ if (routeRequiresCiGate(stateMap, { routingMatrix })) {
25539
+ errors.push(...validateCompletionCiGate(stateMap));
25540
+ }
25541
+ errors.push(...validatePhaseCompleteness(stateMap));
25542
+ errors.push(...validatePreparationTerminalContract(stateMap));
22886
25543
  errors.push(
22887
25544
  ...validateRoutingContract(stateMap, {
22888
- routingMatrix: resolveRoutingMatrix(options)
25545
+ routingMatrix
22889
25546
  })
22890
25547
  );
22891
25548
  }
22892
25549
  if (options.requireModelRouting === true) {
22893
25550
  errors.push(...validateModelRoutingExistence(stateMap));
22894
25551
  }
25552
+ errors.push(
25553
+ ...validateCodexModelRoutingState(
25554
+ stateMap,
25555
+ options.requireCodexModelRouting === true
25556
+ )
25557
+ );
25558
+ errors.push(
25559
+ ...validateCodexTopologyState(
25560
+ stateMap,
25561
+ options.requireCodexTopology === true
25562
+ )
25563
+ );
22895
25564
  return errors;
22896
25565
  }
22897
25566
 
@@ -23267,6 +25936,8 @@ function validateArtifact(input) {
23267
25936
  const options = {
23268
25937
  ...input.requireComplete === void 0 ? {} : { requireComplete: input.requireComplete },
23269
25938
  ...input.requireModelRouting === void 0 ? {} : { requireModelRouting: input.requireModelRouting },
25939
+ ...input.requireCodexModelRouting === void 0 ? {} : { requireCodexModelRouting: input.requireCodexModelRouting },
25940
+ ...input.requireCodexTopology === void 0 ? {} : { requireCodexTopology: input.requireCodexTopology },
23270
25941
  ...input.fs === void 0 ? {} : { fs: input.fs },
23271
25942
  ...input.root === void 0 ? {} : { root: input.root },
23272
25943
  ...input.routingMatrix === void 0 ? {} : { routingMatrix: input.routingMatrix }
@@ -23275,10 +25946,28 @@ function validateArtifact(input) {
23275
25946
  }
23276
25947
  case "epic-orchestrator-state": {
23277
25948
  const options = {
23278
- ...input.requireComplete === void 0 ? {} : { requireComplete: input.requireComplete }
25949
+ ...input.requireComplete === void 0 ? {} : { requireComplete: input.requireComplete },
25950
+ ...input.requireCodexModelRouting === void 0 ? {} : { requireCodexModelRouting: input.requireCodexModelRouting },
25951
+ ...input.requireCodexTopology === void 0 ? {} : { requireCodexTopology: input.requireCodexTopology }
23279
25952
  };
23280
25953
  return validateEpicOrchestratorStateText(input.text, options);
23281
25954
  }
25955
+ case "epic-planner-state": {
25956
+ const options = {
25957
+ ...input.requireReadyForExecution === void 0 ? {} : { requireReadyForExecution: input.requireReadyForExecution },
25958
+ ...input.fs === void 0 || input.root === void 0 || input.artifactPath === void 0 || input.runner === void 0 ? {} : {
25959
+ readinessContext: {
25960
+ workspaceRoot: input.root,
25961
+ artifactPath: input.artifactPath,
25962
+ fileSystem: input.fs,
25963
+ git: new CommandRunnerGitRepository(input.root, input.runner)
25964
+ }
25965
+ }
25966
+ };
25967
+ return validateEpicPlannerStateText(input.text, options);
25968
+ }
25969
+ case "epic-kickoff":
25970
+ return validateEpicKickoffText(input.text);
23282
25971
  default:
23283
25972
  return [`Unsupported artifact type: ${input.artifactType}`];
23284
25973
  }
@@ -23295,6 +25984,11 @@ function validateOrchestrationServiceCall(input) {
23295
25984
  text,
23296
25985
  ...input.requireComplete === void 0 ? {} : { requireComplete: input.requireComplete },
23297
25986
  ...input.requireModelRouting === void 0 ? {} : { requireModelRouting: input.requireModelRouting },
25987
+ ...input.requireCodexModelRouting === void 0 ? {} : { requireCodexModelRouting: input.requireCodexModelRouting },
25988
+ ...input.requireCodexTopology === void 0 ? {} : { requireCodexTopology: input.requireCodexTopology },
25989
+ ...input.requireReadyForExecution === void 0 ? {} : { requireReadyForExecution: input.requireReadyForExecution },
25990
+ artifactPath: artifactFullPath,
25991
+ ...input.runner === void 0 ? {} : { runner: input.runner },
23298
25992
  fs: input.fileSystem,
23299
25993
  root: input.workspaceRoot
23300
25994
  });
@@ -23312,14 +26006,18 @@ ${errors.join("\n")}`
23312
26006
  }
23313
26007
 
23314
26008
  // ../../extensions/drm-copilot/src/lib/validate/build-validate-orchestration-service-call-input.ts
23315
- function buildValidateOrchestrationServiceCallInput(fileSystem, input) {
26009
+ function buildValidateOrchestrationServiceCallInput(fileSystem, input, runner) {
23316
26010
  return {
23317
26011
  fileSystem,
26012
+ ...runner === void 0 ? {} : { runner },
23318
26013
  workspaceRoot: input.workspaceRoot,
23319
26014
  artifactType: input.artifactType,
23320
26015
  artifactPath: input.artifactPath,
23321
26016
  ...input.requireComplete === void 0 ? {} : { requireComplete: input.requireComplete },
23322
- ...input.requireModelRouting === void 0 ? {} : { requireModelRouting: input.requireModelRouting }
26017
+ ...input.requireModelRouting === void 0 ? {} : { requireModelRouting: input.requireModelRouting },
26018
+ ...input.requireCodexModelRouting === void 0 ? {} : { requireCodexModelRouting: input.requireCodexModelRouting },
26019
+ ...input.requireCodexTopology === void 0 ? {} : { requireCodexTopology: input.requireCodexTopology },
26020
+ ...input.requireReadyForExecution === void 0 ? {} : { requireReadyForExecution: input.requireReadyForExecution }
23323
26021
  };
23324
26022
  }
23325
26023
 
@@ -23744,7 +26442,7 @@ function extractStrings(raw, key) {
23744
26442
  return values;
23745
26443
  }
23746
26444
  for (const entry of raw) {
23747
- if (isRecord(entry)) {
26445
+ if (isRecord4(entry)) {
23748
26446
  const value = entry[key];
23749
26447
  if (typeof value === "string") {
23750
26448
  values.push(value);
@@ -23759,7 +26457,7 @@ function extractClosingNumbers(raw) {
23759
26457
  return numbers;
23760
26458
  }
23761
26459
  for (const entry of raw) {
23762
- if (isRecord(entry)) {
26460
+ if (isRecord4(entry)) {
23763
26461
  const num = entry["number"];
23764
26462
  if (typeof num === "number" && Number.isInteger(num)) {
23765
26463
  numbers.push(`#${num}`);
@@ -23782,7 +26480,7 @@ function compareCodePoint(left, right) {
23782
26480
  }
23783
26481
  function authorLogin(record2, key) {
23784
26482
  const raw = record2[key];
23785
- if (isRecord(raw)) {
26483
+ if (isRecord4(raw)) {
23786
26484
  const login = raw["login"];
23787
26485
  if (typeof login === "string") {
23788
26486
  return login;
@@ -23800,7 +26498,7 @@ function issueDetailsImpl(client, numberRef) {
23800
26498
  [client.ghExecutable, "api", `repos/${repo}/issues/${numberRef}`],
23801
26499
  { context: "Fetch issue" }
23802
26500
  );
23803
- if (!isRecord(payload)) {
26501
+ if (!isRecord4(payload)) {
23804
26502
  throw new Error("Unexpected issue payload format.");
23805
26503
  }
23806
26504
  const title = stringField(payload, "title");
@@ -23848,7 +26546,7 @@ function fetchIssueComments(client, commentsUrl) {
23848
26546
  return comments;
23849
26547
  }
23850
26548
  for (const entry of commentPayload) {
23851
- if (!isRecord(entry)) {
26549
+ if (!isRecord4(entry)) {
23852
26550
  continue;
23853
26551
  }
23854
26552
  const login = authorLogin(entry, "user");
@@ -23872,7 +26570,7 @@ function prDetailsImpl(client, numberRef) {
23872
26570
  [client.ghExecutable, "pr", "view", numberRef, "--json", PR_DETAILS_FIELDS],
23873
26571
  { context: "Fetch pull request" }
23874
26572
  );
23875
- if (!isRecord(payload)) {
26573
+ if (!isRecord4(payload)) {
23876
26574
  throw new Error("Unexpected pull request payload format.");
23877
26575
  }
23878
26576
  const title = stringField(payload, "title");
@@ -23917,7 +26615,7 @@ function closingIssuesImpl(client, prNumber) {
23917
26615
  args.splice(3, 0, prNumber);
23918
26616
  }
23919
26617
  const payload = client.requestJson(args, { context: "Fetch closing issues" });
23920
- if (!isRecord(payload)) {
26618
+ if (!isRecord4(payload)) {
23921
26619
  return [];
23922
26620
  }
23923
26621
  return sortedSet(extractClosingNumbers(payload["closingIssuesReferences"]));
@@ -23932,7 +26630,7 @@ function currentPrImpl(client) {
23932
26630
  return null;
23933
26631
  }
23934
26632
  const payload = parseJsonOrNull(result.stdout);
23935
- if (!isRecord(payload)) {
26633
+ if (!isRecord4(payload)) {
23936
26634
  return null;
23937
26635
  }
23938
26636
  const numberRaw = payload["number"];
@@ -24074,7 +26772,7 @@ var GhClient = class {
24074
26772
  return null;
24075
26773
  }
24076
26774
  const payload = parseJsonOrNull(result.stdout);
24077
- if (!isRecord(payload)) {
26775
+ if (!isRecord4(payload)) {
24078
26776
  return null;
24079
26777
  }
24080
26778
  const nameRaw = payload["nameWithOwner"];
@@ -24147,7 +26845,7 @@ var GhClient = class {
24147
26845
  if (payload === null) {
24148
26846
  return null;
24149
26847
  }
24150
- if (isRecord(payload) && "pull_request" in payload) {
26848
+ if (isRecord4(payload) && "pull_request" in payload) {
24151
26849
  return "pull";
24152
26850
  }
24153
26851
  return "issue";
@@ -24178,7 +26876,7 @@ var GhClient = class {
24178
26876
  } catch {
24179
26877
  return null;
24180
26878
  }
24181
- if (!isRecord(payload)) {
26879
+ if (!isRecord4(payload)) {
24182
26880
  return null;
24183
26881
  }
24184
26882
  const contentRaw = payload["content"];
@@ -24215,7 +26913,7 @@ var GhClient = class {
24215
26913
  return [null, []];
24216
26914
  }
24217
26915
  const first = payload[0];
24218
- if (!isRecord(first)) {
26916
+ if (!isRecord4(first)) {
24219
26917
  return [null, []];
24220
26918
  }
24221
26919
  const statusRaw = first["status"] || first["conclusion"];
@@ -24258,7 +26956,7 @@ function parseJsonOrNull(text) {
24258
26956
  return null;
24259
26957
  }
24260
26958
  }
24261
- function isRecord(value) {
26959
+ function isRecord4(value) {
24262
26960
  return typeof value === "object" && value !== null && !Array.isArray(value);
24263
26961
  }
24264
26962
 
@@ -24936,9 +27634,9 @@ function parseVerificationEvidenceMarkdown(params) {
24936
27634
  };
24937
27635
  }
24938
27636
  function parseVerificationEvidenceFile(params) {
24939
- const { fs: fs9, root, feature, relativePath } = params;
27637
+ const { fs: fs9, root, feature, relativePath: relativePath2 } = params;
24940
27638
  const normalizedRoot = toPosixPath2(root).replace(/\/+$/u, "");
24941
- const relPosix = toPosixPath2(relativePath).replace(/^\/+/u, "");
27639
+ const relPosix = toPosixPath2(relativePath2).replace(/^\/+/u, "");
24942
27640
  const markdown = fs9.readTextFile(`${normalizedRoot}/${relPosix}`);
24943
27641
  return parseVerificationEvidenceMarkdown({
24944
27642
  feature,
@@ -24991,7 +27689,7 @@ var SPEC_HEADINGS = [
24991
27689
  "Behavior",
24992
27690
  "Overview"
24993
27691
  ];
24994
- function extractFeatures2(changedFiles) {
27692
+ function extractFeatures3(changedFiles) {
24995
27693
  const features = /* @__PURE__ */ new Set();
24996
27694
  for (const raw of changedFiles) {
24997
27695
  const parts = toPosixPath2(raw).split("/");
@@ -25003,7 +27701,7 @@ function extractFeatures2(changedFiles) {
25003
27701
  }
25004
27702
  function gatherFeatureExcerpts2(fs9, root, changedFiles) {
25005
27703
  const normalizedRoot = toPosixPath2(root).replace(/\/+$/u, "");
25006
- const features = extractFeatures2(changedFiles);
27704
+ const features = extractFeatures3(changedFiles);
25007
27705
  const excerpts = [];
25008
27706
  const baseDir = `${normalizedRoot}/docs/features/active`;
25009
27707
  const promotedDir = `${normalizedRoot}/docs/features/potential/promoted`;
@@ -26258,7 +28956,7 @@ function getSection(content, heading) {
26258
28956
  }
26259
28957
  return match[1].trim();
26260
28958
  }
26261
- function buildBody(workMode, problem, behavior, criteria, constraints, tests, relativePath) {
28959
+ function buildBody(workMode, problem, behavior, criteria, constraints, tests, relativePath2) {
26262
28960
  return `- Work Mode: ${workMode}
26263
28961
  ## Problem / Why
26264
28962
  ${problem}
@@ -26276,20 +28974,20 @@ ${constraints}
26276
28974
  ${tests}
26277
28975
 
26278
28976
  ## Source
26279
- From: ${relativePath}
28977
+ From: ${relativePath2}
26280
28978
  `;
26281
28979
  }
26282
- function buildBugBody(workMode, sections, relativePath) {
28980
+ function buildBugBody(workMode, sections, relativePath2) {
26283
28981
  const parts = [`- Work Mode: ${workMode}`];
26284
28982
  for (const heading of BUG_SECTION_HEADINGS) {
26285
28983
  parts.push(`## ${heading}
26286
28984
  ${sections[heading] ?? ""}`);
26287
28985
  }
26288
28986
  parts.push(`## Source
26289
- From: ${relativePath}`);
28987
+ From: ${relativePath2}`);
26290
28988
  return parts.join("\n\n") + "\n";
26291
28989
  }
26292
- function buildMinorAuditBody(workMode, problem, implementationIntent, acceptanceCriteria, dependenciesRisks, verificationSteps, evidenceChecklist, relativePath) {
28990
+ function buildMinorAuditBody(workMode, problem, implementationIntent, acceptanceCriteria, dependenciesRisks, verificationSteps, evidenceChecklist, relativePath2) {
26293
28991
  return `- Work Mode: ${workMode}
26294
28992
  ## Problem / Why
26295
28993
  ${problem}
@@ -26310,7 +29008,7 @@ ${verificationSteps}
26310
29008
  ${evidenceChecklist}
26311
29009
 
26312
29010
  ## Source
26313
- From: ${relativePath}
29011
+ From: ${relativePath2}
26314
29012
  `;
26315
29013
  }
26316
29014
  function parseIssueReference(output) {
@@ -26475,7 +29173,7 @@ function posixBasename(pathStr) {
26475
29173
  const lastSlash = normalized.lastIndexOf("/");
26476
29174
  return lastSlash === -1 ? normalized : normalized.slice(lastSlash + 1);
26477
29175
  }
26478
- function buildIssueBody(content, selectedMode, promotionType, relativePath) {
29176
+ function buildIssueBody(content, selectedMode, promotionType, relativePath2) {
26479
29177
  if (selectedMode === "minor-audit") {
26480
29178
  const problem2 = getSection(content, "Problem / Why") || PLACEHOLDER;
26481
29179
  const implementationIntent = getSection(content, "Proposed Behavior") || PLACEHOLDER;
@@ -26494,7 +29192,7 @@ function buildIssueBody(content, selectedMode, promotionType, relativePath) {
26494
29192
  dependenciesRisks,
26495
29193
  verificationSteps,
26496
29194
  evidenceChecklist,
26497
- relativePath
29195
+ relativePath2
26498
29196
  );
26499
29197
  }
26500
29198
  if (promotionType === "bug") {
@@ -26502,7 +29200,7 @@ function buildIssueBody(content, selectedMode, promotionType, relativePath) {
26502
29200
  for (const heading of BUG_SECTION_HEADINGS) {
26503
29201
  bugSections[heading] = getSection(content, heading) || PLACEHOLDER;
26504
29202
  }
26505
- return buildBugBody(selectedMode, bugSections, relativePath);
29203
+ return buildBugBody(selectedMode, bugSections, relativePath2);
26506
29204
  }
26507
29205
  const problem = getSection(content, "Problem / Why") || PLACEHOLDER;
26508
29206
  const behavior = getSection(content, "Proposed Behavior") || PLACEHOLDER;
@@ -26516,7 +29214,7 @@ function buildIssueBody(content, selectedMode, promotionType, relativePath) {
26516
29214
  criteria,
26517
29215
  constraints,
26518
29216
  tests,
26519
- relativePath
29217
+ relativePath2
26520
29218
  );
26521
29219
  }
26522
29220
  function promotePotential(options) {
@@ -26551,7 +29249,7 @@ function promotePotential(options) {
26551
29249
  const featurePath = getFeaturePath(featureName);
26552
29250
  const prefix = TITLE_PREFIXES[promotionType] ?? "Feature";
26553
29251
  const issueTitle = normalizeSmartPunctuation(`${prefix}: ${featureName}`);
26554
- const relativePath = computeRelativePath(resolved, workspacePath);
29252
+ const relativePath2 = computeRelativePath(resolved, workspacePath);
26555
29253
  let selectedMode;
26556
29254
  try {
26557
29255
  selectedMode = normalizeRequestedWorkMode(workMode, promotionType);
@@ -26560,7 +29258,7 @@ function promotePotential(options) {
26560
29258
  throw new PromotionError(message);
26561
29259
  }
26562
29260
  const fallbackReason = "";
26563
- let body = buildIssueBody(content, selectedMode, promotionType, relativePath);
29261
+ let body = buildIssueBody(content, selectedMode, promotionType, relativePath2);
26564
29262
  body = normalizeSmartPunctuation(body);
26565
29263
  const messages = [];
26566
29264
  const emitLine = (message) => {
@@ -27916,11 +30614,11 @@ function parseTranscriptLines(lines) {
27916
30614
  continue;
27917
30615
  }
27918
30616
  const parsed = tryParseJson(trimmed);
27919
- if (!isRecord2(parsed)) {
30617
+ if (!isRecord5(parsed)) {
27920
30618
  continue;
27921
30619
  }
27922
30620
  const message = parsed["message"];
27923
- if (!isRecord2(message)) {
30621
+ if (!isRecord5(message)) {
27924
30622
  continue;
27925
30623
  }
27926
30624
  recordModel(message["model"], models, seenModels);
@@ -27935,7 +30633,7 @@ function tryParseJson(text) {
27935
30633
  return void 0;
27936
30634
  }
27937
30635
  }
27938
- function isRecord2(value) {
30636
+ function isRecord5(value) {
27939
30637
  return typeof value === "object" && value !== null && !Array.isArray(value);
27940
30638
  }
27941
30639
  function recordModel(model, models, seenModels) {
@@ -27949,7 +30647,7 @@ function collectAgentToolUseIds(content, agentToolUseIds) {
27949
30647
  return;
27950
30648
  }
27951
30649
  for (const block of content) {
27952
- if (!isRecord2(block)) {
30650
+ if (!isRecord5(block)) {
27953
30651
  continue;
27954
30652
  }
27955
30653
  if (block["type"] === "tool_use" && block["name"] === "Agent" && typeof block["id"] === "string") {
@@ -28268,7 +30966,11 @@ var DefaultRepoAutomationService = class {
28268
30966
  }
28269
30967
  async validateOrchestrationArtifacts(input) {
28270
30968
  return validateOrchestrationServiceCall(
28271
- buildValidateOrchestrationServiceCallInput(this.fileSystem, input)
30969
+ buildValidateOrchestrationServiceCallInput(
30970
+ this.fileSystem,
30971
+ input,
30972
+ this.runner
30973
+ )
28272
30974
  );
28273
30975
  }
28274
30976
  async renderSubagentTree(input) {