SuperAPI AI Directory

wiki-sync

Office & productivity

View repository

Updated 10-16 09:14

About this skill

wiki-sync

When This Skill Activates

  • User wants to push local vault changes to the remote.
  • User wants to pull remote changes into their local vault.
  • User asks about vault sync status, git state, or multi-device coordination.
  • Multiple Claude Code sessions targeting the same vault.
  • Periodic maintenance before or after editing sessions.

Pre-orientation reads

Standard four reads.

Steps

  1. Resolve vault: skillwiki path (record source for context).

Pre-flight peer check (multi-session safe)

Before any git stash or pull/push operation, check for peer sessions:

  1. Run skillwiki sync peers <vault> to detect other sessions with active locks or recent wiki-sync:* stashes.
  2. If any non-self peer is present (locked or has stashes newer than 5 minutes):
    • Surface the peer's session_id, PID, and summary to the user
    • Ask the user to wait for the peer to finish, or pass --force to proceed anyway
    • If --force is not given and peer is detected, abort and exit
  3. Acquire an advisory lock: skillwiki sync lock <vault> --summary "wiki-sync <op>" (where <op> is "pull" or "push")
    • If lock is held (exit code 48), surface the holder (session_id, PID, summary) and abort
  4. Always pair with unlock on exit (success or error):
    • skillwiki sync unlock <vault> in a finally block or error handler

Stash backlog warning

On every invocation, count wiki-sync:* stashes older than 24 hours via skillwiki sync peers:

  • If any old stashes exist, warn the user: "Found N wiki-sync stash(es) older than 24h — audit and clean before proceeding"
  • Do not auto-drop old stashes — the user audits each one

Sync workflow

  1. Run skillwiki sync status <vault>. Read the JSON output.
    • Exit code 0: vault is clean (nothing to sync).
    • Exit code 22: warnings — dirty/ahead/behind (needs action).
  2. Present the current state: status, dirty, ahead, behind, last_commit.
  3. Ask the user which operation they want: push, pull, or both (pull then push).

Push workflow

  1. If vault is dirty, ask the user to review uncommitted changes before proceeding.
  2. Run skillwiki sync lint-delta <vault> --base-ref origin/main. Block only when new_errors > 0. Report full/base/new/resolved. Malformed delta evidence fails closed — do not push.
  3. If lint-delta allows (new_errors = 0), stage and commit:
    • git -C <vault> add -A
    • git -C <vault> commit -m "sync: vault update $(date -u +%Y-%m-%dT%H:%MZ)"
  4. Run git -C <vault> push origin HEAD. Report result.
  5. Append one log.md entry summarizing: files pushed, lint result, commit hash.

Pull workflow

  1. Run skillwiki sync status <vault> --include-stashes to check for untracked file collisions (see Untracked file fingerprint below).
  2. If vault is dirty, stash first with the identifiable name format:
    VAULT="<vault>"
    SESSION_ID="$(echo $CLAUDE_SESSION_ID)" # or fallback to PID/hostname
    CWD_HASH="$(echo -n "$VAULT" | sha256sum | cut -c1-8)"
    ISO_TS="$(date -u +%Y-%m-%dT%H:%MZ)"
    MSG="wiki-sync:${SESSION_ID}:${CWD_HASH}:${ISO_TS}:pre-pull"
    git -C "$VAULT" stash push -m "$MSG"
    
  3. Prefer the canonical pull helper (wiki-pull-with-auto-resolve.sh / vault-presync --execute) so stale-clean rebase state uses recovery-ref + rebase --quit, active rebases fail closed, and only fully materialized commits are dropped. If invoking git directly: git -C <vault> pull --rebase origin HEAD. Report result.
  4. If a stash was created, pop it: git -C <vault> stash pop.
  5. If conflicts occur during stash pop, identify them and present to the user for resolution (see Conflict Resolution below).
  6. Run skillwiki lint <vault> after pull to verify vault integrity.
  7. Append one log.md entry summarizing: commits pulled, lint result, any conflicts.

Pull-then-push workflow

  1. Execute the pull workflow (steps 9-14) first.
  2. Then execute the push workflow (steps 4-8).

Stash naming convention

When wiki-sync creates a stash, use the identifiable message format:

wiki-sync:{session_id}:{cwd_hash}:{iso8601_timestamp}:{summary}
  • session_id: prefer $CLAUDE_SESSION_ID env var if set, else $$ (shell PID), else unknown
  • cwd_hash: first 8 chars of sha256($VAULT path)
  • iso8601_timestamp: e.g., 2026-05-23T03:25:00Z (UTC)
  • summary: short label like pre-pull, pre-push, or custom reason

