# ZCLAW 发布前审计设计文档 > 日期: 2026-04-18 > 目标: 全维度审计系统问题,为首次用户发布做准备 > 方法: 4 专家组并行分析 + 交叉评审 ## 背景 ZCLAW 已完成稳定化基线,进入发布准备阶段。在发布前组织了一次多维度深度审计,通过 4 个专家代理(后端稳定性、前端质量、安全与数据、工程卫生)并行分析,发现并验证了 24 个问题点。经交叉评审后纠正了 4 项原始审计误判。 ## 审计纠正(原始误判) | 原始声称 | 实际情况 | |----------|----------| | Cargo.lock 缺失 | 已提交并跟踪,`git ls-files Cargo.lock` 确认 | | 无 CI/CD | `.github/workflows/ci.yml` + `release.yml` 完整存在 | | src-tauri LOC 偏差 3x | 实际 61,257 行,与 TRUTH.md ~61,400 基本一致 | | Token INTEGER 溢出 | 每行存单次请求 token 不溢出,SUM() 已返回 BIGINT | --- ## 第一层:发布阻塞项(必须修复) ### 1. Director 死锁风险 — P0 CRITICAL **文件**: `crates/zclaw-kernel/src/director.rs:506-536` **问题**: `send_to_agent()` 顺序获取 `pending_requests.lock()`(L506)和 `inbox.lock()`(L519),后者在 `tokio::time::timeout` 内跨 `rx.recv().await` 持有(L521-536)。两个并发调用可互相阻塞。另有一条死信通道 `_response_tx/_response_rx`(L490)从未连接——sender 存入 pending_requests 但 receiver 无人读取。 **验证**: 修复后需添加并发 `send_to_agent()` 测试验证死锁消除。 **修复方案**: 用 `oneshot` channel 重构响应接收模式: - 每次 `send_to_agent()` 创建 `oneshot::channel` - sender 存入 `pending_requests`,receiver 配合 `tokio::time::timeout` 等待 - 新增独立的 inbox 消费任务分发响应到对应 oneshot sender - 变更 `pending_requests` 类型为 `HashMap>` **工时**: 2-4h(重构 + 测试更新) ### 2. Pipeline Executor 内存泄漏 — P0 HIGH **文件**: `crates/zclaw-pipeline/src/executor.rs` **问题**: `runs: RwLock>` 和 `cancellations: RwLock>` 无限增长,无清理路径。 **修复方案**: - 添加 `cleanup(max_age: Duration)` 方法,清除已完成/失败/取消的旧记录 - 在 `execute_with_id()` 完成后自动调用清理 - 设置 `max_completed_runs` 上限(如 100),超限淘汰最旧记录 **工时**: <1h ### 3. Pipeline 步骤超时缺失 + Delay 无上限 — P0 HIGH **文件**: `crates/zclaw-pipeline/src/executor.rs` **问题**: `ExecuteError::Timeout` 已定义但从未触发。每步执行无超时包装。`Action::Delay { ms }` 接受原始 u64,恶意 YAML 可设 `ms: u64::MAX`。 **修复方案**: - 用 `tokio::time::timeout` 包装每步 `execute_action` 调用 - 使用 `PipelineSpec.timeout_secs`(已存在但未使用),cap 在 5 分钟 - Delay ms 上限 60000,超出时 warn 并截断 - `parser.rs`/`parser_v2.rs` 添加 YAML 解析时验证 **工时**: 1-2h ### 4. TRUTH.md Hands 数量偏差 — P0 (文档完整性) **文件**: `docs/TRUTH.md`, `CLAUDE.md` **问题**: 声称 9 个 Hand 启用,实际 kernel 注册 7 个: - 6 个通过 `hands/*.HAND.toml` 扫描注册:Browser/Clip/Collector/Quiz/Researcher/Twitter - 1 个通过 `kernel/mod.rs:96` 编程注册:ReminderHand(`_` 前缀豁免 HAND.toml 扫描,见 `trigger_manager.rs:139`) - Whiteboard/Slideshow/Speech 的 HAND.toml 仅存在于 `.claude/worktrees/` 开发分支,无 `impl Hand for`,未合并到主分支 **修复方案**: - TRUTH.md: 更新为 "6 HAND.toml + Reminder 系统内部 = 7 注册" - CLAUDE.md §6: 明确标注 Whiteboard/Slideshow/Speech 为"开发中,未合并" - 确认桌面 UI 是否展示 9 个 Hand,如有则同步更新 **工时**: <1h ### 5. rate_limit_events 清理 Worker 是空壳 — P0 (数据膨胀) **文件**: `crates/zclaw-saas/src/workers/cleanup_rate_limit.rs` **问题**: Worker body 是 no-op(注释说"rate limit entries are in-memory"),但 main.rs 的 batch flush 确实将限流条目写入数据库。注意:内存中的 DashMap 清理每 300 秒运行一次(`state.rs:118`),但**数据库持久化条目**无限增长,无任何删除机制。 **修复方案**: 实现 Worker body,执行 `DELETE FROM rate_limit_events WHERE created_at < NOW() - INTERVAL '1 hour'`。确认调度器已注册此 Worker(main.rs:47 已注册)。 **工时**: <1h --- ## 第二层:强烈建议修复 ### 6. TypeScript 编译排除安全关键文件 **文件**: `desktop/tsconfig.json` **问题**: 排除了 `ErrorAlert.tsx`(文件已不存在,残留排除项)和 `ErrorBoundary.tsx`(527 行安全关键组件)。 **修复**: 删除排除项,运行 `tsc --noEmit` 验证 ErrorBoundary 无类型错误。 **工时**: <1h ### 7. LlmConfig api_key Debug 泄露 **文件**: `crates/zclaw-kernel/src/config.rs` **问题**: `#[derive(Debug)]` 会在 `format!("{:?}", config)` 中打印 api_key 明文。虽然当前无代码 Debug-print 此结构,但日志调试时容易触发。 **修复**: 移除 `Debug` derive,实现自定义 `Debug` impl 用 `"***REDACTED***"` 遮蔽 api_key。 **工时**: <30min ### 8. 关键 .unwrap() 调用 **文件**: - `crates/zclaw-saas/src/billing/handlers.rs:598` — Response builder unwrap - `desktop/src-tauri/src/classroom_commands/mod.rs:58` — db_path.parent().unwrap() **修复**: 替换为 `map_err` + `?` 传播。 **工时**: <1h ### 9. 静默吞错关键集群 **文件与修复**: - `crates/zclaw-kernel/src/kernel/approvals.rs:88,93,124` — 已有 `tracing::warn!` 日志但级别应为 `error`(审批状态丢失是严重事件) - `crates/zclaw-protocols/src/mcp_transport.rs:429` → 记录僵尸进程风险 - `crates/zclaw-kernel/src/events.rs:21` → `tracing::debug!("Event dropped: {:?}", e)` - `crates/zclaw-runtime/src/tool/builtin/task.rs` → 日志记录 subtask 事件丢失 - `crates/zclaw-growth/src/storage/sqlite.rs` 迁移 → 匹配 `sqlx::Error::Database` 检查 SQLite 错误码 1 子错误 "duplicate column name",区分幂等迁移与真实错误 **工时**: 2-4h ### 10. 缺失数据库索引 **新文件**: `crates/zclaw-saas/migrations/20260418000001_add_missing_indexes.sql` ```sql CREATE INDEX IF NOT EXISTS idx_rle_created_at ON rate_limit_events(created_at); CREATE INDEX IF NOT EXISTS idx_billing_sub_plan ON billing_subscriptions(plan_id); CREATE INDEX IF NOT EXISTS idx_ki_created_by ON knowledge_items(created_by); ``` **工时**: <1h ### 11. 配置验证缺失 **文件**: `crates/zclaw-saas/src/config.rs` **修复**: 在 `SaaSConfig::load()` 添加: - `jwt_expiration_hours >= 1` - `max_connections > 0` - 改善默认 DB URL 连接失败的错误信息 **工时**: <1h ### 12. MCP Transport 响应错配 **文件**: `crates/zclaw-protocols/src/mcp_transport.rs` **问题**: stdin/stdout 分离的 Mutex 可导致并发请求收到错误响应。 **修复**: 合并 stdin + stdout 为单一 Mutex,在 write-then-read 周期内持有锁。 **工时**: 3-4h --- ## 第三层:可延后至首个补丁 | # | 问题 | 工时 | |---|------|------| | 13 | console.log 清理(105处→createLogger) | 2-3h | | 14 | ChatStore 双源真相重构 | 2-4h | | 15 | 33处内联样式→Tailwind | <1h | | 16 | SaaS mixin `prototype: any` 类型约束 | <1h | | 17 | serde_yaml 统一到 serde_yaml_bw | 1-2h | | 18 | 32处 dead_code 审查清理 | 2-4h | | 19 | webhook 废弃表删除迁移 | <30min | | 20 | A2A feature gate 或移除 feature 定义 | <30min | | 21 | dependency 内联声明→workspace 引用 | 1-2h | | 22 | Kernel→Growth 隐式依赖显式化 | <30min | | 23 | noUncheckedIndexedAccess 添加 | 2-4h | | 24 | handStore/configStore duck-typing→discriminator | <1h | --- ## TRUTH.md 数值校准清单 | 指标 | 当前值 | 应更正为 | 验证命令 | |------|--------|----------|----------| | #[test] (crates) | 433 | 425 | `grep -rn '^\s*#\[test\]\s*$' crates/ --include="*.rs" \| wc -l` | | #[tokio::test] (crates) | 368 | 309 | `grep -rn '^\s*#\[tokio::test\]' crates/ --include="*.rs" \| wc -l` | | Zustand Store | 21 | 26 (含子目录) | `find desktop/src/store/ -name "*.ts" \| wc -l` | | Admin V2 页面 | 15 | 17 | `ls admin-v2/src/pages/*.tsx \| wc -l` | | Pipeline YAML | 17 | 18 | `find pipelines/ -name "*.yaml" \| wc -l` | | Hands 启用 | 9 | 7 (6 HAND.toml + Reminder) | `ls hands/*.HAND.toml \| wc -l` + kernel registry | --- ## 实施计划 ### Batch 1: 发布阻塞修复 (Day 1, 上午 + 下午) 按依赖顺序执行(总工时 ~6-9h,建议分上下午): 1. Pipeline 超时 + 内存泄漏 + Delay 上限(#2, #3)— 上午 2. Director 死锁修复(#1)— 上午,可并行 3. rate_limit_events Worker 实现(#5)— 下午 4. TRUTH.md + CLAUDE.md 数值校准(#4)— 下午 **验证**: `cargo test --workspace --exclude zclaw-saas` + `tsc --noEmit` ### Batch 2: 强烈建议修复 (Day 2) 5. tsconfig 修复(#6) 6. LlmConfig Debug 遮蔽(#7) 7. 关键 unwrap 修复(#8) 8. 静默吞错修复 — 关键集群(#9) 9. 缺失索引迁移(#10) 10. Config 验证(#11) 11. MCP Transport 锁合并(#12) **验证**: `cargo test --workspace --exclude zclaw-saas` + `pnpm tsc --noEmit` + `pnpm vitest run` ### Batch 3: 补丁迭代 (Day 3+) 按优先级从高到低处理第三层 12 项。 --- ## 关键文件列表 - `crates/zclaw-kernel/src/director.rs` — P0 Director 死锁 - `crates/zclaw-pipeline/src/executor.rs` — P0 Pipeline 内存泄漏 + 超时 - `crates/zclaw-saas/src/workers/cleanup_rate_limit.rs` — P0 Worker 空壳 - `docs/TRUTH.md` — P0 文档校准 - `desktop/tsconfig.json` — P1 类型排除 - `crates/zclaw-kernel/src/config.rs` — P1 Debug 泄露 - `crates/zclaw-saas/src/billing/handlers.rs` — P1 unwrap - `desktop/src-tauri/src/classroom_commands/mod.rs` — P1 unwrap - `crates/zclaw-protocols/src/mcp_transport.rs` — P1 响应错配 - `crates/zclaw-saas/src/config.rs` — P1 配置验证