| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577 |
- #!/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();
|