This allows any session to list git stash list and identify which stash came from which session/working directory.

Untracked file fingerprint (pre-pull)

Before git pull --rebase, check for untracked files that exist on the remote and may collide:

for f in $(git -C "$VAULT" ls-files --others --exclude-standard); do
  if git -C "$VAULT" cat-file -e "origin/main:$f" 2>/dev/null; then
    # File exists on remote; check if identical
    if diff -q <(git -C "$VAULT" show "origin/main:$f") "$VAULT/$f" >/dev/null 2>&1; then
      # Byte-identical — safe to remove (presync artifact)
      rm "$VAULT/$f"
    else
      # DIFFERENT — surface to user, DO NOT silently --include-untracked
      echo "UNTRACKED COLLISION: $f differs from origin/main — surface to user for resolution"
    fi
  fi
  # If file does not exist on remote, leave it alone (pull won't touch it)
done

If collisions are found (different content), ask the user to resolve manually before pulling.

Conflict Resolution

When merge conflicts are detected:

Frontmatter conflicts

  • For updated: fields: always take the newer timestamp (compare both sides, keep the later one).
  • For all other frontmatter fields: present both versions to the user and ask which to keep.

Body conflicts

  • Do not auto-resolve body conflicts.
  • Mark unresolved regions with ??? on a line by itself between the conflicting versions, so the user can see both sides and decide.
  • Example:
Content from local version
???
Content from remote version
  • After resolving conflicts, run skillwiki lint <vault> to verify before committing.

Modify/delete conflicts

When git pull --rebase reports CONFLICT (modify/delete):

  1. Identify the commit that deleted the file:
    git -C "$VAULT" log --diff-filter=D --pretty=oneline -- <path>
    
  2. Read the commit message and any retro / log entry referencing it to determine if the deletion was intentional or accidental.
  3. Decide:
    • git -C "$VAULT" rm <path> — accept the deletion (rebase continues)
    • git -C "$VAULT" add <path> — keep the local restoration (rebase continues)
  4. git -C "$VAULT" rebase --continue.

Rebase conflict storm (archive commits × snapshot stream)

When many local archive-only commits (e.g., archive: moved X to _archive/) are rebased over an origin/main that receives frequent snapshot commits (e.g., sg01 Snapshot YYYYMMDD_HHMMSS), every archive commit re-triggers the same content conflicts on shared files (log.md, knowledge.md, spec.md). This is predictable and can be resolved systematically.

Detection: 3+ consecutive rebase stops on commits whose message matches ^archive: moved.

Resolution: For each archive commit during the storm:

# Apply --ours to all conflicting files (keep HEAD = origin/main + snapshots)
for f in $(git -C "$VAULT" diff --name-only --diff-filter=U); do
  git -C "$VAULT" checkout --ours "$f" && git -C "$VAULT" add "$f"
done
git -C "$VAULT" rebase --continue

After the storm passes (non-archive commits or clean rebase), pop the stash and handle any remaining conflicts per the normal Conflict Resolution sections above.

Prevention:

  • Sync more frequently — don't let local fall >5 commits behind origin/main
  • Bundle archive commits — skillwiki archive --batch groups 5-10 transcript archives into one commit, reducing rebase surface
  • For vaults with snapshot cron, prefer smaller, more frequent syncs over large batch rebases

See concepts/wiki-sync-rebase-conflict-storm-pattern.md for detailed analysis.

Multi-device coordination

When the user mentions editing from Obsidian desktop and Claude Code on a server (or any two-device setup):

  • Recommend pulling before every editing session on each device.
  • Recommend pushing after every editing session on each device.
  • If both devices edit the same page between syncs, conflicts are inevitable — the Conflict Resolution section handles this.
  • Suggest enabling auto-commit in Obsidian (Community Plugins: obsidian-git) to reduce dirty-state drift.

Rclone-backed vault with git snapshotting (cron pattern)

Some deployments use a cloud-backed vault (rclone mount) with a separate git repository for versioned snapshots. This pattern separates "live working vault" from "versioned backup".

Architecture

~/wiki           → rclone mount to cloud storage (S3/IDrive/etc) — live vault
~/wiki-git       → git repository cloned from GitHub — snapshot target
cron hourly      → rsync ~/wiki/ → ~/wiki-git/ → git commit → git push

On snapshotter hosts, ~/wiki remains the active SkillWiki vault for path resolution unless the operator explicitly configures otherwise. ~/wiki-git is snapshot infrastructure, not the default authoring or dev-loop vault. Agents may author the live vault path when the host policy allows it, but should not point project work or fleet context at ~/wiki-git unless skillwiki path intentionally resolves there.

