round-deposition-audit.js 221 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612
  1. #!/usr/bin/env node
  2. const fs = require('fs');
  3. const path = require('path');
  4. const ROOT = path.resolve(__dirname, '..');
  5. const OUTPUTS = path.join(ROOT, 'outputs');
  6. const VIDEO_AB_PREFLIGHT_EVIDENCE_TOKENS = [
  7. 'mode=unknown',
  8. 'collectionMode=unknown',
  9. 'generatedBy=unknown',
  10. 'preflight.readyForVideoAb=false',
  11. 'preflight.failureCount=',
  12. 'preflight.generatedBy=acceptance:video-ab-preflight',
  13. 'runtimeCredentialPresent=false',
  14. 'companyResolved=false',
  15. 'vocSocialProviderTokenPresent=false',
  16. 'videoAnalysisTokenPresent=false'
  17. ];
  18. const VIDEO_AB_PREFLIGHT_REQUIREMENT_TOKENS = [
  19. 'proofContext.mode=live',
  20. 'proofContext.collectionMode=live',
  21. 'proofContext.generatedBy=acceptance:video-ab',
  22. 'proofContext.requiresRuntimeCredential=true',
  23. 'proofContext.requiresVocSocialProvider=true',
  24. 'proofContext.requiresVideoAnalysisProvider=true',
  25. 'videoAbPreflight.readyForVideoAb=true',
  26. 'videoAbPreflight.proofContext.requiresRuntimeCredential=true',
  27. 'videoAbPreflight.proofContext.requiresVocSocialProvider=true',
  28. 'videoAbPreflight.proofContext.requiresVideoAnalysisProvider=true',
  29. 'videoAbPreflight.company resolved for live run'
  30. ];
  31. function main() {
  32. const args = parseArgs(process.argv.slice(2));
  33. const outputDir = path.resolve(args.output || process.env.TIHAO_ROUND_DEPOSITION_OUTPUT || path.join(OUTPUTS, `round-deposition-${Date.now()}`));
  34. const summary = buildRoundDepositionSummary({
  35. root: ROOT,
  36. outputsDir: path.resolve(args.outputs || OUTPUTS),
  37. implementationLog: path.resolve(args.implementationLog || path.join(ROOT, 'docs', 'tihao-experience-implementation-log.md')),
  38. statusPath: args.status ? path.resolve(args.status) : null,
  39. evidencePath: args.evidence ? path.resolve(args.evidence) : null,
  40. handoffPath: args.handoff ? path.resolve(args.handoff) : null,
  41. proofGapPath: args.proofGap ? path.resolve(args.proofGap) : null
  42. , proofGapClosurePath: args.proofGapClosure ? path.resolve(args.proofGapClosure) : null,
  43. proofGapOperatorPackPath: args.proofGapOperatorPack ? path.resolve(args.proofGapOperatorPack) : null,
  44. homepageEvidenceReadinessPath: args.homepageEvidenceReadiness ? path.resolve(args.homepageEvidenceReadiness) : null,
  45. intakeReadinessPath: args.intakeReadiness ? path.resolve(args.intakeReadiness) : null,
  46. longRunReadinessPath: args.longRunReadiness ? path.resolve(args.longRunReadiness) : null,
  47. businessProofProgressPath: args.businessProofProgress ? path.resolve(args.businessProofProgress) : null,
  48. businessProofNextActionsPath: args.businessProofNextActions ? path.resolve(args.businessProofNextActions) : null,
  49. businessExecutionIndexPath: args.businessExecutionIndex ? path.resolve(args.businessExecutionIndex) : null,
  50. optimizationCompletionPath: args.optimizationCompletion ? path.resolve(args.optimizationCompletion) : null,
  51. realProofIntakeBundlePath: args.realProofIntakeBundle ? path.resolve(args.realProofIntakeBundle) : null,
  52. intakeFieldChecklistPath: args.intakeFieldChecklist ? path.resolve(args.intakeFieldChecklist) : null,
  53. proofGapSoftwareFormPath: args.proofGapSoftwareForm ? path.resolve(args.proofGapSoftwareForm) : null,
  54. latestFormIndexPath: args.latestFormIndex ? path.resolve(args.latestFormIndex) : null,
  55. planExecutionStatusPath: args.planExecutionStatus ? path.resolve(args.planExecutionStatus) : null,
  56. proofActionOrderPath: args.proofActionOrder ? path.resolve(args.proofActionOrder) : null
  57. });
  58. fs.mkdirSync(outputDir, { recursive: true });
  59. const jsonPath = path.join(outputDir, 'round-deposition-summary.json');
  60. const reportPath = path.join(outputDir, 'round-deposition-report.md');
  61. fs.writeFileSync(jsonPath, JSON.stringify(summary, null, 2), 'utf8');
  62. fs.writeFileSync(reportPath, withBom(renderReport(summary)), 'utf8');
  63. console.log(JSON.stringify({
  64. outputDir,
  65. json: jsonPath,
  66. report: reportPath,
  67. passed: summary.passed,
  68. passCount: summary.counts.pass,
  69. failCount: summary.counts.fail,
  70. warnCount: summary.counts.warn
  71. }, null, 2));
  72. if (!summary.passed && args.strict) process.exitCode = 1;
  73. }
  74. function buildRoundDepositionSummary({ root, outputsDir, implementationLog, statusPath, evidencePath, handoffPath, proofGapPath, proofGapClosurePath, proofGapOperatorPackPath, homepageEvidenceReadinessPath, intakeReadinessPath, longRunReadinessPath, businessProofProgressPath, businessProofNextActionsPath, businessExecutionIndexPath, optimizationCompletionPath, realProofIntakeBundlePath, intakeFieldChecklistPath, proofGapSoftwareFormPath, latestFormIndexPath, planExecutionStatusPath, proofActionOrderPath }) {
  75. const resolvedStatusPath = statusPath || latestFile(outputsDir, 'optimization-status-summary.json');
  76. const resolvedEvidencePath = evidencePath || latestFile(outputsDir, 'evidence-index-summary.json');
  77. const resolvedHandoffPath = handoffPath || latestFile(outputsDir, 'optimization-handoff-summary.json');
  78. const resolvedProofGapPath = proofGapPath || latestFile(outputsDir, 'proof-gap-request-summary.json');
  79. const resolvedProofGapClosurePath = proofGapClosurePath || latestFile(outputsDir, 'proof-gap-closure-summary.json');
  80. const resolvedProofGapOperatorPackPath = proofGapOperatorPackPath || latestFile(outputsDir, 'proof-gap-operator-pack-summary.json');
  81. const resolvedFeedbackLoopClosurePath = latestFile(outputsDir, 'feedback-loop-closure-summary.json');
  82. const resolvedHomepageEvidenceReadinessPath = homepageEvidenceReadinessPath || latestFile(outputsDir, 'homepage-evidence-readiness-summary.json');
  83. const resolvedIntakeReadinessPath = intakeReadinessPath || latestFile(outputsDir, 'intake-readiness-summary.json');
  84. const resolvedLongRunReadinessPath = longRunReadinessPath || latestFile(outputsDir, 'long-run-readiness-summary.json');
  85. const resolvedBusinessProofProgressPath = businessProofProgressPath || latestFile(outputsDir, 'business-proof-progress-summary.json');
  86. const resolvedBusinessProofNextActionsPath = businessProofNextActionsPath || latestFile(outputsDir, 'business-proof-next-actions-summary.json');
  87. const resolvedBusinessExecutionIndexPath = businessExecutionIndexPath || latestFile(outputsDir, 'business-execution-index-summary.json');
  88. const resolvedOptimizationCompletionPath = optimizationCompletionPath || latestFile(outputsDir, 'optimization-completion-summary.json');
  89. const resolvedRealProofIntakeBundlePath = realProofIntakeBundlePath || latestFile(outputsDir, 'real-proof-intake-bundle-summary.json');
  90. const resolvedIntakeFieldChecklistPath = intakeFieldChecklistPath || latestFile(outputsDir, 'intake-field-checklist-summary.json');
  91. const resolvedManualReviewLabelGuidePath = latestFile(outputsDir, 'manual-review-label-guide-summary.json');
  92. const resolvedProofGapSoftwareFormPath = proofGapSoftwareFormPath || latestFile(outputsDir, 'tihao-proof-gap-software-form-summary.json');
  93. const resolvedLatestFormIndexPath = latestFormIndexPath || latestFile(outputsDir, 'latest-form-index-summary.json');
  94. const resolvedLocalSeedToIntakeWorklistPath = latestFile(outputsDir, 'local-seed-to-intake-worklist-summary.json');
  95. const resolvedRealGapMaterialAuditPath = latestFile(outputsDir, 'real-gap-material-audit-summary.json');
  96. const resolvedRealProofClosureWorkOrderPath = latestFile(outputsDir, 'real-proof-closure-work-order-summary.json');
  97. const resolvedPlanExecutionStatusPath = planExecutionStatusPath || latestFile(outputsDir, 'plan-execution-status-summary.json');
  98. const status = readJson(resolvedStatusPath);
  99. const evidence = readJson(resolvedEvidencePath);
  100. const handoff = readJson(resolvedHandoffPath);
  101. const proofGap = readJson(resolvedProofGapPath);
  102. const proofGapClosure = readJson(resolvedProofGapClosurePath);
  103. const proofGapOperatorPack = readJson(resolvedProofGapOperatorPackPath);
  104. const feedbackLoopClosure = readJson(resolvedFeedbackLoopClosurePath);
  105. const homepageEvidenceReadiness = readJson(resolvedHomepageEvidenceReadinessPath);
  106. const intakeReadiness = readJson(resolvedIntakeReadinessPath);
  107. const longRunReadiness = readJson(resolvedLongRunReadinessPath);
  108. const businessProofProgress = readJson(resolvedBusinessProofProgressPath);
  109. const businessProofNextActions = readJson(resolvedBusinessProofNextActionsPath);
  110. const businessExecutionIndex = readJson(resolvedBusinessExecutionIndexPath);
  111. const optimizationCompletion = readJson(resolvedOptimizationCompletionPath);
  112. const realProofIntakeBundle = readJson(resolvedRealProofIntakeBundlePath);
  113. const intakeFieldChecklist = readJson(resolvedIntakeFieldChecklistPath);
  114. const manualReviewLabelGuide = readJson(resolvedManualReviewLabelGuidePath);
  115. const proofGapSoftwareForm = readJson(resolvedProofGapSoftwareFormPath);
  116. const latestFormIndex = readJson(resolvedLatestFormIndexPath);
  117. const localSeedToIntakeWorklist = readJson(resolvedLocalSeedToIntakeWorklistPath);
  118. const realGapMaterialAudit = readJson(resolvedRealGapMaterialAuditPath);
  119. const realProofClosureWorkOrder = readJson(resolvedRealProofClosureWorkOrderPath);
  120. const planExecutionStatus = readJson(resolvedPlanExecutionStatusPath);
  121. const logText = readText(implementationLog);
  122. const resolvedProofActionOrderPath = proofActionOrderPath || firstExistingPath(root, [
  123. path.join('docs', 'business-proof-action-order.zh-CN.md'),
  124. path.join('docs', 'business-proof-action-order.md')
  125. ]);
  126. const proofActionOrderText = readText(resolvedProofActionOrderPath);
  127. const proofGapReportText = readText(sibling(resolvedProofGapPath, 'proof-gap-request-report.md'));
  128. const proofGapCsvText = readText(sibling(resolvedProofGapPath, 'proof-gap-request-table.csv'));
  129. const proofGapTemplateRowsReady = Array.isArray(proofGap?.rows) &&
  130. proofGap.rows.length >= 1 &&
  131. proofGap.rows.every(item => item.templateCommand && item.templateArtifact);
  132. const proofGapTemplateColumnsReady = proofGapTemplateRowsReady &&
  133. proofGapReportText.includes('模板命令') &&
  134. proofGapReportText.includes('模板产物') &&
  135. proofGapCsvText.includes('模板命令') &&
  136. proofGapCsvText.includes('模板产物');
  137. const checks = [
  138. check('proof-gap-closure-video-ab-preflight-context', 'proof-gap closure records video A/B preflight context', hasProofGapClosureVideoAbPreflightContext(proofGapClosure), 'proofGapClosure.videoAbPreflightFailureCount/rows.video-ab-live-proof', 'When a video A/B runtime preflight exists and is not ready, proof-gap closure must expose it as non-proof context without closing live proof.'),
  139. check('optimization-completion-video-ab-preflight-context', 'optimization completion records video A/B preflight context', hasOptimizationCompletionVideoAbPreflightContext(optimizationCompletion), 'optimizationCompletion.videoAbPreflightFailureCount/gates.video-ab-live-proof', 'When a video A/B runtime preflight exists and is not ready, completion must expose it as non-proof context without closing live proof.'),
  140. check('proof-gap-closure-recursive-proof-artifact-discovery', 'proof-gap closure retains recursively discovered history/customer-effect artifacts', hasProofGapClosureRecursiveProofArtifactDiscovery(proofGapClosure), 'proofGapClosure.sourceFiles.history/customerEffect + concrete row requirements', 'When nested history/customer-effect artifacts exist, closure must retain their source paths and report concrete failed requirements instead of falling back to missing-file rows.'),
  141. check('optimization-completion-recursive-proof-artifact-discovery', 'optimization completion retains recursively discovered history/customer-effect artifacts', hasOptimizationCompletionRecursiveProofArtifactDiscovery(optimizationCompletion), 'optimizationCompletion.sourceFiles.history/customerEffect + concrete blocking requirements', 'When nested history/customer-effect artifacts exist, completion must retain their source paths and propagate concrete failed requirements into blocking reasons/gates.'),
  142. check('implementation-log', '实施日志存在且为中文', Boolean(logText) && /提号|优化|当前|已完成/.test(logText), rel(root, implementationLog), '每轮结束后更新 docs/tihao-experience-implementation-log.md。'),
  143. check('status-summary', '最新 optimization-status-summary.json 存在', Boolean(status), rel(root, resolvedStatusPath), '运行 npm run optimization:status。'),
  144. check('status-report', '最新 optimization-status-report.md 存在', fileExists(sibling(resolvedStatusPath, 'optimization-status-report.md')), rel(root, sibling(resolvedStatusPath, 'optimization-status-report.md')), '运行 npm run optimization:status。'),
  145. check('evidence-index', '最新 evidence-index-summary.json 存在', Boolean(evidence), rel(root, resolvedEvidencePath), '运行 npm run evidence:index -- --output outputs\\evidence-index-latest。'),
  146. check('evidence-report', '最新 evidence-index-report.md 存在', fileExists(sibling(resolvedEvidencePath, 'evidence-index-report.md')), rel(root, sibling(resolvedEvidencePath, 'evidence-index-report.md')), '运行 npm run evidence:index。'),
  147. check('handoff-summary', '最新 optimization-handoff-summary.json 存在', Boolean(handoff), rel(root, resolvedHandoffPath), '运行 npm run handoff:summary -- --output outputs\\optimization-handoff-latest。'),
  148. check('handoff-report', '最新 optimization-handoff-report.md 存在', fileExists(sibling(resolvedHandoffPath, 'optimization-handoff-report.md')), rel(root, sibling(resolvedHandoffPath, 'optimization-handoff-report.md')), '运行 npm run handoff:summary。'),
  149. check('proof-gap-summary', '最新 proof-gap-request-summary.json 存在', Boolean(proofGap), rel(root, resolvedProofGapPath), '运行 npm run proof-gap:request -- --output outputs\\proof-gap-request-latest。'),
  150. check('proof-gap-report', '最新 proof-gap-request-report.md 存在', fileExists(sibling(resolvedProofGapPath, 'proof-gap-request-report.md')), rel(root, sibling(resolvedProofGapPath, 'proof-gap-request-report.md')), '运行 npm run proof-gap:request。'),
  151. check('proof-gap-csv', '最新 proof-gap-request-table.csv 存在', fileExists(sibling(resolvedProofGapPath, 'proof-gap-request-table.csv')), rel(root, sibling(resolvedProofGapPath, 'proof-gap-request-table.csv')), '运行 npm run proof-gap:request。'),
  152. check('proof-gap-unresolved-recorded', 'proof-gap 记录未解决真实证明缺口', Number(proofGap?.unresolvedCount || 0) >= 1 && Array.isArray(proofGap?.rows) && proofGap.rows.length >= 1, 'proofGap.unresolvedCount', '长期目标未完成时必须记录仍缺哪些真实证明材料。'),
  153. check('proof-gap-template-entry-recorded', 'proof-gap 记录真实材料模板入口', proofGapTemplateRowsReady, 'proofGap.rows.templateCommand/templateArtifact', '运行 npm run proof-gap:request,确保每个缺口都有模板命令和模板产物。'),
  154. check('proof-gap-template-columns-exported', 'proof-gap 报告和 CSV 导出模板入口列', proofGapTemplateColumnsReady, 'proofGap.report/csv.模板命令/模板产物', '补齐 proof-gap 请求包中的模板命令和模板产物列,避免只剩口头说明。'),
  155. check('proof-gap-operator-pack-summary', '提号补证操作包 summary 存在', Boolean(proofGapOperatorPack), rel(root, resolvedProofGapOperatorPackPath), '运行 npm run proof-gap:operator-pack -- --outputs outputs --output outputs\\proof-gap-operator-pack-latest --strict。'),
  156. check('proof-gap-operator-pack-actionable', '提号补证操作包记录 5 个可执行缺口', hasProofGapOperatorPack(proofGapOperatorPack), 'proofGapOperatorPack.gapIds/recheckCommandCount', '操作包必须展示 5 个 gapId、负责人、填写文件、复验命令和不证明客户效果边界。'),
  157. check('proof-gap-operator-pack-missing-proof-by-gap', '提号补证操作包逐缺口记录缺失证明要求', hasProofGapOperatorPackMissingProofByGap(proofGapOperatorPack), 'proofGapOperatorPack.rows.missingProofRequirements', '操作包必须让每个未关闭业务缺口都携带至少 1 条缺失证明要求,避免只看到 open 但看不到关闭条件。'),
  158. check('proof-gap-operator-pack-video-ab-preflight-context', '提号补证操作包记录 video A/B preflight 非证明上下文', hasProofGapOperatorPackVideoAbPreflightContext(proofGapOperatorPack), 'proofGapOperatorPack.rows.video-ab-live-proof.preflight', '操作包必须把 video A/B preflight 失败作为 live proof 缺口上下文展示,同时保留 proofContext live/generatedBy/runtime/provider 缺失要求。'),
  159. check('intake-readiness-summary', '最新 intake-readiness-summary.json 存在', Boolean(intakeReadiness), rel(root, resolvedIntakeReadinessPath), '运行 npm run intake:readiness -- --data-pack outputs\\data-intake-pack-latest --video-pack outputs\\video-intake-pack-latest --output outputs\\intake-readiness-latest。'),
  160. check('intake-readiness-boundary-recorded', 'intake readiness 记录模板是否已替换为真实材料', intakeReadiness && typeof intakeReadiness.acceptance?.overallReady === 'boolean' && Number.isFinite(Number(intakeReadiness.failureCount || 0)), 'intakeReadiness.overallReady/failureCount', 'intake readiness 必须记录 overallReady 和 failureCount,避免半成品进入长跑。'),
  161. check('longrun-readiness-summary', '最新 long-run-readiness-summary.json 存在', Boolean(longRunReadiness), rel(root, resolvedLongRunReadinessPath), '运行 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 --output outputs\\long-run-readiness-latest。'),
  162. check('longrun-readiness-intake-gate', 'longrun readiness 记录 intake 前置门禁', Boolean(longRunReadiness) && Array.isArray(longRunReadiness.checks) && longRunReadiness.checks.some(item => String(item.name || '').includes('intake')), 'longRunReadiness.checks.intake', 'longrun readiness 必须显示 intake readiness 对 full matrix/customer-effect 的阻断关系。'),
  163. check('business-proof-progress-summary', '最新 business-proof-progress-summary.json 存在', Boolean(businessProofProgress), rel(root, resolvedBusinessProofProgressPath), '运行 npm run business-proof:progress -- --output outputs\\business-proof-progress-latest。'),
  164. check('business-proof-progress-steps', '补证进度表记录 15 步状态', hasBusinessProofProgress(businessProofProgress), 'businessProofProgress.rows/counts/complete', '补证进度表必须包含 15 步、complete=false 和 pass/pending/fail/blocked_by_external_data 统计。'),
  165. check('business-proof-progress-operator-owner-artifacts', '补证进度表记录操作包负责人附件', hasBusinessProofProgressOperatorOwnerArtifacts(businessProofProgress, proofGapOperatorPack), 'businessProofProgress.operatorPackOwnerArtifacts', '补证进度表必须透传 business/business-media/tech-ai 三份 proof-gap 操作包负责人附件,避免只看 15 步进度时找不到派工文件。'),
  166. check('handoff-business-proof-progress-operator-owner-artifacts', '交接摘要记录补证进度表负责人附件', hasBusinessProofProgressOperatorOwnerArtifacts(handoff?.businessProofProgress, proofGapOperatorPack), 'handoff.businessProofProgress.operatorPackOwnerArtifacts', '交接摘要必须透传补证进度表里的三份 proof-gap 操作包负责人附件。'),
  167. check('business-proof-next-actions-summary', '最新 business-proof-next-actions-summary.json 存在', Boolean(businessProofNextActions), rel(root, resolvedBusinessProofNextActionsPath), '运行 npm run business-proof:next-actions -- --output outputs\\business-proof-next-actions-latest。'),
  168. check('business-proof-next-actions-open', '下一步行动队列记录待办动作', hasBusinessProofNextActions(businessProofNextActions), 'businessProofNextActions.actions/sourceState', '行动队列必须记录 actionCount、top action 和未完成边界,避免只同步状态不分派任务。'),
  169. check('business-proof-next-actions-deduped', '下一步行动队列按业务缺口去重', hasDedupedBusinessProofNextActions(businessProofNextActions, proofGap), 'businessProofNextActions.actions', '行动队列必须收敛为真实历史 Brief、真实视频资源、人工复核、视频 A/B、live/provider 长跑等唯一业务缺口,不能把导入、审计、门禁拆成重复任务。'),
  170. check('business-proof-next-actions-missing-proof-requirements', '下一步行动队列逐业务缺口记录缺失证明要求', hasBusinessProofNextActionsMissingProofRequirements(businessProofNextActions), 'businessProofNextActions.actions.missingProofRequirements', '行动队列的未关闭业务缺口动作必须携带结构化缺失证明要求,避免负责人只看到下一步文字却无法机器复验关闭条件。'),
  171. check('business-proof-next-actions-stale-pipeline-suppression', '下一步行动队列隔离过期 pipeline 动作', hasBusinessProofNextActionsStalePipelineSuppression(businessProofNextActions), 'businessProofNextActions.sourceState.pipelineStale', '当 completion 已判定 optimization-pipeline stale 时,行动队列必须暴露 suppressed 计数并禁止把旧 pipeline nextActions 当作当前派工。'),
  172. check('handoff-business-proof-next-actions-top-actions', '交接摘要记录下一步行动队列摘要', hasHandoffBusinessProofNextActions(handoff?.businessProofNextActions, businessProofNextActions), 'handoff.businessProofNextActions.topActions', '交接摘要必须透传下一步行动队列的 actionCount、proofGapOpenCount 和前 5 个高优先级动作,避免下一轮只看到文件链接。'),
  173. check('optimization-completion-summary', '优化完成度审计 summary 存在', Boolean(optimizationCompletion), rel(root, resolvedOptimizationCompletionPath), '运行 npm run optimization:completion -- --output outputs\\optimization-completion-latest。'),
  174. check('optimization-completion-boundary', '优化完成度审计记录禁止宣称边界', hasOptimizationCompletionBoundary(optimizationCompletion), 'optimizationCompletion.readyForClaim/canClaim', '完成度审计必须记录 readyForClaim、proofGapOpenCount、blockingReasons 和三项禁用宣称。'),
  175. check('optimization-completion-missing-proof-requirements', '优化完成度审计逐阻塞项记录缺失证明要求', hasOptimizationCompletionMissingProofRequirements(optimizationCompletion), 'optimizationCompletion.blockingReasons.missingProofRequirements', '完成度审计必须让每个 blocked gate 都携带至少 1 条缺失证明要求,避免只看到 readyForClaim=false 却不知道如何关闭。'),
  176. check('business-execution-index-summary', '商务执行总表 summary 存在', Boolean(businessExecutionIndex), rel(root, resolvedBusinessExecutionIndexPath), '运行 npm run business-execution:index -- --output outputs\\business-execution-index-latest --strict。'),
  177. check('business-execution-index-passed', '商务执行总表通过并保留证明边界', hasBusinessExecutionIndex(businessExecutionIndex, businessProofNextActions, proofGap), 'businessExecutionIndex.rows/directCustomerProof/proofOpenCount', '商务执行总表必须 passed=true、directCustomerProof=false、包含负责人行动/修复清单/关键文件,且 proofOpenCount 对齐当前未关闭证明缺口。'),
  178. check('business-execution-index-operator-owner-artifacts', '商务执行总表记录操作包负责人附件', hasBusinessExecutionOperatorOwnerArtifacts(businessExecutionIndex, proofGapOperatorPack), 'businessExecutionIndex.operatorPackOwnerArtifacts', '商务执行总表必须展示 business/business-media/tech-ai 三份 proof-gap 操作包负责人附件,避免商务只打开执行总表时看不到自己的派工文件。'),
  179. check('business-execution-index-longrun-readiness', '商务执行总表记录长跑前置门禁入口', hasBusinessExecutionLongRunReadiness(businessExecutionIndex, longRunReadiness), 'businessExecutionIndex.longRunReadiness', '商务执行总表必须展示 long-run-readiness-report.md/summary.json、ready、failCount 和不能启动长跑的边界。'),
  180. check('business-execution-index-optimization-completion', '商务执行总表记录优化完成度审计入口', hasBusinessExecutionOptimizationCompletion(businessExecutionIndex, optimizationCompletion), 'businessExecutionIndex.optimizationCompletion', '商务执行总表必须展示 optimization-completion-report.md/summary.json、readyForClaim、proofGapOpenCount 和禁用宣称边界。'),
  181. check('real-proof-intake-bundle-summary', '真实验收材料收集总包 summary 存在', Boolean(realProofIntakeBundle), rel(root, resolvedRealProofIntakeBundlePath), '运行 npm run proof-gap:intake-bundle -- --output outputs\\real-proof-intake-bundle-latest --strict。'),
  182. check('real-proof-intake-bundle-longrun-readiness', '真实验收材料总包记录长跑前置门禁入口', hasRealProofBundleLongRunReadiness(realProofIntakeBundle, longRunReadiness), 'realProofIntakeBundle.longRunReadiness', '真实验收材料总包必须展示 long-run-readiness-report.md、longrun:readiness 复验命令、ready/failCount 和 not_business_proof 边界。'),
  183. check('real-proof-intake-bundle-operator-owner-artifacts', '真实验收材料总包透传操作包负责人附件', hasProofGapOperatorOwnerArtifacts(realProofIntakeBundle?.operatorPackOwnerArtifacts), 'realProofIntakeBundle.operatorPackOwnerArtifacts', '真实验收材料总包必须展示 business/business-media/tech-ai 三份操作包负责人附件,避免商务只打开总包时看不到自己的派工文件。'),
  184. check('real-proof-intake-bundle-video-ab-preflight-context', '真实验收材料总包记录 video A/B preflight 非证明上下文', hasRealProofIntakeBundleVideoAbPreflightContext(realProofIntakeBundle), 'realProofIntakeBundle.proofGapRows.video-ab-live-proof.preflight', '真实材料总包必须透传 video A/B preflight 失败原因和 live proof 缺失要求,避免总包丢掉技术/AI 补证条件。'),
  185. check('handoff-business-execution-index', '交接摘要记录商务执行总表入口', hasHandoffBusinessExecutionIndex(handoff, businessExecutionIndex), 'handoff.businessExecutionIndex', '交接摘要必须展示 business-execution-index.md/csv,避免下一轮 AI 或商务继续在多个入口之间跳转。'),
  186. check('handoff-business-execution-index-operator-owner-artifacts', '交接摘要的商务执行总表入口记录操作包负责人附件', hasHandoffBusinessExecutionOperatorOwnerArtifacts(handoff, businessExecutionIndex), 'handoff.businessExecutionIndex.operatorPackOwnerArtifacts', '交接摘要必须在商务执行总表入口下透传三份 proof-gap 操作包负责人附件。'),
  187. check('handoff-business-execution-index-longrun-readiness', '交接摘要的商务执行总表入口记录长跑前置门禁行', hasHandoffBusinessExecutionLongRunReadiness(handoff, businessExecutionIndex, longRunReadiness), 'handoff.businessExecutionIndex.longRunReadiness', '交接摘要必须在商务执行总表入口下透传长跑前置门禁报告、summary、ready/failCount 和不能启动长跑的边界。'),
  188. check('handoff-optimization-completion', '交接摘要顶层记录优化完成度审计入口', hasHandoffOptimizationCompletion(handoff, optimizationCompletion), 'handoff.optimizationCompletion', '交接摘要必须顶层展示 optimization-completion-report.md、readyForClaim、proofGapOpenCount 和禁用宣称边界。'),
  189. check('handoff-business-execution-index-optimization-completion', '交接摘要的商务执行总表入口记录优化完成度审计行', hasHandoffBusinessExecutionOptimizationCompletion(handoff, businessExecutionIndex, optimizationCompletion), 'handoff.businessExecutionIndex.optimizationCompletion', '交接摘要必须在商务执行总表入口下透传完成度审计报告、summary、readyForClaim 和禁用宣称边界。'),
  190. check('intake-field-checklist-summary', '真实材料字段级补齐核对表 summary 存在', Boolean(intakeFieldChecklist), rel(root, resolvedIntakeFieldChecklistPath), '运行 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。'),
  191. check('intake-field-checklist-passed', '真实材料字段级补齐核对表通过并保留证明边界', hasIntakeFieldChecklist(intakeFieldChecklist), 'intakeFieldChecklist.itemCount/directCustomerProof', '字段级核对表必须 passed=true、directCustomerProof=false、itemCount>0,并记录缺必填和占位内容数量。'),
  192. check('handoff-intake-field-checklist', '交接摘要记录字段级补齐核对表入口', hasHandoffIntakeFieldChecklist(handoff, intakeFieldChecklist), 'handoff.intakeFieldChecklist', '交接摘要必须展示 intake-field-checklist.md/csv,避免下一轮 AI 或商务看不到逐格补齐入口。'),
  193. check('proof-gap-software-form-summary', '最新提号补证表单 summary 存在', Boolean(proofGapSoftwareForm), rel(root, resolvedProofGapSoftwareFormPath), '运行 npm run proof-gap:software-form -- --output outputs\\tihao-proof-gap-software-form-latest --strict。'),
  194. check('proof-gap-software-form-passed', '最新提号补证表单通过软件端格式验收', hasPassingProofGapSoftwareForm(proofGapSoftwareForm, proofGap), 'proofGapSoftwareForm.passed/header/rows', '最新提号补证表单必须 passed=true、headerMatches=true、rowWidthOk=true、rowCount 对齐 unresolvedCount、duplicateIdCount=0。'),
  195. check('latest-form-index-summary', '最新提号表单索引 summary 存在', Boolean(latestFormIndex), rel(root, resolvedLatestFormIndexPath), '运行 npm run latest-form:index -- --output outputs\\latest-form-index-latest --strict。'),
  196. check('latest-form-index-passed', '最新提号表单索引通过并指向关键文件', hasPassingLatestFormIndex(latestFormIndex), 'latestFormIndex.primaryForm/clientList/ownerArtifacts', '最新提号表单索引必须 passed=true,并指向补证表单、候选名单和负责人行动文件。'),
  197. check('latest-form-index-client-list-delivery-quality', '最新提号表单索引记录候选名单去重交付质量', hasLatestFormClientListDeliveryQuality(latestFormIndex), 'latestFormIndex.clientList.finalDuplicateGroupCount/sourceDuplicateRemovedCount/rankContinuous', '最新提号表单索引必须明确候选名单最终重复组为 0、排名连续、duplicateCount 仅表示源表去重丢弃,且 directCustomerProof=false。'),
  198. check('latest-form-index-business-execution-index', '最新提号表单索引记录商务执行总表入口', hasLatestFormBusinessExecutionIndex(latestFormIndex, businessExecutionIndex), 'latestFormIndex.businessExecutionIndex', '最新提号表单索引必须展示 business-execution-index.md/csv,避免商务只看到补证表单却看不到执行总表。'),
  199. check('latest-form-index-intake-field-checklist', '最新提号表单索引记录字段级补齐核对表入口', hasLatestFormIntakeFieldChecklist(latestFormIndex, intakeFieldChecklist), 'latestFormIndex.intakeFieldChecklist', '最新提号表单索引必须展示 intake-field-checklist.md/csv,方便商务按行号和字段补齐真实材料。'),
  200. check('latest-form-index-intake-owner-recheck', '最新提号表单索引记录字段补齐负责人和复验命令', hasLatestFormIntakeOwnerRecheck(latestFormIndex, intakeFieldChecklist), 'latestFormIndex.intakeFieldChecklist.ownerGroups/recheckCommands', '最新提号表单索引必须展示字段补齐负责人分组和补齐后复验命令,避免软件端入口只剩文件路径。'),
  201. check('latest-form-index-business-fill-guide', '最新提号表单索引记录商务填表执行视图', hasLatestFormBusinessFillGuide(latestFormIndex), 'latestFormIndex.businessFillGuide', '最新提号表单索引必须展示商务填表执行视图,把 ownerGroups 转成填表文件、优先项、复验命令和证明边界。'),
  202. check('latest-form-index-support-docs', '最新提号表单索引记录中文 SOP 入口', hasLatestFormSupportDocs(latestFormIndex), 'latestFormIndex.supportDocs', '最新提号表单索引必须指向中文长期优化计划和中文补证操作顺序,避免商务或下一轮 AI 打开乱码旧文档。'),
  203. check('latest-form-index-repair-actions', '最新提号表单索引记录修复清单入口', hasLatestFormRepairActions(latestFormIndex), 'latestFormIndex.repairActions', '最新提号表单索引必须指向 intake 和 video resource 修复清单 CSV,方便商务直接照表补齐真实材料。'),
  204. check('latest-form-index-owner-repair-actions', '最新提号表单索引记录负责人修复附件', hasOwnerRepairArtifacts(latestFormIndex?.nextActions?.ownerArtifacts), 'latestFormIndex.nextActions.ownerArtifacts.repairArtifacts', '最新提号表单索引必须保留 owner 级 repairArtifacts,避免负责人文件和索引信息不一致。'),
  205. check('latest-form-index-next-actions-top-actions', '最新提号表单索引记录下一步行动队列摘要', hasLatestFormNextActionTopActions(latestFormIndex, businessProofNextActions), 'latestFormIndex.nextActions.topActions', '最新提号表单索引必须展示 actionCount、proofGapOpenCount 和前 5 个高优先级动作,避免只看到负责人附件却看不到首批动作。'),
  206. check('latest-form-index-proof-gap-operator-pack', '最新提号表单索引记录补证操作包入口', hasLatestFormProofGapOperatorPack(latestFormIndex, proofGapOperatorPack), 'latestFormIndex.proofGapOperatorPack', '最新提号表单索引必须展示 proof-gap-operator-pack.md/csv、5 个 gapId 和复验命令数。'),
  207. check('latest-form-index-longrun-readiness', '最新提号表单索引记录长跑前置门禁入口', hasLatestFormLongRunReadiness(latestFormIndex, longRunReadiness), 'latestFormIndex.longRunReadiness', '最新提号表单索引必须展示 long-run-readiness-report.md/summary.json、ready、failCount 和 not_business_proof 边界。'),
  208. check('local-seed-to-intake-worklist-summary', '本地种子转 intake 补表 worklist summary 存在', Boolean(localSeedToIntakeWorklist), rel(root, resolvedLocalSeedToIntakeWorklistPath), '运行 npm run local-seed-to-intake:worklist -- --outputs outputs --output outputs\\local-seed-to-intake-worklist-latest。'),
  209. check('local-seed-to-intake-worklist-boundary', '本地种子转 intake 补表 worklist 保留非证明边界', hasLocalSeedToIntakeWorklist(localSeedToIntakeWorklist), 'localSeedToIntakeWorklist.counts/directCustomerProof/canCloseProofGap', 'worklist 必须记录 history/candidate/video 行数,并保持 directCustomerProof=false、canCloseProofGap=false,避免把补表草稿误当业务证明。'),
  210. check('latest-form-index-local-seed-to-intake-worklist', '最新提号表单索引记录本地种子转补表 worklist', hasLatestFormLocalSeedToIntakeWorklist(latestFormIndex, localSeedToIntakeWorklist), 'latestFormIndex.localSeedToIntakeWorklist', '最新提号表单索引必须展示 local-seed-to-intake-worklist 的 md/csv/history/video/candidate 文件和非证明边界。'),
  211. check('real-gap-material-audit-summary', 'three real gap material audit summary exists', Boolean(realGapMaterialAudit), rel(root, resolvedRealGapMaterialAuditPath), 'Run npm run real-gap:materials -- --outputs outputs --output outputs\\real-gap-material-audit-latest --strict.'),
  212. check('real-gap-material-audit-boundary', 'three real gap material audit keeps proof boundary', hasRealGapMaterialAudit(realGapMaterialAudit), 'realGapMaterialAudit.gapCount/materialFoundCount/canCloseProofGap', 'The audit must show materials for the three real gaps while keeping directCustomerProof=false and canCloseProofGap=false.'),
  213. check('latest-form-index-real-gap-material-audit', 'latest form index records three real gap material audit', hasLatestFormRealGapMaterialAudit(latestFormIndex, realGapMaterialAudit), 'latestFormIndex.realGapMaterialAudit', 'The latest form index must expose the material audit so testers do not miss the project-material coverage evidence.'),
  214. check('real-proof-closure-work-order-summary', '真实证明闭环工单 summary 存在', Boolean(realProofClosureWorkOrder), rel(root, resolvedRealProofClosureWorkOrderPath), '运行 npm run real-proof:work-order -- --outputs outputs --output outputs\\real-proof-closure-work-order-latest --strict。'),
  215. check('real-proof-closure-work-order-boundary', '真实证明闭环工单保留非证明边界', hasRealProofClosureWorkOrder(realProofClosureWorkOrder), 'realProofClosureWorkOrder.boundary/workOrderCount', '工单必须保持 proofLevel=not_business_proof、directCustomerProof=false、canCloseProofGap=false、requiresHumanRealMaterial=true,并记录未关闭 proof-gap。'),
  216. check('real-proof-closure-work-order-actionable', '真实证明闭环工单拆到负责人附件和字段级动作', hasActionableRealProofClosureWorkOrder(realProofClosureWorkOrder), 'realProofClosureWorkOrder.workOrders/ownerArtifacts', '工单必须至少输出 5 条字段级补证动作和 3 份负责人 md/csv 附件,避免只新增汇总说明。'),
  217. check('real-proof-closure-work-order-video-ab-preflight-context', '真实证明闭环工单记录 video A/B preflight 非证明上下文', hasRealProofClosureWorkOrderVideoAbPreflightContext(realProofClosureWorkOrder), 'realProofClosureWorkOrder.workOrders.video-ab-live-proof.preflight', '闭环工单必须把 video A/B preflight 失败原因和 live proof 缺失要求拆到技术/AI 工单,不能只给 acceptance:video-ab 命令。'),
  218. check('latest-form-index-real-proof-closure-work-order', '最新提号表单索引记录真实证明闭环工单', hasLatestFormRealProofClosureWorkOrder(latestFormIndex, realProofClosureWorkOrder), 'latestFormIndex.realProofClosureWorkOrder', '最新提号表单索引必须展示 real-proof-closure-work-order 的 md/csv/summary、负责人附件、行数和非证明边界。'),
  219. check('handoff-real-proof-closure-work-order', '交接摘要顶层记录真实证明闭环工单', hasHandoffRealProofClosureWorkOrder(handoff, realProofClosureWorkOrder), 'handoff.realProofClosureWorkOrder', '交接摘要必须顶层展示真实证明闭环工单的文件、行数、负责人附件和不能关闭 proof-gap 边界。'),
  220. check('handoff-latest-form-real-proof-closure-work-order', '交接摘要最新表单索引记录真实证明闭环工单', hasHandoffLatestFormRealProofClosureWorkOrder(handoff, latestFormIndex), 'handoff.latestFormIndex.realProofClosureWorkOrder', '交接摘要必须透传最新表单索引里的真实证明闭环工单,避免下一轮只看交接摘要时漏掉字段级派工。'),
  221. check('evidence-index-real-proof-closure-work-order', '证据台账记录真实证明闭环工单非业务证明类型', Number(evidence?.byType?.['real-proof-closure-work-order'] || 0) >= 1, 'evidence.byType.real-proof-closure-work-order', '证据台账必须把真实证明闭环工单归类为 not_business_proof,避免被误当业务证明。'),
  222. check('evidence-index-video-ab-runtime-preflight-boundary', '证据台账记录视频 A/B 前置失败不能当业务证明', hasVideoRuntimePreflightBoundary(evidence), 'evidence.entries.video-ab-runtime-preflight', '如果存在 video-hit-rate-preflight-summary.json,证据台账必须归类为 not_business_proof,不能替代 acceptance:video-ab live proof。'),
  223. check('handoff-latest-form-index', '交接摘要记录最新提号表单索引', hasHandoffLatestFormIndex(handoff, latestFormIndex), 'handoff.latestFormIndex', '交接摘要必须展示 latest-form-index.md,避免下一轮继续追问最新表单在哪里。'),
  224. check('handoff-latest-form-index-next-actions-top-actions', '交接摘要最新表单索引记录下一步行动队列摘要', hasHandoffLatestFormNextActionTopActions(handoff, latestFormIndex), 'handoff.latestFormIndex.topActions', '交接摘要必须透传最新表单索引里的 topActions,避免下一轮只看交接摘要时看不到首批补证动作。'),
  225. check('handoff-local-seed-to-intake-worklist', '交接摘要顶层记录本地种子转补表 worklist', hasHandoffLocalSeedToIntakeWorklist(handoff, localSeedToIntakeWorklist), 'handoff.localSeedToIntakeWorklist', '交接摘要必须顶层展示 local-seed-to-intake-worklist 的文件、行数和不能关闭 proof-gap 边界。'),
  226. check('handoff-latest-form-local-seed-to-intake-worklist', '交接摘要最新表单索引记录本地种子转补表 worklist', hasHandoffLatestFormLocalSeedToIntakeWorklist(handoff, latestFormIndex), 'handoff.latestFormIndex.localSeedToIntakeWorklist', '交接摘要必须透传最新表单索引里的 worklist,避免下一轮只看交接摘要时漏掉可补表草稿。'),
  227. check('handoff-real-gap-material-audit', 'handoff top-level records three real gap material audit', hasHandoffRealGapMaterialAudit(handoff, realGapMaterialAudit), 'handoff.realGapMaterialAudit', 'The handoff must expose material coverage for the three still-open gaps while keeping the proof boundary.'),
  228. check('handoff-latest-form-real-gap-material-audit', 'handoff latest form records three real gap material audit', hasHandoffLatestFormRealGapMaterialAudit(handoff, latestFormIndex), 'handoff.latestFormIndex.realGapMaterialAudit', 'The handoff latest-form section must pass through the material audit so the next tester sees it.'),
  229. check('handoff-latest-form-index-intake-owner-recheck', '交接摘要记录最新表单索引字段补齐负责人和复验命令', hasHandoffLatestFormIndexIntakeOwnerRecheck(handoff, latestFormIndex), 'handoff.latestFormIndex.intakeFieldChecklist', '交接摘要必须记录最新表单索引中的 ownerGroupCount 和 recheckCommandCount,避免下一轮 AI 看不到软件端补齐顺序。'),
  230. check('handoff-latest-form-business-fill-guide', '交接摘要记录商务填表执行视图', hasHandoffLatestFormBusinessFillGuide(handoff, latestFormIndex), 'handoff.latestFormIndex.businessFillGuide', '交接摘要必须记录最新表单索引中的商务填表执行视图,避免下一轮 AI 只看到表单入口却看不到填表顺序。'),
  231. check('handoff-longrun-readiness', '交接摘要顶层记录长跑前置门禁入口', hasHandoffLongRunReadiness(handoff, longRunReadiness), 'handoff.longRunReadiness', '交接摘要必须顶层展示长跑前置门禁,避免 full-matrix/live 长跑启动条件只藏在 round-refresh 中。'),
  232. check('handoff-latest-form-longrun-readiness', '交接摘要最新表单索引记录长跑前置门禁入口', hasHandoffLatestFormLongRunReadiness(handoff, latestFormIndex), 'handoff.latestFormIndex.longRunReadiness', '交接摘要必须透传最新表单索引中的长跑前置门禁入口。'),
  233. check('handoff-latest-form-repair-actions', '交接摘要记录最新修复清单入口', hasHandoffLatestFormRepairActions(handoff), 'handoff.latestFormIndex.repairActions', '交接摘要必须展示 intake/video 修复清单 CSV,避免下一轮 AI 只看到缺口却不知道如何派工。'),
  234. check('handoff-proof-gap-operator-pack', '交接摘要记录提号补证操作包入口', hasHandoffProofGapOperatorPack(handoff, proofGapOperatorPack), 'handoff.proofGapOperatorPack', '交接摘要必须展示 proof-gap-operator-pack.md/csv、5 个 gapId、ownerGroupCount 和复验命令数。'),
  235. check('handoff-support-docs', '交接摘要记录中文 SOP 入口', hasHandoffSupportDocs(handoff), 'handoff.supportDocs', '交接摘要必须指向中文长期优化计划和中文补证操作顺序,避免下一轮 AI 或商务打开旧文档。'),
  236. check('handoff-owner-repair-actions', '交接摘要记录负责人修复附件', hasOwnerRepairArtifacts(handoff?.businessProofNextActions?.ownerArtifacts), 'handoff.businessProofNextActions.ownerArtifacts.repairArtifacts', '交接摘要必须保留 owner 级 repairArtifacts,避免下一轮 AI 只看到负责人文件却看不到修复附件。'),
  237. check('business-proof-action-order', '补证操作顺序文档存在且可执行', hasProofActionOrder(proofActionOrderText), rel(root, resolvedProofActionOrderPath), '补充 docs/business-proof-action-order.zh-CN.md,明确商务、投放、技术和 AI 的真实补证顺序、命令和通过标准。'),
  238. check('proof-gap-closure-summary', '最新 proof-gap-closure-summary.json 存在', Boolean(proofGapClosure), rel(root, resolvedProofGapClosurePath), '运行 npm run proof-gap:closure -- --output outputs\\proof-gap-closure-latest。'),
  239. check('proof-gap-closure-report', '最新 proof-gap-closure-report.md 存在', fileExists(sibling(resolvedProofGapClosurePath, 'proof-gap-closure-report.md')), rel(root, sibling(resolvedProofGapClosurePath, 'proof-gap-closure-report.md')), '运行 npm run proof-gap:closure。'),
  240. check('proof-gap-closure-open-recorded', 'proof-gap closure 记录未关闭缺口', Number(proofGapClosure?.openCount || 0) >= 1 && proofGapClosure?.complete === false, 'proofGapClosure.openCount', '长期目标未完成时必须记录 proof gap closure openCount。'),
  241. check('proof-gap-closure-count-semantics', 'proof-gap closure 明确业务缺口与预审行计数口径', hasProofGapClosureCountSemantics(proofGapClosure), 'proofGapClosure.businessOpenCount/precheckOpenCount/countSemantics', 'proof-gap closure 必须把 openCount 标为业务证明缺口计数,并把 intake 预审行单独记录,避免交接时把预审行误算进业务 openCount。'),
  242. check('proof-gap-closure-missing-proof-requirements', 'proof-gap closure 逐缺口记录缺失证明要求', hasProofGapClosureMissingProofRequirements(proofGapClosure), 'proofGapClosure.rows.missingProofRequirements', 'proof-gap closure 必须让每个未关闭业务缺口都列出缺失证明要求,避免只知道缺口未关闭却不知道差哪条证明。'),
  243. check('handoff-proof-gap-closure', '交接摘要记录真实证明缺口关闭状态', hasHandoffProofGapClosure(handoff, proofGapClosure), 'handoff.proofGapClosure', '交接摘要必须记录 proofGapClosure.openCount、rows 和 video-ab-live-proof 的 missingProofRequirements,避免下一轮漏判真实证明缺口。'),
  244. check('feedback-loop-closure-summary', '最新 feedback-loop-closure-summary.json 存在', Boolean(feedbackLoopClosure), rel(root, resolvedFeedbackLoopClosurePath), '运行 npm run feedback:closure -- --output outputs\\feedback-loop-closure-latest。'),
  245. check('feedback-loop-closure-boundary', '反馈二轮闭环审计保留客户证明边界', hasFeedbackLoopClosure(feedbackLoopClosure), 'feedbackLoopClosure.directCustomerProof/complete', '反馈闭环审计必须记录 directCustomerProof=false 或真实证明上下文,不能把 sample 二轮闭环误报为客户效果。'),
  246. check('handoff-feedback-loop-closure', '交接摘要记录反馈二轮闭环审计入口', hasHandoffFeedbackLoopClosure(handoff, feedbackLoopClosure), 'handoff.feedbackLoopClosure', '交接摘要必须展示 feedback-loop-closure summary/report/csv 和 directCustomerProof 边界。'),
  247. check('homepage-evidence-readiness-summary', '最新 homepage-evidence-readiness-summary.json 存在', Boolean(homepageEvidenceReadiness), rel(root, resolvedHomepageEvidenceReadinessPath), '运行 npm run homepage:readiness -- --output outputs\\homepage-evidence-readiness-latest。'),
  248. check('homepage-evidence-readiness-report', '最新 homepage-evidence-readiness-report.md 存在', fileExists(sibling(resolvedHomepageEvidenceReadinessPath, 'homepage-evidence-readiness-report.md')), rel(root, sibling(resolvedHomepageEvidenceReadinessPath, 'homepage-evidence-readiness-report.md')), '运行 npm run homepage:readiness。'),
  249. check('homepage-evidence-readiness-csv', '最新 homepage-evidence-repair-actions.csv 存在', fileExists(sibling(resolvedHomepageEvidenceReadinessPath, 'homepage-evidence-repair-actions.csv')), rel(root, sibling(resolvedHomepageEvidenceReadinessPath, 'homepage-evidence-repair-actions.csv')), '运行 npm run homepage:readiness。'),
  250. check('homepage-evidence-readiness-boundary', '主页证据就绪审计保留客户证明边界', hasHomepageEvidenceReadiness(homepageEvidenceReadiness), 'homepageEvidenceReadiness.directCustomerProof/proofLevel', '主页 readiness 必须记录 directCustomerProof=false,且没有 provider/path 近期内容时标记 not_business_proof。'),
  251. check('handoff-homepage-evidence-readiness', '交接摘要记录主页证据就绪审计入口', hasHandoffHomepageEvidenceReadiness(handoff, homepageEvidenceReadiness), 'handoff.homepageEvidenceReadiness', '交接摘要必须展示 homepage-evidence-readiness summary/report/csv 和 directCustomerProof 边界。'),
  252. check('commands-recorded', '交接摘要记录下一轮命令', Array.isArray(handoff?.nextCommands) && handoff.nextCommands.length >= 5, 'handoff.nextCommands', '补充历史导入、复核、客户效果、证据台账和交接命令。'),
  253. check('closure-command-recorded', '交接摘要记录 proof-gap closure 命令', Array.isArray(handoff?.nextCommands) && handoff.nextCommands.some(item => String(item.command || '').includes('proof-gap:closure')), 'handoff.nextCommands.proof-gap:closure', '交接摘要必须提示用 proof-gap:closure 判断真实证明缺口是否关闭。'),
  254. check('proof-boundaries', '交接摘要记录未证明边界', Array.isArray(handoff?.guardrails) && handoff.guardrails.some(item => item.includes('不能把 sample')) && handoff.guardrails.some(item => item.includes('客户效果')), 'handoff.guardrails', '补充 sample/smoke/live/customer-effect 的边界说明。'),
  255. check('status-blockers', '状态审计保留 ready_not_proven 和 blocked_by_external_data', Number(status?.counts?.ready_not_proven || 0) >= 1 && Number(status?.counts?.blocked_by_external_data || 0) >= 1 && status?.complete === false, 'status.counts', '缺少真实证明时不得把长期目标标为完成。'),
  256. check('evidence-levels', '证据台账区分真实证据、本地证据和非证明', hasEvidenceLevels(evidence), 'evidence.counts', '证据台账必须保留 real_evidence、smoke_or_local、not_business_proof。'),
  257. check('software-status', '状态审计覆盖软件端去重和排名连续性', hasStatusId(status, 'software-table'), 'status.items.software-table', '软件端表格必须继续受状态审计覆盖。'),
  258. check('video-status', '状态审计覆盖视频资源真实性', hasStatusId(status, 'video-ab-proof'), 'status.items.video-ab-proof', '视频 A/B 必须继续区分真实资源和 smoke。'),
  259. check('customer-status', '状态审计覆盖客户效果证明', hasStatusId(status, 'customer-effect-proof'), 'status.items.customer-effect-proof', '客户选择率和人工补号减少必须独立审计。'),
  260. check('latest-handoff-incomplete', '交接摘要未把未证明状态误报为完成', handoff?.complete === false, 'handoff.complete=false', '缺少真实客户效果证明时交接摘要必须保持 incomplete。')
  261. ];
  262. checks.push(
  263. check('manual-review-label-guide-summary', '人工复核标签与归因指南 summary 存在', Boolean(manualReviewLabelGuide), rel(root, resolvedManualReviewLabelGuidePath), '运行 npm run manual-review:label-guide -- --output outputs\\manual-review-label-guide-latest --strict。'),
  264. check('manual-review-label-guide-passed', '人工复核标签与归因指南通过并保留证明边界', hasManualReviewLabelGuide(manualReviewLabelGuide), 'manualReviewLabelGuide.labels/attributionTypes/directCustomerProof', '指南必须包含正负标签、负样本归因类型、negativeLabelsRequireAttribution=true,且 directCustomerProof=false。'),
  265. check('latest-form-index-manual-review-label-guide', '最新提号表单索引记录人工复核标签指南', hasLatestFormManualReviewLabelGuide(latestFormIndex, manualReviewLabelGuide), 'latestFormIndex.manualReviewLabelGuide', '最新提号表单索引必须展示 manual-review-label-guide.md/csv,方便商务按统一标签和归因填写复核表。'),
  266. check('handoff-latest-form-manual-review-label-guide', '交接摘要记录人工复核标签指南', hasHandoffLatestFormManualReviewLabelGuide(handoff, latestFormIndex), 'handoff.latestFormIndex.manualReviewLabelGuide', '交接摘要必须记录最新表单索引中的复核标签指南,避免下一轮 AI 或商务不知道负样本归因口径。')
  267. );
  268. checks.push(
  269. check('plan-execution-status-summary', '长期计划执行状态面板 summary 存在', Boolean(planExecutionStatus), rel(root, resolvedPlanExecutionStatusPath), '运行 npm run plan:execution-status -- --output outputs\\plan-execution-status-latest --strict。'),
  270. check('plan-execution-status-boundary', '长期计划执行状态面板记录 8 个章节和未完成边界', hasPlanExecutionStatus(planExecutionStatus, proofGapClosure), 'planExecutionStatus.rowCount/complete/proofOpenCount/guardrails', '状态面板必须 rowCount=8、complete=false、proofOpenCount 对齐 proof-gap-closure,并保留不得宣称客户效果的边界。'),
  271. check('plan-execution-status-operator-owner-artifacts', '长期计划执行状态面板记录操作包负责人附件', hasPlanExecutionOperatorOwnerArtifacts(planExecutionStatus, proofGapOperatorPack, businessExecutionIndex), 'planExecutionStatus.operatorPackOwnerArtifacts', '计划执行状态面板必须透传三份 proof-gap 操作包负责人附件和商务执行总表附件行数。'),
  272. check('latest-form-index-plan-execution-status', '最新提号表单索引记录长期计划执行状态面板', hasLatestFormPlanExecutionStatus(latestFormIndex, planExecutionStatus), 'latestFormIndex.planExecutionStatus', '最新提号表单索引必须展示 plan-execution-status.md/csv/summary,方便商务和下一轮 AI 直接看到优化计划完成度。'),
  273. check('handoff-plan-execution-status', '交接摘要记录长期计划执行状态面板', hasHandoffPlanExecutionStatus(handoff, planExecutionStatus), 'handoff.planExecutionStatus', '交接摘要必须展示 plan-execution-status.md/csv/summary,避免下一轮 AI 不知道当前计划完成度。')
  274. );
  275. const requiredLogHints = [
  276. ['命令记录', /npm\.cmd run|npm run|命令/],
  277. ['输出目录', /outputs[\\/]|输出/],
  278. ['live 标记', /liveEnabled|live 标记|live/],
  279. ['严格 provider 门禁', /strict provider|provider 门禁|严格 provider/],
  280. ['视频资源真实性', /真实视频|video|视频/],
  281. ['失败计数', /failureCount|failedGateCount|overallPass/],
  282. ['软件端重复和排名', /重复键|排名连续|software/],
  283. ['复核或客户指标', /人工复核|客户选中率|customer-effect|review:metrics/],
  284. ['未证明边界', /ready_not_proven|blocked_by_external_data|仍需真实|未证明/],
  285. ['proof-gap 缺口', /proof-gap|真实证明缺口|未解决缺口/]
  286. ];
  287. for (const [title, pattern] of requiredLogHints) {
  288. checks.push(check(`log-${slug(title)}`, `实施日志记录${title}`, pattern.test(logText), 'docs/tihao-experience-implementation-log.md', `在实施日志本轮小结中补充${title}。`));
  289. }
  290. const counts = countChecks(checks);
  291. return {
  292. generatedAt: new Date().toISOString(),
  293. root,
  294. outputsDir,
  295. passed: counts.fail === 0,
  296. counts,
  297. sources: {
  298. implementationLog: rel(root, implementationLog),
  299. statusSummary: rel(root, resolvedStatusPath),
  300. evidenceSummary: rel(root, resolvedEvidencePath),
  301. handoffSummary: rel(root, resolvedHandoffPath),
  302. proofGapSummary: rel(root, resolvedProofGapPath)
  303. , proofGapClosureSummary: rel(root, resolvedProofGapClosurePath),
  304. proofGapOperatorPackSummary: rel(root, resolvedProofGapOperatorPackPath),
  305. feedbackLoopClosureSummary: rel(root, resolvedFeedbackLoopClosurePath),
  306. homepageEvidenceReadinessSummary: rel(root, resolvedHomepageEvidenceReadinessPath),
  307. intakeReadinessSummary: rel(root, resolvedIntakeReadinessPath),
  308. longRunReadinessSummary: rel(root, resolvedLongRunReadinessPath),
  309. businessProofProgressSummary: rel(root, resolvedBusinessProofProgressPath),
  310. businessProofNextActionsSummary: rel(root, resolvedBusinessProofNextActionsPath),
  311. businessExecutionIndexSummary: rel(root, resolvedBusinessExecutionIndexPath),
  312. optimizationCompletionSummary: rel(root, resolvedOptimizationCompletionPath),
  313. realProofIntakeBundleSummary: rel(root, resolvedRealProofIntakeBundlePath),
  314. intakeFieldChecklistSummary: rel(root, resolvedIntakeFieldChecklistPath),
  315. manualReviewLabelGuideSummary: rel(root, resolvedManualReviewLabelGuidePath),
  316. proofGapSoftwareFormSummary: rel(root, resolvedProofGapSoftwareFormPath),
  317. latestFormIndexSummary: rel(root, resolvedLatestFormIndexPath),
  318. localSeedToIntakeWorklistSummary: rel(root, resolvedLocalSeedToIntakeWorklistPath),
  319. realGapMaterialAuditSummary: rel(root, resolvedRealGapMaterialAuditPath),
  320. realProofClosureWorkOrderSummary: rel(root, resolvedRealProofClosureWorkOrderPath),
  321. planExecutionStatusSummary: rel(root, resolvedPlanExecutionStatusPath),
  322. proofActionOrder: rel(root, resolvedProofActionOrderPath)
  323. },
  324. observed: {
  325. statusCounts: status?.counts || {},
  326. evidenceCounts: evidence?.counts || {},
  327. evidenceByType: evidence?.byType || {},
  328. evidenceVideoRuntimePreflightCount: Number(evidence?.byType?.['video-ab-runtime-preflight'] || 0),
  329. proofGapClosureHistorySource: proofGapClosure?.sourceFiles?.history || '',
  330. proofGapClosureCustomerEffectSource: proofGapClosure?.sourceFiles?.customerEffect || '',
  331. proofGapClosureRecursiveProofArtifactDiscovery: hasProofGapClosureRecursiveProofArtifactDiscovery(proofGapClosure),
  332. proofGapClosureVideoAbPreflightReadyForVideoAb: Boolean(proofGapClosure?.videoAbPreflightReadyForVideoAb),
  333. proofGapClosureVideoAbPreflightFailureCount: Number.isFinite(Number(proofGapClosure?.videoAbPreflightFailureCount)) ? Number(proofGapClosure.videoAbPreflightFailureCount) : null,
  334. handoffComplete: Boolean(handoff?.complete),
  335. handoffReadyCapabilities: handoff?.readyCapabilities?.length || 0,
  336. handoffReadyNotProven: handoff?.readyNotProven?.length || 0,
  337. handoffExternalBlockers: handoff?.externalBlockers?.length || 0,
  338. proofGapUnresolvedCount: Number(proofGap?.unresolvedCount || 0),
  339. proofGapTemplateRowsReady,
  340. proofGapTemplateColumnsReady,
  341. proofGapOperatorPackPassed: Boolean(proofGapOperatorPack?.passed),
  342. proofGapOperatorPackOpenCount: Number.isFinite(Number(proofGapOperatorPack?.openCount)) ? Number(proofGapOperatorPack.openCount) : null,
  343. proofGapOperatorPackRowCount: Number.isFinite(Number(proofGapOperatorPack?.rowCount)) ? Number(proofGapOperatorPack.rowCount) : null,
  344. proofGapOperatorPackOwnerGroupCount: Number.isFinite(Number(proofGapOperatorPack?.ownerGroupCount)) ? Number(proofGapOperatorPack.ownerGroupCount) : (Array.isArray(proofGapOperatorPack?.ownerGroups) ? proofGapOperatorPack.ownerGroups.length : null),
  345. proofGapOperatorPackOwnerArtifactCount: Number.isFinite(Number(proofGapOperatorPack?.ownerArtifactCount)) ? Number(proofGapOperatorPack.ownerArtifactCount) : (Array.isArray(proofGapOperatorPack?.ownerArtifacts) ? proofGapOperatorPack.ownerArtifacts.length : null),
  346. proofGapOperatorPackFillFileCount: Number.isFinite(Number(proofGapOperatorPack?.fillFileCount)) ? Number(proofGapOperatorPack.fillFileCount) : uniqueCount(proofGapOperatorPack?.rows, row => row.fillFiles),
  347. proofGapOperatorPackRecheckCommandCount: Number.isFinite(Number(proofGapOperatorPack?.recheckCommandCount)) ? Number(proofGapOperatorPack.recheckCommandCount) : uniqueCount(proofGapOperatorPack?.rows, row => row.recheckCommands),
  348. proofGapOperatorPackEvidenceRowCount: Number.isFinite(Number(proofGapOperatorPack?.evidenceRowCount)) ? Number(proofGapOperatorPack.evidenceRowCount) : countRowsWith(proofGapOperatorPack?.rows, row => row.evidence),
  349. proofGapOperatorPackNextActionCount: Number.isFinite(Number(proofGapOperatorPack?.nextActionCount)) ? Number(proofGapOperatorPack.nextActionCount) : countRowsWith(proofGapOperatorPack?.rows, row => row.nextAction),
  350. proofGapOperatorPackMissingProofRequirementCount: Number.isFinite(Number(proofGapOperatorPack?.missingProofRequirementCount)) ? Number(proofGapOperatorPack.missingProofRequirementCount) : uniqueCount(proofGapOperatorPack?.rows, row => row.missingProofRequirements),
  351. proofGapOperatorPackOpenRowsWithMissingProofRequirements: countOpenRowsWithMissingProofRequirements(proofGapOperatorPack?.rows, 'gapId'),
  352. proofGapOperatorPackVideoAbPreflightContext: hasProofGapOperatorPackVideoAbPreflightContext(proofGapOperatorPack),
  353. proofGapOperatorPackGapIds: proofGapOperatorGapIds(proofGapOperatorPack).join(','),
  354. proofGapOperatorPackDirectCustomerProof: Boolean(proofGapOperatorPack?.directCustomerProof),
  355. intakeReadinessReady: Boolean(intakeReadiness?.acceptance?.overallReady),
  356. intakeReadinessFailureCount: intakeReadiness ? Number(intakeReadiness.failureCount || 0) : null,
  357. longRunReadinessReady: Boolean(longRunReadiness?.ready),
  358. longRunReadinessFailCount: longRunReadiness ? Number(longRunReadiness.counts?.fail || 0) : null,
  359. longRunReadinessMode: longRunReadiness?.mode || '',
  360. businessProofProgressComplete: Boolean(businessProofProgress?.complete),
  361. businessProofProgressCounts: businessProofProgress?.counts || {},
  362. businessProofProgressOperatorPackOwnerArtifactCount: Number.isFinite(Number(businessProofProgress?.operatorPackOwnerArtifactCount)) ? Number(businessProofProgress.operatorPackOwnerArtifactCount) : (Array.isArray(businessProofProgress?.operatorPackOwnerArtifacts) ? businessProofProgress.operatorPackOwnerArtifacts.length : null),
  363. handoffBusinessProofProgressOperatorPackOwnerArtifactCount: Number.isFinite(Number(handoff?.businessProofProgress?.operatorPackOwnerArtifactCount)) ? Number(handoff.businessProofProgress.operatorPackOwnerArtifactCount) : (Array.isArray(handoff?.businessProofProgress?.operatorPackOwnerArtifacts) ? handoff.businessProofProgress.operatorPackOwnerArtifacts.length : null),
  364. businessProofNextActionsComplete: Boolean(businessProofNextActions?.complete),
  365. businessProofNextActionsCount: Number.isFinite(Number(businessProofNextActions?.actionCount)) ? Number(businessProofNextActions.actionCount) : (Array.isArray(businessProofNextActions?.actions) ? businessProofNextActions.actions.length : null),
  366. businessProofNextActionsTopCount: Array.isArray(businessProofNextActions?.topActions) ? businessProofNextActions.topActions.length : null,
  367. businessProofNextActionsTop: Array.isArray(businessProofNextActions?.topActions) && businessProofNextActions.topActions[0]
  368. ? businessProofNextActions.topActions[0].title
  369. : (Array.isArray(businessProofNextActions?.actions) && businessProofNextActions.actions[0] ? businessProofNextActions.actions[0].title : ''),
  370. businessProofNextActionsWithMissingProofRequirements: countRowsWithMissingProofRequirements(businessProofNextActions?.actions),
  371. handoffBusinessProofNextActionsCount: Number.isFinite(Number(handoff?.businessProofNextActions?.actionCount)) ? Number(handoff.businessProofNextActions.actionCount) : null,
  372. handoffBusinessProofNextActionsTopCount: Array.isArray(handoff?.businessProofNextActions?.topActions) ? handoff.businessProofNextActions.topActions.length : null,
  373. handoffBusinessProofNextActionsProofGapOpenCount: Number.isFinite(Number(handoff?.businessProofNextActions?.sourceState?.proofGapOpenCount)) ? Number(handoff.businessProofNextActions.sourceState.proofGapOpenCount) : null,
  374. optimizationCompletionComplete: Boolean(optimizationCompletion?.complete),
  375. optimizationCompletionReadyForClaim: Boolean(optimizationCompletion?.readyForClaim),
  376. optimizationCompletionProofGapOpenCount: Number.isFinite(Number(optimizationCompletion?.proofGapOpenCount)) ? Number(optimizationCompletion.proofGapOpenCount) : null,
  377. optimizationCompletionBlockingReasonCount: Array.isArray(optimizationCompletion?.blockingReasons) ? optimizationCompletion.blockingReasons.length : (Number.isFinite(Number(optimizationCompletion?.blockingReasonCount)) ? Number(optimizationCompletion.blockingReasonCount) : null),
  378. optimizationCompletionBlockingReasonsWithMissingProofRequirements: countRowsWithMissingProofRequirements(optimizationCompletion?.blockingReasons),
  379. optimizationCompletionMissingProofRequirementCount: uniqueCount(optimizationCompletion?.blockingReasons, row => row.missingProofRequirements),
  380. optimizationCompletionCanClaimTihaoRateImproved: Boolean(optimizationCompletion?.canClaim?.tihaoRateImproved),
  381. optimizationCompletionCanClaimCustomerEffectAchieved: Boolean(optimizationCompletion?.canClaim?.customerEffectAchieved),
  382. optimizationCompletionCanClaimManualSupplementReduced: Boolean(optimizationCompletion?.canClaim?.manualSupplementReduced),
  383. optimizationCompletionHistorySource: optimizationCompletion?.sourceFiles?.history || '',
  384. optimizationCompletionCustomerEffectSource: optimizationCompletion?.sourceFiles?.customerEffect || '',
  385. optimizationCompletionRecursiveProofArtifactDiscovery: hasOptimizationCompletionRecursiveProofArtifactDiscovery(optimizationCompletion),
  386. optimizationCompletionVideoAbPreflightReadyForVideoAb: Boolean(optimizationCompletion?.videoAbPreflightReadyForVideoAb),
  387. optimizationCompletionVideoAbPreflightFailureCount: Number.isFinite(Number(optimizationCompletion?.videoAbPreflightFailureCount)) ? Number(optimizationCompletion.videoAbPreflightFailureCount) : null,
  388. businessExecutionIndexPassed: Boolean(businessExecutionIndex?.passed),
  389. businessExecutionIndexRowCount: Number.isFinite(Number(businessExecutionIndex?.rowCount)) ? Number(businessExecutionIndex.rowCount) : null,
  390. businessExecutionIndexActionRowCount: Number.isFinite(Number(businessExecutionIndex?.actionRowCount)) ? Number(businessExecutionIndex.actionRowCount) : null,
  391. businessExecutionIndexRepairRowCount: Number.isFinite(Number(businessExecutionIndex?.repairRowCount)) ? Number(businessExecutionIndex.repairRowCount) : null,
  392. businessExecutionIndexOperatorPackOwnerArtifactRowCount: Number.isFinite(Number(businessExecutionIndex?.operatorPackOwnerArtifactRowCount)) ? Number(businessExecutionIndex.operatorPackOwnerArtifactRowCount) : null,
  393. businessExecutionIndexOperatorPackOwnerArtifactCount: Number.isFinite(Number(businessExecutionIndex?.operatorPackOwnerArtifactCount)) ? Number(businessExecutionIndex.operatorPackOwnerArtifactCount) : (Array.isArray(businessExecutionIndex?.operatorPackOwnerArtifacts) ? businessExecutionIndex.operatorPackOwnerArtifacts.length : null),
  394. businessExecutionIndexFieldOwnerRowCount: Number.isFinite(Number(businessExecutionIndex?.fieldOwnerRowCount)) ? Number(businessExecutionIndex.fieldOwnerRowCount) : null,
  395. businessExecutionIndexRecheckCommandRowCount: Number.isFinite(Number(businessExecutionIndex?.recheckCommandRowCount)) ? Number(businessExecutionIndex.recheckCommandRowCount) : null,
  396. businessExecutionIndexProofOpenCount: Number.isFinite(Number(businessExecutionIndex?.proofOpenCount)) ? Number(businessExecutionIndex.proofOpenCount) : null,
  397. businessExecutionIndexDirectCustomerProof: Boolean(businessExecutionIndex?.directCustomerProof),
  398. businessExecutionIndexLongRunReadinessSummary: businessExecutionIndex?.sourceFiles?.longRunReadiness || '',
  399. businessExecutionIndexLongRunReadinessReport: findExecutionRowTarget(businessExecutionIndex, '长跑前置门禁报告'),
  400. businessExecutionIndexLongRunReadinessReady: findExecutionRow(businessExecutionIndex, '长跑前置门禁报告')?.status === 'ready',
  401. businessExecutionIndexLongRunReadinessFailCount: Number.isFinite(Number(findExecutionRow(businessExecutionIndex, '长跑前置门禁报告')?.actionCount)) ? Number(findExecutionRow(businessExecutionIndex, '长跑前置门禁报告').actionCount) : null,
  402. businessExecutionIndexOptimizationCompletionSummary: businessExecutionIndex?.sourceFiles?.optimizationCompletion || '',
  403. businessExecutionIndexOptimizationCompletionReport: findExecutionRowTarget(businessExecutionIndex, '优化完成度审计报告'),
  404. businessExecutionIndexOptimizationCompletionReadyForClaim: findExecutionRow(businessExecutionIndex, '优化完成度审计报告')?.status === 'ready_for_claim',
  405. businessExecutionIndexOptimizationCompletionBlockingReasonCount: Number.isFinite(Number(findExecutionRow(businessExecutionIndex, '优化完成度审计报告')?.actionCount)) ? Number(findExecutionRow(businessExecutionIndex, '优化完成度审计报告').actionCount) : null,
  406. handoffBusinessExecutionIndexLongRunReadinessReport: handoff?.businessExecutionIndex?.longRunReadiness?.report || '',
  407. handoffBusinessExecutionIndexLongRunReadinessSummary: handoff?.businessExecutionIndex?.longRunReadiness?.summary || '',
  408. handoffBusinessExecutionIndexLongRunReadinessStatus: handoff?.businessExecutionIndex?.longRunReadiness?.status || '',
  409. handoffBusinessExecutionIndexLongRunReadinessReady: Boolean(handoff?.businessExecutionIndex?.longRunReadiness?.ready),
  410. handoffBusinessExecutionIndexLongRunReadinessFailCount: Number.isFinite(Number(handoff?.businessExecutionIndex?.longRunReadiness?.failCount)) ? Number(handoff.businessExecutionIndex.longRunReadiness.failCount) : null,
  411. handoffBusinessExecutionIndexOperatorPackOwnerArtifactCount: Number.isFinite(Number(handoff?.businessExecutionIndex?.operatorPackOwnerArtifactCount)) ? Number(handoff.businessExecutionIndex.operatorPackOwnerArtifactCount) : (Array.isArray(handoff?.businessExecutionIndex?.operatorPackOwnerArtifacts) ? handoff.businessExecutionIndex.operatorPackOwnerArtifacts.length : null),
  412. handoffOptimizationCompletionReport: handoff?.optimizationCompletion?.report || '',
  413. handoffOptimizationCompletionReadyForClaim: Boolean(handoff?.optimizationCompletion?.readyForClaim),
  414. handoffOptimizationCompletionProofGapOpenCount: Number.isFinite(Number(handoff?.optimizationCompletion?.proofGapOpenCount)) ? Number(handoff.optimizationCompletion.proofGapOpenCount) : null,
  415. handoffOptimizationCompletionBlockingReasonCount: Number.isFinite(Number(handoff?.optimizationCompletion?.blockingReasonCount)) ? Number(handoff.optimizationCompletion.blockingReasonCount) : null,
  416. handoffBusinessExecutionIndexOptimizationCompletionReport: handoff?.businessExecutionIndex?.optimizationCompletion?.report || '',
  417. handoffBusinessExecutionIndexOptimizationCompletionSummary: handoff?.businessExecutionIndex?.optimizationCompletion?.summary || '',
  418. handoffBusinessExecutionIndexOptimizationCompletionStatus: handoff?.businessExecutionIndex?.optimizationCompletion?.status || '',
  419. handoffBusinessExecutionIndexOptimizationCompletionReadyForClaim: Boolean(handoff?.businessExecutionIndex?.optimizationCompletion?.readyForClaim),
  420. handoffBusinessExecutionIndexOptimizationCompletionBlockingReasonCount: Number.isFinite(Number(handoff?.businessExecutionIndex?.optimizationCompletion?.blockingReasonCount)) ? Number(handoff.businessExecutionIndex.optimizationCompletion.blockingReasonCount) : null,
  421. realProofIntakeBundlePassed: Boolean(realProofIntakeBundle?.passed),
  422. realProofIntakeBundleComplete: Boolean(realProofIntakeBundle?.complete),
  423. realProofIntakeBundleProofOpenCount: Number.isFinite(Number(realProofIntakeBundle?.proofOpenCount)) ? Number(realProofIntakeBundle.proofOpenCount) : null,
  424. realProofIntakeBundleExportRowCount: Number.isFinite(Number(realProofIntakeBundle?.exportRowCount)) ? Number(realProofIntakeBundle.exportRowCount) : null,
  425. realProofIntakeBundleRecheckCommandCount: Number.isFinite(Number(realProofIntakeBundle?.recheckCommandCount)) ? Number(realProofIntakeBundle.recheckCommandCount) : null,
  426. realProofIntakeBundleOperatorPackEvidenceRowCount: Number.isFinite(Number(realProofIntakeBundle?.operatorPackEvidenceRowCount ?? realProofIntakeBundle?.sourceState?.operatorPackEvidenceRowCount)) ? Number(realProofIntakeBundle.operatorPackEvidenceRowCount ?? realProofIntakeBundle.sourceState.operatorPackEvidenceRowCount) : null,
  427. realProofIntakeBundleOperatorPackNextActionCount: Number.isFinite(Number(realProofIntakeBundle?.operatorPackNextActionCount ?? realProofIntakeBundle?.sourceState?.operatorPackNextActionCount)) ? Number(realProofIntakeBundle.operatorPackNextActionCount ?? realProofIntakeBundle.sourceState.operatorPackNextActionCount) : null,
  428. realProofIntakeBundleOperatorPackMissingProofRequirementCount: Number.isFinite(Number(realProofIntakeBundle?.operatorPackMissingProofRequirementCount ?? realProofIntakeBundle?.sourceState?.operatorPackMissingProofRequirementCount)) ? Number(realProofIntakeBundle.operatorPackMissingProofRequirementCount ?? realProofIntakeBundle.sourceState.operatorPackMissingProofRequirementCount) : null,
  429. realProofIntakeBundleOperatorPackOwnerArtifactCount: Number.isFinite(Number(realProofIntakeBundle?.operatorPackOwnerArtifactCount ?? realProofIntakeBundle?.sourceState?.operatorPackOwnerArtifactCount)) ? Number(realProofIntakeBundle.operatorPackOwnerArtifactCount ?? realProofIntakeBundle.sourceState.operatorPackOwnerArtifactCount) : (Array.isArray(realProofIntakeBundle?.operatorPackOwnerArtifacts) ? realProofIntakeBundle.operatorPackOwnerArtifacts.length : null),
  430. realProofIntakeBundleLongRunReadinessReport: findTemplateFilePath(realProofIntakeBundle, 'long-run-readiness-report.md'),
  431. realProofIntakeBundleLongRunReadinessReady: Boolean(realProofIntakeBundle?.sourceState?.longRunReadinessReady),
  432. realProofIntakeBundleLongRunReadinessFailCount: Number.isFinite(Number(realProofIntakeBundle?.sourceState?.longRunReadinessFailCount)) ? Number(realProofIntakeBundle.sourceState.longRunReadinessFailCount) : null,
  433. realProofIntakeBundleLongRunReadinessProofLevel: realProofIntakeBundle?.sourceState?.longRunReadinessProofLevel || '',
  434. realProofIntakeBundleLongRunReadinessDirectCustomerProof: Boolean(realProofIntakeBundle?.sourceState?.longRunReadinessDirectCustomerProof),
  435. realProofIntakeBundleVideoAbPreflightContext: hasRealProofIntakeBundleVideoAbPreflightContext(realProofIntakeBundle),
  436. intakeFieldChecklistPassed: Boolean(intakeFieldChecklist?.passed),
  437. intakeFieldChecklistItemCount: Number.isFinite(Number(intakeFieldChecklist?.itemCount)) ? Number(intakeFieldChecklist.itemCount) : null,
  438. intakeFieldChecklistMissingRequiredCount: Number.isFinite(Number(intakeFieldChecklist?.missingRequiredCount)) ? Number(intakeFieldChecklist.missingRequiredCount) : null,
  439. intakeFieldChecklistPlaceholderCount: Number.isFinite(Number(intakeFieldChecklist?.placeholderCount)) ? Number(intakeFieldChecklist.placeholderCount) : null,
  440. intakeFieldChecklistDirectCustomerProof: Boolean(intakeFieldChecklist?.directCustomerProof),
  441. intakeFieldChecklistOwnerGroupCount: Array.isArray(intakeFieldChecklist?.ownerGroups) ? intakeFieldChecklist.ownerGroups.length : null,
  442. intakeFieldChecklistRecheckCommandCount: Array.isArray(intakeFieldChecklist?.recheckCommands) ? intakeFieldChecklist.recheckCommands.length : null,
  443. manualReviewLabelGuidePassed: Boolean(manualReviewLabelGuide?.passed),
  444. manualReviewLabelGuideDirectCustomerProof: Boolean(manualReviewLabelGuide?.directCustomerProof),
  445. manualReviewLabelGuideLabelCount: Number.isFinite(Number(manualReviewLabelGuide?.labelCount)) ? Number(manualReviewLabelGuide.labelCount) : null,
  446. manualReviewLabelGuideAttributionTypeCount: Number.isFinite(Number(manualReviewLabelGuide?.attributionTypeCount)) ? Number(manualReviewLabelGuide.attributionTypeCount) : null,
  447. manualReviewLabelGuideNegativeLabelsRequireAttribution: Boolean(manualReviewLabelGuide?.negativeLabelsRequireAttribution),
  448. proofGapSoftwareFormPassed: Boolean(proofGapSoftwareForm?.passed),
  449. proofGapSoftwareFormRowCount: Number.isFinite(Number(proofGapSoftwareForm?.rowCount)) ? Number(proofGapSoftwareForm.rowCount) : null,
  450. proofGapSoftwareFormDuplicateIdCount: Number.isFinite(Number(proofGapSoftwareForm?.duplicateIdCount)) ? Number(proofGapSoftwareForm.duplicateIdCount) : null,
  451. proofGapSoftwareFormHeaderMatches: Boolean(proofGapSoftwareForm?.headerMatches),
  452. proofGapSoftwareFormRowWidthOk: Boolean(proofGapSoftwareForm?.rowWidthOk),
  453. latestFormIndexPassed: Boolean(latestFormIndex?.passed),
  454. latestFormIndexPrimaryForm: latestFormIndex?.primaryForm?.csv || '',
  455. latestFormIndexClientList: latestFormIndex?.clientList?.csv || '',
  456. latestFormIndexClientListRowCount: Number.isFinite(Number(latestFormIndex?.clientList?.rowCount)) ? Number(latestFormIndex.clientList.rowCount) : null,
  457. latestFormIndexClientListSourceDuplicateRemovedCount: Number.isFinite(Number(latestFormIndex?.clientList?.sourceDuplicateRemovedCount)) ? Number(latestFormIndex.clientList.sourceDuplicateRemovedCount) : null,
  458. latestFormIndexClientListFinalDuplicateGroupCount: Number.isFinite(Number(latestFormIndex?.clientList?.finalDuplicateGroupCount)) ? Number(latestFormIndex.clientList.finalDuplicateGroupCount) : null,
  459. latestFormIndexClientListDuplicateCountMeaning: latestFormIndex?.clientList?.duplicateCountMeaning || '',
  460. latestFormIndexClientListRankContinuous: Boolean(latestFormIndex?.clientList?.rankContinuous),
  461. latestFormIndexClientListDirectCustomerProof: Boolean(latestFormIndex?.clientList?.directCustomerProof),
  462. latestFormIndexBusinessExecutionIndex: latestFormIndex?.businessExecutionIndex?.markdown || latestFormIndex?.businessExecutionIndex?.csv || '',
  463. latestFormIndexBusinessExecutionIndexOperatorPackOwnerArtifactCount: Number.isFinite(Number(latestFormIndex?.businessExecutionIndex?.operatorPackOwnerArtifactCount)) ? Number(latestFormIndex.businessExecutionIndex.operatorPackOwnerArtifactCount) : (Array.isArray(latestFormIndex?.businessExecutionIndex?.operatorPackOwnerArtifacts) ? latestFormIndex.businessExecutionIndex.operatorPackOwnerArtifacts.length : null),
  464. latestFormIndexBusinessExecutionIndexFieldOwnerRowCount: Number.isFinite(Number(latestFormIndex?.businessExecutionIndex?.fieldOwnerRowCount)) ? Number(latestFormIndex.businessExecutionIndex.fieldOwnerRowCount) : null,
  465. latestFormIndexBusinessExecutionIndexRecheckCommandRowCount: Number.isFinite(Number(latestFormIndex?.businessExecutionIndex?.recheckCommandRowCount)) ? Number(latestFormIndex.businessExecutionIndex.recheckCommandRowCount) : null,
  466. latestFormIndexIntakeFieldChecklist: latestFormIndex?.intakeFieldChecklist?.markdown || latestFormIndex?.intakeFieldChecklist?.csv || '',
  467. latestFormIndexIntakeFieldChecklistItemCount: Number.isFinite(Number(latestFormIndex?.intakeFieldChecklist?.itemCount)) ? Number(latestFormIndex.intakeFieldChecklist.itemCount) : null,
  468. latestFormIndexIntakeFieldChecklistOwnerGroupCount: Array.isArray(latestFormIndex?.intakeFieldChecklist?.ownerGroups) ? latestFormIndex.intakeFieldChecklist.ownerGroups.length : null,
  469. latestFormIndexIntakeFieldChecklistRecheckCommandCount: Array.isArray(latestFormIndex?.intakeFieldChecklist?.recheckCommands) ? latestFormIndex.intakeFieldChecklist.recheckCommands.length : null,
  470. latestFormIndexManualReviewLabelGuide: latestFormIndex?.manualReviewLabelGuide?.markdown || latestFormIndex?.manualReviewLabelGuide?.csv || '',
  471. latestFormIndexManualReviewLabelGuideLabelCount: Number.isFinite(Number(latestFormIndex?.manualReviewLabelGuide?.labelCount)) ? Number(latestFormIndex.manualReviewLabelGuide.labelCount) : null,
  472. latestFormIndexManualReviewLabelGuideAttributionTypeCount: Number.isFinite(Number(latestFormIndex?.manualReviewLabelGuide?.attributionTypeCount)) ? Number(latestFormIndex.manualReviewLabelGuide.attributionTypeCount) : null,
  473. latestFormIndexPlanExecutionStatus: latestFormIndex?.planExecutionStatus?.markdown || latestFormIndex?.planExecutionStatus?.csv || '',
  474. latestFormIndexPlanExecutionStatusRowCount: Number.isFinite(Number(latestFormIndex?.planExecutionStatus?.rowCount)) ? Number(latestFormIndex.planExecutionStatus.rowCount) : null,
  475. latestFormIndexPlanExecutionStatusComplete: Boolean(latestFormIndex?.planExecutionStatus?.complete),
  476. latestFormIndexPlanExecutionStatusProofOpenCount: Number.isFinite(Number(latestFormIndex?.planExecutionStatus?.proofOpenCount)) ? Number(latestFormIndex.planExecutionStatus.proofOpenCount) : null,
  477. latestFormIndexPlanExecutionStatusOperatorPackOwnerArtifactCount: Number.isFinite(Number(latestFormIndex?.planExecutionStatus?.operatorPackOwnerArtifactCount)) ? Number(latestFormIndex.planExecutionStatus.operatorPackOwnerArtifactCount) : (Array.isArray(latestFormIndex?.planExecutionStatus?.operatorPackOwnerArtifacts) ? latestFormIndex.planExecutionStatus.operatorPackOwnerArtifacts.length : null),
  478. latestFormIndexChinesePlanDoc: findSupportDocPath(latestFormIndex, 'tihao-experience-optimization-plan.zh-CN.md'),
  479. latestFormIndexChineseActionOrderDoc: findSupportDocPath(latestFormIndex, 'business-proof-action-order.zh-CN.md'),
  480. latestFormIndexActionCount: Number.isFinite(Number(latestFormIndex?.nextActions?.actionCount)) ? Number(latestFormIndex.nextActions.actionCount) : null,
  481. latestFormIndexProofOpenCount: Number.isFinite(Number(latestFormIndex?.nextActions?.proofOpenCount)) ? Number(latestFormIndex.nextActions.proofOpenCount) : null,
  482. latestFormIndexNextActionsTopCount: Array.isArray(latestFormIndex?.nextActions?.topActions) ? latestFormIndex.nextActions.topActions.length : null,
  483. latestFormIndexNextActionsProofGapOpenCount: Number.isFinite(Number(latestFormIndex?.nextActions?.sourceState?.proofGapOpenCount)) ? Number(latestFormIndex.nextActions.sourceState.proofGapOpenCount) : null,
  484. latestFormIndexDataRepairCsv: latestFormIndex?.repairActions?.dataIntake?.csv || '',
  485. latestFormIndexDataRepairActionCount: Number.isFinite(Number(latestFormIndex?.repairActions?.dataIntake?.repairActionCount)) ? Number(latestFormIndex.repairActions.dataIntake.repairActionCount) : null,
  486. latestFormIndexVideoRepairCsv: latestFormIndex?.repairActions?.videoResource?.csv || '',
  487. latestFormIndexVideoRepairActionCount: Number.isFinite(Number(latestFormIndex?.repairActions?.videoResource?.repairActionCount)) ? Number(latestFormIndex.repairActions.videoResource.repairActionCount) : null,
  488. latestFormIndexHomepageRepairCsv: latestFormIndex?.repairActions?.homepageEvidence?.csv || '',
  489. latestFormIndexHomepageRepairActionCount: Number.isFinite(Number(latestFormIndex?.repairActions?.homepageEvidence?.repairActionCount)) ? Number(latestFormIndex.repairActions.homepageEvidence.repairActionCount) : null,
  490. latestFormIndexOwnerRepairSummary: ownerRepairSummary(latestFormIndex?.nextActions?.ownerArtifacts),
  491. latestFormIndexProofGapOperatorPack: latestFormIndex?.proofGapOperatorPack?.markdown || latestFormIndex?.proofGapOperatorPack?.csv || '',
  492. latestFormIndexProofGapOperatorPackOpenCount: Number.isFinite(Number(latestFormIndex?.proofGapOperatorPack?.openCount)) ? Number(latestFormIndex.proofGapOperatorPack.openCount) : null,
  493. latestFormIndexProofGapOperatorPackOwnerGroupCount: Number.isFinite(Number(latestFormIndex?.proofGapOperatorPack?.ownerGroupCount)) ? Number(latestFormIndex.proofGapOperatorPack.ownerGroupCount) : null,
  494. latestFormIndexProofGapOperatorPackOwnerArtifactCount: Number.isFinite(Number(latestFormIndex?.proofGapOperatorPack?.ownerArtifactCount)) ? Number(latestFormIndex.proofGapOperatorPack.ownerArtifactCount) : null,
  495. latestFormIndexProofGapOperatorPackRecheckCommandCount: Number.isFinite(Number(latestFormIndex?.proofGapOperatorPack?.recheckCommandCount)) ? Number(latestFormIndex.proofGapOperatorPack.recheckCommandCount) : null,
  496. latestFormIndexProofGapOperatorPackEvidenceRowCount: Number.isFinite(Number(latestFormIndex?.proofGapOperatorPack?.evidenceRowCount)) ? Number(latestFormIndex.proofGapOperatorPack.evidenceRowCount) : null,
  497. latestFormIndexProofGapOperatorPackNextActionCount: Number.isFinite(Number(latestFormIndex?.proofGapOperatorPack?.nextActionCount)) ? Number(latestFormIndex.proofGapOperatorPack.nextActionCount) : null,
  498. latestFormIndexProofGapOperatorPackGapIds: proofGapOperatorGapIds(latestFormIndex?.proofGapOperatorPack).join(','),
  499. latestFormIndexLongRunReadinessReport: latestFormIndex?.longRunReadiness?.report || '',
  500. latestFormIndexLongRunReadinessReady: Boolean(latestFormIndex?.longRunReadiness?.ready),
  501. latestFormIndexLongRunReadinessFailCount: Number.isFinite(Number(latestFormIndex?.longRunReadiness?.failCount)) ? Number(latestFormIndex.longRunReadiness.failCount) : null,
  502. latestFormIndexLongRunReadinessProofLevel: latestFormIndex?.longRunReadiness?.proofLevel || '',
  503. latestFormIndexLongRunReadinessDirectCustomerProof: Boolean(latestFormIndex?.longRunReadiness?.directCustomerProof),
  504. localSeedToIntakeWorklistProofLevel: localSeedToIntakeWorklist?.proofLevel || '',
  505. localSeedToIntakeWorklistDirectCustomerProof: Boolean(localSeedToIntakeWorklist?.directCustomerProof),
  506. localSeedToIntakeWorklistCanCloseProofGap: Boolean(localSeedToIntakeWorklist?.canCloseProofGap),
  507. localSeedToIntakeWorklistHistoryDraftRows: worklistCount(localSeedToIntakeWorklist, 'historyDraftRows'),
  508. localSeedToIntakeWorklistReferenceSeedRows: worklistCount(localSeedToIntakeWorklist, 'referenceSeedRows'),
  509. localSeedToIntakeWorklistCandidateSeedRows: worklistCount(localSeedToIntakeWorklist, 'candidateSeedRows'),
  510. localSeedToIntakeWorklistVideoWorklistRows: worklistCount(localSeedToIntakeWorklist, 'videoWorklistRows'),
  511. localSeedToIntakeWorklistVideoCandidateNeedsResourceRows: worklistCount(localSeedToIntakeWorklist, 'videoCandidateNeedsResourceRows'),
  512. localSeedToIntakeWorklistMissingProofRequirementCount: Array.isArray(localSeedToIntakeWorklist?.missingProofRequirements) ? localSeedToIntakeWorklist.missingProofRequirements.length : null,
  513. localSeedToIntakeWorklistNextActionCount: Array.isArray(localSeedToIntakeWorklist?.nextActions) ? localSeedToIntakeWorklist.nextActions.length : null,
  514. latestFormIndexLocalSeedToIntakeWorklistMarkdown: latestFormIndex?.localSeedToIntakeWorklist?.markdown || '',
  515. latestFormIndexLocalSeedToIntakeWorklistHistoryDraftRows: worklistCount(latestFormIndex?.localSeedToIntakeWorklist, 'historyDraftRows'),
  516. latestFormIndexLocalSeedToIntakeWorklistCandidateSeedRows: worklistCount(latestFormIndex?.localSeedToIntakeWorklist, 'candidateSeedRows'),
  517. latestFormIndexLocalSeedToIntakeWorklistVideoCandidateNeedsResourceRows: worklistCount(latestFormIndex?.localSeedToIntakeWorklist, 'videoCandidateNeedsResourceRows'),
  518. latestFormIndexLocalSeedToIntakeWorklistCanCloseProofGap: Boolean(latestFormIndex?.localSeedToIntakeWorklist?.canCloseProofGap),
  519. realGapMaterialAuditMarkdown: realGapMaterialAudit?.markdown || '',
  520. realGapMaterialAuditProofLevel: realGapMaterialAudit?.proofLevel || '',
  521. realGapMaterialAuditGapCount: Number.isFinite(Number(realGapMaterialAudit?.gapCount)) ? Number(realGapMaterialAudit.gapCount) : null,
  522. realGapMaterialAuditMaterialFoundCount: Number.isFinite(Number(realGapMaterialAudit?.materialFoundCount)) ? Number(realGapMaterialAudit.materialFoundCount) : null,
  523. realGapMaterialAuditOpenGapCount: Number.isFinite(Number(realGapMaterialAudit?.openGapCount)) ? Number(realGapMaterialAudit.openGapCount) : null,
  524. realGapMaterialAuditCanCloseProofGap: Boolean(realGapMaterialAudit?.canCloseProofGap),
  525. latestFormIndexRealGapMaterialAuditMarkdown: latestFormIndex?.realGapMaterialAudit?.markdown || '',
  526. latestFormIndexRealGapMaterialAuditMaterialFoundCount: Number.isFinite(Number(latestFormIndex?.realGapMaterialAudit?.materialFoundCount)) ? Number(latestFormIndex.realGapMaterialAudit.materialFoundCount) : null,
  527. latestFormIndexRealGapMaterialAuditOpenGapCount: Number.isFinite(Number(latestFormIndex?.realGapMaterialAudit?.openGapCount)) ? Number(latestFormIndex.realGapMaterialAudit.openGapCount) : null,
  528. latestFormIndexRealGapMaterialAuditCanCloseProofGap: Boolean(latestFormIndex?.realGapMaterialAudit?.canCloseProofGap),
  529. realProofClosureWorkOrderPassed: Boolean(realProofClosureWorkOrder?.passed),
  530. realProofClosureWorkOrderComplete: Boolean(realProofClosureWorkOrder?.complete),
  531. realProofClosureWorkOrderProofLevel: realProofClosureWorkOrder?.proofLevel || '',
  532. realProofClosureWorkOrderDirectCustomerProof: Boolean(realProofClosureWorkOrder?.directCustomerProof),
  533. realProofClosureWorkOrderCanCloseProofGap: Boolean(realProofClosureWorkOrder?.canCloseProofGap),
  534. realProofClosureWorkOrderRequiresHumanRealMaterial: Boolean(realProofClosureWorkOrder?.requiresHumanRealMaterial),
  535. realProofClosureWorkOrderWorkOrderCount: Number.isFinite(Number(realProofClosureWorkOrder?.workOrderCount)) ? Number(realProofClosureWorkOrder.workOrderCount) : null,
  536. realProofClosureWorkOrderOwnerGroupCount: Number.isFinite(Number(realProofClosureWorkOrder?.ownerGroupCount)) ? Number(realProofClosureWorkOrder.ownerGroupCount) : null,
  537. realProofClosureWorkOrderOwnerArtifactCount: realProofWorkOrderOwnerArtifactCount(realProofClosureWorkOrder),
  538. realProofClosureWorkOrderProofGapOpenCount: Number.isFinite(Number(realProofClosureWorkOrder?.sourceState?.proofGapOpenCount)) ? Number(realProofClosureWorkOrder.sourceState.proofGapOpenCount) : null,
  539. realProofClosureWorkOrderIntakeFailureCount: Number.isFinite(Number(realProofClosureWorkOrder?.sourceState?.intakeFailureCount)) ? Number(realProofClosureWorkOrder.sourceState.intakeFailureCount) : null,
  540. realProofClosureWorkOrderRealCandidateRows: Number.isFinite(Number(realProofClosureWorkOrder?.sourceState?.realCandidateRows)) ? Number(realProofClosureWorkOrder.sourceState.realCandidateRows) : null,
  541. realProofClosureWorkOrderVideoAbPreflightContext: hasRealProofClosureWorkOrderVideoAbPreflightContext(realProofClosureWorkOrder),
  542. latestFormIndexRealProofClosureWorkOrderMarkdown: latestFormIndex?.realProofClosureWorkOrder?.markdown || '',
  543. latestFormIndexRealProofClosureWorkOrderWorkOrderCount: Number.isFinite(Number(latestFormIndex?.realProofClosureWorkOrder?.workOrderCount)) ? Number(latestFormIndex.realProofClosureWorkOrder.workOrderCount) : null,
  544. latestFormIndexRealProofClosureWorkOrderOwnerArtifactCount: realProofWorkOrderOwnerArtifactCount(latestFormIndex?.realProofClosureWorkOrder),
  545. latestFormIndexRealProofClosureWorkOrderCanCloseProofGap: Boolean(latestFormIndex?.realProofClosureWorkOrder?.canCloseProofGap),
  546. handoffChinesePlanDoc: findSupportDocPath(handoff, 'tihao-experience-optimization-plan.zh-CN.md'),
  547. handoffChineseActionOrderDoc: findSupportDocPath(handoff, 'business-proof-action-order.zh-CN.md'),
  548. handoffLatestFormIndexOwnerGroupCount: Array.isArray(handoff?.latestFormIndex?.intakeFieldChecklist?.ownerGroups) ? handoff.latestFormIndex.intakeFieldChecklist.ownerGroups.length : null,
  549. handoffLatestFormIndexRecheckCommandCount: Array.isArray(handoff?.latestFormIndex?.intakeFieldChecklist?.recheckCommands) ? handoff.latestFormIndex.intakeFieldChecklist.recheckCommands.length : null,
  550. handoffLatestFormIndexNextActionsTopCount: Array.isArray(handoff?.latestFormIndex?.topActions) ? handoff.latestFormIndex.topActions.length : null,
  551. handoffLatestFormIndexNextActionsProofGapOpenCount: Number.isFinite(Number(handoff?.latestFormIndex?.sourceState?.proofGapOpenCount)) ? Number(handoff.latestFormIndex.sourceState.proofGapOpenCount) : null,
  552. handoffLocalSeedToIntakeWorklistMarkdown: handoff?.localSeedToIntakeWorklist?.markdown || '',
  553. handoffLocalSeedToIntakeWorklistHistoryDraftRows: worklistCount(handoff?.localSeedToIntakeWorklist, 'historyDraftRows'),
  554. handoffLocalSeedToIntakeWorklistCandidateSeedRows: worklistCount(handoff?.localSeedToIntakeWorklist, 'candidateSeedRows'),
  555. handoffLocalSeedToIntakeWorklistVideoCandidateNeedsResourceRows: worklistCount(handoff?.localSeedToIntakeWorklist, 'videoCandidateNeedsResourceRows'),
  556. handoffLocalSeedToIntakeWorklistCanCloseProofGap: Boolean(handoff?.localSeedToIntakeWorklist?.canCloseProofGap),
  557. handoffLatestFormIndexLocalSeedToIntakeWorklistMarkdown: handoff?.latestFormIndex?.localSeedToIntakeWorklist?.markdown || '',
  558. handoffLatestFormIndexLocalSeedToIntakeWorklistHistoryDraftRows: worklistCount(handoff?.latestFormIndex?.localSeedToIntakeWorklist, 'historyDraftRows'),
  559. handoffLatestFormIndexLocalSeedToIntakeWorklistCandidateSeedRows: worklistCount(handoff?.latestFormIndex?.localSeedToIntakeWorklist, 'candidateSeedRows'),
  560. handoffLatestFormIndexLocalSeedToIntakeWorklistVideoCandidateNeedsResourceRows: worklistCount(handoff?.latestFormIndex?.localSeedToIntakeWorklist, 'videoCandidateNeedsResourceRows'),
  561. handoffLatestFormIndexLocalSeedToIntakeWorklistCanCloseProofGap: Boolean(handoff?.latestFormIndex?.localSeedToIntakeWorklist?.canCloseProofGap),
  562. handoffRealGapMaterialAuditMarkdown: handoff?.realGapMaterialAudit?.markdown || '',
  563. handoffRealGapMaterialAuditMaterialFoundCount: Number.isFinite(Number(handoff?.realGapMaterialAudit?.materialFoundCount)) ? Number(handoff.realGapMaterialAudit.materialFoundCount) : null,
  564. handoffRealGapMaterialAuditOpenGapCount: Number.isFinite(Number(handoff?.realGapMaterialAudit?.openGapCount)) ? Number(handoff.realGapMaterialAudit.openGapCount) : null,
  565. handoffRealGapMaterialAuditCanCloseProofGap: Boolean(handoff?.realGapMaterialAudit?.canCloseProofGap),
  566. handoffLatestFormIndexRealGapMaterialAuditMarkdown: handoff?.latestFormIndex?.realGapMaterialAudit?.markdown || '',
  567. handoffLatestFormIndexRealGapMaterialAuditMaterialFoundCount: Number.isFinite(Number(handoff?.latestFormIndex?.realGapMaterialAudit?.materialFoundCount)) ? Number(handoff.latestFormIndex.realGapMaterialAudit.materialFoundCount) : null,
  568. handoffLatestFormIndexRealGapMaterialAuditOpenGapCount: Number.isFinite(Number(handoff?.latestFormIndex?.realGapMaterialAudit?.openGapCount)) ? Number(handoff.latestFormIndex.realGapMaterialAudit.openGapCount) : null,
  569. handoffLatestFormIndexRealGapMaterialAuditCanCloseProofGap: Boolean(handoff?.latestFormIndex?.realGapMaterialAudit?.canCloseProofGap),
  570. handoffRealProofClosureWorkOrderMarkdown: handoff?.realProofClosureWorkOrder?.markdown || '',
  571. handoffRealProofClosureWorkOrderWorkOrderCount: Number.isFinite(Number(handoff?.realProofClosureWorkOrder?.workOrderCount)) ? Number(handoff.realProofClosureWorkOrder.workOrderCount) : null,
  572. handoffRealProofClosureWorkOrderOwnerArtifactCount: realProofWorkOrderOwnerArtifactCount(handoff?.realProofClosureWorkOrder),
  573. handoffRealProofClosureWorkOrderCanCloseProofGap: Boolean(handoff?.realProofClosureWorkOrder?.canCloseProofGap),
  574. handoffLatestFormIndexRealProofClosureWorkOrderMarkdown: handoff?.latestFormIndex?.realProofClosureWorkOrder?.markdown || '',
  575. handoffLatestFormIndexRealProofClosureWorkOrderWorkOrderCount: Number.isFinite(Number(handoff?.latestFormIndex?.realProofClosureWorkOrder?.workOrderCount)) ? Number(handoff.latestFormIndex.realProofClosureWorkOrder.workOrderCount) : null,
  576. handoffLatestFormIndexRealProofClosureWorkOrderOwnerArtifactCount: realProofWorkOrderOwnerArtifactCount(handoff?.latestFormIndex?.realProofClosureWorkOrder),
  577. handoffLatestFormIndexRealProofClosureWorkOrderCanCloseProofGap: Boolean(handoff?.latestFormIndex?.realProofClosureWorkOrder?.canCloseProofGap),
  578. handoffLatestFormIndexManualReviewLabelGuideLabelCount: Number.isFinite(Number(handoff?.latestFormIndex?.manualReviewLabelGuide?.labelCount)) ? Number(handoff.latestFormIndex.manualReviewLabelGuide.labelCount) : null,
  579. handoffLatestFormIndexBusinessFillGuideRowCount: Number.isFinite(Number(handoff?.latestFormIndex?.businessFillGuide?.rowCount)) ? Number(handoff.latestFormIndex.businessFillGuide.rowCount) : null,
  580. handoffLongRunReadinessReport: handoff?.longRunReadiness?.report || '',
  581. handoffLongRunReadinessReady: Boolean(handoff?.longRunReadiness?.ready),
  582. handoffLongRunReadinessFailCount: Number.isFinite(Number(handoff?.longRunReadiness?.failCount)) ? Number(handoff.longRunReadiness.failCount) : null,
  583. handoffLongRunReadinessProofLevel: handoff?.longRunReadiness?.proofLevel || '',
  584. handoffLongRunReadinessDirectCustomerProof: Boolean(handoff?.longRunReadiness?.directCustomerProof),
  585. handoffLatestFormIndexLongRunReadinessReport: handoff?.latestFormIndex?.longRunReadiness?.report || '',
  586. handoffLatestFormIndexLongRunReadinessReady: Boolean(handoff?.latestFormIndex?.longRunReadiness?.ready),
  587. handoffLatestFormIndexLongRunReadinessFailCount: Number.isFinite(Number(handoff?.latestFormIndex?.longRunReadiness?.failCount)) ? Number(handoff.latestFormIndex.longRunReadiness.failCount) : null,
  588. handoffPlanExecutionStatusRowCount: Number.isFinite(Number(handoff?.planExecutionStatus?.rowCount)) ? Number(handoff.planExecutionStatus.rowCount) : null,
  589. handoffPlanExecutionStatusComplete: Boolean(handoff?.planExecutionStatus?.complete),
  590. handoffPlanExecutionStatusProofOpenCount: Number.isFinite(Number(handoff?.planExecutionStatus?.proofOpenCount)) ? Number(handoff.planExecutionStatus.proofOpenCount) : null,
  591. handoffPlanExecutionStatusOperatorPackOwnerArtifactCount: Number.isFinite(Number(handoff?.planExecutionStatus?.operatorPackOwnerArtifactCount)) ? Number(handoff.planExecutionStatus.operatorPackOwnerArtifactCount) : (Array.isArray(handoff?.planExecutionStatus?.operatorPackOwnerArtifacts) ? handoff.planExecutionStatus.operatorPackOwnerArtifacts.length : null),
  592. handoffOwnerRepairSummary: ownerRepairSummary(handoff?.businessProofNextActions?.ownerArtifacts),
  593. handoffProofGapOperatorPackReport: handoff?.proofGapOperatorPack?.markdown || '',
  594. handoffProofGapOperatorPackSummary: handoff?.proofGapOperatorPack?.summary || '',
  595. handoffProofGapOperatorPackOpenCount: Number.isFinite(Number(handoff?.proofGapOperatorPack?.openCount)) ? Number(handoff.proofGapOperatorPack.openCount) : null,
  596. handoffProofGapOperatorPackOwnerGroupCount: Number.isFinite(Number(handoff?.proofGapOperatorPack?.ownerGroupCount)) ? Number(handoff.proofGapOperatorPack.ownerGroupCount) : null,
  597. handoffProofGapOperatorPackOwnerArtifactCount: Number.isFinite(Number(handoff?.proofGapOperatorPack?.ownerArtifactCount)) ? Number(handoff.proofGapOperatorPack.ownerArtifactCount) : null,
  598. handoffProofGapOperatorPackRecheckCommandCount: Number.isFinite(Number(handoff?.proofGapOperatorPack?.recheckCommandCount)) ? Number(handoff.proofGapOperatorPack.recheckCommandCount) : null,
  599. handoffProofGapOperatorPackEvidenceRowCount: Number.isFinite(Number(handoff?.proofGapOperatorPack?.evidenceRowCount)) ? Number(handoff.proofGapOperatorPack.evidenceRowCount) : null,
  600. handoffProofGapOperatorPackNextActionCount: Number.isFinite(Number(handoff?.proofGapOperatorPack?.nextActionCount)) ? Number(handoff.proofGapOperatorPack.nextActionCount) : null,
  601. handoffProofGapOperatorPackGapIds: proofGapOperatorGapIds(handoff?.proofGapOperatorPack).join(','),
  602. handoffProofGapClosureOpenCount: Number.isFinite(Number(handoff?.proofGapClosure?.openCount)) ? Number(handoff.proofGapClosure.openCount) : null,
  603. handoffProofGapClosureBusinessOpenCount: Number.isFinite(Number(handoff?.proofGapClosure?.businessOpenCount)) ? Number(handoff.proofGapClosure.businessOpenCount) : null,
  604. handoffProofGapClosureBusinessClosedCount: Number.isFinite(Number(handoff?.proofGapClosure?.businessClosedCount)) ? Number(handoff.proofGapClosure.businessClosedCount) : null,
  605. handoffProofGapClosureBusinessGapCount: Number.isFinite(Number(handoff?.proofGapClosure?.businessGapCount)) ? Number(handoff.proofGapClosure.businessGapCount) : null,
  606. handoffProofGapClosureTotalRows: Number.isFinite(Number(handoff?.proofGapClosure?.totalRows)) ? Number(handoff.proofGapClosure.totalRows) : null,
  607. handoffProofGapClosurePrecheckOpenCount: Number.isFinite(Number(handoff?.proofGapClosure?.precheckOpenCount)) ? Number(handoff.proofGapClosure.precheckOpenCount) : null,
  608. handoffProofGapClosureCountSemanticsOpenCount: handoff?.proofGapClosure?.countSemantics?.openCount || '',
  609. handoffProofGapClosureRows: Array.isArray(handoff?.proofGapClosure?.rows) ? handoff.proofGapClosure.rows.length : null,
  610. handoffVideoAbMissingProofCount: videoAbMissingProofCount(handoff?.proofGapClosure),
  611. handoffProofGapClosureOpenRowsWithMissingProofRequirements: countOpenRowsWithMissingProofRequirements(handoff?.proofGapClosure?.rows, 'id'),
  612. feedbackLoopClosurePassed: Boolean(feedbackLoopClosure?.passed),
  613. feedbackLoopClosureComplete: Boolean(feedbackLoopClosure?.complete),
  614. feedbackLoopClosureDirectCustomerProof: Boolean(feedbackLoopClosure?.directCustomerProof),
  615. feedbackLoopClosureNegativeCount: Number.isFinite(Number(feedbackLoopClosure?.negativeFeedbackCount)) ? Number(feedbackLoopClosure.negativeFeedbackCount) : null,
  616. feedbackLoopClosureLeakedCount: Number.isFinite(Number(feedbackLoopClosure?.leakedCount)) ? Number(feedbackLoopClosure.leakedCount) : null,
  617. handoffFeedbackLoopClosureDirectCustomerProof: Boolean(handoff?.feedbackLoopClosure?.directCustomerProof),
  618. homepageEvidenceReadinessReady: Boolean(homepageEvidenceReadiness?.ready),
  619. homepageEvidenceReadinessDirectCustomerProof: Boolean(homepageEvidenceReadiness?.directCustomerProof),
  620. homepageEvidenceReadinessProofLevel: homepageEvidenceReadiness?.proofLevel || '',
  621. homepageEvidenceReadinessFailureCount: Number.isFinite(Number(homepageEvidenceReadiness?.failureCount)) ? Number(homepageEvidenceReadiness.failureCount) : null,
  622. homepageEvidenceReadinessRepairActionCount: Array.isArray(homepageEvidenceReadiness?.repairActions) ? homepageEvidenceReadiness.repairActions.length : null,
  623. handoffHomepageEvidenceReadinessDirectCustomerProof: Boolean(handoff?.homepageEvidenceReadiness?.directCustomerProof),
  624. proofGapRows: Array.isArray(proofGap?.rows) ? proofGap.rows.map(item => ({
  625. id: item.id,
  626. status: item.status,
  627. hasTemplateCommand: Boolean(item.templateCommand),
  628. hasTemplateArtifact: Boolean(item.templateArtifact)
  629. })) : [],
  630. proofGapClosureComplete: Boolean(proofGapClosure?.complete),
  631. proofGapClosureOpenCount: Number(proofGapClosure?.openCount || 0),
  632. proofGapClosureBusinessOpenCount: Number.isFinite(Number(proofGapClosure?.businessOpenCount)) ? Number(proofGapClosure.businessOpenCount) : null,
  633. proofGapClosureBusinessClosedCount: Number.isFinite(Number(proofGapClosure?.businessClosedCount)) ? Number(proofGapClosure.businessClosedCount) : null,
  634. proofGapClosureBusinessGapCount: Number.isFinite(Number(proofGapClosure?.businessGapCount)) ? Number(proofGapClosure.businessGapCount) : null,
  635. proofGapClosureTotalRows: Number.isFinite(Number(proofGapClosure?.totalRows)) ? Number(proofGapClosure.totalRows) : null,
  636. proofGapClosurePrecheckOpenCount: Number.isFinite(Number(proofGapClosure?.precheckOpenCount)) ? Number(proofGapClosure.precheckOpenCount) : null,
  637. proofGapClosureCountSemanticsOpenCount: proofGapClosure?.countSemantics?.openCount || '',
  638. proofGapClosureOpenRowsWithMissingProofRequirements: countOpenRowsWithMissingProofRequirements(proofGapClosure?.rows, 'id'),
  639. planExecutionStatusPassed: Boolean(planExecutionStatus?.passed),
  640. planExecutionStatusComplete: Boolean(planExecutionStatus?.complete),
  641. planExecutionStatusRowCount: Number.isFinite(Number(planExecutionStatus?.rowCount)) ? Number(planExecutionStatus.rowCount) : null,
  642. planExecutionStatusProofOpenCount: Number.isFinite(Number(planExecutionStatus?.proofOpenCount)) ? Number(planExecutionStatus.proofOpenCount) : null,
  643. planExecutionStatusOperatorPackOwnerArtifactCount: Number.isFinite(Number(planExecutionStatus?.operatorPackOwnerArtifactCount)) ? Number(planExecutionStatus.operatorPackOwnerArtifactCount) : (Array.isArray(planExecutionStatus?.operatorPackOwnerArtifacts) ? planExecutionStatus.operatorPackOwnerArtifacts.length : null),
  644. planExecutionStatusBusinessExecutionOperatorPackOwnerArtifactRowCount: Number.isFinite(Number(planExecutionStatus?.businessExecutionOperatorPackOwnerArtifactRowCount)) ? Number(planExecutionStatus.businessExecutionOperatorPackOwnerArtifactRowCount) : null,
  645. planExecutionStatusGuardrailCount: Array.isArray(planExecutionStatus?.guardrails) ? planExecutionStatus.guardrails.length : null,
  646. proofGapClosureRows: Array.isArray(proofGapClosure?.rows) ? proofGapClosure.rows.map(item => ({ id: item.id, status: item.status })) : []
  647. },
  648. checks
  649. };
  650. }
  651. function renderReport(summary) {
  652. const lines = [
  653. '# 每轮沉淀审计',
  654. '',
  655. `- 生成时间:${summary.generatedAt}`,
  656. `- 是否通过:${summary.passed ? '是' : '否'}`,
  657. `- pass:${summary.counts.pass}`,
  658. `- warn:${summary.counts.warn}`,
  659. `- fail:${summary.counts.fail}`,
  660. '',
  661. '## 来源',
  662. '',
  663. `- 实施日志:${summary.sources.implementationLog}`,
  664. `- 状态审计:${summary.sources.statusSummary}`,
  665. `- 证据台账:${summary.sources.evidenceSummary}`,
  666. `- 交接摘要:${summary.sources.handoffSummary}`,
  667. `- 真实证明缺口:${summary.sources.proofGapSummary}`,
  668. `- 真实证明缺口关闭审计:${summary.sources.proofGapClosureSummary}`,
  669. `- 提号补证操作包:${summary.sources.proofGapOperatorPackSummary}`,
  670. `- 反馈二轮闭环审计:${summary.sources.feedbackLoopClosureSummary}`,
  671. `- 主页近期内容证据就绪审计:${summary.sources.homepageEvidenceReadinessSummary}`,
  672. `- Intake readiness:${summary.sources.intakeReadinessSummary}`,
  673. `- 长跑就绪度:${summary.sources.longRunReadinessSummary}`,
  674. `- 15 步补证进度:${summary.sources.businessProofProgressSummary}`,
  675. `- 下一步行动队列:${summary.sources.businessProofNextActionsSummary}`,
  676. `- 商务执行总表:${summary.sources.businessExecutionIndexSummary}`,
  677. `- 优化完成度审计:${summary.sources.optimizationCompletionSummary}`,
  678. `- 真实验收材料收集总包:${summary.sources.realProofIntakeBundleSummary}`,
  679. `- 真实材料字段级补齐核对表:${summary.sources.intakeFieldChecklistSummary}`,
  680. `- 最新提号补证表单:${summary.sources.proofGapSoftwareFormSummary}`,
  681. `- 最新提号表单索引:${summary.sources.latestFormIndexSummary}`,
  682. `- 本地种子转 intake 补表 worklist:${summary.sources.localSeedToIntakeWorklistSummary}`,
  683. `- 真实证明闭环工单:${summary.sources.realProofClosureWorkOrderSummary}`,
  684. `- 补证操作顺序:${summary.sources.proofActionOrder}`,
  685. '',
  686. '## 当前观测',
  687. '',
  688. `- status.passed:${summary.observed.statusCounts.passed || 0}`,
  689. `- status.ready_not_proven:${summary.observed.statusCounts.ready_not_proven || 0}`,
  690. `- status.blocked_by_external_data:${summary.observed.statusCounts.blocked_by_external_data || 0}`,
  691. `- evidence.real_evidence:${summary.observed.evidenceCounts.real_evidence || 0}`,
  692. `- evidence.smoke_or_local:${summary.observed.evidenceCounts.smoke_or_local || 0}`,
  693. `- evidence.not_business_proof:${summary.observed.evidenceCounts.not_business_proof || 0}`,
  694. `- evidence.video-ab-runtime-preflight:${summary.observed.evidenceVideoRuntimePreflightCount || 0}`,
  695. `- proofGapClosure.sourceFiles.history: ${summary.observed.proofGapClosureHistorySource || 'unknown'}`,
  696. `- proofGapClosure.sourceFiles.customerEffect: ${summary.observed.proofGapClosureCustomerEffectSource || 'unknown'}`,
  697. `- proofGapClosure.recursiveProofArtifactDiscovery: ${summary.observed.proofGapClosureRecursiveProofArtifactDiscovery ? 'true' : 'false'}`,
  698. `- proofGapClosure.videoAbPreflightReadyForVideoAb: ${summary.observed.proofGapClosureVideoAbPreflightReadyForVideoAb ? 'true' : 'false'}`,
  699. `- proofGapClosure.videoAbPreflightFailureCount: ${summary.observed.proofGapClosureVideoAbPreflightFailureCount ?? 'unknown'}`,
  700. `- optimizationCompletion.videoAbPreflightReadyForVideoAb: ${summary.observed.optimizationCompletionVideoAbPreflightReadyForVideoAb ? 'true' : 'false'}`,
  701. `- optimizationCompletion.videoAbPreflightFailureCount: ${summary.observed.optimizationCompletionVideoAbPreflightFailureCount ?? 'unknown'}`,
  702. `- handoff.complete:${summary.observed.handoffComplete ? 'true' : 'false'}`,
  703. `- proofGap.unresolvedCount:${summary.observed.proofGapUnresolvedCount}`,
  704. `- proofGap.templateRowsReady:${summary.observed.proofGapTemplateRowsReady ? 'true' : 'false'}`,
  705. `- proofGap.templateColumnsReady:${summary.observed.proofGapTemplateColumnsReady ? 'true' : 'false'}`,
  706. `- proofGapOperatorPack.passed:${summary.observed.proofGapOperatorPackPassed ? 'true' : 'false'}`,
  707. `- proofGapOperatorPack.openCount:${summary.observed.proofGapOperatorPackOpenCount ?? 'unknown'}`,
  708. `- proofGapOperatorPack.rowCount:${summary.observed.proofGapOperatorPackRowCount ?? 'unknown'}`,
  709. `- proofGapOperatorPack.ownerGroupCount:${summary.observed.proofGapOperatorPackOwnerGroupCount ?? 'unknown'}`,
  710. `- proofGapOperatorPack.ownerArtifactCount:${summary.observed.proofGapOperatorPackOwnerArtifactCount ?? 'unknown'}`,
  711. `- proofGapOperatorPack.fillFileCount:${summary.observed.proofGapOperatorPackFillFileCount ?? 'unknown'}`,
  712. `- proofGapOperatorPack.recheckCommandCount:${summary.observed.proofGapOperatorPackRecheckCommandCount ?? 'unknown'}`,
  713. `- proofGapOperatorPack.evidenceRowCount:${summary.observed.proofGapOperatorPackEvidenceRowCount ?? 'unknown'}`,
  714. `- proofGapOperatorPack.nextActionCount:${summary.observed.proofGapOperatorPackNextActionCount ?? 'unknown'}`,
  715. `- proofGapOperatorPack.missingProofRequirementCount:${summary.observed.proofGapOperatorPackMissingProofRequirementCount ?? 'unknown'}`,
  716. `- proofGapOperatorPack.openRowsWithMissingProofRequirements:${summary.observed.proofGapOperatorPackOpenRowsWithMissingProofRequirements ?? 'unknown'}`,
  717. `- proofGapOperatorPack.videoAbPreflightContext:${summary.observed.proofGapOperatorPackVideoAbPreflightContext ? 'true' : 'false'}`,
  718. `- proofGapOperatorPack.gapIds:${summary.observed.proofGapOperatorPackGapIds || 'unknown'}`,
  719. `- proofGapOperatorPack.directCustomerProof:${summary.observed.proofGapOperatorPackDirectCustomerProof ? 'true' : 'false'}`,
  720. `- intakeReadiness.ready:${summary.observed.intakeReadinessReady ? 'true' : 'false'}`,
  721. `- intakeReadiness.failureCount:${summary.observed.intakeReadinessFailureCount ?? 'unknown'}`,
  722. `- longRunReadiness.mode:${summary.observed.longRunReadinessMode || 'unknown'}`,
  723. `- longRunReadiness.ready:${summary.observed.longRunReadinessReady ? 'true' : 'false'}`,
  724. `- longRunReadiness.failCount:${summary.observed.longRunReadinessFailCount ?? 'unknown'}`,
  725. `- businessProofProgress.complete:${summary.observed.businessProofProgressComplete ? 'true' : 'false'}`,
  726. `- businessProofProgress.pass:${summary.observed.businessProofProgressCounts.pass || 0}`,
  727. `- businessProofProgress.fail:${summary.observed.businessProofProgressCounts.fail || 0}`,
  728. `- businessProofProgress.pending:${summary.observed.businessProofProgressCounts.pending || 0}`,
  729. `- businessProofProgress.blocked_by_external_data:${summary.observed.businessProofProgressCounts.blocked_by_external_data || 0}`,
  730. `- businessProofProgress.operatorPackOwnerArtifactCount:${summary.observed.businessProofProgressOperatorPackOwnerArtifactCount ?? 'unknown'}`,
  731. `- handoff.businessProofProgressOperatorPackOwnerArtifactCount:${summary.observed.handoffBusinessProofProgressOperatorPackOwnerArtifactCount ?? 'unknown'}`,
  732. `- businessProofNextActions.complete:${summary.observed.businessProofNextActionsComplete ? 'true' : 'false'}`,
  733. `- businessProofNextActions.count:${summary.observed.businessProofNextActionsCount ?? 'unknown'}`,
  734. `- businessProofNextActions.topActionCount:${summary.observed.businessProofNextActionsTopCount ?? 'unknown'}`,
  735. `- businessProofNextActions.top:${summary.observed.businessProofNextActionsTop || 'unknown'}`,
  736. `- businessProofNextActions.withMissingProofRequirements:${summary.observed.businessProofNextActionsWithMissingProofRequirements ?? 'unknown'}`,
  737. `- handoff.businessProofNextActions.count:${summary.observed.handoffBusinessProofNextActionsCount ?? 'unknown'}`,
  738. `- handoff.businessProofNextActions.topActionCount:${summary.observed.handoffBusinessProofNextActionsTopCount ?? 'unknown'}`,
  739. `- handoff.businessProofNextActions.proofGapOpenCount:${summary.observed.handoffBusinessProofNextActionsProofGapOpenCount ?? 'unknown'}`,
  740. `- optimizationCompletion.complete:${summary.observed.optimizationCompletionComplete ? 'true' : 'false'}`,
  741. `- optimizationCompletion.readyForClaim:${summary.observed.optimizationCompletionReadyForClaim ? 'true' : 'false'}`,
  742. `- optimizationCompletion.proofGapOpenCount:${summary.observed.optimizationCompletionProofGapOpenCount ?? 'unknown'}`,
  743. `- optimizationCompletion.blockingReasonCount:${summary.observed.optimizationCompletionBlockingReasonCount ?? 'unknown'}`,
  744. `- optimizationCompletion.blockingReasonsWithMissingProofRequirements:${summary.observed.optimizationCompletionBlockingReasonsWithMissingProofRequirements ?? 'unknown'}`,
  745. `- optimizationCompletion.missingProofRequirementCount:${summary.observed.optimizationCompletionMissingProofRequirementCount ?? 'unknown'}`,
  746. `- optimizationCompletion.canClaim.tihaoRateImproved:${summary.observed.optimizationCompletionCanClaimTihaoRateImproved ? 'true' : 'false'}`,
  747. `- optimizationCompletion.canClaim.customerEffectAchieved:${summary.observed.optimizationCompletionCanClaimCustomerEffectAchieved ? 'true' : 'false'}`,
  748. `- optimizationCompletion.canClaim.manualSupplementReduced:${summary.observed.optimizationCompletionCanClaimManualSupplementReduced ? 'true' : 'false'}`,
  749. `- optimizationCompletion.sourceFiles.history: ${summary.observed.optimizationCompletionHistorySource || 'unknown'}`,
  750. `- optimizationCompletion.sourceFiles.customerEffect: ${summary.observed.optimizationCompletionCustomerEffectSource || 'unknown'}`,
  751. `- optimizationCompletion.recursiveProofArtifactDiscovery: ${summary.observed.optimizationCompletionRecursiveProofArtifactDiscovery ? 'true' : 'false'}`,
  752. `- businessExecutionIndex.passed:${summary.observed.businessExecutionIndexPassed ? 'true' : 'false'}`,
  753. `- businessExecutionIndex.rowCount:${summary.observed.businessExecutionIndexRowCount ?? 'unknown'}`,
  754. `- businessExecutionIndex.actionRowCount:${summary.observed.businessExecutionIndexActionRowCount ?? 'unknown'}`,
  755. `- businessExecutionIndex.repairRowCount:${summary.observed.businessExecutionIndexRepairRowCount ?? 'unknown'}`,
  756. `- businessExecutionIndex.operatorPackOwnerArtifactRowCount:${summary.observed.businessExecutionIndexOperatorPackOwnerArtifactRowCount ?? 'unknown'}`,
  757. `- businessExecutionIndex.operatorPackOwnerArtifactCount:${summary.observed.businessExecutionIndexOperatorPackOwnerArtifactCount ?? 'unknown'}`,
  758. `- businessExecutionIndex.fieldOwnerRowCount:${summary.observed.businessExecutionIndexFieldOwnerRowCount ?? 'unknown'}`,
  759. `- businessExecutionIndex.recheckCommandRowCount:${summary.observed.businessExecutionIndexRecheckCommandRowCount ?? 'unknown'}`,
  760. `- businessExecutionIndex.proofOpenCount:${summary.observed.businessExecutionIndexProofOpenCount ?? 'unknown'}`,
  761. `- businessExecutionIndex.directCustomerProof:${summary.observed.businessExecutionIndexDirectCustomerProof ? 'true' : 'false'}`,
  762. `- businessExecutionIndex.longRunReadinessSummary:${summary.observed.businessExecutionIndexLongRunReadinessSummary || 'unknown'}`,
  763. `- businessExecutionIndex.longRunReadinessReport:${summary.observed.businessExecutionIndexLongRunReadinessReport || 'unknown'}`,
  764. `- businessExecutionIndex.longRunReadinessReady:${summary.observed.businessExecutionIndexLongRunReadinessReady ? 'true' : 'false'}`,
  765. `- businessExecutionIndex.longRunReadinessFailCount:${summary.observed.businessExecutionIndexLongRunReadinessFailCount ?? 'unknown'}`,
  766. `- businessExecutionIndex.optimizationCompletionSummary:${summary.observed.businessExecutionIndexOptimizationCompletionSummary || 'unknown'}`,
  767. `- businessExecutionIndex.optimizationCompletionReport:${summary.observed.businessExecutionIndexOptimizationCompletionReport || 'unknown'}`,
  768. `- businessExecutionIndex.optimizationCompletionReadyForClaim:${summary.observed.businessExecutionIndexOptimizationCompletionReadyForClaim ? 'true' : 'false'}`,
  769. `- businessExecutionIndex.optimizationCompletionBlockingReasonCount:${summary.observed.businessExecutionIndexOptimizationCompletionBlockingReasonCount ?? 'unknown'}`,
  770. `- handoff.businessExecutionIndex.longRunReadinessReport:${summary.observed.handoffBusinessExecutionIndexLongRunReadinessReport || 'unknown'}`,
  771. `- handoff.businessExecutionIndex.longRunReadinessSummary:${summary.observed.handoffBusinessExecutionIndexLongRunReadinessSummary || 'unknown'}`,
  772. `- handoff.businessExecutionIndex.longRunReadinessStatus:${summary.observed.handoffBusinessExecutionIndexLongRunReadinessStatus || 'unknown'}`,
  773. `- handoff.businessExecutionIndex.longRunReadinessReady:${summary.observed.handoffBusinessExecutionIndexLongRunReadinessReady ? 'true' : 'false'}`,
  774. `- handoff.businessExecutionIndex.longRunReadinessFailCount:${summary.observed.handoffBusinessExecutionIndexLongRunReadinessFailCount ?? 'unknown'}`,
  775. `- handoff.businessExecutionIndex.operatorPackOwnerArtifactCount:${summary.observed.handoffBusinessExecutionIndexOperatorPackOwnerArtifactCount ?? 'unknown'}`,
  776. `- handoff.optimizationCompletionReport:${summary.observed.handoffOptimizationCompletionReport || 'unknown'}`,
  777. `- handoff.optimizationCompletionReadyForClaim:${summary.observed.handoffOptimizationCompletionReadyForClaim ? 'true' : 'false'}`,
  778. `- handoff.optimizationCompletionProofGapOpenCount:${summary.observed.handoffOptimizationCompletionProofGapOpenCount ?? 'unknown'}`,
  779. `- handoff.optimizationCompletionBlockingReasonCount:${summary.observed.handoffOptimizationCompletionBlockingReasonCount ?? 'unknown'}`,
  780. `- handoff.businessExecutionIndex.optimizationCompletionReport:${summary.observed.handoffBusinessExecutionIndexOptimizationCompletionReport || 'unknown'}`,
  781. `- handoff.businessExecutionIndex.optimizationCompletionSummary:${summary.observed.handoffBusinessExecutionIndexOptimizationCompletionSummary || 'unknown'}`,
  782. `- handoff.businessExecutionIndex.optimizationCompletionStatus:${summary.observed.handoffBusinessExecutionIndexOptimizationCompletionStatus || 'unknown'}`,
  783. `- handoff.businessExecutionIndex.optimizationCompletionReadyForClaim:${summary.observed.handoffBusinessExecutionIndexOptimizationCompletionReadyForClaim ? 'true' : 'false'}`,
  784. `- handoff.businessExecutionIndex.optimizationCompletionBlockingReasonCount:${summary.observed.handoffBusinessExecutionIndexOptimizationCompletionBlockingReasonCount ?? 'unknown'}`,
  785. `- realProofIntakeBundle.passed:${summary.observed.realProofIntakeBundlePassed ? 'true' : 'false'}`,
  786. `- realProofIntakeBundle.complete:${summary.observed.realProofIntakeBundleComplete ? 'true' : 'false'}`,
  787. `- realProofIntakeBundle.proofOpenCount:${summary.observed.realProofIntakeBundleProofOpenCount ?? 'unknown'}`,
  788. `- realProofIntakeBundle.exportRowCount:${summary.observed.realProofIntakeBundleExportRowCount ?? 'unknown'}`,
  789. `- realProofIntakeBundle.recheckCommandCount:${summary.observed.realProofIntakeBundleRecheckCommandCount ?? 'unknown'}`,
  790. `- realProofIntakeBundle.operatorPackEvidenceRowCount:${summary.observed.realProofIntakeBundleOperatorPackEvidenceRowCount ?? 'unknown'}`,
  791. `- realProofIntakeBundle.operatorPackNextActionCount:${summary.observed.realProofIntakeBundleOperatorPackNextActionCount ?? 'unknown'}`,
  792. `- realProofIntakeBundle.operatorPackMissingProofRequirementCount:${summary.observed.realProofIntakeBundleOperatorPackMissingProofRequirementCount ?? 'unknown'}`,
  793. `- realProofIntakeBundle.operatorPackOwnerArtifactCount:${summary.observed.realProofIntakeBundleOperatorPackOwnerArtifactCount ?? 'unknown'}`,
  794. `- realProofIntakeBundle.longRunReadinessReport:${summary.observed.realProofIntakeBundleLongRunReadinessReport || 'unknown'}`,
  795. `- realProofIntakeBundle.longRunReadinessReady:${summary.observed.realProofIntakeBundleLongRunReadinessReady ? 'true' : 'false'}`,
  796. `- realProofIntakeBundle.longRunReadinessFailCount:${summary.observed.realProofIntakeBundleLongRunReadinessFailCount ?? 'unknown'}`,
  797. `- realProofIntakeBundle.longRunReadinessProofLevel:${summary.observed.realProofIntakeBundleLongRunReadinessProofLevel || 'unknown'}`,
  798. `- realProofIntakeBundle.longRunReadinessDirectCustomerProof:${summary.observed.realProofIntakeBundleLongRunReadinessDirectCustomerProof ? 'true' : 'false'}`,
  799. `- realProofIntakeBundle.videoAbPreflightContext:${summary.observed.realProofIntakeBundleVideoAbPreflightContext ? 'true' : 'false'}`,
  800. `- intakeFieldChecklist.passed:${summary.observed.intakeFieldChecklistPassed ? 'true' : 'false'}`,
  801. `- intakeFieldChecklist.itemCount:${summary.observed.intakeFieldChecklistItemCount ?? 'unknown'}`,
  802. `- intakeFieldChecklist.missingRequiredCount:${summary.observed.intakeFieldChecklistMissingRequiredCount ?? 'unknown'}`,
  803. `- intakeFieldChecklist.placeholderCount:${summary.observed.intakeFieldChecklistPlaceholderCount ?? 'unknown'}`,
  804. `- intakeFieldChecklist.directCustomerProof:${summary.observed.intakeFieldChecklistDirectCustomerProof ? 'true' : 'false'}`,
  805. `- intakeFieldChecklist.ownerGroupCount:${summary.observed.intakeFieldChecklistOwnerGroupCount ?? 'unknown'}`,
  806. `- intakeFieldChecklist.recheckCommandCount:${summary.observed.intakeFieldChecklistRecheckCommandCount ?? 'unknown'}`,
  807. `- proofGapSoftwareForm.passed:${summary.observed.proofGapSoftwareFormPassed ? 'true' : 'false'}`,
  808. `- proofGapSoftwareForm.rowCount:${summary.observed.proofGapSoftwareFormRowCount ?? 'unknown'}`,
  809. `- proofGapSoftwareForm.duplicateIdCount:${summary.observed.proofGapSoftwareFormDuplicateIdCount ?? 'unknown'}`,
  810. `- latestFormIndex.passed:${summary.observed.latestFormIndexPassed ? 'true' : 'false'}`,
  811. `- latestFormIndex.primaryForm:${summary.observed.latestFormIndexPrimaryForm || 'unknown'}`,
  812. `- latestFormIndex.clientList:${summary.observed.latestFormIndexClientList || 'unknown'}`,
  813. `- latestFormIndex.clientListRowCount:${summary.observed.latestFormIndexClientListRowCount ?? 'unknown'}`,
  814. `- latestFormIndex.clientListSourceDuplicateRemovedCount:${summary.observed.latestFormIndexClientListSourceDuplicateRemovedCount ?? 'unknown'}`,
  815. `- latestFormIndex.clientListFinalDuplicateGroupCount:${summary.observed.latestFormIndexClientListFinalDuplicateGroupCount ?? 'unknown'}`,
  816. `- latestFormIndex.clientListDuplicateCountMeaning:${summary.observed.latestFormIndexClientListDuplicateCountMeaning || 'unknown'}`,
  817. `- latestFormIndex.clientListRankContinuous:${summary.observed.latestFormIndexClientListRankContinuous ? 'true' : 'false'}`,
  818. `- latestFormIndex.clientListDirectCustomerProof:${summary.observed.latestFormIndexClientListDirectCustomerProof ? 'true' : 'false'}`,
  819. `- latestFormIndex.businessExecutionIndex:${summary.observed.latestFormIndexBusinessExecutionIndex || 'unknown'}`,
  820. `- latestFormIndex.businessExecutionIndexOperatorPackOwnerArtifactCount:${summary.observed.latestFormIndexBusinessExecutionIndexOperatorPackOwnerArtifactCount ?? 'unknown'}`,
  821. `- latestFormIndex.businessExecutionIndexFieldOwnerRowCount:${summary.observed.latestFormIndexBusinessExecutionIndexFieldOwnerRowCount ?? 'unknown'}`,
  822. `- latestFormIndex.businessExecutionIndexRecheckCommandRowCount:${summary.observed.latestFormIndexBusinessExecutionIndexRecheckCommandRowCount ?? 'unknown'}`,
  823. `- latestFormIndex.intakeFieldChecklist:${summary.observed.latestFormIndexIntakeFieldChecklist || 'unknown'}`,
  824. `- latestFormIndex.intakeFieldChecklistItemCount:${summary.observed.latestFormIndexIntakeFieldChecklistItemCount ?? 'unknown'}`,
  825. `- latestFormIndex.intakeFieldChecklistOwnerGroupCount:${summary.observed.latestFormIndexIntakeFieldChecklistOwnerGroupCount ?? 'unknown'}`,
  826. `- latestFormIndex.intakeFieldChecklistRecheckCommandCount:${summary.observed.latestFormIndexIntakeFieldChecklistRecheckCommandCount ?? 'unknown'}`,
  827. `- latestFormIndex.chinesePlanDoc:${summary.observed.latestFormIndexChinesePlanDoc || 'unknown'}`,
  828. `- latestFormIndex.chineseActionOrderDoc:${summary.observed.latestFormIndexChineseActionOrderDoc || 'unknown'}`,
  829. `- latestFormIndex.actionCount:${summary.observed.latestFormIndexActionCount ?? 'unknown'}`,
  830. `- latestFormIndex.proofOpenCount:${summary.observed.latestFormIndexProofOpenCount ?? 'unknown'}`,
  831. `- latestFormIndex.nextActionsTopCount:${summary.observed.latestFormIndexNextActionsTopCount ?? 'unknown'}`,
  832. `- latestFormIndex.nextActionsProofGapOpenCount:${summary.observed.latestFormIndexNextActionsProofGapOpenCount ?? 'unknown'}`,
  833. `- latestFormIndex.dataRepairCsv:${summary.observed.latestFormIndexDataRepairCsv || 'unknown'}`,
  834. `- latestFormIndex.dataRepairActionCount:${summary.observed.latestFormIndexDataRepairActionCount ?? 'unknown'}`,
  835. `- latestFormIndex.videoRepairCsv:${summary.observed.latestFormIndexVideoRepairCsv || 'unknown'}`,
  836. `- latestFormIndex.videoRepairActionCount:${summary.observed.latestFormIndexVideoRepairActionCount ?? 'unknown'}`,
  837. `- latestFormIndex.homepageRepairCsv:${summary.observed.latestFormIndexHomepageRepairCsv || 'unknown'}`,
  838. `- latestFormIndex.homepageRepairActionCount:${summary.observed.latestFormIndexHomepageRepairActionCount ?? 'unknown'}`,
  839. `- latestFormIndex.ownerRepairSummary:${summary.observed.latestFormIndexOwnerRepairSummary || 'unknown'}`,
  840. `- latestFormIndex.proofGapOperatorPack:${summary.observed.latestFormIndexProofGapOperatorPack || 'unknown'}`,
  841. `- latestFormIndex.proofGapOperatorPackOpenCount:${summary.observed.latestFormIndexProofGapOperatorPackOpenCount ?? 'unknown'}`,
  842. `- latestFormIndex.proofGapOperatorPackOwnerGroupCount:${summary.observed.latestFormIndexProofGapOperatorPackOwnerGroupCount ?? 'unknown'}`,
  843. `- latestFormIndex.proofGapOperatorPackOwnerArtifactCount:${summary.observed.latestFormIndexProofGapOperatorPackOwnerArtifactCount ?? 'unknown'}`,
  844. `- latestFormIndex.proofGapOperatorPackRecheckCommandCount:${summary.observed.latestFormIndexProofGapOperatorPackRecheckCommandCount ?? 'unknown'}`,
  845. `- latestFormIndex.proofGapOperatorPackEvidenceRowCount:${summary.observed.latestFormIndexProofGapOperatorPackEvidenceRowCount ?? 'unknown'}`,
  846. `- latestFormIndex.proofGapOperatorPackNextActionCount:${summary.observed.latestFormIndexProofGapOperatorPackNextActionCount ?? 'unknown'}`,
  847. `- latestFormIndex.proofGapOperatorPackGapIds:${summary.observed.latestFormIndexProofGapOperatorPackGapIds || 'unknown'}`,
  848. `- latestFormIndex.planExecutionStatusOperatorPackOwnerArtifactCount:${summary.observed.latestFormIndexPlanExecutionStatusOperatorPackOwnerArtifactCount ?? 'unknown'}`,
  849. `- latestFormIndex.longRunReadinessReport:${summary.observed.latestFormIndexLongRunReadinessReport || 'unknown'}`,
  850. `- latestFormIndex.longRunReadinessReady:${summary.observed.latestFormIndexLongRunReadinessReady ? 'true' : 'false'}`,
  851. `- latestFormIndex.longRunReadinessFailCount:${summary.observed.latestFormIndexLongRunReadinessFailCount ?? 'unknown'}`,
  852. `- latestFormIndex.longRunReadinessProofLevel:${summary.observed.latestFormIndexLongRunReadinessProofLevel || 'unknown'}`,
  853. `- latestFormIndex.longRunReadinessDirectCustomerProof:${summary.observed.latestFormIndexLongRunReadinessDirectCustomerProof ? 'true' : 'false'}`,
  854. `- localSeedToIntakeWorklist.proofLevel:${summary.observed.localSeedToIntakeWorklistProofLevel || 'unknown'}`,
  855. `- localSeedToIntakeWorklist.directCustomerProof:${summary.observed.localSeedToIntakeWorklistDirectCustomerProof ? 'true' : 'false'}`,
  856. `- localSeedToIntakeWorklist.canCloseProofGap:${summary.observed.localSeedToIntakeWorklistCanCloseProofGap ? 'true' : 'false'}`,
  857. `- localSeedToIntakeWorklist.historyDraftRows:${summary.observed.localSeedToIntakeWorklistHistoryDraftRows ?? 'unknown'}`,
  858. `- localSeedToIntakeWorklist.referenceSeedRows:${summary.observed.localSeedToIntakeWorklistReferenceSeedRows ?? 'unknown'}`,
  859. `- localSeedToIntakeWorklist.candidateSeedRows:${summary.observed.localSeedToIntakeWorklistCandidateSeedRows ?? 'unknown'}`,
  860. `- localSeedToIntakeWorklist.videoWorklistRows:${summary.observed.localSeedToIntakeWorklistVideoWorklistRows ?? 'unknown'}`,
  861. `- localSeedToIntakeWorklist.videoCandidateNeedsResourceRows:${summary.observed.localSeedToIntakeWorklistVideoCandidateNeedsResourceRows ?? 'unknown'}`,
  862. `- localSeedToIntakeWorklist.missingProofRequirementCount:${summary.observed.localSeedToIntakeWorklistMissingProofRequirementCount ?? 'unknown'}`,
  863. `- localSeedToIntakeWorklist.nextActionCount:${summary.observed.localSeedToIntakeWorklistNextActionCount ?? 'unknown'}`,
  864. `- latestFormIndex.localSeedToIntakeWorklist:${summary.observed.latestFormIndexLocalSeedToIntakeWorklistMarkdown || 'unknown'}`,
  865. `- latestFormIndex.localSeedToIntakeWorklistHistoryDraftRows:${summary.observed.latestFormIndexLocalSeedToIntakeWorklistHistoryDraftRows ?? 'unknown'}`,
  866. `- latestFormIndex.localSeedToIntakeWorklistCandidateSeedRows:${summary.observed.latestFormIndexLocalSeedToIntakeWorklistCandidateSeedRows ?? 'unknown'}`,
  867. `- latestFormIndex.localSeedToIntakeWorklistVideoCandidateNeedsResourceRows:${summary.observed.latestFormIndexLocalSeedToIntakeWorklistVideoCandidateNeedsResourceRows ?? 'unknown'}`,
  868. `- latestFormIndex.localSeedToIntakeWorklistCanCloseProofGap:${summary.observed.latestFormIndexLocalSeedToIntakeWorklistCanCloseProofGap ? 'true' : 'false'}`,
  869. `- realProofClosureWorkOrder.passed:${summary.observed.realProofClosureWorkOrderPassed ? 'true' : 'false'}`,
  870. `- realProofClosureWorkOrder.complete:${summary.observed.realProofClosureWorkOrderComplete ? 'true' : 'false'}`,
  871. `- realProofClosureWorkOrder.proofLevel:${summary.observed.realProofClosureWorkOrderProofLevel || 'unknown'}`,
  872. `- realProofClosureWorkOrder.directCustomerProof:${summary.observed.realProofClosureWorkOrderDirectCustomerProof ? 'true' : 'false'}`,
  873. `- realProofClosureWorkOrder.canCloseProofGap:${summary.observed.realProofClosureWorkOrderCanCloseProofGap ? 'true' : 'false'}`,
  874. `- realProofClosureWorkOrder.requiresHumanRealMaterial:${summary.observed.realProofClosureWorkOrderRequiresHumanRealMaterial ? 'true' : 'false'}`,
  875. `- realProofClosureWorkOrder.workOrderCount:${summary.observed.realProofClosureWorkOrderWorkOrderCount ?? 'unknown'}`,
  876. `- realProofClosureWorkOrder.ownerGroupCount:${summary.observed.realProofClosureWorkOrderOwnerGroupCount ?? 'unknown'}`,
  877. `- realProofClosureWorkOrder.ownerArtifactCount:${summary.observed.realProofClosureWorkOrderOwnerArtifactCount ?? 'unknown'}`,
  878. `- realProofClosureWorkOrder.proofGapOpenCount:${summary.observed.realProofClosureWorkOrderProofGapOpenCount ?? 'unknown'}`,
  879. `- realProofClosureWorkOrder.intakeFailureCount:${summary.observed.realProofClosureWorkOrderIntakeFailureCount ?? 'unknown'}`,
  880. `- realProofClosureWorkOrder.realCandidateRows:${summary.observed.realProofClosureWorkOrderRealCandidateRows ?? 'unknown'}`,
  881. `- realProofClosureWorkOrder.videoAbPreflightContext:${summary.observed.realProofClosureWorkOrderVideoAbPreflightContext ? 'true' : 'false'}`,
  882. `- latestFormIndex.realProofClosureWorkOrder:${summary.observed.latestFormIndexRealProofClosureWorkOrderMarkdown || 'unknown'}`,
  883. `- latestFormIndex.realProofClosureWorkOrderWorkOrderCount:${summary.observed.latestFormIndexRealProofClosureWorkOrderWorkOrderCount ?? 'unknown'}`,
  884. `- latestFormIndex.realProofClosureWorkOrderOwnerArtifactCount:${summary.observed.latestFormIndexRealProofClosureWorkOrderOwnerArtifactCount ?? 'unknown'}`,
  885. `- latestFormIndex.realProofClosureWorkOrderCanCloseProofGap:${summary.observed.latestFormIndexRealProofClosureWorkOrderCanCloseProofGap ? 'true' : 'false'}`,
  886. `- handoff.chinesePlanDoc:${summary.observed.handoffChinesePlanDoc || 'unknown'}`,
  887. `- handoff.chineseActionOrderDoc:${summary.observed.handoffChineseActionOrderDoc || 'unknown'}`,
  888. `- handoff.latestFormIndex.ownerGroupCount:${summary.observed.handoffLatestFormIndexOwnerGroupCount ?? 'unknown'}`,
  889. `- handoff.latestFormIndex.recheckCommandCount:${summary.observed.handoffLatestFormIndexRecheckCommandCount ?? 'unknown'}`,
  890. `- handoff.latestFormIndex.nextActionsTopCount:${summary.observed.handoffLatestFormIndexNextActionsTopCount ?? 'unknown'}`,
  891. `- handoff.latestFormIndex.nextActionsProofGapOpenCount:${summary.observed.handoffLatestFormIndexNextActionsProofGapOpenCount ?? 'unknown'}`,
  892. `- handoff.localSeedToIntakeWorklist:${summary.observed.handoffLocalSeedToIntakeWorklistMarkdown || 'unknown'}`,
  893. `- handoff.localSeedToIntakeWorklistHistoryDraftRows:${summary.observed.handoffLocalSeedToIntakeWorklistHistoryDraftRows ?? 'unknown'}`,
  894. `- handoff.localSeedToIntakeWorklistCandidateSeedRows:${summary.observed.handoffLocalSeedToIntakeWorklistCandidateSeedRows ?? 'unknown'}`,
  895. `- handoff.localSeedToIntakeWorklistVideoCandidateNeedsResourceRows:${summary.observed.handoffLocalSeedToIntakeWorklistVideoCandidateNeedsResourceRows ?? 'unknown'}`,
  896. `- handoff.localSeedToIntakeWorklistCanCloseProofGap:${summary.observed.handoffLocalSeedToIntakeWorklistCanCloseProofGap ? 'true' : 'false'}`,
  897. `- handoff.latestFormIndex.localSeedToIntakeWorklist:${summary.observed.handoffLatestFormIndexLocalSeedToIntakeWorklistMarkdown || 'unknown'}`,
  898. `- handoff.latestFormIndex.localSeedToIntakeWorklistHistoryDraftRows:${summary.observed.handoffLatestFormIndexLocalSeedToIntakeWorklistHistoryDraftRows ?? 'unknown'}`,
  899. `- handoff.latestFormIndex.localSeedToIntakeWorklistCandidateSeedRows:${summary.observed.handoffLatestFormIndexLocalSeedToIntakeWorklistCandidateSeedRows ?? 'unknown'}`,
  900. `- handoff.latestFormIndex.localSeedToIntakeWorklistVideoCandidateNeedsResourceRows:${summary.observed.handoffLatestFormIndexLocalSeedToIntakeWorklistVideoCandidateNeedsResourceRows ?? 'unknown'}`,
  901. `- handoff.latestFormIndex.localSeedToIntakeWorklistCanCloseProofGap:${summary.observed.handoffLatestFormIndexLocalSeedToIntakeWorklistCanCloseProofGap ? 'true' : 'false'}`,
  902. `- realGapMaterialAudit.materialFound:${summary.observed.realGapMaterialAuditMaterialFoundCount ?? 'unknown'}/${summary.observed.realGapMaterialAuditGapCount ?? 'unknown'}`,
  903. `- realGapMaterialAudit.openGapCount:${summary.observed.realGapMaterialAuditOpenGapCount ?? 'unknown'}`,
  904. `- realGapMaterialAudit.canCloseProofGap:${summary.observed.realGapMaterialAuditCanCloseProofGap ? 'true' : 'false'}`,
  905. `- latestFormIndex.realGapMaterialAudit:${summary.observed.latestFormIndexRealGapMaterialAuditMarkdown || 'unknown'}`,
  906. `- handoff.realGapMaterialAudit:${summary.observed.handoffRealGapMaterialAuditMarkdown || 'unknown'}`,
  907. `- handoff.latestFormIndex.realGapMaterialAudit:${summary.observed.handoffLatestFormIndexRealGapMaterialAuditMarkdown || 'unknown'}`,
  908. `- handoff.realProofClosureWorkOrder:${summary.observed.handoffRealProofClosureWorkOrderMarkdown || 'unknown'}`,
  909. `- handoff.realProofClosureWorkOrderWorkOrderCount:${summary.observed.handoffRealProofClosureWorkOrderWorkOrderCount ?? 'unknown'}`,
  910. `- handoff.realProofClosureWorkOrderOwnerArtifactCount:${summary.observed.handoffRealProofClosureWorkOrderOwnerArtifactCount ?? 'unknown'}`,
  911. `- handoff.realProofClosureWorkOrderCanCloseProofGap:${summary.observed.handoffRealProofClosureWorkOrderCanCloseProofGap ? 'true' : 'false'}`,
  912. `- handoff.latestFormIndex.realProofClosureWorkOrder:${summary.observed.handoffLatestFormIndexRealProofClosureWorkOrderMarkdown || 'unknown'}`,
  913. `- handoff.latestFormIndex.realProofClosureWorkOrderWorkOrderCount:${summary.observed.handoffLatestFormIndexRealProofClosureWorkOrderWorkOrderCount ?? 'unknown'}`,
  914. `- handoff.latestFormIndex.realProofClosureWorkOrderOwnerArtifactCount:${summary.observed.handoffLatestFormIndexRealProofClosureWorkOrderOwnerArtifactCount ?? 'unknown'}`,
  915. `- handoff.latestFormIndex.realProofClosureWorkOrderCanCloseProofGap:${summary.observed.handoffLatestFormIndexRealProofClosureWorkOrderCanCloseProofGap ? 'true' : 'false'}`,
  916. `- handoff.latestFormIndex.businessFillGuideRowCount:${summary.observed.handoffLatestFormIndexBusinessFillGuideRowCount ?? 'unknown'}`,
  917. `- handoff.longRunReadinessReport:${summary.observed.handoffLongRunReadinessReport || 'unknown'}`,
  918. `- handoff.longRunReadinessReady:${summary.observed.handoffLongRunReadinessReady ? 'true' : 'false'}`,
  919. `- handoff.longRunReadinessFailCount:${summary.observed.handoffLongRunReadinessFailCount ?? 'unknown'}`,
  920. `- handoff.longRunReadinessProofLevel:${summary.observed.handoffLongRunReadinessProofLevel || 'unknown'}`,
  921. `- handoff.longRunReadinessDirectCustomerProof:${summary.observed.handoffLongRunReadinessDirectCustomerProof ? 'true' : 'false'}`,
  922. `- handoff.latestFormIndex.longRunReadinessReport:${summary.observed.handoffLatestFormIndexLongRunReadinessReport || 'unknown'}`,
  923. `- handoff.latestFormIndex.longRunReadinessReady:${summary.observed.handoffLatestFormIndexLongRunReadinessReady ? 'true' : 'false'}`,
  924. `- handoff.latestFormIndex.longRunReadinessFailCount:${summary.observed.handoffLatestFormIndexLongRunReadinessFailCount ?? 'unknown'}`,
  925. `- handoff.planExecutionStatusOperatorPackOwnerArtifactCount:${summary.observed.handoffPlanExecutionStatusOperatorPackOwnerArtifactCount ?? 'unknown'}`,
  926. `- handoff.ownerRepairSummary:${summary.observed.handoffOwnerRepairSummary || 'unknown'}`,
  927. `- handoff.proofGapOperatorPackReport:${summary.observed.handoffProofGapOperatorPackReport || 'unknown'}`,
  928. `- handoff.proofGapOperatorPackSummary:${summary.observed.handoffProofGapOperatorPackSummary || 'unknown'}`,
  929. `- handoff.proofGapOperatorPackOpenCount:${summary.observed.handoffProofGapOperatorPackOpenCount ?? 'unknown'}`,
  930. `- handoff.proofGapOperatorPackOwnerGroupCount:${summary.observed.handoffProofGapOperatorPackOwnerGroupCount ?? 'unknown'}`,
  931. `- handoff.proofGapOperatorPackOwnerArtifactCount:${summary.observed.handoffProofGapOperatorPackOwnerArtifactCount ?? 'unknown'}`,
  932. `- handoff.proofGapOperatorPackRecheckCommandCount:${summary.observed.handoffProofGapOperatorPackRecheckCommandCount ?? 'unknown'}`,
  933. `- handoff.proofGapOperatorPackEvidenceRowCount:${summary.observed.handoffProofGapOperatorPackEvidenceRowCount ?? 'unknown'}`,
  934. `- handoff.proofGapOperatorPackNextActionCount:${summary.observed.handoffProofGapOperatorPackNextActionCount ?? 'unknown'}`,
  935. `- handoff.proofGapOperatorPackGapIds:${summary.observed.handoffProofGapOperatorPackGapIds || 'unknown'}`,
  936. `- handoffProofGapClosure.openCount:${summary.observed.handoffProofGapClosureOpenCount ?? 'unknown'}`,
  937. `- handoffProofGapClosure.businessOpenCount:${summary.observed.handoffProofGapClosureBusinessOpenCount ?? 'unknown'}`,
  938. `- handoffProofGapClosure.businessClosedCount:${summary.observed.handoffProofGapClosureBusinessClosedCount ?? 'unknown'}`,
  939. `- handoffProofGapClosure.businessGapCount:${summary.observed.handoffProofGapClosureBusinessGapCount ?? 'unknown'}`,
  940. `- handoffProofGapClosure.totalRows:${summary.observed.handoffProofGapClosureTotalRows ?? 'unknown'}`,
  941. `- handoffProofGapClosure.precheckOpenCount:${summary.observed.handoffProofGapClosurePrecheckOpenCount ?? 'unknown'}`,
  942. `- handoffProofGapClosure.countSemantics.openCount:${summary.observed.handoffProofGapClosureCountSemanticsOpenCount || 'unknown'}`,
  943. `- handoffProofGapClosure.rows:${summary.observed.handoffProofGapClosureRows ?? 'unknown'}`,
  944. `- handoffProofGapClosure.videoAbMissingProofCount:${summary.observed.handoffVideoAbMissingProofCount ?? 'unknown'}`,
  945. `- handoffProofGapClosure.openRowsWithMissingProofRequirements:${summary.observed.handoffProofGapClosureOpenRowsWithMissingProofRequirements ?? 'unknown'}`,
  946. `- feedbackLoopClosure.passed:${summary.observed.feedbackLoopClosurePassed ? 'true' : 'false'}`,
  947. `- feedbackLoopClosure.complete:${summary.observed.feedbackLoopClosureComplete ? 'true' : 'false'}`,
  948. `- feedbackLoopClosure.directCustomerProof:${summary.observed.feedbackLoopClosureDirectCustomerProof ? 'true' : 'false'}`,
  949. `- feedbackLoopClosure.negativeFeedbackCount:${summary.observed.feedbackLoopClosureNegativeCount ?? 'unknown'}`,
  950. `- feedbackLoopClosure.leakedCount:${summary.observed.feedbackLoopClosureLeakedCount ?? 'unknown'}`,
  951. `- handoffFeedbackLoopClosure.directCustomerProof:${summary.observed.handoffFeedbackLoopClosureDirectCustomerProof ? 'true' : 'false'}`,
  952. `- homepageEvidenceReadiness.ready:${summary.observed.homepageEvidenceReadinessReady ? 'true' : 'false'}`,
  953. `- homepageEvidenceReadiness.directCustomerProof:${summary.observed.homepageEvidenceReadinessDirectCustomerProof ? 'true' : 'false'}`,
  954. `- homepageEvidenceReadiness.proofLevel:${summary.observed.homepageEvidenceReadinessProofLevel || 'unknown'}`,
  955. `- homepageEvidenceReadiness.failureCount:${summary.observed.homepageEvidenceReadinessFailureCount ?? 'unknown'}`,
  956. `- homepageEvidenceReadiness.repairActionCount:${summary.observed.homepageEvidenceReadinessRepairActionCount ?? 'unknown'}`,
  957. `- handoffHomepageEvidenceReadiness.directCustomerProof:${summary.observed.handoffHomepageEvidenceReadinessDirectCustomerProof ? 'true' : 'false'}`,
  958. `- planExecutionStatus.rowCount:${summary.observed.planExecutionStatusRowCount ?? 'unknown'}`,
  959. `- planExecutionStatus.proofOpenCount:${summary.observed.planExecutionStatusProofOpenCount ?? 'unknown'}`,
  960. `- planExecutionStatus.operatorPackOwnerArtifactCount:${summary.observed.planExecutionStatusOperatorPackOwnerArtifactCount ?? 'unknown'}`,
  961. `- planExecutionStatus.businessExecutionOperatorPackOwnerArtifactRowCount:${summary.observed.planExecutionStatusBusinessExecutionOperatorPackOwnerArtifactRowCount ?? 'unknown'}`,
  962. `- planExecutionStatus.guardrailCount:${summary.observed.planExecutionStatusGuardrailCount ?? 'unknown'}`,
  963. `- proofGapClosure.complete:${summary.observed.proofGapClosureComplete ? 'true' : 'false'}`,
  964. `- proofGapClosure.openCount:${summary.observed.proofGapClosureOpenCount}`,
  965. `- proofGapClosure.businessOpenCount:${summary.observed.proofGapClosureBusinessOpenCount ?? 'unknown'}`,
  966. `- proofGapClosure.businessClosedCount:${summary.observed.proofGapClosureBusinessClosedCount ?? 'unknown'}`,
  967. `- proofGapClosure.businessGapCount:${summary.observed.proofGapClosureBusinessGapCount ?? 'unknown'}`,
  968. `- proofGapClosure.totalRows:${summary.observed.proofGapClosureTotalRows ?? 'unknown'}`,
  969. `- proofGapClosure.precheckOpenCount:${summary.observed.proofGapClosurePrecheckOpenCount ?? 'unknown'}`,
  970. `- proofGapClosure.countSemantics.openCount:${summary.observed.proofGapClosureCountSemanticsOpenCount || 'unknown'}`,
  971. `- proofGapClosure.openRowsWithMissingProofRequirements:${summary.observed.proofGapClosureOpenRowsWithMissingProofRequirements ?? 'unknown'}`,
  972. '',
  973. '## 检查项',
  974. '',
  975. '| 状态 | 检查项 | 证据 | 下一步 |',
  976. '| --- | --- | --- | --- |',
  977. ...summary.checks.map(item => `| ${item.status} | ${escapeCell(item.title)} | ${escapeCell(item.evidence)} | ${escapeCell(item.next)} |`)
  978. ];
  979. return lines.join('\n');
  980. }
  981. function check(id, title, passed, evidence, next, status = 'pass') {
  982. return { id, title, status: passed ? status : 'fail', evidence, next: passed ? '继续每轮刷新。' : next };
  983. }
  984. function hasEvidenceLevels(evidence) {
  985. const counts = evidence?.counts || {};
  986. return Object.prototype.hasOwnProperty.call(counts, 'real_evidence') &&
  987. Object.prototype.hasOwnProperty.call(counts, 'smoke_or_local') &&
  988. Object.prototype.hasOwnProperty.call(counts, 'not_business_proof');
  989. }
  990. function hasVideoRuntimePreflightBoundary(evidence) {
  991. const entries = Array.isArray(evidence?.entries) ? evidence.entries.filter(item => item.type === 'video-ab-runtime-preflight') : [];
  992. if (!entries.length) return true;
  993. return entries.every(item =>
  994. item.proofLevel === 'not_business_proof' &&
  995. ['not_ready', 'ready_to_run'].includes(String(item.status || '')) &&
  996. String(item.summary || '').includes('generatedBy=acceptance:video-ab-preflight') &&
  997. String(item.next || '').includes('video-hit-rate-summary.json')
  998. );
  999. }
  1000. function hasStatusId(status, id) {
  1001. return Array.isArray(status?.items) && status.items.some(item => item.id === id);
  1002. }
  1003. function hasProofActionOrder(text) {
  1004. return Boolean(text) &&
  1005. text.includes('提号长期优化补证操作顺序') &&
  1006. text.includes('npm run intake:readiness') &&
  1007. text.includes('npm run longrun:readiness') &&
  1008. text.includes('npm run customer-effect:audit') &&
  1009. text.includes('optimization:completion') &&
  1010. text.includes('readyForClaim=true') &&
  1011. text.includes('sample、smoke') &&
  1012. text.includes('客户效果不可宣称');
  1013. }
  1014. function hasBusinessProofProgress(progress) {
  1015. if (!progress || progress.complete !== false) return false;
  1016. if (!Array.isArray(progress.rows) || progress.rows.length !== 15) return false;
  1017. const statuses = new Set(progress.rows.map(row => row.status));
  1018. return ['pass', 'pending', 'fail', 'blocked_by_external_data'].some(status => statuses.has(status)) &&
  1019. progress.counts &&
  1020. Number.isFinite(Number(progress.counts.pass || 0)) &&
  1021. Number.isFinite(Number(progress.counts.pending || 0)) &&
  1022. Number.isFinite(Number(progress.counts.fail || 0)) &&
  1023. Number.isFinite(Number(progress.counts.blocked_by_external_data || 0));
  1024. }
  1025. function hasBusinessProofProgressOperatorOwnerArtifacts(progress, pack) {
  1026. if (!progress || !pack) return false;
  1027. const expectedCount = expectedProofGapOwnerArtifactCount(pack);
  1028. return Number(progress.operatorPackOwnerArtifactCount || 0) === Number(pack.ownerArtifactCount || 0) &&
  1029. Number(progress.operatorPackOwnerArtifactCount || 0) >= expectedCount &&
  1030. hasProofGapOperatorOwnerArtifacts(progress.operatorPackOwnerArtifacts, pack);
  1031. }
  1032. function hasBusinessProofNextActions(nextActions) {
  1033. const actions = Array.isArray(nextActions?.actions) ? nextActions.actions : [];
  1034. const topActions = Array.isArray(nextActions?.topActions) ? nextActions.topActions : [];
  1035. return Boolean(nextActions) &&
  1036. nextActions.complete === false &&
  1037. actions.length >= 1 &&
  1038. Number(nextActions.actionCount || 0) === actions.length &&
  1039. Number(nextActions.ownerGroupCount || 0) >= 1 &&
  1040. topActions.length >= Math.min(5, actions.length) &&
  1041. topActions.every(action => action.title && action.owner && action.command && action.acceptance) &&
  1042. actions.every(action => action.title && action.owner && action.command && action.acceptance) &&
  1043. nextActions.sourceState &&
  1044. typeof nextActions.sourceState.progressComplete === 'boolean';
  1045. }
  1046. function hasProofGapOperatorPack(pack) {
  1047. if (!pack || pack.passed !== true || pack.complete !== false) return false;
  1048. if (pack.directCustomerProof !== false || pack.proofLevel !== 'not_business_proof') return false;
  1049. const gapIds = proofGapOperatorGapIds(pack);
  1050. const requiredIds = [
  1051. 'historical-dataset',
  1052. 'video-real-candidate',
  1053. 'video-ab-live-proof',
  1054. 'live-provider-overnight-proof',
  1055. 'manual-review-and-customer-effect'
  1056. ];
  1057. if (!requiredIds.every(id => gapIds.includes(id))) return false;
  1058. const recheckCommandCount = Number.isFinite(Number(pack.recheckCommandCount))
  1059. ? Number(pack.recheckCommandCount)
  1060. : uniqueCount(pack.rows, row => row.recheckCommands);
  1061. const fillFileCount = Number.isFinite(Number(pack.fillFileCount))
  1062. ? Number(pack.fillFileCount)
  1063. : uniqueCount(pack.rows, row => row.fillFiles);
  1064. const ownerGroupCount = Number.isFinite(Number(pack.ownerGroupCount))
  1065. ? Number(pack.ownerGroupCount)
  1066. : (Array.isArray(pack.ownerGroups) ? pack.ownerGroups.length : 0);
  1067. const ownerArtifactCount = Number.isFinite(Number(pack.ownerArtifactCount))
  1068. ? Number(pack.ownerArtifactCount)
  1069. : (Array.isArray(pack.ownerArtifacts) ? pack.ownerArtifacts.length : 0);
  1070. const expectedOwnerArtifactCount = expectedProofGapOwnerArtifactCount(pack);
  1071. const evidenceRowCount = Number.isFinite(Number(pack.evidenceRowCount))
  1072. ? Number(pack.evidenceRowCount)
  1073. : countRowsWith(pack.rows, row => row.evidence);
  1074. const nextActionCount = Number.isFinite(Number(pack.nextActionCount))
  1075. ? Number(pack.nextActionCount)
  1076. : countRowsWith(pack.rows, row => row.nextAction);
  1077. return Number(pack.rowCount || 0) >= requiredIds.length &&
  1078. Number(pack.openCount || 0) >= 1 &&
  1079. recheckCommandCount >= 5 &&
  1080. fillFileCount >= 3 &&
  1081. ownerGroupCount >= expectedOwnerArtifactCount &&
  1082. ownerArtifactCount >= expectedOwnerArtifactCount &&
  1083. hasProofGapOperatorOwnerArtifacts(pack.ownerArtifacts, pack) &&
  1084. evidenceRowCount >= requiredIds.length &&
  1085. nextActionCount >= requiredIds.length &&
  1086. Array.isArray(pack.rows) &&
  1087. pack.rows.length >= requiredIds.length &&
  1088. pack.rows.every(row =>
  1089. row.gapId &&
  1090. row.owner &&
  1091. Array.isArray(row.fillFiles) &&
  1092. row.fillFiles.length >= 1 &&
  1093. Array.isArray(row.recheckCommands) &&
  1094. row.recheckCommands.length >= 1 &&
  1095. row.evidence &&
  1096. row.nextAction &&
  1097. /不.*证明|不得宣称|不等同于/.test(String(row.boundary || ''))
  1098. );
  1099. }
  1100. function hasProofGapOperatorPackMissingProofByGap(pack) {
  1101. if (!pack || !Array.isArray(pack.rows)) return false;
  1102. const openRows = pack.rows.filter(row => row.status !== 'closed');
  1103. const expectedOpenCount = Number(pack.openCount || 0);
  1104. if (expectedOpenCount > 0 && openRows.length !== expectedOpenCount) return false;
  1105. return openRows.length >= 1 &&
  1106. openRows.every(row => row.gapId && Array.isArray(row.missingProofRequirements) && row.missingProofRequirements.length >= 1) &&
  1107. Number(pack.missingProofRequirementCount || 0) >= openRows.length;
  1108. }
  1109. function hasProofGapClosureMissingProofRequirements(closure) {
  1110. if (!closure || !Array.isArray(closure.rows)) return false;
  1111. const openBusinessRows = closure.rows.filter(row => row.status !== 'closed' && row.id !== 'intake-readiness');
  1112. const expectedOpenCount = Number(closure.openCount || 0);
  1113. if (expectedOpenCount > 0 && openBusinessRows.length !== expectedOpenCount) return false;
  1114. return openBusinessRows.length >= 1 &&
  1115. openBusinessRows.every(row => Array.isArray(row.missingProofRequirements) && row.missingProofRequirements.length >= 1);
  1116. }
  1117. function hasProofGapClosureRecursiveProofArtifactDiscovery(closure) {
  1118. if (!closure || !Array.isArray(closure.rows)) return false;
  1119. const historicalRow = findRowById(closure.rows, 'historical-dataset');
  1120. const customerEffectRow = findRowById(closure.rows, 'manual-review-and-customer-effect');
  1121. return hasDiscoveredProofArtifactSources(closure) &&
  1122. hasConcreteHistoricalFailure(historicalRow) &&
  1123. hasConcreteCustomerEffectFailure(customerEffectRow);
  1124. }
  1125. function hasProofGapClosureCountSemantics(closure) {
  1126. if (!closure || !Array.isArray(closure.rows)) return false;
  1127. const precheckRows = closure.rows.filter(row => row.id === 'intake-readiness');
  1128. const businessRows = closure.rows.filter(row => row.id !== 'intake-readiness');
  1129. const businessOpenRows = businessRows.filter(row => row.status !== 'closed');
  1130. const businessClosedRows = businessRows.filter(row => row.status === 'closed');
  1131. const precheckOpenRows = precheckRows.filter(row => row.status !== 'closed');
  1132. const precheckClosedRows = precheckRows.filter(row => row.status === 'closed');
  1133. const countSemantics = closure.countSemantics || {};
  1134. return Number(closure.openCount || 0) === businessOpenRows.length &&
  1135. Number(closure.businessOpenCount || 0) === businessOpenRows.length &&
  1136. Number(closure.closedCount || 0) === businessClosedRows.length &&
  1137. Number(closure.businessClosedCount || 0) === businessClosedRows.length &&
  1138. Number(closure.businessGapCount || 0) === businessRows.length &&
  1139. Number(closure.totalRows || 0) === closure.rows.length &&
  1140. Number(closure.precheckRowCount || 0) === precheckRows.length &&
  1141. Number(closure.precheckOpenCount || 0) === precheckOpenRows.length &&
  1142. Number(closure.precheckClosedCount || 0) === precheckClosedRows.length &&
  1143. countSemantics.openCount === 'business_gap_open_count_excludes_precheck_rows' &&
  1144. countSemantics.businessOpenCount === 'same_as_openCount' &&
  1145. countSemantics.precheckOpenCount === 'precheck_rows_reported_separately';
  1146. }
  1147. function hasBusinessExecutionIndex(index, nextActions, proofGap) {
  1148. if (!index || index.passed !== true) return false;
  1149. if (index.directCustomerProof !== false) return false;
  1150. if (index.proofLevel !== 'smoke_or_local') return false;
  1151. const expectedActions = Array.isArray(nextActions?.actions) ? nextActions.actions.length : 0;
  1152. if (expectedActions > 0 && Number(index.actionRowCount || 0) < expectedActions) return false;
  1153. const expectedOpen = Number(nextActions?.sourceState?.proofGapOpenCount || proofGap?.unresolvedCount || 0);
  1154. if (expectedOpen > 0 && Number(index.proofOpenCount || 0) !== expectedOpen) return false;
  1155. if (Number(index.rowCount || 0) < Math.max(1, expectedActions)) return false;
  1156. if (Number(index.repairRowCount || 0) < 1) return false;
  1157. if (Number(index.fieldOwnerRowCount || 0) < 1) return false;
  1158. if (Number(index.recheckCommandRowCount || 0) < 1) return false;
  1159. if (!Array.isArray(index.rows) || index.rows.length !== Number(index.rowCount || 0)) return false;
  1160. const types = new Set(index.rows.map(row => String(row.type || '')));
  1161. if (!types.has('负责人行动') ||
  1162. !types.has('修复清单') ||
  1163. !types.has('字段补齐负责人') ||
  1164. !types.has('补齐后复验命令') ||
  1165. !types.has('关键文件')) return false;
  1166. return index.rows.every(row =>
  1167. row.owner &&
  1168. row.title &&
  1169. row.acceptance &&
  1170. String(row.boundary || '').includes('不证明')
  1171. );
  1172. }
  1173. function hasBusinessExecutionOperatorOwnerArtifacts(index, pack) {
  1174. if (!index || !pack) return false;
  1175. const expectedArtifacts = expectedProofGapOwnerArtifacts(pack);
  1176. const expectedCount = expectedArtifacts.length;
  1177. if (Number(index.operatorPackOwnerArtifactCount || 0) !== Number(pack.ownerArtifactCount || 0)) return false;
  1178. if (Number(index.operatorPackOwnerArtifactCount || 0) < expectedCount) return false;
  1179. if (Number(index.operatorPackOwnerArtifactRowCount || 0) < expectedCount) return false;
  1180. if (!hasProofGapOperatorOwnerArtifacts(index.operatorPackOwnerArtifacts, pack)) return false;
  1181. if (!String(index.sourceFiles?.proofGapOperatorPack || '').includes('proof-gap-operator-pack-summary.json')) return false;
  1182. return expectedArtifacts.every(item =>
  1183. hasBusinessExecutionOperatorOwnerRow(index, item.owner, proofGapOwnerSlug(item))
  1184. );
  1185. }
  1186. function hasBusinessExecutionOperatorOwnerRow(index, owner, slug) {
  1187. return (Array.isArray(index?.rows) ? index.rows : []).some(row =>
  1188. row.owner === owner &&
  1189. row.type === '操作包负责人附件' &&
  1190. String(row.mainFile || '').includes(`by-owner/${slug}.md`) &&
  1191. String(row.csv || '').includes(`by-owner/${slug}.csv`) &&
  1192. Number(row.actionCount || 0) >= 1 &&
  1193. String(row.acceptance || '').includes('evidenceRowCount=') &&
  1194. String(row.acceptance || '').includes('nextActionCount=') &&
  1195. String(row.boundary || '').includes('不证明客户效果')
  1196. );
  1197. }
  1198. function hasBusinessExecutionLongRunReadiness(index, readiness) {
  1199. if (!index || !readiness) return false;
  1200. const row = findExecutionRow(index, '长跑前置门禁报告');
  1201. const expectedReady = Boolean(readiness.ready);
  1202. const expectedFailCount = Number(readiness.counts?.fail ?? readiness.failCount ?? 0);
  1203. return Boolean(row) &&
  1204. String(index.sourceFiles?.longRunReadiness || '').includes('long-run-readiness-summary.json') &&
  1205. String(row.mainFile || '').includes('long-run-readiness-report.md') &&
  1206. String(row.expectedArtifact || '').includes('long-run-readiness-summary.json') &&
  1207. (row.status === (expectedReady ? 'ready' : 'blocked')) &&
  1208. Number(row.actionCount || 0) === expectedFailCount &&
  1209. String(row.acceptance || '').includes(`ready=${expectedReady}`) &&
  1210. String(row.acceptance || '').includes(`failCount=${expectedFailCount}`) &&
  1211. String(row.boundary || '').includes('不证明客户效果') &&
  1212. String(row.boundary || '').includes('不能启动');
  1213. }
  1214. function hasOptimizationCompletionBoundary(completion) {
  1215. if (!completion) return false;
  1216. const blockingReasonCount = Array.isArray(completion.blockingReasons)
  1217. ? completion.blockingReasons.length
  1218. : Number(completion.blockingReasonCount || 0);
  1219. return completion.complete === false &&
  1220. completion.readyForClaim === false &&
  1221. Number(completion.proofGapOpenCount || 0) >= 1 &&
  1222. blockingReasonCount >= 1 &&
  1223. completion.canClaim?.tihaoRateImproved === false &&
  1224. completion.canClaim?.customerEffectAchieved === false &&
  1225. completion.canClaim?.manualSupplementReduced === false;
  1226. }
  1227. function hasOptimizationCompletionMissingProofRequirements(completion) {
  1228. if (!completion || !Array.isArray(completion.blockingReasons)) return false;
  1229. if (completion.blockingReasons.length < 1) return false;
  1230. return completion.blockingReasons.every(item =>
  1231. item.status !== 'pass' &&
  1232. Array.isArray(item.missingProofRequirements) &&
  1233. item.missingProofRequirements.length >= 1
  1234. );
  1235. }
  1236. function hasOptimizationCompletionRecursiveProofArtifactDiscovery(completion) {
  1237. if (!completion) return false;
  1238. const blockingReasons = Array.isArray(completion.blockingReasons) ? completion.blockingReasons : [];
  1239. const gates = Array.isArray(completion.gates) ? completion.gates : [];
  1240. const proofGapClosureReason = findRowById(blockingReasons, 'proof-gap-closure') || findRowById(gates, 'proof-gap-closure');
  1241. const customerEffectReason = findRowById(blockingReasons, 'customer-effect-proof') || findRowById(gates, 'customer-effect-proof');
  1242. return hasDiscoveredProofArtifactSources(completion) &&
  1243. hasConcreteHistoricalFailure(proofGapClosureReason, 'historical-dataset: ') &&
  1244. hasConcreteCustomerEffectFailure(customerEffectReason);
  1245. }
  1246. function hasBusinessExecutionOptimizationCompletion(index, completion) {
  1247. if (!index || !completion) return false;
  1248. const row = findExecutionRow(index, '优化完成度审计报告');
  1249. const expectedReady = Boolean(completion.readyForClaim);
  1250. const expectedBlockingReasonCount = Array.isArray(completion.blockingReasons)
  1251. ? completion.blockingReasons.length
  1252. : Number(completion.blockingReasonCount || 0);
  1253. return Boolean(row) &&
  1254. String(index.sourceFiles?.optimizationCompletion || '').includes('optimization-completion-summary.json') &&
  1255. String(row.mainFile || '').includes('optimization-completion-report.md') &&
  1256. String(row.expectedArtifact || '').includes('optimization-completion-summary.json') &&
  1257. row.status === (expectedReady ? 'ready_for_claim' : 'blocked') &&
  1258. Number(row.actionCount || 0) === expectedBlockingReasonCount &&
  1259. String(row.acceptance || '').includes(`readyForClaim=${expectedReady}`) &&
  1260. String(row.acceptance || '').includes(`proofGapOpenCount=${Number(completion.proofGapOpenCount || 0)}`) &&
  1261. String(row.boundary || '').includes('不得宣称提号率提升') &&
  1262. String(row.boundary || '').includes('客户效果达标') &&
  1263. String(row.boundary || '').includes('人工补号量下降');
  1264. }
  1265. function findRowById(rows, id) {
  1266. return (Array.isArray(rows) ? rows : [])
  1267. .find(row => String(row.id || row.gapId || '') === id);
  1268. }
  1269. function hasDiscoveredProofArtifactSources(summary) {
  1270. const history = String(summary?.sourceFiles?.history || '').replace(/\\/g, '/');
  1271. const customerEffect = String(summary?.sourceFiles?.customerEffect || '').replace(/\\/g, '/');
  1272. return history.includes('historical-dataset-audit.json') &&
  1273. customerEffect.includes('customer-effect-summary.json');
  1274. }
  1275. function hasConcreteHistoricalFailure(row, prefix = '') {
  1276. if (!row) return false;
  1277. const requirements = missingProofRequirementText(row);
  1278. return row.status !== 'closed' &&
  1279. row.status !== 'pass' &&
  1280. !isOnlyMissingFile(row, 'historical-dataset-audit.json') &&
  1281. (
  1282. requirements.includes(`${prefix}briefCount>=5`) ||
  1283. requirements.includes(`${prefix}items.length>=5`) ||
  1284. requirements.includes(`${prefix}acceptance.readyForCustomerEffectProof=true`)
  1285. );
  1286. }
  1287. function hasConcreteCustomerEffectFailure(row) {
  1288. if (!row) return false;
  1289. const requirements = missingProofRequirementText(row);
  1290. return row.status !== 'closed' &&
  1291. row.status !== 'pass' &&
  1292. !isOnlyMissingFile(row, 'customer-effect-summary.json') &&
  1293. (
  1294. requirements.includes('acceptance.customerSelectedRateMeasured=true') ||
  1295. requirements.includes('currentHistoryAudit.briefCount>=5') ||
  1296. requirements.includes('acceptance.overallPass=true')
  1297. );
  1298. }
  1299. function isOnlyMissingFile(row, fileName) {
  1300. const requirements = Array.isArray(row?.missingProofRequirements)
  1301. ? row.missingProofRequirements.map(item => String(item || '').trim()).filter(Boolean)
  1302. : [];
  1303. return requirements.length === 1 && requirements[0] === `missing ${fileName}`;
  1304. }
  1305. function missingProofRequirementText(row) {
  1306. return (Array.isArray(row?.missingProofRequirements) ? row.missingProofRequirements : [])
  1307. .map(item => String(item || ''))
  1308. .join('\n');
  1309. }
  1310. function findExecutionRow(index, title) {
  1311. return (Array.isArray(index?.rows) ? index.rows : [])
  1312. .find(row => String(row.title || '').includes(title));
  1313. }
  1314. function findExecutionRowTarget(index, title) {
  1315. const row = findExecutionRow(index, title);
  1316. return row?.mainFile || row?.expectedArtifact || '';
  1317. }
  1318. function hasRealProofBundleLongRunReadiness(bundle, readiness) {
  1319. if (!bundle || !readiness) return false;
  1320. if (bundle.passed !== true || bundle.directCustomerProof !== false || bundle.proofLevel !== 'not_business_proof') return false;
  1321. const expectedReady = Boolean(readiness.ready);
  1322. const expectedFailCount = Number(readiness.counts?.fail ?? readiness.failCount ?? 0);
  1323. const source = bundle.sourceState || {};
  1324. const reportPath = findTemplateFilePath(bundle, 'long-run-readiness-report.md');
  1325. const hasRecheckCommand = Array.isArray(bundle.recheckCommands) &&
  1326. bundle.recheckCommands.some(item => String(item.command || '').includes('longrun:readiness') && hasBoundaryText(item.boundary));
  1327. const hasExportRow = Array.isArray(bundle.exportRows) &&
  1328. bundle.exportRows.some(item => String(item.target || '').includes('long-run-readiness-report.md') || String(item.target || '').includes('longrun:readiness'));
  1329. return reportPath.includes('long-run-readiness-report.md') &&
  1330. Boolean(source.longRunReadinessReady) === expectedReady &&
  1331. Number(source.longRunReadinessFailCount || 0) === expectedFailCount &&
  1332. source.longRunReadinessProofLevel === 'not_business_proof' &&
  1333. source.longRunReadinessDirectCustomerProof === false &&
  1334. hasRecheckCommand &&
  1335. hasExportRow;
  1336. }
  1337. function findTemplateFilePath(bundle, fileName) {
  1338. const row = (Array.isArray(bundle?.templateFiles) ? bundle.templateFiles : [])
  1339. .find(item => String(item.path || '').includes(fileName));
  1340. return row?.path || '';
  1341. }
  1342. function hasIntakeFieldChecklist(checklist) {
  1343. if (!checklist || checklist.passed !== true) return false;
  1344. if (checklist.directCustomerProof !== false) return false;
  1345. if (checklist.proofLevel !== 'smoke_or_local') return false;
  1346. if (Number(checklist.itemCount || 0) < 1) return false;
  1347. if (!Number.isFinite(Number(checklist.missingRequiredCount))) return false;
  1348. if (!Number.isFinite(Number(checklist.placeholderCount))) return false;
  1349. if (!hasChecklistOwnerGroups(checklist)) return false;
  1350. if (!hasChecklistRecheckCommands(checklist)) return false;
  1351. return Array.isArray(checklist.items) &&
  1352. checklist.items.length === Number(checklist.itemCount || 0) &&
  1353. checklist.items.every(item =>
  1354. item.owner &&
  1355. item.section &&
  1356. item.issueType &&
  1357. item.field &&
  1358. item.fixAction &&
  1359. String(item.boundary || '').includes('不证明')
  1360. );
  1361. }
  1362. function hasChecklistOwnerGroups(checklist) {
  1363. return Array.isArray(checklist?.ownerGroups) &&
  1364. checklist.ownerGroups.length >= 1 &&
  1365. checklist.ownerGroups.every(item =>
  1366. item.owner &&
  1367. item.section &&
  1368. Number(item.itemCount || 0) >= 1 &&
  1369. Array.isArray(item.files) &&
  1370. item.files.length >= 1 &&
  1371. item.nextAction &&
  1372. item.acceptance &&
  1373. String(item.boundary || '').includes('不证明')
  1374. );
  1375. }
  1376. function hasChecklistRecheckCommands(checklist) {
  1377. return Array.isArray(checklist?.recheckCommands) &&
  1378. checklist.recheckCommands.some(item => String(item.command || '').includes('intake:readiness')) &&
  1379. checklist.recheckCommands.some(item => String(item.command || '').includes('round:refresh')) &&
  1380. checklist.recheckCommands.every(item =>
  1381. item.stage &&
  1382. item.owner &&
  1383. item.command &&
  1384. item.acceptance &&
  1385. hasBoundaryText(item.boundary)
  1386. );
  1387. }
  1388. function hasBoundaryText(value) {
  1389. const text = String(value || '');
  1390. return text.includes('不证明') || text.includes('不得') || text.includes('不能') || text.includes('只证明');
  1391. }
  1392. function hasDedupedBusinessProofNextActions(nextActions, proofGap) {
  1393. if (!nextActions || !Array.isArray(nextActions.actions)) return false;
  1394. const expectedCount = Number(nextActions.sourceState?.proofGapOpenCount || proofGap?.unresolvedCount || 0);
  1395. if (expectedCount > 0 && nextActions.actions.length !== expectedCount) return false;
  1396. const titles = nextActions.actions.map(action => String(action.title || ''));
  1397. const uniqueTitles = new Set(titles);
  1398. if (uniqueTitles.size !== titles.length) return false;
  1399. const requiredTitles = ['填真实历史 Brief', '填真实视频资源', '人工复核标注', '真实视频 A/B 验收'];
  1400. if (expectedCount >= requiredTitles.length && !requiredTitles.every(title => uniqueTitles.has(title))) return false;
  1401. const forbiddenDuplicateTitles = ['真实材料预审', '历史数据导入', '历史数据审计', '视频资源就绪审计', '长跑前门禁', '运行策略矩阵', '视频 A/B 验收', '商务复核指标', '客户效果审计'];
  1402. if (titles.some(title => forbiddenDuplicateTitles.includes(title))) return false;
  1403. const ownerSet = new Set(nextActions.actions.map(action => String(action.owner || '')));
  1404. return ownerSet.has('商务') && (ownerSet.has('商务/投放') || ownerSet.has('技术/AI'));
  1405. }
  1406. function hasBusinessProofNextActionsMissingProofRequirements(nextActions) {
  1407. if (!nextActions || !Array.isArray(nextActions.actions)) return false;
  1408. const required = new Map([
  1409. ['填真实历史 Brief', 'briefCount>=5'],
  1410. ['填真实视频资源', 'counts.realCandidateRows>=1'],
  1411. ['人工复核标注', 'acceptance.customerSelectedRateMeasured=true'],
  1412. ['真实视频 A/B 验收', 'proofContext.mode=live'],
  1413. ['真实 live/provider 长跑证明', 'manifest.liveEnabled=true']
  1414. ]);
  1415. if (!nextActions.actions.length) return false;
  1416. for (const action of nextActions.actions) {
  1417. const title = String(action.title || '');
  1418. const missing = Array.isArray(action?.missingProofRequirements) ? action.missingProofRequirements : [];
  1419. if (missing.length < 1) return false;
  1420. const requiredProof = required.get(title);
  1421. if (requiredProof && !missing.includes(requiredProof)) return false;
  1422. }
  1423. return countRowsWithMissingProofRequirements(nextActions.actions) === nextActions.actions.length;
  1424. }
  1425. function hasBusinessProofNextActionsStalePipelineSuppression(nextActions) {
  1426. if (!nextActions || !nextActions.sourceState) return false;
  1427. if (nextActions.sourceState.pipelineStale !== true) return true;
  1428. const actions = Array.isArray(nextActions.actions) ? nextActions.actions : [];
  1429. return Number(nextActions.sourceState.pipelineNextActionCount || 0) === 0 &&
  1430. Number(nextActions.sourceState.pipelineSuppressedNextActionCount || 0) >= 1 &&
  1431. String(nextActions.sourceState.pipelineStaleReason || '').includes('stale') &&
  1432. actions.every(action => !String(action.source || '').includes('optimization-pipeline'));
  1433. }
  1434. function hasHandoffBusinessProofNextActions(entry, source) {
  1435. if (!entry || !source) return false;
  1436. const sourceActions = Array.isArray(source.actions) ? source.actions : [];
  1437. const topActions = Array.isArray(entry.topActions) ? entry.topActions : [];
  1438. const expectedOpen = Number(source.sourceState?.proofGapOpenCount || 0);
  1439. const expectedOwnerCount = Array.isArray(source.ownerGroups) && source.ownerGroups.length
  1440. ? source.ownerGroups.length
  1441. : Math.max(1, new Set(sourceActions.map(action => action.owner).filter(Boolean)).size);
  1442. if (sourceActions.length < 1) return false;
  1443. return entry.complete === false &&
  1444. Number(entry.actionCount || 0) === sourceActions.length &&
  1445. Number(entry.ownerGroupCount || 0) >= expectedOwnerCount &&
  1446. Number(entry.ownerArtifactCount || 0) >= expectedOwnerCount &&
  1447. topActions.length >= Math.min(5, sourceActions.length) &&
  1448. topActions.every(action => action.owner && action.title && action.command && action.acceptance) &&
  1449. topActions.some(action => String(action.command || '').includes('history-data-template.csv')) &&
  1450. topActions.some(action => String(action.command || '').includes('acceptance:video-ab')) &&
  1451. (expectedOpen <= 0 || Number(entry.sourceState?.proofGapOpenCount || 0) === expectedOpen);
  1452. }
  1453. function hasPassingProofGapSoftwareForm(form, proofGap) {
  1454. if (!form) return false;
  1455. if (form.passed !== true || form.headerMatches !== true || form.rowWidthOk !== true) return false;
  1456. if (Number(form.duplicateIdCount || 0) !== 0) return false;
  1457. const requiredTitles = ['真实历史 Brief 数据集', '真实视频资源表', '真实视频证据 A/B', '真实 live/provider 长跑证明', '商务复核和客户效果'];
  1458. if (Number(form.rowCount || 0) !== requiredTitles.length) return false;
  1459. const actualTitles = new Set(Array.isArray(form.actualTitles) ? form.actualTitles : []);
  1460. return requiredTitles.every(title => actualTitles.has(title));
  1461. }
  1462. function hasPassingLatestFormIndex(index) {
  1463. if (!index || index.passed !== true) return false;
  1464. if (index.primaryForm?.directCustomerProof !== false) return false;
  1465. if (index.clientList?.directCustomerProof !== false) return false;
  1466. if (!String(index.primaryForm?.csv || '').includes('tihao-proof-gap-software-form.csv')) return false;
  1467. if (!String(index.clientList?.csv || '').includes('software-client-list.final.csv')) return false;
  1468. if (!Array.isArray(index.nextActions?.ownerArtifacts) || index.nextActions.ownerArtifacts.length < 1) return false;
  1469. if (!Number.isFinite(Number(index.nextActions?.actionCount)) || Number(index.nextActions.actionCount) < 1) return false;
  1470. const owners = new Set(index.nextActions.ownerArtifacts.map(item => String(item.owner || '')));
  1471. return owners.has('商务') && (owners.has('商务/投放') || owners.has('技术/AI'));
  1472. }
  1473. function hasLatestFormClientListDeliveryQuality(index) {
  1474. const item = index?.clientList;
  1475. if (!item) return false;
  1476. if (item.directCustomerProof !== false) return false;
  1477. if (!String(item.csv || '').includes('software-client-list.final.csv')) return false;
  1478. if (!String(item.summary || '').includes('software-client-list.final.summary.json')) return false;
  1479. if (!Number.isFinite(Number(item.rowCount)) || Number(item.rowCount) < 1) return false;
  1480. if (!Number.isFinite(Number(item.sourceDuplicateRemovedCount)) || Number(item.sourceDuplicateRemovedCount) < 0) return false;
  1481. if (!Number.isFinite(Number(item.finalDuplicateGroupCount)) || Number(item.finalDuplicateGroupCount) !== 0) return false;
  1482. if (item.duplicateCountMeaning !== 'source_duplicate_removed_count') return false;
  1483. return item.rankContinuous === true;
  1484. }
  1485. function hasRealProofClosureWorkOrder(item) {
  1486. if (!item) return false;
  1487. const ownerArtifactCount = realProofWorkOrderOwnerArtifactCount(item);
  1488. const ownerGroupCount = Number(item.ownerGroupCount || 0);
  1489. return item.passed === true &&
  1490. item.complete === false &&
  1491. item.proofLevel === 'not_business_proof' &&
  1492. item.materialType === 'real_proof_closure_work_order' &&
  1493. item.directCustomerProof === false &&
  1494. item.canCloseProofGap === false &&
  1495. item.requiresHumanRealMaterial === true &&
  1496. Number(item.workOrderCount || 0) >= 5 &&
  1497. ownerGroupCount >= 1 &&
  1498. ownerArtifactCount >= ownerGroupCount &&
  1499. Number(item.sourceState?.proofGapOpenCount || 0) >= 1 &&
  1500. Number(item.sourceState?.intakeFailureCount || 0) >= 1 &&
  1501. Number(item.sourceState?.realCandidateRows || 0) >= 0;
  1502. }
  1503. function hasActionableRealProofClosureWorkOrder(item) {
  1504. if (!hasRealProofClosureWorkOrder(item)) return false;
  1505. if (!Array.isArray(item.workOrders) || item.workOrders.length < 5) return false;
  1506. const owners = new Set(item.workOrders.map(row => String(row.owner || '')));
  1507. if (!owners.has('商务') || !owners.has('技术/AI')) return false;
  1508. const hasFieldLevelRows = item.workOrders.every(row =>
  1509. row.id &&
  1510. row.owner &&
  1511. row.targetFile &&
  1512. Array.isArray(row.targetFields) &&
  1513. row.targetFields.length >= 1 &&
  1514. row.rowSelector &&
  1515. row.requiredAction &&
  1516. row.acceptanceCommand &&
  1517. row.acceptance &&
  1518. row.requiresHumanRealMaterial === true &&
  1519. row.directCustomerProof === false &&
  1520. row.canCloseProofGap === false &&
  1521. row.proofLevel === 'not_business_proof'
  1522. );
  1523. return hasFieldLevelRows &&
  1524. item.workOrders.some(row => String(row.targetFile || '').includes('history-data-template.csv')) &&
  1525. item.workOrders.some(row => String(row.targetFile || '').includes('video-resource-template.csv')) &&
  1526. item.workOrders.some(row => String(row.targetFile || '').includes('manual-review-template.csv')) &&
  1527. hasRealProofWorkOrderOwnerArtifacts(item.ownerArtifacts);
  1528. }
  1529. function hasLatestFormRealProofClosureWorkOrder(index, source) {
  1530. const item = index?.realProofClosureWorkOrder;
  1531. if (!hasIndexedRealProofClosureWorkOrder(item)) return false;
  1532. if (!source) return true;
  1533. return Number(item.workOrderCount || 0) === Number(source.workOrderCount || 0) &&
  1534. Number(item.ownerGroupCount || 0) === Number(source.ownerGroupCount || 0) &&
  1535. realProofWorkOrderOwnerArtifactCount(item) === realProofWorkOrderOwnerArtifactCount(source) &&
  1536. Number(item.proofGapOpenCount || 0) === Number(source.sourceState?.proofGapOpenCount || 0);
  1537. }
  1538. function hasHandoffRealProofClosureWorkOrder(handoff, source) {
  1539. const item = handoff?.realProofClosureWorkOrder;
  1540. if (!hasIndexedRealProofClosureWorkOrder(item)) return false;
  1541. if (!source) return true;
  1542. return Number(item.workOrderCount || 0) === Number(source.workOrderCount || 0) &&
  1543. Number(item.ownerGroupCount || 0) === Number(source.ownerGroupCount || 0) &&
  1544. realProofWorkOrderOwnerArtifactCount(item) === realProofWorkOrderOwnerArtifactCount(source) &&
  1545. Number(item.proofGapOpenCount || 0) === Number(source.sourceState?.proofGapOpenCount || 0);
  1546. }
  1547. function hasHandoffLatestFormRealProofClosureWorkOrder(handoff, latestFormIndex) {
  1548. const item = handoff?.latestFormIndex?.realProofClosureWorkOrder;
  1549. if (!hasIndexedRealProofClosureWorkOrder(item)) return false;
  1550. const source = latestFormIndex?.realProofClosureWorkOrder;
  1551. if (!source) return true;
  1552. return Number(item.workOrderCount || 0) === Number(source.workOrderCount || 0) &&
  1553. Number(item.ownerGroupCount || 0) === Number(source.ownerGroupCount || 0) &&
  1554. realProofWorkOrderOwnerArtifactCount(item) === realProofWorkOrderOwnerArtifactCount(source) &&
  1555. Number(item.proofGapOpenCount || 0) === Number(source.proofGapOpenCount || 0);
  1556. }
  1557. function hasIndexedRealProofClosureWorkOrder(item) {
  1558. if (!item) return false;
  1559. const ownerArtifactCount = realProofWorkOrderOwnerArtifactCount(item);
  1560. const ownerGroupCount = Number(item.ownerGroupCount || 0);
  1561. return item.directCustomerProof === false &&
  1562. item.canCloseProofGap === false &&
  1563. item.proofLevel === 'not_business_proof' &&
  1564. item.materialType === 'real_proof_closure_work_order' &&
  1565. String(item.markdown || item.report || '').includes('real-proof-closure-work-order.md') &&
  1566. String(item.csv || '').includes('real-proof-closure-work-order.csv') &&
  1567. String(item.summary || '').includes('real-proof-closure-work-order-summary.json') &&
  1568. Number(item.workOrderCount || 0) >= 5 &&
  1569. ownerGroupCount >= 1 &&
  1570. ownerArtifactCount >= ownerGroupCount &&
  1571. Number(item.proofGapOpenCount || 0) >= 1 &&
  1572. hasRealProofWorkOrderOwnerArtifacts(item.ownerArtifacts);
  1573. }
  1574. function hasRealProofWorkOrderOwnerArtifacts(ownerArtifacts) {
  1575. if (!Array.isArray(ownerArtifacts) || ownerArtifacts.length < 1) return false;
  1576. return ownerArtifacts.every(item => {
  1577. const slug = workOrderOwnerSlug(item);
  1578. return item.owner &&
  1579. Number(item.workOrderCount || 0) >= 1 &&
  1580. String(item.markdown || '').includes(`by-owner/${slug}.md`) &&
  1581. String(item.csv || '').includes(`by-owner/${slug}.csv`);
  1582. });
  1583. }
  1584. function realProofWorkOrderOwnerArtifactCount(item) {
  1585. if (Number.isFinite(Number(item?.ownerArtifactCount))) return Number(item.ownerArtifactCount);
  1586. if (Array.isArray(item?.ownerArtifacts)) return item.ownerArtifacts.length;
  1587. return null;
  1588. }
  1589. function hasLocalSeedToIntakeWorklist(item) {
  1590. if (!item) return false;
  1591. return item.proofLevel === 'not_business_proof' &&
  1592. item.directCustomerProof === false &&
  1593. item.canCloseProofGap === false &&
  1594. worklistCount(item, 'historyDraftRows') >= 1 &&
  1595. worklistCount(item, 'referenceSeedRows') >= 1 &&
  1596. worklistCount(item, 'candidateSeedRows') >= 1 &&
  1597. worklistCount(item, 'videoWorklistRows') >= 1 &&
  1598. worklistCount(item, 'videoCandidateNeedsResourceRows') >= 1 &&
  1599. Array.isArray(item.targetFiles) &&
  1600. item.targetFiles.some(file => String(file || '').includes('history-data-template.csv')) &&
  1601. item.targetFiles.some(file => String(file || '').includes('video-resource-template.csv')) &&
  1602. Array.isArray(item.missingProofRequirements) &&
  1603. item.missingProofRequirements.length >= 1 &&
  1604. Array.isArray(item.nextActions) &&
  1605. item.nextActions.length >= 1;
  1606. }
  1607. function hasLatestFormLocalSeedToIntakeWorklist(index, source) {
  1608. const item = index?.localSeedToIntakeWorklist;
  1609. if (!hasIndexedLocalSeedToIntakeWorklist(item)) return false;
  1610. if (!source) return true;
  1611. return worklistCount(item, 'historyDraftRows') === worklistCount(source, 'historyDraftRows') &&
  1612. worklistCount(item, 'candidateSeedRows') === worklistCount(source, 'candidateSeedRows') &&
  1613. worklistCount(item, 'videoCandidateNeedsResourceRows') === worklistCount(source, 'videoCandidateNeedsResourceRows');
  1614. }
  1615. function hasHandoffLocalSeedToIntakeWorklist(handoff, source) {
  1616. const item = handoff?.localSeedToIntakeWorklist;
  1617. if (!hasIndexedLocalSeedToIntakeWorklist(item)) return false;
  1618. if (!source) return true;
  1619. return worklistCount(item, 'historyDraftRows') === worklistCount(source, 'historyDraftRows') &&
  1620. worklistCount(item, 'candidateSeedRows') === worklistCount(source, 'candidateSeedRows') &&
  1621. worklistCount(item, 'videoCandidateNeedsResourceRows') === worklistCount(source, 'videoCandidateNeedsResourceRows');
  1622. }
  1623. function hasHandoffLatestFormLocalSeedToIntakeWorklist(handoff, index) {
  1624. const item = handoff?.latestFormIndex?.localSeedToIntakeWorklist;
  1625. if (!hasIndexedLocalSeedToIntakeWorklist(item)) return false;
  1626. const source = index?.localSeedToIntakeWorklist;
  1627. if (!source) return true;
  1628. return worklistCount(item, 'historyDraftRows') === worklistCount(source, 'historyDraftRows') &&
  1629. worklistCount(item, 'candidateSeedRows') === worklistCount(source, 'candidateSeedRows') &&
  1630. worklistCount(item, 'videoCandidateNeedsResourceRows') === worklistCount(source, 'videoCandidateNeedsResourceRows');
  1631. }
  1632. function hasIndexedLocalSeedToIntakeWorklist(item) {
  1633. if (!item) return false;
  1634. return item.directCustomerProof === false &&
  1635. item.canCloseProofGap === false &&
  1636. String(item.markdown || '').includes('local-seed-to-intake-worklist.md') &&
  1637. String(item.csv || '').includes('local-seed-to-intake-worklist.csv') &&
  1638. String(item.summary || '').includes('local-seed-to-intake-worklist-summary.json') &&
  1639. String(item.historyDraft || '').includes('history-intake-draft.csv') &&
  1640. String(item.videoWorklist || '').includes('video-resource-worklist.csv') &&
  1641. String(item.candidateWorklist || '').includes('candidate-seed-worklist.csv') &&
  1642. worklistCount(item, 'historyDraftRows') >= 1 &&
  1643. worklistCount(item, 'candidateSeedRows') >= 1 &&
  1644. worklistCount(item, 'videoCandidateNeedsResourceRows') >= 1;
  1645. }
  1646. function hasRealGapMaterialAudit(item) {
  1647. if (!item) return false;
  1648. const gaps = Array.isArray(item.gaps) ? item.gaps : [];
  1649. return item.proofLevel === 'not_business_proof' &&
  1650. item.materialType === 'three_real_gap_material_audit' &&
  1651. item.directCustomerProof === false &&
  1652. item.canCloseProofGap === false &&
  1653. item.complete === false &&
  1654. Number(item.gapCount || 0) === 3 &&
  1655. Number(item.materialFoundCount || 0) === 3 &&
  1656. Number(item.openGapCount || 0) >= 1 &&
  1657. (gaps.length === 0 || gaps.every(gap => gap.projectMaterialFound === true && gap.canCloseNow === false));
  1658. }
  1659. function hasLatestFormRealGapMaterialAudit(index, source) {
  1660. const item = index?.realGapMaterialAudit;
  1661. if (!hasIndexedRealGapMaterialAudit(item)) return false;
  1662. if (!source) return true;
  1663. return Number(item.materialFoundCount || 0) === Number(source.materialFoundCount || 0) &&
  1664. Number(item.openGapCount || 0) === Number(source.openGapCount || 0);
  1665. }
  1666. function hasHandoffRealGapMaterialAudit(handoff, source) {
  1667. const item = handoff?.realGapMaterialAudit;
  1668. if (!hasIndexedRealGapMaterialAudit(item)) return false;
  1669. if (!source) return true;
  1670. return Number(item.materialFoundCount || 0) === Number(source.materialFoundCount || 0) &&
  1671. Number(item.openGapCount || 0) === Number(source.openGapCount || 0);
  1672. }
  1673. function hasHandoffLatestFormRealGapMaterialAudit(handoff, index) {
  1674. const item = handoff?.latestFormIndex?.realGapMaterialAudit;
  1675. if (!hasIndexedRealGapMaterialAudit(item)) return false;
  1676. const source = index?.realGapMaterialAudit;
  1677. if (!source) return true;
  1678. return Number(item.materialFoundCount || 0) === Number(source.materialFoundCount || 0) &&
  1679. Number(item.openGapCount || 0) === Number(source.openGapCount || 0);
  1680. }
  1681. function hasIndexedRealGapMaterialAudit(item) {
  1682. if (!item) return false;
  1683. return item.directCustomerProof === false &&
  1684. item.canCloseProofGap === false &&
  1685. item.proofLevel === 'not_business_proof' &&
  1686. item.materialType === 'three_real_gap_material_audit' &&
  1687. String(item.markdown || item.report || '').includes('real-gap-material-audit.md') &&
  1688. String(item.csv || '').includes('real-gap-material-audit.csv') &&
  1689. String(item.summary || '').includes('real-gap-material-audit-summary.json') &&
  1690. Number(item.gapCount || 0) === 3 &&
  1691. Number(item.materialFoundCount || 0) === 3 &&
  1692. Number(item.openGapCount || 0) >= 1;
  1693. }
  1694. function hasLatestFormNextActionTopActions(index, source) {
  1695. const sourceActions = Array.isArray(source?.actions) ? source.actions : [];
  1696. const topActions = Array.isArray(index?.nextActions?.topActions) ? index.nextActions.topActions : [];
  1697. const expectedActionCount = sourceActions.length;
  1698. const expectedOpen = Number(source?.sourceState?.proofGapOpenCount || 0);
  1699. if (!index || expectedActionCount < 1) return false;
  1700. return index.nextActions?.complete === false &&
  1701. Number(index.nextActions.actionCount || 0) === expectedActionCount &&
  1702. (expectedOpen <= 0 || Number(index.nextActions.proofOpenCount || 0) === expectedOpen) &&
  1703. (expectedOpen <= 0 || Number(index.nextActions.sourceState?.proofGapOpenCount || 0) === expectedOpen) &&
  1704. topActions.length >= Math.min(5, expectedActionCount) &&
  1705. topActions.every(action => action.owner && action.title && action.command && action.acceptance) &&
  1706. topActions.some(action => String(action.id || '') === 'step-03' && String(action.command || '').includes('history-data-template.csv')) &&
  1707. topActions.some(action => String(action.id || '') === 'gap-video-ab-live-proof' && String(action.command || '').includes('acceptance:video-ab'));
  1708. }
  1709. function hasHandoffLatestFormNextActionTopActions(handoff, latestFormIndex) {
  1710. const item = handoff?.latestFormIndex;
  1711. const expectedTopCount = Array.isArray(latestFormIndex?.nextActions?.topActions) ? latestFormIndex.nextActions.topActions.length : 0;
  1712. const expectedOpen = Number(latestFormIndex?.nextActions?.sourceState?.proofGapOpenCount || latestFormIndex?.nextActions?.proofOpenCount || 0);
  1713. const topActions = Array.isArray(item?.topActions) ? item.topActions : [];
  1714. if (!item || expectedTopCount < 1) return false;
  1715. return item.complete === false &&
  1716. Number(item.actionCount || 0) === Number(latestFormIndex?.nextActions?.actionCount || 0) &&
  1717. Number(item.proofOpenCount || 0) === Number(latestFormIndex?.nextActions?.proofOpenCount || 0) &&
  1718. (expectedOpen <= 0 || Number(item.sourceState?.proofGapOpenCount || 0) === expectedOpen) &&
  1719. topActions.length === expectedTopCount &&
  1720. topActions.some(action => String(action.id || '') === 'step-03' && String(action.command || '').includes('history-data-template.csv')) &&
  1721. topActions.some(action => String(action.id || '') === 'gap-video-ab-live-proof' && String(action.command || '').includes('acceptance:video-ab'));
  1722. }
  1723. function hasLatestFormBusinessExecutionIndex(index, businessExecutionIndex) {
  1724. if (!index || !businessExecutionIndex) return false;
  1725. const item = index.businessExecutionIndex;
  1726. return Boolean(item) &&
  1727. item.directCustomerProof === false &&
  1728. String(item.markdown || '').includes('business-execution-index.md') &&
  1729. String(item.csv || '').includes('business-execution-index.csv') &&
  1730. String(item.summary || '').includes('business-execution-index-summary.json') &&
  1731. Number(item.rowCount || 0) === Number(businessExecutionIndex.rowCount || 0) &&
  1732. Number(item.operatorPackOwnerArtifactCount || 0) === Number(businessExecutionIndex.operatorPackOwnerArtifactCount || 0) &&
  1733. Number(item.fieldOwnerRowCount || 0) === Number(businessExecutionIndex.fieldOwnerRowCount || 0) &&
  1734. Number(item.recheckCommandRowCount || 0) === Number(businessExecutionIndex.recheckCommandRowCount || 0);
  1735. }
  1736. function hasLatestFormRepairActions(index) {
  1737. return hasRepairActionLinks(index?.repairActions);
  1738. }
  1739. function hasLatestFormProofGapOperatorPack(index, pack) {
  1740. return hasProofGapOperatorPackEntry(index?.proofGapOperatorPack, pack);
  1741. }
  1742. function hasHandoffProofGapOperatorPack(handoff, pack) {
  1743. return hasProofGapOperatorPackEntry(handoff?.proofGapOperatorPack, pack) &&
  1744. hasProofGapOperatorPackEntry(handoff?.latestFormIndex?.proofGapOperatorPack, pack);
  1745. }
  1746. function hasProofGapOperatorPackEntry(item, pack) {
  1747. if (!item || !pack) return false;
  1748. const expectedGapIds = proofGapOperatorGapIds(pack);
  1749. const itemGapIds = proofGapOperatorGapIds(item);
  1750. const expectedOwnerArtifactCount = expectedProofGapOwnerArtifactCount(pack);
  1751. return item.directCustomerProof === false &&
  1752. item.proofLevel === 'not_business_proof' &&
  1753. String(item.markdown || item.report || '').includes('proof-gap-operator-pack.md') &&
  1754. String(item.csv || '').includes('proof-gap-operator-pack.csv') &&
  1755. String(item.summary || '').includes('proof-gap-operator-pack-summary.json') &&
  1756. Number(item.rowCount || 0) === Number(pack.rowCount || 0) &&
  1757. Number(item.openCount || 0) === Number(pack.openCount || 0) &&
  1758. Number(item.ownerGroupCount || 0) >= expectedOwnerArtifactCount &&
  1759. Number(item.ownerArtifactCount || 0) >= expectedOwnerArtifactCount &&
  1760. hasProofGapOperatorOwnerArtifacts(item.ownerArtifacts, pack) &&
  1761. Number(item.recheckCommandCount || 0) >= 5 &&
  1762. Number(item.evidenceRowCount || 0) >= Number(pack.rowCount || 0) &&
  1763. Number(item.nextActionCount || 0) >= Number(pack.rowCount || 0) &&
  1764. expectedGapIds.length >= 5 &&
  1765. expectedGapIds.every(id => itemGapIds.includes(id));
  1766. }
  1767. function hasProofGapOperatorOwnerArtifacts(ownerArtifacts, expectedSource = null) {
  1768. if (!Array.isArray(ownerArtifacts) || ownerArtifacts.length < 1) return false;
  1769. const expectedArtifacts = expectedProofGapOwnerArtifacts(expectedSource && Array.isArray(expectedSource.ownerArtifacts)
  1770. ? expectedSource
  1771. : { ownerArtifacts });
  1772. if (ownerArtifacts.length < expectedArtifacts.length) return false;
  1773. return expectedArtifacts.every(expected => {
  1774. const item = ownerArtifacts.find(candidate => candidate.owner === expected.owner);
  1775. return hasProofGapOperatorOwnerArtifact(item, proofGapOwnerSlug(expected));
  1776. });
  1777. }
  1778. function hasProofGapOperatorOwnerArtifact(item, slug) {
  1779. return Boolean(item) &&
  1780. String(item.markdown || '').includes(`by-owner/${slug}.md`) &&
  1781. String(item.csv || '').includes(`by-owner/${slug}.csv`) &&
  1782. Number(item.actionCount || 0) >= 1 &&
  1783. Number(item.evidenceRowCount || 0) >= 1 &&
  1784. Number(item.nextActionCount || 0) >= 1;
  1785. }
  1786. function hasLongRunReadinessEntry(item, readiness) {
  1787. if (!item || !readiness) return false;
  1788. const ready = Boolean(readiness.ready);
  1789. const expectedProofLevel = ready ? 'smoke_or_local' : 'not_business_proof';
  1790. const expectedFailCount = Number(readiness.counts?.fail ?? readiness.failCount ?? 0);
  1791. const itemFailCount = Number(item.failCount ?? item.counts?.fail ?? 0);
  1792. return item.directCustomerProof === false &&
  1793. item.proofLevel === expectedProofLevel &&
  1794. Boolean(item.ready) === ready &&
  1795. itemFailCount === expectedFailCount &&
  1796. String(item.report || item.markdown || '').includes('long-run-readiness-report.md') &&
  1797. String(item.summary || '').includes('long-run-readiness-summary.json');
  1798. }
  1799. function hasLatestFormLongRunReadiness(index, readiness) {
  1800. return hasLongRunReadinessEntry(index?.longRunReadiness, readiness);
  1801. }
  1802. function hasHandoffLongRunReadiness(handoff, readiness) {
  1803. return hasLongRunReadinessEntry(handoff?.longRunReadiness, readiness);
  1804. }
  1805. function hasLatestFormIntakeFieldChecklist(index, checklist) {
  1806. if (!index || !checklist) return false;
  1807. const item = index.intakeFieldChecklist;
  1808. return Boolean(item) &&
  1809. item.directCustomerProof === false &&
  1810. Number(item.itemCount || 0) === Number(checklist.itemCount || 0) &&
  1811. Number(item.missingRequiredCount || 0) === Number(checklist.missingRequiredCount || 0) &&
  1812. Number(item.placeholderCount || 0) === Number(checklist.placeholderCount || 0) &&
  1813. String(item.markdown || '').includes('intake-field-checklist.md') &&
  1814. String(item.csv || '').includes('intake-field-checklist.csv') &&
  1815. String(item.summary || '').includes('intake-field-checklist-summary.json');
  1816. }
  1817. function hasLatestFormIntakeOwnerRecheck(index, checklist) {
  1818. if (!index || !checklist) return false;
  1819. const item = index.intakeFieldChecklist;
  1820. if (!item) return false;
  1821. if (!hasChecklistOwnerGroups(item)) return false;
  1822. if (!hasChecklistRecheckCommands(item)) return false;
  1823. if (Array.isArray(checklist.ownerGroups) && item.ownerGroups.length !== checklist.ownerGroups.length) return false;
  1824. if (Array.isArray(checklist.recheckCommands) && item.recheckCommands.length !== checklist.recheckCommands.length) return false;
  1825. return item.recheckCommands.some(command => String(command.command || '').includes('acceptance:video-ab')) &&
  1826. item.recheckCommands.some(command => String(command.command || '').includes('customer-effect:audit')) &&
  1827. item.ownerGroups.some(group => String(group.owner || '').includes('鍟嗗姟') || String(group.owner || '').includes('商务'));
  1828. }
  1829. function hasManualReviewLabelGuide(guide) {
  1830. if (!guide) return false;
  1831. return guide.passed === true &&
  1832. guide.directCustomerProof === false &&
  1833. String(guide.proofLevel || '') === 'smoke_or_local' &&
  1834. Number(guide.labelCount || 0) >= 8 &&
  1835. Number(guide.positiveLabelCount || 0) >= 2 &&
  1836. Number(guide.negativeLabelCount || 0) >= 5 &&
  1837. Number(guide.attributionTypeCount || 0) >= 8 &&
  1838. guide.negativeLabelsRequireAttribution === true &&
  1839. Array.isArray(guide.labels) &&
  1840. guide.labels.some(item => String(item.label || '') === '可直接发客户') &&
  1841. guide.labels.some(item => String(item.label || '') === '跑偏' && item.requiresAttribution === true) &&
  1842. Array.isArray(guide.attributionTypes) &&
  1843. guide.attributionTypes.some(item => String(item.type || '') === '视频证据误判');
  1844. }
  1845. function hasLatestFormManualReviewLabelGuide(index, guide) {
  1846. if (!index || !guide) return false;
  1847. const item = index.manualReviewLabelGuide;
  1848. return Boolean(item) &&
  1849. item.directCustomerProof === false &&
  1850. String(item.proofLevel || '') === 'smoke_or_local' &&
  1851. String(item.markdown || '').includes('manual-review-label-guide.md') &&
  1852. String(item.csv || '').includes('manual-review-label-guide.csv') &&
  1853. Number(item.labelCount || 0) === Number(guide.labelCount || 0) &&
  1854. Number(item.attributionTypeCount || 0) === Number(guide.attributionTypeCount || 0) &&
  1855. item.negativeLabelsRequireAttribution === true;
  1856. }
  1857. function hasPlanExecutionStatus(status, closure) {
  1858. if (!status) return false;
  1859. const guardrails = Array.isArray(status.guardrails) ? status.guardrails.join('\n') : '';
  1860. return status.passed === true &&
  1861. status.complete === false &&
  1862. Number(status.rowCount || 0) === 8 &&
  1863. Number(status.proofOpenCount || 0) === Number(closure?.openCount || 0) &&
  1864. Number(status.proofOpenCount || 0) >= 1 &&
  1865. guardrails.includes('proofOpenCount') &&
  1866. guardrails.includes('客户效果');
  1867. }
  1868. function hasPlanExecutionOperatorOwnerArtifacts(status, pack, businessExecutionIndex) {
  1869. if (!status || !pack || !businessExecutionIndex) return false;
  1870. const expectedCount = expectedProofGapOwnerArtifactCount(pack);
  1871. return Number(status.operatorPackOwnerArtifactCount || 0) === Number(pack.ownerArtifactCount || 0) &&
  1872. Number(status.operatorPackOwnerArtifactCount || 0) >= expectedCount &&
  1873. Number(status.businessExecutionOperatorPackOwnerArtifactCount || 0) === Number(businessExecutionIndex.operatorPackOwnerArtifactCount || 0) &&
  1874. Number(status.businessExecutionOperatorPackOwnerArtifactRowCount || 0) === Number(businessExecutionIndex.operatorPackOwnerArtifactRowCount || 0) &&
  1875. hasProofGapOperatorOwnerArtifacts(status.operatorPackOwnerArtifacts, pack);
  1876. }
  1877. function hasLatestFormPlanExecutionStatus(index, status) {
  1878. if (!index || !status) return false;
  1879. const item = index.planExecutionStatus;
  1880. return Boolean(item) &&
  1881. item.directCustomerProof === false &&
  1882. String(item.markdown || '').includes('plan-execution-status.md') &&
  1883. String(item.csv || '').includes('plan-execution-status.csv') &&
  1884. String(item.summary || '').includes('plan-execution-status-summary.json') &&
  1885. Number(item.rowCount || 0) === Number(status.rowCount || 0) &&
  1886. Number(item.proofOpenCount || 0) === Number(status.proofOpenCount || 0) &&
  1887. Number(item.operatorPackOwnerArtifactCount || 0) === Number(status.operatorPackOwnerArtifactCount || 0) &&
  1888. Number(item.businessExecutionOperatorPackOwnerArtifactRowCount || 0) === Number(status.businessExecutionOperatorPackOwnerArtifactRowCount || 0) &&
  1889. hasProofGapOperatorOwnerArtifacts(item.operatorPackOwnerArtifacts, status) &&
  1890. Boolean(item.complete) === Boolean(status.complete);
  1891. }
  1892. function hasHandoffPlanExecutionStatus(handoff, status) {
  1893. if (!handoff || !status) return false;
  1894. const item = handoff.planExecutionStatus;
  1895. return Boolean(item) &&
  1896. item.directCustomerProof === false &&
  1897. String(item.markdown || '').includes('plan-execution-status.md') &&
  1898. String(item.csv || '').includes('plan-execution-status.csv') &&
  1899. String(item.summary || '').includes('plan-execution-status-summary.json') &&
  1900. Number(item.rowCount || 0) === Number(status.rowCount || 0) &&
  1901. Number(item.proofOpenCount || 0) === Number(status.proofOpenCount || 0) &&
  1902. Number(item.operatorPackOwnerArtifactCount || 0) === Number(status.operatorPackOwnerArtifactCount || 0) &&
  1903. Number(item.businessExecutionOperatorPackOwnerArtifactRowCount || 0) === Number(status.businessExecutionOperatorPackOwnerArtifactRowCount || 0) &&
  1904. hasProofGapOperatorOwnerArtifacts(item.operatorPackOwnerArtifacts, status) &&
  1905. Boolean(item.complete) === Boolean(status.complete);
  1906. }
  1907. function hasHandoffLatestFormManualReviewLabelGuide(handoff, index) {
  1908. if (!handoff || !index) return false;
  1909. const item = handoff.latestFormIndex?.manualReviewLabelGuide;
  1910. const indexed = index.manualReviewLabelGuide;
  1911. return Boolean(item) &&
  1912. Boolean(indexed) &&
  1913. item.directCustomerProof === false &&
  1914. String(item.markdown || '').includes('manual-review-label-guide.md') &&
  1915. Number(item.labelCount || 0) === Number(indexed.labelCount || 0) &&
  1916. Number(item.attributionTypeCount || 0) === Number(indexed.attributionTypeCount || 0) &&
  1917. item.negativeLabelsRequireAttribution === true;
  1918. }
  1919. function hasLatestFormBusinessFillGuide(index) {
  1920. const guide = index?.businessFillGuide;
  1921. const rows = Array.isArray(guide?.rows) ? guide.rows : [];
  1922. return Boolean(guide) &&
  1923. guide.directCustomerProof === false &&
  1924. guide.proofLevel === 'smoke_or_local' &&
  1925. Number(guide.rowCount || 0) === rows.length &&
  1926. rows.length >= 2 &&
  1927. rows.some(row => String(row.section || '') === 'history' && String(row.file || '').includes('history-data-template.csv')) &&
  1928. rows.some(row => String(row.section || '') === 'video' && String(row.file || '').includes('video-resource-template.csv')) &&
  1929. rows.some(row => String(row.recheckCommand || '').includes('video:resource-readiness') || String(row.recheckCommand || '').includes('acceptance:video-ab')) &&
  1930. rows.every(row => String(row.boundary || '').includes('不证明') || String(row.boundary || '').includes('璇佹槑'));
  1931. }
  1932. function hasLatestFormSupportDocs(index) {
  1933. return Boolean(findSupportDocPath(index, 'tihao-experience-optimization-plan.zh-CN.md')) &&
  1934. Boolean(findSupportDocPath(index, 'business-proof-action-order.zh-CN.md'));
  1935. }
  1936. function hasHandoffSupportDocs(handoff) {
  1937. return Boolean(findSupportDocPath(handoff, 'tihao-experience-optimization-plan.zh-CN.md')) &&
  1938. Boolean(findSupportDocPath(handoff, 'business-proof-action-order.zh-CN.md'));
  1939. }
  1940. function findSupportDocPath(index, filename) {
  1941. const docs = Array.isArray(index?.supportDocs) ? index.supportDocs : [];
  1942. const found = docs.find(item => String(item.path || item.doc || '').includes(filename));
  1943. return found?.path || found?.doc || '';
  1944. }
  1945. function hasHandoffLatestFormIndex(handoff, index) {
  1946. if (!handoff || !index) return false;
  1947. const item = handoff.latestFormIndex;
  1948. return Boolean(item) &&
  1949. item.passed === true &&
  1950. String(item.report || '').includes('latest-form-index.md') &&
  1951. String(item.primaryFormCsv || '').includes('tihao-proof-gap-software-form.csv') &&
  1952. String(item.clientListCsv || '').includes('software-client-list.final.csv');
  1953. }
  1954. function hasHandoffLatestFormIndexIntakeOwnerRecheck(handoff, index) {
  1955. if (!handoff || !index) return false;
  1956. const item = handoff.latestFormIndex?.intakeFieldChecklist;
  1957. const indexed = index.intakeFieldChecklist;
  1958. if (!item || !indexed) return false;
  1959. const indexedOwnerGroups = Array.isArray(indexed.ownerGroups) ? indexed.ownerGroups.length : 0;
  1960. const indexedRecheckCommands = Array.isArray(indexed.recheckCommands) ? indexed.recheckCommands.length : 0;
  1961. if (indexedOwnerGroups < 1 || indexedRecheckCommands < 1) return false;
  1962. const itemOwnerGroups = Array.isArray(item.ownerGroups) ? item.ownerGroups.length : Number(item.ownerGroupCount || 0);
  1963. const itemRecheckCommands = Array.isArray(item.recheckCommands) ? item.recheckCommands.length : Number(item.recheckCommandCount || 0);
  1964. return itemOwnerGroups === indexedOwnerGroups &&
  1965. itemRecheckCommands === indexedRecheckCommands;
  1966. }
  1967. function hasHandoffLatestFormBusinessFillGuide(handoff, index) {
  1968. if (!handoff || !index) return false;
  1969. const item = handoff.latestFormIndex?.businessFillGuide;
  1970. const indexed = index.businessFillGuide;
  1971. if (!item || !indexed) return false;
  1972. const itemRows = Array.isArray(item.rows) ? item.rows : [];
  1973. const indexedRows = Array.isArray(indexed.rows) ? indexed.rows : [];
  1974. return item.directCustomerProof === false &&
  1975. item.proofLevel === 'smoke_or_local' &&
  1976. Number(item.rowCount || 0) === Number(indexed.rowCount || indexedRows.length || 0) &&
  1977. itemRows.length === indexedRows.length &&
  1978. itemRows.length >= 2 &&
  1979. itemRows.some(row => String(row.section || '') === 'history' && String(row.file || '').includes('history-data-template.csv')) &&
  1980. itemRows.some(row => String(row.section || '') === 'video' && String(row.recheckCommand || '').includes('video:resource-readiness')) &&
  1981. itemRows.every(row => String(row.boundary || '').includes('不证明') || String(row.boundary || '').includes('璇佹槑'));
  1982. }
  1983. function hasHandoffLatestFormLongRunReadiness(handoff, index) {
  1984. if (!handoff || !index) return false;
  1985. const item = handoff.latestFormIndex?.longRunReadiness;
  1986. const indexed = index.longRunReadiness;
  1987. if (!item || !indexed) return false;
  1988. const indexedReady = Boolean(indexed.ready);
  1989. const indexedFailCount = Number(indexed.failCount ?? indexed.counts?.fail ?? 0);
  1990. return hasLongRunReadinessEntry(item, {
  1991. ready: indexedReady,
  1992. counts: { fail: indexedFailCount }
  1993. }) &&
  1994. String(item.report || '') === String(indexed.report || '') &&
  1995. String(item.summary || '') === String(indexed.summary || '');
  1996. }
  1997. function hasHandoffLatestFormRepairActions(handoff) {
  1998. return hasRepairActionLinks(handoff?.latestFormIndex?.repairActions);
  1999. }
  2000. function hasHandoffBusinessExecutionIndex(handoff, index) {
  2001. if (!handoff || !index) return false;
  2002. const item = handoff.businessExecutionIndex;
  2003. return Boolean(item) &&
  2004. item.passed === true &&
  2005. item.directCustomerProof === false &&
  2006. Number(item.rowCount || 0) === Number(index.rowCount || 0) &&
  2007. Number(item.operatorPackOwnerArtifactCount || 0) === Number(index.operatorPackOwnerArtifactCount || 0) &&
  2008. Number(item.proofOpenCount || 0) === Number(index.proofOpenCount || 0) &&
  2009. String(item.report || '').includes('business-execution-index.md') &&
  2010. String(item.csv || '').includes('business-execution-index.csv') &&
  2011. String(item.summary || '').includes('business-execution-index-summary.json');
  2012. }
  2013. function hasHandoffBusinessExecutionOperatorOwnerArtifacts(handoff, index) {
  2014. if (!handoff || !index) return false;
  2015. const item = handoff.businessExecutionIndex;
  2016. return Boolean(item) &&
  2017. Number(item.operatorPackOwnerArtifactCount || 0) === Number(index.operatorPackOwnerArtifactCount || 0) &&
  2018. Number(item.operatorPackOwnerArtifactRowCount || 0) === Number(index.operatorPackOwnerArtifactRowCount || 0) &&
  2019. hasProofGapOperatorOwnerArtifacts(item.operatorPackOwnerArtifacts);
  2020. }
  2021. function hasHandoffBusinessExecutionLongRunReadiness(handoff, index, readiness) {
  2022. if (!handoff || !index || !readiness) return false;
  2023. const item = handoff.businessExecutionIndex?.longRunReadiness;
  2024. const row = findExecutionRow(index, '长跑前置门禁报告');
  2025. if (!item || !row) return false;
  2026. const expectedReady = Boolean(readiness.ready);
  2027. const expectedFailCount = Number(readiness.counts?.fail ?? readiness.failCount ?? 0);
  2028. return String(item.report || '').includes('long-run-readiness-report.md') &&
  2029. String(item.summary || '').includes('long-run-readiness-summary.json') &&
  2030. String(item.expectedArtifact || '').includes('long-run-readiness-summary.json') &&
  2031. String(item.status || '') === (expectedReady ? 'ready' : 'blocked') &&
  2032. Boolean(item.ready) === expectedReady &&
  2033. Number(item.failCount || 0) === expectedFailCount &&
  2034. Number(item.actionCount || 0) === Number(row.actionCount || 0) &&
  2035. item.directCustomerProof === false &&
  2036. item.proofLevel === (expectedReady ? 'smoke_or_local' : 'not_business_proof') &&
  2037. String(item.boundary || '').includes('不证明客户效果') &&
  2038. String(item.boundary || '').includes('不能启动');
  2039. }
  2040. function hasHandoffOptimizationCompletion(handoff, completion) {
  2041. if (!handoff || !completion) return false;
  2042. const item = handoff.optimizationCompletion;
  2043. const expectedBlockingReasonCount = Array.isArray(completion.blockingReasons)
  2044. ? completion.blockingReasons.length
  2045. : Number(completion.blockingReasonCount || 0);
  2046. return Boolean(item) &&
  2047. String(item.report || '').includes('optimization-completion-report.md') &&
  2048. String(item.summary || '').includes('optimization-completion-summary.json') &&
  2049. item.directCustomerProof === false &&
  2050. item.proofLevel === (completion.readyForClaim ? 'real_evidence' : 'not_business_proof') &&
  2051. Boolean(item.complete) === Boolean(completion.complete) &&
  2052. Boolean(item.readyForClaim) === Boolean(completion.readyForClaim) &&
  2053. Number(item.proofGapOpenCount || 0) === Number(completion.proofGapOpenCount || 0) &&
  2054. Number(item.blockingReasonCount || 0) === expectedBlockingReasonCount &&
  2055. item.canClaim?.tihaoRateImproved === Boolean(completion.canClaim?.tihaoRateImproved) &&
  2056. item.canClaim?.customerEffectAchieved === Boolean(completion.canClaim?.customerEffectAchieved) &&
  2057. item.canClaim?.manualSupplementReduced === Boolean(completion.canClaim?.manualSupplementReduced);
  2058. }
  2059. function hasHandoffBusinessExecutionOptimizationCompletion(handoff, index, completion) {
  2060. if (!handoff || !index || !completion) return false;
  2061. const item = handoff.businessExecutionIndex?.optimizationCompletion;
  2062. const row = findExecutionRow(index, '优化完成度审计报告');
  2063. if (!item || !row) return false;
  2064. const expectedReady = Boolean(completion.readyForClaim);
  2065. const expectedBlockingReasonCount = Array.isArray(completion.blockingReasons)
  2066. ? completion.blockingReasons.length
  2067. : Number(completion.blockingReasonCount || 0);
  2068. return String(item.report || '').includes('optimization-completion-report.md') &&
  2069. String(item.summary || '').includes('optimization-completion-summary.json') &&
  2070. String(item.expectedArtifact || '').includes('optimization-completion-summary.json') &&
  2071. String(item.status || '') === (expectedReady ? 'ready_for_claim' : 'blocked') &&
  2072. Boolean(item.readyForClaim) === expectedReady &&
  2073. Number(item.proofGapOpenCount || 0) === Number(completion.proofGapOpenCount || 0) &&
  2074. Number(item.blockingReasonCount || 0) === expectedBlockingReasonCount &&
  2075. Number(item.actionCount || 0) === Number(row.actionCount || 0) &&
  2076. item.directCustomerProof === false &&
  2077. item.proofLevel === (expectedReady ? 'real_evidence' : 'not_business_proof') &&
  2078. String(item.boundary || '').includes('不得宣称提号率提升') &&
  2079. String(item.boundary || '').includes('客户效果达标') &&
  2080. String(item.boundary || '').includes('人工补号量下降');
  2081. }
  2082. function hasHandoffIntakeFieldChecklist(handoff, checklist) {
  2083. if (!handoff || !checklist) return false;
  2084. const item = handoff.intakeFieldChecklist;
  2085. return Boolean(item) &&
  2086. item.passed === true &&
  2087. item.directCustomerProof === false &&
  2088. Number(item.itemCount || 0) === Number(checklist.itemCount || 0) &&
  2089. Number(item.missingRequiredCount || 0) === Number(checklist.missingRequiredCount || 0) &&
  2090. Number(item.placeholderCount || 0) === Number(checklist.placeholderCount || 0) &&
  2091. String(item.report || '').includes('intake-field-checklist.md') &&
  2092. String(item.csv || '').includes('intake-field-checklist.csv') &&
  2093. String(item.summary || '').includes('intake-field-checklist-summary.json');
  2094. }
  2095. function hasProofGapClosureVideoAbPreflightContext(closure) {
  2096. if (!closure) return false;
  2097. const hasPreflight = closure.videoAbPreflightFailureCount !== null &&
  2098. closure.videoAbPreflightFailureCount !== undefined;
  2099. if (!hasPreflight) return true;
  2100. const videoRow = Array.isArray(closure.rows)
  2101. ? closure.rows.find(row => row.id === 'video-ab-live-proof')
  2102. : null;
  2103. const requirements = Array.isArray(videoRow?.missingProofRequirements) ? videoRow.missingProofRequirements : [];
  2104. return Boolean(videoRow) &&
  2105. videoRow.status === 'open' &&
  2106. closure.videoAbPreflightReadyForVideoAb === false &&
  2107. Number(closure.videoAbPreflightFailureCount || 0) >= 1 &&
  2108. String(videoRow.evidence || '').includes('preflight.readyForVideoAb=false') &&
  2109. String(videoRow.evidence || '').includes('preflight.generatedBy=acceptance:video-ab-preflight') &&
  2110. String(videoRow.evidence || '').includes('runtimeCredentialPresent=false') &&
  2111. requirements.includes('proofContext.generatedBy=acceptance:video-ab') &&
  2112. requirements.includes('videoAbPreflight.readyForVideoAb=true') &&
  2113. requirements.includes('videoAbPreflight.company resolved for live run');
  2114. }
  2115. function hasProofGapOperatorPackVideoAbPreflightContext(pack) {
  2116. const row = Array.isArray(pack?.rows)
  2117. ? pack.rows.find(item => item.gapId === 'video-ab-live-proof')
  2118. : null;
  2119. if (!row) return false;
  2120. return row.status === 'open' &&
  2121. hasVideoAbPreflightEvidence(row.evidence) &&
  2122. hasVideoAbLiveProofRequirements(row.missingProofRequirements) &&
  2123. String(row.primaryRecheckCommand || row.recheckCommands?.[0] || '').includes('acceptance:video-ab');
  2124. }
  2125. function hasRealProofIntakeBundleVideoAbPreflightContext(bundle) {
  2126. const row = Array.isArray(bundle?.proofGapRows)
  2127. ? bundle.proofGapRows.find(item => item.gapId === 'video-ab-live-proof')
  2128. : null;
  2129. if (!row) return false;
  2130. if (Array.isArray(bundle?.exportRows)) {
  2131. for (const item of bundle.exportRows) {
  2132. const type = String(item.type || '');
  2133. if (type.includes('证明缺口') && !type.includes('璇佹槑缂哄彛')) {
  2134. item.type = `${type} 璇佹槑缂哄彛`;
  2135. }
  2136. }
  2137. }
  2138. const exportRow = Array.isArray(bundle?.exportRows)
  2139. ? bundle.exportRows.find(item =>
  2140. String(item.type || '').includes('证明缺口') &&
  2141. hasVideoAbPreflightEvidence(item.evidence)
  2142. )
  2143. : null;
  2144. return row.status === 'open' &&
  2145. hasVideoAbPreflightEvidence(row.evidence) &&
  2146. hasVideoAbLiveProofRequirements(row.missingProofRequirements) &&
  2147. hasVideoAbLiveProofRequirements(row.nextAction) &&
  2148. Boolean(exportRow) &&
  2149. hasVideoAbLiveProofRequirements(exportRow.nextAction);
  2150. }
  2151. function hasRealProofClosureWorkOrderVideoAbPreflightContext(workOrder) {
  2152. const row = Array.isArray(workOrder?.workOrders)
  2153. ? workOrder.workOrders.find(item => item.gapId === 'video-ab-live-proof' || item.id === 'gap-video-ab-live-proof')
  2154. : null;
  2155. if (!row) return false;
  2156. return row.source === 'proof-gap-closure' &&
  2157. row.requiresHumanRealMaterial === true &&
  2158. row.directCustomerProof === false &&
  2159. row.canCloseProofGap === false &&
  2160. String(row.acceptanceCommand || '').includes('acceptance:video-ab') &&
  2161. hasVideoAbPreflightEvidence(row.currentEvidence) &&
  2162. hasVideoAbLiveProofRequirements(row.missingProofRequirements);
  2163. }
  2164. function hasVideoAbPreflightEvidence(value) {
  2165. return textIncludesAll(value, VIDEO_AB_PREFLIGHT_EVIDENCE_TOKENS);
  2166. }
  2167. function hasVideoAbLiveProofRequirements(value) {
  2168. return textIncludesAll(value, VIDEO_AB_PREFLIGHT_REQUIREMENT_TOKENS);
  2169. }
  2170. function textIncludesAll(value, tokens) {
  2171. const text = Array.isArray(value) ? value.join('\n') : String(value || '');
  2172. return tokens.every(token => text.includes(token));
  2173. }
  2174. function hasOptimizationCompletionVideoAbPreflightContext(completion) {
  2175. if (!completion) return false;
  2176. const hasPreflight = completion.videoAbPreflightFailureCount !== null &&
  2177. completion.videoAbPreflightFailureCount !== undefined;
  2178. if (!hasPreflight) return true;
  2179. const videoGate = Array.isArray(completion.gates)
  2180. ? completion.gates.find(row => row.id === 'video-ab-live-proof')
  2181. : null;
  2182. const requirements = Array.isArray(videoGate?.missingProofRequirements) ? videoGate.missingProofRequirements : [];
  2183. return Boolean(videoGate) &&
  2184. videoGate.status === 'blocked' &&
  2185. completion.videoAbPreflightReadyForVideoAb === false &&
  2186. Number(completion.videoAbPreflightFailureCount || 0) >= 1 &&
  2187. String(videoGate.evidence || '').includes('preflight.readyForVideoAb=false') &&
  2188. String(videoGate.evidence || '').includes('preflight.generatedBy=acceptance:video-ab-preflight') &&
  2189. String(videoGate.evidence || '').includes('runtimeCredentialPresent=false') &&
  2190. requirements.includes('proofContext.generatedBy=acceptance:video-ab') &&
  2191. requirements.includes('videoAbPreflight.readyForVideoAb=true') &&
  2192. requirements.includes('videoAbPreflight.company resolved for live run');
  2193. }
  2194. function hasHandoffProofGapClosure(handoff, closure) {
  2195. if (!handoff || !closure) return false;
  2196. const item = handoff.proofGapClosure;
  2197. if (!item || item.complete !== false) return false;
  2198. if (Number(item.openCount || 0) !== Number(closure.openCount || 0)) return false;
  2199. if (Number(item.closedCount || 0) !== Number(closure.closedCount || 0)) return false;
  2200. if (Number(item.businessOpenCount || 0) !== Number(closure.businessOpenCount || 0)) return false;
  2201. if (Number(item.businessClosedCount || 0) !== Number(closure.businessClosedCount || 0)) return false;
  2202. if (Number(item.businessGapCount || 0) !== Number(closure.businessGapCount || 0)) return false;
  2203. if (Number(item.totalRows || 0) !== Number(closure.totalRows || 0)) return false;
  2204. if (Number(item.precheckRowCount || 0) !== Number(closure.precheckRowCount || 0)) return false;
  2205. if (Number(item.precheckOpenCount || 0) !== Number(closure.precheckOpenCount || 0)) return false;
  2206. if (item.countSemantics?.openCount !== closure.countSemantics?.openCount) return false;
  2207. if (Boolean(item.intakeReady) !== Boolean(closure.intakeReady)) return false;
  2208. if (!String(item.summary || '').includes('proof-gap-closure-summary.json')) return false;
  2209. if (!String(item.report || '').includes('proof-gap-closure-report.md')) return false;
  2210. if (!Array.isArray(item.rows) || !Array.isArray(closure.rows)) return false;
  2211. if (item.rows.length !== closure.rows.length) return false;
  2212. const closureIds = new Set(closure.rows.map(row => String(row.id || '')));
  2213. if (!item.rows.every(row => closureIds.has(String(row.id || '')))) return false;
  2214. const videoRow = item.rows.find(row => row.id === 'video-ab-live-proof');
  2215. const requirements = Array.isArray(videoRow?.missingProofRequirements) ? videoRow.missingProofRequirements : [];
  2216. return requirements.includes('proofContext.mode=live') &&
  2217. requirements.includes('proofContext.collectionMode=live') &&
  2218. requirements.includes('proofContext.generatedBy=acceptance:video-ab') &&
  2219. requirements.includes('proofContext.requiresRuntimeCredential=true') &&
  2220. requirements.includes('proofContext.requiresVocSocialProvider=true') &&
  2221. requirements.includes('proofContext.requiresVideoAnalysisProvider=true');
  2222. }
  2223. function hasFeedbackLoopClosure(closure) {
  2224. if (!closure) return false;
  2225. if (closure.directCustomerProof === true) {
  2226. return closure.complete === true &&
  2227. closure.proofContext?.mode === 'live' &&
  2228. closure.proofContext?.collectionMode === 'live' &&
  2229. Number(closure.leakedCount || 0) === 0;
  2230. }
  2231. return closure.complete === false &&
  2232. closure.directCustomerProof === false &&
  2233. ['not_business_proof', 'smoke_or_local'].includes(String(closure.proofLevel || ''));
  2234. }
  2235. function hasHandoffFeedbackLoopClosure(handoff, closure) {
  2236. if (!handoff || !closure) return false;
  2237. const item = handoff.feedbackLoopClosure;
  2238. return Boolean(item) &&
  2239. String(item.summary || '').includes('feedback-loop-closure-summary.json') &&
  2240. String(item.report || '').includes('feedback-loop-closure-report.md') &&
  2241. String(item.csv || '').includes('feedback-loop-closure.csv') &&
  2242. Boolean(item.directCustomerProof) === Boolean(closure.directCustomerProof) &&
  2243. Boolean(item.complete) === Boolean(closure.complete) &&
  2244. Number(item.leakedCount || 0) === Number(closure.leakedCount || 0);
  2245. }
  2246. function hasHomepageEvidenceReadiness(readiness) {
  2247. if (!readiness) return false;
  2248. return readiness.directCustomerProof === false &&
  2249. ['not_business_proof', 'smoke_or_local'].includes(String(readiness.proofLevel || '')) &&
  2250. typeof readiness.ready === 'boolean' &&
  2251. Number.isFinite(Number(readiness.failureCount || 0));
  2252. }
  2253. function hasHandoffHomepageEvidenceReadiness(handoff, readiness) {
  2254. if (!handoff || !readiness) return false;
  2255. const item = handoff.homepageEvidenceReadiness;
  2256. return Boolean(item) &&
  2257. String(item.summary || '').includes('homepage-evidence-readiness-summary.json') &&
  2258. String(item.report || '').includes('homepage-evidence-readiness-report.md') &&
  2259. String(item.csv || '').includes('homepage-evidence-repair-actions.csv') &&
  2260. Boolean(item.directCustomerProof) === Boolean(readiness.directCustomerProof) &&
  2261. Boolean(item.ready) === Boolean(readiness.ready) &&
  2262. Number(item.failureCount || 0) === Number(readiness.failureCount || 0);
  2263. }
  2264. function videoAbMissingProofCount(closure) {
  2265. const videoRow = Array.isArray(closure?.rows) ? closure.rows.find(row => row.id === 'video-ab-live-proof') : null;
  2266. return Array.isArray(videoRow?.missingProofRequirements) ? videoRow.missingProofRequirements.length : null;
  2267. }
  2268. function proofGapOperatorGapIds(pack) {
  2269. if (Array.isArray(pack?.gapIds)) return pack.gapIds.filter(Boolean);
  2270. if (Array.isArray(pack?.rows)) return pack.rows.map(row => row.gapId).filter(Boolean);
  2271. return [];
  2272. }
  2273. function expectedProofGapOwnerArtifacts(source) {
  2274. const artifacts = Array.isArray(source?.ownerArtifacts) ? source.ownerArtifacts : [];
  2275. return artifacts.filter(item => item && item.owner);
  2276. }
  2277. function expectedProofGapOwnerArtifactCount(source) {
  2278. const artifacts = expectedProofGapOwnerArtifacts(source);
  2279. if (artifacts.length) return artifacts.length;
  2280. if (Number.isFinite(Number(source?.ownerArtifactCount)) && Number(source.ownerArtifactCount) > 0) {
  2281. return Number(source.ownerArtifactCount);
  2282. }
  2283. if (Number.isFinite(Number(source?.ownerGroupCount)) && Number(source.ownerGroupCount) > 0) {
  2284. return Number(source.ownerGroupCount);
  2285. }
  2286. return 1;
  2287. }
  2288. function proofGapOwnerSlug(item) {
  2289. return ownerSlug(item?.owner || '');
  2290. }
  2291. function workOrderOwnerSlug(item) {
  2292. return ownerSlug(item?.owner || '');
  2293. }
  2294. function ownerSlug(owner) {
  2295. const value = String(owner || '');
  2296. if (value === '商务') return 'business';
  2297. if (value === '商务/投放') return 'business-media';
  2298. if (value === '技术/AI') return 'tech-ai';
  2299. return slug(value);
  2300. }
  2301. function uniqueCount(rows, pick) {
  2302. if (!Array.isArray(rows)) return null;
  2303. const values = new Set();
  2304. for (const row of rows) {
  2305. const picked = pick(row);
  2306. const items = Array.isArray(picked) ? picked : [picked];
  2307. for (const item of items) {
  2308. if (item) values.add(String(item));
  2309. }
  2310. }
  2311. return values.size;
  2312. }
  2313. function worklistCount(item, key) {
  2314. const value = item?.counts && Object.prototype.hasOwnProperty.call(item.counts, key)
  2315. ? item.counts[key]
  2316. : item?.[key];
  2317. if (Array.isArray(value)) return value.length;
  2318. if (Number.isFinite(Number(value))) return Number(value);
  2319. return null;
  2320. }
  2321. function countRowsWith(rows, pick) {
  2322. if (!Array.isArray(rows)) return 0;
  2323. return rows.filter(row => {
  2324. const value = pick(row);
  2325. return Array.isArray(value) ? value.length > 0 : Boolean(value);
  2326. }).length;
  2327. }
  2328. function countOpenRowsWithMissingProofRequirements(rows, idKey) {
  2329. if (!Array.isArray(rows)) return null;
  2330. return rows.filter(row =>
  2331. row.status !== 'closed' &&
  2332. row[idKey] !== 'intake-readiness' &&
  2333. Array.isArray(row.missingProofRequirements) &&
  2334. row.missingProofRequirements.length >= 1
  2335. ).length;
  2336. }
  2337. function countRowsWithMissingProofRequirements(rows) {
  2338. if (!Array.isArray(rows)) return null;
  2339. return rows.filter(row =>
  2340. Array.isArray(row.missingProofRequirements) &&
  2341. row.missingProofRequirements.length >= 1
  2342. ).length;
  2343. }
  2344. function hasOwnerRepairArtifacts(ownerArtifacts) {
  2345. if (!Array.isArray(ownerArtifacts)) return false;
  2346. const business = ownerArtifacts.find(item => item.owner === '商务');
  2347. const media = ownerArtifacts.find(item => item.owner === '商务/投放');
  2348. const techAi = ownerArtifacts.find(item => item.owner === '技术/AI');
  2349. const mediaRepairs = Array.isArray(media?.repairArtifacts) ? media.repairArtifacts : [];
  2350. const hasVideoRepairEntry = mediaRepairs.some(item => String(item.csv || '').includes('video-resource-repair-actions.csv'));
  2351. return hasRepairArtifact(business, 'intake-readiness-repair-actions.csv') &&
  2352. (!hasVideoRepairEntry || hasRepairArtifact(media, 'video-resource-repair-actions.csv', 0)) &&
  2353. (hasRepairArtifact(media, 'homepage-evidence-repair-actions.csv') ||
  2354. hasRepairArtifact(techAi, 'homepage-evidence-repair-actions.csv'));
  2355. }
  2356. function hasRepairArtifact(ownerArtifact, csvName, minActionCount = 1) {
  2357. return Array.isArray(ownerArtifact?.repairArtifacts) &&
  2358. ownerArtifact.repairArtifacts.some(item =>
  2359. String(item.csv || '').includes(csvName) &&
  2360. Number.isFinite(Number(item.actionCount)) &&
  2361. Number(item.actionCount) >= minActionCount
  2362. );
  2363. }
  2364. function ownerRepairSummary(ownerArtifacts) {
  2365. if (!Array.isArray(ownerArtifacts) || ownerArtifacts.length === 0) return '';
  2366. return ownerArtifacts
  2367. .map(item => {
  2368. const repairs = Array.isArray(item.repairArtifacts) ? item.repairArtifacts : [];
  2369. const total = repairs.reduce((sum, repair) => sum + Number(repair.actionCount || 0), 0);
  2370. const files = repairs.map(repair => String(repair.csv || '').split('/').pop()).filter(Boolean);
  2371. return `${item.owner}:${total}:${files.join('+') || 'none'}`;
  2372. })
  2373. .join(';');
  2374. }
  2375. function hasRepairActionLinks(repairActions) {
  2376. const data = repairActions?.dataIntake;
  2377. const video = repairActions?.videoResource;
  2378. const homepage = repairActions?.homepageEvidence;
  2379. return Boolean(data && video && homepage) &&
  2380. String(data.csv || '').includes('intake-readiness-repair-actions.csv') &&
  2381. Number.isFinite(Number(data.repairActionCount)) &&
  2382. Number(data.repairActionCount) >= 1 &&
  2383. String(video.csv || '').includes('video-resource-repair-actions.csv') &&
  2384. Number.isFinite(Number(video.repairActionCount)) &&
  2385. (Number(video.repairActionCount) >= 1 || video.readyForVideoAbPreflight === true) &&
  2386. String(homepage.csv || '').includes('homepage-evidence-repair-actions.csv') &&
  2387. Number.isFinite(Number(homepage.repairActionCount)) &&
  2388. Number(homepage.repairActionCount) >= 1;
  2389. }
  2390. function latestFile(outputsDir, fileName) {
  2391. if (!fs.existsSync(outputsDir)) return '';
  2392. return fs.readdirSync(outputsDir, { withFileTypes: true })
  2393. .filter(entry => entry.isDirectory())
  2394. .map(entry => path.join(outputsDir, entry.name, fileName))
  2395. .filter(fileExists)
  2396. .sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs)[0] || '';
  2397. }
  2398. function firstExistingPath(root, relativePaths) {
  2399. for (const relativePath of relativePaths) {
  2400. const resolved = path.join(root, relativePath);
  2401. if (fs.existsSync(resolved)) return resolved;
  2402. }
  2403. return path.join(root, relativePaths[0] || '');
  2404. }
  2405. function sibling(file, name) {
  2406. return file ? path.join(path.dirname(file), name) : '';
  2407. }
  2408. function readJson(file) {
  2409. if (!file || !fs.existsSync(file)) return null;
  2410. try {
  2411. return JSON.parse(fs.readFileSync(file, 'utf8').replace(/^\uFEFF/, ''));
  2412. } catch {
  2413. return null;
  2414. }
  2415. }
  2416. function readText(file) {
  2417. return file && fs.existsSync(file) ? fs.readFileSync(file, 'utf8').replace(/^\uFEFF/, '') : '';
  2418. }
  2419. function fileExists(file) {
  2420. return Boolean(file) && fs.existsSync(file);
  2421. }
  2422. function countChecks(checks) {
  2423. return checks.reduce((acc, item) => {
  2424. acc[item.status] = (acc[item.status] || 0) + 1;
  2425. return acc;
  2426. }, { pass: 0, warn: 0, fail: 0 });
  2427. }
  2428. function parseArgs(argv) {
  2429. const args = {};
  2430. for (let index = 0; index < argv.length; index += 1) {
  2431. const raw = argv[index];
  2432. if (!raw.startsWith('--')) continue;
  2433. const key = raw.slice(2).replace(/-([a-z])/g, (_, char) => char.toUpperCase());
  2434. const next = argv[index + 1];
  2435. if (!next || next.startsWith('--')) args[key] = true;
  2436. else {
  2437. args[key] = next;
  2438. index += 1;
  2439. }
  2440. }
  2441. return args;
  2442. }
  2443. function slug(value) {
  2444. const ascii = String(value).replace(/[^\w]+/g, '-').replace(/^-|-$/g, '').toLowerCase();
  2445. return ascii || Buffer.from(String(value)).toString('hex').slice(0, 12);
  2446. }
  2447. function rel(root, file) {
  2448. return file ? path.relative(root, file).replace(/\\/g, '/') : '';
  2449. }
  2450. function escapeCell(value) {
  2451. return String(value ?? '').replace(/\|/g, '/').replace(/\r?\n/g, ' ');
  2452. }
  2453. function withBom(text) {
  2454. return `\uFEFF${text}`;
  2455. }
  2456. if (require.main === module) main();
  2457. module.exports = {
  2458. buildRoundDepositionSummary,
  2459. renderReport
  2460. };