Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
重构所有代码和文档中的项目名称,将OpenFang统一更新为ZCLAW。包括: - 配置文件中的项目名称 - 代码注释和文档引用 - 环境变量和路径 - 类型定义和接口名称 - 测试用例和模拟数据 同时优化部分代码结构,移除未使用的模块,并更新相关依赖项。
719 lines
23 KiB
Markdown
719 lines
23 KiB
Markdown
# ZCLAW 架构优化设计规格
|
||
|
||
> **版本**: 1.1
|
||
> **日期**: 2026-03-21
|
||
> **状态**: 待审核
|
||
> **作者**: Claude + 用户协作
|
||
|
||
---
|
||
|
||
## 1. 概述
|
||
|
||
### 1.1 背景
|
||
|
||
ZCLAW 是面向中文用户的 AI Agent 桌面客户端,经过分析发现以下需要改进的领域:
|
||
|
||
- **安全风险**: localStorage 凭据回退明文存储、WebSocket 非 localhost 允许 ws://
|
||
- **性能瓶颈**: 流式更新时重建整个消息数组、无界消息数组
|
||
- **架构问题**: 50+ lib 模块缺乏统一抽象、测试覆盖不足
|
||
|
||
### 1.2 目标
|
||
|
||
- 在 14 周内完成全面架构优化 (含 2 周缓冲)
|
||
- 采用激进架构优先策略
|
||
- 重点优化四个核心系统:对话、Hands、Intelligence、技能
|
||
|
||
### 1.3 术语表
|
||
|
||
| 术语 | 含义 | 备注 |
|
||
|------|------|------|
|
||
| **Valtio** | Proxy-based 状态管理库 | pmnd.rs/valtio,将替代 Zustand |
|
||
| Zustand | 当前使用的状态管理库 | 将被 Valtio 替代 |
|
||
| XState | 状态机库 | 用于 Hands 状态管理 |
|
||
|
||
### 1.4 关键决策
|
||
|
||
| 决策点 | 选择 | 理由 |
|
||
|--------|------|------|
|
||
| 状态管理 | **Valtio** (替代 Zustand) | Proxy 细粒度响应,性能更好 |
|
||
| 安全策略 | 凭据加密存储 + WSS 强制 | 解决实际存在的安全风险 |
|
||
| 整体方案 | A+C 混合 | 渐进式 + 领域驱动结合 |
|
||
|
||
---
|
||
|
||
## 2. 架构设计
|
||
|
||
### 2.1 总体架构
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ UI Layer │
|
||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||
│ │ React Components (60+) │ │
|
||
│ │ - 按领域组织 │ │
|
||
│ │ - 只负责展示和交互 │ │
|
||
│ └──────────────────────────────────────────────────────────┘ │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ State Layer │
|
||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||
│ │ Valtio Stores (基于 Proxy) │ │
|
||
│ │ - 细粒度响应 │ │
|
||
│ │ - 领域划分: Chat, Hands, Intelligence, Skills │ │
|
||
│ └──────────────────────────────────────────────────────────┘ │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ Client Layer │
|
||
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────┐ │
|
||
│ │ Gateway Client │ │ Intelligence Clt │ │ Worker Pool │ │
|
||
│ │ (WebSocket) │ │ (Tauri Commands) │ │ (隔离执行) │ │
|
||
│ └──────────────────┘ └──────────────────┘ └──────────────┘ │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ Backend Layer │
|
||
│ ┌──────────────────┐ ┌──────────────────────────────────────┐│
|
||
│ │ ZCLAW Kernel │ │ Tauri Rust Backend ││
|
||
│ │ (Port 50051) │ │ - Intelligence (心跳/压缩/反思) ││
|
||
│ │ │ │ - Memory (SQLite 持久化) ││
|
||
│ │ │ │ - Browser (WebDriver) ││
|
||
│ └──────────────────┘ └──────────────────────────────────────┘│
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 2.2 领域划分
|
||
|
||
```
|
||
desktop/src/
|
||
├── domains/ # 领域模块 (新建)
|
||
│ ├── chat/ # 对话系统
|
||
│ │ ├── store.ts # Valtio store
|
||
│ │ ├── types.ts # 类型定义
|
||
│ │ ├── api.ts # API 调用
|
||
│ │ └── hooks.ts # React hooks
|
||
│ ├── hands/ # 自动化系统
|
||
│ │ ├── store.ts # 状态机 store
|
||
│ │ ├── machine.ts # XState 状态机
|
||
│ │ ├── executor.ts # 执行器
|
||
│ │ └── types.ts
|
||
│ ├── intelligence/ # 智能层
|
||
│ │ ├── client.ts # 统一客户端
|
||
│ │ ├── cache.ts # 缓存策略
|
||
│ │ └── types.ts
|
||
│ └── skills/ # 技能系统
|
||
│ ├── store.ts
|
||
│ ├── loader.ts # 技能加载器
|
||
│ └── types.ts
|
||
├── workers/ # Web Workers (新建)
|
||
│ ├── browser-worker.ts # 浏览器隔离执行
|
||
│ └── pool.ts # Worker 池管理
|
||
├── shared/ # 共享模块 (新建)
|
||
│ ├── error-handling.ts # 统一错误处理
|
||
│ ├── logging.ts # 统一日志
|
||
│ └── types.ts # 共享类型
|
||
└── components/ # UI 组件 (保持)
|
||
```
|
||
|
||
### 2.3 现有代码分析
|
||
|
||
| 模块 | 现有文件 | 行数 | 迁移策略 |
|
||
|------|----------|------|----------|
|
||
| Chat | `store/chatStore.ts` | 689 | 迁移到 Valtio,保留 API |
|
||
| Hands | `store/handStore.ts` | 535 | 迁移到 XState 状态机 |
|
||
| Intelligence | `lib/intelligence-client.ts` | 956 | 保留,增强缓存层 |
|
||
| Browser | `lib/browser-client.ts` | 461 | 保留,无需 Worker |
|
||
| Secure Storage | `lib/secure-storage.ts` | 350 | 增强,添加加密回退 |
|
||
| Gateway | `lib/gateway-client.ts` | 1100 | 保留,强制 WSS |
|
||
|
||
### 2.4 迁移映射表
|
||
|
||
| 现有文件 | 新位置 | 操作 |
|
||
|----------|--------|------|
|
||
| `store/chatStore.ts` | `domains/chat/store.ts` | 迁移 + 重写 |
|
||
| `store/handStore.ts` | `domains/hands/store.ts` | 迁移 + 状态机 |
|
||
| `store/skillStore.ts` | `domains/skills/store.ts` | 迁移 |
|
||
| `lib/intelligence-client.ts` | `domains/intelligence/client.ts` | 迁移 + 增强 |
|
||
| `lib/error-handling.ts` | `shared/error-handling.ts` | 迁移 |
|
||
| `lib/secure-storage.ts` | `shared/secure-storage.ts` | 增强 |
|
||
| `store/agentStore.ts` | 保留 | 不变 |
|
||
| `store/connectionStore.ts` | 保留 | 不变 |
|
||
| `lib/gateway-client.ts` | 保留 | 不变 |
|
||
|
||
---
|
||
|
||
## 3. 核心组件设计
|
||
|
||
### 3.1 Valtio 状态管理 (替代 Zustand)
|
||
|
||
**问题**: 当前 Zustand 每次更新都会触发整个订阅组件重渲染
|
||
|
||
**解决方案**: 使用 Valtio 的 Proxy 实现细粒度响应
|
||
|
||
```typescript
|
||
// domains/chat/store.ts
|
||
import { proxy, useSnapshot } from 'valtio';
|
||
|
||
interface ChatState {
|
||
messages: Message[];
|
||
conversations: Conversation[];
|
||
currentConversationId: string | null;
|
||
isStreaming: boolean;
|
||
|
||
// Actions
|
||
addMessage: (message: Message) => void;
|
||
updateMessage: (id: string, update: Partial<Message>) => void;
|
||
setStreaming: (streaming: boolean) => void;
|
||
}
|
||
|
||
export const chatState = proxy<ChatState>({
|
||
messages: [],
|
||
conversations: [],
|
||
currentConversationId: null,
|
||
isStreaming: false,
|
||
|
||
addMessage: (message) => {
|
||
chatState.messages.push(message); // 直接 mutate
|
||
},
|
||
|
||
updateMessage: (id, update) => {
|
||
const msg = chatState.messages.find(m => m.id === id);
|
||
if (msg) Object.assign(msg, update); // 细粒度更新
|
||
},
|
||
|
||
setStreaming: (streaming) => {
|
||
chatState.isStreaming = streaming;
|
||
}
|
||
});
|
||
|
||
// Hook
|
||
export function useChatState() {
|
||
return useSnapshot(chatState); // 只在访问的字段变化时重渲染
|
||
}
|
||
|
||
// 组件使用
|
||
function MessageList() {
|
||
const { messages } = useChatState(); // 只订阅 messages
|
||
return messages.map(m => <Message key={m.id} message={m} />);
|
||
}
|
||
|
||
function StreamingIndicator() {
|
||
const { isStreaming } = useChatState(); // 只订阅 isStreaming
|
||
return isStreaming ? <Spinner /> : null;
|
||
}
|
||
```
|
||
|
||
**迁移策略**:
|
||
- 现有 `chatStore.ts` (689 行) 将被重写
|
||
- 保持相同的 API 接口,确保组件无需修改
|
||
- 逐步迁移,先迁移 Chat,再迁移其他 Store
|
||
|
||
**预期收益**:
|
||
- 流式更新时性能提升 70%
|
||
- 代码更简洁 (直接 mutate)
|
||
- 选择性渲染减少不必要的重渲染
|
||
|
||
### 3.2 Hands 状态机 (XState)
|
||
|
||
**问题**: 当前 `handStore.ts` (535 行) 状态转换逻辑分散,难以追踪
|
||
|
||
**解决方案**: 使用 XState 实现状态机,与现有 Store 集成
|
||
|
||
```typescript
|
||
// domains/hands/machine.ts
|
||
import { createMachine, assign } from 'xstate';
|
||
|
||
export const handMachine = createMachine({
|
||
id: 'hand',
|
||
initial: 'idle',
|
||
states: {
|
||
idle: {
|
||
on: { TRIGGER: 'validating' },
|
||
},
|
||
validating: {
|
||
on: {
|
||
VALID: 'checking_approval',
|
||
INVALID: 'error',
|
||
},
|
||
},
|
||
checking_approval: {
|
||
on: {
|
||
NEEDS_APPROVAL: 'pending_approval',
|
||
AUTO_APPROVE: 'executing',
|
||
},
|
||
},
|
||
pending_approval: {
|
||
on: {
|
||
APPROVE: 'executing',
|
||
REJECT: 'cancelled',
|
||
TIMEOUT: 'error',
|
||
},
|
||
},
|
||
executing: {
|
||
on: {
|
||
SUCCESS: 'completed',
|
||
ERROR: 'error',
|
||
TIMEOUT: 'error',
|
||
},
|
||
},
|
||
completed: { on: { RESET: 'idle' } },
|
||
error: { on: { RETRY: 'validating', RESET: 'idle' } },
|
||
cancelled: { on: { RESET: 'idle' } },
|
||
},
|
||
});
|
||
|
||
// domains/hands/store.ts - 与现有 API 集成
|
||
import { createActorContext } from '@xstate/react';
|
||
|
||
export const HandContext = createActorContext(handMachine);
|
||
|
||
// 使用方式
|
||
function HandPanel() {
|
||
const state = HandContext.useSelector(s => s);
|
||
const send = HandContext.useActorRef();
|
||
|
||
return (
|
||
<div>
|
||
{state.matches('pending_approval') && <ApprovalDialog />}
|
||
{state.matches('executing') && <Spinner />}
|
||
</div>
|
||
);
|
||
}
|
||
```
|
||
|
||
**迁移策略**:
|
||
- 现有 `handStore.ts` 保持向后兼容
|
||
- 新建 `domains/hands/` 模块
|
||
- 逐步将状态逻辑迁移到 XState
|
||
- 保持现有组件 API 不变
|
||
|
||
### 3.3 Intelligence 缓存增强
|
||
|
||
**问题**: 现有 `intelligence-client.ts` (956 行) 已有 localStorage 回退缓存,但缺少 LRU 淘汰和 TTL
|
||
|
||
**解决方案**: 增强现有缓存层,添加 LRU + TTL
|
||
|
||
> **注意**: 这是增强现有实现,而非替换
|
||
|
||
```typescript
|
||
// domains/intelligence/cache.ts
|
||
interface CacheEntry<T> {
|
||
value: T;
|
||
expiresAt: number;
|
||
}
|
||
|
||
export class IntelligenceCache {
|
||
private cache = new Map<string, CacheEntry<unknown>>();
|
||
private maxSize = 100;
|
||
private defaultTTL = 5 * 60 * 1000; // 5 minutes
|
||
|
||
get<T>(key: string): T | null {
|
||
const entry = this.cache.get(key);
|
||
if (!entry) return null;
|
||
|
||
if (Date.now() > entry.expiresAt) {
|
||
this.cache.delete(key);
|
||
return null;
|
||
}
|
||
|
||
return entry.value as T;
|
||
}
|
||
|
||
set<T>(key: string, value: T, ttl = this.defaultTTL): void {
|
||
// LRU 淘汰
|
||
if (this.cache.size >= this.maxSize) {
|
||
const firstKey = this.cache.keys().next().value;
|
||
this.cache.delete(firstKey);
|
||
}
|
||
|
||
this.cache.set(key, {
|
||
value,
|
||
expiresAt: Date.now() + ttl,
|
||
});
|
||
}
|
||
|
||
invalidate(pattern: string): void {
|
||
for (const key of this.cache.keys()) {
|
||
if (key.includes(pattern)) {
|
||
this.cache.delete(key);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 安全设计
|
||
|
||
### 4.1 凭据存储加密增强
|
||
|
||
> **现有实现**: `lib/secure-storage.ts` (350 行) 已使用 OS keyring + localStorage fallback
|
||
> **问题**: localStorage fallback 存储明文密钥,存在安全风险
|
||
> **策略**: 增强现有实现,为 localStorage fallback 添加加密
|
||
|
||
```typescript
|
||
// shared/secure-storage.ts
|
||
export class SecureCredentialStorage {
|
||
private async getEncryptionKey(): Promise<CryptoKey> {
|
||
// 从 OS keyring 获取或派生
|
||
const masterKey = await this.getMasterKey();
|
||
return deriveKey(masterKey, SALT, {
|
||
name: 'AES-GCM',
|
||
length: 256,
|
||
});
|
||
}
|
||
|
||
async store(key: string, value: string): Promise<void> {
|
||
const encKey = await this.getEncryptionKey();
|
||
const iv = crypto.getRandomValues(new Uint8Array(12));
|
||
const encrypted = await crypto.subtle.encrypt(
|
||
{ name: 'AES-GCM', iv },
|
||
encKey,
|
||
new TextEncoder().encode(value)
|
||
);
|
||
|
||
// 存储加密后的数据
|
||
const stored = {
|
||
iv: arrayToBase64(iv),
|
||
data: arrayToBase64(new Uint8Array(encrypted)),
|
||
};
|
||
|
||
if (isTauriEnv()) {
|
||
await invoke('secure_store', { key, value: JSON.stringify(stored) });
|
||
} else {
|
||
localStorage.setItem(`enc_${key}`, JSON.stringify(stored));
|
||
}
|
||
}
|
||
|
||
async retrieve(key: string): Promise<string | null> {
|
||
let stored: { iv: string; data: string };
|
||
|
||
if (isTauriEnv()) {
|
||
stored = await invoke('secure_retrieve', { key });
|
||
} else {
|
||
const raw = localStorage.getItem(`enc_${key}`);
|
||
if (!raw) return null;
|
||
stored = JSON.parse(raw);
|
||
}
|
||
|
||
const encKey = await this.getEncryptionKey();
|
||
const decrypted = await crypto.subtle.decrypt(
|
||
{ name: 'AES-GCM', iv: base64ToArray(stored.iv) },
|
||
encKey,
|
||
base64ToArray(stored.data)
|
||
);
|
||
|
||
return new TextDecoder().decode(decrypted);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4.2 WebSocket 安全
|
||
|
||
```typescript
|
||
// shared/websocket-security.ts
|
||
export function createSecureWebSocket(url: string): WebSocket {
|
||
// 强制 WSS for non-localhost
|
||
if (!url.startsWith('wss://') && !isLocalhost(url)) {
|
||
throw new SecurityError(
|
||
'Non-localhost connections must use WSS protocol'
|
||
);
|
||
}
|
||
|
||
const ws = new WebSocket(url);
|
||
|
||
// 添加消息验证
|
||
ws.addEventListener('message', (event) => {
|
||
const message = validateMessage(event.data);
|
||
if (!message) {
|
||
console.error('Invalid message format');
|
||
ws.close(1008, 'Policy Violation');
|
||
}
|
||
});
|
||
|
||
return ws;
|
||
}
|
||
|
||
function isLocalhost(url: string): boolean {
|
||
const parsed = new URL(url);
|
||
return ['localhost', '127.0.0.1', '::1'].includes(parsed.hostname);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 测试策略
|
||
|
||
### 5.1 单元测试
|
||
|
||
```typescript
|
||
// domains/chat/store.test.ts
|
||
describe('ChatState', () => {
|
||
beforeEach(() => {
|
||
chatState.messages = [];
|
||
chatState.isStreaming = false;
|
||
});
|
||
|
||
it('should add message', () => {
|
||
const msg = createMockMessage();
|
||
chatState.addMessage(msg);
|
||
expect(chatState.messages).toHaveLength(1);
|
||
expect(chatState.messages[0]).toEqual(msg);
|
||
});
|
||
|
||
it('should update message content', () => {
|
||
chatState.messages = [createMockMessage({ id: '1', content: 'old' })];
|
||
chatState.updateMessage('1', { content: 'new' });
|
||
expect(chatState.messages[0].content).toBe('new');
|
||
});
|
||
|
||
it('should not trigger re-render for unrelated updates', () => {
|
||
const { result, rerender } = renderHook(() => useChatState());
|
||
|
||
act(() => {
|
||
chatState.isStreaming = true; // 不会触发 MessageList 重渲染
|
||
});
|
||
|
||
// 验证只有订阅 isStreaming 的组件重渲染
|
||
});
|
||
});
|
||
```
|
||
|
||
### 5.2 集成测试
|
||
|
||
```typescript
|
||
// domains/hands/machine.test.ts
|
||
describe('HandMachine', () => {
|
||
it('should transition from idle to validating on TRIGGER', () => {
|
||
const service = interpret(handMachine).start();
|
||
service.send('TRIGGER');
|
||
expect(service.state.value).toBe('validating');
|
||
});
|
||
|
||
it('should reach pending_approval for hands requiring approval', () => {
|
||
const service = interpret(handMachine).start();
|
||
service.send('TRIGGER');
|
||
service.send('VALID');
|
||
service.send('NEEDS_APPROVAL');
|
||
expect(service.state.value).toBe('pending_approval');
|
||
});
|
||
|
||
it('should cancel on REJECT', () => {
|
||
const service = interpret(handMachine).start();
|
||
// ... navigate to pending_approval
|
||
service.send('REJECT');
|
||
expect(service.state.value).toBe('cancelled');
|
||
});
|
||
});
|
||
```
|
||
|
||
### 5.3 E2E 测试场景
|
||
|
||
1. **聊天流程**: 发送消息 → 接收流式响应 → 显示完整消息
|
||
2. **Hands 触发**: 触发 Hand → 审批流程 → 执行 → 结果显示
|
||
3. **Intelligence**: 记忆提取 → 心跳触发 → 反思生成
|
||
4. **技能搜索**: 输入关键词 → 搜索技能 → 查看详情
|
||
|
||
---
|
||
|
||
## 6. 实施计划
|
||
|
||
> **总周期**: 14 周 (含 2 周缓冲)
|
||
|
||
### 6.1 Phase 1: 安全 + 测试 (2周)
|
||
|
||
**目标**: 建立安全基础和测试框架
|
||
|
||
#### TASK-001: 凭据加密存储增强
|
||
- **输入条件**: 现有 `secure-storage.ts` 可用
|
||
- **输出产物**: 增强的 `shared/secure-storage.ts`
|
||
- **验收标准**:
|
||
- localStorage fallback 使用 AES-GCM 加密
|
||
- 所有单元测试通过
|
||
- 无回归问题
|
||
- **预估工时**: 3 人日
|
||
- **依赖**: 无
|
||
|
||
#### TASK-002: 强制 WSS 连接
|
||
- **输入条件**: 现有 `gateway-client.ts` 可用
|
||
- **输出产物**: 更新的 `gateway-client.ts`
|
||
- **验收标准**:
|
||
- 非 localhost 连接必须使用 wss://
|
||
- 测试覆盖边界情况
|
||
- **预估工时**: 1 人日
|
||
- **依赖**: 无
|
||
|
||
#### TASK-003: 测试框架建立
|
||
- **输入条件**: 无
|
||
- **输出产物**: Vitest + Playwright 配置
|
||
- **验收标准**:
|
||
- 覆盖率门禁 60% 生效
|
||
- CI 集成完成
|
||
- **预估工时**: 2 人日
|
||
- **依赖**: 无
|
||
|
||
#### TASK-004: chatStore 基础测试
|
||
- **输入条件**: 测试框架就绪
|
||
- **输出产物**: `store/chatStore.test.ts`
|
||
- **验收标准**:
|
||
- 核心功能测试覆盖
|
||
- 覆盖率 > 80%
|
||
- **预估工时**: 2 人日
|
||
- **依赖**: TASK-003
|
||
|
||
### 6.2 Phase 2: 领域重组 (4周)
|
||
|
||
**目标**: 按领域重组代码,迁移到 Valtio
|
||
|
||
#### TASK-101: 创建 domains/ 目录结构
|
||
- **预估工时**: 1 人日
|
||
|
||
#### TASK-102: 迁移 Chat Store 到 Valtio
|
||
- **输入条件**: 现有 `chatStore.ts` (689 行)
|
||
- **输出产物**: `domains/chat/store.ts`
|
||
- **验收标准**:
|
||
- API 向后兼容
|
||
- 性能提升 > 50%
|
||
- 测试覆盖率 > 90%
|
||
- **预估工时**: 5 人日
|
||
|
||
#### TASK-103: 迁移 Hands Store + XState
|
||
- **输入条件**: 现有 `handStore.ts` (535 行)
|
||
- **输出产物**: `domains/hands/store.ts` + `machine.ts`
|
||
- **验收标准**:
|
||
- 状态机完整实现
|
||
- 审批流程正常
|
||
- 测试覆盖率 > 85%
|
||
- **预估工时**: 5 人日
|
||
|
||
#### TASK-104: 增强 Intelligence 缓存
|
||
- **输入条件**: 现有 `intelligence-client.ts` (956 行)
|
||
- **输出产物**: 增强的缓存层
|
||
- **预估工时**: 3 人日
|
||
|
||
#### TASK-105: 提取共享模块
|
||
- **预估工时**: 2 人日
|
||
|
||
### 6.3 Phase 3: 核心优化 (6周,并行)
|
||
|
||
**Track A: Chat 优化 (2人)**
|
||
- [ ] Valtio 性能优化
|
||
- [ ] 虚拟滚动实现 (react-window)
|
||
- [ ] 消息分页 + 惰性加载
|
||
- [ ] 流式响应 AsyncGenerator
|
||
|
||
**Track B: Hands 优化 (1人)**
|
||
- [ ] XState 状态机完善
|
||
- [ ] 审批流程配置化
|
||
- [ ] 错误恢复机制
|
||
|
||
**Track C: Intelligence 优化 (1人)**
|
||
- [ ] Rust 后端功能完善
|
||
- [ ] LRU 缓存集成
|
||
- [ ] 性能调优
|
||
|
||
### 6.4 Phase 4: 集成 + 清理 (2周 = 1周实施 + 1周缓冲)
|
||
|
||
**目标**: 完成集成,清理旧代码
|
||
|
||
**任务**:
|
||
- [ ] 跨领域集成测试
|
||
- [ ] E2E 测试完善
|
||
- [ ] 清理旧代码
|
||
- [ ] 更新文档
|
||
- [ ] 性能基准测试
|
||
|
||
**交付物**:
|
||
- 完整的测试套件
|
||
- 更新的文档
|
||
- 性能报告
|
||
|
||
---
|
||
|
||
## 7. 验收标准
|
||
|
||
### 7.1 功能验收
|
||
|
||
- [ ] 所有现有功能正常工作
|
||
- [ ] 无回归问题
|
||
- [ ] 新安全功能有效
|
||
|
||
### 7.2 性能验收
|
||
|
||
| 指标 | 当前 | 目标 | 测量方法 |
|
||
|------|------|------|----------|
|
||
| 首屏加载 | ~3s | <2s | Chrome DevTools Performance → navigationStart 到 firstContentfulPaint |
|
||
| 消息渲染 | 卡顿 | 60fps | React DevTools Profiler → MessageList 组件渲染时间 |
|
||
| 1000+ 消息滚动 | 卡顿 | 流畅 | Chrome DevTools Performance → 滚动时 frame rate |
|
||
| 内存占用 | 无界 | <500MB | Chrome Memory Profiler → 堆快照对比 |
|
||
| Store 更新延迟 | ~50ms | <10ms | Performance.mark() 测量状态更新到渲染完成 |
|
||
|
||
### 7.3 安全验收
|
||
|
||
- [ ] localStorage 凭据已加密 (使用 AES-GCM)
|
||
- [ ] 非 localhost 连接强制 WSS
|
||
- [ ] 依赖安全扫描通过 (npm audit)
|
||
- [ ] 密钥不在日志中输出
|
||
- [ ] WebSocket 安全测试通过
|
||
- [ ] 依赖安全扫描通过
|
||
|
||
### 7.4 测试覆盖
|
||
|
||
| 模块 | 目标 |
|
||
|------|------|
|
||
| Chat Store | 90% |
|
||
| Hands Store | 85% |
|
||
| Intelligence Client | 80% |
|
||
| Worker Pool | 85% |
|
||
| 工具函数 | 95% |
|
||
|
||
---
|
||
|
||
## 8. 风险与缓解
|
||
|
||
| 风险 | 概率 | 影响 | 缓解措施 |
|
||
|------|------|------|----------|
|
||
| Valtio 学习曲线 | 中 | 中 | 提前 POC,编写示例 |
|
||
| XState 状态机复杂度 | 中 | 中 | 从简单场景开始,逐步完善 |
|
||
| 迁移导致回归 | 中 | 高 | 每阶段充分测试 |
|
||
| 进度延期 | 中 | 中 | 预留 2 周缓冲 |
|
||
| 团队不熟悉 | 中 | 中 | 培训 + 文档 |
|
||
|
||
---
|
||
|
||
## 9. 附录
|
||
|
||
### 9.1 技术依赖
|
||
|
||
```json
|
||
{
|
||
"dependencies": {
|
||
"valtio": "1.11.2",
|
||
"xstate": "5.18.2",
|
||
"@xstate/react": "4.1.3",
|
||
"react-window": "1.8.10"
|
||
},
|
||
"devDependencies": {
|
||
"vitest": "2.1.8",
|
||
"@playwright/test": "1.49.1",
|
||
"@testing-library/react": "16.1.0"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 9.2 参考文档
|
||
|
||
- [Valtio 文档](https://valtio.pmnd.rs/)
|
||
- [XState 文档](https://xstate.js.org/)
|
||
- [React Window 文档](https://react-window.vercel.app/)
|
||
- [Tauri 安全最佳实践](https://tauri.app/v2/guides/security/)
|
||
|
||
---
|
||
|
||
*文档版本: 1.1 | 最后更新: 2026-03-21*
|