常用工具、保护清单与事务#
先用现成的小工具#
这些工具使用 Python 3.10+ 标准库,面向 POSIX/macOS。它们只读生产来源,输出写入私有目录;不判断旧账号归属、不退出应用、不清登录,也不发布或回滚生产文件。先核验本机解释器版本,将 Python 3.10+ 的实际路径设为 CLEANUP_PYTHON,不能假定命令 python3 已满足版本要求。以下 SKILL_DIR、PRIVATE_DIR、源路径及 SHA 均由本轮实际发现,不能照搬历史值。私有工作目录必须在应用管理根之外,使用无软链接祖先的真实绝对路径。
| 工具 | 作用及边界 |
|---|---|
| json_candidate.py | 默认只预览;按精确对象键路径生成删除候选,显式输出须绑定来源 SHA。保留剩余 token、数值拼写、顺序及删除区间外的原字节;不做整文件重新序列化。 |
| sqlite_snapshot.py | 复制 DB 和对应 WAL,检查源成员集合及前后身份/哈希;仅在私有副本运行 SQLite quick_check,不复制源 SHM。是可检查副本,不是原子快照或逻辑账号验证。 |
| file_guard.py | 为精确保护目录生成/比较只读清单;不复制来源、不解析数据库,也不替代内容及引用验证。用法见下节。 |
精确 JSON 候选#
私有 targets.json 是一个 0600 的 JSON 文件,父目录由当前用户所有且权限为 0700。内容是精确对象键路径,例如 [["account", "oldToken"], ["oldPreference"]];这些只是语法示例,不是 Claude 的固定字段或删除清单。先核实字段、旧身份归属与本轮授权。
# 预览不写来源或候选,只输出状态、目标数量及前后 SHA。
"$CLEANUP_PYTHON" -B "$SKILL_DIR/scripts/json_candidate.py" \
--source "$CONFIG_FILE" --targets "$PRIVATE_DIR/targets.json"
# SOURCE_SHA256 取自刚才的预览;candidate.json 必须不存在。
"$CLEANUP_PYTHON" -B "$SKILL_DIR/scripts/json_candidate.py" \
--source "$CONFIG_FILE" --targets "$PRIVATE_DIR/targets.json" \
--expected-sha256 "$SOURCE_SHA256" --candidate "$PRIVATE_DIR/candidate.json"
只支持严格 UTF-8 JSON 中的对象成员删除,不支持 JSONC、根替换、通配符、数组元素或穿过数组寻址。重复键、缺失目标、重复或祖先/后代重叠路径、非有限数值及来源变化均拒绝;限制为 8 MiB、64 层、100,000 节点、256 个目标。候选不能覆盖已有文件或写在来源的同一父目录;完整应用根仍由代理核对。工具核验剩余结构/token并回读候选,但它不知道某字段是否属于旧账号。
成功退出 0,拒绝/错误退出 2;stdout 无原值,stderr 只给固定错误码。SOURCE_HASH_MISMATCH / SOURCE_CHANGED 要重新核对来源;TARGET_MISSING / DUPLICATE_KEY / OVERLAPPING_TARGETS 要修正目标或改用匹配格式的方法,不能放宽为全局删键。CLEANUP_INCOMPLETE 表示失败候选仍需核对处理,不能当成功交付。
SQLite 私有检查副本#
# PRIVATE_DIR 已是当前用户所有的 0700 目录;db-inspection 必须不存在。
"$CLEANUP_PYTHON" -B "$SKILL_DIR/scripts/sqlite_snapshot.py" \
--source "$DATABASE_FILE" --out-dir "$PRIVATE_DIR/db-inspection"
输出为 0700 目录中的 source.sqlite、存在时的 source.sqlite-wal、副本引擎可能生成的 SHM,以及 0600 report.json。需要绑定预先已确认的 DB 字节时可加 --expected-sha256;该 SHA 只绑定 DB,WAL 仍由本次实际成员及哈希检查覆盖。只处理 SQLite 引擎能读取的格式,不提供解密;总源成员上限 256 MiB,副本检查设有 15 秒进度中止条件。
源 DB/WAL/SHM/journal 按实际存在性核验,不把无 SHM 当无 WAL。非空 rollback journal 报 ROLLBACK_JOURNAL_PRESENT,先调查或等待冷态;来源变动报 SOURCE_CHANGED,不得把失败副本继续当有效输入。非空 WAL 仅按 SQLite WAL 头部格式 检查长度、magic、版本及页大小,不支持的头部报 WAL_HEADER_UNSUPPORTED。报告明确不验证 WAL 帧语义,quick_check 不能证明所有原始 WAL 帧或事务都被引擎采纳。
SQLITE_CHECK_FAILED 包含不可读取格式、损坏或检查未完成,不能据此断言原库损坏。输出不得位于源数据库父目录或其子目录,工具拒绝覆写已有目标及软链接路径。
成功退出 0,拒绝/错误退出 2。成功只证明本次观察期间来源稳定且副本 quick_check 通过,不证明在线多文件原子一致、账号归属、删除效果或可用登录。副本含原始私有数据;失败时工具清退自己的输出,CLEANUP_INCOMPLETE 或进程被中断时须单独核对残留。完成调查后按保留决定清退副本,不能把它遗留为另一套日常来源。
文件保护 helper#
scripts/file_guard.py 只使用 Python 3.10+ 标准库。它读指定源、写私有清单/报告,不复制源文件、不删除源文件、不停止进程、不解析 Claude 数据库。哈希清单不能当回滚备份。
根输入只接受目录。先选择准确的保护根,避开本来获准变更的混合库;混合库另做逻辑非目标比较,单个配置文件可另作哈希验证。每个根取唯一中性标签,根之间不能嵌套/重叠。它会记录空目录和软链接本身,不跟随链接;外部目标需要单独列为根才在保护范围内。使用核实过的真实绝对路径,macOS /var 等软链接别名不能直接作为输入。
# SKILL_DIR 指向安装的 claude-local-cleanup 目录;PRIVATE_DIR 是本次私有证据目录。
# TRANSCRIPT_ROOT / WORKSPACE_ROOT 来自本轮实际发现,勿直接套默认父目录。
umask 077
mkdir -p "$PRIVATE_DIR"
"$CLEANUP_PYTHON" "$SKILL_DIR/scripts/file_guard.py" snapshot \
--root "transcripts=$TRANSCRIPT_ROOT" \
--root "projects=$WORKSPACE_ROOT" \
--out "$PRIVATE_DIR/before.json"
# 实际获准变更完成后,使用完全相同的标签和路径重新生成清单。
"$CLEANUP_PYTHON" "$SKILL_DIR/scripts/file_guard.py" snapshot \
--root "transcripts=$TRANSCRIPT_ROOT" \
--root "projects=$WORKSPACE_ROOT" \
--out "$PRIVATE_DIR/after.json"
"$CLEANUP_PYTHON" "$SKILL_DIR/scripts/file_guard.py" compare \
--before "$PRIVATE_DIR/before.json" \
--after "$PRIVATE_DIR/after.json" \
--out "$PRIVATE_DIR/comparison.json"
snapshot 拒绝不稳定/不可读/异常类型的来源、带软链接祖先的根、覆盖已有输出或在源内写输出。清单对文件记录 SHA-256、大小和 mode,对链接记录目标文字,对目录记录 mode;目录时间不作为内容等价要求。读取期间做变化检测,但不是跨目录原子快照,更不能证明没有其他进程。
读取自身可能改变 atime,不把它作为用户内容被修改的证明。需要校验缓存索引的新鲜度时另按实际格式维护元数据;不能事后修改 mtime/ctime 来掩盖业务差异或让检查通过。
若用户后来明确要求删除保护集合里的特定记忆,单独提供 JSON 数组,内容为精确的 标签/相对路径 键,包括实际被删除的目录项:
["transcripts/example/memory", "transcripts/example/memory/MEMORY.md"]
给 compare 添加 --allow-removed "$PRIVATE_DIR/authorized-removals.json"。每项都必须原先存在、现在缺失;未知、重复、未使用例外应报错。例外不允许内容改变、额外新增或按目录前缀跳过;这份文件只是验证输入,不能代替用户授权。清单键从真实基线读取,示例不是实际路径。
退出码:0 为快照成功或比较通过,1 为存在未获准差异,2 为参数/格式/不稳定来源错误。stdout 只给汇总;清单和比较报告含私有路径,应留在 0700 目录,文件为 0600。不会保存源文件内容。输出 I/O 失败可能留不完整文件,不能拿它当有效基线;检查后只处理本次输出,不覆盖任何原件。可用 "$CLEANUP_PYTHON" -B "$SKILL_DIR/scripts/test_file_guard.py" 运行纯合成回归。
stderr 仅给固定错误类别:PATH_NOT_REAL_DIRECTORY 指向非真实目录/软链接路径,SOURCE_CHANGED 表示观察到源变化,INVALID_MANIFEST / INVALID_REMOVAL_LIST 表示输入清单无效,OUTPUT_EXISTS 表示拒绝覆盖。按失败阶段核对,不为绕过拒绝自动跟随用户目录软链接;类别不包含私有路径,也不能证明是谁改了文件。
会话索引到正文的关联、外部 workdir、数据库语义、磁盘锁及 UI 恢复仍需另行验证。基线一旦退役,就不能再声称后续检查与它逐字节一致。
最小事务清单#
为每批保存:当前版本、授权范围、source 路径/UID/类型/links/哈希、精确操作、变更后的预期、非目标验证方式、保护集合、回滚位置、内容分类状态。摘要不携带秘密;原路径和原件私有保存。
计划默认只读。没有误解授权时直接准备并执行,不额外造审批仪式;缺少对某项副作用的授权时,先准备到具体可审阅,再只问该项。
外部文件写入前与每个步骤的条件#
此冷态约束针对外部替换、移动或重写应用数据文件。受支持的应用原生操作由应用管理写入,不套用停机前置;但仍须验证目标范围、保全与结果,不能为省事使用会伤害新登录/他站数据的整站清除。
- 相关应用、CLI、helper 和数据库写入者确已停止,且没有未知文件占用;核对完整可执行路径,不能按模糊
claude字符串杀进程。 - 来源与计划一致;目标在允许根内、owner 合理、无未评估 symlink/hardlink/特殊文件;不跟随路径意外写到根外。
- 原件备份可核验且必要用户内容已保全。原件和暂存文件均有唯一事务 ID;禁止将上次失败临时文件当本次输入。
- 每次 move/replace 与每次 rollback 前再次判断冷态及来源条件。该守卫有检查到写入的竞争窗口,不能冒称系统级防启动锁;捕获变化即停止并留恢复材料。
执行与恢复#
先在私有副本完成变换、校验格式和所有非目标;生产安装用同卷暂存原件与单文件原子替换,保留每步 journal。跨多个文件的操作可部分完成,所以 journal 必须足以恢复任意已执行前缀。
同一目标分多批清理时,区分“最初封存版本”和“本批修改前版本”。本批暂存/回滚必须能精确恢复当前基线;不能把已做过其他清理的文件替换为更早的原件。提交前保留当前原件至来源内容、派生元数据、索引和使用者验证通过。
失败只处理本事务确切创建/替换的项目。回滚前校验当前内容等于本事务预期写入值,拒绝覆盖后来变化。若应用中途启动,停止一切恢复写入,报告 rollback_pending_cold_state;重新冷态后才继续。不得无声强杀应用以使流程通过。
需要新写适配器时,对实际故障风险做少量有意义的私有 fixture 验证:移走一半失败、替换后失败、回滚中重新启动、非目标值不变、内容保全未通过时不永久删除。只读审查或简单可逆文字修改无需套用这套故障测试。
结果状态应能追溯#
可用状态为 audited_no_action、planned、applied_verified、partial、rollback_pending_cold_state、rolled_back_verified。副本生命周期另记 rollback_available 和退役验证,不要用一个“完成”掩盖部分失败。
最终摘要包含 scope、checked_at、目标计数、读取错误、非目标比较、保护基线及关联验证、归档验证、启动验收是否完成、保留与未知项。只陈述实际运行过的验证;当前文件为零与“这个脚本会检查”不是一回事。
证据注明采用的方法:原始字节、有效记录、内容语义、来源哈希、真实读取与人工归属确认各自有证明范围。未查与明确受阻分开记录,受阻项列具体失败及最小下一步;继续/停止条件以技能入口为准。
后续小批改动可复用已通过基线,以 前一已验证状态 → 本批精确变换与非目标比较 → 当前状态 连成证据链;同时更新当前来源哈希和依赖闭包(包括附件/图库引用)。不要拿旧报告的哈希冒充当前文件,也不要在无新风险时重复整库验收。