#!/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 DOC = path.join(ROOT, 'docs', 'tihao-handoff-index.md'); const REQUIRED_PATHS = [ 'docs/tihao-experience-optimization-plan.md', 'docs/business-proof-action-order.zh-CN.md', 'outputs/latest-form-index-latest/latest-form-index.md', 'outputs/real-proof-intake-bundle-latest/real-proof-intake-bundle.md', 'outputs/real-proof-closure-work-order-latest/real-proof-closure-work-order.md', 'outputs/local-seed-material-index-latest/local-seed-material-index.md', 'outputs/local-seed-to-intake-worklist-latest/local-seed-to-intake-worklist.md', 'outputs/experience-transcript-index-latest/experience-transcript-index.md', 'outputs/real-gap-material-audit-latest/real-gap-material-audit.md', 'outputs/proof-gap-request-latest/proof-gap-request-report.md', 'outputs/proof-gap-operator-pack-latest/proof-gap-operator-pack.md', 'outputs/business-execution-index-latest/business-execution-index.md', 'outputs/business-proof-progress-latest/business-proof-progress-report.md', 'outputs/plan-execution-status-latest/plan-execution-status.md', 'outputs/optimization-completion-latest/optimization-completion-report.md', 'outputs/long-run-readiness-latest/long-run-readiness-report.md', 'outputs/optimization-handoff-latest/optimization-handoff-report.md' ]; const REQUIRED_PHRASES = [ 'proof-gap-closure.openCount=3', '@vocmarket/tihao-sop@1.0.2', 'dist-tags.latest=1.0.2', 'real-gap-material-audit', 'realGapMaterialAudit', 'materialFoundCount=3', 'optimization-handoff.complete=false', 'optimization-status.counts.passed=31', 'optimization-handoff.externalBlockers=1', 'pipeline-scoped customer-effect', 'pipelineBlocked', 'proofGapClosure.complete=true/openCount=0', 'live-provider-overnight-proof` 不再出现在 `externalBlockers`', 'readyNotProven=video-ab-proof,historical-dataset', 'externalBlockers=customer-effect-proof', 'optimization-completion.readyForClaim=false', 'longrun:readiness.ready=false', '商务执行总表已把', 'businessExecutionIndex.longRunReadiness', '长跑前置门禁报告', 'status=blocked', 'actionCount=', '不能宣称提号率提升', '不能宣称客户效果达标', '不能宣称人工补号量下降', '不是业务证明', 'gapIds=historical-dataset', 'fillFileCount=3', 'recheckCommandCount=9', 'ownerGroupCount=2', 'ownerArtifactCount=2', 'evidenceRowCount=5', 'nextActionCount=5', 'operatorPackEvidenceRowCount=5', 'operatorPackNextActionCount=5', 'operatorPackOwnerArtifactCount=2', 'real-proof-closure-work-order', 'workOrderCount=13', 'realProofClosureWorkOrder.ownerArtifactCount=2', 'realProofClosureWorkOrder.canCloseProofGap=false', '商务执行总表已透传', 'operatorPackOwnerArtifactCount=2', '业务证明进度表已透传', 'businessProofProgress.operatorPackOwnerArtifactCount=2', 'businessProofNextActions.actionCount=3', '本地种子材料索引', 'local-seed-material-index', '本地种子转 intake 补表 worklist', 'local-seed-to-intake-worklist', 'historyDraftRows=1', 'candidateSeedRows=12', 'videoCandidateNeedsResourceRows=3', '经验逐字稿索引', 'experience-transcript-index', 'transcriptCount=2', 'coveredRuleCount=8', 'canCloseProofGap=false', '长期计划执行状态面板已透传', 'planExecutionStatus.operatorPackOwnerArtifactCount=2', 'businessExecutionOperatorPackOwnerArtifactRowCount=2', '操作包负责人附件', '真实历史 Brief 数据集', '真实候选视频资源', '真实视频 A/B live proof', 'proofContext.generatedBy=acceptance:video-ab', 'proofContext.mode=unknown', 'TIHAO_SESSION_TOKEN', '真实 live/provider 长跑证明', '商务复核和客户效果证明', '## 最近一次复验结果', 'overallReady=false', 'failureCount=6', 'readyForVideoAbPreflight=true', 'realCandidateRows=1', 'closedCount=2', 'readyForClaim=false', 'videoAbPreflightReadyForVideoAb=false', 'videoAbPreflightFailureCount=4', 'optimizationCompletion.videoAbPreflightFailureCount', 'video A/B preflight 失败上下文进入 closure/completion', 'proofGapOpenCount=3', 'failCount=', '不能用 `0` 或示例值代替', '## 接手角色分工', '## 完成判定矩阵', '## 可说与不可说', 'readyForCustomerEffectProof=true', 'readyForVideoAbPreflight=true', 'longrun:readiness', 'ready=true', 'liveEnabled=true', 'overallPass=true', 'openCount=0', 'complete=true', 'readyForClaim=true', '29 步全部 `pass`', 'round-deposition.passCount=142', 'provider mock、fallback、dry-run', 'npm run longrun:readiness -- --mode full-matrix', 'npm run optimization:completion -- --output outputs\\optimization-completion-latest', 'npm run round:refresh -- --output-root outputs --strict' ]; function main() { assert(fs.existsSync(DOC), 'task handoff index doc should exist'); const text = fs.readFileSync(DOC, 'utf8').replace(/^\uFEFF/, ''); for (const requiredPath of REQUIRED_PATHS) { assert(text.includes(requiredPath), `task handoff index should link ${requiredPath}`); } for (const phrase of REQUIRED_PHRASES) { assert(text.includes(phrase), `task handoff index should include ${phrase}`); } assert(!/sk-[A-Za-z0-9_-]{10,}/.test(text), 'task handoff index should not contain model token patterns'); assert(!/r:[A-Za-z0-9]{20,}/.test(text), 'task handoff index should not contain Parse sessionToken patterns'); assert(!/Authorization\s*[:=]\s*Bearer/i.test(text), 'task handoff index should not expose authorization bearer values'); const closure = readJson(path.join(ROOT, 'outputs', 'proof-gap-closure-latest', 'proof-gap-closure-summary.json')); const proofGapOperatorPack = readJson(path.join(ROOT, 'outputs', 'proof-gap-operator-pack-latest', 'proof-gap-operator-pack-summary.json')); const realProofIntakeBundle = readJson(path.join(ROOT, 'outputs', 'real-proof-intake-bundle-latest', 'real-proof-intake-bundle-summary.json')); const realProofClosureWorkOrder = readJson(path.join(ROOT, 'outputs', 'real-proof-closure-work-order-latest', 'real-proof-closure-work-order-summary.json')); const completion = readJson(path.join(ROOT, 'outputs', 'optimization-completion-latest', 'optimization-completion-summary.json')); const longRunReadiness = readJson(path.join(ROOT, 'outputs', 'long-run-readiness-latest', 'long-run-readiness-summary.json')); const latestForm = readJson(path.join(ROOT, 'outputs', 'latest-form-index-latest', 'latest-form-index-summary.json')); const handoff = readJson(path.join(ROOT, 'outputs', 'optimization-handoff-latest', 'optimization-handoff-summary.json')); const businessExecutionIndex = readJson(path.join(ROOT, 'outputs', 'business-execution-index-latest', 'business-execution-index-summary.json')); const businessProofProgress = readJson(path.join(ROOT, 'outputs', 'business-proof-progress-latest', 'business-proof-progress-summary.json')); const businessProofNextActions = readJson(path.join(ROOT, 'outputs', 'business-proof-next-actions-latest', 'business-proof-next-actions-summary.json')); const planExecutionStatus = readJson(path.join(ROOT, 'outputs', 'plan-execution-status-latest', 'plan-execution-status-summary.json')); const localSeed = readJson(path.join(ROOT, 'outputs', 'local-seed-material-index-latest', 'local-seed-material-index-summary.json')); const seedWorklist = readJson(path.join(ROOT, 'outputs', 'local-seed-to-intake-worklist-latest', 'local-seed-to-intake-worklist-summary.json')); const experienceTranscript = readJson(path.join(ROOT, 'outputs', 'experience-transcript-index-latest', 'experience-transcript-index-summary.json')); assert(closure?.complete === false && Number(closure.openCount || 0) === 3, 'current proof gap closure should still have three open gaps'); assertProofGapOperatorPack(proofGapOperatorPack, 'current proof-gap operator pack'); assertRealProofIntakeBundle(realProofIntakeBundle, 'current real proof intake bundle'); assertRealProofClosureWorkOrder(realProofClosureWorkOrder, 'current real proof closure work order'); assert(completion?.complete === false && completion?.readyForClaim === false && Number(completion.proofGapOpenCount || 0) === Number(closure.openCount || 0), 'current optimization completion audit should not be ready for claim'); assert(completion?.canClaim?.tihaoRateImproved === false && completion?.canClaim?.customerEffectAchieved === false && completion?.canClaim?.manualSupplementReduced === false, 'completion audit should forbid all business-effect claims while incomplete'); assertOptimizationCompletionPreflightContext(completion, 'current optimization completion'); assert(longRunReadiness?.ready === false && Number(longRunReadiness.counts?.fail || 0) > 0, 'current long-run readiness should remain blocked with failing checks'); assertBusinessExecutionLongRunReadiness(businessExecutionIndex, Number(longRunReadiness.counts?.fail || 0), 'current business execution index'); assertBusinessExecutionOptimizationCompletionPreflight(businessExecutionIndex, completion, 'current business execution index'); assertBusinessExecutionOperatorOwnerArtifacts(businessExecutionIndex, 'current business execution index'); assertBusinessProofProgress(businessProofProgress, proofGapOperatorPack, businessExecutionIndex, 'current business proof progress'); assertBusinessProofNextActions(businessProofNextActions, 'current business proof next actions'); assert(localSeed?.proofLevel === 'not_business_proof' && localSeed?.directCustomerProof === false && localSeed?.canCloseProofGap === false, 'current local seed material index should not be business proof or close proof gaps'); assert(Number(localSeed?.existingMaterialCount || 0) >= 1, 'current local seed material index should expose available seed materials'); assertSeedToIntakeWorklist(seedWorklist, 'current local seed to intake worklist'); assertExperienceTranscriptIndex(experienceTranscript, 'current experience transcript index'); assertPlanExecutionStatus(planExecutionStatus, proofGapOperatorPack, businessExecutionIndex, 'current plan execution status'); assert(latestForm?.passed === true && latestForm?.supportDocs?.some(item => item.path?.includes('tihao-handoff-index.md')), 'latest form index should expose task handoff doc'); assert(latestForm?.longRunReadiness?.report?.includes('long-run-readiness-report.md') && latestForm.longRunReadiness.ready === false, 'latest form index should expose long-run readiness entry'); assertOptimizationCompletionPreflightContext(latestForm?.optimizationCompletion, 'latest form index optimization completion'); assertLatestFormNextActionsEntry(latestForm?.nextActions, businessProofNextActions, 'latest form index'); assertPlanExecutionStatusEntry(latestForm?.planExecutionStatus, planExecutionStatus, 'latest form index'); assertProofGapOperatorPackEntry(latestForm?.proofGapOperatorPack, proofGapOperatorPack, 'latest form index'); assertRealProofIntakeBundleEntry(latestForm?.realProofIntakeBundle, realProofIntakeBundle, 'latest form index'); assertRealProofClosureWorkOrderEntry(latestForm?.realProofClosureWorkOrder, realProofClosureWorkOrder, 'latest form index'); assert(handoff?.complete === false && handoff?.supportDocs?.some(item => item.doc?.includes('tihao-handoff-index.md')), 'handoff summary should expose task handoff doc'); assertHandoffProofBlockers(handoff, 'handoff summary'); assert(handoff?.longRunReadiness?.report?.includes('long-run-readiness-report.md') && handoff.longRunReadiness.ready === false, 'handoff summary should expose top-level long-run readiness entry'); assert(handoff?.latestFormIndex?.longRunReadiness?.report?.includes('long-run-readiness-report.md') && handoff.latestFormIndex.longRunReadiness.ready === false, 'handoff latest form index should expose long-run readiness entry'); assertOptimizationCompletionPreflightContext(handoff?.optimizationCompletion, 'handoff optimization completion'); assertOptimizationCompletionPreflightContext(handoff?.latestFormIndex?.optimizationCompletion, 'handoff latest form optimization completion'); assertBusinessProofProgressEntry(handoff?.businessProofProgress, businessProofProgress, 'handoff summary'); assertBusinessProofNextActionsEntry(handoff?.businessProofNextActions, businessProofNextActions, 'handoff summary'); assertHandoffLatestFormNextActionsEntry(handoff?.latestFormIndex, latestForm?.nextActions, 'handoff latest form index'); assertPlanExecutionStatusEntry(handoff?.planExecutionStatus, planExecutionStatus, 'handoff summary'); assertPlanExecutionStatusEntry(handoff?.latestFormIndex?.planExecutionStatus, planExecutionStatus, 'handoff latest form index'); assertProofGapOperatorPackEntry(handoff?.proofGapOperatorPack, proofGapOperatorPack, 'handoff summary'); assertProofGapOperatorPackEntry(handoff?.latestFormIndex?.proofGapOperatorPack, proofGapOperatorPack, 'handoff latest form index'); assertRealProofIntakeBundleEntry(handoff?.realProofIntakeBundle, realProofIntakeBundle, 'handoff summary'); assertRealProofIntakeBundleEntry(handoff?.latestFormIndex?.realProofIntakeBundle, realProofIntakeBundle, 'handoff latest form index'); assertRealProofClosureWorkOrderEntry(handoff?.realProofClosureWorkOrder, realProofClosureWorkOrder, 'handoff summary'); assertRealProofClosureWorkOrderEntry(handoff?.latestFormIndex?.realProofClosureWorkOrder, realProofClosureWorkOrder, 'handoff latest form index'); assertHandoffBusinessExecutionLongRunReadiness(handoff, Number(longRunReadiness.counts?.fail || 0), 'current handoff summary'); assertHandoffBusinessExecutionOptimizationCompletionPreflight(handoff, completion, 'current handoff summary'); assertHandoffBusinessExecutionOperatorOwnerArtifacts(handoff, 'current handoff summary'); const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'tihao-task-handoff-index-')); const latestOutput = path.join(temp, 'latest-form-index'); const handoffOutput = path.join(temp, 'optimization-handoff'); const businessExecutionOutput = path.join(temp, 'business-execution-index'); runNode(['scripts/latest-form-index.js', '--output', latestOutput, '--strict']); runNode(['scripts/generate-optimization-handoff.js', '--output', handoffOutput]); runNode(['scripts/business-execution-index.js', '--output', businessExecutionOutput, '--strict']); const generatedLatest = readJson(path.join(latestOutput, 'latest-form-index-summary.json')); const generatedHandoff = readJson(path.join(handoffOutput, 'optimization-handoff-summary.json')); const generatedBusinessExecutionIndex = readJson(path.join(businessExecutionOutput, 'business-execution-index-summary.json')); assert(generatedLatest?.supportDocs?.some(item => item.path?.includes('tihao-handoff-index.md')), 'generated latest form index should expose task handoff doc'); assert(generatedLatest?.longRunReadiness?.report?.includes('long-run-readiness-report.md'), 'generated latest form index should expose long-run readiness entry'); assertOptimizationCompletionPreflightContext(generatedLatest?.optimizationCompletion, 'generated latest form optimization completion'); assertLatestFormNextActionsEntry(generatedLatest?.nextActions, businessProofNextActions, 'generated latest form index'); assertPlanExecutionStatusEntry(generatedLatest?.planExecutionStatus, planExecutionStatus, 'generated latest form index'); assertProofGapOperatorPackEntry(generatedLatest?.proofGapOperatorPack, proofGapOperatorPack, 'generated latest form index'); assertRealProofIntakeBundleEntry(generatedLatest?.realProofIntakeBundle, realProofIntakeBundle, 'generated latest form index'); assertRealProofClosureWorkOrderEntry(generatedLatest?.realProofClosureWorkOrder, realProofClosureWorkOrder, 'generated latest form index'); assert(generatedHandoff?.supportDocs?.some(item => item.doc?.includes('tihao-handoff-index.md')), 'generated handoff summary should expose task handoff doc'); assertHandoffProofBlockers(generatedHandoff, 'generated handoff summary'); assert(generatedHandoff?.longRunReadiness?.report?.includes('long-run-readiness-report.md'), 'generated handoff should expose top-level long-run readiness entry'); assertOptimizationCompletionPreflightContext(generatedHandoff?.optimizationCompletion, 'generated handoff optimization completion'); assertOptimizationCompletionPreflightContext(generatedHandoff?.latestFormIndex?.optimizationCompletion, 'generated handoff latest form optimization completion'); assertBusinessProofProgressEntry(generatedHandoff?.businessProofProgress, businessProofProgress, 'generated handoff summary'); assertBusinessProofNextActionsEntry(generatedHandoff?.businessProofNextActions, businessProofNextActions, 'generated handoff summary'); assertHandoffLatestFormNextActionsEntry(generatedHandoff?.latestFormIndex, latestForm?.nextActions, 'generated handoff latest form index'); assertPlanExecutionStatusEntry(generatedHandoff?.planExecutionStatus, planExecutionStatus, 'generated handoff summary'); assertProofGapOperatorPackEntry(generatedHandoff?.proofGapOperatorPack, proofGapOperatorPack, 'generated handoff summary'); assertRealProofIntakeBundleEntry(generatedHandoff?.realProofIntakeBundle, realProofIntakeBundle, 'generated handoff summary'); assertRealProofClosureWorkOrderEntry(generatedHandoff?.realProofClosureWorkOrder, realProofClosureWorkOrder, 'generated handoff summary'); assertRealProofClosureWorkOrderEntry(generatedHandoff?.latestFormIndex?.realProofClosureWorkOrder, realProofClosureWorkOrder, 'generated handoff latest form index'); assertHandoffBusinessExecutionLongRunReadiness(generatedHandoff, Number(longRunReadiness.counts?.fail || 0), 'generated handoff summary'); assertHandoffBusinessExecutionOptimizationCompletionPreflight(generatedHandoff, completion, 'generated handoff summary'); assertBusinessExecutionLongRunReadiness(generatedBusinessExecutionIndex, Number(longRunReadiness.counts?.fail || 0), 'generated business execution index'); assertBusinessExecutionOptimizationCompletionPreflight(generatedBusinessExecutionIndex, completion, 'generated business execution index'); assertBusinessExecutionOperatorOwnerArtifacts(generatedBusinessExecutionIndex, 'generated business execution index'); const readme = fs.readFileSync(path.join(ROOT, 'README.md'), 'utf8'); assert(readme.includes('docs/tihao-handoff-index.md'), 'README should link task handoff index'); const packageJson = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8')); assert(packageJson.scripts['task:handoff-index:smoke'] === 'node scripts/task-handoff-index-smoke.js', 'package should expose task handoff smoke script'); assert(packageJson.scripts['optimization:completion'] === 'node scripts/optimization-completion-audit.js', 'package should expose optimization completion audit script'); assert(packageJson.scripts['optimization:completion:smoke'] === 'node scripts/optimization-completion-smoke.js', 'package should expose optimization completion smoke script'); const acceptance = fs.readFileSync(path.join(ROOT, 'scripts', 'acceptance.js'), 'utf8'); assert(acceptance.includes("task:handoff-index:smoke"), 'acceptance should run task handoff smoke'); assert(acceptance.includes("optimization:completion:smoke"), 'acceptance should run optimization completion smoke'); console.log(JSON.stringify({ ok: true, doc: path.relative(ROOT, DOC).replace(/\\/g, '/'), openCount: closure.openCount, operatorPackRecheckCommandCount: proofGapOperatorPack.recheckCommandCount, businessExecutionOperatorPackOwnerArtifactCount: businessExecutionIndex.operatorPackOwnerArtifactCount, businessProofProgressOperatorPackOwnerArtifactCount: businessProofProgress.operatorPackOwnerArtifactCount, businessProofNextActionCount: businessProofNextActions.actions.length, handoffBusinessProofNextActionCount: handoff.businessProofNextActions.actionCount, latestFormIndexNextActionTopCount: latestForm.nextActions.topActions.length, handoffLatestFormIndexNextActionTopCount: handoff.latestFormIndex.topActions.length, planExecutionStatusOperatorPackOwnerArtifactCount: planExecutionStatus.operatorPackOwnerArtifactCount, planExecutionStatusBusinessExecutionOperatorPackOwnerArtifactRowCount: planExecutionStatus.businessExecutionOperatorPackOwnerArtifactRowCount, seedToIntakeHistoryDraftRows: seedWorklist.counts?.historyDraftRows, seedToIntakeCandidateSeedRows: seedWorklist.counts?.candidateSeedRows, seedToIntakeVideoCandidateNeedsResourceRows: seedWorklist.counts?.videoCandidateNeedsResourceRows, experienceTranscriptCount: experienceTranscript.transcriptCount, experienceTranscriptCoveredRuleCount: experienceTranscript.coveredRuleCount, realProofBundleOperatorPackEvidenceRowCount: realProofIntakeBundle.operatorPackEvidenceRowCount, realProofBundleOperatorPackNextActionCount: realProofIntakeBundle.operatorPackNextActionCount, realProofClosureWorkOrderCount: realProofClosureWorkOrder.workOrderCount, realProofClosureWorkOrderOwnerArtifactCount: realProofClosureWorkOrder.ownerArtifacts.length, readyForClaim: completion.readyForClaim, businessExecutionLongRunStatus: findBusinessExecutionLongRunRow(businessExecutionIndex)?.status, latestSupportDocCount: latestForm.supportDocs.length, handoffSupportDocCount: handoff.supportDocs.length }, null, 2)); } function assertRealProofIntakeBundle(bundle, label) { assert(bundle?.passed === true, `${label} should pass`); assert(bundle?.complete === false, `${label} should remain incomplete while proof gaps are open`); assert(bundle?.directCustomerProof === false, `${label} should preserve non-business-proof boundary`); assert(bundle?.proofLevel === 'not_business_proof', `${label} should keep not_business_proof level`); assert(Number(bundle?.proofOpenCount || 0) >= 1, `${label} should keep open proof gaps`); assert(Number(bundle?.proofGapRowCount || 0) === 5, `${label} should expose five proof gap rows`); assert(Number(bundle?.operatorPackEvidenceRowCount || 0) === 5, `${label} should expose operator evidence row count`); assert(Number(bundle?.operatorPackNextActionCount || 0) === 5, `${label} should expose operator next action count`); assert(Number(bundle?.operatorPackMissingProofRequirementCount || 0) >= 1, `${label} should expose operator missing proof requirement count`); assert(Number(bundle?.operatorPackOwnerArtifactCount || 0) >= 1, `${label} should expose operator owner artifact count`); assert(Array.isArray(bundle?.operatorPackOwnerArtifacts) && bundle.operatorPackOwnerArtifacts.length === Number(bundle.operatorPackOwnerArtifactCount || 0), `${label} should expose operator owner artifacts`); assert(bundle.operatorPackOwnerArtifacts.some(item => item.owner === '商务' && item.markdown?.includes('by-owner/business.md') && item.csv?.includes('by-owner/business.csv')), `${label} should expose business owner operator files`); if (Array.isArray(bundle?.proofGapRows)) { assert(bundle.proofGapRows.every(row => row.evidence && row.nextAction), `${label} proof gap rows should carry evidence and next action`); } } function assertHandoffProofBlockers(handoff, label) { const externalIds = new Set((handoff?.externalBlockers || []).map(item => item.id)); const readyNotProvenIds = new Set((handoff?.readyNotProven || []).map(item => item.id)); assert(externalIds.size === 1, `${label} should expose exactly one external proof blocker in the current state`); assert(externalIds.has('customer-effect-proof'), `${label} should keep customer-effect-proof as the only external proof blocker`); assert(!externalIds.has('live-provider-overnight-proof'), `${label} should not list closed live-provider proof as an external blocker`); assert(readyNotProvenIds.has('video-ab-proof'), `${label} should keep video A/B proof as ready-not-proven`); assert(readyNotProvenIds.has('historical-dataset'), `${label} should keep historical dataset as ready-not-proven`); } function assertRealProofIntakeBundleEntry(entry, bundle, label) { assert(entry?.markdown?.includes('real-proof-intake-bundle.md'), `${label} should link real proof intake bundle report`); assert(entry?.csv?.includes('real-proof-intake-bundle.csv'), `${label} should link real proof intake bundle csv`); assert(entry?.summary?.includes('real-proof-intake-bundle-summary.json'), `${label} should link real proof intake bundle summary`); assert(entry?.directCustomerProof === false, `${label} should preserve real proof bundle proof boundary`); assert(entry?.proofLevel === 'not_business_proof', `${label} should preserve real proof bundle proof level`); assert(Number(entry?.proofOpenCount || 0) === Number(bundle?.proofOpenCount || 0), `${label} should mirror real proof bundle open proof count`); assert(Number(entry?.proofGapRowCount || 0) === Number(bundle?.proofGapRowCount || 0), `${label} should mirror real proof bundle proof gap row count`); assert(Number(entry?.operatorPackEvidenceRowCount || 0) === Number(bundle?.operatorPackEvidenceRowCount || 0), `${label} should mirror real proof bundle operator evidence row count`); assert(Number(entry?.operatorPackNextActionCount || 0) === Number(bundle?.operatorPackNextActionCount || 0), `${label} should mirror real proof bundle operator next action count`); assert(Number(entry?.operatorPackMissingProofRequirementCount || 0) === Number(bundle?.operatorPackMissingProofRequirementCount || 0), `${label} should mirror real proof bundle operator missing proof requirement count`); assert(Number(entry?.operatorPackOwnerArtifactCount || 0) === Number(bundle?.operatorPackOwnerArtifactCount || 0), `${label} should mirror real proof bundle operator owner artifact count`); if (bundle?.operatorPackOwnerArtifacts?.some(item => item.owner === '商务/投放')) { assert(entry?.operatorPackOwnerArtifacts?.some(item => item.csv?.includes('by-owner/business-media.csv')), `${label} should mirror real proof bundle business/media owner csv when present`); } } function assertRealProofClosureWorkOrder(workOrder, label) { assert(workOrder?.passed === true, `${label} should pass`); assert(workOrder?.complete === false, `${label} should remain incomplete while proof gaps are open`); assert(workOrder?.directCustomerProof === false, `${label} should preserve non-business-proof boundary`); assert(workOrder?.canCloseProofGap === false, `${label} should not close proof gaps`); assert(workOrder?.proofLevel === 'not_business_proof', `${label} should keep not_business_proof level`); assert(workOrder?.materialType === 'real_proof_closure_work_order', `${label} should classify material type`); assert(workOrder?.requiresHumanRealMaterial === true, `${label} should require human real material`); assert(Number(workOrder?.workOrderCount || 0) >= 5, `${label} should expose work orders`); assert(Number(workOrder?.ownerGroupCount || 0) >= 1, `${label} should expose owner groups`); assert(Array.isArray(workOrder?.ownerArtifacts) && workOrder.ownerArtifacts.length === Number(workOrder.ownerGroupCount || 0), `${label} should expose owner artifacts`); assert(workOrder.ownerArtifacts.some(item => item.markdown?.includes('by-owner/business.md') && item.csv?.includes('by-owner/business.csv')), `${label} should expose business owner files`); assert(workOrder.ownerArtifacts.some(item => item.markdown?.includes('by-owner/tech-ai.md') && item.csv?.includes('by-owner/tech-ai.csv')), `${label} should expose tech/AI owner files`); if (workOrder.ownerArtifacts.some(item => item.owner === '商务/投放')) { assert(workOrder.ownerArtifacts.some(item => item.markdown?.includes('by-owner/business-media.md') && item.csv?.includes('by-owner/business-media.csv')), `${label} should expose business/media owner files when present`); } assert(Number(workOrder?.sourceState?.proofGapOpenCount || 0) >= 1, `${label} should expose open proof gaps`); assert(Number(workOrder?.sourceState?.intakeFailureCount || 0) >= 1, `${label} should expose intake failure count`); assert(Number(workOrder?.sourceState?.realCandidateRows || 0) >= 0, `${label} should expose real candidate rows`); } function assertRealProofClosureWorkOrderEntry(entry, workOrder, label) { assert(entry?.markdown?.includes('real-proof-closure-work-order.md'), `${label} should link real proof closure work order report`); assert(entry?.csv?.includes('real-proof-closure-work-order.csv'), `${label} should link real proof closure work order csv`); assert(entry?.summary?.includes('real-proof-closure-work-order-summary.json'), `${label} should link real proof closure work order summary`); assert(entry?.directCustomerProof === false, `${label} should preserve real proof closure work order proof boundary`); assert(entry?.canCloseProofGap === false, `${label} should preserve real proof closure work order non-closure boundary`); assert(entry?.proofLevel === 'not_business_proof', `${label} should preserve real proof closure work order proof level`); assert(entry?.materialType === 'real_proof_closure_work_order', `${label} should preserve real proof closure work order material type`); assert(entry?.complete === false, `${label} should keep real proof closure work order incomplete state`); assert(Number(entry?.workOrderCount || 0) === Number(workOrder?.workOrderCount || 0), `${label} should mirror real proof closure work order count`); assert(Number(entry?.ownerGroupCount || 0) === Number(workOrder?.ownerGroupCount || 0), `${label} should mirror real proof closure owner group count`); assert(Number(entry?.ownerArtifacts?.length || 0) === Number(workOrder?.ownerArtifacts?.length || 0), `${label} should mirror real proof closure owner artifact count`); assert(Number(entry?.proofGapOpenCount || 0) === Number(workOrder?.sourceState?.proofGapOpenCount || 0), `${label} should mirror real proof closure open proof gaps`); assert(Number(entry?.intakeFailureCount || 0) === Number(workOrder?.sourceState?.intakeFailureCount || 0), `${label} should mirror real proof closure intake failures`); assert(Number(entry?.realCandidateRows || 0) === Number(workOrder?.sourceState?.realCandidateRows || 0), `${label} should mirror real proof closure real candidate rows`); if (workOrder?.ownerArtifacts?.some(item => item.owner === '商务/投放')) { assert(entry?.ownerArtifacts?.some(item => item.csv?.includes('by-owner/business-media.csv')), `${label} should mirror real proof closure business/media owner csv when present`); } assert(entry?.ownerArtifacts?.some(item => item.markdown?.includes('by-owner/tech-ai.md')), `${label} should mirror real proof closure tech owner markdown`); } function assertExperienceTranscriptIndex(index, label) { assert(index?.proofLevel === 'not_business_proof', `${label} should keep not_business_proof level`); assert(index?.materialType === 'experience_seed', `${label} should classify transcripts as experience seed`); assert(index?.directCustomerProof === false, `${label} should not be direct customer proof`); assert(index?.canCloseProofGap === false, `${label} should not close proof gaps`); assert(index?.complete === false, `${label} should not claim completion`); assert(Number(index?.transcriptCount || 0) === 4, `${label} should index four transcripts`); assert(Number(index?.coveredRuleCount || 0) === 13, `${label} should cover thirteen experience rules`); assert(Array.isArray(index?.ruleCoverage) && index.ruleCoverage.every(item => item.proofLevel === 'not_business_proof' && item.canCloseProofGap === false), `${label} rules should preserve proof boundary`); } function assertSeedToIntakeWorklist(worklist, label) { assert(worklist?.proofLevel === 'not_business_proof', `${label} should keep not_business_proof level`); assert(worklist?.materialType === 'seed_to_intake_worklist', `${label} should classify material type`); assert(worklist?.directCustomerProof === false, `${label} should not be direct customer proof`); assert(worklist?.canCloseProofGap === false, `${label} should not close proof gaps`); assert(worklist?.complete === false, `${label} should not claim completion`); assert(Number(worklist?.counts?.historyDraftRows || 0) >= 1, `${label} should expose history draft rows`); assert(Number(worklist?.counts?.candidateSeedRows || 0) >= 1, `${label} should expose candidate seed rows`); assert(Number(worklist?.counts?.videoWorklistRows || 0) >= 1, `${label} should expose video worklist rows`); assert(Number(worklist?.counts?.videoCandidateNeedsResourceRows || 0) >= 1, `${label} should expose candidate videos needing resources`); assert(Array.isArray(worklist?.targetFiles) && worklist.targetFiles.some(file => file.includes('history-data-template.csv')), `${label} should point to history intake template`); assert(Array.isArray(worklist?.targetFiles) && worklist.targetFiles.some(file => file.includes('video-resource-template.csv')), `${label} should point to video resource template`); assert(Array.isArray(worklist?.missingProofRequirements) && worklist.missingProofRequirements.length >= 1, `${label} should expose missing proof requirements`); } function assertProofGapOperatorPack(pack, label) { const gapIds = gapIdsOf(pack); for (const requiredId of ['historical-dataset', 'video-real-candidate', 'video-ab-live-proof', 'live-provider-overnight-proof', 'manual-review-and-customer-effect']) { assert(gapIds.includes(requiredId), `${label} should expose ${requiredId}`); } assert(pack?.passed === true, `${label} should pass`); assert(pack?.complete === false, `${label} should remain incomplete while proof gaps are open`); assert(pack?.directCustomerProof === false, `${label} should preserve non-business-proof boundary`); assert(pack?.proofLevel === 'not_business_proof', `${label} should keep not_business_proof level`); assert(Number(pack?.openCount || 0) >= 1, `${label} should keep open gaps`); assert(Number(pack?.rowCount || 0) === 5, `${label} should expose five rows`); assert(Number(pack?.fillFileCount || 0) === 3, `${label} should expose fill file count`); assert(Number(pack?.recheckCommandCount || 0) === 9, `${label} should expose recheck command count`); assert(Number(pack?.ownerGroupCount || 0) === Number(pack?.ownerArtifactCount || pack?.ownerGroups?.length || 0), `${label} should expose owner group count`); assert(Number(pack?.evidenceRowCount || 0) === 5, `${label} should expose evidence row count`); assert(Number(pack?.nextActionCount || 0) === 5, `${label} should expose next action count`); assert(Number(pack?.missingProofRequirementCount || 0) >= 1, `${label} should expose missing proof requirement count`); } function assertProofGapOperatorPackEntry(entry, pack, label) { assert(entry?.markdown?.includes('proof-gap-operator-pack.md'), `${label} should link proof-gap operator pack report`); assert(entry?.csv?.includes('proof-gap-operator-pack.csv'), `${label} should link proof-gap operator pack csv`); assert(entry?.summary?.includes('proof-gap-operator-pack-summary.json'), `${label} should link proof-gap operator pack summary`); assert(entry?.directCustomerProof === false, `${label} should preserve proof-gap operator pack proof boundary`); assert(entry?.proofLevel === 'not_business_proof', `${label} should preserve proof-gap operator pack proof level`); assert(Number(entry?.openCount || 0) === Number(pack?.openCount || 0), `${label} should mirror proof-gap operator pack open count`); assert(Number(entry?.rowCount || 0) === Number(pack?.rowCount || 0), `${label} should mirror proof-gap operator pack row count`); assert(Number(entry?.fillFileCount || 0) === Number(pack?.fillFileCount || 0), `${label} should mirror proof-gap operator pack fill file count`); assert(Number(entry?.recheckCommandCount || 0) === Number(pack?.recheckCommandCount || 0), `${label} should mirror proof-gap operator pack recheck command count`); assert(Number(entry?.ownerGroupCount || 0) === Number(pack?.ownerGroupCount || 0), `${label} should mirror proof-gap operator pack owner group count`); assert(Number(entry?.evidenceRowCount || 0) === Number(pack?.evidenceRowCount || 0), `${label} should mirror proof-gap operator pack evidence row count`); assert(Number(entry?.nextActionCount || 0) === Number(pack?.nextActionCount || 0), `${label} should mirror proof-gap operator pack next action count`); for (const gapId of gapIdsOf(pack)) { assert(gapIdsOf(entry).includes(gapId), `${label} should mirror proof-gap operator pack gap ID ${gapId}`); } } function gapIdsOf(item) { if (Array.isArray(item?.gapIds)) return item.gapIds.map(value => String(value || '')); if (Array.isArray(item?.rows)) return item.rows.map(row => String(row.gapId || '')).filter(Boolean); return []; } function assertBusinessProofProgress(progress, pack, businessExecutionIndex, label) { assert(progress?.complete === false, `${label} should remain incomplete while proof gaps are open`); assert(Number(progress?.operatorPackOwnerArtifactCount || 0) === Number(pack?.ownerArtifactCount || 0), `${label} should mirror proof-gap operator owner artifact count`); assert(Number(progress?.operatorPackOwnerArtifactCount || 0) === Number((pack?.ownerArtifacts || []).length), `${label} should expose current operator owner artifacts`); assert(Number(progress?.observed?.businessExecutionOperatorPackOwnerArtifactRowCount || 0) === Number(businessExecutionIndex?.operatorPackOwnerArtifactRowCount || 0), `${label} should mirror business execution operator owner artifact row count`); assert(Number(progress?.counts?.blocked_by_external_data || 0) >= 1, `${label} should expose external data blockers`); assertOperatorOwnerArtifactsMatch(progress?.operatorPackOwnerArtifacts, pack?.ownerArtifacts, label); } function assertBusinessProofProgressEntry(entry, progress, label) { assert(entry?.summary?.includes('business-proof-progress-summary.json'), `${label} should link business proof progress summary`); assert(entry?.report?.includes('business-proof-progress-report.md'), `${label} should link business proof progress report`); assert(entry?.complete === false, `${label} should keep business proof progress incomplete state`); assert(Number(entry?.operatorPackOwnerArtifactCount || 0) === Number(progress?.operatorPackOwnerArtifactCount || 0), `${label} should mirror business proof progress operator owner artifact count`); assert(Number(entry?.businessExecutionOperatorPackOwnerArtifactRowCount || 0) === Number(progress?.observed?.businessExecutionOperatorPackOwnerArtifactRowCount || 0), `${label} should mirror business proof progress business execution artifact row count`); assert(Number(entry?.counts?.blocked_by_external_data || 0) === Number(progress?.counts?.blocked_by_external_data || 0), `${label} should mirror business proof progress blocker count`); assertOperatorOwnerArtifactsMatch(entry?.operatorPackOwnerArtifacts, progress?.operatorPackOwnerArtifacts, label); } function assertBusinessProofNextActions(nextActions, label) { assert(nextActions?.complete === false, `${label} should remain incomplete while proof gaps are open`); const actions = Array.isArray(nextActions?.actions) ? nextActions.actions : []; assert(actions.length === Number(nextActions?.actionCount || actions.length), `${label} should mirror deduped next action count`); assert(Number(nextActions?.sourceState?.proofGapOpenCount || 0) >= 1, `${label} should keep open proof gaps`); if (nextActions?.sourceState?.pipelineStale === true) { assert(Number(nextActions.sourceState.pipelineNextActionCount || 0) === 0, `${label} should suppress current pipeline next actions when pipeline is stale`); assert(Number(nextActions.sourceState.pipelineSuppressedNextActionCount || 0) >= 1, `${label} should expose suppressed stale pipeline action count`); assert(actions.every(action => !String(action.source || '').includes('optimization-pipeline')), `${label} should not dispatch stale optimization-pipeline actions`); } assert(nextActions.actions.some(action => action.id === 'step-03' && String(action.command || '').includes('history-data-template.csv')), `${label} should expose historical data fill action`); assert(nextActions.actions.some(action => action.id === 'gap-video-ab-live-proof' && String(action.command || '').includes('acceptance:video-ab')), `${label} should expose video A/B live proof action`); } function assertBusinessProofNextActionsEntry(entry, nextActions, label) { assert(entry?.summary?.includes('business-proof-next-actions-summary.json'), `${label} should link business proof next actions summary`); assert(entry?.report?.includes('business-proof-next-actions-report.md'), `${label} should link business proof next actions report`); assert(entry?.csv?.includes('business-proof-next-actions.csv'), `${label} should link business proof next actions csv`); assert(entry?.complete === false, `${label} should keep next action queue incomplete state`); assert(Number(entry?.actionCount || 0) === Number(nextActions?.actions?.length || 0), `${label} should mirror next action count`); assert(Number(entry?.sourceState?.proofGapOpenCount || 0) === Number(nextActions?.sourceState?.proofGapOpenCount || 0), `${label} should mirror next action proof gap open count`); assert(Boolean(entry?.sourceState?.pipelineStale) === Boolean(nextActions?.sourceState?.pipelineStale), `${label} should mirror next action pipeline stale state`); assert(Number(entry?.sourceState?.pipelineSuppressedNextActionCount || 0) === Number(nextActions?.sourceState?.pipelineSuppressedNextActionCount || 0), `${label} should mirror suppressed stale pipeline action count`); assert(Array.isArray(entry?.topActions) && entry.topActions.length >= Math.min(5, nextActions.actions.length), `${label} should expose top next actions`); assert(entry.topActions.some(action => action.id === 'step-03' && String(action.command || '').includes('history-data-template.csv')), `${label} should expose top historical data action`); assert(entry.topActions.some(action => action.id === 'gap-video-ab-live-proof' && String(action.command || '').includes('acceptance:video-ab')), `${label} should expose top video A/B action`); } function assertLatestFormNextActionsEntry(entry, nextActions, label) { assert(entry?.complete === false, `${label} should keep latest form next action queue incomplete state`); assert(Number(entry?.actionCount || 0) === Number(nextActions?.actions?.length || 0), `${label} should mirror latest form next action count`); assert(Number(entry?.proofOpenCount || 0) === Number(nextActions?.sourceState?.proofGapOpenCount || 0), `${label} should mirror latest form proof gap open count`); assert(Number(entry?.sourceState?.proofGapOpenCount || 0) === Number(nextActions?.sourceState?.proofGapOpenCount || 0), `${label} should mirror latest form next action source proof gap open count`); assert(Boolean(entry?.sourceState?.pipelineStale) === Boolean(nextActions?.sourceState?.pipelineStale), `${label} should mirror latest form next action pipeline stale state`); assert(Number(entry?.sourceState?.pipelineSuppressedNextActionCount || 0) === Number(nextActions?.sourceState?.pipelineSuppressedNextActionCount || 0), `${label} should mirror latest form suppressed stale pipeline count`); assert(Number(entry?.ownerGroupCount || 0) === Number(nextActions?.ownerGroupCount || entry?.ownerGroups?.length || 0), `${label} should mirror latest form next action owner group count`); assert(Number(entry?.ownerArtifactCount || 0) === Number(nextActions?.ownerArtifactCount || entry?.ownerArtifacts?.length || 0), `${label} should mirror latest form next action owner artifact count`); assert(Array.isArray(entry?.topActions) && entry.topActions.length >= Math.min(5, nextActions.actions.length), `${label} should expose latest form top next actions`); assert(entry.topActions.some(action => action.id === 'step-03' && String(action.command || '').includes('history-data-template.csv')), `${label} should expose latest form top historical data action`); assert(entry.topActions.some(action => action.id === 'gap-video-ab-live-proof' && String(action.command || '').includes('acceptance:video-ab')), `${label} should expose latest form top video A/B action`); } function assertHandoffLatestFormNextActionsEntry(entry, latestNextActions, label) { assert(entry?.complete === false, `${label} should keep handoff latest form next action queue incomplete state`); assert(Number(entry?.actionCount || 0) === Number(latestNextActions?.actionCount || 0), `${label} should mirror handoff latest form next action count`); assert(Number(entry?.proofOpenCount || 0) === Number(latestNextActions?.proofOpenCount || 0), `${label} should mirror handoff latest form proof gap open count`); assert(Number(entry?.sourceState?.proofGapOpenCount || 0) === Number(latestNextActions?.sourceState?.proofGapOpenCount || 0), `${label} should mirror handoff latest form source proof gap open count`); assert(Boolean(entry?.sourceState?.pipelineStale) === Boolean(latestNextActions?.sourceState?.pipelineStale), `${label} should mirror handoff latest form pipeline stale state`); assert(Number(entry?.sourceState?.pipelineSuppressedNextActionCount || 0) === Number(latestNextActions?.sourceState?.pipelineSuppressedNextActionCount || 0), `${label} should mirror handoff latest form suppressed stale pipeline count`); assert(Number(entry?.ownerGroupCount || 0) === Number(latestNextActions?.ownerGroupCount || 0), `${label} should mirror handoff latest form owner group count`); assert(Number(entry?.ownerArtifactCount || 0) === Number(latestNextActions?.ownerArtifactCount || 0), `${label} should mirror handoff latest form owner artifact count`); assert(Array.isArray(entry?.topActions) && entry.topActions.length >= Math.min(5, latestNextActions?.topActions?.length || 0), `${label} should expose handoff latest form top next actions`); assert(entry.topActions.some(action => action.id === 'step-03' && String(action.command || '').includes('history-data-template.csv')), `${label} should expose handoff latest form top historical data action`); assert(entry.topActions.some(action => action.id === 'gap-video-ab-live-proof' && String(action.command || '').includes('acceptance:video-ab')), `${label} should expose handoff latest form top video A/B action`); } function assertPlanExecutionStatus(status, pack, businessExecutionIndex, label) { assert(status?.complete === false, `${label} should remain incomplete while proof gaps are open`); assert(status?.directCustomerProof !== true, `${label} should preserve non-business-proof boundary`); assert(Number(status?.rowCount || 0) === 8, `${label} should expose eight plan sections`); assert(Number(status?.proofOpenCount || 0) === Number(pack?.openCount || 0), `${label} should mirror open proof gaps`); assert(Number(status?.operatorPackOwnerArtifactCount || 0) === Number(pack?.ownerArtifactCount || 0), `${label} should mirror proof-gap operator owner artifact count`); assert(Number(status?.businessExecutionOperatorPackOwnerArtifactRowCount || 0) === Number(businessExecutionIndex?.operatorPackOwnerArtifactRowCount || 0), `${label} should mirror business execution owner artifact rows`); assertOperatorOwnerArtifactsMatch(status?.operatorPackOwnerArtifacts, pack?.ownerArtifacts, label); } function assertPlanExecutionStatusEntry(entry, status, label) { assert(entry?.markdown?.includes('plan-execution-status.md'), `${label} should link plan execution status report`); assert(entry?.csv?.includes('plan-execution-status.csv'), `${label} should link plan execution status csv`); assert(entry?.summary?.includes('plan-execution-status-summary.json'), `${label} should link plan execution status summary`); assert(entry?.directCustomerProof === false, `${label} should preserve plan execution proof boundary`); assert(entry?.complete === false, `${label} should keep plan execution incomplete state`); assert(Number(entry?.rowCount || 0) === Number(status?.rowCount || 0), `${label} should mirror plan execution row count`); assert(Number(entry?.proofOpenCount || 0) === Number(status?.proofOpenCount || 0), `${label} should mirror plan execution proof open count`); assert(Number(entry?.operatorPackOwnerArtifactCount || 0) === Number(status?.operatorPackOwnerArtifactCount || 0), `${label} should mirror plan execution operator owner artifact count`); assert(Number(entry?.businessExecutionOperatorPackOwnerArtifactRowCount || 0) === Number(status?.businessExecutionOperatorPackOwnerArtifactRowCount || 0), `${label} should mirror plan execution business execution artifact rows`); assertOperatorOwnerArtifactsMatch(entry?.operatorPackOwnerArtifacts, status?.operatorPackOwnerArtifacts, label); } function assertBusinessExecutionLongRunReadiness(index, failCount, label) { const row = findBusinessExecutionLongRunRow(index); assert(index?.sourceFiles?.longRunReadiness?.includes('long-run-readiness-summary.json'), `${label} should expose long-run readiness source`); assert(row, `${label} should expose long-run readiness key file row`); assert(row.mainFile?.includes('long-run-readiness-report.md'), `${label} should link long-run readiness report`); assert(row.expectedArtifact?.includes('long-run-readiness-summary.json'), `${label} should link long-run readiness summary`); assert(row.status === 'blocked', `${label} should keep long-run readiness blocked while ready=false`); assert(Number(row.actionCount || 0) === failCount, `${label} should mirror long-run readiness fail count`); assert(String(row.boundary || '').includes('不能启动') && String(row.boundary || '').includes('不证明客户效果'), `${label} should preserve non-business-proof boundary`); } function assertBusinessExecutionOperatorOwnerArtifacts(index, label) { assert(Number(index?.operatorPackOwnerArtifactCount || 0) === Number(index?.operatorPackOwnerArtifacts?.length || 0), `${label} should expose current operator owner artifacts`); assert(Number(index?.operatorPackOwnerArtifactRowCount || 0) === Number(index?.operatorPackOwnerArtifacts?.length || 0), `${label} should expose current operator owner artifact rows`); assert(index?.sourceFiles?.proofGapOperatorPack?.includes('proof-gap-operator-pack-summary.json'), `${label} should expose proof-gap operator pack source`); assertOperatorOwnerArtifactsMatch(index?.operatorPackOwnerArtifacts, index?.operatorPackOwnerArtifacts, label); } function assertHandoffBusinessExecutionOperatorOwnerArtifacts(handoff, label) { const item = handoff?.businessExecutionIndex; assert(Number(item?.operatorPackOwnerArtifactCount || 0) === Number(item?.operatorPackOwnerArtifacts?.length || 0), `${label} should expose business execution operator owner artifact count`); assert(Number(item?.operatorPackOwnerArtifactRowCount || 0) === Number(item?.operatorPackOwnerArtifacts?.length || 0), `${label} should expose business execution operator owner artifact row count`); assertOperatorOwnerArtifactsMatch(item?.operatorPackOwnerArtifacts, item?.operatorPackOwnerArtifacts, label); } function assertOperatorOwnerArtifactsMatch(actualArtifacts, expectedArtifacts, label) { const actual = Array.isArray(actualArtifacts) ? actualArtifacts : []; const expected = Array.isArray(expectedArtifacts) ? expectedArtifacts.filter(item => item.owner) : []; assert(actual.length === expected.length, `${label} should mirror current operator owner artifact count`); for (const expectedItem of expected) { const item = actual.find(entry => entry.owner === expectedItem.owner); assert(item, `${label} should expose ${expectedItem.owner} operator artifact`); const markdown = normalizeOwnerArtifactPath(expectedItem.markdown); const csv = normalizeOwnerArtifactPath(expectedItem.csv); if (markdown) assert(normalizeOwnerArtifactPath(item.markdown).includes(markdown), `${label} should expose ${expectedItem.owner} operator markdown`); if (csv) assert(normalizeOwnerArtifactPath(item.csv).includes(csv), `${label} should expose ${expectedItem.owner} operator csv`); assert(Number(item?.actionCount || 0) === Number(expectedItem.actionCount || item?.actionCount || 0), `${label} should mirror ${expectedItem.owner} operator action count`); assert(Number(item?.evidenceRowCount || 0) === Number(expectedItem.evidenceRowCount || item?.evidenceRowCount || 0), `${label} should mirror ${expectedItem.owner} operator evidence rows`); assert(Number(item?.nextActionCount || 0) === Number(expectedItem.nextActionCount || item?.nextActionCount || 0), `${label} should mirror ${expectedItem.owner} operator next actions`); } } function normalizeOwnerArtifactPath(value) { const normalized = String(value || '').replace(/\\/g, '/'); const index = normalized.lastIndexOf('by-owner/'); return index >= 0 ? normalized.slice(index) : normalized; } function assertOperatorOwnerArtifact(ownerArtifacts, owner, slug, label) { const item = (Array.isArray(ownerArtifacts) ? ownerArtifacts : []).find(entry => entry.owner === owner); assert(item?.markdown?.includes(`by-owner/${slug}.md`), `${label} should expose ${owner} operator markdown`); assert(item?.csv?.includes(`by-owner/${slug}.csv`), `${label} should expose ${owner} operator csv`); assert(Number(item?.actionCount || 0) >= 1, `${label} should expose ${owner} operator action count`); assert(Number(item?.evidenceRowCount || 0) >= 1, `${label} should expose ${owner} operator evidence rows`); assert(Number(item?.nextActionCount || 0) >= 1, `${label} should expose ${owner} operator next actions`); } function findBusinessExecutionLongRunRow(index) { return (Array.isArray(index?.rows) ? index.rows : []).find(row => row.title === '长跑前置门禁报告'); } function assertHandoffBusinessExecutionLongRunReadiness(handoff, failCount, label) { const item = handoff?.businessExecutionIndex?.longRunReadiness; assert(item?.report?.includes('long-run-readiness-report.md'), `${label} should expose business execution long-run readiness report row`); assert(item?.summary?.includes('long-run-readiness-summary.json'), `${label} should expose business execution long-run readiness summary`); assert(item.status === 'blocked', `${label} should keep business execution long-run readiness blocked while ready=false`); assert(item.ready === false, `${label} should expose business execution long-run ready=false`); assert(Number(item.failCount || 0) === failCount, `${label} should mirror business execution long-run fail count`); assert(item.directCustomerProof === false, `${label} should keep business execution long-run proof boundary`); } function assertOptimizationCompletionPreflightContext(item, label) { assert(item, `${label} should exist`); assert(item.readyForClaim === false, `${label} should remain not ready for claim`); assert(item.videoAbPreflightReadyForVideoAb === false, `${label} should expose video A/B preflight readiness`); assert(Number(item.videoAbPreflightFailureCount || 0) >= 1, `${label} should expose video A/B preflight failure count`); assert(item.directCustomerProof === false, `${label} should preserve non-customer-proof boundary`); } function assertBusinessExecutionOptimizationCompletionPreflight(index, completion, label) { const row = findBusinessExecutionOptimizationCompletionRow(index); assert(row?.mainFile?.includes('optimization-completion-report.md'), `${label} should expose optimization completion report row`); assert(row?.expectedArtifact?.includes('optimization-completion-summary.json'), `${label} should expose optimization completion summary artifact`); assert(row.status === 'blocked', `${label} should keep optimization completion blocked`); assert(String(row.acceptance || '').includes('readyForClaim=false'), `${label} should expose readyForClaim=false`); assert(String(row.acceptance || '').includes('videoAbPreflightReadyForVideoAb=false'), `${label} should expose video A/B preflight readiness`); assert(String(row.acceptance || '').includes(`videoAbPreflightFailureCount=${Number(completion?.videoAbPreflightFailureCount || 0)}`), `${label} should mirror video A/B preflight failure count`); assert(String(row.boundary || '').includes('不得宣称提号率提升'), `${label} should preserve forbidden-claim boundary`); } function assertHandoffBusinessExecutionOptimizationCompletionPreflight(handoff, completion, label) { const item = handoff?.businessExecutionIndex?.optimizationCompletion; assert(item?.report?.includes('optimization-completion-report.md'), `${label} should expose business execution optimization completion report row`); assert(item?.summary?.includes('optimization-completion-summary.json'), `${label} should expose business execution optimization completion summary`); assert(item.status === 'blocked', `${label} should keep business execution optimization completion blocked`); assert(item.readyForClaim === false, `${label} should expose business execution optimization readyForClaim=false`); assert(item.videoAbPreflightReadyForVideoAb === false, `${label} should expose business execution video A/B preflight readiness`); assert(Number(item.videoAbPreflightFailureCount || 0) === Number(completion?.videoAbPreflightFailureCount || 0), `${label} should mirror business execution video A/B preflight failure count`); assert(item.directCustomerProof === false, `${label} should keep business execution optimization proof boundary`); } function findBusinessExecutionOptimizationCompletionRow(index) { return (Array.isArray(index?.rows) ? index.rows : []).find(row => row.title === '优化完成度审计报告'); } function runNode(args) { const result = spawnSync(process.execPath, args, { cwd: ROOT, encoding: 'utf8', shell: false }); if (result.stdout) process.stdout.write(result.stdout); if (result.stderr) process.stderr.write(result.stderr); assert(result.status === 0, `${args.join(' ')} should pass`); } function readJson(file) { if (!fs.existsSync(file)) return null; return JSON.parse(fs.readFileSync(file, 'utf8').replace(/^\uFEFF/, '')); } function assert(condition, message) { if (!condition) throw new Error(message); } if (require.main === module) main();