#!/usr/bin/env node /** * PoC H3: 从 staging snapshot 回滚的正确性 * * 场景: * 原 src/foo.ts 内容 = "version-1" * Claude Edit 写入 "version-2-BAD" (模拟坏变更) * PostToolUse hook 同步复制一份到 staging//foo.ts (snapshot) * validator 判定失败 * → 回滚: 从 file-history/ 找回 "version-1" 放回原路径; staging 版本 mv 到 quarantine * * 验证点: * 1. 回滚后 src/foo.ts 完全等于原始内容 (字节级) * 2. UTF-8 BOM 保留 * 3. 二进制文件 (\x00 \xff) 不损坏 * 4. quarantine 版本可审计读回 */ 'use strict'; const fs = require('fs'); const os = require('os'); const path = require('path'); const crypto = require('crypto'); const SANDBOX = path.join(__dirname, '..', '..', 'ai-delivery-pipeline', '_poc-sandbox'); function sha256(p) { return crypto.createHash('sha256').update(fs.readFileSync(p)).digest('hex'); } function scenario(name, originalContent, badContent, isBinary) { const workDir = path.join(SANDBOX, 'h3-' + name); fs.mkdirSync(workDir, { recursive: true }); const srcDir = path.join(workDir, 'src'); const historyDir = path.join(workDir, 'file-history'); const stagingDir = path.join(workDir, 'staging', 'session-x'); const quarantineDir = path.join(workDir, 'quarantine'); [srcDir, historyDir, stagingDir, quarantineDir].forEach(d => fs.mkdirSync(d, { recursive: true })); const srcFile = path.join(srcDir, 'foo.bin'); // Step 1: 原始写入 + file-history 快照 (模拟既有能力) fs.writeFileSync(srcFile, originalContent); const historySnap = path.join(historyDir, 'foo.bin.v1'); fs.copyFileSync(srcFile, historySnap); const originalHash = sha256(srcFile); // Step 2: Claude Edit 写入坏版本 fs.writeFileSync(srcFile, badContent); // Step 3: PostToolUse hook 同步 snapshot 到 staging (模拟) const stagingSnap = path.join(stagingDir, 'foo.bin'); fs.copyFileSync(srcFile, stagingSnap); // Step 4: validator 失败 → 回滚 // 4a. 从 file-history 恢复原文件 fs.copyFileSync(historySnap, srcFile); // 4b. staging 版本 mv 到 quarantine const quarantined = path.join(quarantineDir, 'session-x_foo.bin'); fs.renameSync(stagingSnap, quarantined); // 验证 const restoredHash = sha256(srcFile); const restoredContent = fs.readFileSync(srcFile); const quarantineContent = fs.readFileSync(quarantined); const bytesEqual = Buffer.compare(restoredContent, Buffer.isBuffer(originalContent) ? originalContent : Buffer.from(originalContent)) === 0; const quarantineEqual = Buffer.compare(quarantineContent, Buffer.isBuffer(badContent) ? badContent : Buffer.from(badContent)) === 0; const hashMatch = restoredHash === originalHash; return { scenario: name, isBinary, originalHash, restoredHash, hashMatch, bytesEqual, quarantineEqual, pass: hashMatch && bytesEqual && quarantineEqual, }; } function main() { fs.mkdirSync(SANDBOX, { recursive: true }); const results = [ scenario('text-ascii', 'function foo() { return 1; }\n', 'MALICIOUS\n', false), scenario('text-utf8-bom', '中文内容\n测试', 'BAD\n坏内容', false), scenario('text-lf-crlf-mix', 'line1\r\nline2\nline3\r\n', 'line1\nline2\r\nline3', false), scenario('binary-null-bytes', Buffer.from([0x00, 0xff, 0x7f, 0x80, 0x01, 0xde, 0xad, 0xbe, 0xef]), Buffer.from([0xba, 0xd0, 0xc0, 0xde]), true), scenario('empty-file', '', 'not-empty', false), scenario('large-1mb', Buffer.alloc(1024 * 1024, 0xab), Buffer.alloc(1024 * 1024, 0xcd), true), ]; const pass = results.filter(r => r.pass).length; const report = { hypothesis: 'H3 · staging snapshot 回滚的正确性', platform: os.platform() + ' ' + os.release(), nodeVersion: process.version, timestamp: new Date().toISOString(), totalScenarios: results.length, passedScenarios: pass, results, verdict: { pass: pass === results.length, passRate: (pass / results.length * 100).toFixed(1) + '%' }, recommendation: pass === results.length ? '✅ 回滚逻辑字节级正确, 覆盖 6 种文件类型; 可接入生产 (需加 fsync 保证掉电安全)' : '❌ 存在回滚失败场景, 见 results[].pass', }; fs.writeFileSync(path.join(SANDBOX, 'h3-report.json'), JSON.stringify(report, null, 2), 'utf8'); console.log(JSON.stringify(report, null, 2)); } main();