Implementation (wiki-snapshot.sh)

#!/bin/bash
WIKI_DIR="/root/wiki"
GIT_DIR="/root/wiki-git"
DATE=$(date +%Y%m%d_%H%M%S)
# Sync from rclone mount to git repo (quiet mode for slow mounts)
rsync -a --delete -q \
--exclude='.snapshots' --exclude='.git' --exclude='.obsidian' --exclude='.skillwiki' \
"$WIKI_DIR/" "$GIT_DIR/"
cd "$GIT_DIR" || exit 1
git config user.email "cron@hermes.local"
git config user.name "Hermes Snapshot"
# Check for changes
if [ -z "$(git status --porcelain)" ]; then
exit 0  # Nothing to commit
fi
git add -A
git commit -m "Snapshot $DATE"
# Pull with rebase to handle remote changes (e.g., README edits on GitHub)
if ! git pull --rebase origin main 2>/dev/null; then
git pull origin main 2>/dev/null || true
fi
git push origin main || echo "Push failed"

Pitfalls specific to this pattern

  1. Divergent branches from external pushes: If something else pushes to the same GitHub repo (manual edits from macOS desktop, GitHub web UI edits, another server), the local ~/wiki-git will diverge. The --rebase flag handles most cases, but if commits conflict:
cd ~/wiki-git
git rebase --abort 2>/dev/null || true
git fetch origin main
git reset --hard origin/main
bash ~/.hermes/scripts/wiki-snapshot.sh  # Re-sync fresh

Prevention: Avoid editing the GitHub repo directly via web interface or uncoordinated clones. The canonical flow is single-writer-git (see concepts/vault-write-authority-model.md):

  • Server (sg01): agents may author the live vault at ~/wiki; the snapshot job promotes cloud-backed live-vault state into ~/wiki-git, then commits and pushes — sole git writer to main
  • macOS/desktop: edit → wiki-push rclone copy to S3 (NO git push) → consume sg01 snapshots via wiki-fetch-notify (opt-in WIKI_FETCH_PULL_ON_DELTA=1) or manual skillwiki sync
  • wiki-sync skill push is for explicit agent/human edit commits only, not automated background pushes
  1. Slow rsync on rclone mounts: The rclone FUSE mount can be slow for large directory listings. Use rsync -q (quiet) to reduce output overhead, and consider --delete-delay instead of --delete if file churn is high. The rclone mount latency can cause du and find operations to timeout — this is normal, not an error.
  2. Golden Rule violation: Never mix sync methods on the same vault. If using rclone mount + git snapshotting, do NOT also enable Obsidian Sync, Syncthing, or iCloud on ~/wiki. The rclone mount IS the sync mechanism.
  3. Credential exposure: The rclone mount and git remote use different credentials. Ensure git credentials are cached or use HTTPS with token, but never commit rclone config to git.

Stop conditions

  • skillwiki sync status reports not_a_repo — the vault is not a git repository. Advise the user to initialize one.
  • Lint errors are found before a push — do not push until resolved.
  • git push or git pull fails with a network error — report and stop.
  • Peer lock is held or peer stashes exist — abort and ask the user to wait or pass --force.
  • Untracked file collision detected on pull — surface to user for manual resolution.

Forbidden

  • Pushing when lint errors exist.
  • Auto-resolving body conflicts without user review.
  • Force-pushing (git push --force).
  • Modifying files in raw/ to resolve conflicts (N9 — archive and re-ingest instead).
  • Stashing without the wiki-sync:... name format (breaks peer detection).
  • Force-deleting a peer's lockfile (use --force only if peer is confirmed dead).

Convergence safeguards (2026-07-11)

Rebase-state classification

  • stale-clean: recovery ref at tip + git rebase --quit (preserves advanced tip; never abort-reset to orig-head).
  • active (REBASE_HEAD / UU paths): leave untouched; fail closed.
  • Recovery refs live under refs/vault-sync/recovery/<UTC timestamp>.

Materialized-commit proof

Drop a local commit from rebase only when every path is proven present on the target ref (exact blobs; byte-identical added ## log sections). Partial/raw/rename mismatches retain or stop.

Lint-delta fail-closed

  • Fingerprints: <bucket>\0<page>\0<normalized-detail>
  • CLI: skillwiki sync lint-delta <vault> --base-ref origin/main
  • Block publication only when new_errors > 0; inherited full debt remains visible.
  • Missing/malformed delta evidence blocks (never silent lint skip).