import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const rootDir = path.resolve(__dirname, '..', '..'); const boundaryDoc = path.join( rootDir, 'docs', 'superpowers', '计划', '全系统存储治理', '2026-06-17-全系统存储边界矩阵.md', ); const routeDoc = path.join( rootDir, 'docs', 'superpowers', '计划', '全系统存储治理', '2026-06-17-全系统存储执行路线图.md', ); const sourceFiles = { viral: path.join(rootDir, 'src', 'app', 'services', 'viral-analysis.service.ts'), ipOperator: path.join(rootDir, 'src', 'app', 'services', 'ip-operator.service.ts'), assistant: path.join(rootDir, 'src', 'app', 'services', 'assistant.service.ts'), }; const errors = []; const boundaryText = read(boundaryDoc); const routeText = read(routeDoc); assertIncludes(boundaryText, '敏感数据与大对象归档决策', 'Boundary matrix must define sensitive/big-object archival decisions'); assertIncludes(boundaryText, 'viralAnalysis.comment', 'Boundary matrix must cover viral comment child entities'); assertIncludes(boundaryText, 'viralAnalysis.reply', 'Boundary matrix must cover viral reply child entities'); assertIncludes(boundaryText, 'viralAnalysis.transcript', 'Boundary matrix must cover viral transcript child entities'); assertIncludes(boundaryText, 'ipOperator.accountSnapshotWork', 'Boundary matrix must cover account snapshot work child entities'); assertIncludes(boundaryText, 'ipOperator.accountSnapshotComment', 'Boundary matrix must cover account snapshot comment child entities'); assertIncludes(boundaryText, 'ipOperator.planScriptBody', 'Boundary matrix must cover IP operator script body child entities'); assertIncludes(boundaryText, 'ipOperator.planGenerationPreview', 'Boundary matrix must cover IP operator raw preview child entities'); assertIncludes(boundaryText, 'assistantThread', 'Boundary matrix must cover assistant thread storage'); assertIncludes(boundaryText, 'localStorage', 'Boundary matrix must state browser long-term cache boundaries'); assertIncludes(boundaryText, '七牛', 'Boundary matrix must define Qiniu archival placement'); assertIncludes(boundaryText, '决策红线', 'Boundary matrix must include red lines for sensitive data handling'); assertIncludes(routeText, '敏感数据与大对象归档策略复核', 'Route map must track sensitive/big-object policy review as a completed checkpoint'); assertIncludes(routeText, '历史存量数据归档执行', 'Route map must keep real historical data archival execution as a separate remaining checkpoint'); const viralText = read(sourceFiles.viral); assertIncludes(viralText, 'viralAnalysis.comment', 'Viral analysis service must externalize comment samples'); assertIncludes(viralText, 'viralAnalysis.reply', 'Viral analysis service must externalize reply samples'); assertIncludes(viralText, 'viralAnalysis.transcript', 'Viral analysis service must externalize transcripts'); assertIncludes(viralText, 'compactAnalysisForStorage', 'Viral analysis service must compact the main analysis entity'); const ipOperatorText = read(sourceFiles.ipOperator); assertIncludes(ipOperatorText, 'ipOperator.accountSnapshotWork', 'IP operator service must externalize account works'); assertIncludes(ipOperatorText, 'ipOperator.accountSnapshotComment', 'IP operator service must externalize account comments'); assertIncludes(ipOperatorText, 'ipOperator.planScriptBody', 'IP operator service must externalize full script bodies'); assertIncludes(ipOperatorText, 'ipOperator.planGenerationPreview', 'IP operator service must externalize raw generation previews'); const assistantText = read(sourceFiles.assistant); assertIncludes(assistantText, 'assistantThread', 'Assistant service must use a cloud entity for assistant threads'); if (errors.length) { for (const error of errors) console.error(`FAIL ${error}`); process.exit(1); } console.log('Sensitive storage policy validation passed'); function read(filePath) { try { return fs.readFileSync(filePath, 'utf8'); } catch (error) { errors.push(`Cannot read ${path.relative(rootDir, filePath)}: ${error.message}`); return ''; } } function assertIncludes(text, needle, message) { if (!text.includes(needle)) errors.push(message); }