| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558 |
- #!/usr/bin/env node
- const fs = require('fs');
- const path = require('path');
- const ROOT = path.resolve(__dirname, '..');
- const OUTPUTS = path.join(ROOT, 'outputs');
- function main() {
- const args = parseArgs(process.argv.slice(2));
- const outputDir = path.resolve(args.output || process.env.TIHAO_PROOF_GAP_CLOSURE_OUTPUT || path.join(OUTPUTS, `proof-gap-closure-${Date.now()}`));
- const overnightFile = args.overnight || process.env.TIHAO_OVERNIGHT_AGGREGATE || bestOvernightAggregate(OUTPUTS) || latestFile(OUTPUTS, 'aggregate-summary.json');
- const historyFile = args.historyAudit || process.env.TIHAO_HISTORY_AUDIT || bestHistoryAudit(OUTPUTS) || latestFile(OUTPUTS, 'historical-dataset-audit.json');
- const customerEffectFile = args.customerEffect || process.env.TIHAO_CUSTOMER_EFFECT_SUMMARY || bestCustomerEffectSummary(OUTPUTS, historyFile, ROOT) || latestFile(OUTPUTS, 'customer-effect-summary.json');
- const videoAbFile = args.videoAb || process.env.TIHAO_VIDEO_AB_SUMMARY || latestFile(OUTPUTS, 'video-hit-rate-summary.json');
- const videoAbPreflightFile = args.videoAbPreflight || process.env.TIHAO_VIDEO_AB_PREFLIGHT_SUMMARY || latestFile(OUTPUTS, 'video-hit-rate-preflight-summary.json');
- const materialBridgeFile = args.materialBridge || process.env.TIHAO_MATERIAL_BRIDGE_SUMMARY || latestFile(OUTPUTS, 'local-seed-to-intake-worklist-summary.json');
- const summary = buildClosureSummary({
- root: ROOT,
- history: readJsonArg(historyFile),
- intakeReadiness: readJsonArg(args.intakeReadiness || process.env.TIHAO_INTAKE_READINESS_SUMMARY || latestFile(OUTPUTS, 'intake-readiness-summary.json')),
- videoReadiness: readJsonArg(args.videoReadiness || process.env.TIHAO_VIDEO_RESOURCE_READINESS_SUMMARY || latestFile(OUTPUTS, 'video-resource-readiness-summary.json')),
- videoAb: readJsonArg(videoAbFile),
- videoAbPreflight: readJsonArg(videoAbPreflightFile),
- overnight: readJsonArg(overnightFile),
- customerEffect: readJsonArg(customerEffectFile),
- materialBridge: readJsonArg(materialBridgeFile),
- sourceFiles: {
- overnight: relIfInside(ROOT, overnightFile),
- history: relIfInside(ROOT, historyFile),
- customerEffect: relIfInside(ROOT, customerEffectFile),
- videoAb: relIfInside(ROOT, videoAbFile),
- videoAbPreflight: relIfInside(ROOT, videoAbPreflightFile),
- materialBridge: relIfInside(ROOT, materialBridgeFile)
- }
- });
- fs.mkdirSync(outputDir, { recursive: true });
- const jsonPath = path.join(outputDir, 'proof-gap-closure-summary.json');
- const reportPath = path.join(outputDir, 'proof-gap-closure-report.md');
- fs.writeFileSync(jsonPath, JSON.stringify(summary, null, 2), 'utf8');
- fs.writeFileSync(reportPath, withBom(renderReport(summary)), 'utf8');
- console.log(JSON.stringify({
- outputDir,
- json: jsonPath,
- report: reportPath,
- complete: summary.complete,
- closedCount: summary.closedCount,
- openCount: summary.openCount
- }, null, 2));
- if ((args.strict || process.env.TIHAO_PROOF_GAP_CLOSURE_STRICT === 'true') && !summary.complete) {
- process.exitCode = 2;
- }
- }
- function buildClosureSummary({ root, history, intakeReadiness, videoReadiness, videoAb, videoAbPreflight, overnight, customerEffect, materialBridge, sourceFiles = {} }) {
- const intakeMissingRequirements = getIntakeMissingRequirements(intakeReadiness);
- const historyMissingRequirements = getHistoryMissingRequirements(history);
- const videoResourceMissingRequirements = getVideoResourceMissingRequirements(videoReadiness);
- const videoAbMissingRequirements = getVideoAbMissingRequirements(videoAb, videoAbPreflight);
- const customerEffectMissingRequirements = getCustomerEffectMissingRequirements(customerEffect, history, sourceFiles, root);
- const materialBridgeByGap = buildMaterialBridgeByGap(materialBridge);
- const rows = [
- closeRow({
- id: 'intake-readiness',
- title: '真实材料 Intake Readiness 预审',
- closed: intakeMissingRequirements.length === 0,
- evidence: intakeReadiness ? `overallReady=${Boolean(intakeReadiness.acceptance?.overallReady)}, failureCount=${intakeReadiness.failureCount ?? 'unknown'}, history=${Boolean(intakeReadiness.acceptance?.historyReady)}, video=${Boolean(intakeReadiness.acceptance?.videoReady)}, review=${Boolean(intakeReadiness.acceptance?.reviewMetricsReady)}, customerEffect=${Boolean(intakeReadiness.acceptance?.customerEffectReady)}` : 'missing intake-readiness-summary.json',
- required: 'intake:readiness 必须显示 overallReady=true 且 failureCount=0。',
- command: 'npm run intake:readiness -- --data-pack outputs\\data-intake-pack-latest --video-pack outputs\\video-intake-pack-latest --output outputs\\intake-readiness-latest',
- expectedArtifact: 'outputs/intake-readiness-latest/intake-readiness-summary.json',
- missingProofRequirements: intakeMissingRequirements
- }),
- closeRow({
- id: 'historical-dataset',
- title: '真实历史 Brief 数据集',
- closed: historyMissingRequirements.length === 0,
- evidence: history ? `briefCount=${history.briefCount || 0}, readyForCustomerEffectProof=${Boolean(history.acceptance?.readyForCustomerEffectProof)}` : 'missing historical-dataset-audit.json',
- required: 'history:audit 必须显示 readyForCustomerEffectProof=true。',
- command: 'npm run history:from-csv -- --input <历史数据CSV> --output <history-dataset目录>;npm run history:audit -- --input <history-dataset目录> --output <历史审计输出目录> --strict',
- expectedArtifact: '<历史审计输出目录>/historical-dataset-audit.json',
- missingProofRequirements: historyMissingRequirements,
- materialBridge: materialBridgeByGap['historical-dataset']
- }),
- closeRow({
- id: 'video-real-candidate',
- title: '真实候选视频资源',
- closed: videoResourceMissingRequirements.length === 0,
- evidence: videoReadiness ? `realReference=${videoReadiness.counts?.realReferenceRows || 0}, realCandidate=${videoReadiness.counts?.realCandidateRows || 0}, failureCount=${videoReadiness.failureCount ?? 'unknown'}` : 'missing video-resource-readiness-summary.json',
- required: 'video:resource-readiness 必须 readyForVideoAbPreflight=true 且 failureCount=0。',
- command: 'npm run video:resource-readiness -- --input <video-resource-template.csv> --output <视频资源审计输出目录> --strict',
- expectedArtifact: '<视频资源审计输出目录>/video-resource-readiness-summary.json',
- missingProofRequirements: videoResourceMissingRequirements
- }),
- closeRow({
- id: 'video-ab-live-proof',
- title: '真实视频 A/B live proof',
- closed: videoAbMissingRequirements.length === 0,
- evidence: formatVideoAbEvidence(videoAb, videoAbPreflight),
- required: 'acceptance:video-ab 必须在真实 provider 和真实视频资源下通过。',
- command: 'npm run acceptance:video-ab',
- expectedArtifact: '<视频A/B输出目录>/video-hit-rate-summary.json',
- missingProofRequirements: videoAbMissingRequirements,
- materialBridge: materialBridgeByGap['video-ab-live-proof']
- }),
- closeRow({
- id: 'live-provider-overnight-proof',
- title: '真实 live/provider 长跑证明',
- closed: isClosableOvernightAggregate(overnight),
- evidence: overnight ? `liveEnabled=${Boolean(overnight.manifest?.liveEnabled)}, overallPass=${Boolean(overnight.acceptance?.overallPass)}, failureCount=${overnight.failureCount ?? 'unknown'}, failedGateCount=${overnight.acceptance?.failedGateCount ?? overnight.failedGateCount ?? 'unknown'}, source=${sourceFiles.overnight || 'unknown'}` : 'missing aggregate-summary.json',
- required: 'overnight aggregate 必须 liveEnabled=true、overallPass=true、failureCount=0、failedGateCount=0。',
- command: 'npm run longrun:readiness;真实 provider 就绪后运行 npm run overnight:quality',
- expectedArtifact: '<overnight输出目录>/aggregate-summary.json',
- missingProofRequirements: getOvernightMissingRequirements(overnight)
- }),
- closeRow({
- id: 'manual-review-and-customer-effect',
- title: '商务复核和客户效果证明',
- closed: customerEffectMissingRequirements.length === 0,
- evidence: customerEffect ? `overallPass=${Boolean(customerEffect.acceptance?.overallPass)}, customerSelectedRate=${customerEffect.customerSelectedRate ?? 'unknown'}, referenceCustomerSelectedRate=${customerEffect.referenceCustomerSelectedRate ?? 'unknown'}, manualSupplementReductionRate=${customerEffect.manualSupplementReductionRate ?? 'unknown'}, historyAuditPath=${customerEffect.historyAuditPath || 'missing'}` : 'missing customer-effect-summary.json',
- required: 'customer-effect:audit 必须 overallPass=true,并绑定当前通过的 history:audit、客户选择、参考链路和人工补号量。',
- command: 'npm run review:metrics -- --input <已标注CSV> --output <复核指标输出目录> --strict;npm run customer-effect:audit -- --review-csv <已标注CSV> --history-audit <historical-dataset-audit.json> --current-manual-supplement-count <本轮人工补号量> --output <客户效果输出目录> --strict',
- expectedArtifact: '<客户效果输出目录>/customer-effect-summary.json',
- missingProofRequirements: customerEffectMissingRequirements,
- materialBridge: materialBridgeByGap['manual-review-and-customer-effect']
- })
- ];
- const precheckRows = rows.filter(row => row.id === 'intake-readiness');
- const businessRows = rows.filter(row => row.id !== 'intake-readiness');
- const businessClosedCount = businessRows.filter(row => row.status === 'closed').length;
- const businessOpenCount = businessRows.length - businessClosedCount;
- const precheckClosedCount = precheckRows.filter(row => row.status === 'closed').length;
- const precheckOpenCount = precheckRows.length - precheckClosedCount;
- return {
- generatedAt: new Date().toISOString(),
- root,
- sourceFiles,
- complete: businessOpenCount === 0,
- intakeReady: precheckOpenCount === 0,
- closedCount: businessClosedCount,
- openCount: businessOpenCount,
- businessClosedCount,
- businessOpenCount,
- totalRows: rows.length,
- businessGapCount: businessRows.length,
- precheckRowCount: precheckRows.length,
- precheckClosedCount,
- precheckOpenCount,
- videoAbPreflightReadyForVideoAb: Boolean(videoAbPreflight?.readyForVideoAb),
- videoAbPreflightFailureCount: videoAbPreflight ? Number(videoAbPreflight.failureCount ?? 0) : null,
- countSemantics: {
- openCount: 'business_gap_open_count_excludes_precheck_rows',
- closedCount: 'business_gap_closed_count_excludes_precheck_rows',
- businessOpenCount: 'same_as_openCount',
- businessClosedCount: 'same_as_closedCount',
- precheckOpenCount: 'precheck_rows_reported_separately'
- },
- rows,
- guardrails: [
- 'closure 审计只承认真实审计产物,不承认 sample、smoke、模板或 provider fallback。',
- 'intake readiness 是补证预审,不计入业务缺口 openCount;但未通过时会提示模板仍是半成品。',
- '任一缺口为 open 时,不得宣布长期优化目标完成。',
- '不得在任何输入、报告或日志中写入 sessionToken、Authorization、模型 token 或 npm token。'
- ]
- };
- }
- function isRealVideoAbProof(videoAb) {
- return getVideoAbMissingRequirements(videoAb).length === 0;
- }
- function buildMaterialBridgeByGap(materialBridge) {
- const result = {};
- for (const item of materialBridge?.proofGapMaterialBridge || []) {
- if (!item || !item.id) continue;
- result[item.id] = {
- materialFound: Boolean(item.materialFound),
- connectedDraft: item.connectedDraft || '',
- draftRowCount: item.draftRowCount ?? null,
- referenceSeedRowCount: item.referenceSeedRowCount ?? null,
- videoCandidateSeedRowCount: item.videoCandidateSeedRowCount ?? null,
- canCloseProofGap: Boolean(item.canCloseProofGap),
- whyNotClosed: Array.isArray(item.whyNotClosed) ? item.whyNotClosed : []
- };
- }
- return result;
- }
- function getVideoAbMissingRequirements(videoAb, videoAbPreflight = null) {
- const missing = [];
- if (!videoAb) {
- missing.push('missing video-hit-rate-summary.json');
- } else {
- if (!videoAb.acceptance?.passed) missing.push('acceptance.passed=true');
- const context = videoAb.proofContext || {};
- if (context.mode !== 'live') missing.push('proofContext.mode=live');
- if (context.collectionMode !== 'live') missing.push('proofContext.collectionMode=live');
- if (context.generatedBy !== 'acceptance:video-ab') missing.push('proofContext.generatedBy=acceptance:video-ab');
- if (context.requiresRuntimeCredential !== true) missing.push('proofContext.requiresRuntimeCredential=true');
- if (context.requiresVocSocialProvider !== true) missing.push('proofContext.requiresVocSocialProvider=true');
- if (context.requiresVideoAnalysisProvider !== true) missing.push('proofContext.requiresVideoAnalysisProvider=true');
- if (videoAb.enhanced?.provider?.evidence?.providerStatus !== 'ok') missing.push('enhanced.provider.evidence.providerStatus=ok');
- }
- if (missing.length) {
- missing.push(...getVideoAbPreflightMissingRequirements(videoAbPreflight));
- }
- return uniqueList(missing);
- }
- function getVideoAbPreflightMissingRequirements(videoAbPreflight) {
- if (!videoAbPreflight) return [];
- const missing = [];
- if (videoAbPreflight.proofLevel !== 'not_business_proof') missing.push('videoAbPreflight.proofLevel=not_business_proof');
- if (videoAbPreflight.canCloseProofGap !== false) missing.push('videoAbPreflight.canCloseProofGap=false');
- if (videoAbPreflight.proofContext?.generatedBy !== 'acceptance:video-ab-preflight') missing.push('videoAbPreflight.proofContext.generatedBy=acceptance:video-ab-preflight');
- if (videoAbPreflight.readyForVideoAb !== true) {
- missing.push('videoAbPreflight.readyForVideoAb=true');
- for (const requirement of videoAbPreflight.missingProofRequirements || []) {
- missing.push(`videoAbPreflight.${requirement}`);
- }
- }
- return missing;
- }
- function formatVideoAbEvidence(videoAb, videoAbPreflight) {
- const liveEvidence = videoAb
- ? `passed=${Boolean(videoAb.acceptance?.passed)}, mode=${videoAb.proofContext?.mode || 'unknown'}, collectionMode=${videoAb.proofContext?.collectionMode || 'unknown'}, generatedBy=${videoAb.proofContext?.generatedBy || 'unknown'}, provider=${videoAb.enhanced?.provider?.evidence?.providerStatus || 'unknown'}, strongDelta=${videoAb.delta?.strong ?? 'unknown'}, evidenceDelta=${videoAb.delta?.evidenceCards ?? 'unknown'}`
- : 'missing video-hit-rate-summary.json';
- if (!videoAbPreflight || getVideoAbMissingRequirements(videoAb).length === 0) return liveEvidence;
- const runtime = videoAbPreflight.runtime || {};
- const preflightEvidence = [
- `preflight.readyForVideoAb=${Boolean(videoAbPreflight.readyForVideoAb)}`,
- `preflight.failureCount=${videoAbPreflight.failureCount ?? 'unknown'}`,
- `preflight.generatedBy=${videoAbPreflight.proofContext?.generatedBy || 'unknown'}`,
- `preflight.proofLevel=${videoAbPreflight.proofLevel || 'unknown'}`,
- `preflight.canCloseProofGap=${Boolean(videoAbPreflight.canCloseProofGap)}`,
- `runtimeCredentialPresent=${Boolean(runtime.runtimeCredentialPresent)}`,
- `companyResolved=${Boolean(runtime.companyResolved)}`,
- `vocSocialProviderTokenPresent=${Boolean(runtime.vocSocialProviderTokenPresent)}`,
- `videoAnalysisTokenPresent=${Boolean(runtime.videoAnalysisTokenPresent)}`
- ].join(', ');
- return `${liveEvidence}; ${preflightEvidence}`;
- }
- function getIntakeMissingRequirements(intakeReadiness) {
- const missing = [];
- if (!intakeReadiness) return ['missing intake-readiness-summary.json'];
- const acceptance = intakeReadiness.acceptance || {};
- if (acceptance.overallReady !== true) missing.push('acceptance.overallReady=true');
- if (Number(intakeReadiness.failureCount || 0) !== 0) missing.push('failureCount=0');
- if (acceptance.historyReady !== true) missing.push('acceptance.historyReady=true');
- if (acceptance.videoReady !== true) missing.push('acceptance.videoReady=true');
- if (acceptance.reviewMetricsReady !== true) missing.push('acceptance.reviewMetricsReady=true');
- if (acceptance.customerEffectReady !== true) missing.push('acceptance.customerEffectReady=true');
- return missing;
- }
- function getHistoryMissingRequirements(history) {
- const missing = [];
- if (!history) return ['missing historical-dataset-audit.json'];
- const acceptance = history.acceptance || {};
- if (history.acceptance?.readyForCustomerEffectProof !== true) missing.push('acceptance.readyForCustomerEffectProof=true');
- if (Number(history.briefCount || 0) < 5) missing.push('briefCount>=5');
- if (acceptance.parseOk !== true) missing.push('acceptance.parseOk=true');
- if (acceptance.minBriefsMet !== true) missing.push('acceptance.minBriefsMet=true');
- if (acceptance.allHaveBriefText !== true) missing.push('acceptance.allHaveBriefText=true');
- if (acceptance.allHaveManualFinalList !== true) missing.push('acceptance.allHaveManualFinalList=true');
- if (acceptance.allHaveCustomerDecision !== true) missing.push('acceptance.allHaveCustomerDecision=true');
- if (acceptance.allHaveFeedbackReason !== true) missing.push('acceptance.allHaveFeedbackReason=true');
- if (acceptance.allHaveManualSupplementBaseline !== true) missing.push('acceptance.allHaveManualSupplementBaseline=true');
- if (acceptance.categoryCoverageMet !== true) missing.push('acceptance.categoryCoverageMet=true');
- if (Number(history.missingCriticalCount || 0) !== 0) missing.push('missingCriticalCount=0');
- if (!Array.isArray(history.items) || history.items.length < 5) missing.push('items.length>=5');
- if (Number(history.withManualFinalList || 0) < 5) missing.push('withManualFinalList>=5');
- if (Number(history.withCustomerDecision || 0) < 5) missing.push('withCustomerDecision>=5');
- if (Number(history.withRejectionReason || 0) < 1) missing.push('withRejectionReason>=1');
- if (Number(history.withManualSupplementBaseline || 0) < 5) missing.push('withManualSupplementBaseline>=5');
- return missing;
- }
- function getVideoResourceMissingRequirements(videoReadiness) {
- const missing = [];
- if (!videoReadiness) return ['missing video-resource-readiness-summary.json'];
- if (videoReadiness.acceptance?.readyForVideoAbPreflight !== true) missing.push('acceptance.readyForVideoAbPreflight=true');
- if (Number(videoReadiness.failureCount || 0) !== 0) missing.push('failureCount=0');
- if (Number(videoReadiness.counts?.realReferenceRows || 0) < 1) missing.push('counts.realReferenceRows>=1');
- if (Number(videoReadiness.counts?.realCandidateRows || 0) < 1) missing.push('counts.realCandidateRows>=1');
- if (Number(videoReadiness.counts?.videoUrlRows || 0) < 1) missing.push('counts.videoUrlRows>=1');
- return missing;
- }
- function getOvernightMissingRequirements(overnight) {
- const missing = [];
- if (!overnight) return ['missing aggregate-summary.json'];
- if (overnight.manifest?.liveEnabled !== true) missing.push('manifest.liveEnabled=true');
- if (overnight.acceptance?.overallPass !== true) missing.push('acceptance.overallPass=true');
- if (Number(overnight.failureCount || 0) !== 0) missing.push('failureCount=0');
- if (Number(overnight.acceptance?.failedGateCount ?? overnight.failedGateCount ?? 0) !== 0) missing.push('acceptance.failedGateCount=0');
- if (overnight.acceptance?.tokenLeak === true) missing.push('acceptance.tokenLeak=false');
- return missing;
- }
- function getCustomerEffectMissingRequirements(customerEffect, history, sourceFiles = {}, root = process.cwd()) {
- const missing = [];
- if (!customerEffect) return ['missing customer-effect-summary.json'];
- const acceptance = customerEffect.acceptance || {};
- if (acceptance.overallPass !== true) missing.push('acceptance.overallPass=true');
- if (acceptance.reviewMetricsOverallUsable !== true) missing.push('acceptance.reviewMetricsOverallUsable=true');
- if (acceptance.customerSelectedRateMeasured !== true) missing.push('acceptance.customerSelectedRateMeasured=true');
- if (acceptance.customerSelectedRate30Pass !== true) missing.push('acceptance.customerSelectedRate30Pass=true');
- if (acceptance.referenceCustomerSelectedRateMeasured !== true) missing.push('acceptance.referenceCustomerSelectedRateMeasured=true');
- if (acceptance.referenceCustomerSelectedRate40Pass !== true) missing.push('acceptance.referenceCustomerSelectedRate40Pass=true');
- if (acceptance.historyReadyForCustomerEffectProof !== true) missing.push('acceptance.historyReadyForCustomerEffectProof=true');
- if (acceptance.manualSupplementBaselineAvailable !== true) missing.push('acceptance.manualSupplementBaselineAvailable=true');
- if (acceptance.currentManualSupplementAvailable !== true) missing.push('acceptance.currentManualSupplementAvailable=true');
- if (acceptance.manualSupplementReduction50Pass !== true) missing.push('acceptance.manualSupplementReduction50Pass=true');
- if (!customerEffect.reviewPath) missing.push('reviewPath=provided');
- if (!customerEffect.historyAuditPath) missing.push('historyAuditPath=provided');
- if (sourceFiles.history && customerEffect.historyAuditPath && !samePath(root, customerEffect.historyAuditPath, sourceFiles.history)) {
- missing.push('historyAuditPath=current-history-audit');
- }
- if (!history) {
- missing.push('currentHistoryAudit=present');
- } else {
- for (const requirement of getHistoryMissingRequirements(history)) {
- missing.push(`currentHistoryAudit.${requirement}`);
- }
- }
- if (!Number.isFinite(Number(customerEffect.customerSelectedRate))) missing.push('customerSelectedRate=number');
- if (!Number.isFinite(Number(customerEffect.referenceCustomerSelectedRate))) missing.push('referenceCustomerSelectedRate=number');
- if (!Number.isFinite(Number(customerEffect.manualSupplementReductionRate))) missing.push('manualSupplementReductionRate=number');
- if (customerEffect.manualSupplementBaseline?.available !== true) missing.push('manualSupplementBaseline.available=true');
- if (customerEffect.currentManualSupplement?.available !== true) missing.push('currentManualSupplement.available=true');
- return uniqueList([...(customerEffect.missingEvidence || []), ...missing]);
- }
- function closeRow({ id, title, closed, evidence, required, command, expectedArtifact, missingProofRequirements = [], materialBridge = null }) {
- const normalizedMissing = closed ? [] : uniqueList(missingProofRequirements);
- return {
- id,
- title,
- status: closed ? 'closed' : 'open',
- evidence,
- missingProofRequirements: normalizedMissing,
- required,
- command,
- expectedArtifact,
- ...(materialBridge ? { materialBridge } : {}),
- next: closed ? '保持证据链并纳入 evidence:index。' : `${command};产物:${expectedArtifact}`
- };
- }
- function uniqueList(values) {
- return Array.from(new Set((Array.isArray(values) ? values : []).map(value => String(value || '').trim()).filter(Boolean)));
- }
- function renderReport(summary) {
- return [
- '# 提号真实证明缺口关闭审计',
- '',
- `- 生成时间:${summary.generatedAt}`,
- `- 是否全部关闭:${summary.complete ? '是' : '否'}`,
- `- Intake 预审是否就绪:${summary.intakeReady ? '是' : '否'}`,
- `- 已关闭:${summary.closedCount}`,
- `- 未关闭:${summary.openCount}`,
- `- 业务证明缺口总数:${summary.businessGapCount ?? summary.openCount + summary.closedCount}`,
- `- 业务证明缺口已关闭:${summary.businessClosedCount ?? summary.closedCount}`,
- `- 业务证明缺口未关闭:${summary.businessOpenCount ?? summary.openCount}`,
- `- 预审行总数:${summary.precheckRowCount ?? 0}`,
- `- 预审未就绪行:${summary.precheckOpenCount ?? (summary.intakeReady ? 0 : 1)}`,
- '',
- '## 关闭状态',
- '',
- '| 缺口ID | 名称 | 状态 | 当前证据 | 缺失证明 | 关闭标准 | 验证命令 | 期望产物 | 下一步 |',
- '| --- | --- | --- | --- | --- | --- | --- | --- | --- |',
- ...summary.rows.map(row => `| ${row.id} | ${row.title} | ${row.status} | ${escapeCell(row.evidence)} | ${escapeCell((row.missingProofRequirements || []).join(';') || '-')} | ${escapeCell(row.required)} | \`${escapeCell(row.command)}\` | ${escapeCell(row.expectedArtifact)} | ${escapeCell(row.next)} |`),
- '',
- '## 边界',
- '',
- ...summary.guardrails.map(item => `- ${item}`)
- ].join('\n');
- }
- function latestFile(outputsDir, fileName) {
- if (!fs.existsSync(outputsDir)) return '';
- return fs.readdirSync(outputsDir, { withFileTypes: true })
- .filter(entry => entry.isDirectory())
- .map(entry => path.join(outputsDir, entry.name, fileName))
- .filter(file => fs.existsSync(file))
- .sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs)[0] || '';
- }
- function bestHistoryAudit(outputsDir) {
- return bestProofArtifact(outputsDir, 'historical-dataset-audit.json', {
- isClaimable: item => getHistoryMissingRequirements(item.json).length === 0
- });
- }
- function bestCustomerEffectSummary(outputsDir, historyFile = '', root = process.cwd()) {
- const history = readJsonArg(historyFile);
- const sourceFiles = { history: relIfInside(root, historyFile) };
- return bestProofArtifact(outputsDir, 'customer-effect-summary.json', {
- isClaimable: item => getCustomerEffectMissingRequirements(item.json, history, sourceFiles, root).length === 0,
- score: item => {
- if (!historyFile || !item.json?.historyAuditPath) return 0;
- return samePath(root, item.json.historyAuditPath, historyFile) ? 2 : 0;
- }
- });
- }
- function bestProofArtifact(outputsDir, fileName, { isClaimable = () => false, score = () => 0 } = {}) {
- const annotated = findFiles(outputsDir, fileName)
- .filter(file => isBusinessArtifactPath(outputsDir, file))
- .map(file => ({ file, json: readJsonArg(file), mtimeMs: fs.statSync(file).mtimeMs }))
- .filter(item => item.json);
- if (!annotated.length) return '';
- return annotated
- .sort((left, right) => {
- const rightClaimable = safePredicate(isClaimable, right) ? 1 : 0;
- const leftClaimable = safePredicate(isClaimable, left) ? 1 : 0;
- if (rightClaimable !== leftClaimable) return rightClaimable - leftClaimable;
- const rightScore = Number(score(right) || 0);
- const leftScore = Number(score(left) || 0);
- if (rightScore !== leftScore) return rightScore - leftScore;
- return right.mtimeMs - left.mtimeMs;
- })[0].file;
- }
- function safePredicate(predicate, item) {
- try {
- return Boolean(predicate(item));
- } catch (_error) {
- return false;
- }
- }
- function isBusinessArtifactPath(outputsDir, file) {
- const relative = path.relative(outputsDir, file).replace(/\\/g, '/').toLowerCase();
- const segments = relative.split('/').filter(Boolean);
- return !segments.some(segment =>
- segment.includes('smoke') ||
- segment.includes('sample') ||
- segment.includes('debug') ||
- segment === 'fixture' ||
- segment === 'fixtures' ||
- segment === '__tests__' ||
- segment === 'test' ||
- segment === 'tests' ||
- segment === 'tmp' ||
- segment.startsWith('tmp-') ||
- segment === 'temp' ||
- segment.startsWith('temp-')
- );
- }
- function bestOvernightAggregate(outputsDir) {
- const files = findFiles(outputsDir, 'aggregate-summary.json');
- const annotated = files
- .map(file => ({ file, json: readJsonArg(file), mtimeMs: fs.statSync(file).mtimeMs }))
- .filter(item => item.json);
- const closable = annotated
- .filter(item => isClosableOvernightAggregate(item.json))
- .sort((a, b) => b.mtimeMs - a.mtimeMs);
- if (closable.length) return closable[0].file;
- return annotated.sort((a, b) => b.mtimeMs - a.mtimeMs)[0]?.file || '';
- }
- function findFiles(dir, fileName) {
- if (!fs.existsSync(dir)) return [];
- const result = [];
- const stack = [dir];
- while (stack.length) {
- const current = stack.pop();
- for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
- const full = path.join(current, entry.name);
- if (entry.isDirectory()) stack.push(full);
- else if (entry.isFile() && entry.name === fileName) result.push(full);
- }
- }
- return result;
- }
- function isClosableOvernightAggregate(aggregate) {
- return Boolean(
- aggregate?.manifest?.liveEnabled === true &&
- aggregate?.acceptance?.overallPass === true &&
- Number(aggregate?.failureCount || 0) === 0 &&
- Number(aggregate?.acceptance?.failedGateCount ?? aggregate?.failedGateCount ?? 0) === 0 &&
- aggregate?.acceptance?.tokenLeak !== true
- );
- }
- function readJsonArg(file) {
- if (!file) return null;
- const resolved = path.resolve(file);
- if (!fs.existsSync(resolved)) return null;
- return JSON.parse(fs.readFileSync(resolved, 'utf8').replace(/^\uFEFF/, ''));
- }
- function relIfInside(root, file) {
- if (!file) return '';
- const resolvedRoot = path.resolve(root);
- const resolvedFile = path.resolve(file);
- const relative = path.relative(resolvedRoot, resolvedFile);
- if (relative && (relative.startsWith('..') || path.isAbsolute(relative))) return resolvedFile.replace(/\\/g, '/');
- return relative.replace(/\\/g, '/');
- }
- function samePath(root, left, right) {
- const normalize = value => {
- const resolved = path.resolve(root || process.cwd(), value || '');
- return process.platform === 'win32' ? resolved.toLowerCase() : resolved;
- };
- return normalize(left) === normalize(right);
- }
- function parseArgs(argv) {
- const args = {};
- for (let index = 0; index < argv.length; index += 1) {
- const raw = argv[index];
- if (!raw.startsWith('--')) continue;
- const key = raw.slice(2).replace(/-([a-z])/g, (_, char) => char.toUpperCase());
- const next = argv[index + 1];
- if (!next || next.startsWith('--')) args[key] = true;
- else {
- args[key] = next;
- index += 1;
- }
- }
- return args;
- }
- function escapeCell(value) {
- return String(value ?? '').replace(/\|/g, '/').replace(/\r?\n/g, ' ');
- }
- function withBom(text) {
- return `\uFEFF${text}`;
- }
- if (require.main === module) main();
- module.exports = {
- buildClosureSummary,
- renderReport,
- getVideoAbMissingRequirements,
- getIntakeMissingRequirements,
- getHistoryMissingRequirements,
- getVideoResourceMissingRequirements,
- getOvernightMissingRequirements,
- getCustomerEffectMissingRequirements,
- isRealVideoAbProof,
- getVideoAbPreflightMissingRequirements,
- formatVideoAbEvidence,
- bestHistoryAudit,
- bestCustomerEffectSummary,
- bestOvernightAggregate,
- isClosableOvernightAggregate
- };
|