/*
RFC-0221 §4 + §6: build the catch-up report from a lock and the recipient's
current ecosystem — the pure core of handoff.absorb, independent of filesystem writes.
- Do not read files or apply migrators — inputs are already resolved.
- RFC-0221: initial pure report builder.
*/
import type { HandoffLock } from "@warpgogol/werkstatt-engine/schemas";
import { diffCapabilities, worstTier } from "./capability-diff.ts";
import { migratorsToApply } from "../migrators/registry.ts";
import type { CatchupReport, CatchupTier, RegistryView } from "./types.ts";
import { compareEcosystem } from "./version-compare.ts";
export interface BuildReportInput {
lock: HandoffLock;
currentVersion: string;
currentPackagesHash: string;
/** RFC-0364: semantic fingerprint of the recipient's packages/ tree. */
currentPlatformSemanticHash?: string;
registry: RegistryView;
}
function migratorTier(): CatchupTier {
return "yellow";
}
export function buildCatchupReport(input: BuildReportInput): CatchupReport {
const { lock, currentVersion, currentPackagesHash, currentPlatformSemanticHash, registry } =
input;
const comparison = compareEcosystem({
sourceVersion: lock.ecosystem.version,
currentVersion,
sourcePackagesHash: lock.ecosystem.packagesHash ?? "",
currentPackagesHash,
sourcePlatformSemanticHash: lock.ecosystem.platformSemanticHash,
currentPlatformSemanticHash,
});
// A downgrade is refused before any migration is considered.
if (comparison.verdict === "refuse-downgrade") {
return {
app: lock.app,
comparison,
migratorChain: [],
capabilityDiff: [],
overallTier: "red",
};
}
const chain = migratorsToApply(lock.migratorCursor);
const migratorChain = chain.map((m) => ({ id: m.id, description: m.description }));
const capabilityDiff = diffCapabilities(lock.capabilities, registry, chain);
const overallTier = worstTier([
...capabilityDiff.map((d) => d.tier),
...migratorChain.map(() => migratorTier()),
]);
return { app: lock.app, comparison, migratorChain, capabilityDiff, overallTier };
}