@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.
- package/out/mcp-server.js +3160 -458
- package/package.json +1 -1
- package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +24 -0
- package/resources/claude-customizations/.claude/agents/epic-planner.md +121 -0
- package/resources/claude-customizations/.claude/agents/orchestrator.md +2 -2
- package/resources/claude-customizations/.claude/hooks/enforce-epic-invocation-origin.ps1 +246 -0
- package/resources/claude-customizations/.claude/settings.json +9 -2
- package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +7 -0
- package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +189 -0
- package/resources/claude-customizations/.claude/skills/epic-run/SKILL.md +38 -0
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +21 -2
- package/resources/claude-customizations/pack-manifests/core.json +4 -0
- package/resources/codex-and-agents-customizations/.agents/skills/architecture-boundaries/SKILL.md +0 -1
- package/resources/codex-and-agents-customizations/.agents/skills/codex-model-routing/SKILL.md +100 -0
- package/resources/codex-and-agents-customizations/.agents/skills/codex-model-routing/agents/openai.yaml +4 -0
- package/resources/codex-and-agents-customizations/.agents/skills/epic-orchestrate/SKILL.md +170 -0
- package/resources/codex-and-agents-customizations/.agents/skills/epic-plan/SKILL.md +230 -0
- package/resources/codex-and-agents-customizations/.agents/skills/epic-run/SKILL.md +39 -0
- package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +0 -1
- package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +0 -1
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +89 -9
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +39 -4
- package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +0 -1
- package/resources/codex-and-agents-customizations/.agents/skills/tonality/SKILL.md +0 -1
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c1.toml +153 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +153 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +153 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +153 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +153 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +2 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +95 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +95 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +95 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +95 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +95 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +2 -0
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c1.toml +99 -0
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c2.toml +99 -0
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3-elevated.toml +99 -0
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3.toml +99 -0
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c4.toml +99 -0
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer.toml +2 -0
- package/resources/codex-and-agents-customizations/.codex/agents/epic-orchestrator.toml +96 -0
- package/resources/codex-and-agents-customizations/.codex/agents/epic-planner.toml +97 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c1.toml +62 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c2.toml +62 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c3-elevated.toml +62 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c3.toml +62 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c4.toml +62 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer.toml +2 -0
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c1.toml +200 -0
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +200 -0
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +200 -0
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +200 -0
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +200 -0
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +53 -3
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c1.toml +110 -0
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c2.toml +110 -0
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3-elevated.toml +110 -0
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3.toml +110 -0
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c4.toml +110 -0
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer.toml +2 -0
- package/resources/codex-and-agents-customizations/.codex/agents/pr-author-c1.toml +28 -0
- package/resources/codex-and-agents-customizations/.codex/agents/pr-author-c2.toml +28 -0
- package/resources/codex-and-agents-customizations/.codex/agents/pr-author-c3-elevated.toml +28 -0
- package/resources/codex-and-agents-customizations/.codex/agents/pr-author-c3.toml +28 -0
- package/resources/codex-and-agents-customizations/.codex/agents/pr-author-c4.toml +28 -0
- package/resources/codex-and-agents-customizations/.codex/agents/pr-author.toml +2 -0
- package/resources/codex-and-agents-customizations/.codex/agents/prd-feature-c1.toml +55 -0
- package/resources/codex-and-agents-customizations/.codex/agents/prd-feature-c2.toml +55 -0
- package/resources/codex-and-agents-customizations/.codex/agents/prd-feature-c3-elevated.toml +55 -0
- package/resources/codex-and-agents-customizations/.codex/agents/prd-feature-c3.toml +55 -0
- package/resources/codex-and-agents-customizations/.codex/agents/prd-feature-c4.toml +55 -0
- package/resources/codex-and-agents-customizations/.codex/agents/prd-feature.toml +2 -0
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c1.toml +102 -0
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c2.toml +102 -0
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3-elevated.toml +102 -0
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3.toml +102 -0
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c4.toml +102 -0
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer.toml +2 -0
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c1.toml +105 -0
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c2.toml +105 -0
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c3-elevated.toml +105 -0
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c3.toml +105 -0
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c4.toml +105 -0
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher.toml +2 -0
- package/resources/codex-and-agents-customizations/.codex/agents/typescript-engineer-c1.toml +50 -0
- package/resources/codex-and-agents-customizations/.codex/agents/typescript-engineer-c2.toml +50 -0
- package/resources/codex-and-agents-customizations/.codex/agents/typescript-engineer-c3-elevated.toml +50 -0
- package/resources/codex-and-agents-customizations/.codex/agents/typescript-engineer-c3.toml +50 -0
- package/resources/codex-and-agents-customizations/.codex/agents/typescript-engineer-c4.toml +50 -0
- package/resources/codex-and-agents-customizations/.codex/agents/typescript-engineer.toml +2 -0
- package/resources/codex-and-agents-customizations/.codex/config.toml +202 -37
- package/resources/codex-and-agents-customizations/.codex/hooks/authorize-root-epic-invocation.ps1 +236 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/check-powershell-test-purity.ps1 +178 -69
- package/resources/codex-and-agents-customizations/.codex/hooks/check-python-test-purity.ps1 +92 -19
- package/resources/codex-and-agents-customizations/.codex/hooks/codex-agent-profile-attestation.ps1 +199 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/codex-authority-store.ps1 +182 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/codex-epic-child-launch-attestation.ps1 +129 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-checkpoint-monotonic.ps1 +148 -31
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-codex-model-routing.ps1 +198 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +28 -19
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-child-worktree-binding.ps1 +325 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +140 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +292 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-root-invocation.ps1 +135 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-wave-barrier.ps1 +295 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-worktree-removal-gate.ps1 +151 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-evidence-locations.ps1 +99 -34
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +143 -27
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +88 -43
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +65 -20
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +88 -42
- package/resources/codex-and-agents-customizations/.codex/hooks/record-subagent-routing-attestation.ps1 +414 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-bash.ps1 +164 -51
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-codex-subagent-routing.ps1 +154 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-feature-review-coverage.ps1 +58 -23
- package/resources/codex-and-agents-customizations/AGENTS.md +27 -0
- package/resources/codex-and-agents-customizations/pack-manifests/core.json +66 -1
- package/resources/codex-and-agents-customizations/pack-manifests/csharp-legacy.json +6 -1
- package/resources/codex-and-agents-customizations/pack-manifests/csharp-modern.json +6 -1
- package/resources/codex-and-agents-customizations/pack-manifests/powershell.json +6 -1
- package/resources/codex-and-agents-customizations/pack-manifests/python.json +6 -1
- package/resources/codex-and-agents-customizations/pack-manifests/typescript.json +6 -1
- package/resources/config/orchestration-routing.json +167 -0
- package/resources/powershell/PoshQC/PoshQC.Analyzer.psm1 +1 -1
- package/resources/powershell/PoshQC/PoshQC.ScanConfig.psm1 +125 -0
- package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +14 -2
- package/resources/powershell/PoshQC/PoshQC.psd1 +0 -4
- package/resources/powershell/PoshQC/PoshQC.psm1 +26 -6
- 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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
|
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
|
|
17514
|
-
if (
|
|
17537
|
+
const relativePath2 = relativeToPosix(sourcePath, effectiveSource);
|
|
17538
|
+
if (relativePath2 === null) {
|
|
17515
17539
|
continue;
|
|
17516
17540
|
}
|
|
17517
|
-
const destinationPath = joinPosix2(destinationRoot,
|
|
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:
|
|
17966
|
+
fs: publishingFs,
|
|
17885
17967
|
sourceRoot,
|
|
17886
17968
|
...options.artifactRoot === void 0 ? {} : { artifactRoot: options.artifactRoot },
|
|
17887
|
-
rootFolders:
|
|
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
|
|
18159
|
-
if (
|
|
18240
|
+
const relativePath2 = relativeToPosix3(path11, this.repoRoot);
|
|
18241
|
+
if (relativePath2 === null) {
|
|
18160
18242
|
return true;
|
|
18161
18243
|
}
|
|
18162
|
-
if (!isUnderAgentMemory(
|
|
18244
|
+
if (!isUnderAgentMemory(relativePath2)) {
|
|
18163
18245
|
return true;
|
|
18164
18246
|
}
|
|
18165
18247
|
const content = this.readText(path11);
|
|
18166
|
-
return isGeneralMemoryFile(
|
|
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
|
|
18579
|
-
if (matcher.test(
|
|
18580
|
-
matches.push(joinPosix8(normalizedRoot,
|
|
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),
|
|
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-
|
|
22006
|
-
var
|
|
22007
|
-
"
|
|
22008
|
-
"
|
|
22009
|
-
"
|
|
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
|
|
22020
|
-
|
|
22021
|
-
"
|
|
22022
|
-
"
|
|
22023
|
-
"
|
|
22024
|
-
"
|
|
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
|
|
22038
|
-
|
|
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
|
|
22045
|
-
|
|
22046
|
-
|
|
22047
|
-
|
|
22048
|
-
|
|
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
|
-
|
|
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
|
|
22054
|
-
if (
|
|
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
|
-
|
|
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
|
|
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
|
|
22064
|
-
|
|
22065
|
-
|
|
22066
|
-
|
|
22067
|
-
|
|
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
|
-
|
|
22407
|
+
if (!isCanonicalWorktreePath(feature["worktree_path"])) {
|
|
22088
22408
|
errors.push(
|
|
22089
|
-
|
|
22409
|
+
`${prefix}.worktree_path must be a non-empty canonical absolute path.`
|
|
22090
22410
|
);
|
|
22091
22411
|
}
|
|
22092
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
22110
|
-
|
|
22111
|
-
|
|
22112
|
-
|
|
22113
|
-
|
|
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
|
-
|
|
22117
|
-
|
|
22118
|
-
|
|
22119
|
-
|
|
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
|
-
|
|
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 (!
|
|
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 =
|
|
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 (!
|
|
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/
|
|
22235
|
-
|
|
22236
|
-
|
|
22237
|
-
|
|
22238
|
-
|
|
22239
|
-
|
|
22240
|
-
|
|
22241
|
-
|
|
22242
|
-
|
|
22243
|
-
|
|
22244
|
-
|
|
22245
|
-
|
|
22246
|
-
|
|
22247
|
-
|
|
22248
|
-
|
|
22249
|
-
|
|
22250
|
-
|
|
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
|
|
22254
|
-
|
|
22255
|
-
|
|
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
|
-
|
|
22258
|
-
|
|
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
|
-
|
|
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
|
|
22267
|
-
const
|
|
22268
|
-
|
|
22269
|
-
|
|
22270
|
-
|
|
22271
|
-
|
|
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 (
|
|
24231
|
+
if (supplied !== expected) {
|
|
22276
24232
|
errors.push(
|
|
22277
|
-
`
|
|
24233
|
+
`Execution-ready epic planner artifact path must be '${expected}'.`
|
|
22278
24234
|
);
|
|
22279
24235
|
}
|
|
22280
|
-
|
|
22281
|
-
|
|
22282
|
-
|
|
22283
|
-
|
|
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
|
|
22290
|
-
const
|
|
22291
|
-
const
|
|
22292
|
-
|
|
22293
|
-
|
|
22294
|
-
|
|
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
|
-
|
|
24341
|
+
const slug = state["epic_feature_folder"];
|
|
24342
|
+
if (parsed.slug !== slug || parsed.invocationSlug !== slug) {
|
|
22299
24343
|
errors.push(
|
|
22300
|
-
|
|
24344
|
+
`${label} kickoff slug and /epic-run slug must match state '${String(slug)}'.`
|
|
22301
24345
|
);
|
|
22302
24346
|
}
|
|
22303
|
-
if (
|
|
24347
|
+
if (parsed.manifestPath !== state["epic_manifest_path"]) {
|
|
22304
24348
|
errors.push(
|
|
22305
|
-
|
|
24349
|
+
`${label} kickoff manifest reference must match epic_manifest_path.`
|
|
22306
24350
|
);
|
|
22307
24351
|
}
|
|
22308
|
-
|
|
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
|
-
|
|
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
|
|
22318
|
-
|
|
22319
|
-
|
|
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
|
|
22322
|
-
|
|
22323
|
-
|
|
22324
|
-
|
|
22325
|
-
|
|
22326
|
-
|
|
22327
|
-
|
|
22328
|
-
|
|
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
|
|
22332
|
-
if (
|
|
22333
|
-
|
|
22334
|
-
|
|
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
|
-
|
|
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/
|
|
22341
|
-
var
|
|
22342
|
-
|
|
22343
|
-
|
|
22344
|
-
"
|
|
22345
|
-
"
|
|
22346
|
-
"
|
|
22347
|
-
|
|
22348
|
-
|
|
22349
|
-
|
|
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
|
|
22353
|
-
if (value ===
|
|
24504
|
+
function pythonRepr6(value) {
|
|
24505
|
+
if (value === void 0 || value === null) {
|
|
22354
24506
|
return "None";
|
|
22355
24507
|
}
|
|
22356
|
-
if (value ===
|
|
22357
|
-
return "
|
|
24508
|
+
if (typeof value === "string") {
|
|
24509
|
+
return `'${value.replaceAll("\\", "\\\\").replaceAll("'", "\\'")}'`;
|
|
22358
24510
|
}
|
|
22359
|
-
if (value ===
|
|
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
|
|
22365
|
-
const errors =
|
|
22366
|
-
if (!
|
|
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
|
|
22371
|
-
|
|
22372
|
-
|
|
22373
|
-
|
|
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
|
-
|
|
22376
|
-
|
|
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
|
-
|
|
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
|
|
22383
|
-
|
|
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
|
-
`
|
|
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
|
-
|
|
22390
|
-
|
|
22391
|
-
|
|
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
|
-
|
|
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
|
-
|
|
22402
|
-
|
|
22403
|
-
|
|
22404
|
-
|
|
22405
|
-
|
|
22406
|
-
|
|
22407
|
-
|
|
22408
|
-
}
|
|
22409
|
-
|
|
22410
|
-
|
|
22411
|
-
|
|
22412
|
-
|
|
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
|
-
|
|
24771
|
+
"Epic planner checkpoint max_parallel_features must be an integer from 1 through 8."
|
|
22415
24772
|
);
|
|
22416
24773
|
}
|
|
22417
|
-
|
|
22418
|
-
|
|
22419
|
-
|
|
22420
|
-
|
|
22421
|
-
|
|
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
|
-
|
|
24782
|
+
"Execution readiness requires epic_worthiness.verdict 'epic'."
|
|
22424
24783
|
);
|
|
22425
24784
|
}
|
|
22426
|
-
|
|
22427
|
-
|
|
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
|
-
|
|
24792
|
+
...validateEpicPlannerChildLaunchBindings(featureResult.features)
|
|
22430
24793
|
);
|
|
22431
|
-
|
|
22432
|
-
|
|
22433
|
-
|
|
22434
|
-
|
|
22435
|
-
|
|
22436
|
-
|
|
22437
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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 (!
|
|
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 (!
|
|
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
|
|
22548
|
-
return
|
|
25139
|
+
if (value === true) {
|
|
25140
|
+
return "True";
|
|
22549
25141
|
}
|
|
22550
|
-
|
|
25142
|
+
if (value === false) {
|
|
25143
|
+
return "False";
|
|
25144
|
+
}
|
|
25145
|
+
return String(value);
|
|
22551
25146
|
}
|
|
22552
|
-
function
|
|
22553
|
-
const
|
|
22554
|
-
if (
|
|
22555
|
-
|
|
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
|
-
|
|
22558
|
-
|
|
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
|
-
|
|
22561
|
-
|
|
22562
|
-
if (!isObject7(operation)) {
|
|
25158
|
+
requirements.forEach((requirement, index) => {
|
|
25159
|
+
if (!isObject10(requirement)) {
|
|
22563
25160
|
errors.push(
|
|
22564
|
-
`Checkpoint
|
|
25161
|
+
`Checkpoint human_interaction.requirements #${index} must be an object.`
|
|
22565
25162
|
);
|
|
22566
25163
|
return;
|
|
22567
25164
|
}
|
|
22568
|
-
|
|
25165
|
+
const response = requirement["response"];
|
|
25166
|
+
if (typeof response !== "string" || !HUMAN_INTERACTION_RESPONSE_ENUM.has(response)) {
|
|
22569
25167
|
errors.push(
|
|
22570
|
-
`Checkpoint
|
|
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
|
-
|
|
22577
|
-
|
|
22578
|
-
|
|
22579
|
-
|
|
22580
|
-
|
|
22581
|
-
|
|
22582
|
-
|
|
22583
|
-
|
|
22584
|
-
|
|
22585
|
-
|
|
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
|
|
22598
|
-
|
|
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
|
|
25197
|
+
`Checkpoint remediation cycle #${index} plan_path must be a non-empty string.`
|
|
22603
25198
|
);
|
|
22604
25199
|
}
|
|
22605
|
-
|
|
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
|
|
25212
|
+
`Checkpoint remediation cycle #${index} exit_condition_met is true but blocking_count is not 0.`
|
|
22608
25213
|
);
|
|
22609
25214
|
}
|
|
22610
|
-
|
|
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
|
-
`
|
|
25279
|
+
`Preparation checkpoint next_step must be ${pythonRepr8(EXPECTED_NEXT_STEP)}, found ${pythonRepr8(state["next_step"])}.`
|
|
22613
25280
|
);
|
|
22614
25281
|
}
|
|
22615
|
-
const
|
|
22616
|
-
|
|
22617
|
-
|
|
22618
|
-
|
|
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
|
-
|
|
22622
|
-
|
|
22623
|
-
|
|
22624
|
-
|
|
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
|
|
22628
|
-
|
|
22629
|
-
|
|
22630
|
-
|
|
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
|
-
|
|
22634
|
-
|
|
22635
|
-
|
|
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
|
-
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (
|
|
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
|
-
|
|
22884
|
-
errors.push(...
|
|
22885
|
-
|
|
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
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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(
|
|
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
|
|
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 =
|
|
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,
|
|
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: ${
|
|
28977
|
+
From: ${relativePath2}
|
|
26280
28978
|
`;
|
|
26281
28979
|
}
|
|
26282
|
-
function buildBugBody(workMode, sections,
|
|
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: ${
|
|
28987
|
+
From: ${relativePath2}`);
|
|
26290
28988
|
return parts.join("\n\n") + "\n";
|
|
26291
28989
|
}
|
|
26292
|
-
function buildMinorAuditBody(workMode, problem, implementationIntent, acceptanceCriteria, dependenciesRisks, verificationSteps, evidenceChecklist,
|
|
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: ${
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
|
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,
|
|
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 (!
|
|
30617
|
+
if (!isRecord5(parsed)) {
|
|
27920
30618
|
continue;
|
|
27921
30619
|
}
|
|
27922
30620
|
const message = parsed["message"];
|
|
27923
|
-
if (!
|
|
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
|
|
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 (!
|
|
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(
|
|
30969
|
+
buildValidateOrchestrationServiceCallInput(
|
|
30970
|
+
this.fileSystem,
|
|
30971
|
+
input,
|
|
30972
|
+
this.runner
|
|
30973
|
+
)
|
|
28272
30974
|
);
|
|
28273
30975
|
}
|
|
28274
30976
|
async renderSubagentTree(input) {
|