/* 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 }; }