/* RFC-0479: bootstrapping migrator — transforms system.pin.json migratorCursor from SemVer string to string[] (migrator-id list). This is the first migrator in the registry and the only one that touches the pin file itself. Do not migrate content files — only the pin file cursor format. RFC-0479: initial bootstrapping migrator. */ import path from "node:path"; import type { Migrator, SternsystemData, MigrationContext } from "./types.ts"; import { createDefaultIO, type WorkspaceIO } from "@warpgogol/werkstatt-shared/kernel/workspace-io"; export const RFC_0479_MIGRATOR_ID = "rfc-0479"; async function transformPinCursor( data: SternsystemData, ctx: MigrationContext, io: WorkspaceIO = createDefaultIO().io, ): Promise { const pinPath = path.join(data.rootPath, "system.pin.json"); let raw: string; try { raw = await io.readFile(pinPath); } catch { ctx.logger.info(`[migrator rfc-0479] no system.pin.json found — nothing to migrate`); return data; } const json = JSON.parse(raw) as Record; const cursor = json.migratorCursor; if (Array.isArray(cursor)) { ctx.logger.info(`[migrator rfc-0479] migratorCursor already string[] — no-op`); return data; } if (typeof cursor === "string") { json.migratorCursor = [RFC_0479_MIGRATOR_ID]; await io.writeFile(pinPath, JSON.stringify(json, null, 2) + "\n"); ctx.logger.info( `[migrator rfc-0479] transformed migratorCursor from "${cursor}" to ["${RFC_0479_MIGRATOR_ID}"]`, ); return data; } json.migratorCursor = [RFC_0479_MIGRATOR_ID]; await io.writeFile(pinPath, JSON.stringify(json, null, 2) + "\n"); ctx.logger.info( `[migrator rfc-0479] migratorCursor was ${cursor === null ? "null" : typeof cursor} — set to ["${RFC_0479_MIGRATOR_ID}"]`, ); return data; } export const rfc0479Migrator: Migrator = { id: RFC_0479_MIGRATOR_ID, fromVersion: "4.5.0", toVersion: "4.6.0", description: "Bootstrapping migrator: pin migratorCursor string → string[]", transform: async (data, ctx) => { return transformPinCursor(data, ctx); }, };