#!/usr/bin/env node const fs = require('fs'); const os = require('os'); const path = require('path'); const { spawnSync } = require('child_process'); const ROOT = path.resolve(__dirname, '..'); function main() { const outputDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tihao-manual-review-label-guide-')); const result = spawnSync(process.execPath, [ 'scripts/manual-review-label-guide.js', '--output', outputDir, '--strict' ], { cwd: ROOT, encoding: 'utf8', shell: false }); if (result.stdout) process.stdout.write(result.stdout); if (result.stderr) process.stderr.write(result.stderr); assert(result.status === 0, 'manual review label guide should pass'); const summaryPath = path.join(outputDir, 'manual-review-label-guide-summary.json'); const reportPath = path.join(outputDir, 'manual-review-label-guide.md'); const csvPath = path.join(outputDir, 'manual-review-label-guide.csv'); assert(fs.existsSync(summaryPath), 'summary should exist'); assert(fs.existsSync(reportPath), 'report should exist'); assert(fs.existsSync(csvPath), 'csv should exist'); const summary = JSON.parse(fs.readFileSync(summaryPath, 'utf8')); const report = fs.readFileSync(reportPath, 'utf8').replace(/^\uFEFF/, ''); const csv = fs.readFileSync(csvPath, 'utf8').replace(/^\uFEFF/, ''); assert(summary.passed === true, 'summary should pass'); assert(summary.directCustomerProof === false, 'guide must not be direct customer proof'); assert(summary.proofLevel === 'smoke_or_local', 'guide should be smoke/local proof only'); assert(summary.positiveLabelCount >= 2, 'guide should include positive labels'); assert(summary.negativeLabelCount >= 5, 'guide should include negative labels'); assert(summary.negativeLabelsRequireAttribution === true, 'negative labels should require attribution'); assert(summary.attributionTypeCount >= 8, 'guide should include attribution types'); assert(summary.labels.some(item => item.label === '可直接发客户' && item.category === 'positive'), 'positive direct-send label should exist'); assert(summary.labels.some(item => item.label === '跑偏' && item.requiresAttribution), 'negative off-target label should require attribution'); assert(summary.attributionTypes.some(item => item.type === '视频证据误判'), 'video attribution type should exist'); assert(summary.customerEffectAuditRules.some(item => item.includes('customer-effect-summary.json')), 'customer effect audit boundary should be explicit'); assert(report.includes('# 人工复核标签与归因填写指南'), 'report should be Chinese guide'); assert(report.includes('## 人工复核标签'), 'report should include label section'); assert(report.includes('## 负样本归因类型'), 'report should include attribution section'); assert(report.includes('不证明命中率提升'), 'report should not overclaim hit-rate lift'); assert(csv.includes('可直接发客户'), 'csv should include positive label'); assert(csv.includes('视频证据误判'), 'csv should include attribution type'); assert(!/sk-[A-Za-z0-9_-]{10,}/.test(report + csv), 'outputs should not contain model token patterns'); assert(!/r:[A-Za-z0-9]{20,}/.test(report + csv), 'outputs should not contain Parse sessionToken patterns'); assert(!/Authorization/i.test(report + csv), 'outputs should not expose Authorization text'); console.log(JSON.stringify({ ok: true, outputDir, labelCount: summary.labelCount, attributionTypeCount: summary.attributionTypeCount }, null, 2)); } function assert(condition, message) { if (!condition) throw new Error(message); } main();