fix(cicd): harden PaC source artifact verification

This commit is contained in:
Codex
2026-07-11 07:03:12 +02:00
parent aeb26bfe69
commit 2dd8814078
9 changed files with 882 additions and 170 deletions
+2
View File
@@ -108,6 +108,7 @@ export interface AgentRunLaneSpec {
readonly source: {
readonly statusMode: "host-worktree" | "k3s-git-mirror";
readonly repository: string;
readonly worktreeRemote: string;
readonly branch: string;
readonly sourceAuthority: AgentRunSourceAuthoritySpec | null;
readonly sourceSnapshot: AgentRunSourceSnapshotSpec | null;
@@ -595,6 +596,7 @@ function parseLane(lane: string, node: AgentRunNodeSpec, input: Record<string, u
source: {
statusMode,
repository: stringField(source, "repository", `${path}.source`),
worktreeRemote: stringField(source, "worktreeRemote", `${path}.source`),
branch: stringField(source, "branch", `${path}.source`),
sourceAuthority: sourceAuthorityConfig(source.sourceAuthority, `${path}.source.sourceAuthority`),
sourceSnapshot: sourceSnapshotConfig(source.sourceSnapshot, `${path}.source.sourceSnapshot`),
@@ -7,12 +7,22 @@ import { resolve } from "node:path";
import { rootPath } from "./config";
import { applyNodeRuntimeDeployYamlOverlay, nodeRuntimeDeployYamlOverlayShellScript } from "./hwlab-node/deploy-overlay";
import {
assertPacSourceArtifactVerifyWorktree,
canonicalSha256,
canonicalizePacPipelineSpec,
assertHwlabPipelineContracts,
firstPacSourceArtifactDrift,
pacValueEvidence,
pacSourceArtifactYaml,
parsePacSourceArtifactOptions,
sourceArtifactRuntimeAlignment,
} from "./platform-infra-pipelines-as-code-source-artifact";
import { renderPacSourceArtifactRuntimeObserverShell } from "./platform-infra-pipelines-as-code";
import {
pacRuntimeSafeReason,
pacRuntimeSafePath,
pacRuntimeSourceArtifactItem,
pacRuntimeTransportFailureReason,
renderPacSourceArtifactRuntimeObserverShell,
} from "./platform-infra-pipelines-as-code";
import {
canonicalPacPipelineSha256 as nativeCanonicalSha256,
canonicalizePacPipelineSpec as nativeCanonicalize,
@@ -25,14 +35,24 @@ const provenance = {
configRef: "config/example.yaml#lanes.nc01",
effectiveConfigSha256: "sha256:config",
renderer: "agentrun-control-plane",
mode: "embedded-pipeline-spec",
};
const desiredSpec = { params: [], tasks: [] };
const runtimeWrapperSpec = {
taskRunTemplate: {
serviceAccountName: "agentrun-nc01-v02-tekton-runner",
podTemplate: { hostNetwork: true, dnsPolicy: "ClusterFirstWithHostNet", securityContext: { fsGroup: 1000 } },
},
params: [],
workspaces: [],
};
function manifestAnnotations(): Record<string, string> {
return {
"unidesk.ai/owning-config-ref": provenance.configRef,
"unidesk.ai/effective-config-sha256": provenance.effectiveConfigSha256,
"unidesk.ai/source-artifact-renderer": provenance.renderer,
"unidesk.ai/source-artifact-mode": provenance.mode,
};
}
@@ -51,7 +71,7 @@ function pipelineRun(name: string, creationTimestamp: string, spec: Record<strin
labels: { "pipelinesascode.tekton.dev/sha": sourceCommit },
annotations: manifestAnnotations(),
},
spec: { pipelineSpec: spec },
spec: { pipelineSpec: spec, ...structuredClone(runtimeWrapperSpec) },
};
}
@@ -61,6 +81,7 @@ function runtimeInput(commit: string | null = sourceCommit): Record<string, unkn
pipeline: "agentrun-nc01-v02-ci-image-publish",
pipelineRunPrefix: "agentrun-nc01-v02-ci",
desiredSpec,
desiredWrapper: { mode: "embedded-pipeline-spec", executionMode: "pipeline-spec", spec: runtimeWrapperSpec },
provenance,
sourceCommit: commit,
};
@@ -70,7 +91,7 @@ describe("PaC source artifact CLI contract", () => {
test("verify-runtime requires and normalizes a full source commit", () => {
expect(() => parsePacSourceArtifactOptions([
"verify-runtime", "--target", "NC01", "--consumer", "agentrun-nc01-v02", "--source-worktree", "/tmp/repo",
])).toThrow("requires explicit --source-commit");
])).toThrow("source-commit-required");
const parsed = parsePacSourceArtifactOptions([
"verify-runtime", "--target", "NC01", "--consumer", "agentrun-nc01-v02", "--source-worktree", "/tmp/repo", "--source-commit", sourceCommit.toUpperCase(),
]);
@@ -100,20 +121,87 @@ describe("PaC source artifact CLI contract", () => {
"--target", "NC01", "--consumer", "agentrun-nc01-v02", "--source-worktree", rootPath(),
], { cwd: rootPath(), encoding: "utf8", timeout: 30_000 });
expect(result.status).toBe(1);
expect(result.stdout).toContain("origin does not match PaC repository pikasTech-agentrun");
expect(result.stdout).toContain("pikasTech/unidesk.git");
expect(result.stdout).toContain("code=source-worktree-origin-mismatch");
expect(result.stdout).toContain("observedUrlFingerprint=sha256:");
expect(result.stdout).not.toContain("observedOwnerRepo");
expect(result.stdout).not.toContain("stack");
expect(result.stdout).not.toContain("taskSpec");
expect(result.stdout.length).toBeLessThan(1500);
});
test("default, JSON, and full failures never disclose a malicious origin", () => {
const temporary = mkdtempSync(resolve(tmpdir(), "unidesk-pac-origin-test-"));
const secrets = [
"review-user",
"REVIEW_SUPER_SECRET",
"QUERY_SUPER_SECRET",
"Authorization",
"https://review-user:REVIEW_SUPER_SECRET@example.invalid/pikasTech/agentrun.git?token=QUERY_SUPER_SECRET",
];
try {
for (const args of [
["init"],
["config", "user.email", "pac-test@example.invalid"],
["config", "user.name", "PaC Test"],
]) expect(spawnSync("git", args, { cwd: temporary, encoding: "utf8" }).status).toBe(0);
writeFileSync(resolve(temporary, "README.md"), "fixture\n");
expect(spawnSync("git", ["add", "README.md"], { cwd: temporary, encoding: "utf8" }).status).toBe(0);
expect(spawnSync("git", ["commit", "-m", "fixture"], { cwd: temporary, encoding: "utf8" }).status).toBe(0);
expect(spawnSync("git", ["remote", "add", "origin", secrets[4] as string], { cwd: temporary, encoding: "utf8" }).status).toBe(0);
const origins = [
secrets[4] as string,
"audit@example.invalid:pikasTech/agentrun.git?token=SCP_QUERY_SUPER_SECRET",
"https://review-user:REVIEW_SUPER_SECRET@github.com/pikasTech/agentrun.git",
"https://github.com/pikasTech/agentrun.git?token=QUERY_SUPER_SECRET",
"https://github.com/pikasTech/agentrun.git#QUERY_SUPER_SECRET",
];
for (const origin of origins) {
expect(spawnSync("git", ["remote", "set-url", "origin", origin], { cwd: temporary, encoding: "utf8" }).status).toBe(0);
for (const outputFlag of [null, "--json", "--full"] as const) {
const args = [
"scripts/cli.ts", "platform-infra", "pipelines-as-code", "source-artifact", "plan",
"--target", "NC01", "--consumer", "agentrun-nc01-v02", "--source-worktree", temporary,
];
if (outputFlag !== null) args.push(outputFlag);
const result = spawnSync("bun", args, { cwd: rootPath(), encoding: "utf8", timeout: 30_000 });
const output = `${result.stdout}\n${result.stderr}`;
expect(result.status).toBe(1);
expect(output).toContain("source-worktree-origin-mismatch");
expect(output).toContain("observedUrlFingerprint");
expect(output).not.toContain("observedOwnerRepo");
for (const secret of [...secrets, "SCP_QUERY_SUPER_SECRET"]) expect(output).not.toContain(secret);
}
}
} finally {
rmSync(temporary, { recursive: true, force: true });
}
}, 15_000);
test("verification requires a clean worktree at the exact requested commit", () => {
const exact = { head: sourceCommit, clean: true, matchesSourceCommit: true };
expect(() => assertPacSourceArtifactVerifyWorktree("verify-runtime", sourceCommit, exact)).not.toThrow();
expect(() => assertPacSourceArtifactVerifyWorktree("status", sourceCommit, { ...exact, clean: false })).not.toThrow();
expect(() => assertPacSourceArtifactVerifyWorktree("verify-runtime", sourceCommit, { ...exact, clean: false })).toThrow("source-worktree-not-clean");
expect(() => assertPacSourceArtifactVerifyWorktree("verify-runtime", sourceCommit, { ...exact, head: "b".repeat(40), matchesSourceCommit: false })).toThrow("source-worktree-commit-mismatch");
});
test("taskRunTemplate operational facts are declared in owning YAML", () => {
const document = Bun.YAML.parse(readFileSync(rootPath("config", "platform-infra", "pipelines-as-code.yaml"), "utf8")) as Record<string, any>;
const consumers = document.consumers as Array<Record<string, any>>;
for (const template of ["templates.consumers.agentrunV02", "templates.consumers.hwlabV03"]) {
const sourceArtifact = consumers.find((item) => item.extends === template && item.variables?.NODE === "NC01")?.sourceArtifact;
expect(sourceArtifact?.taskRunTemplate).toEqual({ hostNetwork: true, dnsPolicy: "ClusterFirstWithHostNet", fsGroup: 1000 });
}
});
test("undeclared JD01 source artifact owner fails closed", () => {
const result = spawnSync("bun", [
"scripts/cli.ts", "platform-infra", "pipelines-as-code", "source-artifact", "plan",
"--target", "JD01", "--consumer", "agentrun-jd01-v02", "--source-worktree", rootPath(),
], { cwd: rootPath(), encoding: "utf8", timeout: 30_000 });
expect(result.status).toBe(1);
expect(result.stdout).toContain("has no sourceArtifact owner");
expect(result.stdout).toContain("code=source-artifact-operation-failed");
expect(result.stdout).toContain("evidence=type=string");
});
});
@@ -167,6 +255,23 @@ describe("Tekton admission canonicalization", () => {
});
});
describe("source artifact serialization", () => {
test("remote Pipeline artifacts are deterministic reviewable multi-line YAML", () => {
const manifest = {
apiVersion: "tekton.dev/v1",
kind: "Pipeline",
metadata: { name: "fixture" },
spec: { params: [{ name: "revision", type: "string" }], tasks: [{ name: "build", taskSpec: { steps: [{ name: "build", script: "set -eu\necho ok\n" }] } }] },
};
const first = pacSourceArtifactYaml(manifest);
const second = pacSourceArtifactYaml(manifest);
expect(first).toBe(second);
expect(first.split("\n").length).toBeGreaterThan(10);
expect(first).toContain("apiVersion: tekton.dev/v1\nkind: Pipeline\n");
expect(Bun.YAML.parse(first)).toEqual(manifest);
});
});
describe("runtime source-commit selection", () => {
test("same-commit retries select the newest only when spec and provenance agree", () => {
const oldRun = pipelineRun("agentrun-nc01-v02-ci-old", "2026-07-10T00:00:00Z");
@@ -195,6 +300,51 @@ describe("runtime source-commit selection", () => {
expect(observed.embedded.candidateCount).toBe(2);
});
test("embedded mode compares the complete PipelineRun wrapper", () => {
const run = pipelineRun("agentrun-nc01-v02-ci-wrapper", "2026-07-11T00:00:00Z");
(run.spec as Record<string, any>).taskRunTemplate.serviceAccountName = "wrong-service-account";
const observed = observePacSourceArtifactRuntime(runtimeInput(), (_args: string[], label: string) => label === "pipeline-get"
? { kind: "ok", value: pipeline() }
: { kind: "ok", value: { items: [run] } });
expect(observed.embedded.status).toBe("drifted");
expect(observed.embedded.firstDrift?.path).toBe("$.spec.taskRunTemplate.serviceAccountName");
});
test("remote mode validates Pipeline spec live and wrapper on the exact-commit Run", () => {
const remoteProvenance = { ...provenance, mode: "remote-pipeline-annotation" };
const remoteAnnotations = {
...manifestAnnotations(),
"unidesk.ai/source-artifact-mode": "remote-pipeline-annotation",
};
const wrapper = {
mode: "remote-pipeline-annotation",
executionMode: "pipeline-spec",
spec: { ...runtimeWrapperSpec, timeouts: { pipeline: "1h0m0s" } },
};
const run = {
metadata: {
name: "hwlab-nc01-v03-ci-poll-remote",
creationTimestamp: "2026-07-11T00:00:00Z",
labels: { "pipelinesascode.tekton.dev/sha": sourceCommit },
annotations: remoteAnnotations,
},
spec: { pipelineSpec: desiredSpec, ...wrapper.spec },
};
const input = {
...runtimeInput(),
pipeline: "hwlab-nc01-v03-ci-image-publish",
pipelineRunPrefix: "hwlab-nc01-v03-ci-poll",
provenance: remoteProvenance,
desiredWrapper: wrapper,
};
const observed = observePacSourceArtifactRuntime(input, (_args: string[], label: string) => label === "pipeline-get"
? { kind: "ok", value: { metadata: { name: input.pipeline, annotations: remoteAnnotations }, spec: desiredSpec } }
: { kind: "ok", value: { items: [run] } });
expect(observed.live.status).toBe("aligned");
expect(observed.embedded.status).toBe("aligned");
expect(observed.embedded.firstDrift).toBeNull();
});
test("NotFound, missing commit, and transport failures remain distinct", () => {
const noCommit = observePacSourceArtifactRuntime(runtimeInput(null), () => ({ kind: "not-found", reason: "pipeline-get-not-found" }));
expect(noCommit.live.status).toBe("missing");
@@ -202,14 +352,170 @@ describe("runtime source-commit selection", () => {
expect(noCommit.embedded.status).toBe("unavailable");
expect(noCommit.embedded.reason).toBe("source-commit-not-specified");
const transport = observePacSourceArtifactRuntime(runtimeInput(), (_args: string[], label: string) => ({ kind: "unavailable", reason: `${label}-command-failed:126:permission denied` }));
const secret = "Authorization: Bearer REVIEW_RUNTIME_SECRET https://review:password@example.invalid/path?token=QUERY_SECRET";
const transport = observePacSourceArtifactRuntime(runtimeInput(), () => ({ kind: "unavailable", reason: secret }));
expect(transport.live.status).toBe("unavailable");
expect(transport.embedded.status).toBe("unavailable");
expect(transport.live.reason).toContain("permission denied");
expect(transport.embedded.reason).toContain("permission denied");
expect(transport.live.reason).toMatch(/^runtime-observer-reason:type=string,length=\d+,sha256=[0-9a-f]{64}$/u);
expect(transport.embedded.reason).toMatch(/^runtime-observer-reason:type=string,length=\d+,sha256=[0-9a-f]{64}$/u);
expect(JSON.stringify(transport)).not.toContain(secret);
});
test("first drift reports structural evidence without script or token values", () => {
const expectedSecret = "Authorization: Bearer EXPECTED_SUPER_SECRET";
const actualSecret = "https://review-user:ACTUAL_SUPER_SECRET@example.invalid/path?token=QUERY_SUPER_SECRET";
const input = {
...runtimeInput(),
desiredSpec: { tasks: [{ taskSpec: { steps: [{ script: expectedSecret }] } }] },
};
const observed = observePacSourceArtifactRuntime(input, (_args: string[], label: string) => label === "pipeline-get"
? { kind: "ok", value: { metadata: { name: "pipeline", annotations: manifestAnnotations() }, spec: { tasks: [{ taskSpec: { steps: [{ script: actualSecret }] } }] } } }
: { kind: "ok", value: { items: [pipelineRun("agentrun-nc01-v02-ci-secret", "2026-07-11T00:00:00Z", { tasks: [{ taskSpec: { steps: [{ script: actualSecret }] } }] })] } });
const serialized = JSON.stringify(observed);
expect(observed.live.firstDrift?.path).toBe("$.tasks[0].taskSpec.steps[0].script");
expect(observed.live.firstDrift?.expected).toBe(pacValueEvidence(expectedSecret));
expect(observed.live.firstDrift?.actual).toBe(pacValueEvidence(actualSecret));
expect(serialized).not.toContain(expectedSecret);
expect(serialized).not.toContain(actualSecret);
expect(serialized).not.toContain("Authorization");
expect(serialized).not.toContain("QUERY_SUPER_SECRET");
});
test("unknown runtime keys and forged paths are fingerprinted", () => {
const maliciousKey = "HEADER_QUERY_SUPER_SECRET";
const local = firstPacSourceArtifactDrift({ tasks: [], [maliciousKey]: "expected" }, { tasks: [], [maliciousKey]: "actual" });
expect(local?.path).not.toContain(maliciousKey);
expect(local?.path).toContain("type=string,length=");
const maliciousInput = { ...runtimeInput(), desiredSpec: { ...desiredSpec, [maliciousKey]: "expected" } };
const observed = observePacSourceArtifactRuntime(maliciousInput, (_args: string[], label: string) => label === "pipeline-get"
? { kind: "ok", value: { ...pipeline(), spec: { ...desiredSpec, [maliciousKey]: "actual" } } }
: { kind: "ok", value: { items: [pipelineRun("agentrun-nc01-v02-ci-key", "2026-07-11T00:00:00Z", { ...desiredSpec, [maliciousKey]: "actual" })] } });
expect(JSON.stringify(observed)).not.toContain(maliciousKey);
expect(pacRuntimeSafePath(`$.${maliciousKey}`)).toBe(`path(${pacValueEvidence(`$.${maliciousKey}`)})`);
});
test("untrusted runtime annotations and identities are never echoed", () => {
const secret = "Authorization: Bearer OBSERVER_SUPER_SECRET https://u:p@example.invalid/x?token=QUERY_SECRET";
const annotations = {
...manifestAnnotations(),
"unidesk.ai/owning-config-ref": secret,
"unidesk.ai/effective-config-sha256": secret,
"unidesk.ai/source-artifact-renderer": secret,
};
const observed = observePacSourceArtifactRuntime(runtimeInput(), (_args: string[], label: string) => label === "pipeline-get"
? { kind: "ok", value: { metadata: { name: secret, annotations }, spec: desiredSpec } }
: { kind: "ok", value: { items: [{ ...pipelineRun("agentrun-nc01-v02-ci-annotation", "2026-07-11T00:00:00Z"), metadata: { name: secret, creationTimestamp: "2026-07-11T00:00:00Z", labels: { "pipelinesascode.tekton.dev/sha": sourceCommit }, annotations } }] } });
const serialized = JSON.stringify(observed);
expect(observed.live.provenanceAligned).toBe(false);
expect(observed.live.configRef).toBeNull();
expect(observed.live.effectiveConfigSha256).toBeNull();
expect(observed.live.name).toBeNull();
expect(serialized).not.toContain(secret);
expect(serialized).not.toContain("OBSERVER_SUPER_SECRET");
expect(serialized).not.toContain("QUERY_SECRET");
const forged = pacRuntimeSourceArtifactItem({
status: "drifted",
name: "observer-super-secret",
canonicalSha256: `sha256:${"c".repeat(64)}`,
firstDrift: { path: "$.HEADER_QUERY_SUPER_SECRET", expected: secret, actual: secret },
provenanceAligned: false,
configRef: "SECRET/TOKEN#QUERY",
effectiveConfigSha256: `sha256:${"d".repeat(64)}`,
renderer: provenance.renderer,
mode: provenance.mode,
sourceCommit: "e".repeat(40),
reason: secret,
candidateCount: 1,
}, {
configRef: provenance.configRef,
effectiveConfigSha256: `sha256:${"f".repeat(64)}`,
renderer: provenance.renderer,
mode: provenance.mode,
sourceCommit,
exactName: null,
namePrefix: "agentrun-nc01-v02-ci",
});
const forgedSerialized = JSON.stringify(forged);
expect(forged.name).toBeNull();
expect(forged.configRef).toBeNull();
expect(forged.effectiveConfigSha256).toBeNull();
expect(forged.provenanceAligned).toBe(false);
expect(forged.sourceCommit).toBeNull();
expect(forged.firstDrift?.path).not.toContain("HEADER_QUERY_SUPER_SECRET");
expect(forgedSerialized).not.toContain(secret);
expect(forgedSerialized).not.toContain("SECRET/TOKEN#QUERY");
const forgedAligned = pacRuntimeSourceArtifactItem({
status: "aligned",
provenanceAligned: true,
configRef: provenance.configRef,
effectiveConfigSha256: provenance.effectiveConfigSha256,
renderer: "forged-renderer",
mode: provenance.mode,
sourceCommit,
candidateCount: 1,
}, {
configRef: provenance.configRef,
effectiveConfigSha256: provenance.effectiveConfigSha256,
renderer: provenance.renderer,
mode: provenance.mode,
sourceCommit,
exactName: null,
namePrefix: null,
});
expect(forgedAligned.provenanceAligned).toBe(false);
});
test("typed runtime alignment never reports a synthetic pending state", () => {
const aligned = observePacSourceArtifactRuntime(runtimeInput(), (_args: string[], label: string) => label === "pipeline-get"
? { kind: "ok", value: pipeline() }
: { kind: "ok", value: { items: [pipelineRun("agentrun-nc01-v02-ci-aligned", "2026-07-11T00:00:00Z")] } });
const drifted = structuredClone(aligned);
drifted.embedded.status = "drifted";
const exact = { head: sourceCommit, clean: true, matchesSourceCommit: true };
expect(sourceArtifactRuntimeAlignment(null, exact, sourceCommit)).toBe("not-requested");
expect(sourceArtifactRuntimeAlignment(aligned, exact, sourceCommit)).toBe("aligned");
expect(sourceArtifactRuntimeAlignment(drifted, exact, sourceCommit)).toBe("drifted");
expect(sourceArtifactRuntimeAlignment(aligned, { ...exact, clean: false }, sourceCommit)).toBe("unavailable");
expect(JSON.stringify([aligned, drifted])).not.toContain("pending");
});
test("local transport and remote observer failures retain only bounded evidence", () => {
const secret = "Authorization: Bearer TRANSPORT_SUPER_SECRET https://review:password@example.invalid/path?token=QUERY_SECRET";
const localReason = pacRuntimeTransportFailureReason({ exitCode: 126, stdout: secret, stderr: secret });
expect(localReason).toContain(pacValueEvidence(secret));
expect(localReason).not.toContain(secret);
expect(pacRuntimeSafeReason(secret)).toBe(`runtime-observer-reason:${pacValueEvidence(secret)}`);
const temporary = mkdtempSync(resolve(tmpdir(), "unidesk-pac-runtime-redaction-test-"));
try {
const bin = resolve(temporary, "bin");
mkdirSync(bin);
const kubectl = resolve(bin, "kubectl");
writeFileSync(kubectl, `#!/bin/sh\nprintf '%s\\n' '${secret}' >&2\nexit 126\n`);
chmodSync(kubectl, 0o755);
const shell = renderPacSourceArtifactRuntimeObserverShell(runtimeInput());
const result = spawnSync("sh", [], {
input: shell,
encoding: "utf8",
timeout: 30_000,
env: { ...process.env, PATH: `${bin}:${process.env.PATH ?? ""}` },
});
expect(result.status).toBe(0);
expect(result.stdout).toContain("command-failed:126:type=string");
expect(result.stdout).not.toContain(secret);
expect(result.stdout).not.toContain("Authorization");
expect(result.stdout).not.toContain("QUERY_SECRET");
} finally {
rmSync(temporary, { recursive: true, force: true });
}
});
test("temp-file transport handles a payload larger than the current 203 KiB HWLAB Pipeline", () => {
const observerSource = readFileSync(rootPath("scripts", "native", "cicd", "pac-source-artifact-runtime.mjs"), "utf8");
expect(observerSource).not.toMatch(/process\.env\.PAC_SOURCE_ARTIFACT_INPUT(?!_FILE)/u);
const temporary = mkdtempSync(resolve(tmpdir(), "unidesk-pac-runtime-test-"));
try {
const bin = resolve(temporary, "bin");
@@ -220,6 +526,7 @@ describe("runtime source-commit selection", () => {
pipeline: "hwlab-nc01-v03-ci-image-publish",
pipelineRunPrefix: "hwlab-nc01-v03-ci-poll",
desiredSpec: hugeSpec,
desiredWrapper: { mode: "embedded-pipeline-spec", executionMode: "pipeline-spec", spec: runtimeWrapperSpec },
provenance,
sourceCommit,
};
@@ -258,28 +565,6 @@ describe("runtime source-commit selection", () => {
});
});
describe("HWLAB domain Pipeline contract", () => {
const requiredMarkers = [
"unidesk-runtime-gitops-postprocess",
"unidesk-runtime-gitops-verify",
"HWLAB_WORKBENCH_KAFKA_REFRESH_REPLAY_ENABLED",
"HWLAB_WORKBENCH_KAFKA_REFRESH_REPLAY_GROUP_PREFIX",
"HWLAB_WORKBENCH_KAFKA_REFRESH_REPLAY_TIMEOUT_MS",
"HWLAB_WORKBENCH_KAFKA_REFRESH_REPLAY_SCAN_LIMIT",
"HWLAB_WORKBENCH_KAFKA_REFRESH_REPLAY_MATCHED_EVENT_LIMIT",
"HWLAB_WORKBENCH_KAFKA_REFRESH_REPLAY_LIVE_BUFFER_LIMIT",
];
test("accepts the complete renderer contract and rejects every missing critical marker", () => {
const complete = { tasks: [{ taskSpec: { steps: [{ script: requiredMarkers.join("\n") }] } }] };
expect(() => assertHwlabPipelineContracts(complete)).not.toThrow();
for (const marker of requiredMarkers) {
const missing = { tasks: [{ taskSpec: { steps: [{ script: requiredMarkers.filter((item) => item !== marker).join("\n") }] } }] };
expect(() => assertHwlabPipelineContracts(missing)).toThrow(marker);
}
});
});
describe("shared HWLAB deploy overlay", () => {
test("preserves exact undefined/null semantics without mutating its input", () => {
const document = {
@@ -14,11 +14,18 @@ import { nodeRuntimeRenderOverlay } from "./hwlab-node/web-probe";
import { applyNodeRuntimeDeployYamlOverlay } from "./hwlab-node/deploy-overlay";
import type { RenderedCliResult } from "./output";
import { renderedCliResult } from "./agentrun/render";
import { stableJsonSha256 } from "./stable-json";
import { stableJsonSha256, stableJsonValue } from "./stable-json";
export type PacSourceArtifactMode = "embedded-pipeline-spec" | "remote-pipeline-annotation";
export type PacSourceArtifactRenderer = "agentrun-control-plane" | "hwlab-runtime-lane";
export type PacSourceArtifactAction = "plan" | "check" | "write" | "status" | "verify-runtime";
export type PacSourceArtifactRuntimeAlignment = "not-requested" | "aligned" | "drifted" | "missing" | "unavailable";
export interface PacSourceArtifactTaskRunTemplateSpec {
readonly hostNetwork: boolean;
readonly dnsPolicy: "ClusterFirst" | "ClusterFirstWithHostNet" | "Default" | "None";
readonly fsGroup: number;
}
export interface PacSourceArtifactSpec {
readonly mode: PacSourceArtifactMode;
@@ -27,6 +34,7 @@ export interface PacSourceArtifactSpec {
readonly pipelineRunPath: string;
readonly pipelinePath: string | null;
readonly maxKeepRuns: number;
readonly taskRunTemplate: PacSourceArtifactTaskRunTemplateSpec;
}
export interface PacSourceArtifactBinding {
@@ -42,6 +50,7 @@ export interface PacSourceArtifactBinding {
};
readonly repository: {
readonly id: string;
readonly url: string;
readonly cloneUrl: string;
readonly owner: string;
readonly repo: string;
@@ -87,6 +96,7 @@ export interface PacSourceArtifactDrift {
export type PacSourceArtifactRuntimeObserver = (input: {
readonly binding: PacSourceArtifactBinding;
readonly desiredSpec: Record<string, unknown>;
readonly desiredWrapper: Record<string, unknown> | null;
readonly provenance: PacSourceArtifactProvenance;
readonly sourceCommit: string | null;
}) => Promise<PacSourceArtifactRuntimeObservation>;
@@ -95,6 +105,7 @@ interface PacSourceArtifactProvenance {
readonly configRef: string;
readonly effectiveConfigSha256: string;
readonly renderer: PacSourceArtifactRenderer;
readonly mode: PacSourceArtifactMode;
}
interface DesiredArtifact {
@@ -114,6 +125,48 @@ interface SourceInspection {
readonly provenanceAligned: boolean;
}
export interface SourceWorktreeState {
readonly head: string;
readonly clean: boolean;
readonly matchesSourceCommit: boolean | null;
}
export class PacSourceArtifactError extends Error {
readonly code: string;
readonly safeDetails: Readonly<Record<string, string>>;
constructor(code: string, safeDetails: Readonly<Record<string, string>> = {}) {
super(code);
this.name = "PacSourceArtifactError";
this.code = code;
this.safeDetails = safeDetails;
}
}
export interface PacSourceArtifactSafeError {
readonly code: string;
readonly evidence: string;
readonly details: Readonly<Record<string, string>>;
}
export function pacSourceArtifactSafeError(error: unknown): PacSourceArtifactSafeError {
const message = error instanceof Error ? error.message : String(error);
const allowedDetailKeys = new Set([
"expectedUrlFingerprint",
"observedUrlFingerprint",
"head",
"requestedSourceCommit",
]);
const details = error instanceof PacSourceArtifactError
? Object.fromEntries(Object.entries(error.safeDetails).map(([key, value]) => [key, allowedDetailKeys.has(key) ? value : pacValueEvidence(value)]))
: {};
return {
code: error instanceof PacSourceArtifactError ? error.code : "source-artifact-operation-failed",
evidence: pacValueEvidence(message),
details,
};
}
const provenanceKeys = {
configRef: "unidesk.ai/owning-config-ref",
effectiveConfigSha256: "unidesk.ai/effective-config-sha256",
@@ -121,6 +174,15 @@ const provenanceKeys = {
mode: "unidesk.ai/source-artifact-mode",
} as const;
const knownDriftPathKeys = new Set([
"accessModes", "annotations", "apiVersion", "args", "command", "computeResources", "default", "description", "dnsPolicy", "env", "envFrom",
"executionMode", "finally", "fsGroup", "generateName", "hostNetwork", "image", "kind", "labels", "length", "metadata", "mode", "mountPath", "name", "namespace", "onError", "operator",
"optional", "params", "pipeline", "pipelineRef", "pipelineSpec", "podTemplate", "readinessProbe", "readOnly", "requests", "resources",
"results", "retries", "runAfter", "script", "secret", "secretName", "securityContext", "serviceAccountName", "sidecars", "spec", "steps",
"storage", "subPath", "taskRunTemplate", "taskSpec", "tasks", "timeout", "timeouts", "type", "value", "values", "volumeClaimTemplate",
"volumeMounts", "volumes", "when", "workspaces", "workingDir", "wrapper",
]);
export function parsePacSourceArtifactOptions(args: readonly string[]): PacSourceArtifactOptions {
const action = args[0];
if (!isSourceArtifactAction(action)) {
@@ -165,7 +227,7 @@ export function parsePacSourceArtifactOptions(args: readonly string[]): PacSourc
if (!isAbsolute(sourceWorktree)) throw new Error("--source-worktree must be an absolute path");
if (sourceCommit !== null && !/^[0-9a-f]{40}$/u.test(sourceCommit)) throw new Error("--source-commit must be a full 40-character git SHA");
if (sourceCommit !== null && action !== "status" && action !== "verify-runtime") throw new Error("--source-commit is accepted only by source-artifact status or verify-runtime");
if (action === "verify-runtime" && sourceCommit === null) throw new Error("source-artifact verify-runtime requires explicit --source-commit");
if (action === "verify-runtime" && sourceCommit === null) throw new PacSourceArtifactError("source-commit-required");
if (action === "write" && !confirm) throw new Error("source-artifact write requires --confirm");
if (action !== "write" && confirm) throw new Error("--confirm is accepted only by source-artifact write");
return { action, targetId, consumerId, sourceWorktree, sourceCommit, confirm, json, full };
@@ -183,6 +245,13 @@ export async function runPacSourceArtifact(
throw new Error(`source-artifact consumer ${options.consumerId} does not match resolved consumer ${binding.consumer.id}`);
}
const sourceWorktree = validateSourceWorktree(options.sourceWorktree, binding);
const sourceHead = git(sourceWorktree, ["rev-parse", "HEAD"]).toLowerCase();
const sourceWorktreeState: SourceWorktreeState = {
head: sourceHead,
clean: git(sourceWorktree, ["status", "--porcelain=v1", "--untracked-files=all"]).length === 0,
matchesSourceCommit: options.sourceCommit === null ? null : sourceHead === options.sourceCommit,
};
assertPacSourceArtifactVerifyWorktree(options.action, options.sourceCommit, sourceWorktreeState);
const desired = renderDesiredArtifact(binding, sourceWorktree);
let source = inspectSourceArtifact(sourceWorktree, binding, desired);
const written: string[] = [];
@@ -194,14 +263,17 @@ export async function runPacSourceArtifact(
const runtime = needsRuntime
? observeRuntime === undefined
? unavailableRuntimeObservation()
: await observeRuntime({ binding, desiredSpec: desired.desiredSpec, provenance: desired.provenance, sourceCommit: options.sourceCommit })
: await observeRuntime({
binding,
desiredSpec: desired.desiredSpec,
desiredWrapper: options.sourceCommit === null ? null : desiredRuntimePipelineRunWrapper(binding, desired.pipelineRun, options.sourceCommit),
provenance: desired.provenance,
sourceCommit: options.sourceCommit,
})
: null;
const sourceOk = source.aligned && source.provenanceAligned;
const runtimeOk = runtime !== null
&& runtime.live.status === "aligned"
&& runtime.live.provenanceAligned
&& runtime.embedded.status === "aligned"
&& runtime.embedded.provenanceAligned;
const runtimeAlignment = sourceArtifactRuntimeAlignment(runtime, sourceWorktreeState, options.sourceCommit);
const runtimeOk = runtimeAlignment === "aligned";
const ok = options.action === "check"
? sourceOk
: options.action === "verify-runtime"
@@ -233,17 +305,10 @@ export async function runPacSourceArtifact(
namespace: binding.consumer.namespace,
},
source,
sourceWorktreeState,
runtime,
runtimeTarget: { sourceCommit: options.sourceCommit },
runtimeAlignment: runtime === null
? "not-requested"
: runtimeOk
? "aligned"
: runtime.live.status === "missing" || runtime.embedded.status === "missing"
? "missing"
: runtime.live.status === "unavailable" || runtime.embedded.status === "unavailable"
? "unavailable"
: "pending",
runtimeAlignment,
boundary: {
desiredAuthority: "owning YAML plus domain renderer",
liveExportAllowed: false,
@@ -258,32 +323,90 @@ export async function runPacSourceArtifact(
],
valuesPrinted: false,
},
next: sourceOk
? options.action === "verify-runtime"
? null
: `After the consumer PR rolls out, run source-artifact verify-runtime for ${binding.consumer.id} with --source-commit <full-sha>.`
: options.action === "write"
? "Generated files are still inconsistent; inspect firstDrift and do not publish."
: `Run source-artifact write --confirm against the explicit ${sourceWorktree} worktree.`,
next: sourceArtifactNext(binding, options, sourceOk, sourceWorktreeState, runtime, runtimeAlignment),
};
}
export function assertPacSourceArtifactVerifyWorktree(
action: PacSourceArtifactAction,
sourceCommit: string | null,
worktree: SourceWorktreeState,
): void {
if (action !== "verify-runtime") return;
if (sourceCommit === null) throw new PacSourceArtifactError("source-commit-required");
if (!worktree.clean) throw new PacSourceArtifactError("source-worktree-not-clean", { head: worktree.head });
if (worktree.matchesSourceCommit !== true) {
throw new PacSourceArtifactError("source-worktree-commit-mismatch", {
head: worktree.head,
requestedSourceCommit: sourceCommit,
});
}
}
export function sourceArtifactRuntimeAlignment(
runtime: PacSourceArtifactRuntimeObservation | null,
worktree: SourceWorktreeState,
sourceCommit: string | null,
): PacSourceArtifactRuntimeAlignment {
if (runtime === null) return "not-requested";
if (sourceCommit !== null && (!worktree.clean || worktree.matchesSourceCommit !== true)) return "unavailable";
if (runtime.live.status === "unavailable" || runtime.embedded.status === "unavailable") return "unavailable";
if (runtime.live.status === "missing" || runtime.embedded.status === "missing") return "missing";
if (runtime.live.status === "drifted" || runtime.embedded.status === "drifted") return "drifted";
if (!runtime.live.provenanceAligned || !runtime.embedded.provenanceAligned) return "drifted";
return "aligned";
}
function sourceArtifactNext(
binding: PacSourceArtifactBinding,
options: PacSourceArtifactOptions,
sourceOk: boolean,
worktree: SourceWorktreeState,
runtime: PacSourceArtifactRuntimeObservation | null,
runtimeAlignment: PacSourceArtifactRuntimeAlignment,
): string {
if (!sourceOk) {
return options.action === "write"
? "Generated files remain inconsistent; inspect the safe firstDrift evidence and do not publish."
: `Run source-artifact write --confirm for ${binding.consumer.id}, then rerun check.`;
}
if (runtime === null) return `After the consumer PR rolls out, use a clean exact-commit worktree and run verify-runtime for ${binding.consumer.id} with --source-commit <full-sha>.`;
if (!worktree.clean || (options.sourceCommit !== null && worktree.matchesSourceCommit !== true)) {
return "Create a clean worktree at the exact GitHub merge commit, then rerun status or verify-runtime with that full source commit.";
}
if (runtime.live.status === "unavailable") return "Repair the owning control-plane runtime observer or RBAC, then rerun status before verification.";
if (runtime.live.status === "missing") return "Apply the owning YAML control plane, confirm the live Pipeline exists, then rerun verify-runtime.";
if (runtime.live.status === "drifted" || !runtime.live.provenanceAligned) return "Apply the owning YAML control plane so the live Pipeline spec and provenance align, then rerun verify-runtime.";
if (runtime.embedded.status === "unavailable") return "Inspect the exact-commit PaC PipelineRun identity and observer access, then rerun verify-runtime.";
if (runtime.embedded.status === "missing") return "Wait for or repair the exact-commit PaC PipelineRun; do not validate a prefix-selected run.";
if (runtime.embedded.status === "drifted" || !runtime.embedded.provenanceAligned) return "Regenerate and merge the consumer source artifact, then verify the new exact-commit PipelineRun.";
return runtimeAlignment === "aligned"
? "Exact source, live Pipeline, embedded PipelineRun, and provenance are aligned; continue consumer closeout."
: "Resolve the reported runtime layer and rerun verify-runtime.";
}
export function renderPacSourceArtifactResult(result: Record<string, unknown>): RenderedCliResult {
const files = record(result.files);
const desired = record(result.desired);
const source = record(result.source);
const sourceWorktreeState = record(result.sourceWorktreeState);
const runtime = result.runtime === null ? null : record(result.runtime);
const live = runtime === null ? null : record(runtime.live);
const embedded = runtime === null ? null : record(runtime.embedded);
const firstDrift = source.firstDrift === null ? null : record(source.firstDrift);
const liveDrift = live?.firstDrift === null ? null : record(live?.firstDrift);
const embeddedDrift = embedded?.firstDrift === null ? null : record(embedded?.firstDrift);
const lines = [
"PAC SOURCE ARTIFACT",
`target=${text(result.target)} consumer=${text(result.consumer)} node=${text(result.node)} lane=${text(result.lane)}`,
`mode=${text(result.mode)} renderer=${text(result.renderer)} mutation=${text(result.mutation)}`,
`pipelineRun=${text(files.pipelineRun)} pipeline=${text(files.pipeline ?? "-")}`,
`desired=${shortSha(desired.canonicalSha256)} source=${shortSha(source.pipelineCanonicalSha256)} aligned=${text(source.aligned)} provenance=${text(source.provenanceAligned)}`,
`firstDrift=${firstDrift === null ? "-" : `${text(firstDrift.path)} expected=${text(firstDrift.expected)} actual=${text(firstDrift.actual)}`}`,
`runtime=${text(result.runtimeAlignment)} live=${text(live?.name)}@${shortSha(live?.canonicalSha256)} embedded=${text(embedded?.name)}@${shortSha(embedded?.canonicalSha256)} commit=${text(embedded?.sourceCommit ?? record(result.runtimeTarget).sourceCommit)} candidates=${text(embedded?.candidateCount)}`,
`sourceWorktree=head:${shortSha(sourceWorktreeState.head)} clean:${text(sourceWorktreeState.clean)} commitMatch:${text(sourceWorktreeState.matchesSourceCommit)}`,
`sourceDrift=${formatDrift(firstDrift)}`,
`runtime=${text(result.runtimeAlignment)} commit=${text(embedded?.sourceCommit ?? record(result.runtimeTarget).sourceCommit)} candidates=${text(embedded?.candidateCount)}`,
`live=status:${text(live?.status)} name:${text(live?.name)} hash:${shortSha(live?.canonicalSha256)} provenance:${text(live?.provenanceAligned)} drift:${formatDrift(liveDrift)}`,
`embedded=status:${text(embedded?.status)} name:${text(embedded?.name)} hash:${shortSha(embedded?.canonicalSha256)} provenance:${text(embedded?.provenanceAligned)} drift:${formatDrift(embeddedDrift)}`,
`runtimeReason=live:${text(live?.reason)} embedded:${text(embedded?.reason)}`,
`written=${Array.isArray(files.written) && files.written.length > 0 ? files.written.join(",") : "-"}`,
];
@@ -319,18 +442,17 @@ function renderDesiredArtifact(binding: PacSourceArtifactBinding, sourceWorktree
const rendered = sourceArtifact.renderer === "agentrun-control-plane"
? renderAgentRunDesiredPipeline(binding)
: renderHwlabDesiredPipeline(binding, sourceWorktree);
const pipeline = withPipelineProvenance(rendered.pipeline, rendered.provenance, sourceArtifact.mode);
const pipeline = withPipelineProvenance(rendered.pipeline, rendered.provenance);
const desiredSpec = requiredRecord(pipeline.spec, "rendered Pipeline.spec");
assertPipelineIdentity(pipeline, binding);
if (sourceArtifact.renderer === "hwlab-runtime-lane") assertHwlabPipelineContracts(desiredSpec);
const pipelineRun = sourceArtifact.mode === "embedded-pipeline-spec"
? embeddedPipelineRun(binding, desiredSpec, rendered.provenance)
: remotePipelineRun(binding, pipeline, rendered.provenance);
const files = sourceArtifact.mode === "embedded-pipeline-spec"
? [{ path: sourceArtifact.pipelineRunPath, content: yaml(pipelineRun) }]
? [{ path: sourceArtifact.pipelineRunPath, content: pacSourceArtifactYaml(pipelineRun) }]
: [
{ path: requiredString(sourceArtifact.pipelinePath, "sourceArtifact.pipelinePath"), content: `${JSON.stringify(pipeline)}\n` },
{ path: sourceArtifact.pipelineRunPath, content: yaml(pipelineRun) },
{ path: requiredString(sourceArtifact.pipelinePath, "sourceArtifact.pipelinePath"), content: pacSourceArtifactYaml(pipeline) },
{ path: sourceArtifact.pipelineRunPath, content: pacSourceArtifactYaml(pipelineRun) },
];
return { pipeline, pipelineRun, provenance: rendered.provenance, desiredSpec, files };
}
@@ -355,6 +477,7 @@ function renderAgentRunDesiredPipeline(binding: PacSourceArtifactBinding): { pip
configRef: expectedConfigRef,
effectiveConfigSha256: stableJsonSha256(spec),
renderer: "agentrun-control-plane",
mode: binding.consumer.sourceArtifact.mode,
},
};
}
@@ -381,6 +504,7 @@ function renderHwlabDesiredPipeline(binding: PacSourceArtifactBinding, sourceWor
configRef: expectedConfigRef,
effectiveConfigSha256: stableJsonSha256(spec),
renderer: "hwlab-runtime-lane",
mode: binding.consumer.sourceArtifact.mode,
},
};
}
@@ -540,16 +664,52 @@ function pipelineRunLabels(binding: PacSourceArtifactBinding, partOf: "agentrun"
}
function taskRunTemplate(binding: PacSourceArtifactBinding): Record<string, unknown> {
const declared = binding.consumer.sourceArtifact.taskRunTemplate;
return {
serviceAccountName: `{{ service_account }}`,
podTemplate: {
hostNetwork: true,
dnsPolicy: "ClusterFirstWithHostNet",
securityContext: { fsGroup: 1000 },
hostNetwork: declared.hostNetwork,
dnsPolicy: declared.dnsPolicy,
securityContext: { fsGroup: declared.fsGroup },
},
};
}
function desiredRuntimePipelineRunWrapper(
binding: PacSourceArtifactBinding,
pipelineRun: Record<string, unknown>,
sourceCommit: string,
): Record<string, unknown> {
const sourceSpec = structuredClone(requiredRecord(pipelineRun.spec, "generated PipelineRun.spec"));
delete sourceSpec.pipelineSpec;
delete sourceSpec.pipelineRef;
return {
mode: binding.consumer.sourceArtifact.mode,
executionMode: "pipeline-spec",
spec: resolvePacRuntimeTemplates(sourceSpec, binding, sourceCommit),
};
}
function resolvePacRuntimeTemplates(value: unknown, binding: PacSourceArtifactBinding, sourceCommit: string): unknown {
if (Array.isArray(value)) return value.map((item) => resolvePacRuntimeTemplates(item, binding, sourceCommit));
if (isRecord(value)) {
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, resolvePacRuntimeTemplates(item, binding, sourceCommit)]));
}
if (typeof value !== "string") return value;
const templateValues: Readonly<Record<string, string>> = {
...binding.repository.params,
revision: sourceCommit,
repo_url: binding.repository.url,
};
const resolved = value.replace(/\{\{\s*([A-Za-z0-9_]+)\s*\}\}/gu, (_match, key: string) => {
const replacement = templateValues[key];
if (replacement === undefined) throw new PacSourceArtifactError("source-artifact-runtime-template-unresolved", { template: pacValueEvidence(key) });
return replacement;
});
if (/\{\{|\}\}/u.test(resolved)) throw new PacSourceArtifactError("source-artifact-runtime-template-invalid");
return resolved;
}
function pipelineRunParams(binding: PacSourceArtifactBinding, desiredSpec: Record<string, unknown>): Record<string, unknown>[] {
const params = arrayRecords(desiredSpec.params, "Pipeline.spec.params");
return params.map((param) => {
@@ -585,7 +745,7 @@ function pipelineRunWorkspaces(binding: PacSourceArtifactBinding, desiredSpec: R
});
}
function withPipelineProvenance(pipeline: Record<string, unknown>, provenance: PacSourceArtifactProvenance, mode: PacSourceArtifactMode): Record<string, unknown> {
function withPipelineProvenance(pipeline: Record<string, unknown>, provenance: PacSourceArtifactProvenance): Record<string, unknown> {
const next = structuredClone(pipeline);
const metadata = requiredRecord(next.metadata, "rendered Pipeline.metadata");
const annotations = isRecord(metadata.annotations) ? metadata.annotations : {};
@@ -594,7 +754,7 @@ function withPipelineProvenance(pipeline: Record<string, unknown>, provenance: P
[provenanceKeys.configRef]: provenance.configRef,
[provenanceKeys.effectiveConfigSha256]: provenance.effectiveConfigSha256,
[provenanceKeys.renderer]: provenance.renderer,
[provenanceKeys.mode]: mode,
[provenanceKeys.mode]: provenance.mode,
};
return next;
}
@@ -604,7 +764,7 @@ function inspectSourceArtifact(sourceWorktree: string, binding: PacSourceArtifac
const pipelineRunPath = safeArtifactPath(sourceWorktree, sourceArtifact.pipelineRunPath);
const pipelinePath = sourceArtifact.pipelinePath === null ? null : safeArtifactPath(sourceWorktree, sourceArtifact.pipelinePath);
if (!existsSync(pipelineRunPath) || (pipelinePath !== null && !existsSync(pipelinePath))) {
return { status: "missing", aligned: false, pipelineCanonicalSha256: null, pipelineRunCanonicalSha256: null, firstDrift: { path: "$", expected: "generated artifact", actual: "missing" }, provenanceAligned: false };
return { status: "missing", aligned: false, pipelineCanonicalSha256: null, pipelineRunCanonicalSha256: null, firstDrift: drift("$", { artifact: "generated" }, undefined), provenanceAligned: false };
}
const actualPipelineRun = parseYamlRecord(readFileSync(pipelineRunPath, "utf8"), sourceArtifact.pipelineRunPath);
const actualPipeline = sourceArtifact.mode === "remote-pipeline-annotation"
@@ -628,7 +788,7 @@ function inspectSourceArtifact(sourceWorktree: string, binding: PacSourceArtifac
aligned,
pipelineCanonicalSha256: canonicalSha256(requiredRecord(actualPipeline.spec, "source Pipeline.spec")),
pipelineRunCanonicalSha256: canonicalSha256(actualPipelineRun),
firstDrift: pipelineDrift ?? pipelineRunDrift ?? (provenanceAligned ? null : { path: "$.metadata.annotations.provenance", expected: JSON.stringify(desired.provenance), actual: JSON.stringify(actualProvenance) }),
firstDrift: pipelineDrift ?? pipelineRunDrift ?? (provenanceAligned ? null : drift("$.metadata.annotations.provenance", desired.provenance, actualProvenance)),
provenanceAligned,
};
}
@@ -639,34 +799,51 @@ function validateSourceWorktree(input: string, binding: PacSourceArtifactBinding
const top = realpathSync(git(worktree, ["rev-parse", "--show-toplevel"]));
if (top !== worktree) throw new Error(`--source-worktree must be the git worktree root; resolved ${top}`);
const remote = git(worktree, ["remote", "get-url", "origin"]);
const repoIdentity = binding.repository.repo.toLowerCase().replace(/\.git$/u, "");
const canonicalIdentity = repoIdentity.replace(/^pikastech-/u, "pikastech/");
const allowedIdentities = new Set([
canonicalIdentity,
`${binding.repository.owner}/${repoIdentity}`.toLowerCase(),
remoteRepositoryIdentity(binding.repository.cloneUrl),
]);
const expectedRemote = owningSourceRemote(binding);
const expectedIdentity = remoteRepositoryIdentity(expectedRemote);
const observedIdentity = remoteRepositoryIdentity(remote);
if (!allowedIdentities.has(observedIdentity)) {
throw new Error(`source worktree origin does not match PaC repository ${binding.repository.repo}; observed ${remote}`);
if (expectedIdentity === null || observedIdentity === null
|| expectedIdentity.host !== observedIdentity.host
|| expectedIdentity.ownerRepo !== observedIdentity.ownerRepo) {
throw new PacSourceArtifactError("source-worktree-origin-mismatch", {
expectedUrlFingerprint: fingerprint(expectedRemote),
observedUrlFingerprint: fingerprint(remote),
});
}
safeArtifactPath(worktree, binding.consumer.sourceArtifact.pipelineRunPath);
if (binding.consumer.sourceArtifact.pipelinePath !== null) safeArtifactPath(worktree, binding.consumer.sourceArtifact.pipelinePath);
return worktree;
}
function remoteRepositoryIdentity(remote: string): string {
function owningSourceRemote(binding: PacSourceArtifactBinding): string {
if (binding.consumer.sourceArtifact.renderer === "agentrun-control-plane") {
return resolveAgentRunLaneTarget({ node: binding.consumer.node, lane: binding.consumer.lane }).spec.source.worktreeRemote;
}
if (!isHwlabRuntimeLane(binding.consumer.lane)) throw new PacSourceArtifactError("owning-source-lane-unresolved");
return hwlabRuntimeLaneSpecForNode(binding.consumer.lane, binding.consumer.node).gitUrl;
}
function remoteRepositoryIdentity(remote: string): { readonly host: string; readonly ownerRepo: string } | null {
const trimmed = remote.trim();
const scp = /^[^/@\s]+@[^:\s]+:(.+)$/u.exec(trimmed);
let path = scp?.[1] ?? "";
if (path.length === 0) {
const scp = /^[^/@\s]+@([^:\s]+):(.+)$/u.exec(trimmed);
let host = scp?.[1] ?? "";
let path = scp?.[2] ?? "";
if (host.length === 0 || path.length === 0) {
try {
path = new URL(trimmed).pathname;
const parsed = new URL(trimmed);
if (parsed.username.length > 0 || parsed.password.length > 0 || parsed.search.length > 0 || parsed.hash.length > 0) return null;
host = parsed.hostname;
path = parsed.pathname;
} catch {
throw new Error(`git remote must be an SSH or URL repository identity; observed ${remote}`);
return null;
}
}
return path.replace(/^\/+|\/+$/gu, "").replace(/\.git$/iu, "").toLowerCase();
const segments = path.replace(/^\/+|\/+$/gu, "").split("/").filter(Boolean);
if (segments.length !== 2) return null;
const owner = segments[0]?.toLowerCase();
const repo = segments[1]?.replace(/\.git$/iu, "").toLowerCase();
if (!host || !owner || !repo || !/^[a-z0-9_.-]+$/u.test(owner) || !/^[a-z0-9_.-]+$/u.test(repo)) return null;
return { host: host.toLowerCase(), ownerRepo: `${owner}/${repo}` };
}
function writeArtifactFilesAtomically(sourceWorktree: string, files: readonly { readonly path: string; readonly content: string }[]): string[] {
@@ -754,37 +931,23 @@ function assertPipelineIdentity(pipeline: Record<string, unknown>, binding: PacS
if (metadata.namespace !== binding.consumer.namespace) throw new Error(`rendered Pipeline namespace ${String(metadata.namespace)} does not match consumer ${binding.consumer.namespace}`);
}
export function assertHwlabPipelineContracts(spec: Record<string, unknown>): void {
const text = JSON.stringify(spec);
const required = [
"unidesk-runtime-gitops-postprocess",
"unidesk-runtime-gitops-verify",
"HWLAB_WORKBENCH_KAFKA_REFRESH_REPLAY_ENABLED",
"HWLAB_WORKBENCH_KAFKA_REFRESH_REPLAY_GROUP_PREFIX",
"HWLAB_WORKBENCH_KAFKA_REFRESH_REPLAY_TIMEOUT_MS",
"HWLAB_WORKBENCH_KAFKA_REFRESH_REPLAY_SCAN_LIMIT",
"HWLAB_WORKBENCH_KAFKA_REFRESH_REPLAY_MATCHED_EVENT_LIMIT",
"HWLAB_WORKBENCH_KAFKA_REFRESH_REPLAY_LIVE_BUFFER_LIMIT",
];
const missing = required.filter((value) => !text.includes(value));
if (missing.length > 0) throw new Error(`HWLAB domain renderer missing required postprocess/verify refresh contract: ${missing.join(", ")}`);
}
function provenanceFromManifest(manifest: Record<string, unknown>): PacSourceArtifactProvenance | null {
const metadata = isRecord(manifest.metadata) ? manifest.metadata : {};
const annotations = isRecord(metadata.annotations) ? metadata.annotations : {};
const configRef = annotations[provenanceKeys.configRef];
const effectiveConfigSha256 = annotations[provenanceKeys.effectiveConfigSha256];
const renderer = annotations[provenanceKeys.renderer];
if (typeof configRef !== "string" || typeof effectiveConfigSha256 !== "string" || (renderer !== "agentrun-control-plane" && renderer !== "hwlab-runtime-lane")) return null;
return { configRef, effectiveConfigSha256, renderer };
const mode = annotations[provenanceKeys.mode];
if (typeof configRef !== "string" || typeof effectiveConfigSha256 !== "string" || (renderer !== "agentrun-control-plane" && renderer !== "hwlab-runtime-lane") || (mode !== "embedded-pipeline-spec" && mode !== "remote-pipeline-annotation")) return null;
return { configRef, effectiveConfigSha256, renderer, mode };
}
function provenanceEquals(actual: PacSourceArtifactProvenance | null, expected: PacSourceArtifactProvenance): boolean {
return actual !== null
&& actual.configRef === expected.configRef
&& actual.effectiveConfigSha256 === expected.effectiveConfigSha256
&& actual.renderer === expected.renderer;
&& actual.renderer === expected.renderer
&& actual.mode === expected.mode;
}
function firstCanonicalDrift(expected: unknown, actual: unknown, path: string): PacSourceArtifactDrift | null {
@@ -802,8 +965,11 @@ function firstCanonicalDrift(expected: unknown, actual: unknown, path: string):
if (!isRecord(expected) || !isRecord(actual)) return drift(path, expected, actual);
const keys = [...new Set([...Object.keys(expected), ...Object.keys(actual)])].sort();
for (const key of keys) {
if (!Object.prototype.hasOwnProperty.call(expected, key) || !Object.prototype.hasOwnProperty.call(actual, key)) return drift(`${path}.${key}`, expected[key], actual[key]);
const child = firstCanonicalDrift(expected[key], actual[key], `${path}.${key}`);
const expectedOwnsKey = Object.prototype.hasOwnProperty.call(expected, key);
const actualOwnsKey = Object.prototype.hasOwnProperty.call(actual, key);
const childPath = knownDriftPathKeys.has(key) ? `${path}.${key}` : `${path}.[key:${pacValueEvidence(key)}]`;
if (!expectedOwnsKey || !actualOwnsKey) return drift(childPath, expected[key], actual[key]);
const child = firstCanonicalDrift(expected[key], actual[key], childPath);
if (child !== null) return child;
}
return null;
@@ -829,13 +995,29 @@ function isProvenTektonAdmissionDefault(path: readonly (string | number)[], valu
}
function drift(path: string, expected: unknown, actual: unknown): PacSourceArtifactDrift {
return { path, expected: compactValue(expected), actual: compactValue(actual) };
return { path, expected: pacValueEvidence(expected), actual: pacValueEvidence(actual) };
}
function compactValue(value: unknown): string {
const raw = JSON.stringify(value);
if (raw === undefined) return String(value);
return raw.length <= 160 ? raw : `${raw.slice(0, 157)}...`;
export function pacValueEvidence(value: unknown): string {
const type = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
const stable = stableJsonValue(value);
const serialized = JSON.stringify(stable) ?? String(stable);
const length = typeof value === "string"
? Buffer.byteLength(value, "utf8")
: Array.isArray(value)
? value.length
: isRecord(value)
? Object.keys(value).length
: Buffer.byteLength(serialized, "utf8");
return `type=${type},length=${length},sha256=${fingerprint(serialized).replace(/^sha256:/u, "")}`;
}
function fingerprint(value: string): string {
return `sha256:${createHash("sha256").update(value).digest("hex")}`;
}
function formatDrift(value: Record<string, unknown> | null): string {
return value === null ? "-" : `${text(value.path)} expected:${text(value.expected)} actual:${text(value.actual)}`;
}
function unavailableRuntimeObservation(): PacSourceArtifactRuntimeObservation {
@@ -849,8 +1031,8 @@ function parseYamlRecord(text: string, label: string): Record<string, unknown> {
return parsed;
}
function yaml(value: Record<string, unknown>): string {
return `${Bun.YAML.stringify(value).trim()}\n`;
export function pacSourceArtifactYaml(value: Record<string, unknown>): string {
return `${Bun.YAML.stringify(value, null, 2).trim()}\n`;
}
function requiredParam(binding: PacSourceArtifactBinding, key: string): string {
+125 -23
View File
@@ -19,6 +19,8 @@ import {
import { materializeYamlComposition } from "./yaml-composition";
import {
canonicalizePacPipelineSpec,
pacSourceArtifactSafeError,
pacValueEvidence,
parsePacSourceArtifactOptions,
renderPacSourceArtifactResult,
runPacSourceArtifact,
@@ -251,17 +253,26 @@ export async function runPlatformInfraPipelinesAsCodeCommand(config: UniDeskConf
},
repository: {
id: repository.id,
url: repository.url,
cloneUrl: repository.cloneUrl,
owner: repository.owner,
repo: repository.repo,
params: repository.params,
},
};
const result = await runPacSourceArtifact(binding, options, async ({ desiredSpec, provenance, sourceCommit }) => await observeSourceArtifactRuntime(config, target, consumer, desiredSpec, provenance, sourceCommit));
const result = await runPacSourceArtifact(binding, options, async ({ desiredSpec, desiredWrapper, provenance, sourceCommit }) => await observeSourceArtifactRuntime(config, target, consumer, desiredSpec, desiredWrapper, provenance, sourceCommit));
return options.json || options.full ? result : renderPacSourceArtifactResult(result);
} catch (error) {
if (structuredError) throw error;
return sourceArtifactValidationError(error);
const safeError = pacSourceArtifactSafeError(error);
if (structuredError) {
return {
ok: false,
action: "platform-infra-pipelines-as-code-source-artifact-validation",
error: safeError,
next: "Fix the declared owner, exact source identity, worktree proof, or CLI arguments; rerun plan before write.",
};
}
return sourceArtifactValidationError(safeError);
}
}
return { ok: false, error: "unsupported-platform-infra-pipelines-as-code-command", args, help: help() };
@@ -286,12 +297,12 @@ function sourceArtifactHelp(): RenderedCliResult {
return { ok: true, command: "platform-infra-pipelines-as-code-source-artifact-help", renderedText: `${lines.join("\n")}\n`, contentType: "text/plain" };
}
function sourceArtifactValidationError(error: unknown): RenderedCliResult {
const reason = String(error instanceof Error ? error.message : error).replace(/[\r\n]+/gu, " ").slice(0, 1000);
function sourceArtifactValidationError(error: ReturnType<typeof pacSourceArtifactSafeError>): RenderedCliResult {
const details = Object.entries(error.details).map(([key, value]) => `${key}=${value}`).join(" ");
return {
ok: false,
command: "platform-infra-pipelines-as-code-source-artifact-validation",
renderedText: `PAC SOURCE ARTIFACT ERROR\nreason=${reason}\nnext=Fix the explicit target, consumer, source worktree, or owning YAML declaration; rerun plan before write.\n`,
renderedText: `PAC SOURCE ARTIFACT ERROR\ncode=${error.code} evidence=${error.evidence}${details ? ` details=${details}` : ""}\nnext=Fix the declared owner, exact source identity, worktree proof, or CLI arguments; rerun plan before write.\n`,
contentType: "text/plain",
};
}
@@ -442,6 +453,7 @@ function parseSourceArtifact(value: Record<string, unknown>, path: string): PacS
const renderer = y.enumField(value, "renderer", path, ["agentrun-control-plane", "hwlab-runtime-lane"] as const);
const pipelineRunPath = sourceArtifactPath(y.stringField(value, "pipelineRunPath", path), `${path}.pipelineRunPath`);
const pipelinePath = value.pipelinePath === undefined ? null : sourceArtifactPath(y.stringField(value, "pipelinePath", path), `${path}.pipelinePath`);
const taskRunTemplate = y.objectField(value, "taskRunTemplate", path);
if (mode === "embedded-pipeline-spec" && pipelinePath !== null) throw new Error(`${path}.pipelinePath is forbidden for embedded-pipeline-spec`);
if (mode === "remote-pipeline-annotation" && pipelinePath === null) throw new Error(`${path}.pipelinePath is required for remote-pipeline-annotation`);
if (renderer === "agentrun-control-plane" && mode !== "embedded-pipeline-spec") throw new Error(`${path}.renderer agentrun-control-plane requires embedded-pipeline-spec`);
@@ -453,6 +465,11 @@ function parseSourceArtifact(value: Record<string, unknown>, path: string): PacS
pipelineRunPath,
pipelinePath,
maxKeepRuns: positiveInteger(value, "maxKeepRuns", path),
taskRunTemplate: {
hostNetwork: y.booleanField(taskRunTemplate, "hostNetwork", `${path}.taskRunTemplate`),
dnsPolicy: y.enumField(taskRunTemplate, "dnsPolicy", `${path}.taskRunTemplate`, ["ClusterFirst", "ClusterFirstWithHostNet", "Default", "None"] as const),
fsGroup: positiveInteger(taskRunTemplate, "fsGroup", `${path}.taskRunTemplate`),
},
};
}
@@ -466,7 +483,8 @@ async function observeSourceArtifactRuntime(
target: PacTarget,
consumer: PacConsumer,
desiredSpec: Record<string, unknown>,
provenance: { readonly configRef: string; readonly effectiveConfigSha256: string; readonly renderer: string },
desiredWrapper: Record<string, unknown> | null,
provenance: { readonly configRef: string; readonly effectiveConfigSha256: string; readonly renderer: string; readonly mode: string },
sourceCommit: string | null,
): Promise<PacSourceArtifactRuntimeObservation> {
const script = renderPacSourceArtifactRuntimeObserverShell({
@@ -474,24 +492,43 @@ async function observeSourceArtifactRuntime(
pipeline: consumer.pipeline,
pipelineRunPrefix: consumer.pipelineRunPrefix,
desiredSpec: canonicalizePacPipelineSpec(desiredSpec),
desiredWrapper: desiredWrapper === null ? null : canonicalizePacPipelineSpec(desiredWrapper),
provenance,
sourceCommit,
});
const captured = await capture(config, target.route, ["sh"], script);
if (captured.exitCode !== 0) {
const compact = compactCapture(captured);
const detail = String(compact.stderrTail ?? compact.stdoutTail ?? "").trim().split(/\r?\n/u).filter(Boolean)[0]?.replace(/\s+/gu, " ").slice(0, 240);
return unavailableSourceArtifactRuntime(`runtime-transport-exit-${captured.exitCode}${detail ? `:${detail}` : ""}`, sourceCommit);
return unavailableSourceArtifactRuntime(pacRuntimeTransportFailureReason(captured), sourceCommit);
}
const parsed = parseJsonOutput(captured.stdout);
if (parsed === null) return unavailableSourceArtifactRuntime("runtime-observer-invalid-json", sourceCommit);
return {
live: runtimeSourceArtifactItem(parsed.live),
embedded: runtimeSourceArtifactItem(parsed.embedded),
live: pacRuntimeSourceArtifactItem(parsed.live, {
configRef: provenance.configRef,
effectiveConfigSha256: provenance.effectiveConfigSha256,
renderer: provenance.renderer,
mode: provenance.mode,
sourceCommit: null,
exactName: consumer.pipeline,
namePrefix: null,
}),
embedded: pacRuntimeSourceArtifactItem(parsed.embedded, {
configRef: provenance.configRef,
effectiveConfigSha256: provenance.effectiveConfigSha256,
renderer: provenance.renderer,
mode: provenance.mode,
sourceCommit,
exactName: null,
namePrefix: consumer.pipelineRunPrefix,
}),
};
}
export function pacRuntimeTransportFailureReason(captured: { readonly exitCode: number; readonly stdout: string; readonly stderr: string }): string {
return `runtime-transport-exit-${captured.exitCode}:stderr(${pacValueEvidence(captured.stderr)}):stdout(${pacValueEvidence(captured.stdout)})`;
}
export function renderPacSourceArtifactRuntimeObserverShell(inputValue: Record<string, unknown>): string {
const input = JSON.stringify(inputValue);
const observer = readFileSync(sourceArtifactRuntimeObserverFile, "utf8").trimEnd();
@@ -508,32 +545,97 @@ NODE_PAC_SOURCE_ARTIFACT_STATUS
`;
}
function runtimeSourceArtifactItem(value: unknown): PacSourceArtifactRuntimeObservation["live"] {
export function pacRuntimeSourceArtifactItem(value: unknown, expected: {
readonly configRef: string;
readonly effectiveConfigSha256: string;
readonly renderer: string;
readonly mode: string;
readonly sourceCommit: string | null;
readonly exactName: string | null;
readonly namePrefix: string | null;
}): PacSourceArtifactRuntimeObservation["live"] {
if (typeof value !== "object" || value === null || Array.isArray(value)) return unavailableSourceArtifactRuntime("runtime-observer-invalid-item", null).live;
const item = value as Record<string, unknown>;
const status = item.status;
if (status !== "aligned" && status !== "drifted" && status !== "missing" && status !== "unavailable") return unavailableSourceArtifactRuntime("runtime-observer-invalid-status", null).live;
const drift = item.firstDrift;
const observedName = typeof item.name === "string" && item.name.length <= 253 && /^[a-z0-9](?:[-a-z0-9.]*[a-z0-9])?$/u.test(item.name) ? item.name : null;
const name = observedName !== null
&& (expected.exactName === null || observedName === expected.exactName)
&& (expected.namePrefix === null || observedName.startsWith(expected.namePrefix))
? observedName
: null;
const provenanceAligned = item.configRef === expected.configRef
&& item.effectiveConfigSha256 === expected.effectiveConfigSha256
&& item.renderer === expected.renderer
&& item.mode === expected.mode;
return {
status,
name: typeof item.name === "string" ? item.name : null,
canonicalSha256: typeof item.canonicalSha256 === "string" ? item.canonicalSha256 : null,
name,
canonicalSha256: typeof item.canonicalSha256 === "string" && /^sha256:[0-9a-f]{64}$/u.test(item.canonicalSha256) ? item.canonicalSha256 : null,
firstDrift: typeof drift === "object" && drift !== null && !Array.isArray(drift)
? {
path: String((drift as Record<string, unknown>).path ?? "$"),
expected: String((drift as Record<string, unknown>).expected ?? ""),
actual: String((drift as Record<string, unknown>).actual ?? ""),
path: pacRuntimeSafePath((drift as Record<string, unknown>).path),
expected: pacRuntimeSafeEvidence((drift as Record<string, unknown>).expected),
actual: pacRuntimeSafeEvidence((drift as Record<string, unknown>).actual),
}
: null,
provenanceAligned: item.provenanceAligned === true,
configRef: typeof item.configRef === "string" ? item.configRef : null,
effectiveConfigSha256: typeof item.effectiveConfigSha256 === "string" ? item.effectiveConfigSha256 : null,
sourceCommit: typeof item.sourceCommit === "string" ? item.sourceCommit : null,
reason: typeof item.reason === "string" ? item.reason : null,
provenanceAligned,
configRef: item.configRef === expected.configRef && expected.configRef.length <= 512 && /^[A-Za-z0-9_./-]+#[A-Za-z0-9_.-]+$/u.test(expected.configRef) ? expected.configRef : null,
effectiveConfigSha256: item.effectiveConfigSha256 === expected.effectiveConfigSha256 && /^sha256:[0-9a-f]{64}$/u.test(expected.effectiveConfigSha256) ? expected.effectiveConfigSha256 : null,
sourceCommit: item.sourceCommit === expected.sourceCommit && (expected.sourceCommit === null || /^[0-9a-f]{40}$/u.test(expected.sourceCommit)) ? expected.sourceCommit : null,
reason: typeof item.reason === "string" ? pacRuntimeSafeReason(item.reason) : null,
candidateCount: typeof item.candidateCount === "number" && Number.isInteger(item.candidateCount) && item.candidateCount >= 0 ? item.candidateCount : 0,
};
}
export function pacRuntimeSafePath(value: unknown): string {
const path = typeof value === "string" ? value : "$";
const allowedSegments = new Set([
"accessModes", "annotations", "apiVersion", "args", "command", "computeResources", "default", "description", "dnsPolicy", "env", "envFrom",
"executionMode", "finally", "fsGroup", "generateName", "hostNetwork", "image", "kind", "labels", "length", "metadata", "mode", "mountPath", "name", "namespace", "onError", "operator",
"optional", "params", "pipeline", "pipelineRef", "pipelineSpec", "podTemplate", "readinessProbe", "readOnly", "requests", "resources",
"results", "retries", "runAfter", "script", "secret", "secretName", "securityContext", "serviceAccountName", "sidecars", "spec", "steps",
"storage", "subPath", "taskRunTemplate", "taskSpec", "tasks", "timeout", "timeouts", "type", "value", "values", "volumeClaimTemplate",
"volumeMounts", "volumes", "when", "workspaces", "workingDir", "wrapper",
]);
if (!path.startsWith("$")) return `path(${pacValueEvidence(path)})`;
const token = /(?:\.([A-Za-z0-9_-]+))|(?:\[(\d+)\])/gu;
let offset = 1;
for (const match of path.slice(1).matchAll(token)) {
if (match.index !== offset - 1) return `path(${pacValueEvidence(path)})`;
if (match[1] !== undefined && !allowedSegments.has(match[1])) return `path(${pacValueEvidence(path)})`;
offset = 1 + (match.index ?? 0) + match[0].length;
}
return offset === path.length ? path : `path(${pacValueEvidence(path)})`;
}
function pacRuntimeSafeEvidence(value: unknown): string {
return typeof value === "string" && /^type=[a-z]+,length=\d+,sha256=[0-9a-f]{64}$/u.test(value)
? value
: pacValueEvidence(value);
}
export function pacRuntimeSafeReason(value: string): string {
const fixed = new Set([
"source-commit-not-specified",
"source-commit-run-not-found",
"source-commit-identity-conflict",
"pipeline-get-not-found",
"pipelinerun-list-not-found",
"pipelinerun-list-invalid-shape",
"runtime-observer-invalid-json",
"runtime-observer-invalid-item",
"runtime-observer-invalid-status",
"runtime-observer-not-configured",
]);
if (fixed.has(value)) return value;
if (/^source-commit-run-conflict:\d+$/u.test(value)) return value;
if (/^(?:pipeline-get|pipelinerun-list)-command-failed:(?:unknown|\d+):type=string,length=\d+,sha256=[0-9a-f]{64}$/u.test(value)) return value;
if (/^(?:runtime-observer-input-error|runtime-observer-reason):type=string,length=\d+,sha256=[0-9a-f]{64}$/u.test(value)) return value;
return `runtime-observer-reason:${pacValueEvidence(value)}`;
}
function unavailableSourceArtifactRuntime(reason: string, sourceCommit: string | null): PacSourceArtifactRuntimeObservation {
const item = { status: "unavailable" as const, name: null, canonicalSha256: null, firstDrift: null, provenanceAligned: false, configRef: null, effectiveConfigSha256: null, sourceCommit, reason, candidateCount: 0 };
return { live: item, embedded: item };