Real-dir migration

What this layer does

Phase 11 converts pre-existing real directories under each runtime's skills path to symlinks pointing at canonical. Only Robert's machine needs this — Joes start clean and bootstrap symlinks straight away. The migration is non-destructive: real dirs move to a timestamped backup, the symlink replaces them, and divergence triggers a manual 3-way diff rather than silent overwrite.

Files involved

supports --dry, --apply, --runtime <name>.

(Phase 6 reads this to refuse running until it sees a row).

for moved real dirs.

when SHA compare fails; surfaces the 3-way diff candidate.

Procedure

state/bin/sync/migrate-realdirs.sh --runtime gemini --dry
# For each ~/.gemini/skills/snappy-*/:
#   - if real dir: read SKILL.md, find canonical at
#     ~/projects/snappy-kernel/skills/<same-name>/SKILL.md
#   - SHA-256 compare:
#     - identical → safe to symlink
#     - diverged  → REPORT, do NOT touch

state/bin/sync/migrate-realdirs.sh --runtime gemini --apply
# Moves real dirs to ~/.claude/_backups/<ts>/gemini/<name>/
# Creates symlink to canonical
# Logs row to state/log/migrations.ndjson

Divergence resolution policy

When the runtime-local copy and the canonical copy differ, the script emits a 3-way diff candidate against:

  1. canonical~/projects/snappy-kernel/skills/<name>/SKILL.md
  2. runtime-local~/.gemini/skills/snappy-<name>/SKILL.md
  3. DO Spaces remote — last-pushed version of canonical

Robert picks one. The picker writes the chosen content to canonical, runs sync-runtimes.sh to fan out, and only THEN runs migrate-realdirs.sh --apply on the affected name. There is no auto-resolve; divergence implies a human-meaningful edit somewhere.

Operational gotchas

reads state/log/migrations.ndjson; missing row = refuse. Otherwise Phase 6 captures stale canonical state from runtime-local copies that should have migrated first.

prevents collisions across runs. Never delete a backup directory manually — the migration script's only safety net is that the original bytes still exist somewhere.

dir present" (safe to symlink). A symlink pointing somewhere else entirely (not snappy-os) is left alone with a warning.

bootstrap; the script no-ops gracefully.

one shot makes a divergence report unreadable. Run one runtime, resolve, then move to the next.

How to verify it's working

every candidate with status safe-to-symlink, diverged, or already-symlink.

to canonical for every previously-real dir.

moved dir.

success: true.