import type { WorkflowLoopAuditInput } from '../types.js'; interface LoopGate { evidence: string[]; name: string; required: boolean; status: 'missing' | 'not-needed' | 'ok'; suggestion?: string; } function normalizeList(values?: string[]): string[] { return (values || []).map((value) => value.trim()).filter(Boolean); } function buildRequiredGate(name: string, evidence: string[], suggestion: string): LoopGate { return { evidence, name, required: true, status: evidence.length > 0 ? 'ok' : 'missing', suggestion: evidence.length > 0 ? undefined : suggestion, }; } function buildBooleanGate(name: string, ok: boolean, evidence: string[], suggestion: string): LoopGate { return { evidence, name, required: true, status: ok ? 'ok' : 'missing', suggestion: ok ? undefined : suggestion, }; } export function buildWorkflowLoopAudit(input: WorkflowLoopAuditInput = {}): Record { const stage = input.stage || 'finish'; const requireCompletion = input.requireCompletion !== false && stage === 'finish'; const changedProjects = normalizeList(input.changedProjects); const testCases = normalizeList(input.testCases); const verificationEvidence = normalizeList(input.verificationEvidence); const failureRecords = normalizeList(input.failureRecords); const docSyncEvidence = normalizeList(input.docSyncEvidence); const problemRecords = normalizeList(input.problemRecords); const stableSolutions = normalizeList(input.stableSolutions); const reviewEvidence = normalizeList(input.reviewEvidence); const cleanupPreviewDeleted = Math.max(0, input.cleanupPreviewDeleted ?? 0); const cleanupOk = cleanupPreviewDeleted === 0 || input.cleanupExecuted === true; const hasFailure = failureRecords.length > 0; const reusablePattern = input.reusablePattern === true; const needsSolidification = hasFailure || reusablePattern; const ktWorkflowUpdated = input.ktWorkflowUpdated === true; const gates: LoopGate[] = [ buildRequiredGate('task target', [input.taskTitle || ''].filter(Boolean), '写清本轮任务目标。'), buildRequiredGate('test case before test', testCases, '测试前先生成页面/业务/接口测试用例或验证计划。'), buildRequiredGate('verification evidence', verificationEvidence, '补充真实验证证据;只有计划不算完成。'), buildBooleanGate( 'history cleanup', cleanupOk, cleanupOk ? [ cleanupPreviewDeleted === 0 ? 'cleanup-history dry-run deleted=0' : 'cleanup-history executed after stale artifacts were found', ] : [`cleanup-history dry-run deleted=${cleanupPreviewDeleted}`], '执行 kt_cleanup_history dryRun=false 或 pnpm run cleanup-history -- --execute,并复跑 dry-run 确认 deleted=0。', ), buildRequiredGate('documentation sync evidence', docSyncEvidence, '运行 kt_change_doc_sync 并同步 README/API/AGENTS/docs/Obsidian/skills/ktWorkflow 中需要更新的入口;无需更新时写明原因。'), buildRequiredGate('global review evidence', reviewEvidence, '文件改动后跑 kt_global_code_review 或 pnpm run global-review。'), ]; if (needsSolidification) { gates.push( buildRequiredGate('problem record', problemRecords, '写入问题点,至少包含触发条件和证据。'), buildRequiredGate('stable solution', stableSolutions, '写入稳定解法和后续入口。'), buildBooleanGate( 'ktWorkflow upgrade', ktWorkflowUpdated, ktWorkflowUpdated ? ['ktWorkflow updated or audited as not needing executable upgrade'] : [], '可复用问题必须沉淀到 AGENTS.md、skills、docs 或 mcp/ktWorkflow;能自动化的要升级 ktWorkflow。', ), ); } else { gates.push({ evidence: ['无失败或可复用问题。'], name: 'problem solidification', required: false, status: 'not-needed', }); } const missingItems = requireCompletion ? gates .filter((gate) => gate.required && gate.status === 'missing') .map((gate) => `${gate.name}: ${gate.suggestion}`) : []; return { canReportComplete: missingItems.length === 0, commands: [ 'pnpm --dir mcp/ktWorkflow run obsidian-context -- --query ', 'pnpm --dir mcp/ktWorkflow run self-test', 'pnpm --dir mcp/ktWorkflow run cleanup-history -- --dry-run', 'pnpm --dir mcp/ktWorkflow run cleanup-history -- --execute', 'pnpm --dir mcp/ktWorkflow run global-review', 'pnpm --dir mcp/ktWorkflow run workstream-closeout -- --title "<目标>" --verification "<证据>" --problem "<问题记录>" --solution "<稳定解法>"', ], gates, loops: { contextLoop: [ '读取 AGENTS.md / SKILLS.md / TASKS.md。', '用 kt_obsidian_context 获取 ruleAnchors 和 codeAnchors。', '用 kt_inspect_project / kt_risk_scan / kt_suggest_verification 锚定代码上下文。', ], problemSolidificationLoop: [ '失败或主动修复后,先判断是否可复用。', '可复用则记录问题点 / 稳定解法 / 后续入口 / 验证证据。', '能自动化的升级 mcp/ktWorkflow;规则类写 AGENTS.md;长流程写 docs;当前证据写 TASKS.md。', '复跑 self-test / 相关脚本 / global-review。', ], documentationSyncLoop: [ '收集 changedFiles。', '运行 kt_change_doc_sync 生成 requiredDocs。', '更新必要的 README/API/AGENTS/SKILLS/docs/Obsidian/skill/ktWorkflow 入口。', '无法或无需更新时写明原因,并保留在 TASKS.md 或最终报告。', ], testLoop: [ '测试前先写用例或验证计划。', '按用例执行验证并保存证据到 .kt-workspace。', '失败最多三轮,每轮必须改变一个可验证变量。', '每轮结束运行 cleanup-history dry-run;存在待删项则执行 --execute 并复验 deleted=0。', ], }, missingItems, stage, summary: { changedProjects, cleanupPreviewDeleted, docSyncEvidenceCount: docSyncEvidence.length, failureCount: failureRecords.length, problemRecordCount: problemRecords.length, reusablePattern, reviewEvidenceCount: reviewEvidence.length, stableSolutionCount: stableSolutions.length, testCaseCount: testCases.length, verificationEvidenceCount: verificationEvidence.length, }, }; }