Skip to content

Latest commit

 

History

History
231 lines (149 loc) · 13.4 KB

File metadata and controls

231 lines (149 loc) · 13.4 KB

重构候选

src/ 中最大的源文件,按严重程度排名。每个文件在单个文件中完成多个任务,损害了可读性、可测试性和合并冲突频率。

文件 行数 问题
config/schema.rs 7,647 整个系统的所有配置结构体都在一个文件中
onboard/wizard.rs 7,200 整个引导流程在一个类似函数的大块中
channels/mod.rs 6,591 渠道工厂 + 共享逻辑 + 所有接线
agent/loop_.rs 5,599 整个代理编排循环
channels/telegram.rs 4,606 单个渠道实现不应该这么大
providers/mod.rs 2,903 提供商工厂 + 共享转换逻辑
gateway/mod.rs 2,777 HTTP 服务器设置 + 中间件 + 路由

附加说明

  • tools/mod.rs(635 行)有一个 13 参数的 all_tools_with_runtime() 工厂函数,随着工具数量增长会变得更糟。考虑使用注册表/构建器模式。
  • security/policy.rs(2,338 行)混合了策略定义、操作跟踪和验证 —— 可以按关注点拆分。
  • providers/compatible.rs(2,892 行)和 providers/gemini.rs(2,142 行)作为单个提供商实现来说太大了 —— 可能混合了 HTTP 客户端逻辑、响应解析和工具转换。

放错位置的模块:channels/tts.rstools/

