fix: classify codex steer proxy failures
This commit is contained in:
@@ -2,6 +2,7 @@ import { spawnSync } from "node:child_process";
|
||||
import { writeFileSync, unlinkSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { codexSteerTaskForTest } from "./src/code-queue";
|
||||
|
||||
type JsonRecord = Record<string, unknown>;
|
||||
|
||||
@@ -48,15 +49,35 @@ function assertDryRunPrompt(response: JsonRecord, expectedText: string): void {
|
||||
assertCondition(response.ok === true, "CLI dry-run should succeed", response);
|
||||
const data = nestedRecord(response.data, []);
|
||||
assertCondition(data.dryRun === true, "dry-run response should expose dryRun=true", data);
|
||||
const request = nestedRecord(response.data, ["request"]);
|
||||
assertCondition(request.method === "POST", "dry-run should expose request method", request);
|
||||
assertCondition(request.path === "/api/tasks/codex_test_task/steer", "dry-run should expose request path", request);
|
||||
assertCondition(request.stableProxyPath === "/api/microservices/code-queue/proxy/api/tasks/codex_test_task/steer", "dry-run should expose stable proxy path", request);
|
||||
const prompt = nestedRecord(response.data, ["request", "body", "prompt"]);
|
||||
assertCondition(prompt.text === expectedText, "dry-run prompt text mismatch", prompt);
|
||||
assertCondition(prompt.chars === expectedText.length, "dry-run prompt char count mismatch", prompt);
|
||||
assertCondition(prompt.truncated === false, "dry-run prompt must not truncate", prompt);
|
||||
const bodySummary = nestedRecord(response.data, ["request", "bodySummary"]);
|
||||
assertCondition(bodySummary.promptChars === expectedText.length, "dry-run should expose body prompt char count", bodySummary);
|
||||
const commands = nestedRecord(response.data, ["commands"]);
|
||||
assertCondition(String(commands.rawProxy || "").includes("microservice proxy code-queue /api/tasks/codex_test_task/steer --method POST"), "dry-run should expose raw proxy equivalent", commands);
|
||||
}
|
||||
|
||||
function assertReason(result: unknown, reason: string, status: number | null): void {
|
||||
const data = nestedRecord({ data: result }, ["data"]);
|
||||
assertCondition(data.ok === false, "classified steer failure should be ok=false", data);
|
||||
const diagnostics = nestedRecord(data, ["diagnostics"]);
|
||||
assertCondition(diagnostics.reason === reason, "unexpected steer failure reason", diagnostics);
|
||||
assertCondition(diagnostics.status === status, "unexpected steer failure status", diagnostics);
|
||||
assertCondition(typeof diagnostics.retryable === "boolean", "diagnostics should expose retryable boolean", diagnostics);
|
||||
assertCondition(Array.isArray(diagnostics.recommendedCrossChecks), "diagnostics should expose cross-check commands", diagnostics);
|
||||
}
|
||||
|
||||
export function runCodeQueueCliSteerContract(): JsonRecord {
|
||||
const positional = runCli(["codex", "steer", "codex_test_task", "correct the running task", "--dry-run"]);
|
||||
assertDryRunPrompt(positional.json ?? {}, "correct the running task");
|
||||
assertCondition(String(positional.json?.command || "").includes("<prompt:redacted>"), "outer command should redact positional steer prompt", positional.json ?? {});
|
||||
assertCondition(!String(positional.json?.command || "").includes("correct the running task"), "outer command must not echo positional steer prompt", positional.json ?? {});
|
||||
|
||||
const stdin = runCli(["codex", "steer", "codex_test_task", "--prompt-stdin", "--dry-run"], "stdin steer prompt\n");
|
||||
assertDryRunPrompt(stdin.json ?? {}, "stdin steer prompt\n");
|
||||
@@ -85,6 +106,51 @@ export function runCodeQueueCliSteerContract(): JsonRecord {
|
||||
const usage = stringArray(nestedRecord(help.json?.data, []).usage);
|
||||
assertCondition(usage.some((line) => line.includes("codex steer <taskId>")), "codex help should list steer", { usage });
|
||||
|
||||
let dryRunFetchCount = 0;
|
||||
const dryRunDirect = codexSteerTaskForTest("direct_task", ["do not send", "--dry-run"], () => {
|
||||
dryRunFetchCount += 1;
|
||||
return { ok: true, status: 200, body: { ok: true } };
|
||||
});
|
||||
assertCondition(dryRunFetchCount === 0, "dry-run must not call stable proxy helper", { dryRunFetchCount, dryRunDirect });
|
||||
|
||||
const longPrompt = `${"x".repeat(480)}-tail-secret-marker`;
|
||||
const longDryRun = codexSteerTaskForTest("direct_task", [longPrompt, "--dry-run"], () => {
|
||||
throw new Error("dry-run should not fetch");
|
||||
}) as JsonRecord;
|
||||
const longPreview = nestedRecord(longDryRun, ["request", "body", "prompt"]);
|
||||
assertCondition(longPreview.truncated === true, "long dry-run prompt should be truncated", longPreview);
|
||||
assertCondition(!String(longPreview.text || "").includes("tail-secret-marker"), "long dry-run must not leak prompt tail", longPreview);
|
||||
|
||||
let fetchPath = "";
|
||||
let fetchMethod = "";
|
||||
let fetchPrompt = "";
|
||||
const success = codexSteerTaskForTest("direct_task", ["send this"], (path, init) => {
|
||||
fetchPath = path;
|
||||
fetchMethod = String(init?.method || "");
|
||||
fetchPrompt = String((init?.body as JsonRecord | undefined)?.prompt || "");
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
body: {
|
||||
ok: true,
|
||||
task: { id: "direct_task", status: "running", prompt: "p" },
|
||||
queue: { activeTaskIds: ["direct_task"] },
|
||||
},
|
||||
};
|
||||
}) as JsonRecord;
|
||||
assertCondition(fetchPath === "/api/microservices/code-queue/proxy/api/tasks/direct_task/steer", "non-dry-run should use stable proxy path", { fetchPath });
|
||||
assertCondition(fetchMethod === "POST", "non-dry-run should POST", { fetchMethod });
|
||||
assertCondition(fetchPrompt === "send this", "non-dry-run should send raw prompt in body", { fetchPrompt });
|
||||
assertCondition(nestedRecord(success, ["steer"]).accepted === true, "successful steer should report accepted=true", success);
|
||||
|
||||
assertReason(codexSteerTaskForTest("direct_task", ["p"], () => ({ ok: false, exitCode: 1, stderrTail: "Cannot connect to the Docker daemon" })), "backend-core-unreachable", null);
|
||||
assertReason(codexSteerTaskForTest("direct_task", ["p"], () => ({ ok: false, status: 404, body: { ok: false, error: "microservice not found: code-queue" } })), "code-queue-microservice-unregistered", 404);
|
||||
assertReason(codexSteerTaskForTest("direct_task", ["p"], () => ({ ok: false, status: 401, body: { ok: false, error: "unauthorized" } })), "proxy-unauthorized", 401);
|
||||
assertReason(codexSteerTaskForTest("direct_task", ["p"], () => ({ ok: false, status: 404, body: { ok: false, error: "proxy route not found", path: "/api/microservices/code-queue/proxy/api/tasks/direct_task/steer" } })), "proxy-404", 404);
|
||||
assertReason(codexSteerTaskForTest("direct_task", ["p"], () => ({ ok: false, status: 404, body: { ok: false, error: "task not found" } })), "steer-endpoint-404", 404);
|
||||
assertReason(codexSteerTaskForTest("direct_task", ["p"], () => ({ ok: false, status: 409, body: { ok: false, error: "task does not have an active steerable turn" } })), "upstream-runtime-rejected", 409);
|
||||
assertReason(codexSteerTaskForTest("direct_task", ["p"], () => ({ ok: false, status: 504, body: { ok: false, error: "provider HTTP tunnel timed out or disconnected", stage: "http-tunnel-wait" } })), "stable-proxy-failed", 504);
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
checks: [
|
||||
@@ -94,6 +160,11 @@ export function runCodeQueueCliSteerContract(): JsonRecord {
|
||||
"duplicate prompt source failure",
|
||||
"unsupported option failure",
|
||||
"codex help lists steer",
|
||||
"outer command redacts positional steer prompt",
|
||||
"dry-run does not call stable proxy helper",
|
||||
"dry-run prompt preview is bounded",
|
||||
"non-dry-run uses stable proxy helper",
|
||||
"steer failure classification is JSON-consumable",
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user