| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- #!/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 tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'tihao-proof-gap-'));
- const handoff = path.join(tmp, 'optimization-handoff-summary.json');
- const evidence = path.join(tmp, 'evidence-index-summary.json');
- const videoReadiness = path.join(tmp, 'video-resource-readiness-summary.json');
- const closure = path.join(tmp, 'proof-gap-closure-summary.json');
- const output = path.join(tmp, 'proof-gap-output');
- fs.writeFileSync(handoff, JSON.stringify({
- complete: false,
- statusCounts: { passed: 22, ready_not_proven: 2, blocked_by_external_data: 2 },
- evidenceCounts: { real_evidence: 5, smoke_or_local: 22, not_business_proof: 85 },
- readyNotProven: [{ id: 'video-ab-proof' }, { id: 'historical-dataset' }],
- externalBlockers: [{ id: 'live-provider-overnight-proof' }, { id: 'customer-effect-proof' }]
- }, null, 2), 'utf8');
- fs.writeFileSync(evidence, JSON.stringify({
- counts: { real_evidence: 5, smoke_or_local: 22, not_business_proof: 85 }
- }, null, 2), 'utf8');
- fs.writeFileSync(videoReadiness, JSON.stringify({
- acceptance: { readyForVideoAbPreflight: false },
- failureCount: 1,
- counts: { realReferenceRows: 1, realCandidateRows: 0, videoUrlRows: 1 }
- }, null, 2), 'utf8');
- fs.writeFileSync(closure, JSON.stringify({
- complete: false,
- openCount: 5,
- closedCount: 0
- }, null, 2), 'utf8');
- const result = spawnSync(process.execPath, [
- path.join(root, 'scripts', 'generate-proof-gap-request.js'),
- '--handoff',
- handoff,
- '--evidence',
- evidence,
- '--video-readiness',
- videoReadiness,
- '--closure',
- closure,
- '--output',
- output
- ], { cwd: root, encoding: 'utf8' });
- if (result.status !== 0) {
- process.stderr.write(result.stderr || result.stdout || '');
- throw new Error('generate-proof-gap-request should pass');
- }
- const summary = JSON.parse(fs.readFileSync(path.join(output, 'proof-gap-request-summary.json'), 'utf8'));
- const report = fs.readFileSync(path.join(output, 'proof-gap-request-report.md'), 'utf8').replace(/^\uFEFF/, '');
- const csv = fs.readFileSync(path.join(output, 'proof-gap-request-table.csv'), 'utf8').replace(/^\uFEFF/, '');
- assert(summary.complete === false, 'proof gap request must not mark long-term goal complete');
- assert(summary.unresolvedCount >= 4, 'proof gap request should list unresolved proof gaps');
- assert(summary.rows.some(row => row.id === 'video-real-candidate' && row.status === 'missing_real_data'), 'should request real candidate video resources');
- assert(summary.rows.some(row => row.id === 'manual-review-and-customer-effect'), 'should request customer effect proof data');
- assert(summary.rows.every(row => row.templateCommand && row.templateArtifact), 'every proof gap row should include template command and artifact');
- assert(summary.rows.some(row => row.id === 'historical-dataset' && row.templateCommand.includes('data:intake-template')), 'historical dataset gap should point to data intake template');
- assert(summary.rows.some(row => row.id === 'video-real-candidate' && row.templateCommand.includes('video:intake-template')), 'video gap should point to video intake template');
- assert(summary.observed.proofGapClosureOpenCount === 5, 'proof gap request should expose latest closure open count');
- assert(summary.rows.every(row => row.owner !== '技术/投放' && row.owner !== '商务/客户' && row.owner !== '投放/商务'), 'owner labels should be normalized');
- assert(report.includes('# 提号真实证明缺口请求包'), 'report should be Chinese');
- assert(report.includes('模板命令') && report.includes('模板产物'), 'report should expose intake template columns');
- assert(report.includes('closure.openCount:5'), 'report should include closure open count');
- assert(csv.startsWith('缺口ID,负责人,状态'), 'CSV should have business-facing Chinese header');
- assert(csv.includes('模板命令') && csv.includes('模板产物'), 'CSV should expose intake template columns');
- assert(!/(sk-[A-Za-z0-9_-]{20,}|r:[A-Za-z0-9]{20,}|Authorization\s*[:=]\s*Bearer)/i.test(report + csv), 'proof gap request should not contain secrets');
- fs.writeFileSync(videoReadiness, JSON.stringify({
- acceptance: { readyForVideoAbPreflight: true },
- failureCount: 0,
- counts: { realReferenceRows: 1, realCandidateRows: 1, videoUrlRows: 2 }
- }, null, 2), 'utf8');
- fs.writeFileSync(closure, JSON.stringify({
- complete: false,
- openCount: 3,
- closedCount: 2,
- rows: [
- { id: 'video-real-candidate', status: 'closed', evidence: 'realReference=1, realCandidate=1' },
- { id: 'live-provider-overnight-proof', status: 'closed', evidence: 'liveEnabled=true, overallPass=true' },
- { id: 'historical-dataset', status: 'open' },
- { id: 'video-ab-live-proof', status: 'open' },
- { id: 'manual-review-and-customer-effect', status: 'open' }
- ]
- }, null, 2), 'utf8');
- const closureAwareOutput = path.join(tmp, 'proof-gap-output-closure-aware');
- const closureAware = spawnSync(process.execPath, [
- path.join(root, 'scripts', 'generate-proof-gap-request.js'),
- '--handoff',
- handoff,
- '--evidence',
- evidence,
- '--video-readiness',
- videoReadiness,
- '--closure',
- closure,
- '--output',
- closureAwareOutput
- ], { cwd: root, encoding: 'utf8' });
- if (closureAware.status !== 0) {
- process.stderr.write(closureAware.stderr || closureAware.stdout || '');
- throw new Error('closure-aware generate-proof-gap-request should pass');
- }
- const closureAwareSummary = JSON.parse(fs.readFileSync(path.join(closureAwareOutput, 'proof-gap-request-summary.json'), 'utf8'));
- assert(closureAwareSummary.unresolvedCount === 3, `closure-aware request should follow closure openCount=3, got ${closureAwareSummary.unresolvedCount}`);
- assert(closureAwareSummary.rows.some(row => row.id === 'live-provider-overnight-proof' && row.status === 'ready'), 'closed live-provider gap should stay ready in request package');
- assert(closureAwareSummary.rows.some(row => row.id === 'video-real-candidate' && row.status === 'ready'), 'closed video resource gap should stay ready in request package');
- console.log(JSON.stringify({
- ok: true,
- output,
- gapCount: summary.rows.length,
- unresolvedCount: summary.unresolvedCount
- }, null, 2));
- }
- function assert(condition, message) {
- if (!condition) throw new Error(message);
- }
- main();
|