/* Maintains packages/os/site-kernel-handoff/src/mission/mission-io.ts as an authored site-kernel-handoff authored module so agents can evolve it without rediscovering local boundaries. Do not introduce app-specific runtime composition or deployment behavior into this reusable package source file. RFC-0355: initial mission IO helpers. RFC-1149: migrate ambient filesystem and process IO to the WorkspaceIO port across engine and shared */ import path from "node:path"; import { parse as parseYaml, stringify as stringifyYaml } from "yaml"; import { missionManifestSchema, type MissionManifest } from "@warpgogol/werkstatt-engine/schemas"; import { atomicWriteFile } from "../werkstatt/atomic.ts"; import { resolveMissionDir } from "@warpgogol/werkstatt-engine/kernel"; import { createDefaultIO, type WorkspaceIO } from "@warpgogol/werkstatt-shared/kernel/workspace-io"; export { resolveMissionDir }; const MISSIONS_DIR = "missions"; export async function resolveMissionManifestPath( workspaceRoot: string, missionId: string, io: WorkspaceIO = createDefaultIO().io, ): Promise { return path.join(await resolveMissionDir(workspaceRoot, missionId, io), "mission.yaml"); } export async function readMissionManifest( workspaceRoot: string, missionId: string, io: WorkspaceIO = createDefaultIO().io, ): Promise { const filePath = await resolveMissionManifestPath(workspaceRoot, missionId, io); const raw = await io.readFile(filePath); const parsed = parseYaml(raw); return missionManifestSchema.parse(parsed); } export async function writeMissionManifest( workspaceRoot: string, manifest: MissionManifest, io: WorkspaceIO = createDefaultIO().io, ): Promise { const filePath = await resolveMissionManifestPath(workspaceRoot, manifest.missionId, io); const yaml = stringifyYaml(manifest); await atomicWriteFile(filePath, yaml + "\n"); } export async function missionExists( workspaceRoot: string, missionId: string, io: WorkspaceIO = createDefaultIO().io, ): Promise { return await io.exists(await resolveMissionManifestPath(workspaceRoot, missionId, io)); } export async function createMissionDirectories( workspaceRoot: string, missionId: string, io: WorkspaceIO = createDefaultIO().io, ): Promise { const missionDir = await resolveMissionDir(workspaceRoot, missionId, io); await io.mkdir(path.join(missionDir, "workpiece")); await io.mkdir(path.join(missionDir, "evidence")); } export async function resolveMissionEvidenceDir( workspaceRoot: string, missionId: string, io: WorkspaceIO = createDefaultIO().io, ): Promise { return path.join(await resolveMissionDir(workspaceRoot, missionId, io), "evidence", "axiom"); } export async function listMissionDirs( workspaceRoot: string, systemId?: string, io: WorkspaceIO = createDefaultIO().io, ): Promise { const missionsPath = path.join(workspaceRoot, MISSIONS_DIR); if (!(await io.exists(missionsPath))) return []; const entries = await io.readdir(missionsPath); const dirs = entries.filter((e) => e.isDirectory && e.name !== "archive").map((e) => e.name); if (systemId) { return dirs.filter((d) => d.startsWith(`${systemId}-m`)); } return dirs; }