// Evidence serialization only. Root supplies a freshly observed Sky state. globalThis.captureTraceability = async function(name, note, currentState) { if (!/^[a-zA-Z0-9_-]+$/.test(name)) throw Error('Invalid capture name'); if (!currentState?.screenshots?.length) throw Error('Fresh screenshot required'); const {fileURLToPath} = await import('node:url'); const root = fileURLToPath(new URL('.', import.meta.url)).replaceAll('\\','/').replace(/\/$/,''); const run = globalThis.traceabilityRunRoot; if (!run || !run.startsWith(root+'/runs/run-')) throw Error('Bind current run first'); const file = await import('node:fs/promises'); const crypto = await import('node:crypto'); const hash = bytes => crypto.createHash('sha256').update(bytes).digest('hex'); const launchBytes = await file.readFile(run+'/launch.json'); const launch = JSON.parse(launchBytes.toString('utf8').replace(/^\uFEFF/,'')); const manifestBytes = await file.readFile(root+'/input-manifest.json'); if (hash(manifestBytes) !== launch.manifest_sha256) throw Error('Manifest changed'); const manifest = JSON.parse(manifestBytes.toString('utf8').replace(/^\uFEFF/,'')); const saved = manifest.saved_working_path; const savedExists = await file.access(saved).then(()=>true,()=>false); const workingPath = savedExists ? saved : manifest.initial_working.path; const working = await file.readFile(workingPath); const dir = run+'/captures'; await file.mkdir(dir,{recursive:true}); const images=[]; for (let i=0;i