import fs from "fs"; import path from "path"; import { validateCheckCodeRunReport } from "../checkCodeRunner"; export interface DevelopmentCheckCodeFinalizeInput { root: string; deploymentId: string; sourceSuperTaskId: string; developmentNetwork: string; executionId: string; runId: string; now?: () => string; } export interface DevelopmentCheckCodeFinalizeResult { reportPath: string; handoffPath: string; statusPath: string; documentPath: string; } const SAFE_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,199}$/; const safeId = (value: string, label: string): string => { if (!SAFE_ID.test(value)) throw new Error(`${label} is invalid`); return value; }; const readObject = (filePath: string, label: string): Record => { const value = JSON.parse(fs.readFileSync(filePath, "utf8")) as unknown; if (!value || typeof value !== "object" || Array.isArray(value)) { throw new Error(`${label} must be an object`); } return value as Record; }; const object = (value: unknown): Record => ( value && typeof value === "object" && !Array.isArray(value) ? value as Record : {} ); const writeAtomic = (filePath: string, contents: string): void => { fs.mkdirSync(path.dirname(filePath), { recursive: true }); const temporary = `${filePath}.${process.pid}.${Date.now()}.tmp`; try { fs.writeFileSync(temporary, contents); fs.renameSync(temporary, filePath); } catch (error) { if (fs.existsSync(temporary)) fs.unlinkSync(temporary); throw error; } }; const writeJsonAtomic = (filePath: string, value: unknown): void => { writeAtomic(filePath, JSON.stringify(value, null, 2) + "\n"); }; export const finalizeDevelopmentCheckCode = ( input: DevelopmentCheckCodeFinalizeInput, ): DevelopmentCheckCodeFinalizeResult => { const deploymentId = safeId(input.deploymentId, "deploymentId"); const sourceSuperTaskId = safeId(input.sourceSuperTaskId, "sourceSuperTaskId"); const developmentNetwork = safeId(input.developmentNetwork, "developmentNetwork"); const executionId = safeId(input.executionId, "executionId"); const runId = safeId(input.runId, "CheckCode runId"); const updatedAt = (input.now || (() => new Date().toISOString()))(); if (!Number.isFinite(Date.parse(updatedAt))) throw new Error("Finalize timestamp is invalid"); const taskBase = `scripts/tasks/${deploymentId}`; const reportPath = `scripts/checkcode/runs/${runId}/report.json`; const statusPath = `${taskBase}/status.json`; const handoffPath = `${taskBase}/handoff.json`; const documentPath = `${taskBase}/docs/checkcode-report.md`; const absolute = (relative: string): string => path.join(input.root, ...relative.split("/")); const report = validateCheckCodeRunReport(readObject(absolute(reportPath), "CheckCode report")); if ( report.status !== "succeeded" || report.runId !== runId || report.reportPath !== reportPath ) { throw new Error("Development CheckCode requires the selected canonical succeeded report"); } const status = readObject(absolute(statusPath), "status.json"); const phases = object(status.phases); const networks = object(status.networks); const network = object(networks[developmentNetwork]); if ( status.sourceSuperTaskId !== sourceSuperTaskId || status.developmentNetwork !== developmentNetwork || network.registryExecutionId !== executionId || network.deployStatus !== "deployed_pending_code_audit" ) { throw new Error("Development CheckCode status identity is invalid"); } const nextStatus = { ...status, status: "code_checked", phases: { ...phases, checkCode: { networks: [developmentNetwork], status: "succeeded", report: reportPath, }, }, networks: { ...networks, [developmentNetwork]: { ...network, deployStatus: "deployed_pending_code_audit", checkCodeStatus: "succeeded", checkCodeRunId: runId, checkCodeReportPath: reportPath, updatedAt, }, }, updatedAt, }; const existingHandoff = readObject(absolute(handoffPath), "handoff.json"); const { checkCodeReport: _legacyCheckCodeReport, ...handoff } = existingHandoff; const nextHandoff = { ...handoff, stageKind: "check_code", status: "code_checked", sourceSuperTaskId, deploymentId, developmentNetwork, executionId, networks: [developmentNetwork], development_deployed: true, checkCode: { runId, reportPath, artifactHash: report.artifactHash, }, updatedAt, }; const lines = [ "# CheckCode Report", "", `sourceSuperTaskId: ${sourceSuperTaskId}`, `deploymentId: ${deploymentId}`, `developmentNetwork: ${developmentNetwork}`, `executionId: ${executionId}`, `runId: ${runId}`, `artifactHash: ${report.artifactHash}`, `reportPath: ${reportPath}`, `status: ${report.status}`, `startedAt: ${report.startedAt}`, `completedAt: ${report.completedAt}`, "", "## Commands", ...report.commands.map((command) => ( `- ${command.command} ${command.args.join(" ")} — exit ${command.status}` )), "", ]; writeJsonAtomic(absolute(statusPath), nextStatus); writeJsonAtomic(absolute(handoffPath), nextHandoff); writeAtomic(absolute(documentPath), lines.join("\n")); return { reportPath, handoffPath, statusPath, documentPath }; }; export const runDevelopmentCheckCodeFinalizeCli = ( argv: string[], defaultRoot: string = process.cwd(), ): DevelopmentCheckCodeFinalizeResult => { const values = new Map(); for (let index = 0; index < argv.length; index += 1) { const item = argv[index]; if (!item.startsWith("--")) continue; const equalAt = item.indexOf("="); const key = item.slice(2, equalAt > 2 ? equalAt : undefined); const next = argv[index + 1]; const value = equalAt > 2 ? item.slice(equalAt + 1) : next; if (!value || value.startsWith("--")) throw new Error(`--${key} requires a value`); if (values.has(key)) throw new Error(`--${key} may be provided only once`); values.set(key, value.trim()); if (equalAt < 0) index += 1; } const required = (key: string): string => { const value = values.get(key); if (!value) throw new Error(`--${key} is required`); return value; }; return finalizeDevelopmentCheckCode({ root: path.resolve(values.get("root") || defaultRoot), deploymentId: required("deploymentId"), sourceSuperTaskId: required("sourceSuperTaskId"), developmentNetwork: required("developmentNetwork"), executionId: required("executionId"), runId: required("runId"), }); };