channels/tts.rs(642 行,在 PR #2994 中合并)是一个多提供商 TTS 合成系统。它不是一个渠道 —— 它没有实现 Channel 也没有提供双向消息接口。TTS 是代理调用以产生音频输出的能力,符合 Tool 特征(src/tools/traits.rs)。它应该被移动到 src/tools/tts.rs,并实现对应的 Tool,其配置类型从 schema.rschannels 部分提取到 [tools.tts] 配置命名空间。合并时,该模块没有集成到任何调用代码中(重新导出带有 #[allow(unused_imports)]),因此此移动对运行时没有影响。


最佳实践审计发现

来自通用 Rust/Python 最佳实践评审的发现(非项目特定约定)。

严重:生产代码中的 .unwrap()(约 2,800 处)

.unwrap() 出现在 I/O 路径、序列化和安全敏感模块中,超出了测试代码范围。示例:

// cost/tracker.rs
writeln!(file, "{}", serde_json::to_string(&old_record).unwrap()).unwrap();
file.sync_all().unwrap();

Rust 最佳实践:使用 .context("msg")? 或显式处理错误。每个 unwrap 都是瞬态失败时潜在的运行时 panic。

严重:生产路径中的 panic!(28+ 处)

提供商、配对和 CLI 路由使用 panic! 而非返回错误:

// providers/bedrock.rs
panic!("Expected ToolResult block");
// security/pairing.rs
panic!("Generated 10 pairs of codes and all were collisions — CSPRNG failure");

这些应该是 bail!() 或类型化错误变体 —— panic 是不可恢复的,会导致进程崩溃。

严重:全局 clippy 抑制(全局 32+ 个 lint)

main.rslib.rs 在 crate 级别抑制了 too_many_linessimilar_namesdead_codemissing_errors_doc 等许多 lint。这会隐藏新出现的违规。最佳实践:在函数级别抑制并附带理由注释,而非全局抑制。

高:静默错误吞吃(对 Result 使用 let _ = ...,30+ 处)

网关、WebSocket 和技能同步路径静默丢弃 Result 值:

let _ = state.event_tx.send(serde_json::json!({...})).await;
let _ = sender.send(Message::Text(err.to_string().into())).await;
let _ = mark_open_skills_synced(&repo_dir);

至少应该在失败时记录 tracing::warn!。静默丢弃使得分布式调试几乎不可能。

高:上帝结构体 —— 带有 30+ 字段的 Config

每个需要任何配置的子系统都必须持有整个 Config 结构体,造成隐式耦合和臃肿的测试设置。最佳实践:传递窄配置切片或特征绑定的配置对象。

高:安全代码未隔离

Shell 命令验证(300+ 行引号感知解析)、webhook 签名验证和配对逻辑嵌入在大型多用途文件中,而非隔离模块。这增加了安全审计的复杂性,并增加了无关变更导致回归的风险。

中:过多的 .clone()(约 1,227 处)

认证/令牌刷新路径在每个分支上克隆大型结构体。令牌访问等热点路径可以使用 Cow<'_>Arc 而非完整克隆。

中:测试深度 —— 大部分是冒烟测试

存在 193 个测试模块(良好的结构覆盖),但大多数是简单的值断言。缺失:

  • 解析器/验证器的基于属性的测试
  • 多模块流程的集成测试
  • Shell 命令解析器的模糊测试(安全表面)
  • 网络依赖路径的基于模拟的测试

中:依赖数量(82 个直接依赖)

项目声称以大小优化为目标(opt-level = "z"lto = "fat"),同时积累了重量级可选依赖,如 matrix-sdk(完整 E2EE 加密)和 probe-rs(50+ 个传递依赖)。大小目标和功能广度之间的矛盾尚未解决。

低:无安全注释的 unsafe

src/service/mod.rs 中有两处 libc::getuid()unsafe 使用 —— 没有 // SAFETY: 注释。可以使用 nix crate 的安全包装器替代。

低:Python 代码质量

python/ 子树的类型提示很少,关键函数没有 docstring,也没有参数化测试。与 Rust 侧的严谨性不一致。

低:极简的 rustfmt.toml

仅设置了 edition = "2021"。对于这种规模的项目,配置 max_widthimports_granularitygroup_imports 可以在贡献者数量增长时强制一致性。

已解决:CI/CD 安全加固(P1/P2)

第三方操作固定到可变标签;发布工作流被授予过宽的写入权限;分支保护没有复合门控作业;每个 PR 都从源代码编译安全工具。

已在 cicd-best-practices 分支修复:

  • 所有第三方操作都固定到 SHA(P1)
  • 发布工作流权限按作业范围限定(P1)
  • PR 检查中添加了复合 Gate 作业(P2)
  • 通过预构建二进制安装安全工具(P2)

优先级建议

  1. 将非测试代码中的 unwrap/panic 替换为 正确的错误传播 —— 对稳定性影响最大。
  2. 拆分上帝模块 —— 从 channels/mod.rs 中提取运行时编排,隔离安全解析,将 Config 拆分为子配置。
  3. 移除全局 clippy 抑制 —— 逐个修复违规或添加带理由的逐项目 #[allow]
  4. 将 Result 上的 let _ = 替换为 至少 tracing::warn! 日志。
  5. 为安全表面解析器添加基于属性/模糊测试(Shell 命令验证、webhook 签名)。

延后的结构重构

项目清理过程中延后的变更。每个条目包含理由和范围。

src/sop/ 重命名为 src/runbooks/

原因: "SOP" 术语过重,不能传达模块的作用。"Runbooks" 是带有审批门控的触发器驱动自动化流程的行业标准术语。

范围: 重命名模块(src/sop/src/runbooks/),更新配置键([sop][runbooks])、CLI 子命令(zeroclaw sopzeroclaw runbook)、所有内部类型(Sop*Runbook*)、文档(docs/sop/ → 匹配新结构)以及 CLAUDE.md 中的引用。

将国际化文档整合到 docs/i18n/<语言区域>/

原因: 越南语翻译目前存在于三个位置:docs/i18n/vi/(根据 CLAUDE.md 规范)、docs/vi/(有 17 个文件分歧的过时副本)和 docs/*.vi.md(5 个分散的后缀文件)。其他语言区域(zh-CN、ja、ru、fr)的 SUMMARY + README 文件分散在 docs/ 根目录。

计划:

  • 保留 docs/i18n/vi/ 作为规范版本;删除 docs/vi/(过时副本)
  • docs/*.vi.md 文件移动到 docs/i18n/vi/ 下的对应路径
  • docs/SUMMARY.*.mddocs/README.*.md 移动到 docs/i18n/<语言区域>/
  • 创建 docs/i18n/{zh-CN,ja,ru,fr}/ 目录,包含其 README + SUMMARY
  • 根目录 README.*.md 文件保留(GitHub 约定)
  • 英文文档重构完成后,更新 docs/i18n/vi/ 内部结构以匹配新的英文文档布局

TODO:模糊测试 —— 将存根升级为真实覆盖

当前状态: fuzz/fuzz_targets/ 中存在 5 个模糊测试目标,但只有 fuzz_command_validation 测试真实的 ZeroClaw 代码。其他 4 个(fuzz_config_parsefuzz_tool_paramsfuzz_webhook_payloadfuzz_provider_response)仅模糊测试 serde_json::from_str::<Value>toml::from_str::<Value> —— 它们测试第三方 crate 内部,而非 ZeroClaw 逻辑。

将现有存根连接到真实代码路径:

  • fuzz_config_parse:反序列化为 Config,而非 toml::Value
  • fuzz_tool_params:通过实际的 Tool::execute 输入验证
  • fuzz_webhook_payload:通过 webhook 签名验证 + 正文解析
  • fuzz_provider_response:解析为实际的提供商响应类型(Anthropic、OpenAI 等)

为安全表面添加缺失的目标:

  • Shell 命令解析器(引号感知解析,不只是 validate_command_execution
  • 凭证清理(scrub_credentials —— 在 #3024 中已经出现过 UTF-8 边界 panic)
  • 配对代码生成/验证
  • 域名匹配器
  • 提示防护评分
  • 泄露检测器正则表达式

基础设施改进:

  • 添加种子语料库(fuzz/corpus/<目标>/),包含已知良好和边界情况输入;提交到仓库
  • 考虑使用 Arbitrary 派生进行结构化模糊测试,而非原始 &[u8]
  • 设置计划 CI 模糊测试(每日/每周)—— OSS-Fuzz 对开源项目免费
  • 使用 cargo fuzz coverage <目标> 从语料库运行生成 lcov 报告,跟踪模糊测试实际覆盖的代码路径
  • 将崩溃工件(fuzz/artifacts/<目标>/)作为 Issue 跟踪

TODO:e2e-testing 分支的测试基础设施跟进

测试重构工作质量评审期间发现的问题。

1. 运行器文件中的 #[path] 属性模式(已解决)

运行器文件使用 #[path] 属性作为 E0761 的变通方案。 已修复:运行器文件重命名为 test_component.rs 等,目录使用标准 mod.rs 文件。Cargo.toml[[test]] 条目已更新以匹配。cargo test --test component 命令不变。

2. 死基础设施:TestChannelTraceLlmProvider、追踪夹具、verify_expects()

这些是作为脚手架构建的,但没有使用者:

  • tests/support/mock_channel.rsTestChannel)—— 计划用于渠道驱动的系统测试,但代理没有公共的渠道驱动循环 API,因此系统测试直接使用 agent.turn()
  • tests/support/mock_provider.rsTraceLlmProvider)—— 重放 JSON 夹具追踪,但没有测试加载或运行夹具。
  • tests/fixtures/traces/*.json(3 个文件)—— 从未被任何测试加载。
  • tests/support/assertions.rsverify_expects())—— 从未被调用。

要么编写使用这些基础设施的测试,要么移除它们以避免死代码混淆。

3. 网关组件测试与现有 whatsapp_webhook_security.rs 重叠

tests/component/gateway.rs 中有 6 个针对 verify_whatsapp_signature() 的 HMAC 签名验证测试 —— 与 tests/component/whatsapp_webhook_security.rs 中的 8 个测试测试同一个函数。只有 3 个网关常量测试(MAX_BODY_SIZEREQUEST_TIMEOUT_SECSRATE_LIMIT_WINDOW_SECS)提供了真正的新覆盖。考虑将签名测试合并到一个文件中,或从 gateway.rs 中删除重复项。

4. 安全组件测试仅配置 —— 没有行为覆盖

10 个安全测试仅验证配置默认值和 TOML 序列化(AutonomyConfig::default()SecretsConfig、往返)。它们不测试安全行为(策略执行、凭证清理、操作速率限制),因为 src/security/pub(crate) 的。security_config_debug_does_not_leak_api_key 测试是无操作的 —— 它检查泄露,但失败时没有断言(只有注释)。要获得真实的行为覆盖,可以:

  • 让目标安全函数变为 pub 以供测试(例如 scrub_credentialsSecurityPolicy::evaluate
  • src/security/ 中添加 #[cfg(test)] pub 逃生口
  • 改为在 src/security/tests.rs 中编写 crate 内单元测试

5. pub(crate) 可见性阻止了关键子系统的集成测试

securitygateway 模块使用 pub(crate) 可见性,阻止集成测试执行核心逻辑,如 SecurityPolicyGatewayRateLimiterIdempotencyStore。这迫使新的组件测试只能通过狭窄的公共 API 表面(配置结构体、一个签名函数、常量)进行测试。考虑关键安全类型是否应该暴露仅用于测试的公共接口,或者这些测试是否应该作为 crate 内单元测试。

TODO:自动发布公告 —— Twitter/X 集成

当前状态: 发布仅在 GitHub 上发布。没有自动交叉发布到社交渠道。

计划:

  • 添加 .github/workflows/release-tweet.yml,在 release: [published] 时触发
  • 使用 nearform-actions/github-action-notify-twitter(OAuth 1.0a、v1.1 API)或带 OAuth 签名的直接 X API v2 curl
  • 推文模板:发布标签、单行摘要、GitHub 发布链接
  • 跳过预发布(if: "!github.event.release.prerelease"

所需密钥(设置 > 密钥 > Actions):

  • TWITTER_API_KEYTWITTER_API_KEY_SECRET
  • TWITTER_ACCESS_TOKENTWITTER_ACCESS_TOKEN_SECRET

注意事项:

  • 对照 docs/contributing/actions-source-policy.md 审核 —— 将第三方操作固定到提交 SHA 或 vendor
  • X 免费层级:每月 1,500 条推文(足够发布使用)
  • 如果在推文中包含亮点,将发布正文截断为 280 字符