ssotize-local
Give duplicated files or trees one canonical home and make every other path a filesystem identity of it, after an audit and an approval.
Goal
Duplicated files or trees get one canonical home, and every other path its consumers read becomes an identity of it held by this machine's filesystem: a hardlink, symlink, junction or bind mount. Nothing is lost on the way: neither the bytes nor the preservation contract, which covers ACLs, owner and mode, named streams, xattrs and the timestamps that must be kept. Use it when consumers need the same bytes at several paths and textual references cannot serve them.
The -local suffix means portability depends on the mechanism; what survives a clone, a copy or another host is stated per mechanism under Rules. A run that finds no scatter changes nothing, and a stop at any gate is a valid outcome.
Workflow
Every deterministic step runs node scripts/identity.mjs from this skill's folder, with the subcommands probe, digest, compare, link, verify and unlink; every subcommand accepts --json.
- Audit. Call the Skill tool with "ssotize" on the duplicated files or trees for its read-only audit and plan, with linking proposed for each copy whose consumers need the same bytes. Its result is this run's inventory: the occurrence table, the canonical home and its reason, the contradictions, the unique details, and the approval gate the plan must pass. Where it stops, this run stops. When ssotize is not installed, say so and stop, because a link made without its audit and approval skips the gates that make it safe.
- Extend the inventory for links. Add what a link needs: every consumer, alias, trust boundary, hidden and empty entry and nested link, with each link target resolved explicitly. A shared directory includes every member its consumers need.
- Probe, never assume. Run
node scripts/identity.mjs probe <path>...on every candidate and every destination parent, and record the command, exit code, output and time. It reports the path, link state and target, identity and link count, size, mtime, mount root where derivable, and the link capabilities it tested in a temporary directory, which is the only write an audit makes. Probe destination permission, locality, filesystem and mount privilege separately, and resolve every ancestor link and mount. A label proves nothing, and what is unknown stays unproven. - Ask about writers. The human decides, with this exact question: "Can every concurrent writer be excluded for the whole mutation and verification interval by a maintenance window or a lock every writer honors?" Only an explicit yes counts, and it is recorded. A handle listing is a snapshot, not a lock. On no, or no answer, report "Exclusive access unproven for ; no mutation" and stop. Never kill unrelated processes.
- Digest and compare, read-only.
node scripts/identity.mjs digest <path>...prints path, mtime, size, sha256 and identity through every name, with the sorted relative members and a manifest hash.node scripts/identity.mjs compare <a> <b>classifies bytes and identity, and exits 0 for equality or identity, 3 for divergence or unique members, and 1 for errors. Inspect ACLs, owner and mode, named streams, xattrs and timestamps separately, and report any delta that the reads or an identity change caused. The script proves main bytes and members only; a missing tool, a denied read or an unsupported field leaves the contract unproven. - Classify each candidate against the canonical home, one row per case: