ktworkflow-mcp/src/tools/loop.ts

151 lines
6.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<string, unknown> {
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 <keyword>',
'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,
},
};
}