# 常用工具、保护清单与事务

## 先用现成的小工具

这些工具使用 Python 3.10+ 标准库，面向 POSIX/macOS。它们只读生产来源，输出写入私有目录；不判断旧账号归属、不退出应用、不清登录，也不发布或回滚生产文件。先核验本机解释器版本，将 Python 3.10+ 的实际路径设为 `CLEANUP_PYTHON`，不能假定命令 `python3` 已满足版本要求。以下 `SKILL_DIR`、`PRIVATE_DIR`、源路径及 SHA 均由本轮实际发现，不能照搬历史值。私有工作目录必须在应用管理根之外，使用无软链接祖先的真实绝对路径。

| 工具 | 作用及边界 |
|---|---|
| [json_candidate.py](../scripts/json_candidate.py) | 默认只预览；按精确对象键路径生成删除候选，显式输出须绑定来源 SHA。保留剩余 token、数值拼写、顺序及删除区间外的原字节；不做整文件重新序列化。 |
| [sqlite_snapshot.py](../scripts/sqlite_snapshot.py) | 复制 DB 和对应 WAL，检查源成员集合及前后身份/哈希；仅在私有副本运行 SQLite quick_check，不复制源 SHM。是可检查副本，不是原子快照或逻辑账号验证。 |
| [file_guard.py](../scripts/file_guard.py) | 为精确保护目录生成/比较只读清单；不复制来源、不解析数据库，也不替代内容及引用验证。用法见下节。 |

### 精确 JSON 候选

私有 `targets.json` 是一个 0600 的 JSON 文件，父目录由当前用户所有且权限为 0700。内容是精确对象键路径，例如 `[["account", "oldToken"], ["oldPreference"]]`；这些只是语法示例，不是 Claude 的固定字段或删除清单。先核实字段、旧身份归属与本轮授权。

```bash
# 预览不写来源或候选，只输出状态、目标数量及前后 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 私有检查副本

```bash
# 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 头部格式](https://www.sqlite.org/fileformat2.html#wal_file_format) 检查长度、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` 等软链接别名不能直接作为输入。

```bash
# 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 数组，内容为精确的 `标签/相对路径` 键，包括实际被删除的目录项：

```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、目标计数、读取错误、非目标比较、保护基线及关联验证、归档验证、启动验收是否完成、保留与未知项。只陈述实际运行过的验证；当前文件为零与“这个脚本会检查”不是一回事。

证据注明采用的方法：原始字节、有效记录、内容语义、来源哈希、真实读取与人工归属确认各自有证明范围。未查与明确受阻分开记录，受阻项列具体失败及最小下一步；继续/停止条件以技能入口为准。

后续小批改动可复用已通过基线，以 `前一已验证状态 → 本批精确变换与非目标比较 → 当前状态` 连成证据链；同时更新当前来源哈希和依赖闭包（包括附件/图库引用）。不要拿旧报告的哈希冒充当前文件，也不要在无新风险时重复整库验收。
