fix: split github contracts from script check

This commit is contained in:
Codex
2026-06-10 14:36:00 +00:00
parent 420a9a9e85
commit 4ee3a67089
12 changed files with 233 additions and 94 deletions
@@ -0,0 +1,31 @@
import { strict as assert } from "node:assert";
import { parseCheckOptions, runChecks } from "./src/check";
const scriptsOnly = parseCheckOptions(["--scripts-typecheck"]);
assert.equal(scriptsOnly.scriptsTypecheck, true);
assert.equal(scriptsOnly.ghContracts, false);
const full = parseCheckOptions(["--full"]);
assert.equal(full.scriptsTypecheck, true);
assert.equal(full.ghContracts, true);
const explicit = parseCheckOptions(["--gh-contracts"]);
assert.equal(explicit.scriptsTypecheck, false);
assert.equal(explicit.ghContracts, true);
const minimalConfig = {
project: { name: "contract" },
runtime: "contract",
} as never;
const result = runChecks(minimalConfig, { ...scriptsOnly, scriptsTypecheck: false });
const issueGuard = result.items.find((item) => item.name === "gh:issue-guard-contract");
const prFiles = result.items.find((item) => item.name === "gh:pr-files-contract");
const pr = result.items.find((item) => item.name === "gh:pr-contract");
for (const item of [issueGuard, prFiles, pr]) {
assert.ok(item, "GitHub contract item should be visible in check output");
assert.equal(item?.ok, true);
assert.equal((item?.detail as { skipped?: boolean }).skipped, true);
}
console.log("check gh contract scope contract ok");
+30 -15
View File
@@ -49,6 +49,20 @@ function deterministicSteerId(taskId: string, prompt: string): string {
return `steer_${Bun.SHA256.hash(`unidesk-code-queue-steer:v1\0${taskId}\0${prompt}`, "hex").slice(0, 24)}`;
}
function assertLegacyFrozenWrite(result: { status: number | null; stdout: string; stderr: string; json: JsonRecord | null }, command: string): void {
assertCondition(result.status !== 0 && result.json?.ok === false, `${command} should be frozen`, result.json ?? { stdout: result.stdout, stderr: result.stderr });
const data = nestedRecord(result.json?.data, []);
assertCondition(data.ok === false, `${command} frozen payload should be ok=false`, data);
assertCondition(data.frozen === true, `${command} frozen payload should expose frozen=true`, data);
assertCondition(data.mutation === false, `${command} frozen payload should be non-mutating`, data);
assertCondition(data.degradedReason === "legacy-code-queue-frozen", `${command} should use the legacy frozen reason`, data);
assertCondition(data.command === command, `${command} frozen payload should identify the command`, data);
const replacement = nestedRecord(data, ["replacement"]);
assertCondition(String(replacement.sessionsSteer || "").includes("agentrun v01 sessions steer"), `${command} should point to AgentRun sessions steer`, replacement);
const legacy = nestedRecord(data, ["legacy"]);
assertCondition(legacy.noDoubleWrite === true, `${command} should document no double-write`, legacy);
}
function assertDryRunPrompt(response: JsonRecord, expectedText: string): void {
assertCondition(response.ok === true, "CLI dry-run should succeed", response);
const data = nestedRecord(response.data, []);
@@ -82,31 +96,29 @@ function assertReason(result: unknown, reason: string, status: number | null): v
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");
assertLegacyFrozenWrite(positional, "codex steer");
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");
assertLegacyFrozenWrite(stdin, "codex steer");
assertCondition(!stdin.stdout.includes("stdin steer prompt"), "frozen steer must not echo stdin prompt", { stdout: stdin.stdout });
const promptFile = join(tmpdir(), `unidesk-code-queue-steer-${process.pid}.txt`);
writeFileSync(promptFile, "file steer prompt", "utf8");
try {
const fromFile = runCli(["codex", "steer", "codex_test_task", "--prompt-file", promptFile, "--dry-run"]);
assertDryRunPrompt(fromFile.json ?? {}, "file steer prompt");
assertLegacyFrozenWrite(fromFile, "codex steer");
assertCondition(!fromFile.stdout.includes("file steer prompt"), "frozen steer must not echo file prompt", { stdout: fromFile.stdout });
} finally {
unlinkSync(promptFile);
}
const duplicateSource = runCli(["codex", "steer", "codex_test_task", "positional", "--prompt-stdin", "--dry-run"], "stdin\n");
assertCondition(duplicateSource.status !== 0, "duplicate prompt source should fail", duplicateSource.json ?? { stdout: duplicateSource.stdout });
const duplicateMessage = String(nestedRecord(duplicateSource.json, ["error"]).message || "");
assertCondition(duplicateMessage.includes("exactly one prompt source"), "duplicate prompt source error should be explicit", { duplicateMessage });
assertLegacyFrozenWrite(duplicateSource, "codex steer");
const unknownOption = runCli(["codex", "steer", "codex_test_task", "--queue", "default", "prompt", "--dry-run"]);
assertCondition(unknownOption.status !== 0, "unknown steer option should fail", unknownOption.json ?? { stdout: unknownOption.stdout });
const unknownMessage = String(nestedRecord(unknownOption.json, ["error"]).message || "");
assertCondition(unknownMessage.includes("unsupported codex steer option: --queue"), "unknown option error should name option", { unknownMessage });
assertLegacyFrozenWrite(unknownOption, "codex steer");
const help = runCli(["codex", "help"]);
assertCondition(help.status === 0 && help.json?.ok === true, "codex help should succeed", help.json ?? { stdout: help.stdout });
@@ -118,7 +130,10 @@ export function runCodeQueueCliSteerContract(): JsonRecord {
assertCondition(!("error" in (advertisedConfirm.json ?? {})), "advertised steer-confirm command must not fall through to top-level error", advertisedConfirm.json ?? {});
const advertisedDeliveryStatus = String(nestedRecord(advertisedConfirm.json?.data, ["delivery"]).status || "");
assertCondition(["confirmed", "pending", "unknown", "not-supported"].includes(advertisedDeliveryStatus), "advertised steer-confirm command should return structured delivery status", { advertisedDeliveryStatus, json: advertisedConfirm.json });
assertCondition(!String(JSON.stringify(advertisedConfirm.json)).includes("\"message\":\"not found\""), "advertised steer-confirm command must not return top-level not found", advertisedConfirm.json ?? {});
if (advertisedDeliveryStatus === "not-supported") {
const diagnostics = nestedRecord(advertisedConfirm.json?.data, ["diagnostics"]);
assertCondition(diagnostics.reason === "steer-confirmation-endpoint-not-supported", "unsupported steer-confirm should use structured diagnostics", diagnostics);
}
let dryRunFetchCount = 0;
const dryRunDirect = codexSteerTaskForTest("direct_task", ["do not send", "--dry-run"], () => {
@@ -501,11 +516,11 @@ export function runCodeQueueCliSteerContract(): JsonRecord {
return {
ok: true,
checks: [
"steer positional dry-run",
"steer stdin dry-run",
"steer prompt-file dry-run",
"duplicate prompt source failure",
"unsupported option failure",
"legacy steer positional dry-run is frozen",
"legacy steer stdin dry-run is frozen",
"legacy steer prompt-file dry-run is frozen",
"legacy steer duplicate prompt source is frozen",
"legacy steer unsupported option is frozen",
"codex help lists steer",
"advertised steer-confirm CLI command returns structured status",
"outer command redacts positional steer prompt",
@@ -45,6 +45,21 @@ function stringArray(value: unknown): string[] {
return Array.isArray(value) ? value.map((item) => String(item)) : [];
}
function assertLegacyFrozenWrite(result: { status: number | null; stdout: string; stderr: string; json: JsonRecord | null }, command: string): void {
assertCondition(result.status !== 0 && result.json?.ok === false, `${command} should be frozen`, result.json ?? { stdout: result.stdout, stderr: result.stderr });
const data = nestedRecord(result.json?.data, []);
assertCondition(data.ok === false, `${command} frozen payload should be ok=false`, data);
assertCondition(data.frozen === true, `${command} frozen payload should expose frozen=true`, data);
assertCondition(data.mutation === false, `${command} frozen payload should be non-mutating`, data);
assertCondition(data.degradedReason === "legacy-code-queue-frozen", `${command} should use the legacy frozen reason`, data);
assertCondition(data.command === command, `${command} frozen payload should identify the command`, data);
const replacement = nestedRecord(data, ["replacement"]);
assertCondition(String(replacement.queueSubmit || "").includes("agentrun v01 queue submit"), `${command} should point to AgentRun queue submit`, replacement);
assertCondition(String(replacement.sessionsSteer || "").includes("agentrun v01 sessions steer"), `${command} should point to AgentRun sessions steer`, replacement);
const legacy = nestedRecord(data, ["legacy"]);
assertCondition(legacy.noDoubleWrite === true, `${command} should document no double-write`, legacy);
}
function assertDryRunPrompt(response: JsonRecord, expectedText: string): void {
assertCondition(response.ok === true, "submit dry-run should succeed", response);
const data = nestedRecord(response.data, []);
@@ -66,8 +81,8 @@ export function runCodeQueueCliSubmitPromptContract(): JsonRecord {
].join("\n");
const stdin = runCli(["codex", "submit", "--prompt-stdin", "--queue", "prompt-contract", "--dry-run"], multilinePrompt);
assertDryRunPrompt(stdin.json ?? {}, multilinePrompt);
assertCondition(String(stdin.json?.command || "") === "codex submit --prompt-stdin --queue prompt-contract --dry-run", "stdin command should list flags without echoing prompt", stdin.json ?? {});
assertLegacyFrozenWrite(stdin, "codex submit");
assertCondition(!stdin.stdout.includes(multilinePrompt), "frozen submit must not echo stdin prompt", { stdout: stdin.stdout });
const tmp = mkdtempSync(join(tmpdir(), "unidesk-code-queue-submit-"));
const promptFile = join(tmp, "prompt.md");
@@ -75,22 +90,19 @@ export function runCodeQueueCliSubmitPromptContract(): JsonRecord {
writeFileSync(promptFile, filePrompt, "utf8");
try {
const fromFile = runCli(["codex", "submit", "--prompt-file", promptFile, "--queue", "prompt-contract", "--dry-run"]);
assertDryRunPrompt(fromFile.json ?? {}, filePrompt);
assertCondition(String(fromFile.json?.command || "").includes(`--prompt-file ${promptFile}`), "prompt-file command should retain file path for review", fromFile.json ?? {});
assertCondition(!String(fromFile.json?.command || "").includes("file prompt tail"), "prompt-file command must not echo file prompt text", fromFile.json ?? {});
assertLegacyFrozenWrite(fromFile, "codex submit");
assertCondition(!fromFile.stdout.includes("file prompt tail"), "frozen submit must not echo file prompt text", { stdout: fromFile.stdout });
} finally {
rmSync(tmp, { recursive: true, force: true });
}
const positional = runCli(["codex", "submit", "short smoke prompt", "--dry-run"]);
assertDryRunPrompt(positional.json ?? {}, "short smoke prompt");
assertLegacyFrozenWrite(positional, "codex submit");
assertCondition(String(positional.json?.command || "").includes("<prompt:redacted>"), "outer command should redact positional submit prompt", positional.json ?? {});
assertCondition(!String(positional.json?.command || "").includes("short smoke prompt"), "outer command must not echo positional submit prompt", positional.json ?? {});
const duplicateSource = runCli(["codex", "submit", "positional", "--prompt-stdin", "--dry-run"], "stdin\n");
assertCondition(duplicateSource.status !== 0, "duplicate prompt source should fail", duplicateSource.json ?? { stdout: duplicateSource.stdout });
const duplicateMessage = String(nestedRecord(duplicateSource.json, ["error"]).message || "");
assertCondition(duplicateMessage.includes("exactly one prompt source"), "duplicate prompt source error should be explicit", { duplicateMessage });
assertLegacyFrozenWrite(duplicateSource, "codex submit");
const longSubmittedPrompt = `${multilinePrompt}${"submitted prompt body must not be echoed\n".repeat(80)}`;
const submitSuccess = compactSubmitSuccessResponseForTest({
@@ -128,23 +140,23 @@ export function runCodeQueueCliSubmitPromptContract(): JsonRecord {
const promptInput = nestedRecord(data, ["promptInput"]);
const recommended = stringArray(promptInput.recommended);
const examples = nestedRecord(data, ["examples"]);
assertCondition(usage.some((line) => line.includes("--prompt-stdin")), "help usage should include --prompt-stdin", { usage });
assertCondition(usage.some((line) => line.includes("--prompt-file")), "help usage should include --prompt-file", { usage });
assertCondition(usage.some((line) => line.includes("cat <<'PROMPT'")), "help usage should include a quoted heredoc example", { usage });
const submitSummary = nestedRecord(data, ["submitSummary"]);
assertCondition(usage.some((line) => line.includes("codex submit # frozen legacy write entry")), "help usage should document frozen legacy submit", { usage });
assertCondition(recommended.includes("--prompt-stdin") && recommended.includes("--prompt-file"), "help should recommend stdin and file prompt sources", promptInput);
assertCondition(String(promptInput.sourceRule || "").includes("Exactly one prompt source"), "help should document exact prompt source rule", promptInput);
assertCondition(stringArray(examples.stdin).some((line) => line.includes("--prompt-stdin")), "help examples should include stdin command", examples);
assertCondition(String(examples.file || "").includes("--prompt-file"), "help examples should include file command", examples);
assertCondition(String(submitSummary.default || "").includes("legacy-code-queue-frozen"), "help submit summary should document frozen reason", submitSummary);
assertCondition(String(submitSummary.replacement || "").includes("agentrun v01 queue submit"), "help should point new submissions at AgentRun", submitSummary);
assertCondition(String(examples.agentRunSubmit || "").includes("agentrun v01 queue submit"), "help examples should include AgentRun submit", examples);
return {
ok: true,
checks: [
"submit --prompt-stdin preserves multiline quotes and newlines",
"submit --prompt-file preserves reviewed file contents",
"legacy submit --prompt-stdin is frozen and does not echo prompt text",
"legacy submit --prompt-file is frozen and does not echo prompt text",
"submit positional prompt is redacted from the outer command envelope",
"duplicate submit prompt source fails explicitly",
"legacy submit duplicate prompt sources still fail at the frozen boundary",
"submit success confirms write without echoing prompt",
"codex submit help documents stdin/file recommendations and copyable examples",
"codex submit help documents frozen legacy submit and AgentRun replacement",
],
};
}
@@ -27,6 +27,17 @@ function stringArray(value: unknown): string[] {
return Array.isArray(value) ? value.map((item) => String(item)) : [];
}
function assertLegacyFrozenWrite(result: { status: number | null; stdout: string; stderr: string; json: JsonRecord | null }, command: string): void {
assertCondition(result.status !== 0 && result.json?.ok === false, `${command} should be frozen`, result.json ?? { stdout: result.stdout, stderr: result.stderr });
const data = nestedRecord(result.json?.data, []);
assertCondition(data.frozen === true, `${command} frozen payload should expose frozen=true`, data);
assertCondition(data.mutation === false, `${command} frozen payload should be non-mutating`, data);
assertCondition(data.degradedReason === "legacy-code-queue-frozen", `${command} should use the legacy frozen reason`, data);
const replacement = nestedRecord(data, ["replacement"]);
assertCondition(String(replacement.queueSubmit || "").includes("agentrun v01 queue submit"), `${command} should point to AgentRun queue submit`, replacement);
assertCondition(String(replacement.sessionsSteer || "").includes("agentrun v01 sessions steer"), `${command} should point to AgentRun sessions steer`, replacement);
}
function runCli(args: string[], stdin?: string): { status: number | null; stdout: string; stderr: string; json: JsonRecord | null } {
const result = spawnSync("bun", ["scripts/cli.ts", ...args], {
cwd: process.cwd(),
@@ -148,15 +159,12 @@ export function runCodeQueuePromptLintContract(): JsonRecord {
}
const submitDryRun = runCli(["codex", "submit", "--prompt-stdin", "--dry-run"], unclassifiedM3SmokePrompt);
assertCondition(submitDryRun.status === 0 && submitDryRun.json?.ok === true, "submit dry-run should still succeed for commander review", submitDryRun.json ?? { stdout: submitDryRun.stdout });
const submitPromptLint = nestedRecord(submitDryRun.json?.data, ["promptLint"]);
assertCondition(submitPromptLint.dispatchDisposition === "needs-authorization", "submit dry-run should embed prompt lint authorization blocker", submitPromptLint);
assertCondition(submitPromptLint.requiredClass === "live-mutating", "submit dry-run lint should require live-mutating", submitPromptLint);
assertLegacyFrozenWrite(submitDryRun, "codex submit");
assertCondition(!submitDryRun.stdout.includes("res_boxsimu_1"), "frozen submit must not echo prompt text", { stdout: submitDryRun.stdout });
const steerDryRun = runCli(["codex", "steer", "codex_test_task", "--prompt-stdin", "--dry-run"], unclassifiedM3SmokePrompt);
assertCondition(steerDryRun.status === 0 && steerDryRun.json?.ok === true, "steer dry-run should succeed for commander review", steerDryRun.json ?? { stdout: steerDryRun.stdout });
const steerPromptLint = nestedRecord(steerDryRun.json?.data, ["promptLint"]);
assertCondition(steerPromptLint.dispatchDisposition === "needs-authorization", "steer dry-run should embed prompt lint authorization blocker", steerPromptLint);
assertLegacyFrozenWrite(steerDryRun, "codex steer");
assertCondition(!steerDryRun.stdout.includes("res_boxsimu_1"), "frozen steer must not echo prompt text", { stdout: steerDryRun.stdout });
const help = runCli(["codex", "help"]);
assertCondition(help.status === 0 && help.json?.ok === true, "codex help should succeed", help.json ?? { stdout: help.stdout });
@@ -174,8 +182,8 @@ export function runCodeQueuePromptLintContract(): JsonRecord {
"unclassified HWLAB M3 smoke defaults read-only but requires live-mutating authorization",
"prompt-lint evidence redacts secret-looking values",
"prompt-lint CLI is dry-run, non-mutating, and does not echo full prompt text",
"submit --dry-run embeds prompt live-authorization lint",
"steer --dry-run embeds prompt live-authorization lint",
"legacy submit --dry-run is frozen and points to AgentRun",
"legacy steer --dry-run is frozen and points to AgentRun",
"codex help documents prompt-lint and authorization classes",
],
};
+21 -6
View File
@@ -82,6 +82,20 @@ function deterministicResumeId(taskId: string, prompt: string): string {
return `resume_${Bun.SHA256.hash(`unidesk-code-queue-resume:v1\0${taskId}\0${prompt}`, "hex").slice(0, 24)}`;
}
function assertLegacyFrozenWrite(result: { status: number | null; stdout: string; stderr: string; json: JsonRecord | null }, command: string): void {
assertCondition(result.status !== 0 && result.json?.ok === false, `${command} should be frozen`, result.json ?? { stdout: result.stdout, stderr: result.stderr });
const data = nestedRecord(result.json?.data, []);
assertCondition(data.ok === false, `${command} frozen payload should be ok=false`, data);
assertCondition(data.frozen === true, `${command} frozen payload should expose frozen=true`, data);
assertCondition(data.mutation === false, `${command} frozen payload should be non-mutating`, data);
assertCondition(data.degradedReason === "legacy-code-queue-frozen", `${command} should use the legacy frozen reason`, data);
assertCondition(data.command === command, `${command} frozen payload should identify the command`, data);
const replacement = nestedRecord(data, ["replacement"]);
assertCondition(String(replacement.sessionsSteer || "").includes("agentrun v01 sessions steer"), `${command} should point to AgentRun sessions steer`, replacement);
const legacy = nestedRecord(data, ["legacy"]);
assertCondition(legacy.noDoubleWrite === true, `${command} should document no double-write`, legacy);
}
function assertDryRunPrompt(response: JsonRecord, expectedText: string): void {
assertCondition(response.ok === true, "CLI dry-run should succeed", response);
const data = nestedRecord(response.data, []);
@@ -100,25 +114,26 @@ function assertDryRunPrompt(response: JsonRecord, expectedText: string): void {
export function runCodeQueueResumeContract(): JsonRecord {
const positional = runCli(["codex", "resume", "codex_test_task", "fix the PR conflict", "--dry-run"]);
assertDryRunPrompt(positional.json ?? {}, "fix the PR conflict");
assertLegacyFrozenWrite(positional, "codex resume");
assertCondition(String(positional.json?.command || "").includes("<prompt:redacted>"), "outer command should redact positional resume prompt", positional.json ?? {});
assertCondition(!String(positional.json?.command || "").includes("fix the PR conflict"), "outer command must not echo positional resume prompt", positional.json ?? {});
const stdin = runCli(["codex", "resume", "codex_test_task", "--prompt-stdin", "--dry-run"], "stdin resume prompt\n");
assertDryRunPrompt(stdin.json ?? {}, "stdin resume prompt\n");
assertLegacyFrozenWrite(stdin, "codex resume");
assertCondition(!stdin.stdout.includes("stdin resume prompt"), "frozen resume must not echo stdin prompt", { stdout: stdin.stdout });
const promptFile = join(tmpdir(), `unidesk-code-queue-resume-${process.pid}.txt`);
writeFileSync(promptFile, "file resume prompt", "utf8");
try {
const fromFile = runCli(["codex", "resume", "codex_test_task", "--prompt-file", promptFile, "--dry-run"]);
assertDryRunPrompt(fromFile.json ?? {}, "file resume prompt");
assertLegacyFrozenWrite(fromFile, "codex resume");
assertCondition(!fromFile.stdout.includes("file resume prompt"), "frozen resume must not echo file prompt", { stdout: fromFile.stdout });
} finally {
unlinkSync(promptFile);
}
const duplicateSource = runCli(["codex", "resume", "codex_test_task", "positional", "--prompt-stdin", "--dry-run"], "stdin\n");
assertCondition(duplicateSource.status !== 0, "duplicate prompt source should fail", duplicateSource.json ?? { stdout: duplicateSource.stdout });
assertCondition(String(nestedRecord(duplicateSource.json, ["error"]).message || "").includes("exactly one prompt source"), "duplicate prompt source error should be explicit", duplicateSource.json ?? {});
assertLegacyFrozenWrite(duplicateSource, "codex resume");
const help = runCli(["codex", "help"]);
const usage = Array.isArray(nestedRecord(help.json?.data, []).usage) ? nestedRecord(help.json?.data, []).usage as unknown[] : [];
@@ -291,7 +306,7 @@ export function runCodeQueueResumeContract(): JsonRecord {
return {
ok: true,
checks: [
"resume positional/stdin/file dry-runs",
"legacy resume positional/stdin/file dry-runs are frozen",
"bounded disclosure and outer command redaction",
"non-dry-run sends resumeId and omits prompt from output",
"terminal resume accepted with thread reuse metadata",
@@ -54,6 +54,19 @@ function assertSecretFree(output: string): void {
}
}
function assertLegacyFrozenWrite(result: { status: number | null; stdout: string; stderr: string; json: JsonRecord | null }, command: string): void {
assertCondition(result.status !== 0 && result.json?.ok === false, `${command} should be frozen`, result.json ?? { stdout: result.stdout, stderr: result.stderr });
const data = nestedRecord(result.json?.data, []);
assertCondition(data.ok === false, `${command} frozen payload should be ok=false`, data);
assertCondition(data.frozen === true, `${command} frozen payload should expose frozen=true`, data);
assertCondition(data.mutation === false, `${command} frozen payload should be non-mutating`, data);
assertCondition(data.degradedReason === "legacy-code-queue-frozen", `${command} should use the legacy frozen reason`, data);
const replacement = nestedRecord(data, ["replacement"]);
assertCondition(String(replacement.queueSubmit || "").includes("agentrun v01 queue submit"), `${command} should point to AgentRun queue submit`, replacement);
const legacy = nestedRecord(data, ["legacy"]);
assertCondition(legacy.noDoubleWrite === true, `${command} should document no double-write`, legacy);
}
export function runCodeQueueSubmitExecutionModeContract(): JsonRecord {
assertCondition(normalizeRequestedCodeExecutionMode("full-access") === "full-access", "shared parser should preserve short requested mode ids");
assertCondition(normalizeCodeExecutionMode("full-access") === "default", "shared execution-mode normalizer should keep full-access on effective default");
@@ -61,33 +74,12 @@ export function runCodeQueueSubmitExecutionModeContract(): JsonRecord {
assertCondition(requestedCodeExecutionModeIsRecognized("default") === true, "shared recognition helper should accept default mode");
const defaultMode = runCli(["codex", "submit", "execution mode default smoke", "--dry-run"]);
assertCondition(defaultMode.status === 0 && defaultMode.json?.ok === true, "default submit dry-run should succeed", defaultMode.json ?? { stdout: defaultMode.stdout, stderr: defaultMode.stderr });
assertLegacyFrozenWrite(defaultMode, "codex submit");
assertSecretFree(defaultMode.stdout);
const defaultData = nestedRecord(defaultMode.json?.data, []);
const defaultRequest = nestedRecord(defaultData, ["request"]);
const defaultExecutionMode = nestedRecord(defaultData, ["executionMode"]);
const defaultPermissions = nestedRecord(defaultData, ["runnerPermissions"]);
assertCondition(defaultRequest.executionMode === undefined, "default payload should omit executionMode so service default is authoritative", defaultRequest);
assertCondition(defaultExecutionMode.requested === null, "default mode should show no explicit requested mode", defaultExecutionMode);
assertCondition(defaultExecutionMode.effective === "default", "default mode should expose effective default", defaultExecutionMode);
assertCondition(defaultExecutionMode.normalized === false, "default mode should not be reported as normalized", defaultExecutionMode);
assertCondition(defaultExecutionMode.recognized === true, "default mode should be recognized", defaultExecutionMode);
assertCondition(defaultPermissions.observed === false && defaultPermissions.perTaskOverrideSupported === false, "dry-run should mark runner permissions unobserved and non per-task", defaultPermissions);
const fullAccess = runCli(["codex", "submit", "execution mode full access smoke", "--execution-mode", "full-access", "--dry-run"]);
assertCondition(fullAccess.status === 0 && fullAccess.json?.ok === true, "full-access submit dry-run should succeed", fullAccess.json ?? { stdout: fullAccess.stdout, stderr: fullAccess.stderr });
assertLegacyFrozenWrite(fullAccess, "codex submit");
assertSecretFree(fullAccess.stdout);
const fullData = nestedRecord(fullAccess.json?.data, []);
const fullRequest = nestedRecord(fullData, ["request"]);
const fullExecutionMode = nestedRecord(fullData, ["executionMode"]);
assertCondition(fullRequest.executionMode === "full-access", "payload should preserve the requested executionMode value for backend visibility", fullRequest);
assertCondition(fullExecutionMode.requested === "full-access", "full-access request should be visible", fullExecutionMode);
assertCondition(fullExecutionMode.effective === "default", "full-access should normalize to the effective default runtime mode", fullExecutionMode);
assertCondition(fullExecutionMode.recognized === false, "full-access should not be treated as a recognized Code Queue execution mode", fullExecutionMode);
assertCondition(fullExecutionMode.normalized === true, "full-access should explicitly show normalization", fullExecutionMode);
assertCondition(fullExecutionMode.requestedLooksLikeSandbox === true, "full-access should be classified as a sandbox-like request", fullExecutionMode);
assertCondition(String(fullExecutionMode.permissionBoundary || "").includes("runnerPermissions.sandbox"), "permission boundary should point at runnerPermissions.sandbox", fullExecutionMode);
assertCondition(String(fullExecutionMode.warning || "").includes("not applied"), "full-access warning should say it is not a per-task sandbox override", fullExecutionMode);
const promptText = "submitted full-access prompt body must stay omitted";
const submitted = compactSubmitSuccessResponseForTest({
@@ -138,11 +130,11 @@ export function runCodeQueueSubmitExecutionModeContract(): JsonRecord {
return {
ok: true,
checks: [
"default codex submit dry-run omits executionMode, reports effective default, and marks runner permissions unobserved",
"--execution-mode full-access preserves requested mode, reports effective default, and warns that sandbox permissions are service-level",
"legacy codex submit dry-run is frozen and points to AgentRun",
"legacy --execution-mode full-access submit dry-run is frozen without printing credentials",
"real submit summary fixture exposes requested/effective mode plus observed runnerPermissions without prompt echo",
"shared execution-mode helpers preserve requested full-access while normalizing effective runtime to default",
"execution-mode dry-run output does not print credential assignments",
"execution-mode frozen output does not print credential assignments",
],
};
}
@@ -43,6 +43,16 @@ function dataOf(envelope: JsonRecord): JsonRecord {
return asRecord(envelope.data, "data");
}
function assertLegacyFrozenWrite(result: { status: number | null; stdout: string; stderr: string; envelope: JsonRecord }, command: string): void {
assertCondition(result.status !== 0 && result.envelope.ok === false, `${command} should be frozen`, result.envelope);
const data = dataOf(result.envelope);
assertCondition(data.frozen === true, `${command} frozen payload should expose frozen=true`, data);
assertCondition(data.mutation === false, `${command} frozen payload should be non-mutating`, data);
assertCondition(data.degradedReason === "legacy-code-queue-frozen", `${command} should use the legacy frozen reason`, data);
const replacement = asRecord(data.replacement, "replacement");
assertCondition(String(replacement.queueSubmit || "").includes("agentrun v01 queue submit"), `${command} should point to AgentRun queue submit`, replacement);
}
const completePrompt = `
UniDesk#20 #118 / commander prompt boundary lint
@@ -116,9 +126,8 @@ try {
}
const submitDryRun = runCli(["codex", "submit", "--prompt-stdin", "--queue", "prompt-lint-contract", "--dry-run"], incompletePromptWithSecret);
assertCondition(submitDryRun.status === 0 && submitDryRun.envelope.ok === true, "codex submit --dry-run should not be gated by commander prompt-lint", submitDryRun.envelope);
const submitData = dataOf(submitDryRun.envelope);
assertCondition(asRecord(submitData.request, "submit request").prompt !== undefined, "submit dry-run should keep its existing prompt review behavior", submitData);
assertLegacyFrozenWrite(submitDryRun, "codex submit");
assertCondition(!submitDryRun.stdout.includes("ghp_prompt_lint_contract_secret"), "frozen submit must not echo prompt secret", submitDryRun.stdout);
const helpRun = runCli(["commander", "--help"]);
assertCondition(helpRun.status === 0 && helpRun.envelope.ok === true, "commander help should succeed", helpRun.envelope);
@@ -143,7 +152,7 @@ process.stdout.write(`${JSON.stringify({
"missing PR/artifact/DEV rollout/ROLLOUT_OK/PROD-secret-DB-rollback clauses are reported as high risk",
"prompt-lint supports --prompt-file and --stdin",
"prompt-lint output does not echo full prompt or secret-like prompt text",
"commander prompt-lint remains advisory and does not gate codex submit --dry-run",
"commander prompt-lint remains advisory while legacy codex submit stays frozen",
"commander help and host commander reference document the advisory lint entry",
],
}, null, 2)}\n`);
@@ -14,7 +14,7 @@ const parsed = Bun.YAML.parse(readFileSync(configPath, "utf8")) as {
minOwnerConcurrency?: number;
defaultTempUnschedulable?: {
enabled?: boolean;
rules?: Array<{ statusCode?: number; keywords?: string[]; durationMinutes?: number }>;
rules?: Array<{ statusCode?: number; keywords?: string[]; durationMinutes?: number; description?: string }>;
};
};
profiles?: { entries?: Array<{ profile?: string; accountName?: string; capacity?: number; loadFactor?: number; openaiResponsesWebSocketsV2Mode?: string | null }> };
@@ -28,6 +28,8 @@ const defaultPriority = parsed.pool?.defaultAccountPriority ?? 0;
const defaultCapacity = parsed.pool?.defaultAccountCapacity ?? 0;
const defaultLoadFactor = parsed.pool?.defaultAccountLoadFactor ?? 0;
const desiredCapacity = entries.reduce((total, entry) => total + (entry.capacity ?? defaultCapacity), 0);
const explicitMinOwnerConcurrency = parsed.pool?.minOwnerConcurrency;
const resolvedMinOwnerConcurrency = explicitMinOwnerConcurrency ?? desiredCapacity;
const allowedWebSocketModes = new Set(["off", "ctx_pool", "passthrough"]);
const wsEnabledEntries = entries.filter((entry) => entry.openaiResponsesWebSocketsV2Mode && entry.openaiResponsesWebSocketsV2Mode !== "off");
const localWsEnabled = parsed.localCodex?.supportsWebSockets === true || parsed.localCodex?.responsesWebSocketsV2 === true;
@@ -56,7 +58,21 @@ if (localWsEnabled) {
} else {
assertCondition(wsEnabledEntries.length === 0, "local Codex WebSocket transport disabled means all account WSv2 capability declarations must be off or omitted", { localCodex: parsed.localCodex, wsEnabledEntries });
}
assertCondition((parsed.pool?.minOwnerConcurrency ?? 0) >= desiredCapacity, "pool owner concurrency must not bottleneck the declared account capacity set", { minOwnerConcurrency: parsed.pool?.minOwnerConcurrency, desiredCapacity });
assertCondition(
explicitMinOwnerConcurrency === undefined || (Number.isInteger(explicitMinOwnerConcurrency) && explicitMinOwnerConcurrency > 0),
"explicit pool owner concurrency override must be a positive integer when declared",
{ minOwnerConcurrency: explicitMinOwnerConcurrency },
);
assertCondition(
resolvedMinOwnerConcurrency >= desiredCapacity,
"pool owner concurrency must auto-resolve or be configured to cover the declared account capacity set",
{
minOwnerConcurrency: explicitMinOwnerConcurrency,
minOwnerConcurrencySource: explicitMinOwnerConcurrency === undefined ? "auto-capacity-sum" : "yaml",
resolvedMinOwnerConcurrency,
desiredCapacity,
},
);
if (parsed.pool?.defaultTempUnschedulable?.enabled === true) {
assertCondition(rules.length > 0, "enabled temporary unschedulable policy must declare rules", parsed.pool?.defaultTempUnschedulable);
assertCondition(rules.every((rule) => Number.isInteger(rule.statusCode) && (rule.statusCode ?? 0) >= 100 && (rule.statusCode ?? 0) <= 599), "temporary unschedulable rules must declare valid HTTP status codes", rules);
@@ -116,7 +132,7 @@ console.log(JSON.stringify({
ok: true,
checks: [
"routing config is schema-valid without profile-specific test gates",
"pool owner concurrency covers the YAML account capacity set",
"pool owner concurrency auto-resolves or covers the YAML account capacity set",
"profile load factor overrides are YAML-controlled positive integers",
"public Caddy response-header timeout is long enough for Codex compact",
"optional WebSocket mode overrides use supported values",
+51 -10
View File
@@ -19,6 +19,7 @@ const syntaxFiles = [
"scripts/platform-infra-sub2api-codex-routing-contract-test.ts",
"scripts/platform-infra-sub2api-codex-temp-unsched-contract-test.ts",
"scripts/platform-infra-sub2api-http-upstream-contract-test.ts",
"scripts/check-gh-contract-scope-contract-test.ts",
"scripts/src/playwright-cli.ts",
"scripts/src/check.ts",
"scripts/src/artifact-registry.ts",
@@ -86,6 +87,7 @@ export interface CheckOptions {
compose: boolean;
logs: boolean;
recoveryGuardrails: boolean;
ghContracts: boolean;
rust: boolean;
}
@@ -97,6 +99,7 @@ const defaultCheckOptions: CheckOptions = {
compose: false,
logs: false,
recoveryGuardrails: false,
ghContracts: false,
rust: false,
};
@@ -105,15 +108,16 @@ export function checkHelp(): Record<string, unknown> {
ok: true,
command: "check",
usage: [
"bun scripts/cli.ts check [--syntax-only|--full|--files|--scripts-typecheck|--components|--compose|--logs|--recovery-guardrails|--rust]",
"bun scripts/cli.ts check [--syntax-only|--full|--files|--scripts-typecheck|--gh-contracts|--components|--compose|--logs|--recovery-guardrails|--rust]",
"bun scripts/cli.ts check recovery-guardrails",
],
defaultMode: "syntax/config only; Rust is never compiled on the master server by default",
options: [
{ name: "--syntax-only|--basic", description: "Run only config validation, Bun version and TypeScript syntax transpile." },
{ name: "--full", description: "Enable all local checks except Rust compilation." },
{ name: "--full", description: "Enable all non-Rust checks, including explicit GitHub CLI contracts." },
{ name: "--files", description: "Verify required entrypoint files, including backend-core Cargo files." },
{ name: "--scripts-typecheck", description: "Run scripts TypeScript typecheck." },
{ name: "--gh-contracts", description: "Run slower GitHub CLI contract tests; intentionally separate from generic scripts typecheck." },
{ name: "--components", description: "Run component TypeScript typecheck." },
{ name: "--compose", description: "Render Docker Compose config." },
{ name: "--logs", description: "Check unified log rotation policy." },
@@ -143,10 +147,13 @@ export function parseCheckOptions(args: string[]): CheckOptions {
options.compose = true;
options.logs = true;
options.recoveryGuardrails = true;
options.ghContracts = true;
} else if (arg === "--files") {
options.files = true;
} else if (arg === "--scripts-typecheck") {
options.scriptsTypecheck = true;
} else if (arg === "--gh-contracts") {
options.ghContracts = true;
} else if (arg === "--components") {
options.components = true;
} else if (arg === "--compose") {
@@ -171,13 +178,29 @@ function fileItem(path: string): CheckItem {
return { name: `file:${path}`, ok: existsSync(absolute), detail: absolute };
}
function emitCheckProgress(detail: Record<string, unknown>): void {
console.error(JSON.stringify({ event: "unidesk.check.progress", ...detail }));
}
function commandItem(name: string, command: string[], timeoutMs = 30_000, env: NodeJS.ProcessEnv = process.env): CheckItem {
const startedAt = Date.now();
emitCheckProgress({ phase: "started", name, command, timeoutMs });
const result = runCommand(command, repoRoot, { timeoutMs, env });
const durationMs = Date.now() - startedAt;
emitCheckProgress({
phase: result.exitCode === 0 ? "succeeded" : "failed",
name,
durationMs,
exitCode: result.exitCode,
signal: result.signal,
timedOut: result.timedOut,
});
return {
name,
ok: result.exitCode === 0,
detail: {
command,
durationMs,
exitCode: result.exitCode,
signal: result.signal,
timedOut: result.timedOut,
@@ -312,7 +335,7 @@ export function runRecoveryGuardrailsCheck(config: UniDeskConfig): ReturnType<ty
return compactD601RecoveryGuardrails(runD601RecoveryGuardrails(config));
}
export function runChecks(config: UniDeskConfig, options: CheckOptions = defaultCheckOptions): { ok: boolean; mode: string; options: CheckOptions; items: CheckItem[] } {
export function runChecks(config: UniDeskConfig, options: CheckOptions = defaultCheckOptions): { ok: boolean; mode: string; options: CheckOptions; summary: { total: number; passed: number; failed: number; failedItems: string[] }; items: CheckItem[] } {
const items: CheckItem[] = [
{ name: "config:validated", ok: true, detail: { project: config.project.name, runtime: config.runtime } },
commandItem("bun:version", ["bun", "--version"]),
@@ -382,6 +405,7 @@ export function runChecks(config: UniDeskConfig, options: CheckOptions = default
fileItem("scripts/deploy-artifact-matrix-contract-test.ts"),
fileItem("scripts/decision-center-desired-state-contract-test.ts"),
fileItem("scripts/code-queue-prompt-observation-test.ts"),
fileItem("scripts/check-gh-contract-scope-contract-test.ts"),
fileItem("scripts/gh-cli-issue-guard-contract-test.ts"),
fileItem("scripts/gh-cli-pr-files-contract-test.ts"),
fileItem("scripts/gh-cli-pr-contract-test.ts"),
@@ -449,9 +473,7 @@ export function runChecks(config: UniDeskConfig, options: CheckOptions = default
items.push(commandItem("artifact-registry:direct-compose-dry-run-matrix", ["bun", "scripts/artifact-consumer-dry-run-matrix-test.ts"], 30_000));
items.push(commandItem("schedule:cli-contract", ["bun", "scripts/schedule-cli-contract-test.ts"], 30_000));
items.push(commandItem("server:cleanup-plan-contract", ["bun", "scripts/server-cleanup-plan-contract-test.ts"], 30_000));
items.push(commandItem("gh:issue-guard-contract", ["bun", "scripts/gh-cli-issue-guard-contract-test.ts"], 30_000));
items.push(commandItem("gh:pr-files-contract", ["bun", "scripts/gh-cli-pr-files-contract-test.ts"], 30_000));
items.push(commandItem("gh:pr-contract", ["bun", "scripts/gh-cli-pr-contract-test.ts"], 30_000));
items.push(commandItem("check:gh-contract-scope-contract", ["bun", "scripts/check-gh-contract-scope-contract-test.ts"], 30_000));
items.push(commandItem("playwright:cli-wrapper-contract", ["bun", "scripts/playwright-cli-contract-test.ts"], 30_000));
items.push(commandItem("platform-infra:sub2api-codex-local-config-contract", ["bun", "scripts/platform-infra-sub2api-codex-local-config-contract-test.ts"], 30_000));
items.push(commandItem("platform-infra:sub2api-codex-routing-contract", ["bun", "scripts/platform-infra-sub2api-codex-routing-contract-test.ts"], 30_000));
@@ -494,9 +516,7 @@ export function runChecks(config: UniDeskConfig, options: CheckOptions = default
items.push(skippedItem("artifact-registry:direct-compose-dry-run-matrix", "main-server direct artifact consumer dry-run matrix is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("schedule:cli-contract", "Schedule CLI contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("server:cleanup-plan-contract", "Server cleanup dry-run contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("gh:issue-guard-contract", "GitHub issue CLI contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("gh:pr-files-contract", "GitHub PR files/stat contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("gh:pr-contract", "GitHub PR CLI contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("check:gh-contract-scope-contract", "Check option scope contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("playwright:cli-wrapper-contract", "Playwright wrapper/headless/session contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("auth-broker:p0-contract", "Auth Broker P0 skeleton and CLI adapter contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("d601:recovery-guardrails-contract", "D601 recovery guardrails fixture contract is opt-in with script checks", "--scripts-typecheck or --full"));
@@ -507,6 +527,15 @@ export function runChecks(config: UniDeskConfig, options: CheckOptions = default
} else {
items.push(skippedItem("logs:unified-hourly-rotation", "policy scan is opt-in", "--logs or --full"));
}
if (options.ghContracts) {
items.push(commandItem("gh:issue-guard-contract", ["bun", "scripts/gh-cli-issue-guard-contract-test.ts"], 30_000));
items.push(commandItem("gh:pr-files-contract", ["bun", "scripts/gh-cli-pr-files-contract-test.ts"], 30_000));
items.push(commandItem("gh:pr-contract", ["bun", "scripts/gh-cli-pr-contract-test.ts"], 30_000));
} else {
items.push(skippedItem("gh:issue-guard-contract", "GitHub issue CLI contract is opt-in because it can be slower than generic scripts typecheck", "--gh-contracts or --full"));
items.push(skippedItem("gh:pr-files-contract", "GitHub PR files/stat contract is opt-in because it can be slower than generic scripts typecheck", "--gh-contracts or --full"));
items.push(skippedItem("gh:pr-contract", "GitHub PR CLI contract is opt-in because it can be slower than generic scripts typecheck", "--gh-contracts or --full"));
}
if (options.recoveryGuardrails) {
const recovery = runRecoveryGuardrailsCheck(config);
items.push({
@@ -545,5 +574,17 @@ export function runChecks(config: UniDeskConfig, options: CheckOptions = default
} else {
items.push(skippedItem("rust:backend-core", "Rust check/build must run through an approved native k3s CI artifact publication, not on the master server or CD runtime target", "--rust inside native k3s CI with UNIDESK_NATIVE_K3S_RUST_CHECK=1"));
}
return { ok: items.every((item) => item.ok), mode: options.full ? "full" : "basic", options, items };
const failedItems = items.filter((item) => !item.ok).map((item) => item.name);
return {
ok: failedItems.length === 0,
mode: options.full ? "full" : "basic",
options,
summary: {
total: items.length,
passed: items.length - failedItems.length,
failed: failedItems.length,
failedItems,
},
items,
};
}