|
|
|
@@ -88,22 +88,22 @@ mkdirSync(fixtureSource, { recursive: true });
|
|
|
|
|
createSkillSet(fixtureSource, ["docs-spec", "cli-spec"]);
|
|
|
|
|
symlinkSync(fixtureSource, fixtureSymlinkTarget, "dir");
|
|
|
|
|
|
|
|
|
|
const sourceFallback = collectSkillAvailability({
|
|
|
|
|
const missingTargetWithSource = collectSkillAvailability({
|
|
|
|
|
source: fixtureSource,
|
|
|
|
|
target: fixtureMissingTarget,
|
|
|
|
|
requiredSkills: ["docs-spec", "cli-spec"],
|
|
|
|
|
});
|
|
|
|
|
assertCondition(sourceFallback.ok === true, "source exists target missing should keep runner usable", sourceFallback);
|
|
|
|
|
assertCondition(sourceFallback.runnerUsable === true, "source fallback should mark runner usable", sourceFallback);
|
|
|
|
|
assertCondition(sourceFallback.contractOk === false, "source fallback should still mark target projection contract degraded", sourceFallback);
|
|
|
|
|
assertCondition(sourceFallback.degraded === true, "source fallback should remain degraded for host rollout", sourceFallback);
|
|
|
|
|
assertCondition(sourceFallback.blocker === "skills-target-missing", "source fallback should preserve target missing degraded reason", sourceFallback);
|
|
|
|
|
assertCondition(sourceFallback.degradedReason === "skills-target-missing", "source fallback should expose bounded degraded reason", sourceFallback);
|
|
|
|
|
assertCondition(sourceFallback.resolvedPath === fixtureSource, "source fallback should resolve to source path", sourceFallback);
|
|
|
|
|
assertCondition(sourceFallback.resolvedPathSource === "source-fallback", "source fallback should expose resolved path source", sourceFallback);
|
|
|
|
|
assertCondition(sourceFallback.skillCount === 2 && sourceFallback.sourceSkillCount === 2 && sourceFallback.targetSkillCount === 0, "source fallback should expose bounded counts", sourceFallback);
|
|
|
|
|
assertCondition(asRecord(sourceFallback.resolution, "sourceFallback.resolution").runnerEnvValue === fixtureSource, "source fallback should pass resolved path to runner env", sourceFallback.resolution);
|
|
|
|
|
assertCondition(asRecord(sourceFallback.resolution, "sourceFallback.resolution").hostRolloutRequired === true, "source fallback should require host rollout repair", sourceFallback.resolution);
|
|
|
|
|
assertCondition(missingTargetWithSource.ok === false, "source exists target missing must keep runner unavailable until target is projected", missingTargetWithSource);
|
|
|
|
|
assertCondition(missingTargetWithSource.runnerUsable === false, "source must not be passed as the runner skills path when target is missing", missingTargetWithSource);
|
|
|
|
|
assertCondition(missingTargetWithSource.contractOk === false, "missing target must mark target projection contract degraded", missingTargetWithSource);
|
|
|
|
|
assertCondition(missingTargetWithSource.degraded === true, "missing target should remain degraded for host rollout", missingTargetWithSource);
|
|
|
|
|
assertCondition(missingTargetWithSource.blocker === "skills-target-missing", "missing target should preserve target missing degraded reason", missingTargetWithSource);
|
|
|
|
|
assertCondition(missingTargetWithSource.degradedReason === "skills-target-missing", "missing target should expose bounded degraded reason", missingTargetWithSource);
|
|
|
|
|
assertCondition(missingTargetWithSource.resolvedPath === fixtureMissingTarget, "missing target should keep runner path at the expected target", missingTargetWithSource);
|
|
|
|
|
assertCondition(missingTargetWithSource.resolvedPathSource === "missing", "missing target should not expose source fallback resolution", missingTargetWithSource);
|
|
|
|
|
assertCondition(missingTargetWithSource.skillCount === 0 && missingTargetWithSource.sourceSkillCount === 2 && missingTargetWithSource.targetSkillCount === 0, "missing target should expose bounded source and target counts", missingTargetWithSource);
|
|
|
|
|
assertCondition(asRecord(missingTargetWithSource.resolution, "missingTargetWithSource.resolution").runnerEnvValue === fixtureMissingTarget, "missing target should not pass source path to runner env", missingTargetWithSource.resolution);
|
|
|
|
|
assertCondition(asRecord(missingTargetWithSource.resolution, "missingTargetWithSource.resolution").hostRolloutRequired === true, "missing target should require host rollout repair", missingTargetWithSource.resolution);
|
|
|
|
|
|
|
|
|
|
const symlinkOk = collectSkillAvailability({
|
|
|
|
|
source: fixtureSource,
|
|
|
|
@@ -139,14 +139,14 @@ const missing = collectSkillAvailability({
|
|
|
|
|
target: "/path/that/does/not/exist/for-code-queue-skills-test",
|
|
|
|
|
requiredSkills: ["docs-spec", "cli-spec"],
|
|
|
|
|
});
|
|
|
|
|
assertCondition(missing.ok === true, "approved source should keep missing-target runner usable");
|
|
|
|
|
assertCondition(missing.runnerUsable === true, "missing target with approved source should expose runner usable");
|
|
|
|
|
assertCondition(missing.ok === false, "approved source must not keep missing-target runner usable");
|
|
|
|
|
assertCondition(missing.runnerUsable === false, "missing target with approved source should expose runner unavailable");
|
|
|
|
|
assertCondition(missing.contractOk === false, "missing target with approved source should expose hostPath contract degraded");
|
|
|
|
|
assertCondition(missing.degraded === true, "missing target should be degraded");
|
|
|
|
|
assertCondition(missing.blocker === "skills-target-missing", "missing target should expose blocker", missing);
|
|
|
|
|
assertCondition(missing.targetMissingSkills.includes("docs-spec") && missing.targetMissingSkills.includes("cli-spec"), "missing target should list target missing skills", missing);
|
|
|
|
|
assertCondition(missing.resolvedPath === "/home/ubuntu/.agents/skills", "missing target should resolve to approved source", missing);
|
|
|
|
|
assertCondition(missing.resolvedPathSource === "source-fallback", "missing target should expose source fallback", missing);
|
|
|
|
|
assertCondition(missing.resolvedPath === "/path/that/does/not/exist/for-code-queue-skills-test", "missing target should keep the configured target path", missing);
|
|
|
|
|
assertCondition(missing.resolvedPathSource === "missing", "missing target should not expose source fallback", missing);
|
|
|
|
|
assertCondition(missing.valuesPrinted === false, "missing report must also declare valuesPrinted=false");
|
|
|
|
|
|
|
|
|
|
const typoTarget = collectSkillAvailability({
|
|
|
|
@@ -203,8 +203,8 @@ assertCondition(runtimePreflight.includes("skills: SkillAvailabilityReport"), "r
|
|
|
|
|
assertCondition(runtimePreflight.includes("skillsSync: SkillSyncPreflightReport"), "runtime preflight type must include skills sync report");
|
|
|
|
|
assertCondition(runtimePreflight.includes("collectSkillAvailability"), "runtime preflight must collect skills availability");
|
|
|
|
|
assertCondition(runtimePreflight.includes("collectSkillSyncPreflight"), "runtime preflight must collect skills sync preflight");
|
|
|
|
|
assertCondition(runtimePreflight.includes("skills.runnerUsable && ports.codex.ok"), "runtime preflight ok must depend on runner usable skills without blocking on host rollout contract drift");
|
|
|
|
|
assertCondition(indexSource.includes("skills.runnerUsable === true"), "dev-ready must gate on structured runner usable skills");
|
|
|
|
|
assertCondition(runtimePreflight.includes("skills.contractOk && ports.codex.ok"), "runtime preflight ok must depend on the read-only target projection contract");
|
|
|
|
|
assertCondition(indexSource.includes("skills.contractOk === true"), "dev-ready must gate on structured target projection contract");
|
|
|
|
|
assertCondition(indexSource.includes("resolvedRunnerSkillsPath"), "runtime must pass resolved skills path to code agents");
|
|
|
|
|
assertCondition(indexSource.includes("runnerSkillsBlocker"), "scheduler must check skills before starting code agents");
|
|
|
|
|
assertCondition(indexSource.includes("task_blocked_by_runner_skills"), "scheduler must emit structured runner skills blockers");
|
|
|
|
@@ -278,7 +278,7 @@ const skillsPreflightTransport = {
|
|
|
|
|
}),
|
|
|
|
|
};
|
|
|
|
|
const defaultPreflightSummary = asRecord(codexPrPreflightQueryForTest(["--remote"], skillsPreflightTransport), "default preflight summary");
|
|
|
|
|
assertCondition(defaultPreflightSummary.failureKind !== "runner-skills-blocker", "source fallback should not classify as runner skills blocker", defaultPreflightSummary);
|
|
|
|
|
assertCondition(defaultPreflightSummary.failureKind === "runner-skills-blocker", "missing target should classify as runner skills blocker even when source exists", defaultPreflightSummary);
|
|
|
|
|
assertCondition(asRecord(defaultPreflightSummary.skillsContract, "defaultPreflightSummary.skillsContract").hostRolloutRequired === true, "default preflight should expose host rollout blocker separately", defaultPreflightSummary);
|
|
|
|
|
assertCondition(defaultPreflightSummary.preflight === undefined, "default PR preflight should omit detailed preflight internals", defaultPreflightSummary);
|
|
|
|
|
assertCondition(asRecord(defaultPreflightSummary.disclosure, "defaultPreflightSummary.disclosure").fullDetailOmitted === true, "default PR preflight should disclose full detail omission", defaultPreflightSummary.disclosure);
|
|
|
|
@@ -288,11 +288,11 @@ const preflightSummary = asRecord(codexPrPreflightQueryForTest(["--remote", "--f
|
|
|
|
|
const preflight = asRecord(preflightSummary.preflight, "preflight");
|
|
|
|
|
const preflightSkills = asRecord(preflight.skills, "preflight.skills");
|
|
|
|
|
const preflightSkillsSync = asRecord(preflight.skillsSync, "preflight.skillsSync");
|
|
|
|
|
assertCondition(preflightSummary.failureKind !== "runner-skills-blocker", "full preflight should keep source fallback out of runner blocker classification", preflightSummary);
|
|
|
|
|
assertCondition(preflightSummary.failureKind === "runner-skills-blocker", "full preflight should classify missing target as runner blocker", preflightSummary);
|
|
|
|
|
assertCondition(asRecord(preflightSummary.skillsContract, "preflightSummary.skillsContract").degradedReason === "skills-target-missing", "full preflight should expose target missing as contract degraded reason", preflightSummary);
|
|
|
|
|
assertCondition(preflightSkills.target === "/path/that/does/not/exist/for-code-queue-skills-test", "full preflight must show skills target", preflightSkills);
|
|
|
|
|
assertCondition(preflightSkills.resolvedPath === "/home/ubuntu/.agents/skills", "full preflight must show resolved source fallback path", preflightSkills);
|
|
|
|
|
assertCondition(preflightSkills.resolvedPathSource === "source-fallback", "full preflight must show source fallback resolution", preflightSkills);
|
|
|
|
|
assertCondition(preflightSkills.resolvedPath === "/path/that/does/not/exist/for-code-queue-skills-test", "full preflight must keep resolved path at the target", preflightSkills);
|
|
|
|
|
assertCondition(preflightSkills.resolvedPathSource === "missing", "full preflight must not show source fallback resolution", preflightSkills);
|
|
|
|
|
assertCondition(preflightSkillsSync.dryRun === true && preflightSkillsSync.mutation === false, "full preflight must show non-mutating skills sync dry-run", preflightSkillsSync);
|
|
|
|
|
assertCondition(asRecord(preflightSkillsSync.counts, "preflight.skillsSync.counts").missingTargetSkills === 2, "full preflight must show missing target count", preflightSkillsSync);
|
|
|
|
|
assertCondition(asRecord(preflightSkillsSync.plannedActions, "preflight.skillsSync.plannedActions").copy === false, "full preflight must show no copy action", preflightSkillsSync);
|
|
|
|
@@ -403,7 +403,7 @@ process.stdout.write(`${JSON.stringify({
|
|
|
|
|
"skill availability report exposes source, target, requiredSkills, missingSkills, version fingerprint/mtime, degraded/blocker and valuesPrinted=false",
|
|
|
|
|
"skills sync dry-run reports source, target, counts, version fingerprint/mtime, missing skills, permission failures, instructions and no-copy actions",
|
|
|
|
|
"scheduler blocks runner startup with structured infra-blocked output when required skills are unavailable",
|
|
|
|
|
"runtime-preflight, dev-ready, health and PR preflight use the same structured skill and sync reports",
|
|
|
|
|
"runtime-preflight, dev-ready, health and PR preflight require the target projection instead of source fallback",
|
|
|
|
|
"default health/preflight summaries expose bounded skills lifecycle evidence and --full expansion",
|
|
|
|
|
"misspelled skills paths are rejected with forbidden-skills-path-configured before generic missing/unapproved path blockers",
|
|
|
|
|
],
|
|
|
|
|