import type { GateResult } from "./types.ts"; import type { KdConsistencyReport } from "./consistency.ts"; import type { ActiveRun, KdPhase, KdQuestion } from "./types.ts"; import type { KdActionRoute } from "./action-router.ts"; import { nextPhaseForRun } from "./types.ts"; import { discoverMetadataSources } from "./metadata-discovery.ts"; export type KdNextActionKind = | "fix-consistency" | "answer-question" | "resume-snapshot" | "advance-phase" | "collect-metadata-evidence" | "handle-gate" | "phase-work"; export interface KdNextAction { kind: KdNextActionKind; executable: boolean; instruction: string; reason: string; targetPhase?: KdPhase; toolHint?: string; evidencePath?: string; } export function decideNextAction(input: { cwd: string; run: ActiveRun; gate: GateResult; consistency: KdConsistencyReport; openQuestion?: KdQuestion; actionRoute: KdActionRoute; }): KdNextAction { if (input.consistency.errors.length > 0) { return { kind: "fix-consistency", executable: false, instruction: "只处理 Harness 状态一致性错误:读取 Consistency Report,修正 run state、ledger 或重新创建 run;禁止继续执行业务实现。", reason: input.consistency.errors[0]?.message ?? "Harness 状态一致性错误。", }; } if (input.openQuestion) { return { kind: "answer-question", executable: false, instruction: `只处理 ${input.openQuestion.id} 的用户答案;第一动作必须记录 kd_answer_user action=answer id=${input.openQuestion.id} answer=<本轮输入>,再刷新门禁,禁止继续提问或改写实现。`, reason: input.openQuestion.reason ?? input.openQuestion.question, toolHint: "kd_answer_user", }; } if (input.run.resumeSnapshot?.status === "open") { return { kind: "resume-snapshot", executable: false, instruction: input.run.resumeSnapshot.nextAction, reason: input.run.resumeSnapshot.gateReason ?? input.run.resumeSnapshot.contextSummary, }; } if (input.gate.passed && input.actionRoute.intent === "advance-phase") { const target = nextPhaseForRun(input.run); return { kind: target ? "advance-phase" : "phase-work", executable: Boolean(target), instruction: target ? `调用状态机推进到 ${target};推进后执行 ${target} 阶段任务。` : `当前最终阶段 ${input.run.phase} 检查已通过;整理完成状态或执行 /kd done。`, reason: input.actionRoute.reason, targetPhase: target, }; } if (!input.gate.passed) { const metadata = metadataEvidenceInstruction(input.cwd, input.gate.reason ?? "", input.run); if (metadata) return metadata; return { kind: "handle-gate", executable: false, instruction: input.gate.reason ? `只处理门禁阻塞:${input.gate.reason}` : "刷新门禁并处理唯一阻塞项。", reason: input.gate.reason ?? "门禁阻塞。", }; } return { kind: "phase-work", executable: false, instruction: "按当前阶段协议处理本轮输入;如发现事实缺口,先读取项目文件、metadata evidence 和已有工具结果,仍缺业务决策时使用 kd_ask_user。", reason: input.actionRoute.reason, }; } export function formatNextAction(action: KdNextAction): string { return [ `kind:${action.kind}`, `executable:${action.executable ? "yes" : "no"}`, action.targetPhase ? `targetPhase:${action.targetPhase}` : undefined, action.toolHint ? `toolHint:${action.toolHint}` : undefined, action.evidencePath ? `evidencePath:${action.evidencePath}` : undefined, `instruction:${action.instruction}`, `reason:${action.reason}`, ].filter(Boolean).join("\n"); } function metadataEvidenceInstruction(cwd: string, reason: string, run: ActiveRun): KdNextAction | undefined { const localMetadata = discoverMetadataSources(cwd, run).items.find((item) => item.kind === "project-file"); if (/evidence\/cosmic-metadata\.json/i.test(reason)) { if (localMetadata) { return { kind: "collect-metadata-evidence", executable: false, instruction: `缺少 Cosmic 元数据证据,但项目内已发现本地元数据文件 ${localMetadata.path}。第一动作:调用 kd_metadata_collect path=${localMetadata.path} 自动解析并写入当前产品所需 evidence;若解析失败,再使用 kd_cosmic_metadata 或只读 MCP 查询。`, reason, toolHint: "kd_metadata_collect", evidencePath: "evidence/cosmic-metadata.json", }; } return { kind: "collect-metadata-evidence", executable: false, instruction: "缺少 Cosmic 元数据证据。第一动作:从 PLAN、run.facts 或项目文件取得目标 FormId/单据线索,优先调用 kd_cosmic_metadata 生成 evidence/cosmic-metadata.json;若项目已配置数据库 MCP 且官方工具不足,使用只读 MCP 查询 t_meta_entitydesign.fdata 后再用 kd_metadata_parse 或 kcode metadata 解析为 evidence。", reason, toolHint: "kd_cosmic_metadata", evidencePath: "evidence/cosmic-metadata.json", }; } if (/evidence\/data-source\.md/i.test(reason)) { if (localMetadata) { return { kind: "collect-metadata-evidence", executable: false, instruction: `缺少真实数据源证据,但项目内已发现本地元数据文件 ${localMetadata.path}。第一动作:调用 kd_metadata_collect path=${localMetadata.path} 自动解析并写入 evidence/data-source.md;仍缺业务选择时再用 kd_ask_user。`, reason, toolHint: "kd_metadata_collect", evidencePath: "evidence/data-source.md", }; } const toolHint = run.profile?.platform === "enterprise-csharp" || run.profile?.platform === "enterprise-python" ? "kd_metadata_parse" : "kd_metadata_parse"; return { kind: "collect-metadata-evidence", executable: false, instruction: "缺少真实数据源证据。第一动作:先查当前项目元数据文件、BOS/FKERNELXML/fdata 或已配置数据库 MCP 的只读元数据结果,并优先用 kd_metadata_collect 或 kcode meta 写入 evidence/data-source.md;仅需预览时再用 kd_metadata_parse;仍缺业务选择时再用 kd_ask_user。", reason, toolHint, evidencePath: "evidence/data-source.md", }; } return undefined; }