关于 ssotize-local
ssotize-local 是将重复文件或目录树收敛到一个规范位置的技能,适用于需要多个路径提供相同内容的场景。它先审计并取得批准,再按文件系统支持情况建立链接,同时验证内容与保留属性。
主要功能
- 01审计重复项并制定计划
- 02检查路径与链接能力
- 03比较内容及文件属性
- 04按批准方案建立链接
- 05验证链接并支持撤销
适用场景
- 01让多个路径指向同一文件
- 02统一重复目录树的内容来源
- 03审查链接变更及其可逆性
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ lilmgenius/ paperthin --skill ssotize-local安装后在 Claude Code 中输入/ssotize-local确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
agents/openai.yamlscripts/identity.mjsidentity.test.mjs详细介绍
为重复的文件或目录树指定一个规范位置,并在完成审计和批准后,让其他每个路径都成为它在本机文件系统中的同一实体。
目标
重复的文件或目录树应有一个规范位置,其他所有使用者读取的路径都应成为它在本机文件系统中的同一实体:硬链接、符号链接、junction 或绑定挂载。整个过程中不能丢失任何内容:既不能丢失字节,也不能破坏保留约定;该约定涵盖 ACL、所有者和模式、命名流、扩展属性,以及必须保留的时间戳。只有当使用者需要在多个路径访问相同字节,且文本引用无法满足需求时,才使用此方法。
-local 后缀表示可移植性取决于所用机制;克隆、复制或换用其他主机后能否保留,将在“规则”中按机制说明。若运行后发现没有分散副本,则不做任何更改;在任何关卡停止都是有效结果。
工作流
每个确定性步骤都在此技能文件夹中运行 node scripts/identity.mjs,子命令包括 probe、digest、compare、link、verify 和 unlink;每个子命令都接受 --json。
-
审计。 对重复的文件或目录树调用 Skill 工具,并传入 "ssotize",进行只读审计和规划;对于使用者需要相同字节的每个副本,都提议建立链接。其结果即为本次运行的清单:出现位置表、规范位置及其理由、矛盾、独有细节,以及规划必须通过的批准关卡。审计在哪一步停止,本次运行就在哪一步停止。如果未安装 ssotize,请说明并停止,因为未经其审计和批准就建立链接,会跳过确保安全的关卡。
-
扩展链接清单。 补充建立链接所需的信息:每个使用者、别名、信任边界、隐藏项和空项、嵌套链接,并明确解析每个链接目标。共享目录必须包含使用者所需的每个成员。
-
探测,不要假设。 对每个候选路径和每个目标父目录运行
node scripts/identity.mjs probe <path>...,并记录命令、退出代码、输出和时间。它会报告路径、链接状态和目标、实体标识和链接数、大小、修改时间、可推导时的挂载根目录,以及在临时目录中测试的链接能力;临时目录中的测试是审计唯一会执行的写入操作。分别探测目标路径的权限、本地性、文件系统和挂载权限,并解析每个祖先链接和挂载点。标签不能证明任何事,未知信息仍然属于未证实。 -
询问写入者情况。 由人工决定,并使用以下确切问题:“能否通过维护窗口或所有写入者都会遵守的锁,在整个变更和验证期间排除所有并发写入者?”只有明确答“是”才算通过,并须记录。句柄列表只是某一时刻的快照,不是锁。若答“否”或没有回答,则报告 "Exclusive access unproven for ; no mutation" 并停止。绝不要终止无关进程。
-
计算摘要并比较,只读操作。
node scripts/identity.mjs digest <path>...会输出路径、修改时间、大小、sha256 和经过每个名称的实体标识,以及排序后的相对成员和清单哈希。node scripts/identity.mjs compare <a> <b>会对字节和实体标识进行分类;相同或为同一实体时退出码为 0,存在差异或独有成员时为 3,出错时为 1。分别检查 ACL、所有者和模式、命名流、扩展属性和时间戳,并报告读取或实体标识变更造成的任何差异。脚本只能证明主要字节和成员;若缺少工具、读取被拒绝或字段不受支持,则无法证明保留约定。 -
分类每个候选项与规范位置的关系,每种情况占一行:
情况 分类 操作 字节和契约完全相同 符合条件 经批准后建立链接,不备份 字节相同,但契约不同或无法读取 未证实 列出差异,然后停止或明确解决保留问题;备份不能证明两者相同 字节不同 清单已报告的矛盾 停止,等待人工选择或按要求合并;绝不以最新或最大的为准 只存在于一个位置 折叠 在人工单独批准后,再次调用 Skill 工具并传入 "ssotize",处理该项,然后在建立链接前重新审计 已链接到预期的规范项 身份已证实,无需操作 报告并重新验证;绝不重复建立链接 -
报告。 在清单的出现位置表旁,逐路径报告所选层级、被移走的名称、保留位置、证据和回退操作。如果在单独批准下执行了折叠,则对结果重新运行步骤 1 到 6,并根据新清单报告;折叠的批准绝不能替代步骤 8。
-
批准。 在清单的批准关卡向人工提交计划,使用准确措辞 "link if still identical at the mutation step";只有这句措辞涵盖链接步骤。没有这项批准,就在只读报告后停止。该批准不涵盖尚未解决的冲突或之后的变更;折叠、合并或复制均各自需要单独同意。获得批准后,由人工决定必须保留哪些时间戳:根据读取这些时间戳的内容提出建议(按 mtime 判断的同步或构建、备份、账本),并在其作出决定前保留所有时间戳。链接的每个名称都会显示规范项的时间戳,因此必须保留的重复项时间戳应放入步骤 9 要验证的保留内容中。
-
每次写入前重新检查。 在任何目标写入之前(包括备份和协调),立即重新枚举候选项,并针对每个项目,根据已批准的审计重新读取路径、mtime、大小和 sha256、契约、身份及排除证据。任何新增、缺失、不可读、被替换或发生变更的候选项都会使写入停止,并说明文件及其差异;流程返回步骤 1 进行新审计,并在任何写入前返回步骤 8 获取人工的新批准。脚本会对暂存内容和移出内容创建快照,但不会对批准或写入方排除项创建快照;哈希只能缩小竞态窗口,不能消除它。已证实字节和契约相同的项目无需备份;未证实的候选项必须先有经过验证的独立保留副本(包括契约),若无法做到,则停止。
-
建立链接,每种情况选择一个层级。
node scripts/identity.mjs link <canonical> <alias> --mechanism hardlink|symlink|junction [--staged <absent-sibling>]会安装已证实的链接,并完整保留任何被移出的条目。绝不覆盖未经检查的路径,也不递归删除目录;如果失败,则保留原件和暂存内容。根据以下阶梯选择层级:层级 适用情况 方式 文件硬链接 位于同一受支持的本地卷上的常规文件,且有父目录权限 先探测,再使用 link --mechanism hardlink目录硬链接 所有主流系统均不支持 绝不尝试 符号链接 目录的首选方式、文件的后备方式;已探测创建权限和父目录权限 link --mechanism symlinkJunction Windows 目录的后备方式:父目录可写、目标为绝对本地路径,且不是网络目标;不要求位于同一卷 link --mechanism junction绑定挂载 仅限 Linux,且由人工明确选择、已探测权限、挂载点已获批准且为空,并有单独的持久化计划 在脚本外操作;绝不作为无提示的后备方式 复制 仅限人工明确同意 报告为“不再是 SSOT”,绝不能称为链接 这些文档说明了各机制支持什么;它们并非在此计算机上实测,因此每一级仍需执行其第 3 步探测:https://learn.microsoft.com/en-us/windows/win32/fileio/hard-links-and-junctions ,https://man7.org/linux/man-pages/man2/link.2.html ,https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/link.2.html -
每次修改后都进行只读验证。 针对已批准的清单和契约运行
node scripts/identity.mjs verify <canonical> <alias>...。硬链接需要 dev:ino(或卷和文件 ID)相同,且链接计数涵盖整个集合;符号链接或 junction 必须指向规范路径;通过每个名称访问时,所有成员的哈希值都必须相同。字节相同本身绝不能证明身份相同。绑定挂载需要单独证明其挂载、来源和身份。遇到未知情况就停止。绝不要向正在使用的目标写入哨兵;写入测试只能在获准的一次性测试夹具上进行。 -
按机制反向操作,且须先经人工批准。重复探测,并在一次性数据上探测移除操作。运行
node scripts/identity.mjs unlink <alias> --stage <dir>,其中<dir>是别名所在卷上、链接树之外的现有文件夹;该命令会在别名下用已证明独立的副本替换链接;它只移除目录项,绝不删除目标树;junction 以非递归方式移除(https://learn.microsoft.com/en-us/dotnet/api/system.io.directory.delete)。先验证暂存后的契约,因为复制的字节不能证明 ACL、数据流或扩展属性。绝不要通过链接删除内容。证明规范路径的成员、哈希值和契约,以及所有未选中的链接均完好无损;链接计数应符合预期别名数量,该数量不一定为 1。绑定挂载需要显式卸载(忙碌时停止;绝不强制或延迟卸载),确认对应挂载已不存在、来源未改变,并确认挂载点处显现出不同的身份;绝不要暂存到挂载点或移除挂载点,而且卸载不会创建副本。发生部分故障时,保留凭证和原件;无法验证的事项仍视为未完成。
规则
- 第 1 步审计并获批之前,绝不写入目标;探测器的临时目录不属于目标。报告哪些内容保持只读,以及哪些事项需要人工决策;没有发现问题也是有效结果。
- 链接会跨越任何边界时,都必须先与人工确认,无论是从私有区域到公共区域,还是从内部区域到面向客户的区域,因为链接会将共享更改和权限暴露给所有使用者。
- 绝不跟随链接删除其目标,绝不在别名处递归删除,部分失败时保留原件。
- 可移植性取决于具体机制。Git 克隆中的硬链接不携带身份信息。相对符号链接会随复现相同布局而生效,而
core.symlinks=false会将其目标文本写成文件,因此要检查该设置(https://git-scm.com/docs/git-config#Documentation/git-config.txt-coresymlinks),并在目标端验证。绝对符号链接要求目标路径相同。目录联接仅适用于 Windows,且仅限本地绝对路径。绑定挂载需要主机管理员权限和获批的持久化配置。副本不再是 SSOT。 - 如果使用者必须看到新增、删除和替换,优先使用目录链接;每次替换后都重新验证文件硬链接;绝不承诺链接永久有效。
- 已经链接好的集合属于报告中的无操作项:只重新验证,绝不重新链接。
- 下方的故障登记表列出链接建立后可能导致其失效的情况。表中的任何一行都不能替代当前运行所需的证明。
| # | 故障 | 证据 | 重新验证 |
|---|---|---|---|
| 1 | 替换式保存会切断硬链接,而原位保存会保留硬链接 | 有来源,未测量 | 保存后验证整个集合 |
| 2 | 检出或暂存可能写入替换文件 | 有来源 | 任何重写后都验证,包括外部别名 |
| 3 | 未保留链接的还原操作会扩展集合 | 有来源 | 往返操作后验证;绝不能根据选项推断 |
| 4 | 云同步服务可能拒绝链接或将其具体化为副本 | 有来源 | 在每台主机上探测并验证;未知时停止 |
| 5 | 审计后发生的编辑会破坏相等性 | 历史记录 | 写入前重新检查时,指出差异并停止 |
| 6 | 字节相同可能掩盖 ACL、数据流或 xattr 的差异 | 提议 | 重复执行契约证明;未证明则保持停止 |
| 7 | 最终哈希之后仍可能发生写入 | 提议 | 未获得排除该情况的答复时停止;绝不声称竞态已消除 |
验证
完成前,确认:
- 第 1 步审计首先运行,且其审批先于每次写入;未发生散布的运行未做任何更改,停止也作为有效结果报告。
- 已记录探测的各级、排除该情况的答复以及审批。
- 已报告所有写入前的差异,并验证了所有必需的保留项。
- 已证明每个名称下的身份、哈希和契约。
- 对适用的七类故障均已重新验证。
- 若运行了分阶段回退,则已证明其有效;任何未知项都仍未完成。
- 每份回执都包含命令、退出代码、时间、前后身份、保留位置和未解决字段。
- 最终报告说明了哪些内容已链接、哪些内容保持只读,以及还有哪些事项需要人工决策。