Vibe Coding 与工程架构AI 写的代码如何被约束第 3 篇。系列导航见 README。背景Vibe Coding 最容易失控的地方AI 编程工具擅长把功能写出来但它不知道你的产品底线。对清理工具来说底线是不能删系统文件、不能删用户文档、不能在用户没确认时删除中高风险数据。这些约束必须在架构层面显式表达否则生成代码越多风险面越大。Pure 在文件清理主链路上的做法是把约束写进类型和调用链扫描器返回JunkItemCleaner 负责校验与删除安全引擎独立于 UI规则在载入期被过滤。需要先说明仓库没有保留生成对话无法逐文件判定 AI 与人工的贡献下面讨论的是代码中实际存在的约束、调用链和审查痕迹。实际架构比规范更轻但边界清楚架构规范设想了Coordinator、UseCase、AppDependencies等层次实际代码没有采用。真实结构是层代表类型职责不应该做什么AppPureApp、AppDelegate、MenuBarController生命周期、菜单栏、开机启动不处理清理逻辑UIScanViewModel、CleanView、DiskHealthViewModel状态编排与展示不直接删除文件EngineScanEngine、CleanEngine、UninstallEngine扫描与清理流程编排、结果聚合新清理入口负责应用风险策略不实现具体扫描规则ScannerCacheScanner、BrowserScanner、GroupContainerScanner发现候选条目不执行删除CleanerTrashCleaner、UninstallCleaner、GroupContainerCleaner执行校验与删除不负责选择条目安全门槛由具体 Cleaner 实现CoreSafetyEngine、RuleRepository、DatabaseManager、FileSystemKit安全、规则、持久化、文件系统工具多数不依赖 UIL10n依赖 SwiftUIPermissionManager依赖 AppKit两个协议把边界固定下来protocolScanner:Sendable{varcategory:JunkCategory{get}funcscan(progress:escapingSendable(Int,Int64)-Void)asyncthrows-[JunkItem]}protocolCleaner{funcvalidate(_item:JunkItem)throwsfuncclean(_item:JunkItem)throws-URL?}Scanner协议要求category属性与scan返回[JunkItem]实现会读取文件系统、汇报进度、写入扫描缓存但不会删除文件。Cleaner协议只有validate与clean两个要求没有其他默认实现三种 Cleaner 的校验内容并不相同Cleanervalidate的实际检查clean的行为TrashCleaner存在性新建 URL 读取最新大小/修改时间拒绝符号链接大小与时间复核重新执行SafetyEngine解析完整路径后复核危险区域默认移入废纸篓永久删除需通过guardPermanentDelete的安全等级限制仅safe/lowRisk并再次检查符号链接但不会完整重跑元信息校验UninstallCleaner路径必须精确命中本次allowedPaths存在性SafetyLevel ! dangerous最新元信息符号链接大小/时间解析后仍在allowedBases内移入废纸篓GroupContainerCleaner精确命中allowedPaths存在性runningApplicationBundleIDs()命中bundleID则拒绝最新元信息符号链接大小/时间解析后仍在allowedBases内移入废纸篓CleanEngine对真正进入执行队列的条目先调用validate通过后再调用clean。这一步不是装饰但它也不是等价于沙盒的安全保证校验与删除是两次独立调用细节见后文。模块依赖与调用顺序一次典型的扫描与清理流程DatabaseManagerCleanerCleanEngineCleanupAnalyzerScannersScanEngineScanViewModelCleanViewDatabaseManagerCleanerCleanEngineCleanupAnalyzerScannersScanEngineScanViewModelCleanViewalt[校验通过][校验失败]startScan()quickScan(applicationContext:)并发 scan()[JunkItem]ScanResultanalyze(items, context)[CleanupItem]clean(selected, confirmedReview:)cleanDetailed(items, confirmedReview:)validate(item)clean(item)成功 / 抛错CleanErrorrecord(clean_operation)这张图只覆盖CleanView的主链路。CleanEngine 会按条目来源选择具体 Cleaner新的CleanupItem入口中Group Container / Application Container 走GroupContainerCleaner其余走默认/注入的通用 Cleaner生产路径是TrashCleaner旧接口则由调用方传入 Cleaner。大文件、开发者缓存、遗留数据、卸载后剩余数据等入口走旧接口不经过CleanupAnalyzer与confirmedReview详见后文。CleanupAnalyzer确实在扫描结束后运行ScanViewModel.startScan()拿到ScanResult后调用CleanupAnalyzer.analyze(items:context:)把JunkItem转成带source、role、ownership、applicationState、recoverability、risk、score、confidence、reasons、recommendedAction、safetyOverride的CleanupItem。扫描器不计算CleanupRiskGroup Container 扫描器会按容器内部角色产出条目但统一的风险评分仍由后续分析完成。清理历史也不是在 UI 层写入。CleanEngine.process对每个真正尝试过的条目在validate/clean之后写一条clean_operation成功、跳过、失败都会写被风险策略直接拦下、没有进入执行队列的条目不会写历史。卸载流程还会额外写uninstall_operation。当前代码中的两套安全模型当前代码里同时存在两套模型。仓库没有 Git 历史无法证明它们是严格的替代关系这里只描述当前实现中的分工不编造演进时间线。模型取值使用位置SafetyLevelsafe/lowRisk/caution/dangerousJunkItem、SafetyEngine、TrashCleaner、UninstallCleanerCleanupRisksafe/low/medium/high/prohibited另有 0–100 评分CleanupItem、CleanupAnalyzer、CleanupRiskScorer、CleanView、CleanEngine的新入口SafetyLevel主要回答路径是否命中规则或黑名单CleanupRisk在此基础上综合应用生命周期、所有权、数据角色、来源、可恢复性、置信度和其他应用引用。评分从 50 起算按维度加减并映射为90–100safe、75–89low、50–74medium、20–49high、0–19prohibited。硬规则由CleanupRiskScorer.safetyOverride计算输入是路径、来源、角色、所有权与应用状态。关键规则如下switchoverride{case.prohibited:risk.prohibited scoremin(score,10)case.high:riskCleanupRisk.max(riskLevel(forScore:score),.high)case.medium:riskCleanupRisk.max(riskLevel(forScore:score),.medium)case.none:riskriskLevel(forScore:score)}Keychain、iCloud、用户文档、核心系统数据、系统库直接prohibitedApple Group Container、运行中应用数据至少highGroup Container 的非可重建角色至少medium。即使评分为 95也不能推翻这些硬规则——但不能被评分推翻不等于删除一定安全它仍然只是策略判断。confirmedReview的实际含义也需要限定新入口中prohibited一律跳过medium/high只有confirmedReview true才执行safe/low直接执行。CleanView在选中项包含中高风险或设置要求确认时弹出确认面板用户确认后传入true。它不是一个全局允许删除开关也不会让prohibited放行。被风险策略拦下的条目不进入validate直接计为 skipped只有真正进入执行队列的条目才走validate → clean。此外confirmedReview只是一个布尔参数API 本身不验证用户是否真的看过确认面板语义仍依赖调用方。两套模型负责不同阶段的判断CleanupRisk决定条目是否允许进入清理队列以及是否需要用户确认SafetyLevel和 Cleaner 的路径、白名单、元信息检查则约束实际执行的清理操作。前者不是后者的替代品后者也不会重新计算CleanupRisk分数。CleanupRisk.max按sortRank取较高等级所以.high/.medium覆盖是最低风险等级的约束不是重新评分。需要注意validate与clean之间仍存在 TOCTOU 竞争窗口因此这些检查只能降低误删风险不能证明文件在整个操作期间始终满足安全条件。旧接口的兼容性不是等价安全保证CleanEngine目前保留了新旧两套入口且都在生产代码中使用入口主要生产调用方是否经过风险模型与确认Cleaner 选择clean([CleanupItem], confirmedReview:)ScanViewModel.cleanCleanView调用是风险过滤 中高风险确认Group Container 专用 Cleaner其余默认/注入的通用 Cleaner生产路径为TrashCleanerclean([JunkItem])/cleanDetailed([JunkItem])ScanViewModel.trashLeftover、trashLargeFile、trashDeveloperItem、ApplicationDetailView.trashRemaining、UninstallEngine.uninstall先 preflight否不做CleanupAnalyzer、风险过滤或confirmedReview由调用方传入的白名单 Cleaner默认是TrashCleaner两条路径最终都进入CleanEngine.process。对于进入执行队列的条目流程都会先调用validate只有校验通过后才调用clean两条路径使用相同的错误分类和历史记录机制missing/changed/unsafe计为 skippedpermissionDenied/inUse/failed计为 failed其他错误计为 failed。风险策略拦下的条目不会进入逐项执行也不写clean_operation旧接口没有这层风险拦截进入队列的条目都会尝试校验。其中UninstallEngine并不是裸调用旧接口它在删除前先执行UninstallPreflight检查废纸篓、系统应用、存在性、符号链接、Bundle ID、版本变化、运行状态与权限再构造UninstallCleaner的白名单。但 preflight 也不是CleanupRisk模型二者不能互相替代。差异在进入队列之前旧接口不会把CleanupItem的多维风险与confirmedReview应用到条目上。TrashCleaner仍会拒绝SafetyLevel.dangerous但允许cautionUninstallCleaner与GroupContainerCleaner依靠本次白名单与基目录限制范围。旧接口调用分别服务于大文件、开发者缓存、遗留数据和卸载后剩余数据等操作操作范围由allowedPaths与allowedBases限定但不同入口的确认方式并不一致卸载流程有选择/预览 Sheet开发者条目有确认弹窗而大文件、遗留数据、卸载后剩余数据等入口使用直接操作按钮。操作范围明确并不意味着这些入口具有与主清理页相同的风险评估和确认保证。测试中也同时存在两种调用CleanEngineTests、CleanHistoryTests、TrashIntegrationTests使用旧接口覆盖校验、历史与废纸篓流程CleanEngineRiskPolicyTests则覆盖新接口的风险策略与确认参数。因此旧接口是兼容 API不是与主清理页等价的安全保证。仅凭静态阅读没有发现旧入口能绕过Cleaner.validate或删除白名单之外的任意路径但显式单条操作即视为确认是否足够属于产品策略问题。也就是说目前不能判定它是已证实的安全缺陷更准确的分类是确认语义不一致。若未来要求统一应把这些入口迁移到CleanupItemAPI或在 ViewModel 层补充确认而不是假设旧接口继承了新模型的保证。需要人工兜底的几类问题从代码与注释可以观察到几类需要人工兜底的地方。先看unchecked Sendable的实际共享状态类型共享可变状态线程安全依靠什么ScanEngine注入的扫描器/存储引用不可变ProgressCounter的计数ProgressCounter使用NSLock引擎本身无可变共享状态DirectorySizeCacheentries字典NSLock覆盖读写与身份校验ScanCacheSession每次扫描的缓存、统计、待写入集合NSLockcommit()先快照再落库DatabaseManager/FileEntryStoreSQLite 连接句柄NSLockSQLITE_OPEN_FULLMUTEXFileEntryStore使用独立连接SettingsStoresettings与业务快照缓存UI 绑定驱动settings写入snapshot()通过NSLock读取unchecked Sendable的共性是编译器不再证明线程安全正确性依赖锁的范围和调用时序。锁覆盖得好不代表所有并发路径都被测试覆盖这部分仍需要人工审查。其余标注如RuleRepository、CleanEngine、UninstallEngine、SnapshotService、系统 Provider大多共享状态较少本文不逐一展开。资源生命周期也需要人工兜底。SQLite 连接、测试用临时数据库、FileEntryStore的独立连接都有显式close()DatabaseManager.close()的注释明确提到避免 vnode 占用警告说明这是实际踩过的坑。ScanCacheSession.commit()失败时会记录日志而不是中断清理历史写入失败也不会回滚已执行的删除。安全校验的顺序同样值得单独看TrashCleaner.validate依次检查存在性、最新元信息、符号链接、大小与修改时间、SafetyEngine安全等级最后解析完整路径复核危险区域。但validate与clean是两次独立调用之间仍有竞争窗口trashItem/removeItem按路径操作guardPermanentDelete只复核安全等级与符号链接不会重新执行完整元信息校验。这些检查降低误删风险但不能消除 TOCTOU。Coordinator/UseCase没有落地实际是单例 ViewModel是否需要补回应结合多入口的重复流程与编排测试需求判断而不是类型名称本身。代码中的审查 P1-1、审查 P2-7、审查 P3-9等标记表明项目至少经历过按优先级编号的审查或修订涉及规则护栏、错误分类、隐藏文件统计等问题。但仓库没有对应的审查记录无法还原完整讨论也不能据此推断每处修改由谁完成。可维护性的真实问题架构边界清楚不等于代码没有债务。按行数统计几个明显偏大的文件是文件行数问题UI/Clean/CleanView.swift901列表、分组、应用视图、确认页、结果页集中在一个文件Core/Common/Localization/L10n.swift744大量计算属性键与 UI 强绑定UI/Applications/ApplicationDetailView.swift657详情、卸载 Sheet、存储分解混在一起Core/Database/DatabaseManager.swift562多张表与多组业务查询共用一把锁单例ScanEngine.shared、CleanEngine.shared、DatabaseManager.shared等降低了装配成本但也让测试必须通过构造函数注入替身项目确实这样做了ScanEngine(scanners:)、CleanEngine(cleaner:history:)算是可测试性上的补救。为什么没有落地规划中的分层架构规范提出了 Coordinator 与 UseCase本意是把扫描与清理流程从 UI 中抽离。实际实现选择了 Engine ViewModelScanViewModel.startScan()直接调用ScanEngine.quickScan清理也直接调用CleanEngine。这个选择的代价是 ViewModel 承担了部分流程编排职责好处是调用链短、调试路径清晰。是否引入 Coordinator不应只看调用方数量而应看多个入口是否存在重复的流程编排、是否需要共享业务流程以及是否需要独立测试编排逻辑。当前项目已有多个清理入口但它们承担的业务职责和安全策略并不完全相同是否抽取统一的编排层需要结合重复逻辑与实际维护成本判断而不是为了让架构图更整齐而增加一层类型。小结从仓库产物看真正起约束作用的是主清理链路Scanner/Cleaner 分离、SafetyEngine独立、风险模型在扫描后统一分析。但这些约束不是完整的安全保证旧JunkItem接口不经过CleanupAnalyzer与confirmedReview删除阶段与validate之间仍有 TOCTOU 窗口unchecked Sendable的并发正确性依赖锁与人工审查。计划中的分层没有落地、部分文件过大也是明确的维护成本。下一篇进入具体功能讨论磁盘扫描、文件分类、清理安全和应用卸载这四条链路。上一篇项目规划与技术选型下一篇核心功能实现与文件系统安全
企业数字化 ERP 产品动态
相关推荐
LoopForge-Loop Engineering驱动AICoding在屎山上跑起来 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/8 5:04:14
C++语言规范定义 C整个语言体系简单可分成规范定义、实现与工具链以及生态系统这三个层次来理解。规范和定义负责将把需要实现的功能转换成C编程语言代码,并规定文本编写以及语义等内容。实现与工具链负责把C编程代码通过编译器编译成汇编语言,最终转换成计算机语言也就是… · 2026/11/6 3:42:21
平谷在哪里办理电工证网上查询流程全攻略 平谷在哪里办理电工证网上查询流程全攻略 证书过期了不知道怎么复审延期?你不是一个人。很多人拿到特种作业证后,以为一劳永逸,结果一不留神证书就过期了,想复审却发现不知道流程,甚至找不到正确的办理机构。尤其是平谷地区的电工证办理,很多人不知道该… · 2026/11/8 5:03:26
河南洛阳电工证看证过期怎么办?别让含金量变成废纸 河南洛阳电工证看证过期怎么办?别让含金量变成废纸 证书过期了不知道怎么复审延期?别慌,今天咱们就从河南洛阳电工证看证的真实案例说起,带你一步步搞懂怎么操作,别让手中的“含金量”变成一张废纸。 案例背景:焦作建筑工人王师傅的电工证风波… · 2026/11/8 5:03:26
宽城县电工证丢了怎么补办多少钱 宽城县电工证丢了怎么补办多少钱 证书过期了不知道怎么复审延期,现在补办流程复杂,费用也不透明,很多电工朋友都遇到过这类问题,尤其是刚从宽城县出来打工的电工,如果证书丢了或者过期,找工作都成了难题。今天就围绕【宽城县电工证】的办理、补办、复审… · 2026/11/8 5:03:26
工地太忙根本没时间复习考试?曲阳考证电工证多少钱全攻略 工地太忙根本没时间复习考试?曲阳考证电工证多少钱全攻略 工地太忙根本没时间复习考试?你不是一个人。每天穿梭在钢筋水泥之间,手上沾满灰土,脑子里全是工程进度和安全交底,电工证考试的事儿一拖再拖,结果发现: 曲阳考证电工证多少钱… · 2026/11/8 5:03:26
广州深圳电工证年审多少钱,考不过真的白交钱吗? 广州深圳电工证年审多少钱,考不过真的白交钱吗? 怕考不过白交培训费,你不是一个人。去年广州深圳一带有超过3000人报考低压电工证,其中近40%因为没通过考试而投诉培训机构,理由无一例外: 钱花了,证没拿到… · 2026/11/8 5:03:26
电工证跨部门互认怎么报名?劳务班组长避坑指南来了 电工证跨部门互认怎么报名?劳务班组长避坑指南来了 你是不是也遇到过这种情况,手里有个电工证,但去别的省市干活儿时发现不被认可?或者明明有证,到了新单位却被告知要重新考证?这事儿不是个例,很多劳务班组长都碰过。今天我就以新乡市某劳务班组负责人… · 2026/11/8 5:02:26
电工证丢失还能补吗?千万别踩坑,丢了证等于失业 电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。… · 2026/11/8 0:00:20
电力电缆作业证直播报名入口与办理流程良心建议 电力电缆作业证直播报名入口与办理流程良心建议 你是不是也担心学历不高报不了名?别急,今天就给你讲讲 电力电缆作业证直播 的报名流程和那些你必须知道的干货, 良心建议 在这里,别错过。 电力电缆作业证直播报名需要什么条件?… · 2026/11/8 0:00:20
英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 怕考不过白交培训费?英德电工证报考费多少钱,你真的了解吗?现在市面上培训机构五花八门,价格参差不齐,很多人一不小心就花了冤枉钱,结果还考不过。别急,今天就来给你说清楚英德电工证的… · 2026/11/8 0:00:20
电工证丢失还能补吗?千万别踩坑,丢了证等于失业 电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。… · 2026/11/8 0:00:20
电力电缆作业证直播报名入口与办理流程良心建议 电力电缆作业证直播报名入口与办理流程良心建议 你是不是也担心学历不高报不了名?别急,今天就给你讲讲 电力电缆作业证直播 的报名流程和那些你必须知道的干货, 良心建议 在这里,别错过。 电力电缆作业证直播报名需要什么条件?… · 2026/11/8 0:00:20
英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 怕考不过白交培训费?英德电工证报考费多少钱,你真的了解吗?现在市面上培训机构五花八门,价格参差不齐,很多人一不小心就花了冤枉钱,结果还考不过。别急,今天就来给你说清楚英德电工证的… · 2026/11/8 0:00:20
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/7 11:25:08
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/7 12:02:18
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/8 1:18:25