/*
RFC-0840: Persist and restore root-level operator config files between mission workpiece and cache clone.
Provides persistOperatorConfigFiles (mission.close) and restoreOperatorConfigFiles (mission.materialize).
Follows the same pattern as env-persist.ts (RFC-0822) but for non-env operator config files.
- Do not git-commit operator config files — they are untracked artifacts in the cache clone.
- Do not modify file contents during restore — unlike env restoration which rewrites PUBLIC_IMAGE_PROVIDER.
- Do not add generated build outputs to OPERATOR_CONFIG_FILES — those are regenerated by build.prepare.
- Do not add .env* files — those are already handled by RFC-0822.
- RFC-0840: initial operator-config-files module with OPERATOR_CONFIG_FILES, persistOperatorConfigFiles, and restoreOperatorConfigFiles.
- warpgogol-m000170: OPTIONAL_OPERATOR_CONFIG_FILES — src/image-variants.config.yaml persists/restores like the required entries but stays opt-in (presence check + MAT-CONFIG-02 keep covering only the required list).
*/
import path from "node:path";
import { createDefaultIO, type WorkspaceIO } from "@warpgogol/werkstatt-shared/kernel/workspace-io";
export interface OperatorConfigPersistResult {
copied: string[];
skipped: string[];
}
export const OPERATOR_CONFIG_FILES = [
".lighthouse-budget-ignore",
"src/image-delivery.config.yaml",
] as const;
/**
* m000170: optional operator configs — persisted/restored like
* OPERATOR_CONFIG_FILES when present, but NOT required by
* workpiece.config.presence.check (a site may simply never need the knob).
* Excluded from MAT-CONFIG-02's dead-entry scan for the same reason.
*/
export const OPTIONAL_OPERATOR_CONFIG_FILES = ["src/image-variants.config.yaml"] as const;
const ALL_OPERATOR_CONFIG_FILES: readonly string[] = [
...OPERATOR_CONFIG_FILES,
...OPTIONAL_OPERATOR_CONFIG_FILES,
];
export async function persistOperatorConfigFiles(
workpieceDir: string,
cacheCloneDir: string,
io: WorkspaceIO = createDefaultIO().io,
): Promise {
const copied: string[] = [];
const skipped: string[] = [];
for (const entry of ALL_OPERATOR_CONFIG_FILES) {
const src = path.join(workpieceDir, entry);
const dest = path.join(cacheCloneDir, entry);
if (!(await io.exists(src))) continue;
try {
await io.mkdir(path.dirname(dest));
await io.copyFile(src, dest);
copied.push(entry);
} catch {
skipped.push(entry);
}
}
return { copied, skipped };
}
export async function restoreOperatorConfigFiles(
cacheCloneDir: string,
workpieceDir: string,
io: WorkspaceIO = createDefaultIO().io,
): Promise {
const copied: string[] = [];
const skipped: string[] = [];
for (const entry of ALL_OPERATOR_CONFIG_FILES) {
const src = path.join(cacheCloneDir, entry);
const dest = path.join(workpieceDir, entry);
if (!(await io.exists(src))) continue;
try {
await io.mkdir(path.dirname(dest));
await io.copyFile(src, dest);
copied.push(entry);
} catch {
skipped.push(entry);
}
}
return { copied, skipped };
}