fix: 修复 Code Queue PR preflight fallback
This commit is contained in:
@@ -661,6 +661,135 @@ async function main(): Promise<void> {
|
||||
assertCondition(githubTransientPreflight.retryable === true, "GitHub transient full preflight should be retryable", githubTransientPreflight);
|
||||
assertCondition(githubTransientPreflight.commanderAction === "retry-backoff-or-keep-running-if-heartbeat-fresh", "GitHub transient full preflight should expose bounded commander action", githubTransientPreflight);
|
||||
|
||||
const k3sTunnelTimeout = {
|
||||
ok: false,
|
||||
status: 504,
|
||||
body: {
|
||||
ok: false,
|
||||
error: "provider HTTP tunnel timed out or disconnected",
|
||||
stage: "http-tunnel-wait",
|
||||
serviceId: "k3sctl-adapter",
|
||||
providerId: "D601",
|
||||
requestId: "req-k3s-timeout",
|
||||
detail: { retryable: true },
|
||||
},
|
||||
};
|
||||
const fallbackRuntime = rawRuntimePreflightFixture({
|
||||
pullRequestDelivery: {
|
||||
...asRecord(rawRuntimePreflightFixture().pullRequestDelivery),
|
||||
ok: true,
|
||||
credentials: {
|
||||
ghTokenPresent: true,
|
||||
githubTokenPresent: false,
|
||||
ghHostsConfigPresent: false,
|
||||
gitCredentialsPresent: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
const fallbackCalls: string[] = [];
|
||||
const k3sFallbackRecord = asRecord(await codexPrPreflightQueryForTest(["--remote", "--issue", "20"], {
|
||||
coreFetch: (path) => {
|
||||
fallbackCalls.push(path);
|
||||
if (path.includes("remote=1")) return k3sTunnelTimeout;
|
||||
return { ok: true, status: 200, body: { runtimePreflight: fallbackRuntime } };
|
||||
},
|
||||
}));
|
||||
assertCondition(fallbackCalls.length === 2, "k3s tunnel timeout should trigger exactly one fallback runtime-preflight fetch", fallbackCalls);
|
||||
assertCondition(fallbackCalls[0] === "/api/microservices/code-queue/proxy/api/runtime-preflight?remote=1&issue=20", "primary path should request remote runtime probes", fallbackCalls);
|
||||
assertCondition(fallbackCalls[1] === "/api/microservices/code-queue/proxy/api/runtime-preflight?issue=20", "fallback path should omit remote=1 but keep issue query", fallbackCalls);
|
||||
assertCondition(k3sFallbackRecord.ok === true, "transport-degraded fallback should preserve observed runtime preflight result", k3sFallbackRecord);
|
||||
assertCondition(k3sFallbackRecord.failureKind === null, "healthy fallback runtime should not be misclassified as infra-blocked", k3sFallbackRecord);
|
||||
const k3sFallbackSummary = asRecord(k3sFallbackRecord.summary);
|
||||
const k3sFallbackTransport = asRecord(k3sFallbackRecord.transportObservation);
|
||||
const k3sFallbackRuntime = asRecord(k3sFallbackRecord.runtimeObservation);
|
||||
assertCondition(k3sFallbackSummary.transportState === "transport-degraded", "summary should distinguish degraded transport from runtime observation", k3sFallbackSummary);
|
||||
assertCondition(k3sFallbackSummary.runtimePreflightState === "runtime-preflight-observed", "summary should expose observed runtime preflight after fallback", k3sFallbackSummary);
|
||||
assertCondition(k3sFallbackTransport.fallbackUsed === true && k3sFallbackTransport.state === "transport-degraded", "transport observation should mark fallback used", k3sFallbackTransport);
|
||||
assertCondition(asRecord(k3sFallbackTransport.primary).stage === "http-tunnel-wait", "primary transport summary should retain bounded tunnel stage", k3sFallbackTransport.primary);
|
||||
assertCondition(asRecord(k3sFallbackTransport.primary).serviceId === "k3sctl-adapter", "primary transport summary should retain service id", k3sFallbackTransport.primary);
|
||||
assertCondition(k3sFallbackRuntime.source === "fallback-runtime-preflight" && k3sFallbackRuntime.observed === true, "runtime observation should identify fallback source", k3sFallbackRuntime);
|
||||
assertCondition(k3sFallbackRecord.preflight === undefined, "transport fallback default output should omit detailed preflight", k3sFallbackRecord);
|
||||
assertCondition(JSON.stringify(k3sFallbackRecord).length < 14000, "transport fallback default output should stay compact", { chars: JSON.stringify(k3sFallbackRecord).length });
|
||||
|
||||
const bothPathsFailed = asRecord(await codexPrPreflightQueryForTest(["--remote"], {
|
||||
coreFetch: (path) => path.includes("remote=1")
|
||||
? k3sTunnelTimeout
|
||||
: {
|
||||
ok: false,
|
||||
status: 503,
|
||||
body: {
|
||||
ok: false,
|
||||
error: "code-queue runtime unavailable",
|
||||
stage: "runtime-preflight",
|
||||
serviceId: "code-queue",
|
||||
},
|
||||
},
|
||||
}));
|
||||
assertCondition(bothPathsFailed.ok === false, "both unavailable paths should fail", bothPathsFailed);
|
||||
assertCondition(bothPathsFailed.failureKind === "proxy-gap", "both unavailable paths should remain an infra/proxy observation gap", bothPathsFailed);
|
||||
assertCondition(bothPathsFailed.degradedReason === "runtime-preflight-transport-unavailable", "both unavailable paths should expose stable degraded reason", bothPathsFailed);
|
||||
assertCondition(bothPathsFailed.runnerDisposition === "infra-blocked", "both unavailable paths remain infra-blocked", bothPathsFailed);
|
||||
assertCondition(bothPathsFailed.blockingDisposition === "control-plane-observation-gap", "both unavailable paths should not pretend runner capability was observed", bothPathsFailed);
|
||||
const bothSummary = asRecord(bothPathsFailed.summary);
|
||||
const bothTransport = asRecord(bothPathsFailed.transportObservation);
|
||||
const bothRuntime = asRecord(bothPathsFailed.runtimeObservation);
|
||||
const bothScheduler = asRecord(bothPathsFailed.schedulerPreflight);
|
||||
assertCondition(bothSummary.transportState === "transport-blocked", "both paths failed summary should report transport-blocked", bothSummary);
|
||||
assertCondition(bothSummary.runtimePreflightState === "runtime-preflight-unobserved", "both paths failed summary should report runtime unobserved", bothSummary);
|
||||
assertCondition(bothSummary.schedulerPreflightAuthReady === null, "transport-blocked should not infer scheduler auth missing", bothSummary);
|
||||
assertCondition(bothTransport.fallbackUsed === false && bothRuntime.observed === false, "both paths failed should expose fallback attempted but runtime unobserved", { bothTransport, bothRuntime });
|
||||
assertCondition(bothScheduler.authReady === null && Array.isArray(bothScheduler.missing) && bothScheduler.missing.length === 0, "transport-blocked scheduler preflight should be unknown, not auth-missing", bothScheduler);
|
||||
|
||||
const skillsFallbackRecord = asRecord(await codexPrPreflightQueryForTest(["--remote"], {
|
||||
coreFetch: (path) => path.includes("remote=1")
|
||||
? k3sTunnelTimeout
|
||||
: {
|
||||
ok: true,
|
||||
status: 200,
|
||||
body: {
|
||||
runtimePreflight: rawRuntimePreflightFixture({
|
||||
ok: false,
|
||||
skills: {
|
||||
ok: false,
|
||||
runnerUsable: false,
|
||||
contractOk: false,
|
||||
blocker: "skills-target-missing",
|
||||
degradedReason: "skills-target-missing",
|
||||
resolution: { hostRolloutRequired: true },
|
||||
valuesPrinted: false,
|
||||
},
|
||||
skillsSync: {
|
||||
ok: false,
|
||||
degraded: true,
|
||||
blocker: "skills-target-missing",
|
||||
dryRun: true,
|
||||
mutation: false,
|
||||
valuesPrinted: false,
|
||||
},
|
||||
pullRequestDelivery: {
|
||||
...asRecord(rawRuntimePreflightFixture().pullRequestDelivery),
|
||||
ok: true,
|
||||
credentials: {
|
||||
ghTokenPresent: true,
|
||||
githubTokenPresent: false,
|
||||
ghHostsConfigPresent: false,
|
||||
gitCredentialsPresent: false,
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
}));
|
||||
assertCondition(skillsFallbackRecord.ok === false, "skills blocker observed through fallback should fail preflight", skillsFallbackRecord);
|
||||
assertCondition(skillsFallbackRecord.failureKind === "runner-skills-blocker", "skills-target-missing should classify as runner capability degraded, not proxy failure", skillsFallbackRecord);
|
||||
assertCondition(skillsFallbackRecord.degradedReason === "skills-target-missing", "skills degraded reason should preserve skills-target-missing", skillsFallbackRecord);
|
||||
assertCondition(skillsFallbackRecord.blockingDisposition === "runner-capability-degraded", "skills blocker should expose runner capability degraded disposition", skillsFallbackRecord);
|
||||
assertCondition(asRecord(skillsFallbackRecord.summary).transportState === "transport-degraded", "skills fallback should still expose degraded transport", skillsFallbackRecord.summary);
|
||||
assertCondition(asRecord(skillsFallbackRecord.summary).status === "runner-capability-degraded", "skills fallback summary should separate runner capability degradation", skillsFallbackRecord.summary);
|
||||
assertCondition(asRecord(skillsFallbackRecord.transportObservation).fallbackUsed === true, "skills fallback should mark transport fallback used", skillsFallbackRecord.transportObservation);
|
||||
assertCondition(asRecord(skillsFallbackRecord.runtimeObservation).observed === true, "skills fallback should report runtime observed", skillsFallbackRecord.runtimeObservation);
|
||||
assertCondition(asRecord(skillsFallbackRecord.skillsContract).degradedReason === "skills-target-missing", "skills contract should expose missing target", skillsFallbackRecord.skillsContract);
|
||||
|
||||
let observedDryRunPath = "";
|
||||
const dryRunContract = await codexPrPreflightQueryForTest([
|
||||
"--remote",
|
||||
@@ -934,6 +1063,9 @@ async function main(): Promise<void> {
|
||||
"auth missing returns auth-missing with broker/auth-broker-needed",
|
||||
"proxy failures return proxy-gap",
|
||||
"GitHub DNS/API failures return github-transient with retry/backoff guidance",
|
||||
"k3sctl HTTP tunnel 504 falls back to lightweight runtime-preflight observation",
|
||||
"double runtime-preflight transport failure remains infra-blocked without inferring auth gaps",
|
||||
"skills-target-missing fallback is runner-capability-degraded, not proxy unavailable",
|
||||
"git remote failures return git-remote-gap",
|
||||
"combined push/PR create dry-run contract stays read-only and separates system gh from UniDesk gh CLI",
|
||||
"broker-issued token, env-token, and missing-token branches expose authBroker source/capability/nextAction",
|
||||
|
||||
Reference in New Issue
Block a user