test: guard code queue artifact dry-run readiness

This commit is contained in:
Codex
2026-05-21 14:31:00 +00:00
parent 9a4624a6b1
commit 7e171dd904
10 changed files with 291 additions and 31 deletions
+82 -6
View File
@@ -158,6 +158,9 @@ const prodArtifactLiveApplyBlockedServiceIds = new Map<string, string>([
["met-nonlinear", "met-nonlinear is blocked for live artifact deploy because config.json points at docker/unidesk/Dockerfile.ml while the compose service is met-nonlinear-ts."],
["k3sctl-adapter", "k3sctl-adapter is an infrastructure control bridge; this executor exposes artifact consumer plan/dry-run only. Real production deployment requires supervisor confirmation outside this task."],
]);
const devArtifactLiveApplyBlockedServiceIds = new Map<string, string>([
["code-queue", "Code Queue DEV live apply is self-bootstrap sensitive: a running Code Queue task may produce dry-run evidence only. A human operator or supervisor must explicitly authorize DEV apply outside Code Queue after reviewing the CI artifact digest and dry-run target list."],
]);
const artifactConsumerDryRunBlockedServiceIds = new Map<string, string>([
["met-nonlinear", "runtime-verification-blocked: CI publishes the ML image Dockerfile contract, but the long-running Compose service is met-nonlinear-ts; CD cannot yet prove the running container image label matches the requested commit."],
]);
@@ -1141,11 +1144,41 @@ function unsupportedPlanTarget(serviceId: string, environment: DeployEnvironment
};
}
function codeQueueSelfBootstrapGuard(environment: DeployEnvironment): Record<string, unknown> | undefined {
if (environment !== "dev" && environment !== "prod") return undefined;
return {
check: "code-queue-self-bootstrap-guard",
selfBootstrapBlocked: true,
requiresSupervisorApproval: true,
actorBoundary: "a running Code Queue task may publish contract evidence and dry-run plans only; it must not deploy Code Queue itself",
devApply: "requires explicit supervisor or human authorization outside the running Code Queue task",
prodApply: "unsupported until a separate supervisor-approved production Code Queue CD design exists",
allowedWithoutApproval: [
"CI artifact publication",
"deploy plan",
"deploy apply --dry-run",
"artifact-registry deploy-service --dry-run",
],
forbiddenActions: [
"self-deploy Code Queue from Code Queue",
"run a non-dry-run DEV apply from Code Queue",
"production namespace mutation",
"production manifest mutation",
"scheduler or runner restart",
"active task interrupt",
"active task cancel",
],
environment,
};
}
function codeQueueCiCdBoundary(serviceId: string, environment: DeployEnvironment): Record<string, unknown> | undefined {
if (serviceId !== "code-queue") return undefined;
return {
serviceId,
selfDeployBlocked: true,
requiresSupervisorApproval: true,
selfBootstrapGuard: codeQueueSelfBootstrapGuard(environment),
ciProducer: {
command: "bun scripts/cli.ts ci publish-user-service --service code-queue --commit <full-sha>",
allowed: true,
@@ -1157,7 +1190,9 @@ function codeQueueCiCdBoundary(serviceId: string, environment: DeployEnvironment
? {
environment: "dev",
dryRunCommand: "bun scripts/cli.ts deploy apply --env dev --service code-queue --commit <full-sha> --dry-run",
liveApplyCommandShape: "bun scripts/cli.ts deploy apply --env dev --service code-queue --commit <full-sha> --run-now",
liveApplyCommandShape: null,
liveApplyAllowed: false,
requiresSupervisorApproval: true,
allowedTarget: "unidesk-dev Code Queue scheduler/read/write/provider-egress-proxy only",
manualAuthorizationPoint: "operator reviews CI artifact summary and dev dry-run plan, then explicitly authorizes DEV apply outside this Code Queue task",
prodMutationAllowed: false,
@@ -1166,6 +1201,8 @@ function codeQueueCiCdBoundary(serviceId: string, environment: DeployEnvironment
environment: "prod",
dryRunCommand: "bun scripts/cli.ts deploy plan --env prod --service code-queue",
liveApplyCommandShape: null,
liveApplyAllowed: false,
requiresSupervisorApproval: true,
allowedTarget: null,
manualAuthorizationPoint: "PROD requires a future supervisor-approved Code Queue CD design; current runner output is plan/unsupported only",
prodMutationAllowed: false,
@@ -3072,6 +3109,10 @@ function environmentDryRunPlan(
: planTarget;
const registryRepository = service.artifact?.repository ?? `unidesk/${service.id}`;
const stableImage = service.consumer?.target.stableImage;
const liveApplyBlockedReason = environment === "prod"
? prodArtifactLiveApplyBlockedServiceIds.get(service.id) ?? null
: devArtifactLiveApplyBlockedServiceIds.get(service.id) ?? null;
const dryRunOnly = unsupported || liveApplyBlockedReason !== null || dryRunBlockedReason !== null;
return {
id: service.id,
repo: service.repo,
@@ -3119,8 +3160,10 @@ function environmentDryRunPlan(
"unidesk.ai/dockerfile": serviceConfig.repository.dockerfile,
},
noRuntimeSourceBuild: unsupported || (service.consumer?.noRuntimeSourceBuild ?? effectivePlanKind !== "d601-dev-target-side-build"),
dryRunOnly: unsupported || (environment === "prod" && prodArtifactLiveApplyBlockedServiceIds.has(service.id)) || dryRunBlockedReason !== null,
blockedReason: unsupportedReason ?? dryRunBlockedReason ?? (environment === "prod" ? prodArtifactLiveApplyBlockedServiceIds.get(service.id) ?? null : null),
dryRunOnly,
blockedReason: unsupportedReason ?? dryRunBlockedReason ?? liveApplyBlockedReason,
requiresSupervisorApproval: service.id === "code-queue" || liveApplyBlockedReason !== null,
selfBootstrapGuard: service.id === "code-queue" ? codeQueueSelfBootstrapGuard(environment) : undefined,
runtimeSecrets,
sourceOfTruth: deployJsonContract ? deployJsonSourceOfTruth(service, environment) : undefined,
driftCheck: deployJsonContract ? {
@@ -3162,11 +3205,12 @@ function environmentDryRunPlan(
reason: dryRunBlockedReason,
dryRunOnly: true,
}
: environment === "prod" && prodArtifactLiveApplyBlockedServiceIds.has(service.id)
: liveApplyBlockedReason !== null
? {
allowed: false,
reason: prodArtifactLiveApplyBlockedServiceIds.get(service.id),
reason: liveApplyBlockedReason,
dryRunOnly: true,
requiresSupervisorApproval: true,
}
: environment === "prod"
? { allowed: prodArtifactConsumerServiceIds.has(service.id) }
@@ -3240,7 +3284,17 @@ function prodArtifactUnsupportedResult(services: DeployManifestService[]): Recor
repo: service.repo,
commitId: service.commitId,
supported: false,
reason: "No standardized prod D601 registry artifact consumer is implemented for this service.",
reason: unsupportedEnvironmentPlanReason(service.id, "prod"),
requiresSupervisorApproval: service.id === "code-queue",
selfBootstrapGuard: service.id === "code-queue" ? codeQueueSelfBootstrapGuard("prod") : undefined,
affectedRuntime: service.id === "code-queue" ? {
dryRunMutation: false,
plannedTarget: null,
productionNamespaceAffected: false,
productionSchedulerRunnerAffected: false,
activeTaskControlAffected: false,
activeTaskInterruptCancelAffected: false,
} : undefined,
})),
policy: "prod deploy must not silently fall back to legacy D601 maintenance-channel source builds",
supportedServices: Array.from(prodArtifactConsumerServiceIds),
@@ -3294,6 +3348,26 @@ function prodArtifactLiveApplyBlockedResult(services: DeployManifestService[]):
};
}
function devArtifactLiveApplyBlockedResult(services: DeployManifestService[]): Record<string, unknown> {
return {
ok: false,
supported: true,
error: "live-dev-apply-blocked",
services: services.map((service) => ({
id: service.id,
repo: service.repo,
commitId: service.commitId,
supported: true,
liveApplyAllowed: false,
requiresSupervisorApproval: true,
reason: devArtifactLiveApplyBlockedServiceIds.get(service.id) ?? "live DEV artifact apply is blocked by policy",
selfBootstrapGuard: service.id === "code-queue" ? codeQueueSelfBootstrapGuard("dev") : undefined,
})),
policy: "dev dry-run/plan is available, but a running Code Queue task must not perform live DEV Code Queue apply without explicit supervisor or human authorization outside Code Queue",
dryRunCommandShape: "bun scripts/cli.ts deploy apply --env dev --service <id> --dry-run",
};
}
async function runArtifactConsumerApplyNow(
manifest: DeployManifest,
options: DeployOptions,
@@ -3472,6 +3546,8 @@ export async function runDeployCommand(config: UniDeskConfig | null, args: strin
throw new Error("deploy apply --env dev cannot mix artifact consumer services with target-side rollout services in one invocation; pass --service");
}
if (devArtifactServices.length > 0) {
const blocked = devArtifactServices.filter((service) => devArtifactLiveApplyBlockedServiceIds.has(service.id));
if (!options.dryRun && blocked.length > 0) return devArtifactLiveApplyBlockedResult(blocked);
if (options.dryRun || options.runNow) return await runArtifactConsumerApplyNow(manifest, options, "dev", devArtifactServices);
return devArtifactApplyJob(args, options);
}