fix: expose code queue runner skills contract
This commit is contained in:
+130
-4
@@ -6382,6 +6382,118 @@ function compactSkillsSyncStatus(value: unknown, full = false): Record<string, u
|
||||
return compact;
|
||||
}
|
||||
|
||||
function compactStringArray(value: unknown): string[] {
|
||||
return Array.from(new Set(asArray(value).map((item) => String(item ?? "")).filter((item) => item.length > 0)));
|
||||
}
|
||||
|
||||
function firstStringField(...values: unknown[]): string | null {
|
||||
for (const value of values) {
|
||||
if (typeof value === "string" && value.length > 0) return value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function firstNumberField(...values: unknown[]): number | null {
|
||||
for (const value of values) {
|
||||
if (typeof value === "number" && Number.isFinite(value)) return value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function mergeStringArrays(...values: unknown[]): string[] {
|
||||
return Array.from(new Set(values.flatMap((value) => compactStringArray(value))));
|
||||
}
|
||||
|
||||
function compactPrPreflightSkillsContract(
|
||||
skills: Record<string, unknown> | null,
|
||||
skillsSync: Record<string, unknown> | null,
|
||||
existing: Record<string, unknown> | null = null,
|
||||
): Record<string, unknown> | null {
|
||||
if (skills === null && skillsSync === null && existing === null) return null;
|
||||
const resolution = asRecord(skills?.resolution);
|
||||
const sourceReport = asRecord(skillsSync?.source);
|
||||
const targetReport = asRecord(skillsSync?.target);
|
||||
const expected = asRecord(skillsSync?.expected);
|
||||
const counts = asRecord(skillsSync?.counts);
|
||||
const missing = asRecord(skillsSync?.missing);
|
||||
const existingResolution = asRecord(existing?.resolution);
|
||||
const source = firstStringField(existing?.source, skills?.source, sourceReport?.path, expected?.source);
|
||||
const target = firstStringField(existing?.target, skills?.target, targetReport?.path, expected?.target, skills?.path, skills?.resolvedPath);
|
||||
const resolvedPath = firstStringField(existing?.resolvedPath, skills?.resolvedPath, skills?.path, resolution?.resolvedPath, target);
|
||||
const sourceMissingSkills = mergeStringArrays(existing?.sourceMissingSkills, skills?.sourceMissingSkills, missing?.sourceSkills, sourceReport?.missingSkills);
|
||||
const targetMissingSkills = mergeStringArrays(existing?.targetMissingSkills, skills?.targetMissingSkills, missing?.targetSkills, targetReport?.missingSkills);
|
||||
const requiredSkills = mergeStringArrays(existing?.requiredSkills, skills?.requiredSkills, expected?.requiredSkills, sourceReport?.requiredSkills, targetReport?.requiredSkills);
|
||||
const missingSkills = mergeStringArrays(existing?.missingSkills, skills?.missingSkills, targetMissingSkills);
|
||||
const sourceSkillCount = firstNumberField(existing?.sourceSkillCount, skills?.sourceSkillCount, counts?.sourceSkills, sourceReport?.skillCount);
|
||||
const targetSkillCount = firstNumberField(existing?.targetSkillCount, skills?.targetSkillCount, counts?.targetSkills, targetReport?.skillCount, skills?.skillCount);
|
||||
const blocker = firstStringField(existing?.degradedReason, existing?.blocker, skills?.degradedReason, skills?.blocker, skillsSync?.blocker);
|
||||
const contractOk = existing?.ok === true || skills?.contractOk === true || skills?.runnerUsable === true || skills?.ok === true && skillsSync?.ok !== false || skillsSync?.ok === true && skills === null;
|
||||
const degraded = !contractOk;
|
||||
const forbiddenPathBlocker = blocker === "forbidden-skills-path-configured" || blocker === "forbidden-skills-path-present";
|
||||
const sourceHasRequiredSkills = sourceMissingSkills.length === 0
|
||||
&& (sourceReport?.exists === true && sourceReport?.readable === true || typeof sourceSkillCount === "number" && sourceSkillCount > 0);
|
||||
const targetProjectionMissing = !forbiddenPathBlocker
|
||||
&& (
|
||||
blocker === "skills-target-missing"
|
||||
|| blocker === "skills-target-not-directory"
|
||||
|| blocker === "skills-target-permission-failed"
|
||||
|| blocker === "skills-target-not-readonly"
|
||||
|| blocker === "required-target-skills-missing"
|
||||
|| targetMissingSkills.length > 0
|
||||
|| targetReport?.exists === false
|
||||
|| skills?.resolvedPathSource === "missing"
|
||||
);
|
||||
const hostRolloutRequired = existing?.hostRolloutRequired === true
|
||||
|| existingResolution?.hostRolloutRequired === true
|
||||
|| resolution?.hostRolloutRequired === true
|
||||
|| degraded && sourceHasRequiredSkills && targetProjectionMissing;
|
||||
const resolvedPathSource = firstStringField(existing?.resolvedPathSource, skills?.resolvedPathSource, resolution?.resolvedPathSource)
|
||||
?? (targetProjectionMissing ? "missing" : null);
|
||||
const pathSpelling = compactObjectFields(asRecord(existing?.pathSpelling) ?? asRecord(skills?.pathSpelling) ?? asRecord(skillsSync?.pathSpelling), [
|
||||
"expectedTarget",
|
||||
"forbiddenPathChecked",
|
||||
"forbiddenPathExists",
|
||||
"forbiddenPathConfigured",
|
||||
"forbiddenPathRoles",
|
||||
"forbiddenPathMustNotBeUsed",
|
||||
]);
|
||||
const repairHint = firstStringField(existing?.repairHint, skills?.repairHint)
|
||||
?? (degraded
|
||||
? hostRolloutRequired && source !== null && target !== null
|
||||
? `Source ${source} has the required skills; restore the read-only ${source} -> ${target} projection before starting runners.`
|
||||
: source !== null && target !== null
|
||||
? `Mount ${source} read-only at ${target}, set UNIDESK_SKILLS_PATH=${target}, and remove any forbidden skills path spelling.`
|
||||
: "Restore the approved runner skills source/target projection before starting runners."
|
||||
: null);
|
||||
return {
|
||||
ok: contractOk,
|
||||
degraded,
|
||||
source,
|
||||
target,
|
||||
resolvedPath,
|
||||
resolvedPathSource,
|
||||
hostRolloutRequired,
|
||||
degradedReason: blocker,
|
||||
blocker,
|
||||
requiredSkills,
|
||||
missingSkills,
|
||||
sourceSkillCount,
|
||||
targetSkillCount,
|
||||
sourceMissingSkills,
|
||||
targetMissingSkills,
|
||||
sourceHasRequiredSkills,
|
||||
targetProjected: !targetProjectionMissing && !degraded,
|
||||
repairHint,
|
||||
valuesPrinted: false,
|
||||
...(pathSpelling === null ? {} : { pathSpelling }),
|
||||
note: degraded
|
||||
? hostRolloutRequired
|
||||
? "runner skills source is available, but the read-only target projection is missing and needs controlled host rollout repair"
|
||||
: "runner skills target projection contract is degraded; use repairHint before starting runners"
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
function codeQueueDevReady(): unknown {
|
||||
const response = unwrapCodexResponse(coreInternalFetch(codeQueueProxyPath("/api/dev-ready")));
|
||||
const devReady = asRecord(response.body.devReady) ?? {};
|
||||
@@ -6903,7 +7015,19 @@ function compactRecommendedActions(record: Record<string, unknown>): Record<stri
|
||||
}
|
||||
|
||||
function compactPrPreflightCommanderView(record: Record<string, unknown>, options: CodexPrPreflightOptions): Record<string, unknown> {
|
||||
if (options.full) return record;
|
||||
const preflight = asRecord(record.preflight);
|
||||
const normalizedSkillsContract = compactPrPreflightSkillsContract(
|
||||
compactSkillsStatus(record.skills ?? preflight?.skills),
|
||||
compactSkillsSyncStatus(record.skillsSync ?? preflight?.skillsSync, options.full),
|
||||
asRecord(record.skillsContract) ?? asRecord(preflight?.skillsContract),
|
||||
);
|
||||
if (options.full) {
|
||||
return normalizedSkillsContract === null ? record : {
|
||||
...record,
|
||||
skillsContract: normalizedSkillsContract,
|
||||
...(preflight === null ? {} : { preflight: { ...preflight, skillsContract: normalizedSkillsContract } }),
|
||||
};
|
||||
}
|
||||
|
||||
const tokenCoverage = prPreflightTokenCoverage(record);
|
||||
const authBroker = prPreflightAuthBroker(record);
|
||||
@@ -6969,7 +7093,7 @@ function compactPrPreflightCommanderView(record: Record<string, unknown>, option
|
||||
capabilitySource: authBrokerCapability?.source ?? tokenCoverage?.credentialSource ?? null,
|
||||
},
|
||||
},
|
||||
skillsContract: record.skillsContract ?? undefined,
|
||||
skillsContract: normalizedSkillsContract ?? undefined,
|
||||
activeRunnerPrCapability: {
|
||||
scope: activeRunnerDevContainer.scope ?? "current-cli-process",
|
||||
tokenCandidatePresent: activeRunnerTokenCandidatePresent,
|
||||
@@ -7329,6 +7453,7 @@ function compactPrRuntimePreflight(preflight: Record<string, unknown>, options:
|
||||
const githubTransient = githubTransientEvidence(pull);
|
||||
const skillsBlocked = skills !== null && skills.runnerUsable !== true && skills.ok !== true;
|
||||
const skillsContractDegraded = skills !== null && skills.contractOk !== true;
|
||||
const skillsContract = compactPrPreflightSkillsContract(skills, skillsSync);
|
||||
const failureKind = !tokenCoverage.ok
|
||||
? "auth-missing"
|
||||
: skillsBlocked
|
||||
@@ -7375,12 +7500,13 @@ function compactPrRuntimePreflight(preflight: Record<string, unknown>, options:
|
||||
},
|
||||
skills,
|
||||
skillsSync,
|
||||
skillsContract: {
|
||||
skillsContract: skillsContract ?? {
|
||||
ok: !skillsContractDegraded,
|
||||
degraded: skillsContractDegraded,
|
||||
hostRolloutRequired: asRecord(skills?.resolution)?.hostRolloutRequired === true,
|
||||
degradedReason: skills?.degradedReason ?? skills?.blocker ?? skillsSync?.blocker ?? null,
|
||||
note: skillsContractDegraded ? "runner skills are usable through the resolved path, but the read-only target projection still needs host rollout repair" : null,
|
||||
valuesPrinted: false,
|
||||
note: skillsContractDegraded ? "runner skills target projection contract is degraded; use repairHint before starting runners" : null,
|
||||
},
|
||||
tokenCoverage,
|
||||
authBroker: authBrokerNeededStatus(tokenCoverage, authBrokerRuntime, systemGhBinary, unideskGhCli),
|
||||
|
||||
Reference in New Issue
Block a user