ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

项目规划与技术选型:把“清理“拆成可验证的工程问题

项目规划与技术选型:把“清理“拆成可验证的工程问题

项目规划与技术选型:把"清理"拆成可验证的工程问题

背景:清理软件最容易错在哪里

清理类工具的核心风险不在"扫不到文件",而在"删错文件"。一旦把用户文档、数据库或正在运行的应用数据当成垃圾,后果不可逆。因此 Pure 的规划不是先列功能清单,而是先定不可突破的原则。

规划文档macOS系统垃圾清理软件完整功能规划-v1.1.md把原则写得很直白:

  • 安全优先:任何文件进入"可清理"列表前都必须经过规则判断;
  • 不以"垃圾总量"制造焦虑:拆成"安全清理 / 建议检查 / 不建议删除";
  • 每一项都要解释原因;
  • 默认可恢复,默认移入废纸篓。

这些原则在代码里大多有对应实现:SafetyEngine的 Allowlist/Denylist、reasonKey的语义化文案、CleanPolicy默认.moveToTrash,以及清理确认页对不同风险等级的差异化处理。

需求拆解:从产品语言到模块语言

规划文档按功能域展开,实际代码按职责分层。对应关系大致如下:

规划中的需求域实际模块关键约束
智能扫描ScanEngine+Scanner/*并发编排、可取消、进度节流
垃圾清理Core/CleanEngine+Cleaner/*清理前再校验、默认废纸篓
应用管理/卸载ApplicationRegistry+UninstallEngine系统应用保护、运行中保护、白名单
开发者清理developer.json+ 各 Analyzer规则驱动、按工具分组
存储与磁盘健康Core/Storage+Core/System以只读分析为主,APFS 快照删除为受控例外
历史与忽略DatabaseManagerSQLite、幂等迁移
设置与生命周期SettingsStore+AppDelegate单一读写入口、即时生效

拆解的重点不是"分了多少文件",而是主要清理路径都有明确的输入、输出和复核点。例如扫描器的输出只是JunkItem,它不是删除指令;文件清理必须经过Cleaner.validate,卸载与快照删除则走各自的白名单或前置校验。

MVP 切片:先打通管道,再铺功能面

架构规范给出的是 V0.1–V0.4 的 MVP 建议:V0.1 核心管道与基础清理;V0.2 浏览器、应用扫描、忽略与 FDA;V0.3 残留、卸载、大文件与存储树;V0.4 开发者工具。功能进度清单则记录了另一条更细的 V0.0.1–V0.2.0 路线:

V0.0.1 核心管道:扫描 / 安全 / 清理 + 测试 V0.0.2 权限体系与 Full Disk Access 引导 V0.0.3 应用识别 / 浏览器缓存 / 历史 / 忽略列表 V0.0.4 卸载 / 残留 / 大文件 / 文件夹分析 V0.1.0 开发者清理(Xcode、Homebrew、Maven…) V0.1.1 IDE 与运行时(JetBrains、VS Code、Cursor) V0.2.0 存储趋势 / 最近增长 / 磁盘健康(启动项管理未完成)

这段路线来自功能进度清单.md,属于文档记录,不是本次逐项验收的结果。按该文档,V0.2.0 的存储趋势、最近增长、空间可视化与磁盘健康已完成,启动项管理仍未开始;文档的"下一步待办"也写明 V0.2.0 仅剩此项。不过工程配置里的版本号仍是 0.1.1 (6),与路线图标签 V0.2.0 不一致。路线图标签与工程版本号不是同一套编号,不能互相推断完成状态。

MVP 是否达标,也不应只看页面是否存在。更可靠的判断是核心链路的各环节是否有测试覆盖;CleanEngineTests、SafetyEngineTests、TrashCleanerTests、TrashIntegrationTests等分别覆盖策略、校验、清理与废纸篓流程,但它们不等于一次端到端的完整验收。

技术选型:每个选择都对应一个约束

决策选择代价 / 约束
UI 框架SwiftUI 为主,AppKit 补齐菜单栏、Finder 集成、运行应用状态仍要 AppKit
并发Swift 6 严格并发 + 少量锁部分类型必须@unchecked Sendable,正确性靠人工审查
数据库直接 SQLite C API无 ORM,SQL 与迁移需手写;但可控、依赖少
规则JSON 文件 + 载入期护栏规则可配置,但错误配置可能扩大清理范围
沙盒不启用 App Sandbox直接读取用户目录与其他应用数据;仍需服从文件权限、TCC/FDA 与系统保护
签名与分发工程配置自动签名 + Hardened Runtime;实际产物为 ad-hoc无 Developer ID、未公证,spctl拒绝;不能视为可分发构建
工程XcodeGen新增目录后必须重新生成工程

其中不启用沙盒需要更准确地表述。清理工具需要读取~/Library/Caches、~/Library/Logs与其他应用的容器;在 App Sandbox 下,如果没有用户授权或 security-scoped bookmark,这些目录通常无法直接访问。Pure 选择不启用 App Sandbox,以获得直接读取能力,但这不等于无限制访问:进程仍受 Unix 文件权限、TCC(包括 Full Disk Access)、SIP 与系统保护目录的约束。因此系统沙盒不再作为主要边界,应用内仍需自建防护。

规则、白名单、删除前复核、废纸篓策略分别解决不同问题:规则决定"哪些路径允许清理",白名单决定"本次操作只碰哪些路径",删除前复核处理"扫描后是否变化或被替换",废纸篓提供恢复路径。它们降低误删与 TOCTOU 风险,但不能替代沙盒,也不能消除所有竞争条件。

签名同样需要区分配置与实际产物:工程配置为CODE_SIGN_STYLE=Automatic、ENABLE_HARDENED_RUNTIME=YES、DEVELOPMENT_TEAM为空;本次 Release 产物实际是 ad-hoc 签名(无 Team ID),签名带 runtime 标志,但包含get-task-allow,spctl判定为 rejected。Hardened Runtime 是运行时保护开关,Developer ID 是分发身份,公证是 Apple 的扫描与票据流程;当前产物未使用 Developer ID,也未公证,正式分发链路尚未建立,不能把"自动签名 + Hardened Runtime"理解为已经可发布。

AI 辅助规划的边界:文档能约束什么

可以确认的是:仓库中存在功能规划、架构规范与 UI 规范,代码里保留了规划 §xx等注释,说明实现曾以这些条款为参照。无法确认的是:这些文档由谁撰写、AI 在规划阶段具体参与了哪些环节;仓库没有对话记录,不能还原过程。因此这里讨论文档与代码的关系,而不是 AI 的贡献过程。

从工程效果看,规格的价值在于把口头需求变成可检查的条款:安全等级、目录结构、UI 状态、错误分类都写到具体行为,偏离时能指出具体条款,而不是凭感觉争论。但文档不会自动约束代码,真正起作用的是实现者是否把条款落到类型、默认值和测试里。

文档也会漂移:进度清单 v1.8 写本地化键 617,当前解析 623(620 条双语);规范设想的AppDependencies、ScanCoordinator、UseCase 层在代码中不存在。这说明文档需要和代码一起维护。

规划留下的三个缺口

  1. 有 Git 建议但没有落地:架构规范写了.gitignore、分支与 commit message 建议,但当前目录不是 Git 仓库,git log返回 fatal;没有历史,无法精确复盘,也无法安全回滚。
  2. 交付链路没有进入 MVP:Developer ID 签名、公证、DMG 与安装验证都没有完成记录,导致"能构建"与"能交付"之间存在明显距离。
  3. 测试计划缺少稳定性约束:388 项测试本次全量 387 通过、1 项性能用例被终止;隔离重跑该套件 2 项通过,但根因未确认。规划没有定义"全量必须稳定通过"的验收口径。

小结

Pure 的规划做对了一件事:先把"不能删什么"写清楚,再讨论"能删什么"。技术选型也围绕这个原则展开:不启用沙盒换来直接读取能力;规则、白名单、删除前复核与默认废纸篓降低风险,但不能替代沙盒,也不能消除所有竞争条件。

下一篇文章会讨论这些规划如何落到具体代码结构,以及哪些约束在实现中保留、哪些只停留在文档里。

  • 上一篇:项目总览
  • 下一篇:Vibe Coding 与工程架构
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进