| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325 |
- #!/usr/bin/env node
- const fs = require('fs');
- const path = require('path');
- const ROOT = path.resolve(__dirname, '..');
- const DEFAULT_OUTPUT = path.join(ROOT, 'outputs', 'latest-form-index-latest');
- function main() {
- const args = parseArgs(process.argv.slice(2));
- const outputsDir = path.resolve(args.outputs || path.join(ROOT, 'outputs'));
- const outputDir = path.resolve(args.output || DEFAULT_OUTPUT);
- const summary = buildLatestFormIndex({ root: ROOT, outputsDir });
- fs.mkdirSync(outputDir, { recursive: true });
- const summaryPath = path.join(outputDir, 'latest-form-index-summary.json');
- const reportPath = path.join(outputDir, 'latest-form-index.md');
- fs.writeFileSync(summaryPath, JSON.stringify(summary, null, 2), 'utf8');
- fs.writeFileSync(reportPath, withBom(renderReport(summary)), 'utf8');
- console.log(JSON.stringify({
- outputDir,
- summary: summaryPath,
- report: reportPath,
- passed: summary.passed,
- primaryForm: summary.primaryForm?.csv || null,
- clientList: summary.clientList?.csv || null
- }, null, 2));
- if (args.strict && !summary.passed) process.exitCode = 1;
- }
- function buildLatestFormIndex({ root, outputsDir }) {
- const proofFormDir = latestOutputDir(outputsDir, /^tihao-proof-gap-software-form-/) ||
- path.join(outputsDir, 'tihao-proof-gap-software-form-latest');
- const clientListDir = latestOutputDir(outputsDir, /^software-client-/) ||
- path.join(outputsDir, 'software-client-latest');
- const nextActionsDir = latestOutputDir(outputsDir, /^business-proof-next-actions-/) ||
- path.join(outputsDir, 'business-proof-next-actions-latest');
- const handoffDir = latestOutputDir(outputsDir, /^optimization-handoff-/) ||
- path.join(outputsDir, 'optimization-handoff-latest');
- const intakeReadinessDir = latestOutputDir(outputsDir, /^intake-readiness-/) ||
- path.join(outputsDir, 'intake-readiness-latest');
- const videoResourceReadinessDir = latestOutputDir(outputsDir, /^video-resource-readiness-/) ||
- path.join(outputsDir, 'video-resource-readiness-latest');
- const homepageEvidenceReadinessDir = latestOutputDir(outputsDir, /^homepage-evidence-readiness-/) ||
- path.join(outputsDir, 'homepage-evidence-readiness-latest');
- const longRunReadinessDir = latestOutputDir(outputsDir, /^long-run-readiness-/) ||
- path.join(outputsDir, 'long-run-readiness-latest');
- const intakeFieldChecklistDir = latestOutputDir(outputsDir, /^intake-field-checklist-/) ||
- path.join(outputsDir, 'intake-field-checklist-latest');
- const manualReviewLabelGuideDir = latestOutputDir(outputsDir, /^manual-review-label-guide-/) ||
- path.join(outputsDir, 'manual-review-label-guide-latest');
- const businessExecutionIndexDir = latestOutputDir(outputsDir, /^business-execution-index-/) ||
- path.join(outputsDir, 'business-execution-index-latest');
- const planExecutionStatusDir = latestOutputDir(outputsDir, /^plan-execution-status-/) ||
- path.join(outputsDir, 'plan-execution-status-latest');
- const proofGapRequestDir = latestOutputDir(outputsDir, /^proof-gap-request-/) ||
- path.join(outputsDir, 'proof-gap-request-latest');
- const proofGapClosureDir = latestOutputDir(outputsDir, /^proof-gap-closure-/) ||
- path.join(outputsDir, 'proof-gap-closure-latest');
- const proofGapOperatorPackDir = latestOutputDir(outputsDir, /^proof-gap-operator-pack-/) ||
- path.join(outputsDir, 'proof-gap-operator-pack-latest');
- const realProofIntakeBundleDir = latestOutputDir(outputsDir, /^real-proof-intake-bundle-/) ||
- path.join(outputsDir, 'real-proof-intake-bundle-latest');
- const realProofClosureWorkOrderDir = latestOutputDir(outputsDir, /^real-proof-closure-work-order-/) ||
- path.join(outputsDir, 'real-proof-closure-work-order-latest');
- const optimizationCompletionDir = latestOutputDir(outputsDir, /^optimization-completion-/) ||
- path.join(outputsDir, 'optimization-completion-latest');
- const localSeedToIntakeWorklistDir = latestOutputDir(outputsDir, /^local-seed-to-intake-worklist-/) ||
- path.join(outputsDir, 'local-seed-to-intake-worklist-latest');
- const realGapMaterialAuditDir = latestOutputDir(outputsDir, /^real-gap-material-audit-/) ||
- path.join(outputsDir, 'real-gap-material-audit-latest');
- const proofSummary = readJsonIfExists(path.join(proofFormDir, 'tihao-proof-gap-software-form-summary.json'));
- const clientSummary = readJsonIfExists(path.join(clientListDir, 'software-client-list.final.summary.json'));
- const nextActionsSummary = readJsonIfExists(path.join(nextActionsDir, 'business-proof-next-actions-summary.json'));
- const handoffSummary = readJsonIfExists(path.join(handoffDir, 'optimization-handoff-summary.json'));
- const intakeReadinessSummary = readJsonIfExists(path.join(intakeReadinessDir, 'intake-readiness-summary.json'));
- const videoResourceReadinessSummary = readJsonIfExists(path.join(videoResourceReadinessDir, 'video-resource-readiness-summary.json'));
- const homepageEvidenceReadinessSummary = readJsonIfExists(path.join(homepageEvidenceReadinessDir, 'homepage-evidence-readiness-summary.json'));
- const longRunReadinessSummary = readJsonIfExists(path.join(longRunReadinessDir, 'long-run-readiness-summary.json'));
- const intakeFieldChecklistSummary = readJsonIfExists(path.join(intakeFieldChecklistDir, 'intake-field-checklist-summary.json'));
- const manualReviewLabelGuideSummary = readJsonIfExists(path.join(manualReviewLabelGuideDir, 'manual-review-label-guide-summary.json'));
- const businessExecutionIndexSummary = readJsonIfExists(path.join(businessExecutionIndexDir, 'business-execution-index-summary.json'));
- const planExecutionStatusSummary = readJsonIfExists(path.join(planExecutionStatusDir, 'plan-execution-status-summary.json'));
- const proofGapRequestSummary = readJsonIfExists(path.join(proofGapRequestDir, 'proof-gap-request-summary.json'));
- const proofGapClosureSummary = readJsonIfExists(path.join(proofGapClosureDir, 'proof-gap-closure-summary.json'));
- const proofGapOperatorPackSummary = readJsonIfExists(path.join(proofGapOperatorPackDir, 'proof-gap-operator-pack-summary.json'));
- const realProofIntakeBundleSummary = readJsonIfExists(path.join(realProofIntakeBundleDir, 'real-proof-intake-bundle-summary.json'));
- const realProofClosureWorkOrderSummary = readJsonIfExists(path.join(realProofClosureWorkOrderDir, 'real-proof-closure-work-order-summary.json'));
- const optimizationCompletionSummary = readJsonIfExists(path.join(optimizationCompletionDir, 'optimization-completion-summary.json'));
- const localSeedToIntakeWorklistSummary = readJsonIfExists(path.join(localSeedToIntakeWorklistDir, 'local-seed-to-intake-worklist-summary.json'));
- const realGapMaterialAuditSummary = readJsonIfExists(path.join(realGapMaterialAuditDir, 'real-gap-material-audit-summary.json'));
- const primaryForm = fileGroup(root, proofFormDir, {
- csv: 'tihao-proof-gap-software-form.csv',
- markdown: 'tihao-proof-gap-software-form.md',
- summary: 'tihao-proof-gap-software-form-summary.json'
- });
- const clientList = fileGroup(root, clientListDir, {
- csv: 'software-client-list.final.csv',
- markdown: 'software-client-list.final.md',
- summary: 'software-client-list.final.summary.json'
- });
- const intakeFieldChecklist = fileGroup(root, intakeFieldChecklistDir, {
- csv: 'intake-field-checklist.csv',
- markdown: 'intake-field-checklist.md',
- summary: 'intake-field-checklist-summary.json'
- });
- const manualReviewLabelGuide = fileGroup(root, manualReviewLabelGuideDir, {
- csv: 'manual-review-label-guide.csv',
- markdown: 'manual-review-label-guide.md',
- summary: 'manual-review-label-guide-summary.json'
- });
- const businessExecutionIndex = fileGroup(root, businessExecutionIndexDir, {
- csv: 'business-execution-index.csv',
- markdown: 'business-execution-index.md',
- summary: 'business-execution-index-summary.json'
- });
- const planExecutionStatus = fileGroup(root, planExecutionStatusDir, {
- csv: 'plan-execution-status.csv',
- markdown: 'plan-execution-status.md',
- summary: 'plan-execution-status-summary.json'
- });
- const proofGapRequest = fileGroup(root, proofGapRequestDir, {
- csv: 'proof-gap-request-table.csv',
- markdown: 'proof-gap-request-report.md',
- summary: 'proof-gap-request-summary.json'
- });
- const proofGapClosure = fileGroup(root, proofGapClosureDir, {
- markdown: 'proof-gap-closure-report.md',
- summary: 'proof-gap-closure-summary.json'
- });
- const proofGapOperatorPack = fileGroup(root, proofGapOperatorPackDir, {
- csv: 'proof-gap-operator-pack.csv',
- markdown: 'proof-gap-operator-pack.md',
- summary: 'proof-gap-operator-pack-summary.json'
- });
- const realProofIntakeBundle = fileGroup(root, realProofIntakeBundleDir, {
- csv: 'real-proof-intake-bundle.csv',
- markdown: 'real-proof-intake-bundle.md',
- summary: 'real-proof-intake-bundle-summary.json'
- });
- const realProofClosureWorkOrder = fileGroup(root, realProofClosureWorkOrderDir, {
- csv: 'real-proof-closure-work-order.csv',
- markdown: 'real-proof-closure-work-order.md',
- summary: 'real-proof-closure-work-order-summary.json'
- });
- const optimizationCompletion = fileGroup(root, optimizationCompletionDir, {
- markdown: 'optimization-completion-report.md',
- summary: 'optimization-completion-summary.json'
- });
- const localSeedToIntakeWorklist = fileGroup(root, localSeedToIntakeWorklistDir, {
- markdown: 'local-seed-to-intake-worklist.md',
- csv: 'local-seed-to-intake-worklist.csv',
- summary: 'local-seed-to-intake-worklist-summary.json',
- historyDraft: 'history-intake-draft.csv',
- videoWorklist: 'video-resource-worklist.csv',
- candidateWorklist: 'candidate-seed-worklist.csv'
- });
- const realGapMaterialAudit = fileGroup(root, realGapMaterialAuditDir, {
- markdown: 'real-gap-material-audit.md',
- csv: 'real-gap-material-audit.csv',
- summary: 'real-gap-material-audit-summary.json'
- });
- const longRunReadiness = fileGroup(root, longRunReadinessDir, {
- report: 'long-run-readiness-report.md',
- summary: 'long-run-readiness-summary.json'
- });
- const ownerArtifacts = normalizeOwnerArtifacts(root, nextActionsDir, nextActionsSummary);
- const proofGapOperatorOwnerArtifacts = normalizeOwnerArtifacts(root, proofGapOperatorPackDir, proofGapOperatorPackSummary);
- const realProofOperatorPackOwnerArtifacts = normalizePassThroughOwnerArtifacts(root, realProofIntakeBundleSummary?.operatorPackOwnerArtifacts || []);
- const businessExecutionOperatorPackOwnerArtifacts = normalizePassThroughOwnerArtifacts(root, businessExecutionIndexSummary?.operatorPackOwnerArtifacts || []);
- const planExecutionOperatorPackOwnerArtifacts = normalizePassThroughOwnerArtifacts(root, planExecutionStatusSummary?.operatorPackOwnerArtifacts || []);
- const intakeOwnerGroups = normalizeIntakeOwnerGroups(root, intakeFieldChecklistSummary?.ownerGroups || []);
- const intakeRecheckCommands = normalizeRecheckCommands(intakeFieldChecklistSummary?.recheckCommands || []);
- const businessFillGuide = buildBusinessFillGuide({
- ownerGroups: intakeOwnerGroups,
- recheckCommands: intakeRecheckCommands
- });
- const proofOpenCount = Number(nextActionsSummary?.sourceState?.proofGapOpenCount ?? handoffSummary?.businessProofNextActions?.actionCount ?? 0);
- const proofGapClosureCounts = normalizeProofGapClosureCounts(proofGapClosureSummary);
- const actionCount = Number(nextActionsSummary?.actionCount ?? (Array.isArray(nextActionsSummary?.actions) ? nextActionsSummary.actions.length : 0));
- const nextActionSourceState = normalizeBusinessProofNextActionSourceState(nextActionsSummary?.sourceState);
- const nextActionTopActions = normalizeBusinessProofNextTopActions(
- Array.isArray(nextActionsSummary?.topActions) && nextActionsSummary.topActions.length
- ? nextActionsSummary.topActions
- : nextActionsSummary?.actions
- );
- const primaryPassed = Boolean(proofSummary?.passed);
- const clientExists = fs.existsSync(path.join(clientListDir, 'software-client-list.final.csv'));
- const passed = primaryPassed && Boolean(primaryForm.csv) && actionCount >= 1;
- const supportDocs = [
- {
- title: '长期优化计划主文件',
- path: relIfExists(root, path.join(root, 'docs', 'tihao-experience-optimization-plan.md')),
- usage: '当前目标文件;用于执行长期优化计划、同步每轮进度、确认验收标准和完成边界。'
- },
- {
- title: '长期优化计划中文执行版',
- path: relIfExists(root, path.join(root, 'docs', 'tihao-experience-optimization-plan.zh-CN.md')),
- usage: '用于下一轮 AI 或项目负责人理解长期目标、策略矩阵、验收标准和完成边界。'
- },
- {
- title: '补证操作顺序中文执行版',
- path: relIfExists(root, path.join(root, 'docs', 'business-proof-action-order.zh-CN.md')),
- usage: '用于商务、投放、技术/AI 按 15 步顺序补齐真实材料和验收证据。'
- }
- ,
- {
- title: '提号任务交接入口',
- path: relIfExists(root, path.join(root, 'docs', 'tihao-handoff-index.md')),
- usage: '下一轮 AI、商务或投放接手时优先打开;按顺序定位主计划、补证顺序、最新表单索引、真实材料总包、proof-gap 请求包、商务执行总表和交接摘要。'
- }
- ];
- return {
- generatedAt: new Date().toISOString(),
- root,
- outputsDir,
- passed,
- purpose: '给商务和下一轮 AI 快速定位最新提号补证表单、候选名单、负责人行动文件和验收边界。',
- primaryForm: {
- title: '最新提号补证表单',
- usage: '用于分派真实历史 Brief、真实视频资源、视频 A/B、live 长跑和客户效果审计补证任务。',
- directCustomerProof: false,
- csv: primaryForm.csv,
- markdown: primaryForm.markdown,
- summary: primaryForm.summary,
- generatedAt: proofSummary?.generatedAt || null,
- passed: primaryPassed,
- rowCount: Number(proofSummary?.rowCount || 0),
- duplicateIdCount: Number(proofSummary?.duplicateIdCount || 0),
- headerMatches: Boolean(proofSummary?.headerMatches),
- rowWidthOk: Boolean(proofSummary?.rowWidthOk)
- },
- clientList: {
- title: '最新软件端候选名单',
- usage: '用于商务复核候选博主;当前仍需结合风险提示和人工复核标签,不等同于客户效果证明。',
- directCustomerProof: false,
- exists: clientExists,
- csv: clientList.csv,
- markdown: clientList.markdown,
- summary: clientList.summary,
- generatedAt: clientSummary?.generatedAt || null,
- rowCount: Number(clientSummary?.rowCount || clientSummary?.finalRowCount || clientSummary?.outputRows || 0),
- duplicateGroups: clientSummary?.duplicateGroups || clientSummary?.duplicateSummary || null,
- duplicateCount: Number(clientSummary?.duplicateCount || 0),
- duplicateCountMeaning: clientSummary?.duplicateCountMeaning || 'source_duplicate_removed_count',
- sourceDuplicateRemovedCount: Number(clientSummary?.sourceDuplicateRemovedCount ?? clientSummary?.duplicateCount ?? 0),
- finalDuplicateGroupCount: Number(clientSummary?.finalDuplicateGroupCount ?? finalDuplicateGroupCount(clientSummary?.duplicateAudit) ?? 0),
- rankContinuous: Boolean(clientSummary?.rankContinuous),
- dedupeRule: clientSummary?.dedupeRule || ''
- },
- intakeFieldChecklist: {
- title: '真实材料字段级补齐核对表',
- usage: '用于商务、投放按行号和字段补齐真实历史 Brief、人工复核字段和真实视频资源;不等同于客户效果证明。',
- directCustomerProof: false,
- exists: Boolean(intakeFieldChecklist.markdown || intakeFieldChecklist.csv),
- csv: intakeFieldChecklist.csv,
- markdown: intakeFieldChecklist.markdown,
- summary: intakeFieldChecklist.summary,
- generatedAt: intakeFieldChecklistSummary?.generatedAt || null,
- itemCount: Number(intakeFieldChecklistSummary?.itemCount || 0),
- missingRequiredCount: Number(intakeFieldChecklistSummary?.missingRequiredCount || 0),
- placeholderCount: Number(intakeFieldChecklistSummary?.placeholderCount || 0),
- ownerGroups: intakeOwnerGroups,
- recheckCommands: intakeRecheckCommands,
- passed: Boolean(intakeFieldChecklistSummary?.passed)
- },
- manualReviewLabelGuide: {
- title: '人工复核标签与归因指南',
- usage: '用于商务填写人工复核标签、负样本归因、反馈原因和客户选择;它只规范复核口径,不等同于客户效果证明。',
- directCustomerProof: false,
- proofLevel: manualReviewLabelGuideSummary?.proofLevel || 'smoke_or_local',
- exists: Boolean(manualReviewLabelGuide.markdown || manualReviewLabelGuide.csv),
- csv: manualReviewLabelGuide.csv,
- markdown: manualReviewLabelGuide.markdown,
- summary: manualReviewLabelGuide.summary,
- generatedAt: manualReviewLabelGuideSummary?.generatedAt || null,
- passed: Boolean(manualReviewLabelGuideSummary?.passed),
- labelCount: Number(manualReviewLabelGuideSummary?.labelCount || 0),
- positiveLabelCount: Number(manualReviewLabelGuideSummary?.positiveLabelCount || 0),
- negativeLabelCount: Number(manualReviewLabelGuideSummary?.negativeLabelCount || 0),
- attributionTypeCount: Number(manualReviewLabelGuideSummary?.attributionTypeCount || 0),
- negativeLabelsRequireAttribution: Boolean(manualReviewLabelGuideSummary?.negativeLabelsRequireAttribution)
- },
- businessFillGuide,
- businessExecutionIndex: {
- title: '商务补证执行总表',
- usage: '用于商务、投放和技术/AI 在一张表里查看负责人行动、修复清单、操作包负责人附件、字段补齐负责人、补齐后复验命令和关键文件;不等同于客户效果证明。',
- directCustomerProof: false,
- exists: Boolean(businessExecutionIndex.markdown || businessExecutionIndex.csv),
- csv: businessExecutionIndex.csv,
- markdown: businessExecutionIndex.markdown,
- summary: businessExecutionIndex.summary,
- generatedAt: businessExecutionIndexSummary?.generatedAt || null,
- passed: Boolean(businessExecutionIndexSummary?.passed),
- rowCount: Number(businessExecutionIndexSummary?.rowCount || 0),
- actionRowCount: Number(businessExecutionIndexSummary?.actionRowCount || 0),
- repairRowCount: Number(businessExecutionIndexSummary?.repairRowCount || 0),
- operatorPackOwnerArtifactRowCount: Number(businessExecutionIndexSummary?.operatorPackOwnerArtifactRowCount || 0),
- operatorPackOwnerArtifactCount: Number(businessExecutionIndexSummary?.operatorPackOwnerArtifactCount || businessExecutionOperatorPackOwnerArtifacts.length || 0),
- operatorPackOwnerArtifacts: businessExecutionOperatorPackOwnerArtifacts,
- fieldOwnerRowCount: Number(businessExecutionIndexSummary?.fieldOwnerRowCount || 0),
- recheckCommandRowCount: Number(businessExecutionIndexSummary?.recheckCommandRowCount || 0),
- proofOpenCount: Number(businessExecutionIndexSummary?.proofOpenCount || 0)
- },
- planExecutionStatus: {
- title: '长期优化计划执行状态面板',
- usage: '用于商务和下一轮 AI 在一张表里查看长期优化计划 8 个章节的执行状态、当前证据、下一步、验收标准和证明边界;不等同于客户效果证明。',
- directCustomerProof: false,
- exists: Boolean(planExecutionStatus.markdown || planExecutionStatus.csv),
- csv: planExecutionStatus.csv,
- markdown: planExecutionStatus.markdown,
- summary: planExecutionStatus.summary,
- generatedAt: planExecutionStatusSummary?.generatedAt || null,
- passed: Boolean(planExecutionStatusSummary?.passed),
- complete: Boolean(planExecutionStatusSummary?.complete),
- rowCount: Number(planExecutionStatusSummary?.rowCount || 0),
- proofOpenCount: Number(planExecutionStatusSummary?.proofOpenCount || 0),
- operatorPackOwnerArtifactCount: Number(planExecutionStatusSummary?.operatorPackOwnerArtifactCount || planExecutionOperatorPackOwnerArtifacts.length || 0),
- operatorPackOwnerArtifacts: planExecutionOperatorPackOwnerArtifacts,
- businessExecutionOperatorPackOwnerArtifactCount: Number(planExecutionStatusSummary?.businessExecutionOperatorPackOwnerArtifactCount || 0),
- businessExecutionOperatorPackOwnerArtifactRowCount: Number(planExecutionStatusSummary?.businessExecutionOperatorPackOwnerArtifactRowCount || 0),
- roundPassed: Boolean(planExecutionStatusSummary?.roundPassed),
- guardrailCount: Array.isArray(planExecutionStatusSummary?.guardrails) ? planExecutionStatusSummary.guardrails.length : 0
- },
- proofGapRequest: {
- title: '真实证明缺口请求包',
- usage: '用于把仍未关闭的真实证明缺口整理成对商务、投放和技术/AI 的请求清单;只请求补证,不证明客户效果。',
- directCustomerProof: false,
- proofLevel: 'smoke_or_local',
- exists: Boolean(proofGapRequest.markdown || proofGapRequest.csv),
- csv: proofGapRequest.csv,
- markdown: proofGapRequest.markdown,
- summary: proofGapRequest.summary,
- generatedAt: proofGapRequestSummary?.generatedAt || null,
- complete: Boolean(proofGapRequestSummary?.complete),
- unresolvedCount: Number(proofGapRequestSummary?.unresolvedCount || 0),
- rowCount: Array.isArray(proofGapRequestSummary?.rows) ? proofGapRequestSummary.rows.length : 0,
- proofGapClosureOpenCount: Number(proofGapRequestSummary?.observed?.proofGapClosureOpenCount || 0)
- },
- proofGapClosure: {
- title: '提号真实证明缺口关闭审计',
- usage: '用于判断真实业务证明缺口是否关闭;openCount/businessOpenCount 只统计业务缺口,intake 预审行用 precheckOpenCount 单独记录。',
- directCustomerProof: false,
- proofLevel: proofGapClosureSummary?.complete ? 'real_evidence' : 'not_business_proof',
- exists: Boolean(proofGapClosure.markdown || proofGapClosure.summary),
- markdown: proofGapClosure.markdown,
- summary: proofGapClosure.summary,
- generatedAt: proofGapClosureSummary?.generatedAt || null,
- complete: Boolean(proofGapClosureSummary?.complete),
- intakeReady: Boolean(proofGapClosureSummary?.intakeReady),
- 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
- },
- proofGapOperatorPack: {
- title: '提号补证操作包',
- usage: '用于把 5 个真实证明缺口转成负责人、填写文件、待补字段、复验命令和通过标准;只安排补证,不证明客户效果。',
- directCustomerProof: false,
- proofLevel: proofGapOperatorPackSummary?.proofLevel || 'not_business_proof',
- exists: Boolean(proofGapOperatorPack.markdown || proofGapOperatorPack.csv),
- csv: proofGapOperatorPack.csv,
- markdown: proofGapOperatorPack.markdown,
- summary: proofGapOperatorPack.summary,
- generatedAt: proofGapOperatorPackSummary?.generatedAt || null,
- passed: Boolean(proofGapOperatorPackSummary?.passed),
- complete: Boolean(proofGapOperatorPackSummary?.complete),
- rowCount: Number(proofGapOperatorPackSummary?.rowCount || 0),
- openCount: Number(proofGapOperatorPackSummary?.openCount || 0),
- gapIds: Array.isArray(proofGapOperatorPackSummary?.gapIds)
- ? proofGapOperatorPackSummary.gapIds
- : (Array.isArray(proofGapOperatorPackSummary?.rows) ? proofGapOperatorPackSummary.rows.map(row => row.gapId).filter(Boolean) : []),
- fillFileCount: Number(proofGapOperatorPackSummary?.fillFileCount || 0),
- recheckCommandCount: Number(proofGapOperatorPackSummary?.recheckCommandCount || 0),
- ownerGroupCount: Number(proofGapOperatorPackSummary?.ownerGroupCount || (Array.isArray(proofGapOperatorPackSummary?.ownerGroups) ? proofGapOperatorPackSummary.ownerGroups.length : 0)),
- ownerArtifactCount: Number(proofGapOperatorPackSummary?.ownerArtifactCount || proofGapOperatorOwnerArtifacts.length || 0),
- ownerArtifacts: proofGapOperatorOwnerArtifacts,
- evidenceRowCount: Number(proofGapOperatorPackSummary?.evidenceRowCount || 0),
- nextActionCount: Number(proofGapOperatorPackSummary?.nextActionCount || 0),
- missingProofRequirementCount: Number(proofGapOperatorPackSummary?.missingProofRequirementCount || 0)
- },
- realProofIntakeBundle: {
- title: '真实验收材料收集总包',
- usage: '用于把真实材料模板、字段缺口、修复清单、证明缺口和复验命令集中成一个商务可打开的入口;不等同于客户效果证明。',
- directCustomerProof: false,
- proofLevel: realProofIntakeBundleSummary?.proofLevel || 'not_business_proof',
- exists: Boolean(realProofIntakeBundle.markdown || realProofIntakeBundle.csv),
- csv: realProofIntakeBundle.csv,
- markdown: realProofIntakeBundle.markdown,
- summary: realProofIntakeBundle.summary,
- generatedAt: realProofIntakeBundleSummary?.generatedAt || null,
- passed: Boolean(realProofIntakeBundleSummary?.passed),
- complete: Boolean(realProofIntakeBundleSummary?.complete),
- proofOpenCount: Number(realProofIntakeBundleSummary?.proofOpenCount || 0),
- fillRowCount: Number(realProofIntakeBundleSummary?.fillRowCount || 0),
- proofGapRowCount: Number(realProofIntakeBundleSummary?.proofGapRowCount || 0),
- recheckCommandCount: Number(realProofIntakeBundleSummary?.recheckCommandCount || 0),
- exportRowCount: Number(realProofIntakeBundleSummary?.exportRowCount || 0),
- operatorPackEvidenceRowCount: Number(realProofIntakeBundleSummary?.operatorPackEvidenceRowCount || realProofIntakeBundleSummary?.sourceState?.operatorPackEvidenceRowCount || 0),
- operatorPackNextActionCount: Number(realProofIntakeBundleSummary?.operatorPackNextActionCount || realProofIntakeBundleSummary?.sourceState?.operatorPackNextActionCount || 0),
- operatorPackMissingProofRequirementCount: Number(realProofIntakeBundleSummary?.operatorPackMissingProofRequirementCount || realProofIntakeBundleSummary?.sourceState?.operatorPackMissingProofRequirementCount || 0),
- operatorPackOwnerArtifactCount: Number(realProofIntakeBundleSummary?.operatorPackOwnerArtifactCount || realProofIntakeBundleSummary?.sourceState?.operatorPackOwnerArtifactCount || realProofOperatorPackOwnerArtifacts.length || 0),
- operatorPackOwnerArtifacts: realProofOperatorPackOwnerArtifacts
- },
- realProofClosureWorkOrder: {
- title: '真实证明闭环工单',
- usage: '用于把 intake/video/proof-gap/completion 的未关闭项拆成负责人、目标模板、字段、行范围、种子线索和复验命令;不写入正式模板,不等同于客户效果证明。',
- directCustomerProof: false,
- proofLevel: realProofClosureWorkOrderSummary?.proofLevel || 'not_business_proof',
- materialType: realProofClosureWorkOrderSummary?.materialType || 'real_proof_closure_work_order',
- exists: Boolean(realProofClosureWorkOrder.markdown || realProofClosureWorkOrder.csv),
- csv: realProofClosureWorkOrder.csv,
- markdown: realProofClosureWorkOrder.markdown,
- summary: realProofClosureWorkOrder.summary,
- generatedAt: realProofClosureWorkOrderSummary?.generatedAt || null,
- passed: Boolean(realProofClosureWorkOrderSummary?.passed),
- complete: Boolean(realProofClosureWorkOrderSummary?.complete),
- canCloseProofGap: Boolean(realProofClosureWorkOrderSummary?.canCloseProofGap),
- workOrderCount: Number(realProofClosureWorkOrderSummary?.workOrderCount || 0),
- ownerGroupCount: Number(realProofClosureWorkOrderSummary?.ownerGroupCount || 0),
- seedSuggestionCount: Number(realProofClosureWorkOrderSummary?.seedSuggestionCount || 0),
- proofGapOpenCount: Number(realProofClosureWorkOrderSummary?.sourceState?.proofGapOpenCount || 0),
- intakeFailureCount: Number(realProofClosureWorkOrderSummary?.sourceState?.intakeFailureCount || 0),
- videoFailureCount: Number(realProofClosureWorkOrderSummary?.sourceState?.videoFailureCount || 0),
- realCandidateRows: Number(realProofClosureWorkOrderSummary?.sourceState?.realCandidateRows || 0),
- ownerArtifacts: normalizeWorkOrderOwnerArtifacts(root, realProofClosureWorkOrderSummary?.ownerArtifacts || [])
- },
- localSeedToIntakeWorklist: {
- title: '本地种子转 intake 补表 worklist',
- usage: '用于把本地 DHA Brief、PoC 候选和参考账号解析转成可转写的历史数据草稿、视频资源补表清单和候选复核清单;不写入正式 intake 模板,不等同于客户效果证明。',
- directCustomerProof: false,
- proofLevel: localSeedToIntakeWorklistSummary?.proofLevel || 'not_business_proof',
- materialType: localSeedToIntakeWorklistSummary?.materialType || '',
- exists: Boolean(localSeedToIntakeWorklist.markdown || localSeedToIntakeWorklist.csv),
- markdown: localSeedToIntakeWorklist.markdown,
- csv: localSeedToIntakeWorklist.csv,
- summary: localSeedToIntakeWorklist.summary,
- historyDraft: localSeedToIntakeWorklist.historyDraft,
- videoWorklist: localSeedToIntakeWorklist.videoWorklist,
- candidateWorklist: localSeedToIntakeWorklist.candidateWorklist,
- generatedAt: localSeedToIntakeWorklistSummary?.generatedAt || null,
- complete: Boolean(localSeedToIntakeWorklistSummary?.complete),
- canCloseProofGap: Boolean(localSeedToIntakeWorklistSummary?.canCloseProofGap),
- historyDraftRows: Number(localSeedToIntakeWorklistSummary?.counts?.historyDraftRows || 0),
- referenceSeedRows: Number(localSeedToIntakeWorklistSummary?.counts?.referenceSeedRows || 0),
- candidateSeedRows: Number(localSeedToIntakeWorklistSummary?.counts?.candidateSeedRows || 0),
- videoWorklistRows: Number(localSeedToIntakeWorklistSummary?.counts?.videoWorklistRows || 0),
- videoCandidateNeedsResourceRows: Number(localSeedToIntakeWorklistSummary?.counts?.videoCandidateNeedsResourceRows || 0),
- missingProofRequirementCount: Array.isArray(localSeedToIntakeWorklistSummary?.missingProofRequirements) ? localSeedToIntakeWorklistSummary.missingProofRequirements.length : 0,
- targetFiles: Array.isArray(localSeedToIntakeWorklistSummary?.targetFiles) ? localSeedToIntakeWorklistSummary.targetFiles : [],
- nextActionCount: Array.isArray(localSeedToIntakeWorklistSummary?.nextActions) ? localSeedToIntakeWorklistSummary.nextActions.length : 0
- },
- realGapMaterialAudit: {
- title: 'three real gap material audit',
- usage: 'Shows that project materials exist for the three still-open proof gaps; it is not customer-effect, video A/B, or manual-supplement proof.',
- directCustomerProof: false,
- proofLevel: realGapMaterialAuditSummary?.proofLevel || 'not_business_proof',
- materialType: realGapMaterialAuditSummary?.materialType || 'three_real_gap_material_audit',
- exists: Boolean(realGapMaterialAudit.markdown || realGapMaterialAudit.csv),
- markdown: realGapMaterialAudit.markdown,
- csv: realGapMaterialAudit.csv,
- summary: realGapMaterialAudit.summary,
- generatedAt: realGapMaterialAuditSummary?.generatedAt || null,
- complete: Boolean(realGapMaterialAuditSummary?.complete),
- canCloseProofGap: Boolean(realGapMaterialAuditSummary?.canCloseProofGap),
- gapCount: Number(realGapMaterialAuditSummary?.gapCount || 0),
- materialFoundCount: Number(realGapMaterialAuditSummary?.materialFoundCount || 0),
- openGapCount: Number(realGapMaterialAuditSummary?.openGapCount || 0),
- sourceState: realGapMaterialAuditSummary?.sourceState || {},
- gapIds: Array.isArray(realGapMaterialAuditSummary?.gaps) ? realGapMaterialAuditSummary.gaps.map(item => item.id).filter(Boolean) : []
- },
- optimizationCompletion: {
- title: '优化完成度审计',
- usage: '用于汇总长期优化是否具备完成声明条件;readyForClaim=false 时不得宣称提号率提升、客户效果达标或人工补号量下降。',
- directCustomerProof: false,
- proofLevel: optimizationCompletionSummary?.readyForClaim ? 'real_evidence' : 'not_business_proof',
- exists: Boolean(optimizationCompletion.markdown || optimizationCompletion.summary),
- markdown: optimizationCompletion.markdown,
- summary: optimizationCompletion.summary,
- generatedAt: optimizationCompletionSummary?.generatedAt || null,
- complete: Boolean(optimizationCompletionSummary?.complete),
- readyForClaim: Boolean(optimizationCompletionSummary?.readyForClaim),
- proofGapOpenCount: Number(optimizationCompletionSummary?.proofGapOpenCount || 0),
- blockingReasonCount: Number(optimizationCompletionSummary?.blockingReasonCount ?? (Array.isArray(optimizationCompletionSummary?.blockingReasons) ? optimizationCompletionSummary.blockingReasons.length : 0)),
- blockingReasonsWithMissingProofRequirements: Number(optimizationCompletionSummary?.blockingReasonsWithMissingProofRequirements ?? countRowsWithMissingProofRequirements(optimizationCompletionSummary?.blockingReasons)),
- missingProofRequirementCount: Number(optimizationCompletionSummary?.missingProofRequirementCount ?? uniqueMissingProofRequirementCount(optimizationCompletionSummary?.blockingReasons)),
- videoAbPreflightReadyForVideoAb: Boolean(optimizationCompletionSummary?.videoAbPreflightReadyForVideoAb),
- videoAbPreflightFailureCount: Number.isFinite(Number(optimizationCompletionSummary?.videoAbPreflightFailureCount)) ? Number(optimizationCompletionSummary.videoAbPreflightFailureCount) : null,
- canClaim: {
- tihaoRateImproved: Boolean(optimizationCompletionSummary?.canClaim?.tihaoRateImproved),
- customerEffectAchieved: Boolean(optimizationCompletionSummary?.canClaim?.customerEffectAchieved),
- manualSupplementReduced: Boolean(optimizationCompletionSummary?.canClaim?.manualSupplementReduced)
- }
- },
- longRunReadiness: {
- title: '长跑前置门禁',
- usage: '用于判断 full-matrix/live 长跑是否可启动;ready=false 时只能继续补真实材料,不证明客户效果。',
- directCustomerProof: false,
- proofLevel: longRunReadinessSummary?.ready ? 'smoke_or_local' : 'not_business_proof',
- exists: Boolean(longRunReadiness.report || longRunReadiness.summary),
- report: longRunReadiness.report,
- summary: longRunReadiness.summary,
- generatedAt: longRunReadinessSummary?.generatedAt || null,
- mode: longRunReadinessSummary?.mode || null,
- ready: Boolean(longRunReadinessSummary?.ready),
- passCount: Number(longRunReadinessSummary?.counts?.pass || 0),
- failCount: Number(longRunReadinessSummary?.counts?.fail || 0),
- warnCount: Number(longRunReadinessSummary?.counts?.warn || 0),
- nextCommandCount: Array.isArray(longRunReadinessSummary?.nextCommands) ? longRunReadinessSummary.nextCommands.length : 0,
- boundaryCount: Array.isArray(longRunReadinessSummary?.boundaries) ? longRunReadinessSummary.boundaries.length : 0
- },
- nextActions: {
- complete: Boolean(nextActionsSummary?.complete),
- actionCount,
- proofOpenCount,
- ownerGroupCount: Array.isArray(nextActionsSummary?.ownerGroups) ? nextActionsSummary.ownerGroups.length : 0,
- ownerArtifactCount: ownerArtifacts.length,
- sourceState: nextActionSourceState,
- topActions: nextActionTopActions,
- ownerGroups: nextActionsSummary?.ownerGroups || [],
- ownerArtifacts
- },
- supportDocs,
- repairActions: {
- dataIntake: {
- title: '历史数据/商务复核修复清单',
- csv: relIfExists(root, path.join(intakeReadinessDir, 'intake-readiness-repair-actions.csv')),
- report: relIfExists(root, path.join(intakeReadinessDir, 'intake-readiness-report.md')),
- summary: relIfExists(root, path.join(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(videoResourceReadinessDir, 'video-resource-repair-actions.csv')),
- report: relIfExists(root, path.join(videoResourceReadinessDir, 'video-resource-readiness-report.md')),
- summary: relIfExists(root, path.join(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(homepageEvidenceReadinessDir, 'homepage-evidence-repair-actions.csv')),
- report: relIfExists(root, path.join(homepageEvidenceReadinessDir, 'homepage-evidence-readiness-report.md')),
- summary: relIfExists(root, path.join(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)
- }
- },
- handoff: {
- complete: Boolean(handoffSummary?.complete),
- summary: relIfExists(root, path.join(handoffDir, 'optimization-handoff-summary.json')),
- report: relIfExists(root, path.join(handoffDir, 'optimization-handoff-report.md'))
- },
- guardrails: [
- '最新提号补证表单只安排补证任务,不证明客户效果完成。',
- '最新软件端候选名单需要商务复核和客户反馈后才能进入客户效果审计。',
- '不能把 sample、smoke、模板、provider fallback 或接口 200 当成业务证明。',
- '本索引不得包含 sessionToken、鉴权头、模型 token 或 npm token。'
- ],
- recommendedNextCommand: 'npm run round:refresh -- --output-root outputs --strict'
- };
- }
- function renderReport(summary) {
- const lines = [
- '# 最新提号表单索引',
- '',
- `- 生成时间:${summary.generatedAt}`,
- `- 是否通过:${summary.passed ? '是' : '否'}`,
- `- 用途:${summary.purpose}`,
- '',
- '## 当前可用文件',
- '',
- '| 类型 | 路径 | 状态 | 用法 |',
- '| --- | --- | --- | --- |',
- tableRow([
- summary.primaryForm.title,
- summary.primaryForm.csv || '缺失',
- summary.primaryForm.passed ? `通过,${summary.primaryForm.rowCount} 行,无重复 ID` : '未通过或缺失',
- summary.primaryForm.usage
- ]),
- tableRow([
- summary.clientList.title,
- summary.clientList.csv || '缺失',
- summary.clientList.exists
- ? `存在,${summary.clientList.rowCount || '行数待摘要确认'} 行;最终重复组 ${summary.clientList.finalDuplicateGroupCount ?? 'unknown'};排名连续 ${summary.clientList.rankContinuous ? '是' : '待确认'};源表去重丢弃 ${summary.clientList.sourceDuplicateRemovedCount ?? 0} 行`
- : '缺失',
- summary.clientList.usage
- ]),
- tableRow([
- summary.businessExecutionIndex.title,
- summary.businessExecutionIndex.markdown || summary.businessExecutionIndex.csv || '缺失',
- summary.businessExecutionIndex.exists
- ? `通过=${summary.businessExecutionIndex.passed},${summary.businessExecutionIndex.rowCount} 行,操作包负责人附件 ${summary.businessExecutionIndex.operatorPackOwnerArtifactCount},字段负责人 ${summary.businessExecutionIndex.fieldOwnerRowCount},复验命令 ${summary.businessExecutionIndex.recheckCommandRowCount}`
- : '缺失',
- summary.businessExecutionIndex.usage
- ]),
- tableRow([
- summary.planExecutionStatus.title,
- summary.planExecutionStatus.markdown || summary.planExecutionStatus.csv || '缺失',
- summary.planExecutionStatus.exists
- ? `complete=${summary.planExecutionStatus.complete},rowCount=${summary.planExecutionStatus.rowCount},proofOpenCount=${summary.planExecutionStatus.proofOpenCount},operatorPackOwnerArtifactCount=${summary.planExecutionStatus.operatorPackOwnerArtifactCount}`
- : '缺失',
- summary.planExecutionStatus.usage
- ]),
- tableRow([
- summary.proofGapRequest.title,
- summary.proofGapRequest.markdown || summary.proofGapRequest.csv || '缺失',
- summary.proofGapRequest.exists
- ? `complete=${summary.proofGapRequest.complete},unresolved=${summary.proofGapRequest.unresolvedCount},rows=${summary.proofGapRequest.rowCount}`
- : '缺失',
- summary.proofGapRequest.usage
- ]),
- tableRow([
- summary.proofGapClosure.title,
- summary.proofGapClosure.markdown || summary.proofGapClosure.summary || '缺失',
- summary.proofGapClosure.exists
- ? `complete=${summary.proofGapClosure.complete},openCount=${summary.proofGapClosure.openCount},businessOpenCount=${summary.proofGapClosure.businessOpenCount},precheckOpenCount=${summary.proofGapClosure.precheckOpenCount},totalRows=${summary.proofGapClosure.totalRows}`
- : '缺失',
- summary.proofGapClosure.usage
- ]),
- tableRow([
- summary.proofGapOperatorPack.title,
- summary.proofGapOperatorPack.markdown || summary.proofGapOperatorPack.csv || '缺失',
- summary.proofGapOperatorPack.exists
- ? `passed=${summary.proofGapOperatorPack.passed},openCount=${summary.proofGapOperatorPack.openCount},rowCount=${summary.proofGapOperatorPack.rowCount},ownerGroupCount=${summary.proofGapOperatorPack.ownerGroupCount},ownerArtifactCount=${summary.proofGapOperatorPack.ownerArtifactCount},recheckCommandCount=${summary.proofGapOperatorPack.recheckCommandCount},evidenceRowCount=${summary.proofGapOperatorPack.evidenceRowCount},nextActionCount=${summary.proofGapOperatorPack.nextActionCount},gapIds=${summary.proofGapOperatorPack.gapIds.join(';')}`
- : '缺失',
- summary.proofGapOperatorPack.usage
- ]),
- tableRow([
- summary.realProofIntakeBundle.title,
- summary.realProofIntakeBundle.markdown || summary.realProofIntakeBundle.csv || '缺失',
- summary.realProofIntakeBundle.exists
- ? `passed=${summary.realProofIntakeBundle.passed},proofOpenCount=${summary.realProofIntakeBundle.proofOpenCount},填表 ${summary.realProofIntakeBundle.fillRowCount},复验命令 ${summary.realProofIntakeBundle.recheckCommandCount},operatorPackEvidenceRowCount=${summary.realProofIntakeBundle.operatorPackEvidenceRowCount},operatorPackNextActionCount=${summary.realProofIntakeBundle.operatorPackNextActionCount},operatorPackOwnerArtifactCount=${summary.realProofIntakeBundle.operatorPackOwnerArtifactCount}`
- : '缺失',
- summary.realProofIntakeBundle.usage
- ]),
- tableRow([
- summary.realProofClosureWorkOrder.title,
- summary.realProofClosureWorkOrder.markdown || summary.realProofClosureWorkOrder.csv || '缺失',
- summary.realProofClosureWorkOrder.exists
- ? `passed=${summary.realProofClosureWorkOrder.passed},complete=${summary.realProofClosureWorkOrder.complete},workOrderCount=${summary.realProofClosureWorkOrder.workOrderCount},ownerGroupCount=${summary.realProofClosureWorkOrder.ownerGroupCount},proofGapOpenCount=${summary.realProofClosureWorkOrder.proofGapOpenCount},intakeFailureCount=${summary.realProofClosureWorkOrder.intakeFailureCount},realCandidateRows=${summary.realProofClosureWorkOrder.realCandidateRows},canCloseProofGap=${summary.realProofClosureWorkOrder.canCloseProofGap}`
- : '缺失',
- summary.realProofClosureWorkOrder.usage
- ]),
- tableRow([
- summary.localSeedToIntakeWorklist.title,
- summary.localSeedToIntakeWorklist.markdown || summary.localSeedToIntakeWorklist.csv || '缺失',
- summary.localSeedToIntakeWorklist.exists
- ? `proofLevel=${summary.localSeedToIntakeWorklist.proofLevel},historyDraftRows=${summary.localSeedToIntakeWorklist.historyDraftRows},candidateSeedRows=${summary.localSeedToIntakeWorklist.candidateSeedRows},videoWorklistRows=${summary.localSeedToIntakeWorklist.videoWorklistRows},videoCandidateNeedsResourceRows=${summary.localSeedToIntakeWorklist.videoCandidateNeedsResourceRows}`
- : '缺失',
- summary.localSeedToIntakeWorklist.usage
- ]),
- tableRow([
- summary.realGapMaterialAudit.title,
- summary.realGapMaterialAudit.markdown || summary.realGapMaterialAudit.csv || 'missing',
- summary.realGapMaterialAudit.exists
- ? `proofLevel=${summary.realGapMaterialAudit.proofLevel}, materialFound=${summary.realGapMaterialAudit.materialFoundCount}/${summary.realGapMaterialAudit.gapCount}, openGapCount=${summary.realGapMaterialAudit.openGapCount}, canCloseProofGap=${summary.realGapMaterialAudit.canCloseProofGap}`
- : 'missing',
- summary.realGapMaterialAudit.usage
- ]),
- tableRow([
- summary.optimizationCompletion.title,
- summary.optimizationCompletion.markdown || summary.optimizationCompletion.summary || '缺失',
- summary.optimizationCompletion.exists
- ? `complete=${summary.optimizationCompletion.complete},readyForClaim=${summary.optimizationCompletion.readyForClaim},proofGapOpenCount=${summary.optimizationCompletion.proofGapOpenCount},blockingReasons=${summary.optimizationCompletion.blockingReasonCount},missingProofRequirementCount=${summary.optimizationCompletion.missingProofRequirementCount},videoAbPreflightReadyForVideoAb=${summary.optimizationCompletion.videoAbPreflightReadyForVideoAb},videoAbPreflightFailureCount=${summary.optimizationCompletion.videoAbPreflightFailureCount ?? 'unknown'}`
- : '缺失',
- summary.optimizationCompletion.usage
- ]),
- tableRow([
- summary.longRunReadiness.title,
- summary.longRunReadiness.report || summary.longRunReadiness.summary || '缺失',
- summary.longRunReadiness.exists
- ? `mode=${summary.longRunReadiness.mode || 'unknown'},ready=${summary.longRunReadiness.ready},pass=${summary.longRunReadiness.passCount},fail=${summary.longRunReadiness.failCount},warn=${summary.longRunReadiness.warnCount}`
- : '缺失',
- summary.longRunReadiness.usage
- ]),
- '',
- '## 下一步行动队列摘要',
- '',
- `- complete:${summary.nextActions.complete ? 'true' : 'false'}`,
- `- actionCount:${summary.nextActions.actionCount}`,
- `- proofOpenCount:${summary.nextActions.proofOpenCount}`,
- `- ownerGroupCount:${summary.nextActions.ownerGroupCount || 0}`,
- `- ownerArtifactCount:${summary.nextActions.ownerArtifactCount || 0}`,
- `- intakeFailureCount:${summary.nextActions.sourceState?.intakeFailureCount || 0}`,
- `- videoFailureCount:${summary.nextActions.sourceState?.videoFailureCount || 0}`,
- `- pipelineReadyForClaim:${summary.nextActions.sourceState?.pipelineReadyForClaim ? 'true' : 'false'}`,
- `- pipelineStale:${summary.nextActions.sourceState?.pipelineStale ? 'true' : 'false'}`,
- `- pipelineSuppressedNextActionCount:${summary.nextActions.sourceState?.pipelineSuppressedNextActionCount || 0}`,
- summary.nextActions.sourceState?.pipelineStaleReason ? `- pipelineStaleReason:${summary.nextActions.sourceState.pipelineStaleReason}` : '',
- '- 边界:该队列只用于补证派工和复验;proofOpenCount 不为 0 时不得宣称提号率、客户效果或人工补号量改善。',
- '',
- '| ID | 优先级 | 负责人 | 动作 | 状态 | 命令 | 通过标准 | 缺证明项 |',
- '| --- | ---: | --- | --- | --- | --- | --- | ---: |',
- ...summary.nextActions.topActions.map(item => tableRow([
- item.id || '缺失',
- item.priority || item.order || 0,
- item.owner || '缺失',
- item.title || '缺失',
- item.status || '缺失',
- item.command || '缺失',
- item.acceptance || '缺失',
- item.missingProofRequirementCount || 0
- ])),
- '',
- '## 负责人行动文件',
- '',
- '| 负责人 | 动作数 | Markdown | CSV | 修复附件 |',
- '| --- | ---: | --- | --- | --- |',
- ...summary.nextActions.ownerArtifacts.map(item => tableRow([
- item.owner,
- item.actionCount,
- item.markdown || '缺失',
- item.csv || '缺失',
- renderRepairArtifactSummary(item.repairArtifacts || [])
- ])),
- '',
- '## proof-gap 操作包负责人文件',
- '',
- '| 负责人 | 动作数 | Markdown | CSV | 缺口 | 当前证据行 | 下一步动作 |',
- '| --- | ---: | --- | --- | --- | ---: | ---: |',
- ...summary.proofGapOperatorPack.ownerArtifacts.map(item => tableRow([
- item.owner,
- item.actionCount,
- item.markdown || '缺失',
- item.csv || '缺失',
- (item.gapIds || []).join('; ') || '缺失',
- item.evidenceRowCount,
- item.nextActionCount
- ])),
- '',
- '## 商务执行总表操作包负责人附件',
- '',
- '| 负责人 | 动作数 | Markdown | CSV | 缺口 | 当前证据行 | 下一步动作 |',
- '| --- | ---: | --- | --- | --- | ---: | ---: |',
- ...summary.businessExecutionIndex.operatorPackOwnerArtifacts.map(item => tableRow([
- item.owner,
- item.actionCount,
- item.markdown || '缺失',
- item.csv || '缺失',
- (item.gapIds || []).join('; ') || '缺失',
- item.evidenceRowCount,
- item.nextActionCount
- ])),
- '',
- '## 真实验收总包操作包负责人附件',
- '',
- '| 负责人 | 动作数 | Markdown | CSV | 缺口 | 当前证据行 | 下一步动作 |',
- '| --- | ---: | --- | --- | --- | ---: | ---: |',
- ...summary.realProofIntakeBundle.operatorPackOwnerArtifacts.map(item => tableRow([
- item.owner,
- item.actionCount,
- item.markdown || '缺失',
- item.csv || '缺失',
- (item.gapIds || []).join('; ') || '缺失',
- item.evidenceRowCount,
- item.nextActionCount
- ])),
- '',
- '## 真实证明闭环工单负责人附件',
- '',
- '| 负责人 | 工单数 | Markdown | CSV | 缺口 | 目标文件 |',
- '| --- | ---: | --- | --- | --- | --- |',
- ...summary.realProofClosureWorkOrder.ownerArtifacts.map(item => tableRow([
- item.owner,
- item.workOrderCount,
- item.markdown || '缺失',
- item.csv || '缺失',
- (item.gapIds || []).join('; ') || '缺失',
- (item.targetFiles || []).join('; ') || '缺失'
- ])),
- '',
- '## 本地种子转 intake 补表 worklist',
- '',
- renderLocalSeedToIntakeWorklist(summary.localSeedToIntakeWorklist),
- '',
- '## three real gap material audit',
- '',
- renderRealGapMaterialAudit(summary.realGapMaterialAudit),
- '',
- '## 修复清单入口',
- '',
- '| 类型 | CSV | 当前问题数 | 修复动作数 | 状态 |',
- '| --- | --- | ---: | ---: | --- |',
- tableRow([
- summary.repairActions.dataIntake.title,
- summary.repairActions.dataIntake.csv || '缺失',
- summary.repairActions.dataIntake.failureCount,
- summary.repairActions.dataIntake.repairActionCount,
- summary.repairActions.dataIntake.ready ? '已就绪' : '待补齐'
- ]),
- tableRow([
- summary.repairActions.homepageEvidence.title,
- summary.repairActions.homepageEvidence.csv || '缺失',
- summary.repairActions.homepageEvidence.failureCount,
- summary.repairActions.homepageEvidence.repairActionCount,
- summary.repairActions.homepageEvidence.ready ? '主页证据已就绪' : '待补主页近期内容证据'
- ]),
- tableRow([
- summary.repairActions.videoResource.title,
- summary.repairActions.videoResource.csv || '缺失',
- summary.repairActions.videoResource.failureCount,
- summary.repairActions.videoResource.repairActionCount,
- summary.repairActions.videoResource.readyForVideoAbPreflight ? '可进入视频 A/B 前置' : '待补真实视频资源'
- ]),
- '',
- '## 当前边界',
- '',
- `- 补证行动数:${summary.nextActions.actionCount}`,
- `- 未关闭证明缺口:${summary.nextActions.proofOpenCount}`,
- `- proofGapClosure.businessOpenCount:${summary.proofGapClosure.businessOpenCount}`,
- `- proofGapClosure.precheckOpenCount:${summary.proofGapClosure.precheckOpenCount}`,
- `- optimizationCompletion.videoAbPreflightReadyForVideoAb:${summary.optimizationCompletion.videoAbPreflightReadyForVideoAb ? 'true' : 'false'}`,
- `- optimizationCompletion.videoAbPreflightFailureCount:${summary.optimizationCompletion.videoAbPreflightFailureCount ?? 'unknown'}`,
- `- 交接摘要 complete:${summary.handoff.complete}`,
- '',
- '## 下一步命令',
- '',
- '```powershell',
- summary.recommendedNextCommand,
- '```',
- '',
- '## 防误用边界',
- '',
- ...summary.guardrails.map(item => `- ${item}`)
- ];
- const supportDocLines = [
- '## 中文 SOP 入口',
- '',
- '| 文档 | 路径 | 用法 |',
- '| --- | --- | --- |',
- ...summary.supportDocs.map(item => tableRow([
- item.title,
- item.path || '缺失',
- item.usage
- ])),
- ''
- ];
- const insertAt = lines.findIndex((line, index) => index > 10 && String(line).startsWith('## '));
- const currentFilesEnd = lines.findIndex((line, index) => index > 10 && line === '' && String(lines[index + 1] || '').startsWith('## '));
- if (currentFilesEnd > 0) {
- lines.splice(currentFilesEnd, 0, tableRow([
- summary.intakeFieldChecklist.title,
- summary.intakeFieldChecklist.markdown || summary.intakeFieldChecklist.csv || '缺失',
- summary.intakeFieldChecklist.exists ? `存在,${summary.intakeFieldChecklist.itemCount} 项,缺必填 ${summary.intakeFieldChecklist.missingRequiredCount},占位 ${summary.intakeFieldChecklist.placeholderCount}` : '缺失',
- summary.intakeFieldChecklist.usage
- ]), tableRow([
- summary.manualReviewLabelGuide.title,
- summary.manualReviewLabelGuide.markdown || summary.manualReviewLabelGuide.csv || '缺失',
- summary.manualReviewLabelGuide.exists
- ? `通过=${summary.manualReviewLabelGuide.passed},标签 ${summary.manualReviewLabelGuide.labelCount},归因 ${summary.manualReviewLabelGuide.attributionTypeCount}`
- : '缺失',
- summary.manualReviewLabelGuide.usage
- ]), '');
- }
- if (summary.intakeFieldChecklist.ownerGroups.length) {
- lines.splice(currentFilesEnd + 2, 0, ...renderIntakeOwnerGroups(summary.intakeFieldChecklist.ownerGroups));
- }
- if (summary.businessFillGuide?.rows?.length) {
- const ownerGroupInsertAt = lines.findIndex(line => line === '## 负责人行动文件');
- lines.splice(ownerGroupInsertAt > 0 ? ownerGroupInsertAt : currentFilesEnd + 2, 0, ...renderBusinessFillGuide(summary.businessFillGuide));
- }
- if (summary.intakeFieldChecklist.recheckCommands.length) {
- const ownerGroupInsertAt = lines.findIndex(line => line === '## 负责人行动文件');
- lines.splice(ownerGroupInsertAt > 0 ? ownerGroupInsertAt : currentFilesEnd + 2, 0, ...renderRecheckCommands(summary.intakeFieldChecklist.recheckCommands));
- }
- lines.splice(insertAt > 0 ? insertAt : 0, 0, ...supportDocLines);
- return lines.join('\n');
- }
- function renderLocalSeedToIntakeWorklist(worklist) {
- if (!worklist || !worklist.exists) {
- return '- 尚未找到本地种子转 intake 补表 worklist,请运行 `npm run local-seed-to-intake:worklist -- --outputs outputs --output outputs\\local-seed-to-intake-worklist-latest`。';
- }
- return [
- `- 报告:${worklist.markdown || '缺失'}`,
- `- 汇总 CSV:${worklist.csv || '缺失'}`,
- `- Summary:${worklist.summary || '缺失'}`,
- `- history-intake-draft:${worklist.historyDraft || '缺失'}`,
- `- video-resource-worklist:${worklist.videoWorklist || '缺失'}`,
- `- candidate-seed-worklist:${worklist.candidateWorklist || '缺失'}`,
- `- proofLevel:${worklist.proofLevel || 'not_business_proof'}`,
- `- materialType:${worklist.materialType || 'seed_to_intake_worklist'}`,
- `- directCustomerProof:${worklist.directCustomerProof ? 'true' : 'false'}`,
- `- canCloseProofGap:${worklist.canCloseProofGap ? '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}`,
- `- 目标模板:${(worklist.targetFiles || []).join(';') || '缺失'}`,
- '- 边界:该 worklist 只把本地种子转成补表草稿;空字段仍需商务/投放补真实材料,不能关闭 proof-gap。'
- ].join('\n');
- }
- function renderRealGapMaterialAudit(audit) {
- if (!audit || !audit.exists) {
- return '- missing; run `npm run real-gap:materials -- --outputs outputs --output outputs\\real-gap-material-audit-latest --strict`.';
- }
- 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 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
- ])),
- ''
- ];
- }
- function renderRecheckCommands(recheckCommands) {
- return [
- '## 补齐后复验命令',
- '',
- '| 阶段 | 负责人 | 命令 | 通过标准 | 边界 |',
- '| --- | --- | --- | --- | --- |',
- ...recheckCommands.map(item => tableRow([
- item.stage,
- item.owner,
- item.command,
- item.acceptance,
- item.boundary
- ])),
- ''
- ];
- }
- function buildBusinessFillGuide({ ownerGroups, recheckCommands }) {
- const rows = (ownerGroups || []).map((group, index) => {
- const recheck = recheckForSection(group.section, recheckCommands);
- return {
- order: index + 1,
- owner: group.owner || '',
- section: group.section || '',
- file: (group.files || [])[0] || '',
- firstItemIds: group.firstItemIds || [],
- issueCount: Number(group.itemCount || 0),
- missingRequiredCount: Number(group.missingRequiredCount || 0),
- placeholderCount: Number(group.placeholderCount || 0),
- fillAction: group.nextAction || '',
- recheckCommand: recheck?.command || '',
- acceptance: group.acceptance || recheck?.acceptance || '',
- boundary: group.boundary || '补齐材料,不证明客户效果'
- };
- });
- return {
- title: '商务填表执行视图',
- directCustomerProof: false,
- proofLevel: 'smoke_or_local',
- rowCount: rows.length,
- rows,
- guardrail: '本视图只帮助商务按表补齐真实材料,不代表客户效果已达标。'
- };
- }
- function recheckForSection(section, recheckCommands) {
- const commands = Array.isArray(recheckCommands) ? recheckCommands : [];
- if (section === 'video') {
- return commands.find(item => /video:resource-readiness/.test(item.command)) ||
- commands.find(item => /acceptance:video-ab/.test(item.command)) ||
- null;
- }
- if (section === 'history' || section === 'review') {
- return commands.find(item => /intake:readiness/.test(item.command)) ||
- commands.find(item => /customer-effect:audit/.test(item.command)) ||
- null;
- }
- return commands[0] || null;
- }
- function renderBusinessFillGuide(guide) {
- return [
- `## ${guide.title}`,
- '',
- '| 顺序 | 负责人 | 表单区域 | 填表文件 | 优先项 | 问题数 | 填表动作 | 补齐后复验 | 验收 | 边界 |',
- '| ---: | --- | --- | --- | --- | ---: | --- | --- | --- | --- |',
- ...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}`,
- ''
- ];
- }
- 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 => relIfPossible(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 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 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 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) {
- const artifacts = Array.isArray(summary?.ownerArtifacts) && summary.ownerArtifacts.length
- ? summary.ownerArtifacts
- : (summary?.ownerGroups || []).map(group => {
- const slug = ownerSlug(group.owner);
- return {
- owner: group.owner,
- actionCount: group.actionCount,
- topPriority: group.topPriority,
- markdown: path.join('by-owner', `${slug}.md`).replace(/\\/g, '/'),
- csv: path.join('by-owner', `${slug}.csv`).replace(/\\/g, '/'),
- gapIds: group.gapIds || [],
- evidenceRowCount: 0,
- nextActionCount: 0,
- repairArtifacts: []
- };
- });
- return artifacts.map(item => {
- const markdown = path.join(dir, item.markdown || '');
- const csv = path.join(dir, item.csv || '');
- return {
- owner: item.owner,
- actionCount: Number(item.actionCount || 0),
- topPriority: Number(item.topPriority || 0),
- markdown: relIfExists(root, markdown),
- csv: relIfExists(root, csv),
- gapIds: Array.isArray(item.gapIds) ? item.gapIds : [],
- evidenceRowCount: Number(item.evidenceRowCount || 0),
- nextActionCount: Number(item.nextActionCount || 0),
- repairArtifacts: normalizeRepairArtifacts(item.repairArtifacts || [])
- };
- });
- }
- function normalizePassThroughOwnerArtifacts(root, ownerArtifacts) {
- if (!Array.isArray(ownerArtifacts)) return [];
- return ownerArtifacts.map(item => ({
- owner: item.owner || '',
- actionCount: Number(item.actionCount || 0),
- topPriority: Number(item.topPriority || 0),
- markdown: relIfPossible(root, item.markdown || ''),
- csv: relIfPossible(root, item.csv || ''),
- gapIds: Array.isArray(item.gapIds) ? item.gapIds : [],
- evidenceRowCount: Number(item.evidenceRowCount || 0),
- nextActionCount: Number(item.nextActionCount || 0),
- repairArtifacts: normalizeRepairArtifacts(item.repairArtifacts || [])
- }));
- }
- function normalizeWorkOrderOwnerArtifacts(root, 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: relIfPossible(root, item.markdown || ''),
- csv: relIfPossible(root, item.csv || ''),
- targetFiles: Array.isArray(item.targetFiles) ? item.targetFiles : [],
- gapIds: Array.isArray(item.gapIds) ? item.gapIds : []
- }));
- }
- 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 ownerSlug(owner) {
- if (owner === '商务') return 'business';
- if (owner === '商务/投放') return 'business-media';
- if (owner === '技术/AI') return 'tech-ai';
- return String(owner || 'owner').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '') || 'owner';
- }
- function fileGroup(root, dir, files) {
- return Object.fromEntries(Object.entries(files).map(([key, file]) => [
- key,
- relIfExists(root, path.join(dir, file))
- ]));
- }
- function latestOutputDir(outputsDir, pattern) {
- if (!fs.existsSync(outputsDir)) return null;
- 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] || null;
- }
- function readJsonIfExists(file) {
- if (!fs.existsSync(file)) return null;
- return JSON.parse(fs.readFileSync(file, 'utf8').replace(/^\uFEFF/, ''));
- }
- function relIfExists(root, file) {
- return fs.existsSync(file) ? path.relative(root, file).replace(/\\/g, '/') : null;
- }
- function relIfPossible(root, file) {
- if (!file) return '';
- const resolvedRoot = path.resolve(root);
- const resolvedFile = path.resolve(file);
- const relative = path.relative(resolvedRoot, resolvedFile);
- if (relative && (relative.startsWith('..') || path.isAbsolute(relative))) return String(file).replace(/\\/g, '/');
- return relative.replace(/\\/g, '/');
- }
- function finalDuplicateGroupCount(duplicateAudit) {
- if (!duplicateAudit) return null;
- return Object.values(duplicateAudit)
- .reduce((sum, groups) => sum + (Array.isArray(groups) ? groups.length : 0), 0);
- }
- 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 tableRow(cells) {
- return `| ${cells.map(cell => String(cell ?? '').replace(/\|/g, '/').replace(/\r?\n/g, ' ')).join(' | ')} |`;
- }
- function parseArgs(argv) {
- const result = {};
- for (let index = 0; index < argv.length; index += 1) {
- const arg = argv[index];
- if (!arg.startsWith('--')) continue;
- const key = arg.slice(2).replace(/-([a-z])/g, (_, char) => char.toUpperCase());
- const next = argv[index + 1];
- if (!next || next.startsWith('--')) result[key] = true;
- else {
- result[key] = next;
- index += 1;
- }
- }
- return result;
- }
- function withBom(text) {
- return `\uFEFF${text}`;
- }
- if (require.main === module) main();
- module.exports = {
- buildLatestFormIndex
- };
|