#!/usr/bin/env node const fs = require('fs'); const path = require('path'); const { buildStatusSummary } = require('./optimization-status-audit'); const { buildEvidenceIndex } = require('./evidence-index'); const ROOT = path.resolve(__dirname, '..'); const OUTPUTS = path.join(ROOT, 'outputs'); function main() { const args = parseArgs(process.argv.slice(2)); const outputDir = path.resolve(args.output || process.env.TIHAO_HANDOFF_OUTPUT || path.join(OUTPUTS, `optimization-handoff-${Date.now()}`)); const summary = buildHandoffSummary({ root: ROOT, outputsDir: path.resolve(args.outputs || OUTPUTS) }); fs.mkdirSync(outputDir, { recursive: true }); const jsonPath = path.join(outputDir, 'optimization-handoff-summary.json'); const reportPath = path.join(outputDir, 'optimization-handoff-report.md'); fs.writeFileSync(jsonPath, JSON.stringify(summary, null, 2), 'utf8'); fs.writeFileSync(reportPath, withBom(renderReport(summary)), 'utf8'); console.log(JSON.stringify({ outputDir, json: jsonPath, report: reportPath, complete: summary.complete, readyCapabilities: summary.readyCapabilities.length, externalBlockers: summary.externalBlockers.length }, null, 2)); } function buildHandoffSummary({ root, outputsDir }) { const status = buildStatusSummary(root); const evidence = buildEvidenceIndex({ root, outputsDir }); const latestDataIntakePack = latestEvidenceDir(evidence, 'data-intake-pack'); const latestBusinessProofProgress = latestEvidenceDir(evidence, 'business-proof-progress'); const businessProofProgressSummary = latestBusinessProofProgress ? readJsonSummary(root, latestBusinessProofProgress, 'business-proof-progress-summary.json') : null; const latestBusinessProofNextActions = latestOutputDir(outputsDir, /^business-proof-next-actions-/); const businessProofNextActionsSummary = latestBusinessProofNextActions ? readBusinessProofNextActionsSummary(root, latestBusinessProofNextActions) : null; const latestBusinessExecutionIndex = latestOutputDir(outputsDir, /^business-execution-index-/); const businessExecutionIndexSummary = latestBusinessExecutionIndex ? readJsonSummary(root, latestBusinessExecutionIndex, 'business-execution-index-summary.json') : null; const latestProofGapSoftwareForm = latestOutputDir(outputsDir, /^tihao-proof-gap-software-form-/); const proofGapSoftwareFormSummary = latestProofGapSoftwareForm ? readJsonSummary(root, latestProofGapSoftwareForm, 'tihao-proof-gap-software-form-summary.json') : null; const latestProofGapRequest = latestOutputDir(outputsDir, /^proof-gap-request-/); const proofGapRequestSummary = latestProofGapRequest ? readJsonSummary(root, latestProofGapRequest, 'proof-gap-request-summary.json') : null; const latestProofGapClosure = latestOutputDir(outputsDir, /^proof-gap-closure-/); const proofGapClosureSummary = latestProofGapClosure ? readJsonSummary(root, latestProofGapClosure, 'proof-gap-closure-summary.json') : null; const latestFeedbackLoopClosure = latestOutputDir(outputsDir, /^feedback-loop-closure-/); const feedbackLoopClosureSummary = latestFeedbackLoopClosure ? readJsonSummary(root, latestFeedbackLoopClosure, 'feedback-loop-closure-summary.json') : null; const latestHomepageEvidenceReadiness = latestOutputDir(outputsDir, /^homepage-evidence-readiness-/); const homepageEvidenceReadinessSummary = latestHomepageEvidenceReadiness ? readJsonSummary(root, latestHomepageEvidenceReadiness, 'homepage-evidence-readiness-summary.json') : null; const latestLongRunReadiness = latestOutputDir(outputsDir, /^long-run-readiness-/); const longRunReadinessSummary = latestLongRunReadiness ? readJsonSummary(root, latestLongRunReadiness, 'long-run-readiness-summary.json') : null; const latestIntakeFieldChecklist = latestOutputDir(outputsDir, /^intake-field-checklist-/); const intakeFieldChecklistSummary = latestIntakeFieldChecklist ? readJsonSummary(root, latestIntakeFieldChecklist, 'intake-field-checklist-summary.json') : null; const latestFormIndex = latestOutputDir(outputsDir, /^latest-form-index-/); const latestFormIndexSummary = latestFormIndex ? readJsonSummary(root, latestFormIndex, 'latest-form-index-summary.json') : null; const latestLocalSeedToIntakeWorklist = latestOutputDir(outputsDir, /^local-seed-to-intake-worklist-/); const localSeedToIntakeWorklistSummary = latestLocalSeedToIntakeWorklist ? readJsonSummary(root, latestLocalSeedToIntakeWorklist, 'local-seed-to-intake-worklist-summary.json') : latestFormIndexSummary?.localSeedToIntakeWorklist || null; const latestRealGapMaterialAudit = latestOutputDir(outputsDir, /^real-gap-material-audit-/); const realGapMaterialAuditSummary = latestRealGapMaterialAudit ? readJsonSummary(root, latestRealGapMaterialAudit, 'real-gap-material-audit-summary.json') : latestFormIndexSummary?.realGapMaterialAudit || null; const latestPlanExecutionStatus = latestOutputDir(outputsDir, /^plan-execution-status-/); const planExecutionStatusSummary = latestPlanExecutionStatus ? readJsonSummary(root, latestPlanExecutionStatus, 'plan-execution-status-summary.json') : latestFormIndexSummary?.planExecutionStatus || null; const latestProofGapOperatorPack = latestOutputDir(outputsDir, /^proof-gap-operator-pack-/); const proofGapOperatorPackSummary = latestProofGapOperatorPack ? readJsonSummary(root, latestProofGapOperatorPack, 'proof-gap-operator-pack-summary.json') : latestFormIndexSummary?.proofGapOperatorPack || null; const latestRealProofIntakeBundle = latestOutputDir(outputsDir, /^real-proof-intake-bundle-/); const realProofIntakeBundleSummary = latestRealProofIntakeBundle ? readJsonSummary(root, latestRealProofIntakeBundle, 'real-proof-intake-bundle-summary.json') : latestFormIndexSummary?.realProofIntakeBundle || null; const latestRealProofClosureWorkOrder = latestOutputDir(outputsDir, /^real-proof-closure-work-order-/); const realProofClosureWorkOrderSummary = latestRealProofClosureWorkOrder ? readJsonSummary(root, latestRealProofClosureWorkOrder, 'real-proof-closure-work-order-summary.json') : latestFormIndexSummary?.realProofClosureWorkOrder || null; const latestRepairActions = latestFormIndexSummary?.repairActions || buildRepairActionsSummary(root); const latestOptimizationPipeline = latestOutputDir(outputsDir, /^optimization-pipeline-/); const optimizationPipelineSummary = latestOptimizationPipeline ? readOptimizationPipelineSummary(root, latestOptimizationPipeline) : null; const latestOptimizationCompletion = latestOutputDir(outputsDir, /^optimization-completion-/); const optimizationCompletionSummary = latestOptimizationCompletion ? readJsonSummary(root, latestOptimizationCompletion, 'optimization-completion-summary.json') : latestFormIndexSummary?.optimizationCompletion || null; const latestEvidenceIndex = latestOutputDir(outputsDir, /^evidence-index-/) || latestEvidenceDir(evidence, 'optimization-status'); const proofActionOrderPath = firstExistingPath(root, [ path.join('docs', 'business-proof-action-order.zh-CN.md'), path.join('docs', 'business-proof-action-order.md') ]); const supportDocs = [ { title: '长期优化计划主文件', path: path.join(root, 'docs', 'tihao-experience-optimization-plan.md'), purpose: '当前目标文件;用于执行长期优化计划、同步每轮进度、确认验收标准和完成边界。' }, { title: '长期优化计划中文执行版', path: path.join(root, 'docs', 'tihao-experience-optimization-plan.zh-CN.md'), purpose: '用于下一轮 AI 或项目负责人理解长期目标、策略矩阵、验收标准和完成边界。' }, { title: '补证操作顺序中文执行版', path: path.join(root, 'docs', 'business-proof-action-order.zh-CN.md'), purpose: '用于商务、投放、技术/AI 按 15 步顺序补齐真实材料和验收证据。' } , { title: '提号任务交接入口', path: path.join(root, 'docs', 'tihao-handoff-index.md'), purpose: '下一轮 AI、商务或投放接手时优先打开;按顺序定位主计划、补证顺序、最新表单索引、真实材料总包、proof-gap 请求包、商务执行总表和交接摘要。' } ] .filter(item => fs.existsSync(item.path)) .map(item => ({ title: item.title, doc: rel(root, item.path), purpose: item.purpose })); const readyCapabilities = status.items .filter(item => item.status === 'passed') .map(publicStatusItem); const readyNotProven = status.items .filter(item => item.status === 'ready_not_proven') .map(publicStatusItem); const externalBlockers = status.items .filter(item => item.status === 'blocked_by_external_data') .map(publicStatusItem); const businessProofNextActionSourceState = normalizeBusinessProofNextActionSourceState(businessProofNextActionsSummary?.sourceState); const latestFormNextActionSourceState = normalizeBusinessProofNextActionSourceState(latestFormIndexSummary?.nextActions?.sourceState); const pipelineStaleSummary = buildPipelineStaleSummary(businessProofNextActionSourceState, latestFormNextActionSourceState); const proofGapClosureCounts = normalizeProofGapClosureCounts(proofGapClosureSummary); return { generatedAt: new Date().toISOString(), root, outputsDir, complete: status.complete, statusCounts: status.counts, evidenceCounts: evidence.counts, evidenceByType: evidence.byType, readyCapabilities, readyNotProven, externalBlockers, pipelineStaleSummary, dataIntakePack: latestDataIntakePack ? { dir: latestDataIntakePack, historyTemplate: artifactRef(root, artifactPath(root, latestDataIntakePack, 'history-data-template.csv')), manualReviewTemplate: artifactRef(root, artifactPath(root, latestDataIntakePack, 'manual-review-template.csv')), readme: artifactRef(root, artifactPath(root, latestDataIntakePack, 'README.md')) } : null, localSeedToIntakeWorklist: latestLocalSeedToIntakeWorklist || latestFormIndexSummary?.localSeedToIntakeWorklist ? normalizeLocalSeedToIntakeWorklist({ ...(latestFormIndexSummary?.localSeedToIntakeWorklist || {}), ...(localSeedToIntakeWorklistSummary || {}), markdown: latestFormIndexSummary?.localSeedToIntakeWorklist?.markdown || (latestLocalSeedToIntakeWorklist ? artifactRef(root, artifactPath(root, latestLocalSeedToIntakeWorklist, 'local-seed-to-intake-worklist.md')) : ''), csv: latestFormIndexSummary?.localSeedToIntakeWorklist?.csv || (latestLocalSeedToIntakeWorklist ? artifactRef(root, artifactPath(root, latestLocalSeedToIntakeWorklist, 'local-seed-to-intake-worklist.csv')) : ''), summary: latestFormIndexSummary?.localSeedToIntakeWorklist?.summary || (latestLocalSeedToIntakeWorklist ? artifactRef(root, artifactPath(root, latestLocalSeedToIntakeWorklist, 'local-seed-to-intake-worklist-summary.json')) : ''), historyDraft: latestFormIndexSummary?.localSeedToIntakeWorklist?.historyDraft || (latestLocalSeedToIntakeWorklist ? artifactRef(root, artifactPath(root, latestLocalSeedToIntakeWorklist, 'history-intake-draft.csv')) : ''), videoWorklist: latestFormIndexSummary?.localSeedToIntakeWorklist?.videoWorklist || (latestLocalSeedToIntakeWorklist ? artifactRef(root, artifactPath(root, latestLocalSeedToIntakeWorklist, 'video-resource-worklist.csv')) : ''), candidateWorklist: latestFormIndexSummary?.localSeedToIntakeWorklist?.candidateWorklist || (latestLocalSeedToIntakeWorklist ? artifactRef(root, artifactPath(root, latestLocalSeedToIntakeWorklist, 'candidate-seed-worklist.csv')) : '') }) : null, realGapMaterialAudit: latestRealGapMaterialAudit || latestFormIndexSummary?.realGapMaterialAudit ? normalizeRealGapMaterialAudit({ ...(latestFormIndexSummary?.realGapMaterialAudit || {}), ...(realGapMaterialAuditSummary || {}), markdown: latestFormIndexSummary?.realGapMaterialAudit?.markdown || (latestRealGapMaterialAudit ? artifactRef(root, artifactPath(root, latestRealGapMaterialAudit, 'real-gap-material-audit.md')) : ''), csv: latestFormIndexSummary?.realGapMaterialAudit?.csv || (latestRealGapMaterialAudit ? artifactRef(root, artifactPath(root, latestRealGapMaterialAudit, 'real-gap-material-audit.csv')) : ''), summary: latestFormIndexSummary?.realGapMaterialAudit?.summary || (latestRealGapMaterialAudit ? artifactRef(root, artifactPath(root, latestRealGapMaterialAudit, 'real-gap-material-audit-summary.json')) : '') }) : null, proofActionOrder: proofActionOrderPath ? { doc: rel(root, proofActionOrderPath), purpose: '先按 15 步补齐真实历史 Brief、真实视频资源、商务复核和客户效果证明,再启动长跑或宣称效果。' } : null, supportDocs, businessProofProgress: latestBusinessProofProgress ? { dir: latestBusinessProofProgress, summary: artifactRef(root, artifactPath(root, latestBusinessProofProgress, 'business-proof-progress-summary.json')), report: artifactRef(root, artifactPath(root, latestBusinessProofProgress, 'business-proof-progress-report.md')), complete: Boolean(businessProofProgressSummary?.complete), counts: businessProofProgressSummary?.counts || {}, operatorPackOwnerArtifactCount: Number(businessProofProgressSummary?.operatorPackOwnerArtifactCount || (Array.isArray(businessProofProgressSummary?.operatorPackOwnerArtifacts) ? businessProofProgressSummary.operatorPackOwnerArtifacts.length : 0)), operatorPackOwnerArtifacts: normalizeProofGapOperatorOwnerArtifacts(businessProofProgressSummary?.operatorPackOwnerArtifacts || []), proofGapOperatorPackOwnerArtifactCount: Number(businessProofProgressSummary?.observed?.proofGapOperatorPackOwnerArtifactCount || 0), businessExecutionOperatorPackOwnerArtifactCount: Number(businessProofProgressSummary?.observed?.businessExecutionOperatorPackOwnerArtifactCount || 0), businessExecutionOperatorPackOwnerArtifactRowCount: Number(businessProofProgressSummary?.observed?.businessExecutionOperatorPackOwnerArtifactRowCount || 0) } : null, businessProofNextActions: latestBusinessProofNextActions ? { dir: latestBusinessProofNextActions, summary: artifactRef(root, artifactPath(root, latestBusinessProofNextActions, 'business-proof-next-actions-summary.json')), report: artifactRef(root, artifactPath(root, latestBusinessProofNextActions, 'business-proof-next-actions-report.md')), csv: artifactRef(root, artifactPath(root, latestBusinessProofNextActions, 'business-proof-next-actions.csv')), complete: Boolean(businessProofNextActionsSummary?.complete), actionCount: Number(businessProofNextActionsSummary?.actionCount ?? (Array.isArray(businessProofNextActionsSummary?.actions) ? businessProofNextActionsSummary.actions.length : 0)), ownerGroupCount: Number(businessProofNextActionsSummary?.ownerGroupCount ?? (Array.isArray(businessProofNextActionsSummary?.ownerGroups) ? businessProofNextActionsSummary.ownerGroups.length : 0)), ownerArtifactCount: Number(businessProofNextActionsSummary?.ownerArtifactCount ?? (Array.isArray(businessProofNextActionsSummary?.ownerArtifacts) ? businessProofNextActionsSummary.ownerArtifacts.length : 0)), sourceState: businessProofNextActionSourceState, topActions: normalizeBusinessProofNextTopActions( Array.isArray(businessProofNextActionsSummary?.topActions) && businessProofNextActionsSummary.topActions.length ? businessProofNextActionsSummary.topActions : businessProofNextActionsSummary?.actions ), ownerGroups: businessProofNextActionsSummary ? businessProofNextActionsSummary.ownerGroups || [] : [], ownerArtifacts: normalizeOwnerArtifacts(root, latestBusinessProofNextActions, businessProofNextActionsSummary) } : null, businessExecutionIndex: latestBusinessExecutionIndex ? { dir: latestBusinessExecutionIndex, summary: artifactRef(root, artifactPath(root, latestBusinessExecutionIndex, 'business-execution-index-summary.json')), report: artifactRef(root, artifactPath(root, latestBusinessExecutionIndex, 'business-execution-index.md')), csv: artifactRef(root, artifactPath(root, latestBusinessExecutionIndex, 'business-execution-index.csv')), passed: Boolean(businessExecutionIndexSummary?.passed), rowCount: Number(businessExecutionIndexSummary?.rowCount || 0), repairRowCount: Number(businessExecutionIndexSummary?.repairRowCount || 0), operatorPackOwnerArtifactRowCount: Number(businessExecutionIndexSummary?.operatorPackOwnerArtifactRowCount || 0), operatorPackOwnerArtifactCount: Number(businessExecutionIndexSummary?.operatorPackOwnerArtifactCount || (Array.isArray(businessExecutionIndexSummary?.operatorPackOwnerArtifacts) ? businessExecutionIndexSummary.operatorPackOwnerArtifacts.length : 0)), operatorPackOwnerArtifacts: normalizeProofGapOperatorOwnerArtifacts(businessExecutionIndexSummary?.operatorPackOwnerArtifacts || []), proofOpenCount: Number(businessExecutionIndexSummary?.proofOpenCount || 0), directCustomerProof: Boolean(businessExecutionIndexSummary?.directCustomerProof), longRunReadiness: normalizeBusinessExecutionLongRunReadiness(businessExecutionIndexSummary, longRunReadinessSummary), optimizationCompletion: normalizeBusinessExecutionOptimizationCompletion(businessExecutionIndexSummary, optimizationCompletionSummary) } : null, proofGapClosure: latestProofGapClosure ? { dir: latestProofGapClosure, summary: artifactRef(root, artifactPath(root, latestProofGapClosure, 'proof-gap-closure-summary.json')), report: artifactRef(root, artifactPath(root, latestProofGapClosure, 'proof-gap-closure-report.md')), complete: Boolean(proofGapClosureSummary?.complete), openCount: Number(proofGapClosureSummary?.openCount || 0), closedCount: Number(proofGapClosureSummary?.closedCount || 0), businessOpenCount: proofGapClosureCounts.businessOpenCount, businessClosedCount: proofGapClosureCounts.businessClosedCount, businessGapCount: proofGapClosureCounts.businessGapCount, totalRows: proofGapClosureCounts.totalRows, precheckRowCount: proofGapClosureCounts.precheckRowCount, precheckOpenCount: proofGapClosureCounts.precheckOpenCount, precheckClosedCount: proofGapClosureCounts.precheckClosedCount, countSemantics: proofGapClosureCounts.countSemantics, intakeReady: Boolean(proofGapClosureSummary?.intakeReady), rows: normalizeProofGapClosureRows(proofGapClosureSummary) } : null, feedbackLoopClosure: latestFeedbackLoopClosure ? { dir: latestFeedbackLoopClosure, summary: artifactRef(root, artifactPath(root, latestFeedbackLoopClosure, 'feedback-loop-closure-summary.json')), report: artifactRef(root, artifactPath(root, latestFeedbackLoopClosure, 'feedback-loop-closure-report.md')), csv: artifactRef(root, artifactPath(root, latestFeedbackLoopClosure, 'feedback-loop-closure.csv')), passed: Boolean(feedbackLoopClosureSummary?.passed), complete: Boolean(feedbackLoopClosureSummary?.complete), directCustomerProof: Boolean(feedbackLoopClosureSummary?.directCustomerProof), proofLevel: feedbackLoopClosureSummary?.proofLevel || '', negativeFeedbackCount: Number(feedbackLoopClosureSummary?.negativeFeedbackCount || 0), blockedCreatorCount: Number(feedbackLoopClosureSummary?.blockedCreatorCount || 0), leakedCount: Number(feedbackLoopClosureSummary?.leakedCount || 0), teamRuleSuggestionCount: Number(feedbackLoopClosureSummary?.teamRuleSuggestionCount || 0), missingInputs: feedbackLoopClosureSummary?.missingInputs || [] } : null, homepageEvidenceReadiness: latestHomepageEvidenceReadiness ? { dir: latestHomepageEvidenceReadiness, summary: artifactRef(root, artifactPath(root, latestHomepageEvidenceReadiness, 'homepage-evidence-readiness-summary.json')), report: artifactRef(root, artifactPath(root, latestHomepageEvidenceReadiness, 'homepage-evidence-readiness-report.md')), csv: artifactRef(root, artifactPath(root, latestHomepageEvidenceReadiness, 'homepage-evidence-repair-actions.csv')), ready: Boolean(homepageEvidenceReadinessSummary?.ready), complete: Boolean(homepageEvidenceReadinessSummary?.complete), directCustomerProof: Boolean(homepageEvidenceReadinessSummary?.directCustomerProof), proofLevel: homepageEvidenceReadinessSummary?.proofLevel || '', failureCount: Number(homepageEvidenceReadinessSummary?.failureCount || 0), providerEvidenceCreators: Number(homepageEvidenceReadinessSummary?.counts?.providerEvidenceCreators || 0), creatorsWithPosts: Number(homepageEvidenceReadinessSummary?.counts?.creatorsWithPosts || 0), creatorsWithEnoughRecentPosts: Number(homepageEvidenceReadinessSummary?.counts?.creatorsWithEnoughRecentPosts || 0), repairActionCount: Array.isArray(homepageEvidenceReadinessSummary?.repairActions) ? homepageEvidenceReadinessSummary.repairActions.length : 0 } : null, proofGapSoftwareForm: latestProofGapSoftwareForm ? { dir: latestProofGapSoftwareForm, summary: artifactRef(root, artifactPath(root, latestProofGapSoftwareForm, 'tihao-proof-gap-software-form-summary.json')), markdown: artifactRef(root, artifactPath(root, latestProofGapSoftwareForm, 'tihao-proof-gap-software-form.md')), csv: artifactRef(root, artifactPath(root, latestProofGapSoftwareForm, 'tihao-proof-gap-software-form.csv')), passed: Boolean(proofGapSoftwareFormSummary?.passed), rowCount: Number(proofGapSoftwareFormSummary?.rowCount || 0), duplicateIdCount: Number(proofGapSoftwareFormSummary?.duplicateIdCount || 0), headerMatches: Boolean(proofGapSoftwareFormSummary?.headerMatches), rowWidthOk: Boolean(proofGapSoftwareFormSummary?.rowWidthOk) } : null, intakeFieldChecklist: latestIntakeFieldChecklist ? { dir: latestIntakeFieldChecklist, summary: artifactRef(root, artifactPath(root, latestIntakeFieldChecklist, 'intake-field-checklist-summary.json')), report: artifactRef(root, artifactPath(root, latestIntakeFieldChecklist, 'intake-field-checklist.md')), csv: artifactRef(root, artifactPath(root, latestIntakeFieldChecklist, 'intake-field-checklist.csv')), passed: Boolean(intakeFieldChecklistSummary?.passed), itemCount: Number(intakeFieldChecklistSummary?.itemCount || 0), missingRequiredCount: Number(intakeFieldChecklistSummary?.missingRequiredCount || 0), placeholderCount: Number(intakeFieldChecklistSummary?.placeholderCount || 0), ownerGroups: normalizeIntakeOwnerGroups(root, intakeFieldChecklistSummary?.ownerGroups || []), recheckCommands: normalizeRecheckCommands(intakeFieldChecklistSummary?.recheckCommands || []), directCustomerProof: Boolean(intakeFieldChecklistSummary?.directCustomerProof) } : null, latestFormIndex: latestFormIndex ? { dir: latestFormIndex, summary: artifactRef(root, artifactPath(root, latestFormIndex, 'latest-form-index-summary.json')), report: artifactRef(root, artifactPath(root, latestFormIndex, 'latest-form-index.md')), passed: Boolean(latestFormIndexSummary?.passed), primaryFormCsv: latestFormIndexSummary?.primaryForm?.csv || '', clientListCsv: latestFormIndexSummary?.clientList?.csv || '', complete: Boolean(latestFormIndexSummary?.nextActions?.complete), actionCount: Number(latestFormIndexSummary?.nextActions?.actionCount || 0), proofOpenCount: Number(latestFormIndexSummary?.nextActions?.proofOpenCount || 0), ownerGroupCount: Number(latestFormIndexSummary?.nextActions?.ownerGroupCount || (Array.isArray(latestFormIndexSummary?.nextActions?.ownerGroups) ? latestFormIndexSummary.nextActions.ownerGroups.length : 0)), ownerArtifactCount: Number(latestFormIndexSummary?.nextActions?.ownerArtifactCount || (Array.isArray(latestFormIndexSummary?.nextActions?.ownerArtifacts) ? latestFormIndexSummary.nextActions.ownerArtifacts.length : 0)), sourceState: latestFormNextActionSourceState, topActions: normalizeBusinessProofNextTopActions(latestFormIndexSummary?.nextActions?.topActions), intakeFieldChecklist: buildLatestFormIndexChecklistSummary({ root, latestFormIndexSummary, latestIntakeFieldChecklist, intakeFieldChecklistSummary }), manualReviewLabelGuide: normalizeManualReviewLabelGuide(latestFormIndexSummary?.manualReviewLabelGuide), businessFillGuide: normalizeBusinessFillGuide(latestFormIndexSummary?.businessFillGuide), planExecutionStatus: normalizePlanExecutionStatus(latestFormIndexSummary?.planExecutionStatus || planExecutionStatusSummary), proofGapRequest: normalizeProofGapRequest(latestFormIndexSummary?.proofGapRequest || (latestProofGapRequest ? { ...(proofGapRequestSummary || {}), markdown: artifactRef(root, artifactPath(root, latestProofGapRequest, 'proof-gap-request-report.md')), csv: artifactRef(root, artifactPath(root, latestProofGapRequest, 'proof-gap-request-table.csv')), summary: artifactRef(root, artifactPath(root, latestProofGapRequest, 'proof-gap-request-summary.json')) } : proofGapRequestSummary)), proofGapOperatorPack: normalizeProofGapOperatorPack(latestFormIndexSummary?.proofGapOperatorPack || proofGapOperatorPackSummary), realProofIntakeBundle: normalizeRealProofIntakeBundle(latestFormIndexSummary?.realProofIntakeBundle || realProofIntakeBundleSummary), realProofClosureWorkOrder: normalizeRealProofClosureWorkOrder(latestFormIndexSummary?.realProofClosureWorkOrder || realProofClosureWorkOrderSummary), localSeedToIntakeWorklist: normalizeLocalSeedToIntakeWorklist(latestFormIndexSummary?.localSeedToIntakeWorklist || localSeedToIntakeWorklistSummary), realGapMaterialAudit: normalizeRealGapMaterialAudit(latestFormIndexSummary?.realGapMaterialAudit || realGapMaterialAuditSummary), longRunReadiness: normalizeLongRunReadiness(latestFormIndexSummary?.longRunReadiness || (latestLongRunReadiness ? { ...(longRunReadinessSummary || {}), report: artifactRef(root, artifactPath(root, latestLongRunReadiness, 'long-run-readiness-report.md')), summary: artifactRef(root, artifactPath(root, latestLongRunReadiness, 'long-run-readiness-summary.json')) } : longRunReadinessSummary)), optimizationCompletion: normalizeOptimizationCompletion(latestFormIndexSummary?.optimizationCompletion || (latestOptimizationCompletion ? { ...(optimizationCompletionSummary || {}), report: artifactRef(root, artifactPath(root, latestOptimizationCompletion, 'optimization-completion-report.md')), summary: artifactRef(root, artifactPath(root, latestOptimizationCompletion, 'optimization-completion-summary.json')) } : optimizationCompletionSummary)), repairActions: latestRepairActions } : null, planExecutionStatus: latestPlanExecutionStatus || latestFormIndexSummary?.planExecutionStatus ? normalizePlanExecutionStatus({ ...(latestFormIndexSummary?.planExecutionStatus || {}), ...(planExecutionStatusSummary || {}), markdown: latestFormIndexSummary?.planExecutionStatus?.markdown || (latestPlanExecutionStatus ? artifactRef(root, artifactPath(root, latestPlanExecutionStatus, 'plan-execution-status.md')) : ''), csv: latestFormIndexSummary?.planExecutionStatus?.csv || (latestPlanExecutionStatus ? artifactRef(root, artifactPath(root, latestPlanExecutionStatus, 'plan-execution-status.csv')) : ''), summary: latestFormIndexSummary?.planExecutionStatus?.summary || (latestPlanExecutionStatus ? artifactRef(root, artifactPath(root, latestPlanExecutionStatus, 'plan-execution-status-summary.json')) : '') }) : null, proofGapRequest: latestProofGapRequest || latestFormIndexSummary?.proofGapRequest ? normalizeProofGapRequest({ ...(latestFormIndexSummary?.proofGapRequest || {}), ...(proofGapRequestSummary || {}), markdown: latestFormIndexSummary?.proofGapRequest?.markdown || (latestProofGapRequest ? artifactRef(root, artifactPath(root, latestProofGapRequest, 'proof-gap-request-report.md')) : ''), csv: latestFormIndexSummary?.proofGapRequest?.csv || (latestProofGapRequest ? artifactRef(root, artifactPath(root, latestProofGapRequest, 'proof-gap-request-table.csv')) : ''), summary: latestFormIndexSummary?.proofGapRequest?.summary || (latestProofGapRequest ? artifactRef(root, artifactPath(root, latestProofGapRequest, 'proof-gap-request-summary.json')) : '') }) : null, proofGapOperatorPack: latestProofGapOperatorPack || latestFormIndexSummary?.proofGapOperatorPack ? normalizeProofGapOperatorPack({ ...(latestFormIndexSummary?.proofGapOperatorPack || {}), ...(proofGapOperatorPackSummary || {}), ownerArtifacts: latestFormIndexSummary?.proofGapOperatorPack?.ownerArtifacts || proofGapOperatorPackSummary?.ownerArtifacts || [], ownerArtifactCount: latestFormIndexSummary?.proofGapOperatorPack?.ownerArtifactCount || proofGapOperatorPackSummary?.ownerArtifactCount || 0, markdown: latestFormIndexSummary?.proofGapOperatorPack?.markdown || (latestProofGapOperatorPack ? artifactRef(root, artifactPath(root, latestProofGapOperatorPack, 'proof-gap-operator-pack.md')) : ''), csv: latestFormIndexSummary?.proofGapOperatorPack?.csv || (latestProofGapOperatorPack ? artifactRef(root, artifactPath(root, latestProofGapOperatorPack, 'proof-gap-operator-pack.csv')) : ''), summary: latestFormIndexSummary?.proofGapOperatorPack?.summary || (latestProofGapOperatorPack ? artifactRef(root, artifactPath(root, latestProofGapOperatorPack, 'proof-gap-operator-pack-summary.json')) : '') }) : null, realProofIntakeBundle: latestRealProofIntakeBundle || latestFormIndexSummary?.realProofIntakeBundle ? normalizeRealProofIntakeBundle({ ...(latestFormIndexSummary?.realProofIntakeBundle || {}), ...(realProofIntakeBundleSummary || {}), markdown: latestFormIndexSummary?.realProofIntakeBundle?.markdown || (latestRealProofIntakeBundle ? artifactRef(root, artifactPath(root, latestRealProofIntakeBundle, 'real-proof-intake-bundle.md')) : ''), csv: latestFormIndexSummary?.realProofIntakeBundle?.csv || (latestRealProofIntakeBundle ? artifactRef(root, artifactPath(root, latestRealProofIntakeBundle, 'real-proof-intake-bundle.csv')) : ''), summary: latestFormIndexSummary?.realProofIntakeBundle?.summary || (latestRealProofIntakeBundle ? artifactRef(root, artifactPath(root, latestRealProofIntakeBundle, 'real-proof-intake-bundle-summary.json')) : '') }) : null, realProofClosureWorkOrder: latestRealProofClosureWorkOrder || latestFormIndexSummary?.realProofClosureWorkOrder ? normalizeRealProofClosureWorkOrder({ ...(latestFormIndexSummary?.realProofClosureWorkOrder || {}), ...(realProofClosureWorkOrderSummary || {}), markdown: latestFormIndexSummary?.realProofClosureWorkOrder?.markdown || (latestRealProofClosureWorkOrder ? artifactRef(root, artifactPath(root, latestRealProofClosureWorkOrder, 'real-proof-closure-work-order.md')) : ''), csv: latestFormIndexSummary?.realProofClosureWorkOrder?.csv || (latestRealProofClosureWorkOrder ? artifactRef(root, artifactPath(root, latestRealProofClosureWorkOrder, 'real-proof-closure-work-order.csv')) : ''), summary: latestFormIndexSummary?.realProofClosureWorkOrder?.summary || (latestRealProofClosureWorkOrder ? artifactRef(root, artifactPath(root, latestRealProofClosureWorkOrder, 'real-proof-closure-work-order-summary.json')) : '') }) : null, realGapMaterialAudit: latestRealGapMaterialAudit || latestFormIndexSummary?.realGapMaterialAudit ? normalizeRealGapMaterialAudit({ ...(latestFormIndexSummary?.realGapMaterialAudit || {}), ...(realGapMaterialAuditSummary || {}), markdown: latestFormIndexSummary?.realGapMaterialAudit?.markdown || (latestRealGapMaterialAudit ? artifactRef(root, artifactPath(root, latestRealGapMaterialAudit, 'real-gap-material-audit.md')) : ''), csv: latestFormIndexSummary?.realGapMaterialAudit?.csv || (latestRealGapMaterialAudit ? artifactRef(root, artifactPath(root, latestRealGapMaterialAudit, 'real-gap-material-audit.csv')) : ''), summary: latestFormIndexSummary?.realGapMaterialAudit?.summary || (latestRealGapMaterialAudit ? artifactRef(root, artifactPath(root, latestRealGapMaterialAudit, 'real-gap-material-audit-summary.json')) : '') }) : null, optimizationCompletion: latestOptimizationCompletion || latestFormIndexSummary?.optimizationCompletion ? normalizeOptimizationCompletion({ ...(latestFormIndexSummary?.optimizationCompletion || {}), ...(optimizationCompletionSummary || {}), report: latestFormIndexSummary?.optimizationCompletion?.markdown || latestFormIndexSummary?.optimizationCompletion?.report || (latestOptimizationCompletion ? artifactRef(root, artifactPath(root, latestOptimizationCompletion, 'optimization-completion-report.md')) : ''), summary: latestFormIndexSummary?.optimizationCompletion?.summary || (latestOptimizationCompletion ? artifactRef(root, artifactPath(root, latestOptimizationCompletion, 'optimization-completion-summary.json')) : '') }) : null, longRunReadiness: latestLongRunReadiness || latestFormIndexSummary?.longRunReadiness ? normalizeLongRunReadiness({ ...(latestFormIndexSummary?.longRunReadiness || {}), ...(longRunReadinessSummary || {}), report: latestFormIndexSummary?.longRunReadiness?.report || (latestLongRunReadiness ? artifactRef(root, artifactPath(root, latestLongRunReadiness, 'long-run-readiness-report.md')) : ''), summary: latestFormIndexSummary?.longRunReadiness?.summary || (latestLongRunReadiness ? artifactRef(root, artifactPath(root, latestLongRunReadiness, 'long-run-readiness-summary.json')) : '') }) : null, optimizationPipeline: latestOptimizationPipeline ? { dir: latestOptimizationPipeline, summary: artifactRef(root, artifactPath(root, latestOptimizationPipeline, 'optimization-pipeline-summary.json')), report: artifactRef(root, artifactPath(root, latestOptimizationPipeline, 'optimization-pipeline-report.md')), counts: optimizationPipelineSummary?.counts || {}, readyForClaim: Boolean(optimizationPipelineSummary?.readyForClaim), nextActions: normalizePipelineNextActions(optimizationPipelineSummary) } : null, latestEvidenceIndex, nextCommands: buildNextCommands(), guardrails: [ '不能把 sample、smoke、dry-run 或 provider fallback 当成客户效果证明。', '没有真实视频 URL、封面、字幕、ASR、帧图或正文时,不能声明视频分析完成。', '没有客户选择、历史人工补号量基线和本轮人工补号量时,不能声明客户效果完成。', '任何报告、日志、模板和输出都不得写入 sessionToken、鉴权头、模型 token 或 npm token。' ] }; } function publicStatusItem(item) { return { id: item.id, requirement: item.requirement, evidence: item.evidence, next: item.next }; } function buildNextCommands() { const commands = [ { stage: '生成真实数据收集包', command: 'npm run data:intake-template -- --output outputs\\data-intake-pack-latest', when: '商务还没有统一模板时先运行。' }, { stage: '真实材料就绪预审', command: 'npm run intake:readiness -- --data-pack outputs\\data-intake-pack-latest --video-pack outputs\\video-intake-pack-latest --output outputs\\intake-readiness-latest', when: '商务/投放填完历史数据、人工复核和视频资源模板后先运行;它会拦截示例数据、半成品和缺客户选择的表。' }, { stage: '长跑前置门禁', command: 'npm run longrun:readiness -- --mode full-matrix --intake-readiness outputs\\intake-readiness-latest\\intake-readiness-summary.json --proof-gap outputs\\proof-gap-request-latest\\proof-gap-request-summary.json --proof-gap-closure outputs\\proof-gap-closure-latest\\proof-gap-closure-summary.json --output outputs\\long-run-readiness-latest', when: '启动 full-matrix/live 长跑或客户效果审计前运行;ready=false 时只能继续补真实材料,不能宣称长期目标完成。' }, { stage: '收集包一键审计 Pipeline', command: 'npm run optimization:pipeline -- --data-pack outputs\\data-intake-pack-latest --video-pack outputs\\video-intake-pack-latest --current-manual-supplement-count <本轮人工补号量> --output <输出目录> --strict', when: '商务/投放已经按统一收集包填完真实历史数据、人工复核和视频资源后运行;它会先跑 intake readiness,再串起 history/video/review/customer-effect/evidence。' }, { stage: '历史数据导入', command: 'npm run history:from-csv -- --input <历史数据CSV> --output ', when: '商务填完历史 Brief、人工名单、客户选择和历史人工补号量基线后运行。' }, { stage: '历史数据审计', command: 'npm run history:audit -- --input --output <历史审计输出目录> --strict', when: '确认历史数据能否支撑长跑和客户效果证明。' }, { stage: '商务复核指标', command: 'npm run review:metrics -- --input <已标注CSV> --output <复核指标输出目录> --strict', when: '本轮 AI 候选已经被商务标注后运行。' }, { stage: '主页近期内容证据就绪审计', command: 'npm run homepage:readiness -- --input --output outputs\\homepage-evidence-readiness-latest', when: '真实主页近期内容 provider 或本地证据补齐后运行;检查最近 10/20 篇内容、封面、标题、互动、发布时间和风险信号是否足以支撑强推荐复核。' }, { stage: '本地种子转 intake 补表 worklist', command: 'npm run local-seed-to-intake:worklist -- --outputs outputs --output outputs\\local-seed-to-intake-worklist-latest', when: '需要把本地 DHA Brief、PoC 候选和参考账号解析转成可转写的 history/video/manual-review 草稿时运行;它不写正式模板,也不证明客户效果。' }, { stage: '真实证明闭环工单', command: 'npm run real-proof:work-order -- --outputs outputs --output outputs\\real-proof-closure-work-order-latest --strict', when: '需要把 intake/video/proof-gap/completion 的未关闭项拆到负责人、字段、行范围、种子线索和复验命令时运行;工单不写正式模板,也不证明客户效果。' }, { stage: '视频资源收集包', command: 'npm run video:intake-template -- --output outputs\\video-intake-pack-latest', when: '需要真实测试参考视频/候选视频风格分析前运行,交给商务补视频 URL、封面、ASR、帧图或正文证据。' }, { stage: '视频资源就绪审计', command: 'npm run video:resource-readiness -- --input --output <视频资源审计输出目录> --strict', when: '商务补齐视频资源表后运行;通过只代表 A/B 前置资源齐备,不代表视频分析已提升命中率。' }, { stage: '视频 A/B 验收', command: 'npm run acceptance:video-ab', when: '真实 sessionToken、company、VOC social provider 和视频分析 provider 都配置好后运行。' }, { stage: '客户效果审计', command: 'npm run customer-effect:audit -- --review-csv <已标注CSV> --history-audit --current-manual-supplement-count <本轮人工补号量> --output <客户效果输出目录> --strict', when: '客户选择和人工补号量都到位后运行。' }, { stage: '真实证明缺口请求包', command: 'npm run proof-gap:request -- --output outputs\\proof-gap-request-latest', when: '每轮结束时生成,交给商务或下一轮 AI 对照补齐真实证明材料。' }, { stage: '真实证明缺口关闭审计', command: 'npm run proof-gap:closure -- --output outputs\\proof-gap-closure-latest', when: '真实历史数据、视频资源、视频 A/B、live 长跑或客户效果产物更新后运行;只有 openCount=0 才能进入长期完成判断。' }, { stage: '优化完成度审计', command: 'npm run optimization:completion -- --output outputs\\optimization-completion-latest', when: '每轮 proof-gap closure、客户效果、视频 A/B 或 live/provider 证据更新后运行;只有 readyForClaim=true 才能进入业务效果声明。' }, { stage: '反馈二轮闭环审计', command: 'npm run feedback:closure -- --feedback <反馈CSV> --memory <记忆JSON> --result <第二轮tihao-sourcing-result.json> --output outputs\\feedback-loop-closure-latest', when: '商务/客户负反馈导入记忆并重跑第二轮后运行,检查拉黑、跑偏、调性不符账号是否仍泄漏进可发客户名单。' }, { stage: '15 步补证进度表', command: 'npm run business-proof:progress -- --output outputs\\business-proof-progress-latest', when: '每轮结束时运行,用于同步 15 步补证当前卡点;它不替代客户效果证明。' }, { stage: '下一步行动队列', command: 'npm run business-proof:next-actions -- --output outputs\\business-proof-next-actions-latest', when: '每轮结束时运行,把 blocked/fail/pending 状态转成可分派的负责人、命令和通过标准。' }, { stage: '商务执行总表', command: 'npm run business-execution:index -- --output outputs\\business-execution-index-latest --strict', when: '每轮补证行动变化后运行,把负责人行动、修复清单、最新表单和验收边界汇总成商务可执行 Markdown/CSV。' }, { stage: '最新提号补证表单', command: 'npm run proof-gap:software-form -- --output outputs\\tihao-proof-gap-software-form-latest --strict', when: '每轮补证任务变化后运行,导出软件端固定 17 列 CSV/Markdown 表单;它只安排补证任务,不证明客户效果。' }, { stage: '最新提号表单索引', command: 'npm run latest-form:index -- --output outputs\\latest-form-index-latest --strict', when: '需要快速定位最新补证表单、软件端候选名单和负责人行动文件时运行;常规每轮沉淀优先使用 round:refresh。' }, { stage: '真实验收材料收集总包', command: 'npm run proof-gap:intake-bundle -- --output outputs\\real-proof-intake-bundle-latest --strict', when: '需要给商务一个一站式入口时运行;它会集中真实材料模板、字段缺口、修复清单、证明缺口和复验命令。' }, { stage: '每轮沉淀一键刷新', command: 'npm run round:refresh -- --output-root outputs --strict', when: '每轮优化结束后优先运行,按固定顺序刷新提号补证表单、proof-gap 请求/关闭审计、业务补证进度、下一步行动队列、状态、最新表单索引、交接摘要、完成度审计、证据台账和 round deposition。' }, { stage: '证据台账', command: 'npm run evidence:index -- --output outputs\\evidence-index-latest', when: '需要单独刷新证明等级时运行;常规每轮沉淀优先使用 round:refresh。' }, { stage: '交接摘要', command: 'npm run handoff:summary -- --output outputs\\optimization-handoff-latest', when: '需要单独刷新交接摘要时运行;常规每轮沉淀优先使用 round:refresh。' } ]; commands.push({ stage: '字段级补齐核对表', command: 'npm run intake:field-checklist -- --data-pack outputs\\data-intake-pack-latest --video-pack outputs\\video-intake-pack-latest --output outputs\\intake-field-checklist-latest', when: '真实材料预审未通过时运行,把缺字段、占位内容和缺最小样本量拆成商务可逐格补齐的 CSV/Markdown。' }); return commands; } function renderReport(summary) { const lines = [ '# 提号优化交接摘要', '', `- 生成时间:${summary.generatedAt}`, `- 是否完成长期目标:${summary.complete ? '是' : '否'}`, '', '## 当前状态', '', `- 已通过能力:${summary.statusCounts.passed || 0}`, `- 已具备入口但未真实证明:${summary.statusCounts.ready_not_proven || 0}`, `- 受真实外部数据阻塞:${summary.statusCounts.blocked_by_external_data || 0}`, '', '## 真实证明缺口关闭状态', '', summary.proofGapClosure ? renderProofGapClosure(summary.proofGapClosure) : '- 尚未找到 proof-gap closure 摘要,请运行 `npm run proof-gap:closure -- --output outputs\\proof-gap-closure-latest`。', '', '## 反馈二轮闭环审计', '', summary.feedbackLoopClosure ? renderFeedbackLoopClosure(summary.feedbackLoopClosure) : '- 尚未找到反馈二轮闭环审计,请运行 `npm run feedback:closure -- --output outputs\\feedback-loop-closure-latest`。当前不得声明反馈闭环客户效果证明,`directCustomerProof=false`。', '', '## 主页近期内容证据就绪审计', '', summary.homepageEvidenceReadiness ? renderHomepageEvidenceReadiness(summary.homepageEvidenceReadiness) : '- 尚未找到主页近期内容证据就绪审计,请运行 `npm run homepage:readiness -- --output outputs\\homepage-evidence-readiness-latest`。当前不能把 fallback 当作强推荐证据。', '', '## 长跑前置门禁', '', summary.longRunReadiness ? renderLongRunReadiness(summary.longRunReadiness) : '- 尚未找到长跑前置门禁,请运行 `npm run longrun:readiness -- --mode full-matrix --output outputs\\long-run-readiness-latest`。当前不得启动真实 live/full-matrix 长跑。', '', '## 优化完成度审计', '', summary.optimizationCompletion ? renderOptimizationCompletion(summary.optimizationCompletion) : '- 尚未找到优化完成度审计,请运行 `npm run optimization:completion -- --output outputs\\optimization-completion-latest`。当前不得宣称长期优化目标完成。', '', '## 证据台账', '', `- real_evidence:${summary.evidenceCounts.real_evidence || 0}`, `- smoke_or_local:${summary.evidenceCounts.smoke_or_local || 0}`, `- not_business_proof:${summary.evidenceCounts.not_business_proof || 0}`, '', '| 类型 | 数量 |', '| --- | --- |', ...Object.entries(summary.evidenceByType || {}).sort().map(([type, count]) => `| ${escapeCell(type)} | ${count} |`), '', '## 已可本地复用的能力', '', ...summary.readyCapabilities.map(item => `- ${item.id}:${item.requirement}`), '', '## 已准备好但不能宣称效果的项目', '', ...summary.readyNotProven.map(item => `- ${item.id}:${item.requirement}。下一步:${item.next}`), '', '## 仍需真实数据的阻塞项', '', ...summary.externalBlockers.map(item => `- ${item.id}:${item.requirement}。下一步:${item.next}`), '', '## 真实数据入口', '', summary.dataIntakePack ? `- 历史数据模板:${summary.dataIntakePack.historyTemplate}\n- 商务复核模板:${summary.dataIntakePack.manualReviewTemplate}\n- 填写说明:${summary.dataIntakePack.readme}` : '- 尚未找到数据收集包,请先运行 `npm run data:intake-template -- --output outputs\\data-intake-pack-latest`。', '', '## 本地种子转 intake 补表 worklist', '', summary.localSeedToIntakeWorklist ? renderLocalSeedToIntakeWorklist(summary.localSeedToIntakeWorklist) : '- 尚未找到本地种子转 intake 补表 worklist,请运行 `npm run local-seed-to-intake:worklist -- --outputs outputs --output outputs\\local-seed-to-intake-worklist-latest`。', '', '## 中文 SOP 入口', '', summary.supportDocs && summary.supportDocs.length ? renderSupportDocs(summary.supportDocs) : '- 尚未找到中文 SOP 执行文档,请优先补齐 `docs/tihao-experience-optimization-plan.zh-CN.md` 和 `docs/business-proof-action-order.zh-CN.md`。', '', '## 补证操作顺序', '', summary.proofActionOrder ? `- 文档:${summary.proofActionOrder.doc}\n- 用途:${summary.proofActionOrder.purpose}` : '- 尚未找到 `docs/business-proof-action-order.md`,请先补齐真实补证操作顺序。', '', '## 补证进度表', '', summary.businessProofProgress ? renderBusinessProofProgress(summary.businessProofProgress) : '- 尚未找到补证进度表,请运行 `npm run business-proof:progress -- --output outputs\\business-proof-progress-latest`。', '', '## 下一步行动队列', '', summary.businessProofNextActions ? renderBusinessProofNextActions(summary.businessProofNextActions) : '- 尚未找到下一步行动队列,请运行 `npm run business-proof:next-actions -- --output outputs\\business-proof-next-actions-latest`。', '', '## Pipeline stale 摘要', '', renderPipelineStaleSummary(summary.pipelineStaleSummary), '', '## 负责人行动文件', '', summary.businessProofNextActions && summary.businessProofNextActions.ownerArtifacts.length ? renderOwnerArtifacts(summary.businessProofNextActions.ownerArtifacts) : '- 尚未找到负责人分组文件,请运行 `npm run business-proof:next-actions -- --output outputs\\business-proof-next-actions-latest`。', '', '## 商务执行总表', '', summary.businessExecutionIndex ? renderBusinessExecutionIndex(summary.businessExecutionIndex) : '- 尚未找到商务执行总表,请运行 `npm run business-execution:index -- --output outputs\\business-execution-index-latest --strict`。', '', '## 最新提号补证表单', '', summary.proofGapSoftwareForm ? renderProofGapSoftwareForm(summary.proofGapSoftwareForm) : '- 尚未找到最新提号补证表单,请运行 `npm run proof-gap:software-form -- --output outputs\\tihao-proof-gap-software-form-latest --strict`。', '', '## 真实材料字段级补齐核对表', '', summary.intakeFieldChecklist ? renderIntakeFieldChecklist(summary.intakeFieldChecklist) : '- 尚未找到字段级补齐核对表,请运行 `npm run intake:field-checklist -- --data-pack outputs\\data-intake-pack-latest --video-pack outputs\\video-intake-pack-latest --output outputs\\intake-field-checklist-latest`。', '', '## 最新提号表单索引', '', summary.latestFormIndex ? renderLatestFormIndex(summary.latestFormIndex) : '- 尚未找到最新提号表单索引,请运行 `npm run latest-form:index -- --output outputs\\latest-form-index-latest --strict`。', '', '## 长期计划执行状态面板', '', summary.planExecutionStatus ? renderPlanExecutionStatus(summary.planExecutionStatus) : '- 尚未找到长期计划执行状态面板,请运行 `npm run plan:execution-status -- --output outputs\\plan-execution-status-latest --strict`。', '', '## 真实证明缺口请求包', '', summary.proofGapRequest ? renderProofGapRequest(summary.proofGapRequest) : '- 尚未找到真实证明缺口请求包,请运行 `npm run proof-gap:request -- --output outputs\\proof-gap-request-latest`。', '', '## 提号补证操作包', '', summary.proofGapOperatorPack ? renderProofGapOperatorPack(summary.proofGapOperatorPack) : '- 尚未找到提号补证操作包,请运行 `npm run proof-gap:operator-pack -- --output outputs\\proof-gap-operator-pack-latest --strict`。', '', '## 真实验收材料收集总包', '', summary.realProofIntakeBundle ? renderRealProofIntakeBundle(summary.realProofIntakeBundle) : '- 尚未找到真实验收材料收集总包,请运行 `npm run proof-gap:intake-bundle -- --output outputs\\real-proof-intake-bundle-latest --strict`。', '', '## 真实证明闭环工单', '', summary.realProofClosureWorkOrder ? renderRealProofClosureWorkOrder(summary.realProofClosureWorkOrder) : '- 尚未找到真实证明闭环工单,请运行 `npm run real-proof:work-order -- --outputs outputs --output outputs\\real-proof-closure-work-order-latest --strict`。该工单只指导补真实材料,不证明客户效果。', '', '## Pipeline 下一步动作', '', summary.optimizationPipeline ? renderPipelineNextActions(summary.optimizationPipeline) : '- 尚未找到 `outputs/optimization-pipeline-latest`,请运行收集包一键审计 Pipeline。', '', '## 下一轮命令', '', '| 阶段 | 命令 | 使用时机 |', '| --- | --- | --- |', ...summary.nextCommands.map(item => `| ${escapeCell(item.stage)} | \`${escapeCell(item.command)}\` | ${escapeCell(item.when)} |`), '', '## 边界', '', ...summary.guardrails.map(item => `- ${item}`) ]; return lines.join('\n'); } function latestEvidenceDir(evidence, type) { const item = (evidence.entries || []).find(entry => entry.type === type); return item ? item.dir : ''; } function latestOutputDir(outputsDir, pattern) { if (!fs.existsSync(outputsDir)) return ''; const dirs = fs.readdirSync(outputsDir, { withFileTypes: true }) .filter(entry => entry.isDirectory() && pattern.test(entry.name)) .map(entry => path.join(outputsDir, entry.name)) .sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs); return dirs[0] ? artifactRef(ROOT, dirs[0]) : ''; } function firstExistingPath(root, relativePaths) { for (const relativePath of relativePaths) { const resolved = path.join(root, relativePath); if (fs.existsSync(resolved)) return resolved; } return ''; } function renderSupportDocs(docs) { return [ '| 文档 | 路径 | 用途 |', '| --- | --- | --- |', ...docs.map(item => tableRow([item.title, item.doc, item.purpose])) ].join('\n'); } function readBusinessProofNextActionsSummary(root, dir) { const summaryPath = artifactPath(root, dir, 'business-proof-next-actions-summary.json'); if (!fs.existsSync(summaryPath)) return null; try { return JSON.parse(fs.readFileSync(summaryPath, 'utf8')); } catch { return null; } } function readOptimizationPipelineSummary(root, dir) { return readJsonSummary(root, dir, 'optimization-pipeline-summary.json'); } function readJsonSummary(root, dir, name) { const summaryPath = artifactPath(root, dir, name); if (!fs.existsSync(summaryPath)) return null; try { return JSON.parse(fs.readFileSync(summaryPath, 'utf8')); } catch { return null; } } function renderProofGapSoftwareForm(form) { return [ `- CSV:${form.csv}`, `- Markdown:${form.markdown}`, `- Summary:${form.summary}`, `- passed:${form.passed ? 'true' : 'false'}`, `- rowCount:${form.rowCount}`, `- duplicateIdCount:${form.duplicateIdCount}`, `- headerMatches:${form.headerMatches ? 'true' : 'false'}`, `- rowWidthOk:${form.rowWidthOk ? 'true' : 'false'}` ].join('\n'); } function renderIntakeFieldChecklist(checklist) { const lines = [ `- 报告:${checklist.report}`, `- CSV:${checklist.csv}`, `- Summary:${checklist.summary}`, `- passed:${checklist.passed ? 'true' : 'false'}`, `- itemCount:${checklist.itemCount}`, `- missingRequiredCount:${checklist.missingRequiredCount}`, `- placeholderCount:${checklist.placeholderCount}`, `- directCustomerProof:${checklist.directCustomerProof ? 'true' : 'false'}`, '- 用途:给商务/投放逐字段补齐真实历史 Brief、人工复核、客户选择和视频资源;它不证明客户效果完成。' ]; if (checklist.ownerGroups?.length) { lines.push('', '### 按负责人补齐顺序', '', renderIntakeOwnerGroups(checklist.ownerGroups)); } if (checklist.recheckCommands?.length) { lines.push('', '### 补齐后复验命令', '', renderRecheckCommands(checklist.recheckCommands)); } return lines.join('\n'); } function renderLatestFormIndex(index) { const lines = [ `- 报告:${index.report}`, `- Summary:${index.summary}`, `- passed:${index.passed ? 'true' : 'false'}`, `- 最新补证表单:${index.primaryFormCsv || '缺失'}`, `- 最新候选名单:${index.clientListCsv || '缺失'}`, `- complete:${index.complete ? 'true' : 'false'}`, `- actionCount:${index.actionCount}`, `- proofOpenCount:${index.proofOpenCount}`, `- ownerGroupCount:${index.ownerGroupCount || 0}`, `- ownerArtifactCount:${index.ownerArtifactCount || 0}`, `- intakeFailureCount:${index.sourceState?.intakeFailureCount || 0}`, `- videoFailureCount:${index.sourceState?.videoFailureCount || 0}`, `- pipelineReadyForClaim:${index.sourceState?.pipelineReadyForClaim ? 'true' : 'false'}`, '- 用途:给商务和下一轮 AI 快速定位当前该打开的表单;它不证明客户效果完成。' ]; if (Array.isArray(index.topActions) && index.topActions.length) { lines.push( '', '### 下一步行动队列摘要', '', '| ID | 优先级 | 负责人 | 动作 | 状态 | 命令 | 通过标准 | 缺证明项 |', '| --- | ---: | --- | --- | --- | --- | --- | ---: |', ...index.topActions.map(item => tableRow([ item.id || 'missing', item.priority || item.order || 0, item.owner || 'missing', item.title || 'missing', item.status || 'missing', item.command || 'missing', item.acceptance || 'missing', item.missingProofRequirementCount || 0 ])) ); } if (index.intakeFieldChecklist) { lines.push( '', '### 字段补齐核对表索引', '', `- 报告:${index.intakeFieldChecklist.markdown || '缺失'}`, `- CSV:${index.intakeFieldChecklist.csv || '缺失'}`, `- ownerGroupCount:${Array.isArray(index.intakeFieldChecklist.ownerGroups) ? index.intakeFieldChecklist.ownerGroups.length : 0}`, `- recheckCommandCount:${Array.isArray(index.intakeFieldChecklist.recheckCommands) ? index.intakeFieldChecklist.recheckCommands.length : 0}` ); } if (index.businessFillGuide?.rows?.length) { lines.push('', renderBusinessFillGuide(index.businessFillGuide)); } if (index.manualReviewLabelGuide) { lines.push( '', '### 人工复核标签与归因指南', '', `- report: ${index.manualReviewLabelGuide.markdown || 'missing'}`, `- csv: ${index.manualReviewLabelGuide.csv || 'missing'}`, `- labelCount: ${index.manualReviewLabelGuide.labelCount || 0}`, `- attributionTypeCount: ${index.manualReviewLabelGuide.attributionTypeCount || 0}`, `- directCustomerProof: ${index.manualReviewLabelGuide.directCustomerProof ? 'true' : 'false'}` ); } if (index.planExecutionStatus) { lines.push('', '### 长期计划执行状态面板', '', renderPlanExecutionStatus(index.planExecutionStatus)); } if (index.proofGapRequest) { lines.push('', '### 真实证明缺口请求包', '', renderProofGapRequest(index.proofGapRequest)); } if (index.proofGapOperatorPack) { lines.push('', '### 提号补证操作包', '', renderProofGapOperatorPack(index.proofGapOperatorPack)); } if (index.realProofIntakeBundle) { lines.push('', '### 真实验收材料收集总包', '', renderRealProofIntakeBundle(index.realProofIntakeBundle)); } if (index.realProofClosureWorkOrder) { lines.push('', '### 真实证明闭环工单', '', renderRealProofClosureWorkOrder(index.realProofClosureWorkOrder)); } if (index.localSeedToIntakeWorklist) { lines.push('', '### 本地种子转 intake 补表 worklist', '', renderLocalSeedToIntakeWorklist(index.localSeedToIntakeWorklist)); } if (index.realGapMaterialAudit) { lines.push('', '### three real gap material audit', '', renderRealGapMaterialAudit(index.realGapMaterialAudit)); } if (index.longRunReadiness) { lines.push('', '### 长跑前置门禁', '', renderLongRunReadiness(index.longRunReadiness)); } if (index.optimizationCompletion) { lines.push('', '### 优化完成度审计', '', renderOptimizationCompletion(index.optimizationCompletion)); } const repairRows = renderLatestFormRepairActions(index.repairActions); if (repairRows) lines.push('', '### 修复清单入口', '', repairRows); return lines.join('\n'); } function renderBusinessProofProgress(progress) { const counts = progress.counts || {}; const lines = [ `- 摘要:${progress.summary || 'missing'}`, `- 报告:${progress.report || 'missing'}`, `- complete: ${progress.complete ? 'true' : 'false'}`, `- pass: ${counts.pass || 0}`, `- pending: ${counts.pending || 0}`, `- fail: ${counts.fail || 0}`, `- blocked_by_external_data: ${counts.blocked_by_external_data || 0}`, `- operatorPackOwnerArtifactCount: ${progress.operatorPackOwnerArtifactCount || 0}`, `- businessExecutionOperatorPackOwnerArtifactRowCount: ${progress.businessExecutionOperatorPackOwnerArtifactRowCount || 0}`, '- 边界:该进度表只同步 15 步补证状态和负责人派工文件;complete=false 或 proof-gap 未关闭时,不得宣称客户效果、命中率提升或人工补号量下降。' ]; if (Array.isArray(progress.operatorPackOwnerArtifacts) && progress.operatorPackOwnerArtifacts.length) { lines.push( '', '| 负责人 | 动作数 | Markdown | CSV | 缺口 |', '| --- | ---: | --- | --- | --- |', ...progress.operatorPackOwnerArtifacts.map(item => tableRow([ item.owner, item.actionCount, item.markdown || 'missing', item.csv || 'missing', (item.gapIds || []).join(';') || 'missing' ])) ); } return lines.join('\n'); } function renderPlanExecutionStatus(status) { const lines = [ `- report: ${status.markdown || 'missing'}`, `- csv: ${status.csv || 'missing'}`, `- summary: ${status.summary || 'missing'}`, `- passed: ${status.passed ? 'true' : 'false'}`, `- complete: ${status.complete ? 'true' : 'false'}`, `- rowCount: ${status.rowCount || 0}`, `- proofOpenCount: ${status.proofOpenCount || 0}`, `- operatorPackOwnerArtifactCount: ${status.operatorPackOwnerArtifactCount || 0}`, `- businessExecutionOperatorPackOwnerArtifactRowCount: ${status.businessExecutionOperatorPackOwnerArtifactRowCount || 0}`, `- directCustomerProof: ${status.directCustomerProof ? 'true' : 'false'}`, '- 边界:该面板只同步计划执行进度;proofOpenCount 不为 0 时,不得宣称客户效果、命中率提升或人工补号量下降。' ]; if (Array.isArray(status.operatorPackOwnerArtifacts) && status.operatorPackOwnerArtifacts.length) { lines.push( '', '| 负责人 | 动作数 | Markdown | CSV | 缺口 |', '| --- | ---: | --- | --- | --- |', ...status.operatorPackOwnerArtifacts.map(item => tableRow([ item.owner, item.actionCount, item.markdown || 'missing', item.csv || 'missing', (item.gapIds || []).join(';') || 'missing' ])) ); } return lines.join('\n'); } function renderProofGapRequest(request) { return [ `- report: ${request.markdown || 'missing'}`, `- csv: ${request.csv || 'missing'}`, `- summary: ${request.summary || 'missing'}`, `- complete: ${request.complete ? 'true' : 'false'}`, `- unresolvedCount: ${request.unresolvedCount || 0}`, `- rowCount: ${request.rowCount || 0}`, `- proofGapClosureOpenCount: ${request.proofGapClosureOpenCount || 0}`, `- directCustomerProof: ${request.directCustomerProof ? 'true' : 'false'}`, '- 边界:该请求包只列出仍需补齐的真实证明材料;不能作为客户效果、命中率提升或人工补号量下降证明。' ].join('\n'); } function renderProofGapOperatorPack(pack) { const lines = [ `- report: ${pack.markdown || 'missing'}`, `- csv: ${pack.csv || 'missing'}`, `- summary: ${pack.summary || 'missing'}`, `- passed: ${pack.passed ? 'true' : 'false'}`, `- complete: ${pack.complete ? 'true' : 'false'}`, `- openCount: ${pack.openCount || 0}`, `- rowCount: ${pack.rowCount || 0}`, `- gapIds: ${(pack.gapIds || []).join(';') || 'missing'}`, `- fillFileCount: ${pack.fillFileCount || 0}`, `- recheckCommandCount: ${pack.recheckCommandCount || 0}`, `- ownerGroupCount: ${pack.ownerGroupCount || 0}`, `- ownerArtifactCount: ${pack.ownerArtifactCount || 0}`, `- evidenceRowCount: ${pack.evidenceRowCount || 0}`, `- nextActionCount: ${pack.nextActionCount || 0}`, `- missingProofRequirementCount: ${pack.missingProofRequirementCount || 0}`, `- directCustomerProof: ${pack.directCustomerProof ? 'true' : 'false'}`, '- 边界:该操作包只用于补证派工;proofOpenCount 不为 0 时,不得宣称客户效果、命中率提升或人工补号量下降。' ]; if (Array.isArray(pack.ownerArtifacts) && pack.ownerArtifacts.length) { lines.push( '', '| 负责人 | 动作数 | Markdown | CSV | 缺口 |', '| --- | ---: | --- | --- | --- |', ...pack.ownerArtifacts.map(item => tableRow([ item.owner, item.actionCount, item.markdown || 'missing', item.csv || 'missing', (item.gapIds || []).join(';') || 'missing' ])) ); } return lines.join('\n'); } function renderRealProofIntakeBundle(bundle) { const lines = [ `- report: ${bundle.markdown || 'missing'}`, `- csv: ${bundle.csv || 'missing'}`, `- summary: ${bundle.summary || 'missing'}`, `- passed: ${bundle.passed ? 'true' : 'false'}`, `- complete: ${bundle.complete ? 'true' : 'false'}`, `- proofOpenCount: ${bundle.proofOpenCount || 0}`, `- fillRowCount: ${bundle.fillRowCount || 0}`, `- proofGapRowCount: ${bundle.proofGapRowCount || 0}`, `- recheckCommandCount: ${bundle.recheckCommandCount || 0}`, `- operatorPackEvidenceRowCount: ${bundle.operatorPackEvidenceRowCount || 0}`, `- operatorPackNextActionCount: ${bundle.operatorPackNextActionCount || 0}`, `- operatorPackMissingProofRequirementCount: ${bundle.operatorPackMissingProofRequirementCount || 0}`, `- operatorPackOwnerArtifactCount: ${bundle.operatorPackOwnerArtifactCount || 0}`, `- directCustomerProof: ${bundle.directCustomerProof ? 'true' : 'false'}`, '- 边界:该总包只集中真实材料模板、字段缺口、修复清单和复验命令;proofOpenCount 不为 0 时,不得宣称客户效果。' ]; if (Array.isArray(bundle.operatorPackOwnerArtifacts) && bundle.operatorPackOwnerArtifacts.length) { lines.push( '', '| 负责人 | 动作数 | Markdown | CSV | 缺口 |', '| --- | ---: | --- | --- | --- |', ...bundle.operatorPackOwnerArtifacts.map(item => tableRow([ item.owner, item.actionCount, item.markdown || 'missing', item.csv || 'missing', (item.gapIds || []).join(';') || 'missing' ])) ); } return lines.join('\n'); } function renderRealProofClosureWorkOrder(workOrder) { const lines = [ `- report: ${workOrder.markdown || 'missing'}`, `- csv: ${workOrder.csv || 'missing'}`, `- summary: ${workOrder.summary || 'missing'}`, `- passed: ${workOrder.passed ? 'true' : 'false'}`, `- complete: ${workOrder.complete ? 'true' : 'false'}`, `- workOrderCount: ${workOrder.workOrderCount || 0}`, `- ownerGroupCount: ${workOrder.ownerGroupCount || 0}`, `- proofGapOpenCount: ${workOrder.proofGapOpenCount || 0}`, `- intakeFailureCount: ${workOrder.intakeFailureCount || 0}`, `- videoFailureCount: ${workOrder.videoFailureCount || 0}`, `- realCandidateRows: ${workOrder.realCandidateRows || 0}`, `- canCloseProofGap: ${workOrder.canCloseProofGap ? 'true' : 'false'}`, `- directCustomerProof: ${workOrder.directCustomerProof ? 'true' : 'false'}`, '- 边界:该工单只把缺口拆到目标模板、字段、行范围、种子线索和复验命令;不写入正式模板,不证明客户效果。' ]; if (Array.isArray(workOrder.ownerArtifacts) && workOrder.ownerArtifacts.length) { lines.push( '', '| 负责人 | 工单数 | Markdown | CSV | 缺口 | 目标文件 |', '| --- | ---: | --- | --- | --- | --- |', ...workOrder.ownerArtifacts.map(item => tableRow([ item.owner, item.workOrderCount, item.markdown || 'missing', item.csv || 'missing', (item.gapIds || []).join(';') || 'missing', (item.targetFiles || []).join(';') || 'missing' ])) ); } return lines.join('\n'); } function renderBusinessFillGuide(guide) { return [ '### 商务填表执行视图', '', `- rowCount:${Number(guide.rowCount || guide.rows.length || 0)}`, `- directCustomerProof:${guide.directCustomerProof ? 'true' : 'false'}`, `- proofLevel:${guide.proofLevel || 'unknown'}`, '', '| 顺序 | 负责人 | 表单区域 | 填表文件 | 优先项 | 问题数 | 填表动作 | 补齐后复验 | 验收 | 边界 |', '| ---: | --- | --- | --- | --- | ---: | --- | --- | --- | --- |', ...guide.rows.map(item => tableRow([ item.order, item.owner, item.section, item.file || '缺失', (item.firstItemIds || []).join('、') || '无', item.issueCount, item.fillAction, item.recheckCommand || '待补齐后运行 round:refresh', item.acceptance, item.boundary ])), '', `- ${guide.guardrail || '本视图只帮助商务按表补齐真实材料,不代表客户效果已达标。'}` ].join('\n'); } function renderIntakeOwnerGroups(ownerGroups) { return [ '| 负责人 | 范围 | 字段项 | 缺必填 | 占位 | 文件 | 下一步 | 通过标准 | 边界 |', '| --- | --- | ---: | ---: | ---: | --- | --- | --- | --- |', ...ownerGroups.map(item => tableRow([ item.owner, item.section, item.itemCount, item.missingRequiredCount, item.placeholderCount, (item.files || []).join(';') || '缺失', item.nextAction, item.acceptance, item.boundary ])) ].join('\n'); } function renderRecheckCommands(recheckCommands) { return [ '| 阶段 | 负责人 | 命令 | 通过标准 | 边界 |', '| --- | --- | --- | --- | --- |', ...recheckCommands.map(item => tableRow([ item.stage, item.owner, item.command, item.acceptance, item.boundary ])) ].join('\n'); } function renderLatestFormRepairActions(repairActions) { if (!repairActions) return ''; const rows = [repairActions.dataIntake, repairActions.videoResource, repairActions.homepageEvidence].filter(Boolean); if (!rows.length) return ''; return [ '| 类型 | CSV | 当前问题数 | 修复动作数 | 状态 |', '| --- | --- | ---: | ---: | --- |', ...rows.map(item => tableRow([ item.title || '修复清单', item.csv || '缺失', Number(item.failureCount || 0), Number(item.repairActionCount || 0), item.ready || item.readyForVideoAbPreflight ? '已就绪' : '待补齐' ])) ].join('\n'); } function buildRepairActionsSummary(root) { const intakeReadinessDir = latestOutputDir(/^intake-readiness-/) || 'outputs/intake-readiness-latest'; const videoResourceReadinessDir = latestOutputDir(/^video-resource-readiness-/) || 'outputs/video-resource-readiness-latest'; const homepageEvidenceReadinessDir = latestOutputDir(/^homepage-evidence-readiness-/) || 'outputs/homepage-evidence-readiness-latest'; const intakeReadinessSummary = readJsonSummary(root, intakeReadinessDir, 'intake-readiness-summary.json'); const videoResourceReadinessSummary = readJsonSummary(root, videoResourceReadinessDir, 'video-resource-readiness-summary.json'); const homepageEvidenceReadinessSummary = readJsonSummary(root, homepageEvidenceReadinessDir, 'homepage-evidence-readiness-summary.json'); return { dataIntake: { title: '历史数据/商务复核修复清单', csv: relIfExists(root, path.join(root, intakeReadinessDir, 'intake-readiness-repair-actions.csv')), report: relIfExists(root, path.join(root, intakeReadinessDir, 'intake-readiness-report.md')), summary: relIfExists(root, path.join(root, intakeReadinessDir, 'intake-readiness-summary.json')), failureCount: Number(intakeReadinessSummary?.failureCount || 0), repairActionCount: Number(intakeReadinessSummary?.repairActions?.length || 0), ready: Boolean(intakeReadinessSummary?.acceptance?.overallReady) }, videoResource: { title: '视频资源修复清单', csv: relIfExists(root, path.join(root, videoResourceReadinessDir, 'video-resource-repair-actions.csv')), report: relIfExists(root, path.join(root, videoResourceReadinessDir, 'video-resource-readiness-report.md')), summary: relIfExists(root, path.join(root, videoResourceReadinessDir, 'video-resource-readiness-summary.json')), failureCount: Number(videoResourceReadinessSummary?.failureCount || 0), repairActionCount: Number(videoResourceReadinessSummary?.repairActions?.length || 0), readyForVideoAbPreflight: Boolean(videoResourceReadinessSummary?.acceptance?.readyForVideoAbPreflight) }, homepageEvidence: { title: '主页近期内容证据修复清单', csv: relIfExists(root, path.join(root, homepageEvidenceReadinessDir, 'homepage-evidence-repair-actions.csv')), report: relIfExists(root, path.join(root, homepageEvidenceReadinessDir, 'homepage-evidence-readiness-report.md')), summary: relIfExists(root, path.join(root, homepageEvidenceReadinessDir, 'homepage-evidence-readiness-summary.json')), failureCount: Number(homepageEvidenceReadinessSummary?.failureCount || 0), repairActionCount: Number(homepageEvidenceReadinessSummary?.repairActions?.length || 0), ready: Boolean(homepageEvidenceReadinessSummary?.ready), directCustomerProof: Boolean(homepageEvidenceReadinessSummary?.directCustomerProof) } }; } function buildLatestFormIndexChecklistSummary({ root, latestFormIndexSummary, latestIntakeFieldChecklist, intakeFieldChecklistSummary }) { const indexed = latestFormIndexSummary?.intakeFieldChecklist || null; const ownerGroups = Array.isArray(indexed?.ownerGroups) && indexed.ownerGroups.length ? indexed.ownerGroups : normalizeIntakeOwnerGroups(root, intakeFieldChecklistSummary?.ownerGroups || []); const recheckCommands = Array.isArray(indexed?.recheckCommands) && indexed.recheckCommands.length ? indexed.recheckCommands : normalizeRecheckCommands(intakeFieldChecklistSummary?.recheckCommands || []); if (!indexed && !latestIntakeFieldChecklist && !ownerGroups.length && !recheckCommands.length) return null; return { ...(indexed || {}), markdown: indexed?.markdown || (latestIntakeFieldChecklist ? artifactRef(root, artifactPath(root, latestIntakeFieldChecklist, 'intake-field-checklist.md')) : ''), csv: indexed?.csv || (latestIntakeFieldChecklist ? artifactRef(root, artifactPath(root, latestIntakeFieldChecklist, 'intake-field-checklist.csv')) : ''), ownerGroups, recheckCommands }; } function normalizeBusinessFillGuide(guide) { if (!guide || !Array.isArray(guide.rows) || guide.rows.length === 0) return null; const rows = guide.rows.map((item, index) => ({ order: Number(item.order || index + 1), owner: item.owner || '', section: item.section || '', file: item.file || '', firstItemIds: Array.isArray(item.firstItemIds) ? item.firstItemIds : [], issueCount: Number(item.issueCount || 0), missingRequiredCount: Number(item.missingRequiredCount || 0), placeholderCount: Number(item.placeholderCount || 0), fillAction: item.fillAction || '', recheckCommand: item.recheckCommand || '', acceptance: item.acceptance || '', boundary: item.boundary || '' })); return { title: guide.title || '商务填表执行视图', directCustomerProof: Boolean(guide.directCustomerProof), proofLevel: guide.proofLevel || 'smoke_or_local', rowCount: Number(guide.rowCount || rows.length), rows, guardrail: guide.guardrail || '本视图只帮助商务按表补齐真实材料,不代表客户效果已达标。' }; } function normalizeManualReviewLabelGuide(guide) { if (!guide) return null; return { markdown: guide.markdown || '', csv: guide.csv || '', summary: guide.summary || '', passed: Boolean(guide.passed), directCustomerProof: Boolean(guide.directCustomerProof), proofLevel: guide.proofLevel || 'smoke_or_local', labelCount: Number(guide.labelCount || 0), positiveLabelCount: Number(guide.positiveLabelCount || 0), negativeLabelCount: Number(guide.negativeLabelCount || 0), attributionTypeCount: Number(guide.attributionTypeCount || 0), negativeLabelsRequireAttribution: Boolean(guide.negativeLabelsRequireAttribution) }; } function normalizePlanExecutionStatus(status) { if (!status) return null; return { markdown: status.markdown || status.report || status.files?.report || '', csv: status.csv || status.files?.csv || '', summary: status.summary || status.files?.summary || '', passed: Boolean(status.passed), complete: Boolean(status.complete), directCustomerProof: false, rowCount: Number(status.rowCount || 0), proofOpenCount: Number(status.proofOpenCount || 0), operatorPackOwnerArtifactCount: Number(status.operatorPackOwnerArtifactCount || (Array.isArray(status.operatorPackOwnerArtifacts) ? status.operatorPackOwnerArtifacts.length : 0)), operatorPackOwnerArtifacts: normalizeProofGapOperatorOwnerArtifacts(status.operatorPackOwnerArtifacts || []), businessExecutionOperatorPackOwnerArtifactCount: Number(status.businessExecutionOperatorPackOwnerArtifactCount || 0), businessExecutionOperatorPackOwnerArtifactRowCount: Number(status.businessExecutionOperatorPackOwnerArtifactRowCount || 0), roundPassed: Boolean(status.roundPassed), guardrailCount: Array.isArray(status.guardrails) ? status.guardrails.length : Number(status.guardrailCount || 0) }; } function normalizeProofGapRequest(request) { if (!request) return null; return { markdown: request.markdown || request.report || request.files?.report || '', csv: request.csv || request.files?.csv || '', summary: request.summary || request.files?.summary || '', complete: Boolean(request.complete), directCustomerProof: false, proofLevel: request.proofLevel || 'smoke_or_local', unresolvedCount: Number(request.unresolvedCount || 0), rowCount: Array.isArray(request.rows) ? request.rows.length : Number(request.rowCount || 0), proofGapClosureOpenCount: Number(request.proofGapClosureOpenCount || request.observed?.proofGapClosureOpenCount || 0) }; } function normalizeProofGapOperatorPack(pack) { if (!pack) return null; return { markdown: pack.markdown || pack.report || pack.files?.report || '', csv: pack.csv || pack.files?.csv || '', summary: pack.summary || pack.files?.summary || '', passed: Boolean(pack.passed), complete: Boolean(pack.complete), directCustomerProof: false, proofLevel: pack.proofLevel || 'not_business_proof', rowCount: Number(pack.rowCount || 0), openCount: Number(pack.openCount || 0), gapIds: Array.isArray(pack.gapIds) ? pack.gapIds : (Array.isArray(pack.rows) ? pack.rows.map(row => row.gapId).filter(Boolean) : []), fillFileCount: Number(pack.fillFileCount || 0), recheckCommandCount: Number(pack.recheckCommandCount || 0), ownerGroupCount: Number(pack.ownerGroupCount || (Array.isArray(pack.ownerGroups) ? pack.ownerGroups.length : 0)), ownerArtifactCount: Number(pack.ownerArtifactCount || (Array.isArray(pack.ownerArtifacts) ? pack.ownerArtifacts.length : 0)), ownerArtifacts: normalizeProofGapOperatorOwnerArtifacts(pack.ownerArtifacts || []), evidenceRowCount: Number(pack.evidenceRowCount || 0), nextActionCount: Number(pack.nextActionCount || 0), missingProofRequirementCount: Number(pack.missingProofRequirementCount || 0) }; } function normalizeProofGapOperatorOwnerArtifacts(ownerArtifacts) { if (!Array.isArray(ownerArtifacts)) return []; return ownerArtifacts.map(item => ({ owner: item.owner || '', actionCount: Number(item.actionCount || 0), topPriority: Number(item.topPriority || 0), markdown: item.markdown || '', csv: item.csv || '', gapIds: Array.isArray(item.gapIds) ? item.gapIds : [], evidenceRowCount: Number(item.evidenceRowCount || 0), nextActionCount: Number(item.nextActionCount || 0) })); } function normalizeWorkOrderOwnerArtifacts(ownerArtifacts) { if (!Array.isArray(ownerArtifacts)) return []; return ownerArtifacts.map(item => ({ owner: item.owner || '', workOrderCount: Number(item.workOrderCount || item.actionCount || 0), topPriority: Number(item.topPriority || 0), markdown: item.markdown || '', csv: item.csv || '', targetFiles: Array.isArray(item.targetFiles) ? item.targetFiles : [], gapIds: Array.isArray(item.gapIds) ? item.gapIds : [] })); } function normalizeRealProofIntakeBundle(bundle) { if (!bundle) return null; return { markdown: bundle.markdown || bundle.report || bundle.files?.report || '', csv: bundle.csv || bundle.files?.csv || '', summary: bundle.summary || bundle.files?.summary || '', passed: Boolean(bundle.passed), complete: Boolean(bundle.complete), directCustomerProof: false, proofLevel: bundle.proofLevel || 'not_business_proof', proofOpenCount: Number(bundle.proofOpenCount || 0), fillRowCount: Number(bundle.fillRowCount || 0), proofGapRowCount: Number(bundle.proofGapRowCount || 0), repairRowCount: Number(bundle.repairRowCount || 0), recheckCommandCount: Number(bundle.recheckCommandCount || 0), exportRowCount: Number(bundle.exportRowCount || 0), operatorPackEvidenceRowCount: Number(bundle.operatorPackEvidenceRowCount || bundle.sourceState?.operatorPackEvidenceRowCount || 0), operatorPackNextActionCount: Number(bundle.operatorPackNextActionCount || bundle.sourceState?.operatorPackNextActionCount || 0), operatorPackMissingProofRequirementCount: Number(bundle.operatorPackMissingProofRequirementCount || bundle.sourceState?.operatorPackMissingProofRequirementCount || 0), operatorPackOwnerArtifactCount: Number(bundle.operatorPackOwnerArtifactCount || bundle.sourceState?.operatorPackOwnerArtifactCount || (Array.isArray(bundle.operatorPackOwnerArtifacts) ? bundle.operatorPackOwnerArtifacts.length : 0)), operatorPackOwnerArtifacts: normalizeProofGapOperatorOwnerArtifacts(bundle.operatorPackOwnerArtifacts || []) }; } function normalizeRealProofClosureWorkOrder(workOrder) { if (!workOrder) return null; return { markdown: workOrder.markdown || workOrder.report || workOrder.files?.report || '', csv: workOrder.csv || workOrder.files?.csv || '', summary: workOrder.summary || workOrder.files?.summary || '', passed: Boolean(workOrder.passed), complete: Boolean(workOrder.complete), directCustomerProof: false, canCloseProofGap: Boolean(workOrder.canCloseProofGap), proofLevel: workOrder.proofLevel || 'not_business_proof', materialType: workOrder.materialType || 'real_proof_closure_work_order', workOrderCount: Number(workOrder.workOrderCount || 0), ownerGroupCount: Number(workOrder.ownerGroupCount || 0), seedSuggestionCount: Number(workOrder.seedSuggestionCount || 0), proofGapOpenCount: Number(workOrder.proofGapOpenCount || workOrder.sourceState?.proofGapOpenCount || 0), intakeFailureCount: Number(workOrder.intakeFailureCount || workOrder.sourceState?.intakeFailureCount || 0), videoFailureCount: Number(workOrder.videoFailureCount || workOrder.sourceState?.videoFailureCount || 0), realCandidateRows: Number(workOrder.realCandidateRows || workOrder.sourceState?.realCandidateRows || 0), ownerArtifacts: normalizeWorkOrderOwnerArtifacts(workOrder.ownerArtifacts || []) }; } function normalizeLocalSeedToIntakeWorklist(worklist) { if (!worklist) return null; const counts = worklist.counts || {}; const countOf = (value, fallback) => { if (Array.isArray(value)) return value.length; if (Number.isFinite(Number(value))) return Number(value); if (Array.isArray(fallback)) return fallback.length; return Number(fallback || 0); }; return { markdown: worklist.markdown || worklist.report || '', csv: worklist.csv || '', summary: worklist.summary || '', historyDraft: worklist.historyDraft || '', videoWorklist: worklist.videoWorklist || '', candidateWorklist: worklist.candidateWorklist || '', proofLevel: worklist.proofLevel || 'not_business_proof', materialType: worklist.materialType || 'seed_to_intake_worklist', complete: Boolean(worklist.complete), directCustomerProof: false, canCloseProofGap: Boolean(worklist.canCloseProofGap), historyDraftRows: countOf(worklist.historyDraftRows, counts.historyDraftRows), referenceSeedRows: countOf(worklist.referenceSeedRows, counts.referenceSeedRows), candidateSeedRows: countOf(worklist.candidateSeedRows, counts.candidateSeedRows), videoWorklistRows: countOf(worklist.videoWorklistRows, counts.videoWorklistRows), videoCandidateNeedsResourceRows: countOf(worklist.videoCandidateNeedsResourceRows, counts.videoCandidateNeedsResourceRows), missingProofRequirementCount: Number(worklist.missingProofRequirementCount ?? (Array.isArray(worklist.missingProofRequirements) ? worklist.missingProofRequirements.length : 0)), nextActionCount: Number(worklist.nextActionCount ?? (Array.isArray(worklist.nextActions) ? worklist.nextActions.length : 0)), targetFiles: Array.isArray(worklist.targetFiles) ? worklist.targetFiles : [] }; } function normalizeLongRunReadiness(readiness) { if (!readiness) return null; return { report: readiness.report || readiness.markdown || readiness.files?.report || '', summary: readiness.summary || readiness.files?.summary || '', mode: readiness.mode || '', ready: Boolean(readiness.ready), directCustomerProof: false, proofLevel: readiness.proofLevel || (readiness.ready ? 'smoke_or_local' : 'not_business_proof'), passCount: Number(readiness.passCount ?? readiness.counts?.pass ?? 0), failCount: Number(readiness.failCount ?? readiness.counts?.fail ?? 0), warnCount: Number(readiness.warnCount ?? readiness.counts?.warn ?? 0), nextCommandCount: Number(readiness.nextCommandCount ?? (Array.isArray(readiness.nextCommands) ? readiness.nextCommands.length : 0)), boundaryCount: Number(readiness.boundaryCount ?? (Array.isArray(readiness.boundaries) ? readiness.boundaries.length : 0)) }; } function normalizeRealGapMaterialAudit(audit) { if (!audit) return null; return { markdown: audit.markdown || audit.report || '', csv: audit.csv || '', summary: audit.summary || '', proofLevel: audit.proofLevel || 'not_business_proof', materialType: audit.materialType || 'three_real_gap_material_audit', complete: Boolean(audit.complete), directCustomerProof: false, canCloseProofGap: Boolean(audit.canCloseProofGap), gapCount: Number(audit.gapCount || 0), materialFoundCount: Number(audit.materialFoundCount || 0), openGapCount: Number(audit.openGapCount || 0), sourceState: audit.sourceState || {}, gapIds: Array.isArray(audit.gapIds) ? audit.gapIds : (Array.isArray(audit.gaps) ? audit.gaps.map(item => item.id).filter(Boolean) : []) }; } function normalizeOptimizationCompletion(completion) { if (!completion) return null; const blockingReasonCount = Number(completion.blockingReasonCount ?? (Array.isArray(completion.blockingReasons) ? completion.blockingReasons.length : 0)); const blockingReasonsWithMissingProofRequirements = Number(completion.blockingReasonsWithMissingProofRequirements ?? countRowsWithMissingProofRequirements(completion.blockingReasons)); const missingProofRequirementCount = Number(completion.missingProofRequirementCount ?? uniqueMissingProofRequirementCount(completion.blockingReasons)); return { report: completion.report || completion.markdown || completion.files?.report || '', summary: completion.summary || completion.files?.summary || '', complete: Boolean(completion.complete), readyForClaim: Boolean(completion.readyForClaim), proofGapOpenCount: Number(completion.proofGapOpenCount || 0), blockingReasonCount, blockingReasonsWithMissingProofRequirements, missingProofRequirementCount, videoAbPreflightReadyForVideoAb: Boolean(completion.videoAbPreflightReadyForVideoAb), videoAbPreflightFailureCount: Number.isFinite(Number(completion.videoAbPreflightFailureCount)) ? Number(completion.videoAbPreflightFailureCount) : null, directCustomerProof: false, proofLevel: completion.readyForClaim ? 'real_evidence' : 'not_business_proof', canClaim: { tihaoRateImproved: Boolean(completion.canClaim?.tihaoRateImproved), customerEffectAchieved: Boolean(completion.canClaim?.customerEffectAchieved), manualSupplementReduced: Boolean(completion.canClaim?.manualSupplementReduced) }, nextRequiredCommandCount: Array.isArray(completion.nextRequiredCommands) ? completion.nextRequiredCommands.length : 0 }; } function normalizeBusinessExecutionLongRunReadiness(indexSummary, readinessSummary) { if (!indexSummary) return null; const row = (Array.isArray(indexSummary.rows) ? indexSummary.rows : []) .find(item => String(item.title || '').includes('长跑前置门禁报告')); const summary = indexSummary.sourceFiles?.longRunReadiness || readinessSummary?.summary || readinessSummary?.files?.summary || ''; if (!row && !summary) return null; const ready = Boolean(readinessSummary?.ready); const failCount = Number(readinessSummary?.counts?.fail ?? readinessSummary?.failCount ?? row?.actionCount ?? 0); return { report: row?.mainFile || readinessSummary?.report || readinessSummary?.files?.report || '', summary, status: row?.status || (ready ? 'ready' : 'blocked'), actionCount: Number(row?.actionCount ?? failCount), ready, failCount, expectedArtifact: row?.expectedArtifact || summary, acceptance: row?.acceptance || '', boundary: row?.boundary || '', directCustomerProof: false, proofLevel: ready ? 'smoke_or_local' : 'not_business_proof' }; } function normalizeBusinessExecutionOptimizationCompletion(indexSummary, completionSummary) { if (!indexSummary) return null; const row = (Array.isArray(indexSummary.rows) ? indexSummary.rows : []) .find(item => String(item.title || '').includes('优化完成度审计报告')); const summary = indexSummary.sourceFiles?.optimizationCompletion || completionSummary?.summary || completionSummary?.files?.summary || ''; if (!row && !summary) return null; const readyForClaim = Boolean(completionSummary?.readyForClaim); const blockingReasonCount = Number(completionSummary?.blockingReasonCount ?? (Array.isArray(completionSummary?.blockingReasons) ? completionSummary.blockingReasons.length : row?.actionCount ?? 0)); const blockingReasonsWithMissingProofRequirements = Number(completionSummary?.blockingReasonsWithMissingProofRequirements ?? countRowsWithMissingProofRequirements(completionSummary?.blockingReasons)); const missingProofRequirementCount = Number(completionSummary?.missingProofRequirementCount ?? uniqueMissingProofRequirementCount(completionSummary?.blockingReasons)); return { report: row?.mainFile || completionSummary?.report || completionSummary?.files?.report || '', summary, status: row?.status || (readyForClaim ? 'ready_for_claim' : 'blocked'), actionCount: Number(row?.actionCount ?? blockingReasonCount), complete: Boolean(completionSummary?.complete), readyForClaim, proofGapOpenCount: Number(completionSummary?.proofGapOpenCount || 0), blockingReasonCount, blockingReasonsWithMissingProofRequirements, missingProofRequirementCount, videoAbPreflightReadyForVideoAb: Boolean(completionSummary?.videoAbPreflightReadyForVideoAb), videoAbPreflightFailureCount: Number.isFinite(Number(completionSummary?.videoAbPreflightFailureCount)) ? Number(completionSummary.videoAbPreflightFailureCount) : null, expectedArtifact: row?.expectedArtifact || summary, acceptance: row?.acceptance || '', boundary: row?.boundary || '', directCustomerProof: false, proofLevel: readyForClaim ? 'real_evidence' : 'not_business_proof' }; } function normalizeIntakeOwnerGroups(root, ownerGroups) { if (!Array.isArray(ownerGroups)) return []; return ownerGroups.map(item => ({ owner: item.owner || '', section: item.section || '', itemCount: Number(item.itemCount || 0), missingRequiredCount: Number(item.missingRequiredCount || 0), placeholderCount: Number(item.placeholderCount || 0), files: Array.isArray(item.files) ? item.files.map(file => artifactRef(root, file)) : [], firstItemIds: Array.isArray(item.firstItemIds) ? item.firstItemIds : [], nextAction: item.nextAction || '', acceptance: item.acceptance || '', boundary: item.boundary || '' })); } function normalizeRecheckCommands(recheckCommands) { if (!Array.isArray(recheckCommands)) return []; return recheckCommands.map(item => ({ stage: item.stage || '', owner: item.owner || '', command: item.command || '', acceptance: item.acceptance || '', boundary: item.boundary || '' })); } function normalizeBusinessProofNextActionSourceState(sourceState) { if (!sourceState || typeof sourceState !== 'object') return {}; return { progressComplete: Boolean(sourceState.progressComplete), progressCounts: sourceState.progressCounts || {}, proofGapOpenCount: Number(sourceState.proofGapOpenCount || 0), intakeFailureCount: Number(sourceState.intakeFailureCount || 0), videoFailureCount: Number(sourceState.videoFailureCount || 0), homepageFailureCount: Number(sourceState.homepageFailureCount || 0), pipelineReadyForClaim: Boolean(sourceState.pipelineReadyForClaim), pipelineFailCount: Number(sourceState.pipelineFailCount || 0), pipelineNextActionCount: Number(sourceState.pipelineNextActionCount || 0), pipelineRawNextActionCount: Number(sourceState.pipelineRawNextActionCount || 0), pipelineSuppressedNextActionCount: Number(sourceState.pipelineSuppressedNextActionCount || 0), pipelineStale: Boolean(sourceState.pipelineStale), pipelineStaleReason: sourceState.pipelineStaleReason || '', pipelineStaleMissingProofRequirements: Array.isArray(sourceState.pipelineStaleMissingProofRequirements) ? sourceState.pipelineStaleMissingProofRequirements : [] }; } function buildPipelineStaleSummary(primarySourceState, fallbackSourceState) { const primaryHasState = primarySourceState && Object.keys(primarySourceState).length > 0; const fallbackHasState = fallbackSourceState && Object.keys(fallbackSourceState).length > 0; const sourceState = primaryHasState ? primarySourceState : (fallbackHasState ? fallbackSourceState : {}); const missingProofRequirements = Array.isArray(sourceState.pipelineStaleMissingProofRequirements) ? sourceState.pipelineStaleMissingProofRequirements : []; return { pipelineStale: Boolean(sourceState.pipelineStale), pipelineReadyForClaim: Boolean(sourceState.pipelineReadyForClaim), pipelineNextActionCount: Number(sourceState.pipelineNextActionCount || 0), pipelineRawNextActionCount: Number(sourceState.pipelineRawNextActionCount || 0), pipelineSuppressedNextActionCount: Number(sourceState.pipelineSuppressedNextActionCount || 0), pipelineStaleReason: sourceState.pipelineStaleReason || '', pipelineStaleMissingProofRequirementCount: missingProofRequirements.length, pipelineStaleMissingProofRequirements: missingProofRequirements, boundary: 'stale optimization-pipeline nextActions are not current dispatch or completion evidence; rerun optimization:pipeline only after real materials are ready.' }; } function normalizeBusinessProofNextTopActions(actions, limit = 5) { if (!Array.isArray(actions)) return []; return actions .slice() .sort((left, right) => actionSortValue(left) - actionSortValue(right)) .slice(0, limit) .map(item => ({ id: item.id || '', source: item.source || '', order: Number(item.order || 0), priority: Number(item.priority || 0), owner: item.owner || '', title: item.title || '', status: item.status || '', evidence: item.evidence || '', command: item.command || '', expectedArtifact: item.expectedArtifact || '', acceptance: item.acceptance || '', missingProofRequirementCount: Array.isArray(item.missingProofRequirements) ? item.missingProofRequirements.length : 0, missingProofRequirements: Array.isArray(item.missingProofRequirements) ? item.missingProofRequirements : [] })); } function actionSortValue(item) { const priority = Number(item?.priority); const order = Number(item?.order); const primary = Number.isFinite(priority) && priority > 0 ? priority : 999999; const secondary = Number.isFinite(order) && order > 0 ? order / 100000 : 0; return primary + secondary; } function normalizeOwnerArtifacts(root, dir, summary) { if (!summary || !Array.isArray(summary.ownerArtifacts)) return []; return summary.ownerArtifacts.map(item => ({ owner: item.owner, actionCount: item.actionCount, topPriority: item.topPriority, markdown: item.markdown ? artifactRef(root, artifactPath(root, dir, item.markdown)) : '', csv: item.csv ? artifactRef(root, artifactPath(root, dir, item.csv)) : '', repairArtifacts: normalizeRepairArtifacts(item.repairArtifacts || []) })); } function renderPipelineStaleSummary(summary) { if (!summary) return '- 尚未生成 pipeline stale 摘要。'; return [ `- pipelineStale: ${summary.pipelineStale ? 'true' : 'false'}`, `- pipelineReadyForClaim: ${summary.pipelineReadyForClaim ? 'true' : 'false'}`, `- pipelineNextActionCount: ${summary.pipelineNextActionCount || 0}`, `- pipelineRawNextActionCount: ${summary.pipelineRawNextActionCount || 0}`, `- pipelineSuppressedNextActionCount: ${summary.pipelineSuppressedNextActionCount || 0}`, `- pipelineStaleMissingProofRequirementCount: ${summary.pipelineStaleMissingProofRequirementCount || 0}`, summary.pipelineStaleReason ? `- pipelineStaleReason: ${summary.pipelineStaleReason}` : '', `- 边界:${summary.boundary || 'stale pipeline 动作不能作为当前派工或完成证据。'}` ].filter(Boolean).join('\n'); } function renderBusinessProofNextActions(queue) { const sourceState = queue.sourceState || {}; const lines = [ `- 摘要:${queue.summary || 'missing'}`, `- 报告:${queue.report || 'missing'}`, `- CSV:${queue.csv || 'missing'}`, `- complete: ${queue.complete ? 'true' : 'false'}`, `- actionCount: ${queue.actionCount || 0}`, `- ownerGroupCount: ${queue.ownerGroupCount || 0}`, `- ownerArtifactCount: ${queue.ownerArtifactCount || 0}`, `- proofGapOpenCount: ${sourceState.proofGapOpenCount || 0}`, `- intakeFailureCount: ${sourceState.intakeFailureCount || 0}`, `- videoFailureCount: ${sourceState.videoFailureCount || 0}`, `- pipelineReadyForClaim: ${sourceState.pipelineReadyForClaim ? 'true' : 'false'}`, `- pipelineStale: ${sourceState.pipelineStale ? 'true' : 'false'}`, `- pipelineSuppressedNextActionCount: ${sourceState.pipelineSuppressedNextActionCount || 0}`, sourceState.pipelineStaleReason ? `- pipelineStaleReason: ${sourceState.pipelineStaleReason}` : '', '- 边界:该行动队列只把 blocked/fail/pending 转成派工动作;actionCount 不代表业务证明完成,proof-gap 未关闭时不得宣称提号率、客户效果或人工补号量改善。' ]; if (Array.isArray(queue.topActions) && queue.topActions.length) { lines.push( '', '| ID | 优先级 | 负责人 | 动作 | 状态 | 命令 | 通过标准 | 缺证明项 |', '| --- | ---: | --- | --- | --- | --- | --- | ---: |', ...queue.topActions.map(item => tableRow([ item.id || 'missing', item.priority || item.order || 0, item.owner || 'missing', item.title || 'missing', item.status || 'missing', item.command || 'missing', item.acceptance || 'missing', item.missingProofRequirementCount || 0 ])) ); } return lines.join('\n'); } function renderOwnerArtifacts(ownerArtifacts) { const lines = [ '| 负责人 | 行动数 | 最高优先级 | Markdown | CSV | 修复附件 |', '| --- | --- | --- | --- | --- | --- |' ]; for (const item of ownerArtifacts) { lines.push(`| ${escapeCell(item.owner)} | ${item.actionCount} | ${item.topPriority} | ${escapeCell(item.markdown)} | ${escapeCell(item.csv)} | ${escapeCell(renderRepairArtifactSummary(item.repairArtifacts || []))} |`); } return lines.join('\n'); } function renderBusinessExecutionIndex(index) { const lines = [ `- 报告:${index.report}`, `- CSV:${index.csv}`, `- Summary:${index.summary}`, `- passed:${index.passed ? 'true' : 'false'}`, `- rowCount:${index.rowCount}`, `- repairRowCount:${index.repairRowCount}`, `- operatorPackOwnerArtifactCount:${index.operatorPackOwnerArtifactCount || 0}`, `- proofOpenCount:${index.proofOpenCount}`, `- directCustomerProof:${index.directCustomerProof ? 'true' : 'false'}` ]; if (Array.isArray(index.operatorPackOwnerArtifacts) && index.operatorPackOwnerArtifacts.length) { lines.push( '', '| 负责人 | 动作数 | Markdown | CSV | 缺口 |', '| --- | ---: | --- | --- | --- |', ...index.operatorPackOwnerArtifacts.map(item => tableRow([ item.owner, item.actionCount, item.markdown || 'missing', item.csv || 'missing', (item.gapIds || []).join(';') || 'missing' ])) ); } if (index.longRunReadiness) { lines.push( `- longRunReadiness.report:${index.longRunReadiness.report || 'missing'}`, `- longRunReadiness.summary:${index.longRunReadiness.summary || 'missing'}`, `- longRunReadiness.status:${index.longRunReadiness.status || 'unknown'}`, `- longRunReadiness.ready:${index.longRunReadiness.ready ? 'true' : 'false'}`, `- longRunReadiness.failCount:${index.longRunReadiness.failCount}`, `- longRunReadiness.directCustomerProof:${index.longRunReadiness.directCustomerProof ? 'true' : 'false'}` ); } if (index.optimizationCompletion) { lines.push( `- optimizationCompletion.report:${index.optimizationCompletion.report || 'missing'}`, `- optimizationCompletion.summary:${index.optimizationCompletion.summary || 'missing'}`, `- optimizationCompletion.status:${index.optimizationCompletion.status || 'unknown'}`, `- optimizationCompletion.readyForClaim:${index.optimizationCompletion.readyForClaim ? 'true' : 'false'}`, `- optimizationCompletion.proofGapOpenCount:${index.optimizationCompletion.proofGapOpenCount}`, `- optimizationCompletion.blockingReasonCount:${index.optimizationCompletion.blockingReasonCount}`, `- optimizationCompletion.videoAbPreflightReadyForVideoAb:${index.optimizationCompletion.videoAbPreflightReadyForVideoAb ? 'true' : 'false'}`, `- optimizationCompletion.videoAbPreflightFailureCount:${index.optimizationCompletion.videoAbPreflightFailureCount ?? 'unknown'}`, `- optimizationCompletion.directCustomerProof:${index.optimizationCompletion.directCustomerProof ? 'true' : 'false'}` ); } return lines.join('\n'); } function normalizeProofGapClosureRows(summary) { if (!summary || !Array.isArray(summary.rows)) return []; return summary.rows.map(row => ({ id: row.id || '', title: row.title || '', status: row.status || '', evidence: row.evidence || '', missingProofRequirements: Array.isArray(row.missingProofRequirements) ? row.missingProofRequirements.filter(Boolean) : [], required: row.required || '', command: row.command || '', expectedArtifact: row.expectedArtifact || '' })); } function normalizeProofGapClosureCounts(summary) { const rows = Array.isArray(summary?.rows) ? summary.rows : []; const precheckRows = rows.filter(row => row.id === 'intake-readiness'); const businessRows = rows.filter(row => row.id !== 'intake-readiness'); const fallbackBusinessOpen = businessRows.filter(row => row.status === 'open').length; const fallbackBusinessClosed = businessRows.filter(row => row.status === 'closed').length; const fallbackPrecheckOpen = precheckRows.filter(row => row.status === 'open').length; const fallbackPrecheckClosed = precheckRows.filter(row => row.status === 'closed').length; return { businessOpenCount: numberOr(summary?.businessOpenCount, numberOr(summary?.openCount, fallbackBusinessOpen)), businessClosedCount: numberOr(summary?.businessClosedCount, numberOr(summary?.closedCount, fallbackBusinessClosed)), businessGapCount: numberOr(summary?.businessGapCount, businessRows.length), totalRows: numberOr(summary?.totalRows, rows.length), precheckRowCount: numberOr(summary?.precheckRowCount, precheckRows.length), precheckOpenCount: numberOr(summary?.precheckOpenCount, fallbackPrecheckOpen), precheckClosedCount: numberOr(summary?.precheckClosedCount, fallbackPrecheckClosed), countSemantics: summary?.countSemantics || { openCount: 'business_gap_open_count_excludes_precheck_rows', closedCount: 'business_gap_closed_count_excludes_precheck_rows', businessOpenCount: 'same_as_openCount', businessClosedCount: 'same_as_closedCount', precheckOpenCount: 'precheck_rows_reported_separately' } }; } function numberOr(value, fallback) { const number = Number(value); return Number.isFinite(number) ? number : fallback; } function renderProofGapClosure(closure) { const lines = [ `- Summary:${closure.summary}`, `- 报告:${closure.report}`, `- complete:${closure.complete ? 'true' : 'false'}`, `- openCount:${closure.openCount}`, `- closedCount:${closure.closedCount}`, `- businessOpenCount:${closure.businessOpenCount}`, `- businessClosedCount:${closure.businessClosedCount}`, `- businessGapCount:${closure.businessGapCount}`, `- totalRows:${closure.totalRows}`, `- precheckRowCount:${closure.precheckRowCount}`, `- precheckOpenCount:${closure.precheckOpenCount}`, `- intakeReady:${closure.intakeReady ? 'true' : 'false'}`, `- countSemantics.openCount:${closure.countSemantics?.openCount || 'unknown'}`, '' ]; if (!closure.rows.length) { lines.push('- 暂无 proof-gap closure 行。'); return lines.join('\n'); } lines.push('| 缺口ID | 状态 | 当前证据 | 缺失证明 | 验收标准 | 下一步命令 |'); lines.push('| --- | --- | --- | --- | --- | --- |'); for (const row of closure.rows) { if (row.status !== 'open') continue; lines.push(`| ${escapeCell(row.id)} | ${escapeCell(row.status)} | ${escapeCell(row.evidence)} | ${escapeCell(row.missingProofRequirements.join(';') || '-')} | ${escapeCell(row.required)} | \`${escapeCell(row.command)}\` |`); } return lines.join('\n'); } function renderFeedbackLoopClosure(closure) { return [ `- Summary:${closure.summary}`, `- 报告:${closure.report}`, `- CSV:${closure.csv}`, `- passed:${closure.passed ? 'true' : 'false'}`, `- complete:${closure.complete ? 'true' : 'false'}`, `- directCustomerProof:${closure.directCustomerProof ? 'true' : 'false'}`, `- proofLevel:${closure.proofLevel || 'unknown'}`, `- negativeFeedbackCount:${closure.negativeFeedbackCount}`, `- blockedCreatorCount:${closure.blockedCreatorCount}`, `- leakedCount:${closure.leakedCount}`, `- teamRuleSuggestionCount:${closure.teamRuleSuggestionCount}`, `- missingInputs:${closure.missingInputs.length ? closure.missingInputs.join(';') : '无'}` ].join('\n'); } function renderHomepageEvidenceReadiness(readiness) { return [ `- Summary:${readiness.summary}`, `- 报告:${readiness.report}`, `- 修复 CSV:${readiness.csv}`, `- ready:${readiness.ready ? 'true' : 'false'}`, `- complete:${readiness.complete ? 'true' : 'false'}`, `- directCustomerProof:${readiness.directCustomerProof ? 'true' : 'false'}`, `- proofLevel:${readiness.proofLevel || 'unknown'}`, `- failureCount:${readiness.failureCount}`, `- providerEvidenceCreators:${readiness.providerEvidenceCreators}`, `- creatorsWithPosts:${readiness.creatorsWithPosts}`, `- creatorsWithEnoughRecentPosts:${readiness.creatorsWithEnoughRecentPosts}`, `- repairActionCount:${readiness.repairActionCount}`, '- 边界:只证明主页证据是否可用于复核,不证明客户命中率提升。' ].join('\n'); } function renderLongRunReadiness(readiness) { return [ `- report: ${readiness.report || 'missing'}`, `- summary: ${readiness.summary || 'missing'}`, `- mode: ${readiness.mode || 'unknown'}`, `- ready=${readiness.ready ? 'true' : 'false'}`, `- passCount: ${readiness.passCount || 0}`, `- failCount: ${readiness.failCount || 0}`, `- warnCount: ${readiness.warnCount || 0}`, `- directCustomerProof: ${readiness.directCustomerProof ? 'true' : 'false'}`, `- proofLevel: ${readiness.proofLevel || 'not_business_proof'}`, `- nextCommandCount: ${readiness.nextCommandCount || 0}`, `- boundaryCount: ${readiness.boundaryCount || 0}`, '- 边界:该门禁只判断 full-matrix/live 长跑是否可启动;ready=false 时不得宣称客户效果、命中率提升或人工补号量下降。' ].join('\n'); } function renderLocalSeedToIntakeWorklist(worklist) { return [ `- 报告:${worklist.markdown || 'missing'}`, `- 汇总 CSV:${worklist.csv || 'missing'}`, `- Summary:${worklist.summary || 'missing'}`, `- history-intake-draft:${worklist.historyDraft || 'missing'}`, `- video-resource-worklist:${worklist.videoWorklist || 'missing'}`, `- candidate-seed-worklist:${worklist.candidateWorklist || 'missing'}`, `- proofLevel: ${worklist.proofLevel || 'not_business_proof'}`, `- materialType: ${worklist.materialType || 'seed_to_intake_worklist'}`, `- directCustomerProof: ${worklist.directCustomerProof ? 'true' : 'false'}`, `- canCloseProofGap: ${worklist.canCloseProofGap ? 'true' : 'false'}`, `- complete: ${worklist.complete ? 'true' : 'false'}`, `- historyDraftRows: ${worklist.historyDraftRows || 0}`, `- referenceSeedRows: ${worklist.referenceSeedRows || 0}`, `- candidateSeedRows: ${worklist.candidateSeedRows || 0}`, `- videoWorklistRows: ${worklist.videoWorklistRows || 0}`, `- videoCandidateNeedsResourceRows: ${worklist.videoCandidateNeedsResourceRows || 0}`, `- missingProofRequirementCount: ${worklist.missingProofRequirementCount || 0}`, `- nextActionCount: ${worklist.nextActionCount || 0}`, `- targetFiles: ${(worklist.targetFiles || []).join(';') || 'missing'}`, '- 边界:该 worklist 只把本地种子转成补表草稿;空字段仍需商务/投放补真实材料,不能关闭 proof-gap。' ].join('\n'); } function renderRealGapMaterialAudit(audit) { return [ `- report: ${audit.markdown || 'missing'}`, `- csv: ${audit.csv || 'missing'}`, `- summary: ${audit.summary || 'missing'}`, `- proofLevel: ${audit.proofLevel || 'not_business_proof'}`, `- materialType: ${audit.materialType || 'three_real_gap_material_audit'}`, `- directCustomerProof: ${audit.directCustomerProof ? 'true' : 'false'}`, `- canCloseProofGap: ${audit.canCloseProofGap ? 'true' : 'false'}`, `- complete: ${audit.complete ? 'true' : 'false'}`, `- materialFound: ${audit.materialFoundCount || 0}/${audit.gapCount || 0}`, `- openGapCount: ${audit.openGapCount || 0}`, `- gapIds: ${(audit.gapIds || []).join('; ') || 'missing'}`, '- boundary: material coverage does not prove tihao-rate improvement, customer effect, video A/B lift, or manual supplement reduction.' ].join('\n'); } function renderOptimizationCompletion(completion) { return [ `- report: ${completion.report || 'missing'}`, `- summary: ${completion.summary || 'missing'}`, `- complete: ${completion.complete ? 'true' : 'false'}`, `- readyForClaim: ${completion.readyForClaim ? 'true' : 'false'}`, `- proofGapOpenCount: ${completion.proofGapOpenCount}`, `- blockingReasonCount: ${completion.blockingReasonCount}`, `- blockingReasonsWithMissingProofRequirements: ${completion.blockingReasonsWithMissingProofRequirements || 0}`, `- missingProofRequirementCount: ${completion.missingProofRequirementCount || 0}`, `- videoAbPreflightReadyForVideoAb: ${completion.videoAbPreflightReadyForVideoAb ? 'true' : 'false'}`, `- videoAbPreflightFailureCount: ${completion.videoAbPreflightFailureCount ?? 'unknown'}`, `- directCustomerProof: ${completion.directCustomerProof ? 'true' : 'false'}`, `- proofLevel: ${completion.proofLevel || 'not_business_proof'}`, `- canClaim.tihaoRateImproved: ${completion.canClaim?.tihaoRateImproved ? 'true' : 'false'}`, `- canClaim.customerEffectAchieved: ${completion.canClaim?.customerEffectAchieved ? 'true' : 'false'}`, `- canClaim.manualSupplementReduced: ${completion.canClaim?.manualSupplementReduced ? 'true' : 'false'}`, `- nextRequiredCommandCount: ${completion.nextRequiredCommandCount || 0}`, '- 边界:readyForClaim=false 时不得宣称提号率提升、客户效果达标或人工补号量下降。' ].join('\n'); } function normalizeRepairArtifacts(repairArtifacts) { return repairArtifacts.map(item => ({ title: item.title || '', csv: item.csv || '', actionCount: Number(item.actionCount || 0), topPriority: Number(item.topPriority || 0), fields: Array.isArray(item.fields) ? item.fields : [] })); } function renderRepairArtifactSummary(repairArtifacts) { if (!repairArtifacts.length) return '无'; return repairArtifacts .map(item => `${item.title || '修复清单'}:${item.csv || '缺失'}(${item.actionCount || 0})`) .join(';'); } function tableRow(cells) { return `| ${cells.map(escapeCell).join(' | ')} |`; } function normalizePipelineNextActions(summary) { if (!summary || !Array.isArray(summary.nextActions)) return []; return summary.nextActions.map(item => ({ priority: item.priority, owner: item.owner, title: item.title, step: item.step, command: item.command, expectedArtifact: item.expectedArtifact, acceptance: item.acceptance })); } function renderPipelineNextActions(pipeline) { const lines = [ `- 摘要:${pipeline.summary}`, `- 报告:${pipeline.report}`, `- readyForClaim:${pipeline.readyForClaim ? 'true' : 'false'}`, `- pass:${pipeline.counts.pass || 0}`, `- fail:${pipeline.counts.fail || 0}`, `- nextActions:${pipeline.nextActions.length}`, '' ]; if (!pipeline.nextActions.length) { lines.push('- 暂无 pipeline 下一步动作。'); return lines.join('\n'); } lines.push('| 优先级 | 负责人 | 动作 | 触发步骤 | 执行命令 | 通过标准 |'); lines.push('| --- | --- | --- | --- | --- | --- |'); for (const item of pipeline.nextActions) { lines.push(`| ${item.priority} | ${escapeCell(item.owner)} | ${escapeCell(item.title)} | ${escapeCell(item.step)} | ${escapeCell(item.command)} | ${escapeCell(item.acceptance)} |`); } return lines.join('\n'); } function parseArgs(argv) { const args = {}; for (let index = 0; index < argv.length; index += 1) { const raw = argv[index]; if (!raw.startsWith('--')) continue; const key = raw.slice(2).replace(/-([a-z])/g, (_, char) => char.toUpperCase()); const next = argv[index + 1]; if (!next || next.startsWith('--')) args[key] = true; else { args[key] = next; index += 1; } } return args; } function rel(root, file) { return path.relative(root, file).replace(/\\/g, '/'); } function artifactPath(root, dir, ...parts) { const base = path.isAbsolute(dir) ? dir : path.join(root, dir); return path.join(base, ...parts); } function artifactRef(root, file) { const resolvedRoot = path.resolve(root); const resolvedFile = path.resolve(file); const relative = path.relative(resolvedRoot, resolvedFile); if (relative && (relative.startsWith('..') || path.isAbsolute(relative))) return resolvedFile.replace(/\\/g, '/'); return relative.replace(/\\/g, '/'); } function relIfExists(root, file) { return fs.existsSync(file) ? rel(root, file) : null; } function countRowsWithMissingProofRequirements(rows) { if (!Array.isArray(rows)) return 0; return rows.filter(row => Array.isArray(row.missingProofRequirements) && row.missingProofRequirements.length >= 1 ).length; } function uniqueMissingProofRequirementCount(rows) { if (!Array.isArray(rows)) return 0; const values = new Set(); for (const row of rows) { const requirements = Array.isArray(row.missingProofRequirements) ? row.missingProofRequirements : []; for (const requirement of requirements) { if (requirement) values.add(String(requirement)); } } return values.size; } function escapeCell(value) { return String(value ?? '').replace(/\|/g, '/').replace(/\r?\n/g, ' '); } function withBom(text) { return `\uFEFF${text}`; } if (require.main === module) main(); module.exports = { buildHandoffSummary, renderReport };