import type { WorkstreamCloseoutInput, WorkstreamReusablePattern } from '../types.js'; import { workstreamReusablePatternValues } from '../core/constants.js'; import { cleanupEvidenceShowsDeletedZero, normalizeCleanupDeletedCount } from './cleanupEvidence.js'; import { defaultReviewProjects } from './review.js'; interface UpgradeTarget { files: string[]; label: string; prompt: string; } const upgradeTargets = { blocker: { files: ['src/tools/blocker.ts', 'src/tools/workflow.ts', 'README.md'], label: '卡点固化', prompt: '把重复失败的命令、稳定解法、停止条件和后续入口沉淀成 kt_blocker_resolution 或收尾规则。', }, cleanup: { files: ['src/tools/cleanup.ts', 'src/tools/verification.ts', 'README.md'], label: '清理需求', prompt: '把历史产物、验证进程或远程临时目录清理变成可复用命令或验证提醒。', }, 'command-template': { files: ['src/tools/workflow.ts', 'src/tools/testing.ts', 'scripts/'], label: '命令模板', prompt: '把反复使用且容易写错的命令模板封装到工作流、业务测试计划或脚本。', }, 'deploy-observation': { files: ['src/tools/deployObservation.ts', 'src/tools/workflow.ts', 'src/tools/testing.ts', 'README.md'], label: '部署观测', prompt: '把 Jenkins/K8s build、commit、镜像、Deployment、Pod、/health/runtime、日志和 smoke 观测步骤写成固定计划。', }, none: { files: [], label: '无可执行升级', prompt: '明确说明本轮没有新的可复用流程、误报、清理或命令模板需要沉淀。', }, 'review-false-positive': { files: ['src/tools/review.ts', 'src/selfTest.ts', 'README.md'], label: '复审误报', prompt: '把已确认的误报规则沉淀到 review 过滤器和 self-test,避免后续污染上下文。', }, 'test-flow': { files: ['src/tools/testing.ts', 'scripts/', 'README.md'], label: '测试链路', prompt: '把新的真实接口、页面、QQBot、部署或业务 smoke 流程加入测试计划或脚本。', }, } satisfies Record; function normalizeList(values?: string[]): string[] { return (values || []).map((value) => value.trim()).filter(Boolean); } function normalizePatterns(values?: WorkstreamReusablePattern[]): WorkstreamReusablePattern[] { const validValues = new Set(workstreamReusablePatternValues); const uniqueValues = Array.from( new Set((values || []).filter((value) => validValues.has(value))), ); const patterns: WorkstreamReusablePattern[] = uniqueValues.length > 0 ? uniqueValues : ['none']; return patterns.length > 1 ? patterns.filter((pattern) => pattern !== 'none') : patterns; } function buildPhase(name: string, evidence: string[], emptyHint: string): Record { return { evidence, name, status: evidence.length > 0 ? 'ok' : 'missing', suggestion: evidence.length > 0 ? undefined : emptyHint, }; } export function buildWorkstreamCloseout(input: WorkstreamCloseoutInput = {}): Record { const title = input.title?.trim() || ''; const changedProjects = normalizeList(input.changedProjects); const cleanupEvidence = normalizeList(input.cleanupEvidence); const cleanupFinalDeleted = normalizeCleanupDeletedCount(input.cleanupFinalDeleted); const cleanupOk = cleanupFinalDeleted === 0 || cleanupEvidenceShowsDeletedZero(cleanupEvidence); const docSyncEvidence = normalizeList(input.docSyncEvidence); const verificationEvidence = normalizeList(input.verificationEvidence); const problemRecords = normalizeList(input.problemRecords); const reviewEvidence = normalizeList(input.reviewEvidence); const stableSolutions = normalizeList(input.stableSolutions); const superpowersReviewEvidence = normalizeList(input.superpowersReviewEvidence); const patterns = normalizePatterns(input.reusablePatterns); const upgradeNeeded = patterns.some((pattern) => pattern !== 'none'); const ktWorkflowUpdated = input.ktWorkflowUpdated === true; const missingItems = [ title ? '' : '填写本轮大方向名称或目标。', verificationEvidence.length > 0 ? '' : '补充测试或 smoke 验证证据;上线功能必须包含线上自测证据,只有计划或 Jenkins/K8s 成功不算闭环完成。', docSyncEvidence.length > 0 ? '' : '补充文档同步证据;无需更新时也要显式写明原因。', cleanupOk ? '' : '补充历史产物清理证据;至少包含 cleanup-history dry-run deleted=0 或结构化 cleanupFinalDeleted=0。', reviewEvidence.length > 0 ? '' : '补充 KT 全局 review 证据。', superpowersReviewEvidence.length > 0 ? '' : '补充 Superpowers code review 证据。', problemRecords.length > 0 ? '' : '补充问题记录;如无新卡点,显式写“无新卡点”。', stableSolutions.length > 0 ? '' : '补充稳定解法;如无新解法,显式写“无新增稳定解法”。', upgradeNeeded && !ktWorkflowUpdated ? '本轮识别到可复用模式,先升级 ktWorkflow 对应规则或脚本,再报告完成。' : '', ].filter(Boolean); const targetProjects = changedProjects.length > 0 ? changedProjects : [...defaultReviewProjects]; const selectedUpgradeTargets = patterns.map((pattern) => ({ pattern, ...upgradeTargets[pattern], })); return { canReportComplete: missingItems.length === 0, closeoutOrder: [ buildPhase('development', title ? [title] : [], '写清本轮完成的开发目标。'), buildPhase( 'test/smoke verification', verificationEvidence, '补一条可复验的验证证据;上线功能必须补线上自测证据。', ), buildPhase('documentation sync', docSyncEvidence, '补充 kt_change_doc_sync 证据或无需更新说明。'), { evidence: [ ...cleanupEvidence, ...(cleanupFinalDeleted === undefined ? [] : [`cleanupFinalDeleted=${cleanupFinalDeleted}`]), ], name: 'history cleanup', status: cleanupOk ? 'ok' : 'missing', suggestion: cleanupOk ? undefined : '补充 cleanup-history dry-run deleted=0 证据或传入 cleanupFinalDeleted=0。', }, buildPhase('KT global review', reviewEvidence, '补充 kt_global_code_review 或 global-review 证据。'), buildPhase('Superpowers code review', superpowersReviewEvidence, '补充 Superpowers reviewer 创建/完成/关闭和处理结论证据。'), buildPhase('problem record', problemRecords, '记录问题点;没有问题也要显式写明。'), buildPhase('stable solution', stableSolutions, '记录稳定解法;没有新增解法也要显式写明。'), { evidence: upgradeNeeded ? [ ktWorkflowUpdated ? input.upgradeNotes || '已升级 ktWorkflow。' : '待升级 ktWorkflow。', ] : [input.upgradeNotes || '无新的可执行 ktWorkflow 升级需求。'], name: 'ktWorkflow upgrade', status: upgradeNeeded ? (ktWorkflowUpdated ? 'ok' : 'missing') : 'not-needed', }, ], commands: [ 'git status --short', 'pnpm run typecheck', 'pnpm run self-test', 'pnpm run global-review', 'pnpm run workstream-closeout -- --superpowers-review "Superpowers reviewer completed; no Critical/Important findings"', 'git diff --check', ], missingItems, patterns, targetProjects, title: title || '未命名大方向', upgradeNeeded, upgradeTargets: selectedUpgradeTargets, }; }