151 lines
6.4 KiB
TypeScript
151 lines
6.4 KiB
TypeScript
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,
|
||
},
|
||
};
|
||
}
|