#!/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 temp = fs.mkdtempSync(path.join(os.tmpdir(), 'tihao-round-refresh-')); const outputRoot = path.join(temp, 'outputs'); seedRequiredOutputs(outputRoot); const result = spawnSync(process.execPath, ['scripts/refresh-round-artifacts.js', '--output-root', outputRoot, '--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, 'round refresh command should pass'); const refresh = readJson(path.join(outputRoot, 'round-refresh-summary.json')); const form = readJson(path.join(outputRoot, 'tihao-proof-gap-software-form-latest', 'tihao-proof-gap-software-form-summary.json')); const proofGap = readJson(path.join(outputRoot, 'proof-gap-request-latest', 'proof-gap-request-summary.json')); const closure = readJson(path.join(outputRoot, 'proof-gap-closure-latest', 'proof-gap-closure-summary.json')); const feedbackClosure = readJson(path.join(outputRoot, 'feedback-loop-closure-latest', 'feedback-loop-closure-summary.json')); const homepageReadiness = readJson(path.join(outputRoot, 'homepage-evidence-readiness-latest', 'homepage-evidence-readiness-summary.json')); const intakeReadiness = readJson(path.join(outputRoot, 'intake-readiness-latest', 'intake-readiness-summary.json')); const videoReadiness = readJson(path.join(outputRoot, 'video-resource-readiness-latest', 'video-resource-readiness-summary.json')); const livePreflight = readJson(path.join(outputRoot, 'live-preflight-latest', 'live-preflight-summary.json')); const longRunReadiness = readJson(path.join(outputRoot, 'long-run-readiness-latest', 'long-run-readiness-summary.json')); const fieldChecklist = readJson(path.join(outputRoot, 'intake-field-checklist-latest', 'intake-field-checklist-summary.json')); const manualReviewLabelGuide = readJson(path.join(outputRoot, 'manual-review-label-guide-latest', 'manual-review-label-guide-summary.json')); const progress = readJson(path.join(outputRoot, 'business-proof-progress-latest', 'business-proof-progress-summary.json')); const nextActions = readJson(path.join(outputRoot, 'business-proof-next-actions-latest', 'business-proof-next-actions-summary.json')); const businessExecution = readJson(path.join(outputRoot, 'business-execution-index-latest', 'business-execution-index-summary.json')); const status = readJson(path.join(outputRoot, 'optimization-status-latest', 'optimization-status-summary.json')); const evidence = readJson(path.join(outputRoot, 'evidence-index-latest', 'evidence-index-summary.json')); const handoff = readJson(path.join(outputRoot, 'optimization-handoff-latest', 'optimization-handoff-summary.json')); const completion = readJson(path.join(outputRoot, 'optimization-completion-latest', 'optimization-completion-summary.json')); const planExecution = readJson(path.join(outputRoot, 'plan-execution-status-latest', 'plan-execution-status-summary.json')); const operatorPack = readJson(path.join(outputRoot, 'proof-gap-operator-pack-latest', 'proof-gap-operator-pack-summary.json')); const intakeBundle = readJson(path.join(outputRoot, 'real-proof-intake-bundle-latest', 'real-proof-intake-bundle-summary.json')); const softwareRefresh = readJson(path.join(outputRoot, 'software-client-latest', 'software-client-refresh-summary.json')); const softwareClient = readJson(path.join(outputRoot, 'software-client-latest', 'software-client-list.final.summary.json')); const latestForm = readJson(path.join(outputRoot, 'latest-form-index-latest', 'latest-form-index-summary.json')); const localSeed = readJson(path.join(outputRoot, 'local-seed-material-index-latest', 'local-seed-material-index-summary.json')); const seedWorklist = readJson(path.join(outputRoot, 'local-seed-to-intake-worklist-latest', 'local-seed-to-intake-worklist-summary.json')); const realGapMaterialAudit = readJson(path.join(outputRoot, 'real-gap-material-audit-latest', 'real-gap-material-audit-summary.json')); const realProofClosureWorkOrder = readJson(path.join(outputRoot, 'real-proof-closure-work-order-latest', 'real-proof-closure-work-order-summary.json')); const experienceTranscript = readJson(path.join(outputRoot, 'experience-transcript-index-latest', 'experience-transcript-index-summary.json')); const round = readJson(path.join(outputRoot, 'round-deposition-latest', 'round-deposition-summary.json')); const expectedOwnerArtifactCount = Number(operatorPack.ownerArtifactCount || operatorPack.ownerArtifacts?.length || 0); const expectedBusinessOwnerArtifactRows = Number(businessExecution.operatorPackOwnerArtifactRowCount || expectedOwnerArtifactCount); const expectedWorkOrderCount = Number(realProofClosureWorkOrder.workOrderCount || 0); const expectedProofGapOpenCount = Number(closure.openCount || 0); const expectedIntakeFailureCount = Number(intakeReadiness.failureCount || 0); const expectedRealCandidateRows = Number(videoReadiness.counts?.realCandidateRows || 0); assert(refresh.passed === true, 'refresh summary should pass'); assert(refresh.steps.length === 29, 'refresh should run twenty-nine ordered steps'); assert(refresh.steps.map(item => item.id).join(',') === 'proof-gap-software-form,proof-gap-request,proof-gap-closure,feedback-loop-closure,homepage-evidence-readiness,intake-readiness,video-resource-readiness,live-preflight,longrun-readiness,intake-field-checklist,manual-review-label-guide,proof-gap-operator-pack,business-proof-progress,business-proof-next-actions,optimization-completion,business-execution-index,optimization-status,plan-execution-status,real-proof-intake-bundle,software-client-list,local-seed-material-index,local-seed-to-intake-worklist,real-gap-material-audit,real-proof-closure-work-order,latest-form-index,optimization-handoff,experience-transcript-index,evidence-index,round-deposition', 'refresh order should be stable'); assert(form.passed === true && form.rowCount === 5 && form.duplicateIdCount === 0, 'software proof-gap form should pass'); assert(proofGap.unresolvedCount >= 1, 'proof gap request should refresh unresolved gaps'); assert(closure.openCount >= 1 && closure.complete === false, 'proof gap closure should refresh open gaps'); assert(feedbackClosure.complete === false && feedbackClosure.directCustomerProof === false, 'feedback loop closure should keep proof boundary'); assert(homepageReadiness.directCustomerProof === false, 'homepage readiness should keep proof boundary'); assert(homepageReadiness.proofLevel === 'not_business_proof' || homepageReadiness.proofLevel === 'smoke_or_local', 'homepage readiness should classify proof level'); assert(intakeReadiness.acceptance?.overallReady === false && intakeReadiness.failureCount >= 1, 'intake readiness should refresh current incomplete real-material state'); assertVideoResourceReadiness(videoReadiness); assert(typeof livePreflight.readyForLiveAcceptance === 'boolean', 'live preflight should refresh live acceptance readiness'); assert(typeof livePreflight.readyForVideoAb === 'boolean', 'live preflight should refresh video A/B readiness'); assert(livePreflight.strict === true && livePreflight.allowFailure === true, 'round refresh live preflight should run strict checks without failing the refresh'); assert(longRunReadiness.ready === false && Number(longRunReadiness.counts?.fail || 0) >= 1, 'longrun readiness should refresh blocked full-matrix gate'); assert(refresh.artifacts.intakeReadiness?.includes('intake-readiness-summary.json'), 'refresh artifacts should expose intake readiness summary'); assert(refresh.artifacts.videoResourceReadiness?.includes('video-resource-readiness-summary.json'), 'refresh artifacts should expose video resource readiness summary'); assert(refresh.artifacts.livePreflight?.includes('live-preflight-summary.json'), 'refresh artifacts should expose live preflight summary'); assert(refresh.artifacts.longRunReadiness?.includes('long-run-readiness-summary.json'), 'refresh artifacts should expose longrun readiness summary'); assert(fieldChecklist.passed === true && fieldChecklist.itemCount >= 1, 'field checklist should refresh field-level intake gaps'); assert(fieldChecklist.directCustomerProof === false, 'field checklist should keep proof boundary'); assert(manualReviewLabelGuide.passed === true, 'manual review label guide should refresh'); assert(manualReviewLabelGuide.directCustomerProof === false, 'manual review label guide should keep proof boundary'); assert(manualReviewLabelGuide.negativeLabelsRequireAttribution === true, 'manual review label guide should require negative attribution'); assert(manualReviewLabelGuide.attributionTypeCount >= 8, 'manual review label guide should expose attribution types'); assert(progress.complete === false && progress.counts.blocked_by_external_data >= 1, 'business proof progress should refresh incomplete proof state'); assert(progress.operatorPackOwnerArtifactCount === expectedOwnerArtifactCount, 'business proof progress should expose operator owner attachments'); assert(progress.operatorPackOwnerArtifacts?.some(item => item.csv?.includes('by-owner/business.csv')), 'business proof progress should expose business operator owner csv'); assert(nextActions.complete === false && nextActions.actions.length >= 1, 'business proof next actions should refresh actionable queue'); assert(businessExecution.passed === true && businessExecution.rowCount >= 1, 'business execution index should refresh executable business table'); assert(businessExecution.directCustomerProof === false, 'business execution index should keep proof boundary'); assert(businessExecution.operatorPackOwnerArtifactCount === expectedOwnerArtifactCount, 'business execution index should expose operator pack owner attachments'); assert(businessExecution.sourceFiles?.longRunReadiness?.includes('long-run-readiness-summary.json'), 'business execution index should expose longrun readiness source'); assert(businessExecution.rows?.some(row => row.title?.includes('长跑前置门禁') && row.mainFile?.includes('long-run-readiness-report.md')), 'business execution index should expose longrun readiness report row'); assert(businessExecution.sourceFiles?.optimizationCompletion?.includes('optimization-completion-summary.json'), 'business execution index should expose optimization completion source'); assert(businessExecution.rows?.some(row => row.title?.includes('优化完成度审计') && row.mainFile?.includes('optimization-completion-report.md')), 'business execution index should expose optimization completion report row'); assert(status.complete === false && status.counts.ready_not_proven >= 1, 'status should preserve unfinished boundary'); assert(completion.complete === false && completion.readyForClaim === false, 'completion audit should preserve unfinished boundary'); assert(completion.proofGapOpenCount >= 1, 'completion audit should expose open proof gaps'); assert(completion.canClaim.tihaoRateImproved === false, 'completion audit should forbid tihao-rate claim'); assert(completion.canClaim.customerEffectAchieved === false, 'completion audit should forbid customer-effect claim'); assert(completion.canClaim.manualSupplementReduced === false, 'completion audit should forbid manual-supplement claim'); assert(evidence.byType['proof-gap-software-form'] === 1, 'evidence index should classify latest software form'); assert(evidence.byType['optimization-completion'] === 1, 'evidence index should classify completion audit'); assert(evidence.byType['proof-gap-operator-pack'] === 1, 'evidence index should classify proof gap operator pack'); assert(evidence.byType['real-proof-intake-bundle'] === 1, 'evidence index should classify real proof intake bundle'); assert(evidence.byType['local-seed-material-index'] === 1, 'evidence index should classify local seed material index'); assert(evidence.byType['local-seed-to-intake-worklist'] === 1, 'evidence index should classify seed-to-intake worklist'); assert(evidence.byType['real-gap-material-audit'] === 1, 'evidence index should classify real gap material audit'); assert(evidence.byType['real-proof-closure-work-order'] === 1, 'evidence index should classify real proof closure work order'); assert(evidence.byType['experience-transcript-index'] === 1, 'evidence index should classify experience transcript index'); assert(evidence.byType['feedback-loop-closure'] === 1, 'evidence index should classify feedback loop closure'); assert(evidence.byType['homepage-evidence-readiness'] === 1, 'evidence index should classify homepage readiness'); assert(evidence.byType['intake-field-checklist'] === 1, 'evidence index should classify field checklist'); assert(evidence.counts.smoke_or_local >= 1, 'evidence index should include smoke/local evidence'); assert(handoff.proofGapSoftwareForm?.passed === true, 'handoff should expose proof-gap software form'); assert(handoff.feedbackLoopClosure?.directCustomerProof === false, 'handoff should expose feedback loop proof boundary'); assert(handoff.homepageEvidenceReadiness?.directCustomerProof === false, 'handoff should expose homepage readiness proof boundary'); assert(handoff.latestFormIndex?.passed === true, 'handoff should expose latest form index'); assert(handoff.supportDocs?.some(item => item.doc?.includes('tihao-handoff-index.md')), 'handoff should expose task handoff index doc'); assert(handoff.latestFormIndex?.manualReviewLabelGuide?.markdown?.includes('manual-review-label-guide.md'), 'handoff should expose manual review label guide'); assert(handoff.latestFormIndex?.planExecutionStatus?.markdown?.includes('plan-execution-status.md'), 'handoff latest form index should expose plan execution status'); assert(handoff.planExecutionStatus?.markdown?.includes('plan-execution-status.md'), 'handoff should expose top-level plan execution status'); assert(handoff.planExecutionStatus?.complete === false, 'handoff plan execution status should keep incomplete boundary'); assert(handoff.planExecutionStatus?.operatorPackOwnerArtifactCount === expectedOwnerArtifactCount, 'handoff plan execution status should expose operator owner attachments'); assert(handoff.latestFormIndex?.planExecutionStatus?.operatorPackOwnerArtifactCount === expectedOwnerArtifactCount, 'handoff latest form plan execution status should expose operator owner attachments'); assert(handoff.latestFormIndex?.proofGapRequest?.markdown?.includes('proof-gap-request-report.md'), 'handoff latest form index should expose proof gap request'); assert(handoff.proofGapRequest?.markdown?.includes('proof-gap-request-report.md'), 'handoff should expose top-level proof gap request'); assert(handoff.latestFormIndex?.proofGapOperatorPack?.markdown?.includes('proof-gap-operator-pack.md'), 'handoff latest form index should expose proof gap operator pack'); assert(handoff.proofGapOperatorPack?.markdown?.includes('proof-gap-operator-pack.md'), 'handoff should expose top-level proof gap operator pack'); assert(handoff.latestFormIndex?.realProofIntakeBundle?.markdown?.includes('real-proof-intake-bundle.md'), 'handoff latest form index should expose real proof intake bundle'); assert(handoff.realProofIntakeBundle?.markdown?.includes('real-proof-intake-bundle.md'), 'handoff should expose top-level real proof intake bundle'); assert(latestForm.realProofClosureWorkOrder?.markdown?.includes('real-proof-closure-work-order.md'), 'latest form index should expose real proof closure work order report'); assert(latestForm.realProofClosureWorkOrder?.csv?.includes('real-proof-closure-work-order.csv'), 'latest form index should expose real proof closure work order csv'); assert(latestForm.realProofClosureWorkOrder?.summary?.includes('real-proof-closure-work-order-summary.json'), 'latest form index should expose real proof closure work order summary'); assert(latestForm.realProofClosureWorkOrder?.directCustomerProof === false, 'latest form real proof closure work order should keep proof boundary'); assert(latestForm.realProofClosureWorkOrder?.proofLevel === 'not_business_proof', 'latest form real proof closure work order should keep proof level'); assert(latestForm.realProofClosureWorkOrder?.materialType === 'real_proof_closure_work_order', 'latest form real proof closure work order should expose material type'); assert(latestForm.realProofClosureWorkOrder?.complete === false, 'latest form real proof closure work order should not claim completion'); assert(latestForm.realProofClosureWorkOrder?.canCloseProofGap === false, 'latest form real proof closure work order should not close proof gaps'); assert(latestForm.realProofClosureWorkOrder?.workOrderCount === expectedWorkOrderCount, 'latest form real proof closure work order should expose work order count'); assert(latestForm.realProofClosureWorkOrder?.ownerArtifacts?.length === expectedOwnerArtifactCount, 'latest form real proof closure work order should expose owner artifacts'); assert(handoff.realProofClosureWorkOrder?.markdown?.includes('real-proof-closure-work-order.md'), 'handoff should expose top-level real proof closure work order'); assert(handoff.realProofClosureWorkOrder?.workOrderCount === expectedWorkOrderCount, 'handoff real proof closure work order should expose work order count'); assert(handoff.realProofClosureWorkOrder?.canCloseProofGap === false, 'handoff real proof closure work order should not close proof gaps'); assert(handoff.realProofClosureWorkOrder?.ownerArtifacts?.some(item => item.csv?.includes('by-owner/business.csv')), 'handoff real proof closure work order should expose business owner csv'); assert(handoff.latestFormIndex?.realProofClosureWorkOrder?.markdown?.includes('real-proof-closure-work-order.md'), 'handoff latest form index should expose real proof closure work order'); assert(handoff.latestFormIndex?.realProofClosureWorkOrder?.workOrderCount === expectedWorkOrderCount, 'handoff latest form real proof closure work order should expose work order count'); assert(handoff.latestFormIndex?.realProofClosureWorkOrder?.ownerArtifacts?.some(item => item.markdown?.includes('by-owner/tech-ai.md')), 'handoff latest form real proof closure work order should expose tech owner markdown'); assert(latestForm.localSeedToIntakeWorklist?.markdown?.includes('local-seed-to-intake-worklist.md'), 'latest form index should expose seed-to-intake worklist report'); assert(latestForm.localSeedToIntakeWorklist?.historyDraft?.includes('history-intake-draft.csv'), 'latest form index should expose seed-to-intake history draft'); assert(latestForm.localSeedToIntakeWorklist?.videoWorklist?.includes('video-resource-worklist.csv'), 'latest form index should expose seed-to-intake video worklist'); assert(latestForm.localSeedToIntakeWorklist?.candidateWorklist?.includes('candidate-seed-worklist.csv'), 'latest form index should expose seed-to-intake candidate worklist'); assert(latestForm.localSeedToIntakeWorklist?.directCustomerProof === false, 'latest form seed-to-intake worklist should keep proof boundary'); assert(latestForm.localSeedToIntakeWorklist?.canCloseProofGap === false, 'latest form seed-to-intake worklist should not close proof gaps'); assert(Number(latestForm.localSeedToIntakeWorklist?.candidateSeedRows || 0) >= 1, 'latest form seed-to-intake worklist should expose candidate seed rows'); assert(latestForm.realGapMaterialAudit?.markdown?.includes('real-gap-material-audit.md'), 'latest form index should expose real gap material audit report'); assert(latestForm.realGapMaterialAudit?.summary?.includes('real-gap-material-audit-summary.json'), 'latest form index should expose real gap material audit summary'); assert(latestForm.realGapMaterialAudit?.materialType === 'three_real_gap_material_audit', 'latest form real gap material audit should classify material type'); assert(latestForm.realGapMaterialAudit?.directCustomerProof === false, 'latest form real gap material audit should keep proof boundary'); assert(latestForm.realGapMaterialAudit?.canCloseProofGap === false, 'latest form real gap material audit should not close proof gaps'); assert(latestForm.realGapMaterialAudit?.materialFoundCount === realGapMaterialAudit.materialFoundCount, 'latest form real gap material audit should expose material coverage count'); assert(handoff.localSeedToIntakeWorklist?.markdown?.includes('local-seed-to-intake-worklist.md'), 'handoff should expose top-level seed-to-intake worklist'); assert(handoff.latestFormIndex?.localSeedToIntakeWorklist?.historyDraft?.includes('history-intake-draft.csv'), 'handoff latest form index should expose seed-to-intake history draft'); assert(handoff.latestFormIndex?.localSeedToIntakeWorklist?.directCustomerProof === false, 'handoff latest form seed-to-intake worklist should keep proof boundary'); assert(handoff.realGapMaterialAudit?.markdown?.includes('real-gap-material-audit.md'), 'handoff should expose top-level real gap material audit'); assert(handoff.realGapMaterialAudit?.materialFoundCount === realGapMaterialAudit.materialFoundCount, 'handoff real gap material audit should expose material coverage count'); assert(handoff.realGapMaterialAudit?.canCloseProofGap === false, 'handoff real gap material audit should not close proof gaps'); assert(handoff.latestFormIndex?.realGapMaterialAudit?.summary?.includes('real-gap-material-audit-summary.json'), 'handoff latest form index should expose real gap material audit'); assert(planExecution.passed === true, 'plan execution status should pass'); assert(planExecution.complete === false, 'plan execution status should not claim completion'); assert(planExecution.rowCount === 8, 'plan execution status should map eight plan sections'); assert(planExecution.proofOpenCount >= 1, 'plan execution status should expose open proof gaps'); assert(planExecution.operatorPackOwnerArtifactCount === expectedOwnerArtifactCount, 'plan execution status should expose operator owner attachments'); assert(planExecution.businessExecutionOperatorPackOwnerArtifactRowCount === expectedBusinessOwnerArtifactRows, 'plan execution status should expose business execution operator rows'); assert(planExecution.rows.some(row => row.section === '关键差距' && row.status === 'open'), 'plan execution status should keep key gaps open'); assert(planExecution.guardrails.some(item => item.includes('不证明客户效果')), 'plan execution status should keep proof boundary'); assert(latestForm.primaryForm?.passed === true, 'latest form index should expose passed proof-gap form'); assert(latestForm.supportDocs?.some(item => item.path?.includes('tihao-handoff-index.md')), 'latest form index should expose task handoff index doc'); assert(operatorPack.passed === true && operatorPack.rowCount === 5 && operatorPack.openCount >= 1, 'proof gap operator pack should pass and keep open gaps'); assert(operatorPack.directCustomerProof === false, 'proof gap operator pack should not be direct customer proof'); assert(intakeBundle.passed === true && intakeBundle.proofOpenCount >= 1 && intakeBundle.fillRowCount >= 3, 'real proof intake bundle should pass and expose fill rows'); assert(intakeBundle.directCustomerProof === false, 'real proof intake bundle should not be direct customer proof'); assert(intakeBundle.templateFiles?.some(item => item.path?.includes('long-run-readiness-report.md')), 'real proof intake bundle should expose longrun readiness report'); assert(intakeBundle.recheckCommands?.some(item => item.command?.includes('longrun:readiness')), 'real proof intake bundle should expose longrun readiness recheck command'); assert(intakeBundle.sourceState?.longRunReadinessReady === false, 'real proof intake bundle should expose longrun ready=false'); assert(Number(intakeBundle.sourceState?.longRunReadinessFailCount || 0) >= 1, 'real proof intake bundle should expose longrun fail count'); assert(softwareRefresh.passed === true && softwareRefresh.skipped === false, 'software client list should refresh from latest manual review sample'); assert(softwareRefresh.sourceDuplicateRemovedCount >= 0, 'software client refresh should expose source duplicate rows removed'); assert(softwareRefresh.finalDuplicateGroupCount === 0, 'software client refresh should keep final duplicate groups at zero'); assert(softwareRefresh.duplicateCountMeaning === 'source_duplicate_removed_count', 'software client refresh should define duplicate count meaning'); assert(softwareRefresh.rankContinuous === true, 'software client refresh should keep rank continuity'); assert(softwareClient.finalDuplicateGroupCount === 0, 'software client summary should keep zero final duplicate groups'); assert(refresh.artifacts.softwareClientList?.includes('software-client-refresh-summary.json'), 'refresh artifacts should expose software client refresh summary'); assert(latestForm.manualReviewLabelGuide?.markdown?.includes('manual-review-label-guide.md'), 'latest form index should expose manual review label guide'); assert(latestForm.planExecutionStatus?.markdown?.includes('plan-execution-status.md'), 'latest form index should expose plan execution status'); assert(latestForm.planExecutionStatus?.rowCount === 8, 'latest form index should expose plan execution row count'); assert(latestForm.planExecutionStatus?.complete === false, 'latest form index should keep plan execution incomplete boundary'); assert(latestForm.planExecutionStatus?.operatorPackOwnerArtifactCount === expectedOwnerArtifactCount, 'latest form index should expose plan execution operator owner attachments'); assert(latestForm.proofGapRequest?.markdown?.includes('proof-gap-request-report.md'), 'latest form index should expose proof gap request'); assert(latestForm.proofGapRequest?.directCustomerProof === false, 'latest form index should keep proof gap request proof boundary'); assert(latestForm.proofGapOperatorPack?.markdown?.includes('proof-gap-operator-pack.md'), 'latest form index should expose proof gap operator pack'); assert(latestForm.proofGapOperatorPack?.directCustomerProof === false, 'latest form index should keep operator pack proof boundary'); assert(latestForm.businessExecutionIndex?.operatorPackOwnerArtifactCount === expectedOwnerArtifactCount, 'latest form index should expose business execution operator owner attachments'); assert(latestForm.realProofIntakeBundle?.markdown?.includes('real-proof-intake-bundle.md'), 'latest form index should expose real proof intake bundle'); assert(latestForm.realProofIntakeBundle?.directCustomerProof === false, 'latest form index should keep real proof intake bundle proof boundary'); assert(localSeed.directCustomerProof === false, 'local seed material index should keep proof boundary'); assert(localSeed.canCloseProofGap === false, 'local seed material index should not close proof gaps'); assert(refresh.artifacts.localSeedMaterialIndex?.includes('local-seed-material-index-summary.json'), 'refresh artifacts should expose local seed material index'); assert(seedWorklist.directCustomerProof === false, 'seed-to-intake worklist should keep proof boundary'); assert(seedWorklist.canCloseProofGap === false, 'seed-to-intake worklist should not close proof gaps'); assert(seedWorklist.materialType === 'seed_to_intake_worklist', 'seed-to-intake worklist should classify material type'); assert(refresh.artifacts.localSeedToIntakeWorklist?.includes('local-seed-to-intake-worklist-summary.json'), 'refresh artifacts should expose seed-to-intake worklist'); assert(realGapMaterialAudit.materialType === 'three_real_gap_material_audit', 'real gap material audit should classify material type'); assert(realGapMaterialAudit.proofLevel === 'not_business_proof', 'real gap material audit should keep proof boundary'); assert(realGapMaterialAudit.directCustomerProof === false, 'real gap material audit should not be direct customer proof'); assert(realGapMaterialAudit.canCloseProofGap === false, 'real gap material audit should not close proof gaps'); assert(realGapMaterialAudit.complete === false, 'real gap material audit should not claim completion'); assert(realGapMaterialAudit.gapCount === 3 && realGapMaterialAudit.materialFoundCount === 3 && realGapMaterialAudit.openGapCount >= 1, 'real gap material audit should expose three covered but still-open gaps'); assert(refresh.artifacts.realGapMaterialAudit?.includes('real-gap-material-audit-summary.json'), 'refresh artifacts should expose real gap material audit'); assert(realProofClosureWorkOrder.passed === true, 'real proof closure work order should pass'); assert(realProofClosureWorkOrder.complete === false, 'real proof closure work order should not claim completion'); assert(realProofClosureWorkOrder.proofLevel === 'not_business_proof', 'real proof closure work order should keep not-business-proof level'); assert(realProofClosureWorkOrder.directCustomerProof === false, 'real proof closure work order should not be direct customer proof'); assert(realProofClosureWorkOrder.canCloseProofGap === false, 'real proof closure work order should not close proof gaps'); assert(realProofClosureWorkOrder.materialType === 'real_proof_closure_work_order', 'real proof closure work order should classify material type'); assert(realProofClosureWorkOrder.workOrderCount === expectedWorkOrderCount, 'real proof closure work order should expose current work orders'); assert(realProofClosureWorkOrder.ownerArtifacts?.length === expectedOwnerArtifactCount, 'real proof closure work order should expose current owner artifacts'); assert(realProofClosureWorkOrder.ownerArtifacts.some(item => item.markdown?.includes('by-owner/business.md') && item.csv?.includes('by-owner/business.csv')), 'real proof closure work order should expose business owner files'); if (realProofClosureWorkOrder.ownerArtifacts.some(item => item.owner === '商务/投放')) { assert(realProofClosureWorkOrder.ownerArtifacts.some(item => item.markdown?.includes('by-owner/business-media.md') && item.csv?.includes('by-owner/business-media.csv')), 'real proof closure work order should expose business/media owner files when present'); } assert(realProofClosureWorkOrder.ownerArtifacts.some(item => item.markdown?.includes('by-owner/tech-ai.md') && item.csv?.includes('by-owner/tech-ai.csv')), 'real proof closure work order should expose tech/AI owner files'); assert(realProofClosureWorkOrder.sourceState?.proofGapOpenCount === expectedProofGapOpenCount, 'real proof closure work order should expose open proof gap count'); assert(realProofClosureWorkOrder.sourceState?.intakeFailureCount === expectedIntakeFailureCount, 'real proof closure work order should expose intake failures'); assert(realProofClosureWorkOrder.sourceState?.realCandidateRows === expectedRealCandidateRows, 'real proof closure work order should expose real candidate rows'); assert(refresh.artifacts.realProofClosureWorkOrder?.includes('real-proof-closure-work-order-summary.json'), 'refresh artifacts should expose real proof closure work order'); assert(experienceTranscript.directCustomerProof === false, 'experience transcript index should keep proof boundary'); assert(experienceTranscript.canCloseProofGap === false, 'experience transcript index should not close proof gaps'); assert(experienceTranscript.materialType === 'experience_seed', 'experience transcript index should classify as experience seed'); assert(refresh.artifacts.experienceTranscriptIndex?.includes('experience-transcript-index-summary.json'), 'refresh artifacts should expose experience transcript index'); assert(latestForm.manualReviewLabelGuide?.negativeLabelsRequireAttribution === true, 'latest form index should keep negative attribution rule'); assert(latestForm.clientList?.csv?.includes('software-client-list.final.csv'), 'latest form index should expose software client list when present'); assert(latestForm.clientList?.directCustomerProof === false, 'latest form index should keep candidate list proof boundary'); assert(latestForm.clientList?.finalDuplicateGroupCount === 0, 'latest form index should expose zero final duplicate groups'); assert(latestForm.clientList?.duplicateCountMeaning === 'source_duplicate_removed_count', 'latest form index should expose duplicate count meaning'); assert(latestForm.repairActions?.dataIntake?.csv?.includes('intake-readiness-repair-actions.csv'), 'latest form index should expose data intake repair actions'); assert(latestForm.repairActions?.videoResource?.csv?.includes('video-resource-repair-actions.csv'), 'latest form index should expose video resource repair actions'); assert(handoff.latestFormIndex?.repairActions?.dataIntake?.csv?.includes('intake-readiness-repair-actions.csv'), 'handoff should expose data intake repair actions'); assert(handoff.latestFormIndex?.repairActions?.videoResource?.csv?.includes('video-resource-repair-actions.csv'), 'handoff should expose video resource repair actions'); assert(handoff.businessExecutionIndex?.longRunReadiness?.report?.includes('long-run-readiness-report.md'), 'handoff should expose business execution longrun readiness report row'); assert(handoff.businessExecutionIndex?.longRunReadiness?.status === 'blocked', 'handoff should expose business execution longrun blocked status'); assert(handoff.businessExecutionIndex?.operatorPackOwnerArtifactCount === expectedOwnerArtifactCount, 'handoff should expose business execution operator owner attachments'); assert(round.passed === true, 'round deposition should pass'); assert(round.observed.proofGapSoftwareFormPassed === true, 'round deposition should observe software form'); assert(round.observed.manualReviewLabelGuidePassed === true, 'round deposition should observe manual review label guide'); assert(round.observed.latestFormIndexManualReviewLabelGuide?.includes('manual-review-label-guide.md'), 'round deposition should observe indexed manual review label guide'); assert(round.observed.latestFormIndexPlanExecutionStatus?.includes('plan-execution-status.md'), 'round deposition should observe indexed plan execution status'); assert(round.observed.planExecutionStatusRowCount === 8, 'round deposition should observe plan execution row count'); assert(round.observed.planExecutionStatusComplete === false, 'round deposition should observe plan execution incomplete boundary'); assert(round.observed.planExecutionStatusProofOpenCount >= 1, 'round deposition should observe plan execution proof open count'); assert(round.observed.latestFormIndexDataRepairCsv?.includes('intake-readiness-repair-actions.csv'), 'round deposition should observe data intake repair csv'); assert(round.observed.latestFormIndexVideoRepairCsv?.includes('video-resource-repair-actions.csv'), 'round deposition should observe video resource repair csv'); assert(round.observed.latestFormIndexClientListFinalDuplicateGroupCount === 0, 'round deposition should observe zero final duplicate groups'); assert(round.observed.latestFormIndexClientListDuplicateCountMeaning === 'source_duplicate_removed_count', 'round deposition should observe duplicate count meaning'); assert(round.observed.businessExecutionIndexLongRunReadinessReport?.includes('long-run-readiness-report.md'), 'round deposition should observe business execution longrun readiness report'); assert(round.observed.businessExecutionIndexOperatorPackOwnerArtifactCount === expectedOwnerArtifactCount, 'round deposition should observe business execution operator owner attachment count'); assert(round.observed.handoffBusinessExecutionIndexLongRunReadinessReport?.includes('long-run-readiness-report.md'), 'round deposition should observe handoff business execution longrun readiness report'); assert(round.observed.handoffBusinessExecutionIndexLongRunReadinessStatus === 'blocked', 'round deposition should observe handoff business execution longrun blocked status'); assert(round.observed.realProofIntakeBundleLongRunReadinessReport?.includes('long-run-readiness-report.md'), 'round deposition should observe real proof bundle longrun readiness report'); assert(round.observed.realProofIntakeBundleLongRunReadinessReady === false, 'round deposition should observe real proof bundle longrun ready=false'); assert(round.observed.realProofClosureWorkOrderWorkOrderCount === expectedWorkOrderCount, 'round deposition should observe real proof closure work order count'); assert(round.observed.realProofClosureWorkOrderCanCloseProofGap === false, 'round deposition should observe real proof closure work order boundary'); assert(round.observed.realProofClosureWorkOrderOwnerArtifactCount === expectedOwnerArtifactCount, 'round deposition should observe real proof closure work order owner artifacts'); assert(round.observed.latestFormIndexRealProofClosureWorkOrderMarkdown?.includes('real-proof-closure-work-order.md'), 'round deposition should observe latest form real proof closure work order'); assert(round.observed.handoffRealProofClosureWorkOrderMarkdown?.includes('real-proof-closure-work-order.md'), 'round deposition should observe handoff real proof closure work order'); assert(round.observed.handoffLatestFormIndexRealProofClosureWorkOrderMarkdown?.includes('real-proof-closure-work-order.md'), 'round deposition should observe handoff latest form real proof closure work order'); assert(round.observed.realGapMaterialAuditMaterialFoundCount === realGapMaterialAudit.materialFoundCount, 'round deposition should observe real gap material audit coverage'); assert(round.observed.realGapMaterialAuditCanCloseProofGap === false, 'round deposition should observe real gap material audit non-closure boundary'); assert(round.observed.latestFormIndexRealGapMaterialAuditMarkdown?.includes('real-gap-material-audit.md'), 'round deposition should observe latest form real gap material audit'); assert(round.observed.handoffRealGapMaterialAuditMarkdown?.includes('real-gap-material-audit.md'), 'round deposition should observe handoff real gap material audit'); assert(round.observed.evidenceByType['proof-gap-software-form'] === 1, 'round deposition should read refreshed evidence index'); assert(round.observed.evidenceByType['optimization-completion'] === 1, 'round deposition should read completion audit evidence index'); assert(round.observed.evidenceByType['proof-gap-operator-pack'] === 1, 'round deposition should read operator pack evidence index'); assert(round.observed.evidenceByType['real-proof-intake-bundle'] === 1, 'round deposition should read real proof intake bundle evidence index'); assert(round.observed.evidenceByType['local-seed-material-index'] === 1, 'round deposition should read local seed material evidence index'); assert(round.observed.evidenceByType['local-seed-to-intake-worklist'] === 1, 'round deposition should read seed-to-intake worklist evidence index'); assert(round.observed.evidenceByType['real-gap-material-audit'] === 1, 'round deposition should read real gap material audit evidence index'); assert(round.observed.evidenceByType['real-proof-closure-work-order'] === 1, 'round deposition should read real proof closure work order evidence index'); assert(round.observed.evidenceByType['experience-transcript-index'] === 1, 'round deposition should read experience transcript evidence index'); assert(round.observed.evidenceByType['feedback-loop-closure'] === 1, 'round deposition should read feedback loop evidence index'); assert(round.observed.evidenceByType['homepage-evidence-readiness'] === 1, 'round deposition should read homepage readiness evidence index'); console.log(JSON.stringify({ ok: true, outputRoot, evidenceTotal: evidence.total, roundPassCount: round.counts.pass }, null, 2)); } function readJson(file) { return JSON.parse(fs.readFileSync(file, 'utf8')); } function assertVideoResourceReadiness(videoReadiness) { const ready = videoReadiness.acceptance?.readyForVideoAbPreflight === true; const realCandidateRows = Number(videoReadiness.counts?.realCandidateRows || 0); if (ready) { assert(Number(videoReadiness.failureCount || 0) === 0, 'ready video resource state should have zero failures'); assert(realCandidateRows >= 1, 'ready video resource state should expose real candidate rows'); return; } assert(Number(videoReadiness.failureCount || 0) >= 1, 'blocked video resource state should expose failures'); } function seedRequiredOutputs(outputRoot) { fs.mkdirSync(outputRoot, { recursive: true }); [ 'proof-gap-request-latest', 'proof-gap-closure-latest', 'intake-readiness-latest', 'video-resource-readiness-latest', 'data-intake-pack-latest', 'video-intake-pack-latest', 'long-run-readiness-latest', 'business-proof-progress-latest', 'business-proof-next-actions-latest', 'optimization-pipeline-latest', 'video-hit-rate-preflight-latest', 'software-client-latest' ].forEach(name => copyOutputDir(name, outputRoot)); copySoftwareClientSourceSeed(outputRoot); copyRealEvidenceSeed(outputRoot); copyVideoAbAuditSeed(outputRoot); } function copySoftwareClientSourceSeed(outputRoot) { const summaryPath = path.join(ROOT, 'outputs', 'software-client-latest', 'software-client-list.final.summary.json'); if (!fs.existsSync(summaryPath)) return; const summary = readJson(summaryPath); const inputPath = path.resolve(ROOT, summary.input || ''); if (!fs.existsSync(inputPath)) return; const sourceDir = path.dirname(inputPath); const relativeDir = path.relative(path.join(ROOT, 'outputs'), sourceDir); if (!relativeDir || relativeDir.startsWith('..') || path.isAbsolute(relativeDir)) return; copyDir(sourceDir, path.join(outputRoot, relativeDir)); } function copyOutputDir(name, outputRoot) { const source = path.join(ROOT, 'outputs', name); if (!fs.existsSync(source)) return; copyDir(source, path.join(outputRoot, name)); } function copyRealEvidenceSeed(outputRoot) { const outputs = path.join(ROOT, 'outputs'); if (!fs.existsSync(outputs)) return; for (const entry of fs.readdirSync(outputs, { withFileTypes: true })) { if (!entry.isDirectory() || !entry.name.startsWith('overnight-quality-')) continue; const dir = path.join(outputs, entry.name); const aggregatePath = path.join(dir, 'aggregate-summary.json'); if (!fs.existsSync(aggregatePath)) continue; const aggregate = JSON.parse(fs.readFileSync(aggregatePath, 'utf8')); const liveProof = Boolean(aggregate.manifest?.liveEnabled) && aggregate.acceptance?.overallPass === true && Number(aggregate.failureCount || 0) === 0 && Number(aggregate.acceptance?.failedGateCount ?? aggregate.failedGateCount ?? 0) === 0; if (!liveProof) continue; copyDir(dir, path.join(outputRoot, entry.name)); return; } } function copyVideoAbAuditSeed(outputRoot) { const outputs = path.join(ROOT, 'outputs'); if (!fs.existsSync(outputs)) return; for (const entry of fs.readdirSync(outputs, { withFileTypes: true })) { if (!entry.isDirectory() || !entry.name.startsWith('video-hit-rate-audit-')) continue; const dir = path.join(outputs, entry.name); if (!fs.existsSync(path.join(dir, 'video-hit-rate-summary.json'))) continue; copyDir(dir, path.join(outputRoot, entry.name)); return; } } function copyDir(source, target) { fs.mkdirSync(target, { recursive: true }); for (const entry of fs.readdirSync(source, { withFileTypes: true })) { const from = path.join(source, entry.name); const to = path.join(target, entry.name); if (entry.isDirectory()) copyDir(from, to); else if (entry.isFile()) fs.copyFileSync(from, to); } } function assert(condition, message) { if (!condition) throw new Error(message); } if (require.main === module) main();