fix: require code queue runner skills target mount

This commit is contained in:
Codex
2026-05-23 23:22:56 +00:00
parent ea39ff1592
commit e62f1c21d4
4 changed files with 42 additions and 50 deletions
@@ -2478,12 +2478,12 @@ function collectSkillsSyncPreflight(): JsonValue {
}
function resolvedRunnerSkillsPath(): string {
return currentSkillAvailability().resolution.runnerEnvValue;
return config.skillsPath;
}
function runnerSkillsBlocker(): Record<string, JsonValue> | null {
const skills = currentSkillAvailability();
if (skills.runnerUsable) return null;
if (skills.contractOk) return null;
const pathSpelling = {
expectedTarget: skills.pathSpelling.expectedTarget,
forbiddenPathChecked: skills.pathSpelling.forbiddenPathChecked,
@@ -2565,7 +2565,7 @@ function collectDevReady(): JsonValue {
const sshSharedReady = existsSync("/root/.ssh") && sshKeyProbe.ok && sshKeyProbe.output.trim().length > 0;
const skills = collectSkillsStatus() as Record<string, JsonValue>;
const skillsSync = collectSkillsSyncPreflight() as Record<string, JsonValue>;
const skillsReady = skills.ok === true || skills.runnerUsable === true;
const skillsReady = skills.contractOk === true;
const runtimePreflight = runtimePreflightJson(collectRuntimePreflight({ includeRemote: false, includePushDryRun: false }));
const ok = missingTools.length === 0 && dockerProbe.ok && composeProbe.ok && workdirExists && dockerSocketExists && codexConfigReady && sshSharedReady && skillsReady;
const value: JsonValue = {
@@ -656,7 +656,7 @@ export function collectRuntimePreflight(options: RuntimePreflightOptions = {}):
};
const pullRequestDelivery = collectPullRequestDeliveryPreflight(options, checkedAt);
return {
ok: skills.runnerUsable && ports.codex.ok && ports.opencode.ok && pullRequestDelivery.ok,
ok: skills.contractOk && ports.codex.ok && ports.opencode.ok && pullRequestDelivery.ok,
checkedAt,
cwd: process.cwd(),
pid: process.pid,
@@ -438,41 +438,33 @@ export function collectSkillAvailability(options: SkillAvailabilityOptions): Ski
const sourceProbe = collectSyncPathReport(source, source === defaultSource, requiredSkills).report;
const targetProbe = collectSyncPathReport(target, target === expectedTarget || normalizedPathText(target) === normalizedPathText(source), requiredSkills).report;
const targetSymlinkToSource = targetProbe.symlink && sameResolvedPath(targetProbe.realPath, sourceProbe.realPath);
const targetHasRequiredSkills = reportHasRequiredSkills(targetProbe);
const sourceHasRequiredSkills = reportHasRequiredSkills(sourceProbe);
const targetHasRequiredSkills = reportHasRequiredSkills(targetProbe);
const targetReadonlyOk = targetProbe.readonly || targetSymlinkToSource || normalizedPathText(target) === normalizedPathText(source);
const targetReady = targetHasRequiredSkills && targetReadonlyOk;
const targetBlocker = targetHasRequiredSkills && targetSymlinkToSource ? null : targetBlockerFor(targetProbe);
const sourceBlocker = sourceBlockerFor(sourceProbe);
const missingBoth = !targetProbe.exists && !sourceProbe.exists;
const resolutionSource: SkillAvailabilityReport["resolvedPathSource"] = forbiddenPathConfigured || missingBoth
const resolutionSource: SkillAvailabilityReport["resolvedPathSource"] = forbiddenPathConfigured || !targetReady
? "missing"
: targetReady
? targetSymlinkToSource ? "target-symlink" : "target"
: sourceHasRequiredSkills
? "source-fallback"
: "missing";
const resolvedPath = resolutionSource === "target" || resolutionSource === "target-symlink"
? target
: resolutionSource === "source-fallback"
? source
: target;
const selectedReport = resolutionSource === "source-fallback" ? sourceProbe : targetProbe;
const runnerUsable = !forbiddenPathConfigured && resolutionSource !== "missing";
const contractOk = runnerUsable && resolutionSource !== "source-fallback" && targetReady && !forbiddenPathExists;
: targetSymlinkToSource ? "target-symlink" : "target";
const resolvedPath = target;
const selectedReport = targetProbe;
const runnerUsable = !forbiddenPathConfigured && targetReady && !forbiddenPathExists;
const contractOk = runnerUsable;
const blocker = forbiddenPathConfigured
? "forbidden-skills-path-configured"
: forbiddenPathExists
? "forbidden-skills-path-present"
: missingBoth
? "skills-source-and-target-missing"
: !runnerUsable
? targetBlocker ?? sourceBlocker ?? "required-skills-missing"
: contractOk
? null
: targetBlocker ?? (forbiddenPathExists ? "forbidden-skills-path-present" : null);
: null;
const degradedReason = contractOk ? null : blocker;
const ok = runnerUsable;
const ok = contractOk;
const missingSkills = selectedReport.missingSkills;
const skills = resolutionSource === "source-fallback" ? sourceProbe.skills : targetProbe.skills;
const skills = targetProbe.skills;
return {
ok,
runnerUsable,
@@ -497,7 +489,7 @@ export function collectSkillAvailability(options: SkillAvailabilityOptions): Ski
passesToRunnerEnv: true,
targetBlocker,
degradedReason,
hostRolloutRequired: runnerUsable && !contractOk,
hostRolloutRequired: sourceHasRequiredSkills && !contractOk,
},
mountPoint: selectedReport.mountPoint,
exists: targetProbe.exists,
@@ -549,8 +541,8 @@ export function collectSkillAvailability(options: SkillAvailabilityOptions): Ski
},
repairHint: contractOk
? null
: runnerUsable
? `Runner can use ${resolvedPath}; restore the read-only ${defaultSource} -> ${expectedTarget} projection so host rollout no longer reports ${degradedReason ?? "skills contract degraded"}.`
: sourceHasRequiredSkills
? `Source ${defaultSource} has the required skills; restore the read-only ${defaultSource} -> ${expectedTarget} projection before starting runners.`
: `Mount ${defaultSource} read-only at ${expectedTarget}, set UNIDESK_SKILLS_PATH=${expectedTarget}, and remove any forbidden skills path spelling.`,
error: selectedReport.error,
valuesPrinted: false,