/* resolver — resolve component artifacts into the runtime artifact index for activation. Do not activate — resolution produces the artifact index only. RFC-1097: sweep — werkstatt-engine clean Sweep batch 4: 73 Compass headers on headerless engine files (certification, component-runtime, isolation, evolution, testing), real KEY_DECISIONS on 75 files (kernel, cache, dht, swim, gitmesh, runtime), ~80 purpose expansions (CONTRACT-02/PURPOSE-02), non-goals on 13 CONTRACT-03 files, CS-07 history literal fix repo-wide (253 files). Policy: .template.ts/.template.astro excludedPaths. werkstatt-engine now 0 diagnostics. */ import type { ComponentDeclaration, ComponentId, CapabilityId, CapabilityProvideV1, ResolvedComponentIdentityV1, ResolvedComponentSetV1, } from "@warpgogol/werkstatt-shared/component"; import type { DesiredState } from "@warpgogol/werkstatt-shared/kernel"; import { parseComponentDeclaration } from "../component/schemas.ts"; import { computeSetHash, computeDependencyGraphHash, computeGrantSetHash, computeEffectPolicyHash, computeIsolationPolicyHash, } from "../component/identity.ts"; import type { Sha256Digest } from "@warpgogol/werkstatt-shared/fingerprint"; import type { ResolutionViolationV1, ResolutionProofV1 } from "./resolution-proof.ts"; import { createResolutionProof } from "./resolution-proof.ts"; export interface ComponentArtifactIndexV1 { readonly artifacts: ReadonlyMap; } export interface AdmittedGrantSetV1 { readonly admitted: ReadonlyArray<{ scope: string; resource: string }>; } export interface ResolveOptions { readonly desiredState: DesiredState; readonly effectPolicyHash?: string; readonly isolationPolicyHash?: string; } export type ResolutionResultV1 = | { status: "resolved"; set: ResolvedComponentSetV1; proof: ResolutionProofV1 } | { status: "blocked"; violations: ResolutionViolationV1[] }; interface Edge { from: ComponentId; to: ComponentId; } function semverSatisfies(version: string, compatibility: string): boolean { const vParts = version.split(".").map(Number); if (vParts.length !== 3 || vParts.some((n) => Number.isNaN(n))) { return false; } const [vMajor, vMinor, vPatch] = vParts; const trimmed = compatibility.trim(); if (trimmed.startsWith("^")) { const reqParts = trimmed.slice(1).split(".").map(Number); if (reqParts.length !== 3 || reqParts.some((n) => Number.isNaN(n))) { return false; } const [rMajor, rMinor, rPatch] = reqParts; if (rMajor !== vMajor) return false; if (vMinor < rMinor) return false; if (vMinor === rMinor && vPatch < rPatch) return false; return true; } if (trimmed.startsWith("~")) { const reqParts = trimmed.slice(1).split(".").map(Number); if (reqParts.length !== 3 || reqParts.some((n) => Number.isNaN(n))) { return false; } const [rMajor, rMinor] = reqParts; return rMajor === vMajor && rMinor === vMinor; } if (/^\d+\.\d+\.\d+$/.test(trimmed)) { return version === trimmed; } return false; } function findProviders( capability: CapabilityId, manifests: readonly ComponentDeclaration[], ): Array<{ manifest: ComponentDeclaration; provide: CapabilityProvideV1 }> { const providers: Array<{ manifest: ComponentDeclaration; provide: CapabilityProvideV1 }> = []; for (const m of manifests) { for (const p of m.provides) { if (p.capability === capability) { providers.push({ manifest: m, provide: p }); } } } return providers; } function detectCycle( edges: readonly Edge[], componentIds: readonly ComponentId[], ): ComponentId[] | null { const adj = new Map(); for (const id of componentIds) { adj.set(id, []); } for (const e of edges) { adj.get(e.from)?.push(e.to); } const WHITE = 0, GRAY = 1, BLACK = 2; const color = new Map(); for (const id of componentIds) { color.set(id, WHITE); } let cyclePath: ComponentId[] | null = null; function dfs(u: ComponentId, path: ComponentId[]): boolean { color.set(u, GRAY); path.push(u); for (const v of adj.get(u) ?? []) { if (color.get(v) === GRAY) { const idx = path.indexOf(v); cyclePath = path.slice(idx).concat(v); return true; } if (color.get(v) === WHITE) { if (dfs(v, path)) return true; } } path.pop(); color.set(u, BLACK); return false; } for (const id of componentIds) { if (color.get(id) === WHITE) { if (dfs(id, [])) return cyclePath; } } return null; } function topologicalSort( edges: readonly Edge[], componentIds: readonly ComponentId[], ): { order: ComponentId[] | null; maxDepth: number } { const inDegree = new Map(); for (const id of componentIds) { inDegree.set(id, 0); } for (const e of edges) { inDegree.set(e.to, (inDegree.get(e.to) ?? 0) + 1); } const adj = new Map(); for (const id of componentIds) { adj.set(id, []); } for (const e of edges) { adj.get(e.from)?.push(e.to); } const queue: ComponentId[] = componentIds.filter((id) => (inDegree.get(id) ?? 0) === 0).sort(); const order: ComponentId[] = []; const depth = new Map(); for (const id of queue) { depth.set(id, 0); } while (queue.length > 0) { const u = queue.shift()!; order.push(u); const neighbors = (adj.get(u) ?? []).sort(); for (const v of neighbors) { const newDeg = (inDegree.get(v) ?? 1) - 1; inDegree.set(v, newDeg); const d = Math.max(depth.get(v) ?? 0, (depth.get(u) ?? 0) + 1); depth.set(v, d); if (newDeg === 0) { queue.push(v); queue.sort(); } } } if (order.length !== componentIds.length) { return { order: null, maxDepth: 0 }; } const maxDepth = Math.max(...Array.from(depth.values()), 0); return { order, maxDepth }; } export function resolve(options: ResolveOptions): ResolutionResultV1 { const { desiredState } = options; const desired = [...desiredState.components.values()]; const violations: ResolutionViolationV1[] = []; // 1. Validate every manifest for (const manifest of desired) { const result = parseComponentDeclaration(manifest); if (result.status === "fail") { for (const v of result.violations) { violations.push({ code: "RESOLUTION-01", componentId: manifest.componentId, capability: null, message: `manifest validation failed: ${v.rule} — ${v.message}`, }); } } } if (violations.length > 0) { return { status: "blocked", violations }; } // 2. Check artifact availability for (const manifest of desired) { const available = desiredState.availableArtifacts.get(manifest.componentId); if (!available) { violations.push({ code: "RESOLUTION-02", componentId: manifest.componentId, capability: null, message: `artifact not available for ${manifest.componentId}`, }); } else if (available !== manifest.artifactHash) { violations.push({ code: "RESOLUTION-02", componentId: manifest.componentId, capability: null, message: `artifact hash mismatch for ${manifest.componentId}: expected ${manifest.artifactHash}, got ${available}`, }); } } if (violations.length > 0) { return { status: "blocked", violations }; } // 3. Check admitted grants const admittedSet = new Set(desiredState.admittedGrants.map((g) => `${g.scope}:${g.resource}`)); for (const manifest of desired) { for (const grant of manifest.requestedGrants) { const key = `${grant.scope}:${grant.resource}`; if (!admittedSet.has(key)) { violations.push({ code: "RESOLUTION-03", componentId: manifest.componentId, capability: null, message: `grant not admitted: ${key} for ${manifest.componentId}`, }); } } } if (violations.length > 0) { return { status: "blocked", violations }; } // 4. Match required capabilities const edges: Edge[] = []; for (const manifest of desired) { for (const req of manifest.requires) { const providers = findProviders(req.capability, desired); if (providers.length === 0 && !req.optional) { violations.push({ code: "RESOLUTION-04", componentId: manifest.componentId, capability: req.capability, message: `no provider for required capability ${req.capability} required by ${manifest.componentId}`, }); continue; } if (providers.length === 0 && req.optional) { continue; } const compatible = providers.filter( (p) => semverSatisfies(p.provide.version, req.compatibility) && p.provide.schemaHash === req.schemaHash, ); if (compatible.length === 0) { if (!req.optional) { violations.push({ code: "RESOLUTION-05", componentId: manifest.componentId, capability: req.capability, message: `no compatible provider for ${req.capability} (compatibility ${req.compatibility}) required by ${manifest.componentId}`, }); } continue; } if (compatible.length > 1) { violations.push({ code: "RESOLUTION-06", componentId: manifest.componentId, capability: req.capability, message: `multiple compatible providers for ${req.capability}: ${compatible.map((c) => c.manifest.componentId).join(", ")}`, }); continue; } const provider = compatible[0]!; if (provider.manifest.componentId !== manifest.componentId) { edges.push({ from: manifest.componentId, to: provider.manifest.componentId }); } } } if (violations.length > 0) { return { status: "blocked", violations }; } // 5. Detect cycles const componentIds = desired.map((m) => m.componentId); const cycle = detectCycle(edges, componentIds); if (cycle) { violations.push({ code: "RESOLUTION-07", componentId: cycle[0] ?? null, capability: null, message: `dependency cycle detected: ${cycle.join(" -> ")}`, cyclePath: cycle, }); return { status: "blocked", violations }; } // 6. Topological sort with canonical tie-breaking const { order, maxDepth } = topologicalSort(edges, componentIds); if (!order) { violations.push({ code: "RESOLUTION-07", componentId: null, capability: null, message: "topological sort failed (cycle or missing component)", }); return { status: "blocked", violations }; } // 7. Build resolved set const manifestMap = new Map( componentIds.map((id) => { const m = desired.find((d) => d.componentId === id)!; return [id, m] as const; }), ); const resolvedComponents: ResolvedComponentIdentityV1[] = order.map((id) => { const m = manifestMap.get(id)!; return { componentId: m.componentId, version: m.version, artifactHash: m.artifactHash, }; }); const dependencyGraphHash = computeDependencyGraphHash( edges.map((e) => ({ from: e.from, to: e.to })), ); const grantSetHash = computeGrantSetHash( desired.flatMap((m) => m.requestedGrants.map((g) => ({ componentId: m.componentId, scope: g.scope, resource: g.resource, attenuated: g.attenuated, })), ), ); const effectPolicyHash = computeEffectPolicyHash( desired.flatMap((m) => m.effects.map((e) => ({ componentId: m.componentId, effectClass: e.effectClass, })), ), ); const isolationPolicyHash = computeIsolationPolicyHash( desired.map((m) => ({ componentId: m.componentId, tier: m.isolation.tier, adapterId: m.isolation.adapterId, })), ); const setWithoutHash: Omit = { schema: "werkstatt/resolved-component-set@1", profileId: desiredState.profileId, components: resolvedComponents, dependencyGraphHash, grantSetHash, effectPolicyHash: options.effectPolicyHash || effectPolicyHash, isolationPolicyHash: options.isolationPolicyHash || isolationPolicyHash, }; const setHash = computeSetHash(setWithoutHash); const set: ResolvedComponentSetV1 = { ...setWithoutHash, setHash }; const proof = createResolutionProof(desiredState.profileId, set, edges.length, maxDepth); return { status: "resolved", set, proof }; }