fix: tighten code queue pr preflight contract
This commit is contained in:
@@ -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`);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user