fix: tighten code queue pr preflight contract

This commit is contained in:
Codex
2026-05-21 12:55:59 +00:00
parent d709b74bb3
commit a9929cc3d9
5 changed files with 296 additions and 17 deletions
@@ -69,6 +69,30 @@ function remoteControlPlaneResult(overrides: Partial<JsonRecord> = {}): JsonReco
missing: [],
scope: "scheduler-runner-env",
},
authBroker: {
ok: true,
source: "GH_TOKEN",
needed: false,
configured: false,
runnerDisposition: "ready",
failureKind: null,
degradedReason: null,
runnerEnvTokenRequiredWithoutBroker: true,
brokerCredentialSource: null,
valuesPrinted: false,
evidence: {
envTokenMissing: false,
missing: [],
systemGhBinaryOk: true,
systemGhBinaryRequiredForWrites: false,
unideskGhCliObserved: true,
unideskGhCliOk: true,
unideskGhCliRequiresSystemGhBinary: false,
systemGhMissingMisclassifiedAsUniDeskCliMissing: false,
},
next: [],
reference: "docs/reference/auth-broker.md#post-v1githubpr-preflight",
},
prCapabilityContract: {
targetBranch: "master",
tokenSource: "GH_TOKEN",
@@ -231,7 +255,7 @@ async function main(): Promise<void> {
});
const remoteControlPlaneMissingRecord = asRecord(authMissing);
assertCondition(remoteControlPlaneMissingRecord.ok === false, "missing control plane should fail", remoteControlPlaneMissingRecord);
assertCondition(remoteControlPlaneMissingRecord.failureKind === "proxy-gap", "missing control plane should classify as proxy-gap", remoteControlPlaneMissingRecord);
assertCondition(remoteControlPlaneMissingRecord.failureKind === "control-plane-missing", "missing control plane should classify as control-plane-missing", remoteControlPlaneMissingRecord);
assertCondition(remoteControlPlaneMissingRecord.degradedReason === "remote-control-plane-unreachable", "missing control plane should classify as remote-control-plane-unreachable", remoteControlPlaneMissingRecord);
assertCondition(asRecord(remoteControlPlaneMissingRecord.controlPlane).localBackendCoreMissing === true, "local backend-core absence should remain evidence only", remoteControlPlaneMissingRecord.controlPlane);
@@ -277,16 +301,211 @@ async function main(): Promise<void> {
const gitRemoteGapRecord = asRecord(gitRemoteGap);
assertCondition(gitRemoteGapRecord.failureKind === "git-remote-gap", "git probe failures should stay structured", gitRemoteGapRecord);
const proxyGap = await codexPrPreflightQueryForTest(["--remote"], {
config: null,
coreFetch: () => ({
ok: true,
status: 200,
body: {
runtimePreflight: {
ok: false,
checkedAt: "2026-05-20T00:00:00.000Z",
cwd: "/workspace/unidesk",
pid: 123,
pullRequestDelivery: {
ok: false,
checkedAt: "2026-05-20T00:00:00.000Z",
tools: {
git: { ok: true, path: "/usr/bin/git", version: "git version 2.43.0" },
gh: { ok: true, path: "/usr/bin/gh", version: "gh version 2.45.0" },
jq: { ok: true, path: "/usr/bin/jq", version: "jq-1.7" },
ssh: { ok: true, path: "/usr/bin/ssh", version: "OpenSSH_9.6" },
curl: { ok: true, path: "/usr/bin/curl", version: "curl 8.5.0" },
},
unideskGhCli: { ok: true, path: "/workspace/unidesk/scripts/cli.ts", present: true },
credentials: {
ghTokenPresent: true,
githubTokenPresent: false,
ghHostsConfigPresent: false,
gitCredentialsPresent: false,
},
git: {
insideWorktree: true,
branch: "feature/code-queue-pr-preflight",
head: "abc1234",
originMaster: "def5678",
remoteOrigin: "git@github.com:pikasTech/unidesk.git",
home: "/root",
homeWritable: true,
knownHostsPresent: true,
privateKeyPresent: true,
},
githubContext: {
host: "github.com",
apiBaseUrl: "https://api.github.com",
repo: "pikasTech/unidesk",
issueProbeNumber: 20,
},
egress: {
proxy: {
selectedProxyHost: "missing-egress-proxy.unidesk.svc.cluster.local",
selectedProxyPort: "18789",
selectedProxyHostResolvable: false,
},
githubDefault: { command: "curl", args: ["-IsS", "https://github.com"], ok: false, exitCode: 6, signal: null, error: null, stdout: "", stderr: "Could not resolve proxy" },
apiDefault: { command: "curl", args: ["-IsS", "https://api.github.com"], ok: false, exitCode: 6, signal: null, error: null, stdout: "", stderr: "Could not resolve proxy" },
issueApi: null,
},
remote: {
gitLsRemote: { command: "git", args: ["ls-remote", "--heads", "origin", "master"], ok: true, exitCode: 0, signal: null, error: null, stdout: "abc1234\trefs/heads/master\n", stderr: "" },
gitHttpsLsRemote: null,
githubSshAuthenticated: true,
ghAuthStatus: { command: "gh", args: ["auth", "status"], ok: true, exitCode: 0, signal: null, error: null, stdout: "", stderr: "" },
ghRepoView: { command: "gh", args: ["repo", "view", "pikasTech/unidesk"], ok: true, exitCode: 0, signal: null, error: null, stdout: "", stderr: "" },
ghIssueView: { command: "gh", args: ["issue", "view", "20"], ok: true, exitCode: 0, signal: null, error: null, stdout: "", stderr: "" },
ghPrReadOnly: { command: "gh", args: ["pr", "list"], ok: true, exitCode: 0, signal: null, error: null, stdout: "", stderr: "" },
},
limitations: [
"configured GitHub egress proxy host is not resolvable: missing-egress-proxy.unidesk.svc.cluster.local",
"GitHub HTTPS probe failed with the default environment/proxy",
],
risks: [],
},
ports: {},
},
},
}),
});
const proxyGapRecord = asRecord(proxyGap);
assertCondition(proxyGapRecord.failureKind === "proxy-gap", "proxy failures should classify as proxy-gap", proxyGapRecord);
assertCondition(proxyGapRecord.degradedReason === "configured GitHub egress proxy host is not resolvable: missing-egress-proxy.unidesk.svc.cluster.local", "proxy degraded reason should point at the proxy", proxyGapRecord);
let observedDryRunPath = "";
const dryRunContract = await codexPrPreflightQueryForTest([
"--remote",
"--push-dry-run",
"--push-dry-run-ref",
"refs/heads/probe/code-queue-pr-capability",
"--pr-create-dry-run",
"--pr-create-dry-run-head",
"code-queue/issue-35-pr-dry-run-probe",
"--issue",
"20",
], {
config: null,
coreFetch: (path) => {
observedDryRunPath = path;
return {
ok: true,
status: 200,
body: {
runtimePreflight: {
ok: false,
checkedAt: "2026-05-20T00:00:00.000Z",
cwd: "/workspace/unidesk",
pid: 123,
pullRequestDelivery: {
ok: false,
checkedAt: "2026-05-20T00:00:00.000Z",
tools: {
git: { ok: true, path: "/usr/bin/git", version: "git version 2.43.0" },
gh: { ok: false, path: null, version: null },
hub: { ok: false, path: null, version: null },
jq: { ok: true, path: "/usr/bin/jq", version: "jq-1.7" },
ssh: { ok: true, path: "/usr/bin/ssh", version: "OpenSSH_9.6" },
curl: { ok: true, path: "/usr/bin/curl", version: "curl 8.5.0" },
},
unideskGhCli: { ok: true, path: "/workspace/unidesk/scripts/cli.ts", present: true },
credentials: {
ghTokenPresent: false,
githubTokenPresent: false,
ghHostsConfigPresent: false,
gitCredentialsPresent: false,
},
git: {
insideWorktree: true,
branch: "code-queue/issue-35-pr-dry-run-probe",
head: "abc1234",
originMaster: "def5678",
remoteOrigin: "git@github.com:pikasTech/unidesk.git",
home: "/root",
homeWritable: true,
knownHostsPresent: true,
privateKeyPresent: false,
},
githubContext: {
host: "github.com",
apiBaseUrl: "https://api.github.com",
repo: "pikasTech/unidesk",
issueProbeNumber: 20,
},
egress: {
proxy: {
selectedProxyHost: "d601-provider-egress-proxy.unidesk.svc.cluster.local",
selectedProxyPort: "18789",
selectedProxyHostResolvable: true,
},
githubDefault: { command: "curl", args: ["-IsS", "https://github.com"], ok: true, exitCode: 0, signal: null, error: null, stdout: "", stderr: "" },
apiDefault: { command: "curl", args: ["-IsS", "https://api.github.com"], ok: true, exitCode: 0, signal: null, error: null, stdout: "", stderr: "" },
issueApi: { command: "sh", args: ["-lc", "curl issue"], ok: false, exitCode: 1, signal: null, error: null, stdout: "http_status=404", stderr: "" },
},
remote: {
gitLsRemote: { command: "git", args: ["ls-remote", "--heads", "origin", "master"], ok: true, exitCode: 0, signal: null, error: null, stdout: "abc1234\trefs/heads/master\n", stderr: "" },
gitHttpsLsRemote: { command: "git", args: ["ls-remote", "--heads", "https://github.com/pikasTech/unidesk.git", "master"], ok: false, exitCode: 128, signal: null, error: null, stdout: "", stderr: "Authentication failed" },
githubSshAuthenticated: true,
ghAuthStatus: null,
ghRepoView: null,
ghIssueView: null,
ghPrReadOnly: null,
},
pushDryRun: { command: "git", args: ["push", "--dry-run", "origin", "HEAD:refs/heads/probe/code-queue-pr-capability"], ok: false, exitCode: 128, signal: null, error: null, stdout: "", stderr: "Permission denied" },
prCreateDryRun: { command: "sh", args: ["-lc", "bun scripts/cli.ts gh pr create --dry-run"], ok: false, exitCode: 1, signal: null, error: null, stdout: "", stderr: "GH_TOKEN/GITHUB_TOKEN missing" },
limitations: [
"GH_TOKEN/GITHUB_TOKEN is not present; gh cannot create PRs unless another gh credential store is mounted",
"git push --dry-run failed for probe branch",
"PR create dry-run body/command guard failed",
],
risks: [
"system gh binary is missing; UniDesk REST gh CLI remains the supported PR create/comment path when scripts/cli.ts and GH_TOKEN/GITHUB_TOKEN are available",
],
},
ports: {},
},
},
};
},
});
assertCondition(observedDryRunPath === "/api/microservices/code-queue/proxy/api/runtime-preflight?remote=1&pushDryRun=1&pushDryRunRef=refs%2Fheads%2Fprobe%2Fcode-queue-pr-capability&prCreateDryRun=1&prCreateDryRunHead=code-queue%2Fissue-35-pr-dry-run-probe&issue=20", "combined dry-run query should pass all requested guards", { observedDryRunPath });
const dryRunRecord = asRecord(dryRunContract);
assertCondition(dryRunRecord.failureKind === "auth-missing", "missing runner token should remain auth-missing even when system gh is absent", dryRunRecord);
const dryRunPreflight = asRecord(dryRunRecord.preflight);
const dryRunAuthBroker = asRecord(dryRunPreflight.authBroker);
assertCondition(dryRunAuthBroker.source === "broker/auth-broker-needed", "missing runner token should expose broker/auth-broker-needed", dryRunAuthBroker);
assertCondition(dryRunAuthBroker.degradedReason === "auth-broker-needed", "auth broker degraded reason should be explicit", dryRunAuthBroker);
const dryRunBrokerEvidence = asRecord(dryRunAuthBroker.evidence);
assertCondition(dryRunBrokerEvidence.systemGhBinaryOk === false, "system gh absence should be reported separately", dryRunBrokerEvidence);
assertCondition(dryRunBrokerEvidence.unideskGhCliOk === true, "UniDesk REST gh CLI should not be marked unavailable because system gh is missing", dryRunBrokerEvidence);
assertCondition(dryRunBrokerEvidence.systemGhMissingMisclassifiedAsUniDeskCliMissing === false, "system gh absence must not be misclassified", dryRunBrokerEvidence);
const dryRunPrContract = asRecord(dryRunPreflight.prCapabilityContract);
assertCondition(asRecord(dryRunPrContract.pushDryRun).requested === true, "push dry-run should be requested", dryRunPrContract);
assertCondition(asRecord(dryRunPrContract.pushDryRun).writesRemote === false, "push dry-run must be marked non-writing", dryRunPrContract);
assertCondition(asRecord(dryRunPrContract.prCreateDryRun).requested === true, "PR create dry-run should be requested", dryRunPrContract);
assertCondition(asRecord(dryRunPrContract.prCreateDryRun).writesRemote === false, "PR create dry-run must be marked non-writing", dryRunPrContract);
assertCondition(asRecord(dryRunPrContract.prCreateDryRun).headBranch === "code-queue/issue-35-pr-dry-run-probe", "PR dry-run head should come from the option", dryRunPrContract);
process.stdout.write(`${JSON.stringify({
ok: true,
checks: [
"runner-like local target-stack absence does not block remote fallback",
"remote control plane fallback preserves ready preflight",
"missing remote control plane returns proxy-gap",
"auth missing returns auth-missing",
"missing remote control plane returns control-plane-missing",
"auth missing returns auth-missing with broker/auth-broker-needed",
"proxy failures return proxy-gap",
"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",
],
observedLocalPath,
observedDryRunPath,
}, null, 2)}\n`);
}
+70 -10
View File
@@ -222,7 +222,7 @@ interface CodexPrPreflightOptions {
full: boolean;
}
type CodeQueuePrPreflightFailureKind = "auth-missing" | "proxy-gap" | "git-remote-gap" | "target-stack-not-running";
type CodeQueuePrPreflightFailureKind = "auth-missing" | "proxy-gap" | "git-remote-gap" | "control-plane-missing" | "target-stack-not-running";
interface CodeQueuePrPreflightTransport {
config?: UniDeskConfig | null;
@@ -2400,13 +2400,21 @@ function compactToolStatus(value: unknown): Record<string, unknown> {
}
function compactUniDeskGhCliStatus(value: unknown): Record<string, unknown> {
const cli = asRecord(value) ?? {};
const cli = asRecord(value);
const observed = cli !== null;
const ok = observed ? cli.ok === true : null;
return {
ok: cli.ok ?? false,
path: cli.path ?? null,
present: cli.present ?? false,
ok,
observed,
path: observed ? cli.path ?? null : null,
present: observed ? cli.present ?? false : null,
role: "repo-native REST GitHub CLI used by bun scripts/cli.ts gh",
requiresSystemGhBinary: false,
unavailableReason: ok === true
? null
: observed
? "scripts-cli-missing"
: "runtime-preflight-did-not-report-unidesk-gh-cli",
};
}
@@ -2440,10 +2448,45 @@ function tokenCoverageStatus(credentials: Record<string, unknown>): Record<strin
};
}
function authBrokerNeededStatus(tokenCoverage: Record<string, unknown>, systemGhBinary: Record<string, unknown>, unideskGhCli: Record<string, unknown>): Record<string, unknown> {
const missing = Array.isArray(tokenCoverage.missing) ? tokenCoverage.missing.map(String) : [];
const needed = tokenCoverage.ok !== true;
return {
ok: !needed,
source: needed ? "broker/auth-broker-needed" : tokenCoverage.source ?? "runner-env-token",
needed,
configured: false,
runnerDisposition: needed ? "infra-blocked" : "ready",
failureKind: needed ? "auth-missing" : null,
degradedReason: needed ? "auth-broker-needed" : null,
runnerEnvTokenRequiredWithoutBroker: true,
brokerCredentialSource: null,
valuesPrinted: false,
evidence: {
envTokenMissing: needed,
missing,
systemGhBinaryOk: systemGhBinary.ok === true,
systemGhBinaryRequiredForWrites: false,
unideskGhCliObserved: unideskGhCli.observed ?? false,
unideskGhCliOk: unideskGhCli.ok ?? null,
unideskGhCliRequiresSystemGhBinary: false,
systemGhMissingMisclassifiedAsUniDeskCliMissing: false,
},
next: needed
? [
"inject GH_TOKEN or GITHUB_TOKEN into the Code Queue scheduler runtime secret",
"or configure the planned auth-broker so PR preflight can use a broker-held GitHub credential without exposing runner env tokens",
]
: [],
reference: "docs/reference/auth-broker.md#post-v1githubpr-preflight",
};
}
function compactPrRuntimePreflight(preflight: Record<string, unknown>, options: CodexPrPreflightOptions): Record<string, unknown> {
const pull = asRecord(preflight.pullRequestDelivery) ?? {};
const tools = asRecord(pull.tools) ?? {};
const unideskGhCli = compactUniDeskGhCliStatus(pull.unideskGhCli);
const systemGhBinary = compactToolStatus(tools.gh);
const credentials = asRecord(pull.credentials) ?? {};
const git = asRecord(pull.git) ?? {};
const githubContext = asRecord(pull.githubContext) ?? {};
@@ -2455,12 +2498,28 @@ function compactPrRuntimePreflight(preflight: Record<string, unknown>, options:
const limitations = Array.isArray(pull.limitations) ? pull.limitations.map(String) : [];
const risks = Array.isArray(pull.risks) ? pull.risks.map(String) : [];
const ok = preflight.ok === true && tokenCoverage.ok === true;
const failureKind = !tokenCoverage.ok
? "auth-missing"
: limitations.some((item) => item.includes("git ls-remote") || item.includes("git push --dry-run failed"))
? "git-remote-gap"
: !preflight.ok
? "proxy-gap"
: null;
const degradedReason = failureKind === "auth-missing"
? "GH_TOKEN/GITHUB_TOKEN missing"
: failureKind === "git-remote-gap"
? "git remote probe failed"
: failureKind === "proxy-gap"
? limitations.find((item) => item.includes("proxy") || item.includes("auth") || item.includes("egress") || item.includes("reachable")) ?? null
: null;
const defaultPushDryRunRef = "refs/heads/probe/code-queue-pr-capability-dryrun";
const pushDryRunRef = options.pushDryRunRef ?? defaultPushDryRunRef;
const targetBranch = "master";
const expectedHeadBranch = options.prCreateDryRunHead ?? (typeof git.branch === "string" && git.branch.length > 0 ? git.branch : "<head-branch>");
const result: Record<string, unknown> = {
ok,
failureKind,
degradedReason,
checkedAt: preflight.checkedAt ?? pull.checkedAt ?? null,
runner: {
serviceId: "code-queue",
@@ -2470,6 +2529,7 @@ function compactPrRuntimePreflight(preflight: Record<string, unknown>, options:
pid: preflight.pid ?? null,
},
tokenCoverage,
authBroker: authBrokerNeededStatus(tokenCoverage, systemGhBinary, unideskGhCli),
prCapabilityContract: {
targetBranch,
tokenSource: tokenCoverage.source,
@@ -2510,8 +2570,8 @@ function compactPrRuntimePreflight(preflight: Record<string, unknown>, options:
},
tools: {
git: compactToolStatus(tools.git),
gh: compactToolStatus(tools.gh),
systemGhBinary: compactToolStatus(tools.gh),
gh: systemGhBinary,
systemGhBinary,
hub: compactToolStatus(tools.hub),
jq: compactToolStatus(tools.jq),
ssh: compactToolStatus(tools.ssh),
@@ -2595,7 +2655,7 @@ function queryRemoteMainServerPrPreflight(optionArgs: string[], config: UniDeskC
return {
ok: false,
runnerDisposition: "infra-blocked",
failureKind: "proxy-gap",
failureKind: "control-plane-missing",
degradedReason: "remote-control-plane-unreachable",
message,
controlPlane: {
@@ -2620,7 +2680,7 @@ function queryRemoteMainServerPrPreflight(optionArgs: string[], config: UniDeskC
return {
ok: false,
runnerDisposition: "infra-blocked",
failureKind: "proxy-gap",
failureKind: "control-plane-missing",
degradedReason: "remote-control-plane-unreachable",
message,
controlPlane: {
@@ -2696,7 +2756,7 @@ function codeQueuePrPreflight(optionArgs: string[] = [], transport: CodeQueuePrP
}
if (localRecord?.ok !== true) {
if (options.remote && localTargetStackMissing) {
const failureKind: CodeQueuePrPreflightFailureKind = "proxy-gap";
const failureKind: CodeQueuePrPreflightFailureKind = "control-plane-missing";
const degradedReason = "remote-control-plane-unreachable";
return {
...(localRecord ?? {}),