#!/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, '..'); const outputDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tihao-handoff-')); function main() { const outputsDir = path.join(root, 'outputs'); const operatorPackResult = spawnSync(process.execPath, [ path.join(root, 'scripts', 'proof-gap-operator-pack.js'), '--outputs', outputsDir, '--output', path.join(outputsDir, 'proof-gap-operator-pack-latest'), '--strict' ], { cwd: root, encoding: 'utf8' }); if (operatorPackResult.status !== 0) { process.stderr.write(operatorPackResult.stderr || operatorPackResult.stdout || ''); throw new Error('proof-gap-operator-pack 执行失败'); } const bundleResult = spawnSync(process.execPath, [ path.join(root, 'scripts', 'real-proof-intake-bundle.js'), '--output', path.join(outputsDir, 'real-proof-intake-bundle-latest'), '--strict' ], { cwd: root, encoding: 'utf8' }); if (bundleResult.status !== 0) { process.stderr.write(bundleResult.stderr || bundleResult.stdout || ''); throw new Error('real-proof-intake-bundle 执行失败'); } const completionResult = spawnSync(process.execPath, [ path.join(root, 'scripts', 'optimization-completion-audit.js'), '--outputs', outputsDir, '--output', path.join(outputsDir, 'optimization-completion-latest') ], { cwd: root, encoding: 'utf8' }); if (completionResult.status !== 0) { process.stderr.write(completionResult.stderr || completionResult.stdout || ''); throw new Error('optimization-completion-audit 执行失败'); } const businessIndexResult = spawnSync(process.execPath, [ path.join(root, 'scripts', 'business-execution-index.js'), '--outputs', outputsDir, '--output', path.join(outputsDir, 'business-execution-index-latest'), '--strict' ], { cwd: root, encoding: 'utf8' }); if (businessIndexResult.status !== 0) { process.stderr.write(businessIndexResult.stderr || businessIndexResult.stdout || ''); throw new Error('business-execution-index 执行失败'); } const progressResult = spawnSync(process.execPath, [ path.join(root, 'scripts', 'business-proof-progress.js'), '--outputs', outputsDir, '--output', path.join(outputsDir, 'business-proof-progress-latest') ], { cwd: root, encoding: 'utf8' }); if (progressResult.status !== 0) { process.stderr.write(progressResult.stderr || progressResult.stdout || ''); throw new Error('business-proof-progress 执行失败'); } const nextActionsResult = spawnSync(process.execPath, [ path.join(root, 'scripts', 'business-proof-next-actions.js'), '--outputs', outputsDir, '--output', path.join(outputsDir, 'business-proof-next-actions-latest') ], { cwd: root, encoding: 'utf8' }); if (nextActionsResult.status !== 0) { process.stderr.write(nextActionsResult.stderr || nextActionsResult.stdout || ''); throw new Error('business-proof-next-actions 执行失败'); } const planExecutionResult = spawnSync(process.execPath, [ path.join(root, 'scripts', 'plan-execution-status.js'), '--outputs', outputsDir, '--output', path.join(outputsDir, 'plan-execution-status-latest'), '--strict' ], { cwd: root, encoding: 'utf8' }); if (planExecutionResult.status !== 0) { process.stderr.write(planExecutionResult.stderr || planExecutionResult.stdout || ''); throw new Error('plan-execution-status 执行失败'); } const localSeedWorklistResult = spawnSync(process.execPath, [ path.join(root, 'scripts', 'local-seed-to-intake-worklist.js'), '--outputs', outputsDir, '--output', path.join(outputsDir, 'local-seed-to-intake-worklist-latest') ], { cwd: root, encoding: 'utf8' }); if (localSeedWorklistResult.status !== 0) { process.stderr.write(localSeedWorklistResult.stderr || localSeedWorklistResult.stdout || ''); throw new Error('local-seed-to-intake-worklist 执行失败'); } const realProofClosureWorkOrderResult = spawnSync(process.execPath, [ path.join(root, 'scripts', 'real-proof-closure-work-order.js'), '--outputs', outputsDir, '--output', path.join(outputsDir, 'real-proof-closure-work-order-latest'), '--strict' ], { cwd: root, encoding: 'utf8' }); if (realProofClosureWorkOrderResult.status !== 0) { process.stderr.write(realProofClosureWorkOrderResult.stderr || realProofClosureWorkOrderResult.stdout || ''); throw new Error('real-proof-closure-work-order 执行失败'); } const latestFormResult = spawnSync(process.execPath, [ path.join(root, 'scripts', 'latest-form-index.js'), '--outputs', outputsDir, '--output', path.join(outputsDir, 'latest-form-index-latest'), '--strict' ], { cwd: root, encoding: 'utf8' }); if (latestFormResult.status !== 0) { process.stderr.write(latestFormResult.stderr || latestFormResult.stdout || ''); throw new Error('latest-form-index 执行失败'); } const result = spawnSync(process.execPath, [ path.join(root, 'scripts', 'generate-optimization-handoff.js'), '--output', outputDir ], { cwd: root, encoding: 'utf8' }); if (result.status !== 0) { process.stderr.write(result.stderr || result.stdout || ''); throw new Error('generate-optimization-handoff 执行失败'); } const jsonPath = path.join(outputDir, 'optimization-handoff-summary.json'); const reportPath = path.join(outputDir, 'optimization-handoff-report.md'); assert(fs.existsSync(jsonPath), 'handoff summary json should exist'); assert(fs.existsSync(reportPath), 'handoff report should exist'); const summary = JSON.parse(fs.readFileSync(jsonPath, 'utf8')); const report = fs.readFileSync(reportPath, 'utf8').replace(/^\uFEFF/, ''); const operatorOwnerArtifactCount = Number(summary.proofGapOperatorPack?.ownerArtifactCount || 0); const nextActionCount = Number(summary.businessProofNextActions?.actionCount || 0); const nextActionSourceState = summary.businessProofNextActions?.sourceState || {}; const latestFormNextActionSourceState = summary.latestFormIndex?.sourceState || {}; const pipelineStaleSummary = summary.pipelineStaleSummary || {}; const workOrderCount = Number(summary.realProofClosureWorkOrder?.workOrderCount || 0); assert(summary.complete === false, 'handoff must not mark long-term goal complete without real proof'); assert(summary.readyCapabilities.length >= 1, 'handoff should list ready local capabilities'); assert(summary.readyNotProven.length >= 1, 'handoff should list ready-not-proven work'); assert(summary.externalBlockers.length >= 1, 'handoff should list external-data blockers'); assert(summary.nextCommands.some(item => item.command.includes('customer-effect:audit')), 'handoff should include customer-effect audit command'); assert(summary.nextCommands.some(item => item.command.includes('intake:readiness')), 'handoff should include intake readiness audit command'); assert(summary.nextCommands.some(item => item.command.includes('longrun:readiness')), 'handoff should include long-run readiness command'); assert(summary.nextCommands.some(item => item.command.includes('intake:field-checklist')), 'handoff should include intake field checklist command'); assert(summary.nextCommands.some(item => item.command.includes('video:intake-template')), 'handoff should include video intake command'); assert(summary.nextCommands.some(item => item.command.includes('video:resource-readiness')), 'handoff should include video resource readiness command'); assert(summary.nextCommands.some(item => item.command.includes('acceptance:video-ab')), 'handoff should include video A/B command'); assert(summary.nextCommands.some(item => item.command.includes('proof-gap:request')), 'handoff should include proof gap request command'); assert(summary.nextCommands.some(item => item.command.includes('proof-gap:closure')), 'handoff should include proof gap closure command'); assert(summary.nextCommands.some(item => item.command.includes('feedback:closure')), 'handoff should include feedback loop closure command'); assert(summary.nextCommands.some(item => item.command.includes('business-proof:progress')), 'handoff should include business proof progress command'); assert(summary.nextCommands.some(item => item.command.includes('business-proof:next-actions')), 'handoff should include business proof next actions command'); assert(summary.nextCommands.some(item => item.command.includes('business-execution:index')), 'handoff should include business execution index command'); assert(summary.nextCommands.some(item => item.command.includes('proof-gap:software-form')), 'handoff should include proof gap software form command'); assert(summary.nextCommands.some(item => item.command.includes('latest-form:index')), 'handoff should include latest form index command'); assert(summary.nextCommands.some(item => item.command.includes('local-seed-to-intake:worklist')), 'handoff should include seed-to-intake worklist command'); assert(summary.nextCommands.some(item => item.command.includes('proof-gap:intake-bundle')), 'handoff should include real proof intake bundle command'); assert(summary.nextCommands.some(item => item.command.includes('real-proof:work-order')), 'handoff should include real proof closure work order command'); assert(summary.nextCommands.some(item => item.command.includes('round:refresh') && item.command.includes('--strict')), 'handoff should include ordered round refresh command'); assert(summary.nextCommands.some(item => item.command.includes('optimization:pipeline') && item.command.includes('--data-pack') && item.command.includes('--video-pack')), 'handoff should include data/video pack optimization pipeline command'); assert(summary.proofActionOrder && summary.proofActionOrder.doc.includes('business-proof-action-order'), 'handoff should include business proof action order document'); assert(summary.proofActionOrder.doc.includes('business-proof-action-order.zh-CN.md'), 'handoff should prefer Chinese business proof action order document'); assert(Array.isArray(summary.supportDocs) && summary.supportDocs.length >= 2, 'handoff should include Chinese SOP support docs'); assert(summary.supportDocs.some(item => item.doc.includes('tihao-experience-optimization-plan.zh-CN.md')), 'handoff should include Chinese optimization plan document'); assert(summary.supportDocs.some(item => item.doc.includes('tihao-experience-optimization-plan.md')), 'handoff should include main optimization plan document'); assert(summary.supportDocs.some(item => item.doc.includes('business-proof-action-order.zh-CN.md')), 'handoff should include Chinese action order document'); assert(summary.supportDocs.some(item => item.doc.includes('tihao-handoff-index.md')), 'handoff should include task handoff index document'); assert(summary.businessProofProgress?.summary?.includes('business-proof-progress-summary.json'), 'handoff should include business proof progress summary'); assert(summary.businessProofProgress?.report?.includes('business-proof-progress-report.md'), 'handoff should include business proof progress report'); assert(summary.businessProofProgress?.complete === false, 'handoff business proof progress should keep incomplete state'); assert(summary.businessProofProgress?.counts?.blocked_by_external_data >= 1, 'handoff business proof progress should expose external blockers'); assert(summary.businessProofProgress?.operatorPackOwnerArtifactCount === operatorOwnerArtifactCount, 'handoff business proof progress should expose operator owner attachment count'); assert(summary.businessProofProgress?.businessExecutionOperatorPackOwnerArtifactRowCount === operatorOwnerArtifactCount, 'handoff business proof progress should expose business execution operator attachment rows'); assert(summary.businessProofProgress?.operatorPackOwnerArtifacts?.some(item => item.owner === '商务' && item.csv?.includes('by-owner/business.csv')), 'handoff business proof progress should expose business owner operator csv'); assert(summary.businessProofNextActions && summary.businessProofNextActions.ownerArtifacts.length >= 1, 'handoff should include owner action artifacts'); assert(summary.businessProofNextActions.complete === false, 'handoff should keep next action queue incomplete while proof gaps remain'); assert(summary.businessProofNextActions.actionCount === nextActionCount, 'handoff should expose next action count'); assert(summary.businessProofNextActions.sourceState?.proofGapOpenCount >= 1, 'handoff should expose next action proof gap open count'); assert(nextActionSourceState.pipelineStale === true, 'handoff should expose stale pipeline state for next actions'); assert(Number(nextActionSourceState.pipelineNextActionCount || 0) === 0, 'handoff should expose zero current pipeline actions when stale'); assert(Number(nextActionSourceState.pipelineRawNextActionCount || 0) >= 1, 'handoff should retain raw stale pipeline action count'); assert(Number(nextActionSourceState.pipelineSuppressedNextActionCount || 0) === Number(nextActionSourceState.pipelineRawNextActionCount || 0), 'handoff should suppress every stale pipeline action'); assert(String(nextActionSourceState.pipelineStaleReason || '').includes('stale'), 'handoff should expose stale pipeline reason'); assert(Array.isArray(nextActionSourceState.pipelineStaleMissingProofRequirements) && nextActionSourceState.pipelineStaleMissingProofRequirements.includes('optimizationPipeline.readyForClaim=true'), 'handoff should expose stale pipeline missing proof requirements'); assert(summary.businessProofNextActions.ownerGroupCount >= 1, 'handoff should expose next action owner groups'); assert(summary.businessProofNextActions.ownerArtifactCount >= summary.businessProofNextActions.ownerGroupCount, 'handoff should expose next action owner artifact count'); assert(summary.businessProofNextActions.topActions?.every(item => !String(item.source || '').includes('optimization-pipeline')), 'handoff should not expose stale optimization-pipeline top actions'); assert(summary.businessProofNextActions.topActions?.some(item => item.id === 'step-03' && item.command?.includes('history-data-template.csv')), 'handoff should expose top historical data action'); assert(summary.businessProofNextActions.topActions?.some(item => item.id === 'gap-video-ab-live-proof' && item.command?.includes('acceptance:video-ab')), 'handoff should expose top video A/B proof action'); assert(summary.businessProofNextActions.ownerArtifacts.some(item => item.markdown.includes('by-owner/')), 'handoff should include owner markdown files'); assert(summary.businessProofNextActions.ownerArtifacts.some(item => item.csv.includes('by-owner/')), 'handoff should include owner csv files'); assert(summary.businessProofNextActions.ownerArtifacts.some(item => item.owner === '商务' && item.repairArtifacts?.some(artifact => artifact.csv.includes('intake-readiness-repair-actions.csv'))), 'handoff should include business owner data repair artifact'); assert(summary.businessProofNextActions.ownerArtifacts.some(item => item.repairArtifacts?.some(artifact => artifact.csv.includes('homepage-evidence-repair-actions.csv') || artifact.csv.includes('video-resource-repair-actions.csv'))), 'handoff should include owner repair artifacts'); assert(pipelineStaleSummary.pipelineStale === true, 'handoff should expose top-level pipeline stale summary'); assert(pipelineStaleSummary.pipelineReadyForClaim === false, 'top-level pipeline stale summary should keep readyForClaim false'); assert(Number(pipelineStaleSummary.pipelineNextActionCount || 0) === 0, 'top-level pipeline stale summary should expose zero current pipeline actions'); assert(Number(pipelineStaleSummary.pipelineRawNextActionCount || 0) === Number(nextActionSourceState.pipelineRawNextActionCount || 0), 'top-level pipeline stale summary should mirror raw pipeline action count'); assert(Number(pipelineStaleSummary.pipelineSuppressedNextActionCount || 0) === Number(nextActionSourceState.pipelineSuppressedNextActionCount || 0), 'top-level pipeline stale summary should mirror suppressed pipeline action count'); assert(Number(pipelineStaleSummary.pipelineStaleMissingProofRequirementCount || 0) === nextActionSourceState.pipelineStaleMissingProofRequirements.length, 'top-level pipeline stale summary should expose missing proof requirement count'); assert(String(pipelineStaleSummary.boundary || '').includes('not current dispatch'), 'top-level pipeline stale summary should expose dispatch boundary'); assert(summary.businessExecutionIndex && summary.businessExecutionIndex.summary.includes('business-execution-index-summary.json'), 'handoff should include business execution index summary'); assert(summary.businessExecutionIndex.report.includes('business-execution-index.md'), 'handoff should include business execution index report'); assert(summary.businessExecutionIndex.csv.includes('business-execution-index.csv'), 'handoff should include business execution index csv'); assert(summary.businessExecutionIndex.directCustomerProof === false, 'business execution index should not be direct customer proof'); assert(summary.businessExecutionIndex.operatorPackOwnerArtifactCount === operatorOwnerArtifactCount, 'handoff business execution index should expose operator pack owner artifact count'); assert(summary.businessExecutionIndex.operatorPackOwnerArtifactRowCount === operatorOwnerArtifactCount, 'handoff business execution index should expose operator pack owner artifact rows'); assert(summary.businessExecutionIndex.operatorPackOwnerArtifacts?.some(item => item.owner === '商务' && item.csv?.includes('by-owner/business.csv')), 'handoff business execution index should expose business operator owner csv'); assert(summary.businessExecutionIndex.longRunReadiness?.report?.includes('long-run-readiness-report.md'), 'handoff business execution index should expose long-run readiness report row'); assert(summary.businessExecutionIndex.longRunReadiness?.summary?.includes('long-run-readiness-summary.json'), 'handoff business execution index should expose long-run readiness source summary'); assert(summary.businessExecutionIndex.longRunReadiness?.status === 'blocked', 'handoff business execution index should keep long-run readiness blocked while ready=false'); assert(summary.businessExecutionIndex.longRunReadiness?.ready === false, 'handoff business execution index should expose long-run ready=false'); assert(summary.businessExecutionIndex.longRunReadiness?.failCount >= 1, 'handoff business execution index should expose long-run fail count'); assert(summary.businessExecutionIndex.longRunReadiness?.directCustomerProof === false, 'handoff business execution long-run entry should not be direct customer proof'); assert(summary.businessExecutionIndex.optimizationCompletion?.report?.includes('optimization-completion-report.md'), 'handoff business execution index should expose optimization completion report row'); assert(summary.businessExecutionIndex.optimizationCompletion?.summary?.includes('optimization-completion-summary.json'), 'handoff business execution index should expose optimization completion source summary'); assert(summary.businessExecutionIndex.optimizationCompletion?.status === 'blocked', 'handoff business execution index should keep optimization completion blocked while readyForClaim=false'); assert(summary.businessExecutionIndex.optimizationCompletion?.readyForClaim === false, 'handoff business execution index should expose readyForClaim=false'); assert(summary.businessExecutionIndex.optimizationCompletion?.proofGapOpenCount >= 1, 'handoff business execution index should expose completion proof gap open count'); assert(summary.businessExecutionIndex.optimizationCompletion?.blockingReasonsWithMissingProofRequirements >= 1, 'handoff business execution index should expose completion blockers with missing proof requirements'); assert(summary.businessExecutionIndex.optimizationCompletion?.missingProofRequirementCount >= 1, 'handoff business execution index should expose completion missing proof requirement count'); assert(summary.businessExecutionIndex.optimizationCompletion?.videoAbPreflightReadyForVideoAb === false, 'handoff business execution index should expose video A/B preflight readiness'); assert(Number.isFinite(Number(summary.businessExecutionIndex.optimizationCompletion?.videoAbPreflightFailureCount)), 'handoff business execution index should expose video A/B preflight failure count'); assert(summary.businessExecutionIndex.optimizationCompletion?.directCustomerProof === false, 'handoff business execution completion entry should not be direct customer proof'); assert(summary.proofGapClosure && summary.proofGapClosure.summary.includes('proof-gap-closure-summary.json'), 'handoff should include proof gap closure summary'); assert(summary.proofGapClosure.report.includes('proof-gap-closure-report.md'), 'handoff should include proof gap closure report'); assert(summary.proofGapClosure.openCount >= 1, 'handoff should expose proof gap closure open count'); assert(summary.proofGapClosure.businessOpenCount === summary.proofGapClosure.openCount, 'handoff should keep openCount as businessOpenCount alias'); assert(summary.proofGapClosure.businessClosedCount === summary.proofGapClosure.closedCount, 'handoff should keep closedCount as businessClosedCount alias'); assert(summary.proofGapClosure.businessGapCount >= summary.proofGapClosure.businessOpenCount, 'handoff should expose business proof gap count'); assert(summary.proofGapClosure.precheckRowCount >= 1, 'handoff should expose precheck row count separately'); assert(summary.proofGapClosure.totalRows >= summary.proofGapClosure.businessGapCount + summary.proofGapClosure.precheckRowCount, 'handoff should expose total rows including prechecks'); assert(summary.proofGapClosure.countSemantics?.openCount === 'business_gap_open_count_excludes_precheck_rows', 'handoff should expose proof gap closure count semantics'); assert(summary.proofGapClosure.rows.some(row => row.id === 'video-ab-live-proof' && row.missingProofRequirements.includes('proofContext.mode=live')), 'handoff should expose video A/B missing live proof requirement'); if (summary.feedbackLoopClosure) { assert(summary.feedbackLoopClosure.summary.includes('feedback-loop-closure-summary.json'), 'handoff should include feedback loop closure summary'); assert(summary.feedbackLoopClosure.report.includes('feedback-loop-closure-report.md'), 'handoff should include feedback loop closure report'); assert(summary.feedbackLoopClosure.csv.includes('feedback-loop-closure.csv'), 'handoff should include feedback loop closure csv'); assert(summary.feedbackLoopClosure.directCustomerProof === false, 'feedback loop closure should not be direct customer proof by default'); } if (summary.homepageEvidenceReadiness) { assert(summary.homepageEvidenceReadiness.summary.includes('homepage-evidence-readiness-summary.json'), 'handoff should include homepage evidence readiness summary'); assert(summary.homepageEvidenceReadiness.report.includes('homepage-evidence-readiness-report.md'), 'handoff should include homepage evidence readiness report'); assert(summary.homepageEvidenceReadiness.csv.includes('homepage-evidence-repair-actions.csv'), 'handoff should include homepage evidence repair csv'); assert(summary.homepageEvidenceReadiness.directCustomerProof === false, 'homepage evidence readiness should not be direct customer proof'); assert(['not_business_proof', 'smoke_or_local'].includes(summary.homepageEvidenceReadiness.proofLevel), 'homepage evidence readiness should classify proof level'); } assert(summary.longRunReadiness?.summary?.includes('long-run-readiness-summary.json'), 'handoff should include long-run readiness summary'); assert(summary.longRunReadiness?.report?.includes('long-run-readiness-report.md'), 'handoff should include long-run readiness report'); assert(summary.longRunReadiness?.directCustomerProof === false, 'long-run readiness should not be direct customer proof'); assert(summary.longRunReadiness?.proofLevel === 'not_business_proof', 'long-run readiness should keep not-business-proof level while blocked'); assert(summary.longRunReadiness?.ready === false, 'long-run readiness should expose ready=false while proof gaps remain'); assert(summary.longRunReadiness?.failCount >= 1, 'long-run readiness should expose failing preflight checks'); assert(summary.optimizationCompletion?.summary?.includes('optimization-completion-summary.json'), 'handoff should include optimization completion summary'); assert(summary.optimizationCompletion?.report?.includes('optimization-completion-report.md'), 'handoff should include optimization completion report'); assert(summary.optimizationCompletion?.directCustomerProof === false, 'optimization completion should not be direct customer proof while blocked'); assert(summary.optimizationCompletion?.proofLevel === 'not_business_proof', 'optimization completion should keep not-business-proof level while blocked'); assert(summary.optimizationCompletion?.readyForClaim === false, 'optimization completion should expose readyForClaim=false while proof gaps remain'); assert(summary.optimizationCompletion?.blockingReasonsWithMissingProofRequirements >= 1, 'optimization completion should expose blockers with missing proof requirements'); assert(summary.optimizationCompletion?.missingProofRequirementCount >= 1, 'optimization completion should expose missing proof requirement count'); assert(summary.optimizationCompletion?.videoAbPreflightReadyForVideoAb === false, 'optimization completion should expose video A/B preflight readiness'); assert(Number.isFinite(Number(summary.optimizationCompletion?.videoAbPreflightFailureCount)), 'optimization completion should expose video A/B preflight failure count'); assert(summary.optimizationCompletion?.canClaim?.tihaoRateImproved === false, 'optimization completion should forbid tihao-rate claim'); assert(summary.optimizationCompletion?.canClaim?.customerEffectAchieved === false, 'optimization completion should forbid customer-effect claim'); assert(summary.optimizationCompletion?.canClaim?.manualSupplementReduced === false, 'optimization completion should forbid manual-supplement claim'); assert(summary.proofGapSoftwareForm && summary.proofGapSoftwareForm.summary.includes('tihao-proof-gap-software-form-summary.json'), 'handoff should include latest proof gap software form summary'); assert(summary.proofGapSoftwareForm.csv.includes('tihao-proof-gap-software-form.csv'), 'handoff should include latest proof gap software form csv'); assert(summary.proofGapSoftwareForm.markdown.includes('tihao-proof-gap-software-form.md'), 'handoff should include latest proof gap software form markdown'); assert(summary.proofGapSoftwareForm.passed === true, 'handoff should record latest proof gap software form pass state'); assert(summary.proofGapSoftwareForm.rowCount === 5, 'handoff should record latest proof gap software form row count'); assert(summary.proofGapSoftwareForm.duplicateIdCount === 0, 'handoff should record latest proof gap software form duplicate id count'); assert(summary.intakeFieldChecklist && summary.intakeFieldChecklist.summary.includes('intake-field-checklist-summary.json'), 'handoff should include intake field checklist summary'); assert(summary.intakeFieldChecklist.report.includes('intake-field-checklist.md'), 'handoff should include intake field checklist report'); assert(summary.intakeFieldChecklist.csv.includes('intake-field-checklist.csv'), 'handoff should include intake field checklist csv'); assert(summary.intakeFieldChecklist.directCustomerProof === false, 'intake field checklist should not be direct customer proof'); assert(Array.isArray(summary.intakeFieldChecklist.ownerGroups) && summary.intakeFieldChecklist.ownerGroups.length >= 1, 'handoff should include intake field checklist owner groups'); assert(summary.intakeFieldChecklist.ownerGroups.some(item => item.owner === '商务' && item.files?.some(file => file.includes('history-data-template.csv'))), 'handoff should include business history owner group files'); assert(Array.isArray(summary.intakeFieldChecklist.recheckCommands) && summary.intakeFieldChecklist.recheckCommands.length >= 1, 'handoff should include intake field checklist recheck commands'); assert(summary.intakeFieldChecklist.recheckCommands.some(item => item.command.includes('intake:readiness')), 'handoff should include intake readiness recheck command'); assert(summary.intakeFieldChecklist.recheckCommands.some(item => item.command.includes('acceptance:video-ab')), 'handoff should include video A/B recheck command'); assert(summary.latestFormIndex && summary.latestFormIndex.summary.includes('latest-form-index-summary.json'), 'handoff should include latest form index summary'); assert(summary.latestFormIndex.report.includes('latest-form-index.md'), 'handoff should include latest form index report'); assert(summary.latestFormIndex.passed === true, 'handoff should record latest form index pass state'); assert(latestFormNextActionSourceState.pipelineStale === true, 'handoff latest form index should mirror stale pipeline state'); assert(Number(latestFormNextActionSourceState.pipelineNextActionCount || 0) === 0, 'handoff latest form index should mirror zero current pipeline actions when stale'); assert(Number(latestFormNextActionSourceState.pipelineSuppressedNextActionCount || 0) === Number(nextActionSourceState.pipelineSuppressedNextActionCount || 0), 'handoff latest form index should mirror suppressed stale pipeline count'); assert(summary.latestFormIndex.primaryFormCsv.includes('tihao-proof-gap-software-form.csv'), 'handoff should record latest indexed proof form csv'); assert(summary.latestFormIndex.clientListCsv.includes('software-client-list.final.csv'), 'handoff should record latest indexed client list csv'); assert(summary.latestFormIndex.intakeFieldChecklist?.ownerGroups?.length >= 1, 'handoff should record latest form index field owner groups'); assert(summary.latestFormIndex.intakeFieldChecklist?.recheckCommands?.length >= 1, 'handoff should record latest form index recheck commands'); assert(summary.latestFormIndex.manualReviewLabelGuide?.markdown?.includes('manual-review-label-guide.md'), 'handoff should record latest form manual review label guide'); assert(summary.latestFormIndex.manualReviewLabelGuide?.directCustomerProof === false, 'handoff should carry manual review label guide proof boundary'); assert(summary.latestFormIndex.manualReviewLabelGuide?.negativeLabelsRequireAttribution === true, 'handoff should carry manual review negative attribution rule'); assert(summary.latestFormIndex.businessFillGuide?.directCustomerProof === false, 'handoff should carry latest form business fill guide proof boundary'); assert(summary.latestFormIndex.businessFillGuide?.rowCount >= 2, 'handoff should carry latest form business fill guide rows'); assert(summary.latestFormIndex.businessFillGuide.rows.some(item => item.section === 'history' && item.file.includes('history-data-template.csv')), 'handoff should carry history fill guide row'); assert(summary.latestFormIndex.businessFillGuide.rows.some(item => item.section === 'video' && item.recheckCommand.includes('video:resource-readiness')), 'handoff should carry video fill guide recheck command'); assert(summary.latestFormIndex.planExecutionStatus?.markdown?.includes('plan-execution-status.md'), 'handoff should record latest form plan execution status'); assert(summary.latestFormIndex.planExecutionStatus?.directCustomerProof === false, 'handoff should carry plan execution proof boundary through latest form index'); assert(summary.latestFormIndex.planExecutionStatus?.rowCount === 8, 'handoff should carry plan execution eight-section status'); assert(summary.latestFormIndex.planExecutionStatus?.complete === false, 'handoff should carry plan execution incomplete state'); assert(summary.latestFormIndex.planExecutionStatus?.operatorPackOwnerArtifactCount === operatorOwnerArtifactCount, 'handoff should carry latest form plan execution operator owner artifact count'); assert(summary.latestFormIndex.planExecutionStatus?.operatorPackOwnerArtifacts?.some(item => item.csv?.includes('by-owner/business.csv')), 'handoff should carry latest form plan execution business owner csv'); assert(summary.latestFormIndex.longRunReadiness?.report?.includes('long-run-readiness-report.md'), 'handoff should carry latest form long-run readiness report'); assert(summary.latestFormIndex.longRunReadiness?.ready === false, 'handoff should carry latest form long-run ready=false state'); assert(summary.latestFormIndex.longRunReadiness?.directCustomerProof === false, 'handoff should carry long-run readiness proof boundary through latest form index'); assert(summary.latestFormIndex.optimizationCompletion?.report?.includes('optimization-completion-report.md'), 'handoff should carry latest form optimization completion report'); assert(summary.latestFormIndex.optimizationCompletion?.summary?.includes('optimization-completion-summary.json'), 'handoff should carry latest form optimization completion summary'); assert(summary.latestFormIndex.optimizationCompletion?.readyForClaim === false, 'handoff should carry latest form optimization readyForClaim=false state'); assert(summary.latestFormIndex.optimizationCompletion?.videoAbPreflightReadyForVideoAb === false, 'handoff should carry latest form video A/B preflight readiness'); assert(Number.isFinite(Number(summary.latestFormIndex.optimizationCompletion?.videoAbPreflightFailureCount)), 'handoff should carry latest form video A/B preflight failure count'); assert(summary.latestFormIndex.optimizationCompletion?.directCustomerProof === false, 'handoff should carry optimization completion proof boundary through latest form index'); assert(summary.localSeedToIntakeWorklist?.markdown?.includes('local-seed-to-intake-worklist.md'), 'handoff should include top-level seed-to-intake worklist report'); assert(summary.localSeedToIntakeWorklist?.historyDraft?.includes('history-intake-draft.csv'), 'handoff should include top-level seed-to-intake history draft'); assert(summary.localSeedToIntakeWorklist?.videoWorklist?.includes('video-resource-worklist.csv'), 'handoff should include top-level seed-to-intake video worklist'); assert(summary.localSeedToIntakeWorklist?.candidateWorklist?.includes('candidate-seed-worklist.csv'), 'handoff should include top-level seed-to-intake candidate worklist'); assert(summary.localSeedToIntakeWorklist?.directCustomerProof === false, 'handoff seed-to-intake worklist should not be direct customer proof'); assert(summary.localSeedToIntakeWorklist?.proofLevel === 'not_business_proof', 'handoff seed-to-intake worklist should keep not-business-proof level'); assert(summary.localSeedToIntakeWorklist?.materialType === 'seed_to_intake_worklist', 'handoff seed-to-intake worklist should keep material type'); assert(summary.localSeedToIntakeWorklist?.canCloseProofGap === false, 'handoff seed-to-intake worklist should not close proof gaps'); assert(summary.localSeedToIntakeWorklist?.candidateSeedRows >= 1, 'handoff seed-to-intake worklist should expose candidate seed rows'); assert(summary.latestFormIndex.localSeedToIntakeWorklist?.historyDraft?.includes('history-intake-draft.csv'), 'handoff should carry latest form seed-to-intake history draft'); assert(summary.latestFormIndex.localSeedToIntakeWorklist?.directCustomerProof === false, 'handoff latest form seed-to-intake worklist should keep proof boundary'); assert(summary.realProofClosureWorkOrder?.markdown?.includes('real-proof-closure-work-order.md'), 'handoff should include real proof closure work order report'); assert(summary.realProofClosureWorkOrder?.csv?.includes('real-proof-closure-work-order.csv'), 'handoff should include real proof closure work order csv'); assert(summary.realProofClosureWorkOrder?.summary?.includes('real-proof-closure-work-order-summary.json'), 'handoff should include real proof closure work order summary'); assert(summary.realProofClosureWorkOrder?.directCustomerProof === false, 'real proof closure work order should not be direct customer proof'); assert(summary.realProofClosureWorkOrder?.proofLevel === 'not_business_proof', 'real proof closure work order should keep not-business-proof level'); assert(summary.realProofClosureWorkOrder?.materialType === 'real_proof_closure_work_order', 'real proof closure work order should expose material type'); assert(summary.realProofClosureWorkOrder?.complete === false, 'real proof closure work order should not claim completion'); assert(summary.realProofClosureWorkOrder?.canCloseProofGap === false, 'real proof closure work order should not close proof gaps'); assert(summary.realProofClosureWorkOrder?.workOrderCount >= 5, 'real proof closure work order should expose work orders'); assert(summary.realProofClosureWorkOrder?.ownerGroupCount >= 1, 'real proof closure work order should expose owner groups'); assert(summary.realProofClosureWorkOrder?.proofGapOpenCount >= 1, 'real proof closure work order should expose open proof gaps'); assert(summary.realProofClosureWorkOrder?.intakeFailureCount >= 1, 'real proof closure work order should expose intake failures'); assert(summary.realProofClosureWorkOrder?.realCandidateRows >= 0, 'real proof closure work order should expose real candidate rows'); assert(summary.realProofClosureWorkOrder?.ownerArtifacts?.some(item => item.csv?.includes('by-owner/business.csv')), 'real proof closure work order should expose business owner csv'); assert(summary.realProofClosureWorkOrder?.ownerArtifacts?.some(item => item.csv?.includes('by-owner/tech-ai.csv')), 'real proof closure work order should expose tech/AI owner csv'); if (summary.realProofClosureWorkOrder?.ownerArtifacts?.some(item => item.owner === '商务/投放')) { assert(summary.realProofClosureWorkOrder.ownerArtifacts.some(item => item.markdown?.includes('by-owner/business-media.md')), 'real proof closure work order should expose business/media owner markdown when present'); } assert(summary.latestFormIndex.realProofClosureWorkOrder?.markdown?.includes('real-proof-closure-work-order.md'), 'handoff should carry latest form real proof closure work order'); assert(summary.latestFormIndex.realProofClosureWorkOrder?.workOrderCount === workOrderCount, 'handoff latest form real proof closure work order should expose work order count'); assert(summary.latestFormIndex.realProofClosureWorkOrder?.canCloseProofGap === false, 'handoff latest form real proof closure work order should keep non-closure boundary'); if (summary.latestFormIndex.realProofClosureWorkOrder?.ownerArtifacts?.some(item => item.owner === '商务/投放')) { assert(summary.latestFormIndex.realProofClosureWorkOrder.ownerArtifacts.some(item => item.csv?.includes('by-owner/business-media.csv')), 'handoff latest form real proof closure work order should expose business/media csv when present'); } assert(summary.planExecutionStatus?.markdown?.includes('plan-execution-status.md'), 'handoff should include top-level plan execution status'); assert(summary.planExecutionStatus?.csv?.includes('plan-execution-status.csv'), 'handoff should include plan execution status csv'); assert(summary.planExecutionStatus?.summary?.includes('plan-execution-status-summary.json'), 'handoff should include plan execution status summary'); assert(summary.planExecutionStatus?.directCustomerProof === false, 'plan execution status should not be direct customer proof'); assert(summary.planExecutionStatus?.rowCount === 8, 'plan execution status should expose eight plan sections'); assert(summary.planExecutionStatus?.complete === false, 'plan execution status should keep incomplete state while proof gaps remain'); assert(summary.planExecutionStatus?.operatorPackOwnerArtifactCount === operatorOwnerArtifactCount, 'plan execution status should expose operator owner attachment count'); assert(summary.planExecutionStatus?.businessExecutionOperatorPackOwnerArtifactRowCount === operatorOwnerArtifactCount, 'plan execution status should expose business execution operator attachment rows'); if (summary.planExecutionStatus?.operatorPackOwnerArtifacts?.some(item => item.owner === '商务/投放')) { assert(summary.planExecutionStatus.operatorPackOwnerArtifacts.some(item => item.markdown?.includes('by-owner/business-media.md')), 'plan execution status should expose business/media owner markdown when present'); } assert(summary.proofGapRequest?.markdown?.includes('proof-gap-request-report.md'), 'handoff should include proof gap request report'); assert(summary.proofGapRequest?.csv?.includes('proof-gap-request-table.csv'), 'handoff should include proof gap request csv'); assert(summary.proofGapRequest?.summary?.includes('proof-gap-request-summary.json'), 'handoff should include proof gap request summary'); assert(summary.proofGapRequest?.directCustomerProof === false, 'proof gap request should not be direct customer proof'); assert(summary.proofGapRequest?.proofLevel === 'smoke_or_local', 'proof gap request should keep smoke/local proof level'); assert(summary.proofGapRequest?.unresolvedCount >= 1, 'proof gap request should expose unresolved gaps'); assert(summary.latestFormIndex.proofGapRequest?.markdown?.includes('proof-gap-request-report.md'), 'handoff should carry latest form proof gap request report'); assert(summary.latestFormIndex.proofGapRequest?.directCustomerProof === false, 'handoff should carry proof gap request boundary through latest form index'); assert(summary.proofGapOperatorPack?.markdown?.includes('proof-gap-operator-pack.md'), 'handoff should include proof gap operator pack report'); assert(summary.proofGapOperatorPack?.csv?.includes('proof-gap-operator-pack.csv'), 'handoff should include proof gap operator pack csv'); assert(summary.proofGapOperatorPack?.summary?.includes('proof-gap-operator-pack-summary.json'), 'handoff should include proof gap operator pack summary'); assert(summary.proofGapOperatorPack?.directCustomerProof === false, 'proof gap operator pack should not be direct customer proof'); assert(summary.proofGapOperatorPack?.proofLevel === 'not_business_proof', 'proof gap operator pack should keep not-business-proof level'); assert(summary.proofGapOperatorPack?.openCount >= 1, 'proof gap operator pack should expose open count'); assert(summary.proofGapOperatorPack?.rowCount === 5, 'proof gap operator pack should expose five rows'); assert(summary.proofGapOperatorPack?.ownerGroupCount >= operatorOwnerArtifactCount, 'proof gap operator pack should expose owner group count'); assert(summary.proofGapOperatorPack?.ownerArtifactCount === operatorOwnerArtifactCount, 'proof gap operator pack should expose owner artifact count'); assert(Array.isArray(summary.proofGapOperatorPack?.ownerArtifacts) && summary.proofGapOperatorPack.ownerArtifacts.length === operatorOwnerArtifactCount, 'proof gap operator pack should expose owner artifacts'); assert(summary.proofGapOperatorPack.ownerArtifacts.some(item => item.owner === '商务' && item.markdown?.includes('by-owner/business.md') && item.csv?.includes('by-owner/business.csv')), 'proof gap operator pack should expose business owner files'); assert(summary.proofGapOperatorPack.ownerArtifacts.some(item => item.owner === '技术/AI' && item.markdown?.includes('by-owner/tech-ai.md') && item.csv?.includes('by-owner/tech-ai.csv')), 'proof gap operator pack should expose tech/AI owner files'); if (summary.proofGapOperatorPack.ownerArtifacts.some(item => item.owner === '商务/投放')) { assert(summary.proofGapOperatorPack.ownerArtifacts.some(item => item.markdown?.includes('by-owner/business-media.md') && item.csv?.includes('by-owner/business-media.csv')), 'proof gap operator pack should expose business/media owner files when present'); } assert(summary.proofGapOperatorPack?.recheckCommandCount >= 5, 'proof gap operator pack should expose recheck command count'); assert(summary.proofGapOperatorPack?.evidenceRowCount === 5, 'proof gap operator pack should expose evidence row count'); assert(summary.proofGapOperatorPack?.nextActionCount === 5, 'proof gap operator pack should expose next action count'); assert(summary.proofGapOperatorPack?.missingProofRequirementCount >= 1, 'proof gap operator pack should expose missing proof requirement count'); assert(Array.isArray(summary.proofGapOperatorPack?.gapIds) && summary.proofGapOperatorPack.gapIds.includes('historical-dataset') && summary.proofGapOperatorPack.gapIds.includes('manual-review-and-customer-effect'), 'proof gap operator pack should expose stable gap ids'); assert(summary.latestFormIndex.proofGapOperatorPack?.markdown?.includes('proof-gap-operator-pack.md'), 'handoff should carry latest form proof gap operator pack report'); assert(summary.latestFormIndex.proofGapOperatorPack?.ownerArtifactCount === operatorOwnerArtifactCount, 'handoff should carry latest form proof gap operator owner artifact count'); assert(summary.latestFormIndex.proofGapOperatorPack?.ownerArtifacts?.some(item => item.csv?.includes('by-owner/business.csv')), 'handoff should carry latest form business owner operator csv'); assert(summary.latestFormIndex.proofGapOperatorPack?.recheckCommandCount >= 5, 'handoff should carry latest form proof gap operator recheck count'); assert(summary.latestFormIndex.proofGapOperatorPack?.evidenceRowCount === 5, 'handoff should carry latest form proof gap operator evidence row count'); assert(summary.latestFormIndex.proofGapOperatorPack?.nextActionCount === 5, 'handoff should carry latest form proof gap operator next action count'); assert(summary.realProofIntakeBundle?.markdown?.includes('real-proof-intake-bundle.md'), 'handoff should include real proof intake bundle report'); assert(summary.realProofIntakeBundle?.csv?.includes('real-proof-intake-bundle.csv'), 'handoff should include real proof intake bundle csv'); assert(summary.realProofIntakeBundle?.directCustomerProof === false, 'real proof intake bundle should not be direct customer proof'); assert(summary.realProofIntakeBundle?.proofLevel === 'not_business_proof', 'real proof intake bundle should keep not-business-proof level'); assert(summary.realProofIntakeBundle?.proofOpenCount >= 1, 'real proof intake bundle should expose open proof count'); assert(summary.realProofIntakeBundle?.fillRowCount >= 3, 'real proof intake bundle should expose fill rows'); assert(summary.realProofIntakeBundle?.operatorPackEvidenceRowCount === 5, 'real proof intake bundle should expose operator pack evidence row count'); assert(summary.realProofIntakeBundle?.operatorPackNextActionCount === 5, 'real proof intake bundle should expose operator pack next action count'); assert(summary.realProofIntakeBundle?.operatorPackMissingProofRequirementCount >= 1, 'real proof intake bundle should expose operator pack missing proof requirement count'); assert(summary.realProofIntakeBundle?.operatorPackOwnerArtifactCount === operatorOwnerArtifactCount, 'real proof intake bundle should expose operator pack owner artifact count'); assert(summary.realProofIntakeBundle?.operatorPackOwnerArtifacts?.some(item => item.csv?.includes('by-owner/business.csv')), 'real proof intake bundle should expose business operator owner csv'); assert(summary.latestFormIndex.realProofIntakeBundle?.operatorPackEvidenceRowCount === 5, 'handoff should carry latest form real proof bundle operator evidence row count'); assert(summary.latestFormIndex.realProofIntakeBundle?.operatorPackNextActionCount === 5, 'handoff should carry latest form real proof bundle operator next action count'); assert(summary.latestFormIndex.realProofIntakeBundle?.operatorPackOwnerArtifactCount === operatorOwnerArtifactCount, 'handoff should carry latest form real proof bundle operator owner artifact count'); if (summary.latestFormIndex.realProofIntakeBundle?.operatorPackOwnerArtifacts?.some(item => item.owner === '商务/投放')) { assert(summary.latestFormIndex.realProofIntakeBundle.operatorPackOwnerArtifacts.some(item => item.markdown?.includes('by-owner/business-media.md')), 'handoff should carry latest form real proof bundle business/media owner markdown when present'); } assert(summary.latestFormIndex.repairActions?.dataIntake?.csv?.includes('intake-readiness-repair-actions.csv'), 'handoff should record data intake repair actions csv'); assert(summary.latestFormIndex.repairActions?.videoResource?.csv?.includes('video-resource-repair-actions.csv'), 'handoff should record video repair actions csv'); assert(summary.optimizationPipeline && summary.optimizationPipeline.summary.includes('optimization-pipeline-summary.json'), 'handoff should include optimization pipeline summary'); assert(Array.isArray(summary.optimizationPipeline.nextActions), 'handoff should expose optimization pipeline next actions'); assert(report.includes('# 提号优化交接摘要'), 'handoff report should be Chinese'); assert(report.includes('## 补证操作顺序'), 'handoff report should include proof action order section'); assert(report.includes('## 补证进度表'), 'handoff report should include proof progress section'); assert(report.includes('business-proof-progress-summary.json'), 'handoff report should include business proof progress summary'); assert(report.includes(`operatorPackOwnerArtifactCount: ${operatorOwnerArtifactCount}`), 'handoff report should include business proof progress operator owner attachment count'); assert(report.includes('blocked_by_external_data'), 'handoff report should include business proof progress blocker count'); assert(report.includes('by-owner/business.csv'), 'handoff report should include business proof progress business owner csv'); assert(report.includes('## 下一步行动队列'), 'handoff report should include next actions section'); assert(report.includes(`actionCount: ${nextActionCount}`), 'handoff report should include next action count'); assert(report.includes('proofGapOpenCount'), 'handoff report should include next action source proof gap open count'); assert(report.includes('pipelineStale: true'), 'handoff report should include stale pipeline state'); assert(report.includes(`pipelineSuppressedNextActionCount: ${nextActionSourceState.pipelineSuppressedNextActionCount}`), 'handoff report should include suppressed stale pipeline action count'); assert(report.includes('pipelineStaleReason:'), 'handoff report should include stale pipeline reason'); assert(report.includes('step-03'), 'handoff report should include top historical data action id'); assert(report.includes('gap-video-ab-live-proof'), 'handoff report should include top video A/B action id'); assert(report.includes('acceptance:video-ab'), 'handoff report should include top video A/B command'); assert(report.includes('## Pipeline stale 摘要'), 'handoff report should include top-level pipeline stale summary section'); assert(report.includes(`pipelineRawNextActionCount: ${pipelineStaleSummary.pipelineRawNextActionCount}`), 'handoff report should include raw stale pipeline action count'); assert(report.includes(`pipelineStaleMissingProofRequirementCount: ${pipelineStaleSummary.pipelineStaleMissingProofRequirementCount}`), 'handoff report should include stale pipeline missing proof count'); assert(report.includes('## 负责人行动文件'), 'handoff report should include owner action artifact section'); assert(report.includes('## 商务执行总表'), 'handoff report should include business execution index section'); assert(report.includes('business-execution-index.csv'), 'handoff report should include business execution index csv'); assert(report.includes('businessExecutionOperatorPackOwnerArtifactRowCount'), 'handoff report should include plan execution operator owner attachment row count'); assert(report.includes(`operatorPackOwnerArtifactCount:${operatorOwnerArtifactCount}`), 'handoff report should include business execution operator owner artifact count'); assert(report.includes('## 真实证明缺口关闭状态'), 'handoff report should include proof gap closure status section'); assert(report.includes('businessOpenCount'), 'handoff report should include business proof gap open count semantics'); assert(report.includes('precheckOpenCount'), 'handoff report should include precheck proof gap count separately'); assert(report.includes(`operatorPackOwnerArtifactCount: ${operatorOwnerArtifactCount}`), 'handoff report should include real proof bundle operator owner artifact count'); assert(report.includes('by-owner/tech-ai.csv'), 'handoff report should include real proof bundle tech/AI owner csv'); assert(report.includes('proofContext.mode=live'), 'handoff report should include video A/B missing proof requirement'); assert(report.includes('## 反馈二轮闭环审计'), 'handoff report should include feedback loop closure section'); if (summary.feedbackLoopClosure) { assert(report.includes('feedback-loop-closure-summary.json'), 'handoff report should include feedback loop closure summary'); } assert(report.includes('## 主页近期内容证据就绪审计'), 'handoff report should include homepage evidence readiness section'); if (summary.homepageEvidenceReadiness) { assert(report.includes('homepage-evidence-readiness-summary.json'), 'handoff report should include homepage evidence readiness summary'); assert(report.includes('homepage-evidence-repair-actions.csv'), 'handoff report should include homepage evidence repair csv'); } assert(report.includes('## 长跑前置门禁'), 'handoff report should include long-run readiness section'); assert(report.includes('long-run-readiness-report.md'), 'handoff report should include long-run readiness report'); assert(report.includes('ready=false'), 'handoff report should include long-run ready=false boundary'); assert(report.includes('## 优化完成度审计'), 'handoff report should include optimization completion section'); assert(report.includes('optimization-completion-report.md'), 'handoff report should include optimization completion report'); assert(report.includes('readyForClaim: false'), 'handoff report should include readyForClaim=false boundary'); assert(report.includes('missingProofRequirementCount:'), 'handoff report should include optimization completion missing proof requirement count'); assert(report.includes('videoAbPreflightReadyForVideoAb:'), 'handoff report should include optimization completion video A/B preflight readiness'); assert(report.includes('videoAbPreflightFailureCount:'), 'handoff report should include optimization completion video A/B preflight failure count'); assert(report.includes('optimizationCompletion.readyForClaim'), 'handoff business execution section should include optimization completion readyForClaim'); assert(report.includes('optimizationCompletion.videoAbPreflightFailureCount'), 'handoff business execution section should include optimization completion video A/B preflight failure count'); assert(report.includes('### 优化完成度审计'), 'handoff latest form section should include optimization completion subsection'); assert(report.includes('videoAbPreflightFailureCount:'), 'handoff report should show latest form optimization video A/B preflight failure count'); assert(report.includes('directCustomerProof'), 'handoff report should show feedback loop proof boundary'); assert(report.includes('## 最新提号补证表单'), 'handoff report should include latest proof gap software form section'); assert(report.includes('## 真实证明缺口请求包'), 'handoff report should include proof gap request section'); assert(report.includes('proof-gap-request-report.md'), 'handoff report should include proof gap request report'); assert(report.includes('## 提号补证操作包'), 'handoff report should include proof gap operator pack section'); assert(report.includes('proof-gap-operator-pack.md'), 'handoff report should include proof gap operator pack report'); assert(report.includes('gapIds: historical-dataset'), 'handoff report should include proof gap operator stable ids'); assert(report.includes(`ownerArtifactCount: ${operatorOwnerArtifactCount}`), 'handoff report should include proof gap operator owner artifact count'); assert(report.includes('by-owner/business.csv'), 'handoff report should include business owner operator csv'); if (summary.proofGapOperatorPack.ownerArtifacts.some(item => item.owner === '商务/投放')) { assert(report.includes('by-owner/business-media.md'), 'handoff report should include business/media owner operator markdown when present'); } assert(report.includes('recheckCommandCount'), 'handoff report should include proof gap operator recheck count'); assert(report.includes('evidenceRowCount: 5'), 'handoff report should include proof gap operator evidence row count'); assert(report.includes('nextActionCount: 5'), 'handoff report should include proof gap operator next action count'); assert(report.includes('tihao-proof-gap-software-form.csv'), 'handoff report should list latest proof gap software form csv'); assert(report.includes('passed:true'), 'handoff report should show latest proof gap software form pass state'); assert(report.includes('rowCount:5'), 'handoff report should show latest proof gap software form row count'); assert(report.includes('## 真实材料字段级补齐核对表'), 'handoff report should include intake field checklist section'); assert(report.includes('intake-field-checklist.csv'), 'handoff report should include intake field checklist csv'); assert(report.includes('### 按负责人补齐顺序'), 'handoff report should include field checklist owner group section'); assert(report.includes('history-data-template.csv'), 'handoff report should include history template from owner groups'); assert(report.includes('### 补齐后复验命令'), 'handoff report should include field checklist recheck command section'); assert(report.includes('npm run intake:readiness'), 'handoff report should include intake readiness recheck command'); assert(report.includes('npm run acceptance:video-ab'), 'handoff report should include video A/B recheck command'); assert(report.includes('## 最新提号表单索引'), 'handoff report should include latest form index section'); assert(report.includes('latest-form-index.md'), 'handoff report should list latest form index report'); assert(report.includes('software-client-list.final.csv'), 'handoff report should list indexed software client list'); assert(report.includes('## 本地种子转 intake 补表 worklist'), 'handoff report should include top-level seed-to-intake worklist section'); assert(report.includes('### 本地种子转 intake 补表 worklist'), 'handoff report should include latest form seed-to-intake worklist section'); assert(report.includes('local-seed-to-intake-worklist.md'), 'handoff report should include seed-to-intake worklist report'); assert(report.includes('history-intake-draft.csv'), 'handoff report should include seed-to-intake history draft'); assert(report.includes('video-resource-worklist.csv'), 'handoff report should include seed-to-intake video worklist'); assert(report.includes('candidate-seed-worklist.csv'), 'handoff report should include seed-to-intake candidate worklist'); assert(report.includes('seed_to_intake_worklist'), 'handoff report should include seed-to-intake material type'); assert(report.includes('## 真实证明闭环工单'), 'handoff report should include top-level real proof closure work order section'); assert(report.includes('### 真实证明闭环工单'), 'handoff report should include latest form real proof closure work order section'); assert(report.includes('real-proof-closure-work-order.md'), 'handoff report should include real proof closure work order report'); assert(report.includes(`workOrderCount: ${workOrderCount}`), 'handoff report should include real proof closure work order count'); assert(report.includes('canCloseProofGap: false'), 'handoff report should include real proof closure work order boundary'); if (summary.realProofClosureWorkOrder?.ownerArtifacts?.some(item => item.owner === '商务/投放')) { assert(report.includes('by-owner/business-media.csv'), 'handoff report should include real proof closure work order business/media csv when present'); } assert(report.includes('by-owner/tech-ai.md'), 'handoff report should include real proof closure work order tech owner markdown'); assert(report.includes('ownerGroupCount'), 'handoff report should include latest form field owner group count'); assert(report.includes('recheckCommandCount'), 'handoff report should include latest form recheck command count'); assert(report.includes('manual-review-label-guide.md'), 'handoff report should include manual review label guide'); assert(report.includes('plan-execution-status.md'), 'handoff report should include plan execution status'); assert(report.includes('### 长跑前置门禁'), 'handoff report should include latest form long-run readiness section'); assert(report.includes('## 真实验收材料收集总包'), 'handoff report should include real proof intake bundle section'); assert(report.includes('real-proof-intake-bundle.md'), 'handoff report should include real proof intake bundle report'); assert(report.includes('operatorPackEvidenceRowCount: 5'), 'handoff report should include real proof bundle operator evidence row count'); assert(report.includes('operatorPackNextActionCount: 5'), 'handoff report should include real proof bundle operator next action count'); assert(report.includes('## 闀挎湡璁″垝鎵ц鐘舵€侀潰鏉?') || report.includes('## 长期计划执行状态面板'), 'handoff report should include plan execution status section'); assert(report.includes('人工复核标签与归因指南'), 'handoff report should include manual review label guide section'); assert(report.includes('### 商务填表执行视图'), 'handoff report should include business fill guide section'); assert(report.includes('本视图只帮助商务按表补齐真实材料'), 'handoff report should include business fill guide guardrail'); assert(report.includes('### 修复清单入口'), 'handoff report should include latest form repair action section'); assert(report.includes('intake-readiness-repair-actions.csv'), 'handoff report should include data intake repair action csv'); assert(report.includes('video-resource-repair-actions.csv'), 'handoff report should include video resource repair action csv'); assert(report.includes('by-owner/'), 'handoff report should list owner action artifact files'); assert(report.includes('修复附件'), 'handoff report should show owner repair artifact column'); assert(report.includes('## Pipeline 下一步动作'), 'handoff report should include pipeline next actions section'); assert(report.includes('readyForClaim'), 'handoff report should include pipeline readyForClaim status'); assert(report.includes('收集包一键审计 Pipeline'), 'handoff report should include data/video pack pipeline command'); assert(report.includes('真实验收材料收集总包'), 'handoff report should include real proof intake bundle command/section'); assert(report.includes('## 中文 SOP 入口'), 'handoff report should include Chinese SOP section'); assert(report.includes('tihao-experience-optimization-plan.zh-CN.md'), 'handoff report should link Chinese optimization plan document'); assert(report.includes('tihao-experience-optimization-plan.md'), 'handoff report should link main optimization plan document'); assert(report.includes('business-proof-action-order.zh-CN.md'), 'handoff report should link Chinese proof action order document'); assert(report.includes('tihao-handoff-index.md'), 'handoff report should link task handoff index document'); assert(report.includes('不能把 sample、smoke、dry-run'), 'handoff report should include proof boundary'); assert(!/sk-[A-Za-z0-9_-]{10,}/.test(report), 'handoff report should not contain model token patterns'); assert(!/r:[A-Za-z0-9]{20,}/.test(report), 'handoff report should not contain Parse sessionToken patterns'); assert(!/Authorization/i.test(report), 'handoff report should not expose Authorization text'); console.log(JSON.stringify({ ok: true, outputDir, readyCapabilities: summary.readyCapabilities.length, readyNotProven: summary.readyNotProven.length, externalBlockers: summary.externalBlockers.length }, null, 2)); } function assert(condition, message) { if (!condition) throw new Error(message); } main();