export type CharterModuleKey = | "module_1" | "module_2" | "module_3" | "module_4" | "module_5" | "module_6"; export interface CharterModuleFieldSchema { key: string; label: string; required?: boolean; editable?: boolean; multiline?: boolean; rows?: number; placeholder?: string; } export interface CharterModuleSchema { key: CharterModuleKey; title: string; shortTitle: string; fields: CharterModuleFieldSchema[]; } export interface CharterModuleDraft { metadata: { status: string; confidence_score: string; last_validated_at: string; evidence_refs: string; escalation_reason: string; }; modules: Record>; } export interface ModuleProgress { requiredTotal: number; requiredDone: number; percent: number; done: boolean; missing: string[]; } export interface CharterProgress { requiredTotal: number; requiredDone: number; requiredPercent: number; completedModules: number; moduleProgress: Record; } export const CHARTER_MODULE_SCHEMAS: CharterModuleSchema[] = [ { key: "module_1", title: "模块1:触发与目标", shortTitle: "模块1 触发目标", fields: [ { key: "problem_statement", label: "问题陈述(必填)", required: true, multiline: true, rows: 3, placeholder: "一句话问题定义,避免模糊表达", }, { key: "goal_statement", label: "目标陈述(必填)", required: true, multiline: true, rows: 3, placeholder: "包含当前基线、目标值、目标日期", }, { key: "kpis", label: "KPI 定义(必填)", required: true, multiline: true, rows: 4, placeholder: "每行一个指标,建议包含口径/负责人/数据源", }, { key: "ai_validations", label: "AI 校验规则", editable: false, multiline: true, rows: 3, }, { key: "escalation_trigger", label: "升级触发条件", editable: false, multiline: true, rows: 2, }, ], }, { key: "module_2", title: "模块2:范围与对齐检查", shortTitle: "模块2 范围对齐", fields: [ { key: "in_scope", label: "范围内(必填)", required: true, multiline: true, rows: 4, placeholder: "每行一条功能 + 验收条件", }, { key: "out_of_scope", label: "范围外(必填)", required: true, multiline: true, rows: 4, placeholder: "每行一条排除项 + 边界示例", }, { key: "alignment_qa", label: "5个固定对齐问答(必填)", required: true, multiline: true, rows: 5, }, { key: "ai_validations", label: "AI 校验规则", editable: false, multiline: true, rows: 3, }, { key: "escalation_trigger", label: "升级触发条件", editable: false, multiline: true, rows: 2, }, ], }, { key: "module_3", title: "模块3:模型与技术依赖", shortTitle: "模块3 模型依赖", fields: [ { key: "primary_model", label: "主模型(必填)", required: true, placeholder: "例如:主推理模型", }, { key: "fallback_model", label: "备用模型(必填)", required: true, placeholder: "无则写“无”", }, { key: "capability_table", label: "能力边界/成本表(必填)", required: true, multiline: true, rows: 4, }, { key: "dependency_map", label: "技术依赖图(必填)", required: true, multiline: true, rows: 4, }, { key: "business_view", label: "业务视图", multiline: true, rows: 3, }, { key: "engineering_view", label: "工程视图", multiline: true, rows: 3, }, { key: "ai_validations", label: "AI 校验规则", editable: false, multiline: true, rows: 3, }, { key: "escalation_trigger", label: "升级触发条件", editable: false, multiline: true, rows: 2, }, ], }, { key: "module_4", title: "模块4:时间线与资源规划", shortTitle: "模块4 计划资源", fields: [ { key: "milestones", label: "里程碑计划(必填)", required: true, multiline: true, rows: 4, placeholder: "每行一个里程碑 + 开始/结束/依赖", }, { key: "role_effort_estimates", label: "角色工时估算(必填)", required: true, multiline: true, rows: 4, }, { key: "capability_owner_assignment", label: "能力图谱分配(必填)", required: true, multiline: true, rows: 3, }, { key: "buffer_policy", label: "缓冲策略", multiline: true, rows: 2, }, { key: "ai_validations", label: "AI 校验规则", editable: false, multiline: true, rows: 3, }, { key: "escalation_trigger", label: "升级触发条件", editable: false, multiline: true, rows: 2, }, ], }, { key: "module_5", title: "模块5:风险与应对", shortTitle: "模块5 风险应对", fields: [ { key: "risk_register", label: "风险台账(必填)", required: true, multiline: true, rows: 4, placeholder: "每行一个风险:类别/概率/影响/负责人", }, { key: "trigger_rules", label: "触发规则(必填)", required: true, multiline: true, rows: 3, }, { key: "execution_rule", label: "触发后执行规则(必填)", required: true, multiline: true, rows: 3, }, { key: "ai_validations", label: "AI 校验规则", editable: false, multiline: true, rows: 3, }, { key: "escalation_trigger", label: "升级触发条件", editable: false, multiline: true, rows: 2, }, ], }, { key: "module_6", title: "模块6:附件与治理", shortTitle: "模块6 附件治理", fields: [ { key: "attachment_metadata", label: "附件元数据(必填)", required: true, multiline: true, rows: 4, }, { key: "attachment_summary", label: "附件摘要/决策提取(必填)", required: true, multiline: true, rows: 3, }, { key: "search_tags", label: "检索标签(必填)", required: true, multiline: true, rows: 2, }, { key: "ai_validations", label: "AI 校验规则", editable: false, multiline: true, rows: 3, }, { key: "governance_rule", label: "治理规则", multiline: true, rows: 2, }, ], }, ]; function toRecord(value: unknown): Record { if (!value || typeof value !== "object" || Array.isArray(value)) { return {}; } return value as Record; } const CHARTER_ZH_DICTIONARY: Array<[RegExp, string]> = [ [ /Develop an AI-powered automated short video generation platform with script-to-video automation and launch an API for programmatic video content generation\./gi, "建设 AI 驱动的短视频自动化生成平台,实现脚本转视频并开放程序化视频生成 API。", ], [ /Develop a script-to-video automation feature for Pixelle-Video\./gi, "为 Pixelle-Video 开发脚本转视频自动化能力。", ], [ /Launch an API for programmatic video content generation\./gi, "上线程序化视频内容生成 API。", ], [ /Scale the AI video infrastructure to support increased demand\./gi, "扩容 AI 视频基础设施以支撑增长需求。", ], [ /Successful deployment of the script-to-video automation feature\./gi, "脚本转视频自动化能力成功上线。", ], [ /API launch with a minimum of 100 active users within the first three months\./gi, "API 上线后前三个月活跃用户不少于 100。", ], [ /50% increase in AI video infrastructure capacity within six months of the funding round\./gi, "融资后六个月内 AI 视频基础设施容量提升 50%。", ], [ /Design and implement the script-to-video automation feature\./gi, "设计并实现脚本转视频自动化能力。", ], [ /Develop and deploy the API for programmatic video content generation\./gi, "开发并部署程序化视频内容生成 API。", ], [ /Expand the AI video infrastructure to handle increased load\./gi, "扩展 AI 视频基础设施以承载增长负载。", ], [ /Develop additional features not directly related to the core script-to-video automation and API functionalities\./gi, "不开发与核心脚本转视频和 API 功能无直接关系的附加功能。", ], [ /Manage marketing and customer acquisition activities\./gi, "不纳入市场营销与获客运营活动。", ], [ /Availability of skilled AI and video development resources\./gi, "依赖具备 AI 与视频开发能力的人才资源供给。", ], [ /Successful integration of the API with third-party platforms\./gi, "依赖 API 与第三方平台的顺利集成。", ], [ /Sufficient funding to support the scaling of the AI video infrastructure\./gi, "依赖足够资金支撑 AI 视频基础设施扩容。", ], [ /Technical challenges in implementing the script-to-video automation feature\./gi, "脚本转视频自动化实现存在技术挑战风险。", ], [ /Limited adoption of the API due to technical or market issues\./gi, "受技术或市场因素影响,API 采纳率可能不足。", ], [ /Inadequate scaling of the AI video infrastructure leading to performance issues\./gi, "基础设施扩容不足可能引发性能问题。", ], [/script-to-video/gi, "脚本转视频"], [/programmatic video content generation/gi, "程序化视频内容生成"], [/AI video infrastructure/gi, "AI 视频基础设施"], [/funding round/gi, "融资轮次"], [/within the first three months/gi, "前三个月内"], [/within six months/gi, "六个月内"], [/successful deployment/gi, "成功上线"], ]; function applyCharterZhDictionary(text: string): string { let output = String(text || ""); if (!output.trim()) return ""; for (const [pattern, replacement] of CHARTER_ZH_DICTIONARY) { output = output.replace(pattern, replacement); } return output; } function toText(value: unknown): string { if (value === null || value === undefined) return ""; if (typeof value === "string") return applyCharterZhDictionary(value); if (typeof value === "number" || typeof value === "boolean") return String(value); if (Array.isArray(value)) { return value .map((item) => { if (item === null || item === undefined) return ""; if (typeof item === "string") return applyCharterZhDictionary(item); if (typeof item === "number" || typeof item === "boolean") return String(item); return applyCharterZhDictionary(JSON.stringify(item)); }) .filter((line) => line.trim().length > 0) .join("\n"); } try { return applyCharterZhDictionary(JSON.stringify(value, null, 2)); } catch { return applyCharterZhDictionary(String(value)); } } function splitLines(input: string): string[] { return input .split(/\r?\n/) .map((line) => line.trim()) .filter((line) => line.length > 0); } function ensureModuleMap(): Record> { return { module_1: {}, module_2: {}, module_3: {}, module_4: {}, module_5: {}, module_6: {}, }; } function ensureModuleProgressMap(): Record { return { module_1: { requiredTotal: 0, requiredDone: 0, percent: 0, done: false, missing: [] }, module_2: { requiredTotal: 0, requiredDone: 0, percent: 0, done: false, missing: [] }, module_3: { requiredTotal: 0, requiredDone: 0, percent: 0, done: false, missing: [] }, module_4: { requiredTotal: 0, requiredDone: 0, percent: 0, done: false, missing: [] }, module_5: { requiredTotal: 0, requiredDone: 0, percent: 0, done: false, missing: [] }, module_6: { requiredTotal: 0, requiredDone: 0, percent: 0, done: false, missing: [] }, }; } function setIfEmpty(target: Record, key: string, value: unknown): void { if ((target[key] || "").trim().length > 0) return; const text = toText(value).trim(); if (text.length > 0) { target[key] = text; } } function applyLegacyMapping( root: Record, modules: Record> ): void { setIfEmpty(modules.module_1, "problem_statement", root.problem_statement ?? root.rationale); setIfEmpty(modules.module_1, "goal_statement", root.goal_statement ?? root.decision); setIfEmpty(modules.module_1, "kpis", root.success_metrics ?? root.core_metrics); setIfEmpty(modules.module_2, "in_scope", root.in_scope ?? root.scope_in); setIfEmpty(modules.module_2, "out_of_scope", root.out_of_scope ?? root.scope_out); setIfEmpty(modules.module_2, "alignment_qa", root.alignment_checklist ?? root.alignment_questions); setIfEmpty(modules.module_3, "primary_model", root.primary_model ?? root.model_name ?? root.model); setIfEmpty(modules.module_3, "fallback_model", root.fallback_model); setIfEmpty(modules.module_3, "capability_table", root.capability_table ?? root.model_capabilities); setIfEmpty(modules.module_3, "dependency_map", root.dependency_map ?? root.dependencies ?? root.tech_dependencies); setIfEmpty(modules.module_4, "milestones", root.milestones); setIfEmpty(modules.module_4, "role_effort_estimates", root.role_effort_estimates ?? root.resource_plan); setIfEmpty(modules.module_4, "capability_owner_assignment", root.capability_owner_assignment ?? root.assignment); setIfEmpty(modules.module_5, "risk_register", root.risk_register ?? root.risks); setIfEmpty(modules.module_5, "trigger_rules", root.trigger_rules); setIfEmpty(modules.module_5, "execution_rule", root.execution_rule ?? root.fallback_plan); setIfEmpty(modules.module_6, "attachment_metadata", root.attachment_metadata ?? root.attachments); setIfEmpty(modules.module_6, "attachment_summary", root.attachment_summary ?? root.notes ?? root.misc_notes); setIfEmpty(modules.module_6, "search_tags", root.search_tags ?? root.tags); } export function createEmptyCharterModuleDraft(): CharterModuleDraft { const modules = ensureModuleMap(); for (const schema of CHARTER_MODULE_SCHEMAS) { for (const field of schema.fields) { modules[schema.key][field.key] = ""; } } return { metadata: { status: "draft", confidence_score: "", last_validated_at: "", evidence_refs: "", escalation_reason: "", }, modules, }; } export function parseCharterModuleDraft(content: Record | null | undefined): CharterModuleDraft { const root = toRecord(content); const metadata = toRecord(root.metadata); const modulesRoot = toRecord(root.modules); const draft = createEmptyCharterModuleDraft(); draft.metadata.status = toText(metadata.status ?? root.status) || "draft"; draft.metadata.confidence_score = toText(metadata.confidence_score ?? root.confidence_score); draft.metadata.last_validated_at = toText(metadata.last_validated_at ?? root.last_validated_at); draft.metadata.evidence_refs = toText(metadata.evidence_refs ?? root.evidence_refs); draft.metadata.escalation_reason = toText(metadata.escalation_reason ?? root.escalation_reason); for (const schema of CHARTER_MODULE_SCHEMAS) { const moduleRaw = toRecord(modulesRoot[schema.key]); const directModuleRaw = toRecord(root[schema.key]); for (const field of schema.fields) { draft.modules[schema.key][field.key] = toText(moduleRaw[field.key] ?? directModuleRaw[field.key]); } } applyLegacyMapping(root, draft.modules); return draft; } export function buildCharterContentPayload( draft: CharterModuleDraft, originalContent: Record | null | undefined ): Record { const root = toRecord(originalContent); const payload: Record = { ...root, schema_version: "charter_ai_first_v1", }; const confidence = Number(draft.metadata.confidence_score); payload.metadata = { status: draft.metadata.status.trim() || "draft", confidence_score: Number.isFinite(confidence) ? Math.max(0, Math.min(1, confidence)) : null, last_validated_at: draft.metadata.last_validated_at.trim() || null, evidence_refs: splitLines(draft.metadata.evidence_refs), escalation_reason: draft.metadata.escalation_reason.trim() || null, }; const modules: Record> = {}; for (const schema of CHARTER_MODULE_SCHEMAS) { modules[schema.key] = {}; for (const field of schema.fields) { modules[schema.key][field.key] = draft.modules[schema.key][field.key] || ""; } } payload.modules = modules; const rawSignalIds = Array.isArray(root.signal_ids) ? root.signal_ids : []; payload.signal_ids = rawSignalIds.filter((sid) => Number.isFinite(Number(sid))).map((sid) => Number(sid)); const metrics = splitLines(draft.modules.module_1.kpis); payload.success_metrics = metrics; payload.core_metrics = metrics; payload.rationale = draft.modules.module_1.problem_statement || ""; payload.decision = draft.modules.module_1.goal_statement || ""; payload.in_scope = splitLines(draft.modules.module_2.in_scope); payload.out_of_scope = splitLines(draft.modules.module_2.out_of_scope); payload.risks = splitLines(draft.modules.module_5.risk_register); return payload; } export function calculateCharterProgress(draft: CharterModuleDraft): CharterProgress { const moduleProgress = ensureModuleProgressMap(); let requiredTotal = 0; let requiredDone = 0; let completedModules = 0; for (const schema of CHARTER_MODULE_SCHEMAS) { const requiredFields = schema.fields.filter((field) => !!field.required); const missing: string[] = []; let done = 0; for (const field of requiredFields) { const value = draft.modules[schema.key][field.key] || ""; if (value.trim().length > 0) { done += 1; } else { missing.push(field.label.replace("(必填)", "")); } } const total = requiredFields.length; const percent = total > 0 ? Math.round((done / total) * 100) : 100; const isDone = total === done; moduleProgress[schema.key] = { requiredTotal: total, requiredDone: done, percent, done: isDone, missing, }; requiredTotal += total; requiredDone += done; if (isDone) completedModules += 1; } return { requiredTotal, requiredDone, requiredPercent: requiredTotal > 0 ? Math.round((requiredDone / requiredTotal) * 100) : 100, completedModules, moduleProgress, }; }