93 lines
6.3 KiB
TypeScript
93 lines
6.3 KiB
TypeScript
import { readFileSync } from "node:fs";
|
|
import { collectSkillAvailability } from "../src/components/microservices/code-queue/src/skill-availability";
|
|
|
|
type JsonRecord = Record<string, unknown>;
|
|
|
|
function assertCondition(condition: unknown, message: string, detail: unknown = {}): void {
|
|
if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`);
|
|
}
|
|
|
|
function asRecord(value: unknown, label: string): JsonRecord {
|
|
assertCondition(typeof value === "object" && value !== null && !Array.isArray(value), `${label} must be an object`, value);
|
|
return value as JsonRecord;
|
|
}
|
|
|
|
function countOccurrences(haystack: string, needle: string): number {
|
|
return haystack.split(needle).length - 1;
|
|
}
|
|
|
|
const productionManifest = readFileSync("src/components/microservices/k3sctl-adapter/k3s/code-queue.k8s.yaml", "utf8");
|
|
const devManifest = readFileSync("src/components/microservices/k3sctl-adapter/k3s/dev/unidesk-dev-code-queue.k8s.yaml", "utf8");
|
|
const runtimePreflight = readFileSync("src/components/microservices/code-queue/src/runtime-preflight.ts", "utf8");
|
|
const indexSource = readFileSync("src/components/microservices/code-queue/src/index.ts", "utf8");
|
|
const skillModule = readFileSync("src/components/microservices/code-queue/src/skill-availability.ts", "utf8");
|
|
const promptSource = readFileSync("src/components/microservices/code-queue/src/prompts.ts", "utf8");
|
|
const docsReference = readFileSync("docs/reference/code-queue-supervision.md", "utf8");
|
|
const forbiddenPathLiteral = [".ag", "nets/skills"].join("");
|
|
|
|
assertCondition(!productionManifest.includes(forbiddenPathLiteral), "production manifest must not propagate misspelled skills path");
|
|
assertCondition(!devManifest.includes(forbiddenPathLiteral), "dev manifest must not propagate misspelled skills path");
|
|
assertCondition(!promptSource.includes(forbiddenPathLiteral), "runner prompt must not mention misspelled skills path");
|
|
assertCondition(!skillModule.includes(forbiddenPathLiteral), "skill availability implementation must not propagate misspelled skills path literal");
|
|
assertCondition(!docsReference.includes(forbiddenPathLiteral), "reference docs must not propagate misspelled skills path literal");
|
|
assertCondition(countOccurrences(productionManifest, "name: UNIDESK_SKILLS_PATH") === 3, "production read/write/scheduler must set UNIDESK_SKILLS_PATH", {
|
|
count: countOccurrences(productionManifest, "name: UNIDESK_SKILLS_PATH"),
|
|
});
|
|
assertCondition(countOccurrences(productionManifest, "mountPath: /root/.agents/skills") === 3, "production read/write/scheduler must mount skills target", {
|
|
count: countOccurrences(productionManifest, "mountPath: /root/.agents/skills"),
|
|
});
|
|
assertCondition(countOccurrences(productionManifest, "path: /home/ubuntu/.agents/skills") === 3, "production read/write/scheduler must use hostPath source of truth", {
|
|
count: countOccurrences(productionManifest, "path: /home/ubuntu/.agents/skills"),
|
|
});
|
|
assertCondition(countOccurrences(productionManifest, "name: skills-dir") >= 6, "production manifest must define skills-dir mounts and volumes", {
|
|
count: countOccurrences(productionManifest, "name: skills-dir"),
|
|
});
|
|
assertCondition(devManifest.includes("path: /home/ubuntu/.agents/skills"), "dev manifest should keep the same hostPath source of truth");
|
|
|
|
const available = collectSkillAvailability({
|
|
source: "/home/ubuntu/.agents/skills",
|
|
target: "/home/ubuntu/.agents/skills",
|
|
requiredSkills: ["docs-spec", "cli-spec", "frontend-design", "playwright-cli"],
|
|
});
|
|
assertCondition(available.source === "/home/ubuntu/.agents/skills", "skill report must expose source");
|
|
assertCondition(available.target === "/home/ubuntu/.agents/skills", "skill report must expose target");
|
|
assertCondition(Array.isArray(available.requiredSkills) && available.requiredSkills.includes("docs-spec"), "skill report must expose requiredSkills");
|
|
assertCondition(Array.isArray(available.missingSkills), "skill report must expose missingSkills");
|
|
assertCondition(available.valuesPrinted === false, "skill report must declare valuesPrinted=false");
|
|
assertCondition(asRecord(available.pathSpelling, "pathSpelling").forbiddenPathMustNotBeUsed === true, "skill report must flag misspelled path risk without spreading the literal path");
|
|
assertCondition(!JSON.stringify(available).includes(forbiddenPathLiteral), "skill report must not propagate misspelled path literal");
|
|
assertCondition(!JSON.stringify(available).includes("GH_TOKEN"), "skill report must not include secret environment names unrelated to skills");
|
|
|
|
const missing = collectSkillAvailability({
|
|
source: "/home/ubuntu/.agents/skills",
|
|
target: "/path/that/does/not/exist/for-code-queue-skills-test",
|
|
requiredSkills: ["docs-spec", "cli-spec"],
|
|
});
|
|
assertCondition(missing.ok === false, "missing target should fail");
|
|
assertCondition(missing.degraded === true, "missing target should be degraded");
|
|
assertCondition(missing.blocker === "skills-target-missing", "missing target should expose blocker", missing);
|
|
assertCondition(missing.missingSkills.includes("docs-spec") && missing.missingSkills.includes("cli-spec"), "missing target should list required missing skills", missing);
|
|
assertCondition(missing.valuesPrinted === false, "missing report must also declare valuesPrinted=false");
|
|
|
|
assertCondition(runtimePreflight.includes("skills: SkillAvailabilityReport"), "runtime preflight type must include skills report");
|
|
assertCondition(runtimePreflight.includes("collectSkillAvailability"), "runtime preflight must collect skills availability");
|
|
assertCondition(runtimePreflight.includes("skills.ok && ports.codex.ok"), "runtime preflight ok must depend on skills.ok");
|
|
assertCondition(indexSource.includes("const skillsReady = skills.ok === true"), "dev-ready must gate on structured skills ok");
|
|
|
|
process.stdout.write(`${JSON.stringify({
|
|
ok: true,
|
|
checks: [
|
|
"production Code Queue mounts /home/ubuntu/.agents/skills read-only at /root/.agents/skills",
|
|
"skill availability report exposes source, target, requiredSkills, missingSkills, degraded/blocker and valuesPrinted=false",
|
|
"runtime-preflight and dev-ready use the same structured skill report",
|
|
"misspelled skills paths are only surfaced as a forbidden diagnostic risk",
|
|
],
|
|
observedRunner: {
|
|
source: available.source,
|
|
target: available.target,
|
|
ok: available.ok,
|
|
missingSkills: available.missingSkills,
|
|
valuesPrinted: available.valuesPrinted,
|
|
},
|
|
}, null, 2)}\n`);
|