import { readFileSync } from "node:fs"; type JsonRecord = Record; type DocTypeCode = "DCSN" | "GOAL" | "PLAN" | "RPRT" | "ACTN" | "ISSU" | "RETR" | "RQST" | "RESP" | "MINS"; interface DocMetadata { docNo: string; docType: DocTypeCode | ""; priority: string; year: string; sequence: string; } const docTypeLabels: Record = { DCSN: "决策/决议", GOAL: "目标", PLAN: "计划", RPRT: "报告", ACTN: "行动", ISSU: "问题", RETR: "复盘", RQST: "请示", RESP: "批复/答复", MINS: "会议纪要", }; const docTypeOrder: DocTypeCode[] = ["DCSN", "GOAL", "PLAN", "RPRT", "ACTN", "ISSU", "RETR", "RQST", "RESP", "MINS"]; const docTypeSet = new Set(docTypeOrder); const docNoPattern = /\bDC[-−–—]([A-Z]{2,5})[-−–—]([A-Z][0-9])[-−–—](\d{4})[-−–—](\d{1,6})\b/iu; function assertCondition(condition: unknown, message: string, detail: JsonRecord = {}): void { if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`); } function source(path: string): string { return readFileSync(path, "utf8"); } function includesAll(text: string, snippets: string[]): boolean { return snippets.every((snippet) => text.includes(snippet)); } function assertEqual(actual: T, expected: T, message: string, detail: JsonRecord = {}): void { assertCondition(Object.is(actual, expected), message, { ...detail, actual, expected }); } function stringField(record: JsonRecord, keys: string[]): string { for (const key of keys) { const value = record[key]; if (typeof value === "string" && value.trim()) return value.trim(); if (typeof value === "number" && Number.isFinite(value)) return String(value); } return ""; } function recordTags(record: JsonRecord): string[] { const tags = record.tags; return Array.isArray(tags) ? tags.map((tag) => String(tag)) : []; } function tagValues(record: JsonRecord, prefix: string): string[] { const normalized = `${prefix.toLowerCase()}:`; return recordTags(record) .map((tag) => tag.trim()) .filter((tag) => tag.toLowerCase().startsWith(normalized)) .map((tag) => tag.slice(normalized.length).trim()) .filter(Boolean); } function normalizeDocType(value: unknown): DocTypeCode | "" { const upper = String(value || "").trim().toUpperCase(); return docTypeSet.has(upper) ? upper as DocTypeCode : ""; } function normalizeSequence(value: unknown): string { const raw = String(value || "").trim(); if (!/^\d+$/u.test(raw)) return ""; return String(Number(raw)).padStart(3, "0"); } function parseDocNo(value: unknown): DocMetadata | null { const match = String(value || "").match(docNoPattern); if (match === null) return null; const docType = normalizeDocType(match[1]); const priority = String(match[2] || "").toUpperCase(); const year = String(match[3] || ""); const sequence = normalizeSequence(match[4]); if (!docType || !/^P[0-3]$/u.test(priority) || !year || !sequence) return null; return { docNo: `DC-${docType}-${priority}-${year}-${sequence}`, docType, priority, year, sequence, }; } function mergeParsed(metadata: DocMetadata, parsed: DocMetadata | null): void { if (parsed === null) return; if (!metadata.docNo) metadata.docNo = parsed.docNo; if (!metadata.docType) metadata.docType = parsed.docType; if (!metadata.priority) metadata.priority = parsed.priority; if (!metadata.year) metadata.year = parsed.year; if (!metadata.sequence) metadata.sequence = parsed.sequence; } function completeDocNo(metadata: DocMetadata): string { return metadata.docType && metadata.priority && metadata.year && metadata.sequence ? `DC-${metadata.docType}-${metadata.priority}-${metadata.year}-${metadata.sequence}` : ""; } function bodyFirstWindow(record: JsonRecord): string { const body = stringField(record, ["body", "summary", "markdown"]); return body.replace(/\r\n?/gu, "\n").split(/\n\s*\n/gu).map((part) => part.trim()).find(Boolean) || ""; } function extractDocMetadata(record: JsonRecord): DocMetadata { const metadata: DocMetadata = { docNo: "", docType: "", priority: "", year: "", sequence: "" }; mergeParsed(metadata, parseDocNo(stringField(record, ["docNo", "documentNo", "documentNumber", "documentId"]))); metadata.docType ||= normalizeDocType(stringField(record, ["docType", "documentType", "documentKind"])); metadata.priority ||= stringField(record, ["docPriority", "priority", "level"]).toUpperCase(); metadata.year ||= stringField(record, ["docYear", "year"]); metadata.sequence ||= normalizeSequence(stringField(record, ["docSeq", "sequence", "seq", "docSequence", "documentSequence"])); metadata.docNo ||= completeDocNo(metadata); mergeParsed(metadata, parseDocNo(stringField(record, ["title"]))); for (const value of tagValues(record, "doc-no")) mergeParsed(metadata, parseDocNo(value)); metadata.docType ||= normalizeDocType(tagValues(record, "doc-type")[0]); metadata.priority ||= String(tagValues(record, "doc-priority")[0] || "").toUpperCase(); metadata.year ||= tagValues(record, "doc-year")[0] || ""; metadata.sequence ||= normalizeSequence(tagValues(record, "doc-sequence")[0]); metadata.docNo ||= completeDocNo(metadata); mergeParsed(metadata, parseDocNo(bodyFirstWindow(record))); metadata.docNo ||= completeDocNo(metadata); return metadata; } function extractDocNo(record: JsonRecord): string { return extractDocMetadata(record).docNo; } function groupCount(records: JsonRecord[], type: string): number { const groups = new Map(); for (const code of docTypeOrder) groups.set(code, []); for (const record of records) { const code = extractDocMetadata(record).docType || "DCSN"; groups.get(code)?.push(record); } return groups.get(type as DocTypeCode)?.length || 0; } export function runDecisionCenterWorkspaceContract(): JsonRecord { const frontend = source("src/components/frontend/src/decision-center.tsx"); const css = source("src/components/frontend/public/style.css"); const titleDcsn = { id: "title-dcsn", title: "DC-DCSN-P0-2026-001 决策记录", tags: [], }; const tagGoal = { id: "tag-goal", title: "目标文书", tags: ["doc-no:DC-GOAL-P0-2026-002"], }; const bodyRprt = { id: "body-rprt", title: "报告正文", body: "DC-RPRT-P2-2026-003\n\n报告正文第一段。", tags: [], }; const tagRetr = { id: "tag-retr", title: "复盘文书", tags: ["doc-type:RETR"], }; const structuredPlan = { id: "structured-plan", title: "结构化计划文书", docType: "PLAN", priority: "P1", year: 2026, sequence: 4, }; assertEqual(extractDocNo(titleDcsn), "DC-DCSN-P0-2026-001", "title prefix must extract complete DCSN doc number"); assertEqual(extractDocMetadata(titleDcsn).docType, "DCSN", "title prefix must extract DCSN doc type"); assertEqual(extractDocNo(tagGoal), "DC-GOAL-P0-2026-002", "doc-no tag must extract complete GOAL doc number"); assertEqual(extractDocMetadata(tagGoal).docType, "GOAL", "doc-no tag must extract GOAL doc type"); assertEqual(extractDocNo(bodyRprt), "DC-RPRT-P2-2026-003", "body first paragraph must fallback extract complete RPRT doc number"); assertEqual(extractDocMetadata(bodyRprt).priority, "P2", "body fallback must extract RPRT priority"); assertEqual(extractDocMetadata(tagRetr).docType, "RETR", "doc-type tag must extract RETR doc type"); assertEqual(docTypeLabels.RETR, "复盘", "RETR label must be 复盘"); assertEqual(extractDocNo(structuredPlan), "DC-PLAN-P1-2026-004", "structured fields must compose complete PLAN doc number"); assertEqual(groupCount([titleDcsn, tagGoal, bodyRprt, tagRetr, structuredPlan], "DCSN"), 1, "DCSN records must be grouped by parsed type"); assertEqual(groupCount([titleDcsn, tagGoal, bodyRprt, tagRetr, structuredPlan], "GOAL"), 1, "GOAL records must be grouped by parsed type"); assertEqual(groupCount([titleDcsn, tagGoal, bodyRprt, tagRetr, structuredPlan], "RPRT"), 1, "RPRT records must be grouped by parsed type"); assertEqual(groupCount([titleDcsn, tagGoal, bodyRprt, tagRetr, structuredPlan], "RETR"), 1, "RETR records must be grouped by parsed type"); assertEqual(groupCount([titleDcsn, tagGoal, bodyRprt, tagRetr, structuredPlan], "PLAN"), 1, "PLAN records must be grouped by structured type"); assertCondition( includesAll(frontend, [ "export function extractDocMetadata(record: any): DocMetadata", "export function extractDocNo(record: any): string", "export function buildDocTypeTree(records: any[]): Array<{ type: DocTypeCode", "function buildTagGroups(records: any[]): Array<{ tag: string", "DOC_TYPE_CODES = [\"DCSN\", \"GOAL\", \"PLAN\", \"RPRT\", \"ACTN\", \"ISSU\", \"RETR\", \"RQST\", \"RESP\", \"MINS\"]", "docTypeLabels: Record", ]), "frontend must implement exported doc metadata extraction, doc-type tree, and tag grouping", ); assertCondition( includesAll(frontend, [ "function DocTreeNode({ record, depth, onSelect, selectedId }: AnyRecord)", "function DocTreeGroup({ group, onSelect, selectedId, defaultOpen }: AnyRecord)", "function TagTreeGroup({ group, onSelect, selectedId, defaultOpen }: AnyRecord)", "function DocTypeTree({ records, onSelect, selectedId, activeGrouping }: AnyRecord)", ]), "frontend must implement DocTreeNode, DocTreeGroup, TagTreeGroup, and DocTypeTree components", ); assertCondition( includesAll(frontend, [ "function DocDetailSidebar({ record, onSelect, onSave, saving, error, saveMsg, onRaw }: AnyRecord)", "function DocEditor({ form, saving, onChange, onSubmit }: AnyRecord)", "function DocViewer({ record }: AnyRecord)", "function DocDetailHeader({ record, onBack }: AnyRecord)", ]), "frontend must implement DocDetailSidebar, DocEditor, DocViewer, and DocDetailHeader", ); assertCondition( includesAll(frontend, [ "function DocWorkspace({ records, selectedRecord, onSelect, onSave, saving, saveError, saveMsg, onRaw }: AnyRecord)", "h(DocWorkspace, {", "activeView === \"workspace\"", "decision-tab-workspace", ]), "frontend must implement DocWorkspace component and workspace tab", ); assertCondition( includesAll(frontend, [ "mode === \"view\"", "mode === \"edit\"", "setMode(\"view\")", "setMode(\"edit\")", "doc-sidebar-mode-tabs", ]), "frontend must implement read/edit mode toggle in DocDetailSidebar", ); assertCondition( includesAll(frontend, [ "selectedRecord?.id", "setState((prev: any) => ({ ...prev, selectedDoc: record", "state.selectedDoc", ]), "frontend must track selectedDoc in state and sync on selection", ); assertCondition( includesAll(frontend, [ "await onSave(form)", "recordPayloadFromForm(form)", "setRecordSaveState({ saving: false, error: \"\", message:", "setState((prev: any) => ({ ...prev, selectedDoc: saved", ]), "frontend must call onSave, payload conversion, error handling, and sync selectedDoc after save", ); assertCondition( css.includes(".doc-workspace"), "CSS must include .doc-workspace layout", ); assertCondition( css.includes(".doc-sidebar"), "CSS must include .doc-sidebar", ); assertCondition( css.includes("grid-template-columns: minmax(200px, 260px) minmax(0, 1fr) minmax(0, 50%)"), "CSS must implement three-column layout with right sidebar at 50%", ); assertCondition( includesAll(css, [ ".doc-type-tree", ".doc-tree-group", ".doc-tree-node", ".doc-list-item", ".doc-full-markdown", ".doc-editor-form", ]), "CSS must include all workspace component styles", ); assertCondition( includesAll(frontend, [ "extractDocMetadata(record)", "recordTagValues(record, \"doc-no\")", "firstBodyWindow(record)", ]), "extractDocNo must use metadata extraction from title, doc-no tag, and body fallback", ); assertCondition( includesAll(frontend, [ "grouping === \"type\"", "grouping === \"tag\"", "setGrouping(\"type\")", "setGrouping(\"tag\")", ]), "workspace must support type and tag grouping toggle", ); return { ok: true, checks: [ "doc-no-extraction-functions", "doc-type-tree-components", "doc-detail-sidebar-components", "doc-workspace-component", "read-edit-mode-toggle", "selected-doc-state-sync", "save-and-sync", "workspace-css-layout", "workspace-css-components", "three-column-layout-50-percent", "doc-metadata-fixtures", "type-tag-grouping-toggle", ], }; } if (import.meta.main) { process.stdout.write(`${JSON.stringify(runDecisionCenterWorkspaceContract(), null, 2)}\n`); }