// 裁决纯逻辑:完整性判定(未决禁验收)、accept 请求体组装、缺判高亮映射。 // 对齐 C3 accept 契约 + R5 冲突 gate(裁决 conflict_index 须覆盖 range(len(conflicts)))。 import type { AcceptRequest, ConflictDecision } from "@/lib/api/types"; export type Verdict = "accept" | "ignore" | "manual"; // 单个冲突的本地裁决草稿(按下标定位)。 export interface DecisionDraft { verdict: Verdict | null; note: string; } // 初始化 N 个冲突的裁决草稿(全部未决)。 export function emptyDecisions(conflictCount: number): DecisionDraft[] { return Array.from({ length: conflictCount }, () => ({ verdict: null, note: "", })); } // 不可变更新:设置某个冲突的 verdict。 export function setVerdict( drafts: readonly DecisionDraft[], index: number, verdict: Verdict, ): DecisionDraft[] { return drafts.map((d, i) => (i === index ? { ...d, verdict } : d)); } // 不可变更新:设置某个冲突的 note。 export function setNote( drafts: readonly DecisionDraft[], index: number, note: string, ): DecisionDraft[] { return drafts.map((d, i) => (i === index ? { ...d, note } : d)); } // 是否全部已决:每个冲突都有 verdict(覆盖 range(len(conflicts)))。零冲突 → 直通。 export function allResolved(drafts: readonly DecisionDraft[]): boolean { return drafts.every((d) => d.verdict !== null); } // 本地仍未决的冲突下标(用于禁用态提示)。 export function unresolvedIndices(drafts: readonly DecisionDraft[]): number[] { return drafts.flatMap((d, i) => (d.verdict === null ? [i] : [])); } // 组装 accept 请求体(仅含已决项;note 去空白后非空才带)。 export function buildAcceptRequest( finalText: string, drafts: readonly DecisionDraft[], ): AcceptRequest { const decisions: ConflictDecision[] = drafts.flatMap((d, i) => { if (d.verdict === null) return []; const trimmed = d.note.trim(); const decision: ConflictDecision = { conflict_index: i, verdict: d.verdict, }; return [trimmed ? { ...decision, note: trimmed } : decision]; }); return { final_text: finalText, decisions }; } // 从 409 CONFLICT_UNRESOLVED 的 details 提取缺判下标集合(用于报告卡高亮)。 export function missingConflictIndices(details: unknown): Set { if (typeof details !== "object" || details === null) return new Set(); const raw = (details as { missing_conflict_indices?: unknown }) .missing_conflict_indices; if (!Array.isArray(raw)) return new Set(); return new Set(raw.filter((n): n is number => typeof n === "number")); }