diff --git a/server/lib/22-claude-config.sh b/server/lib/22-claude-config.sh index 03775bc..c911c14 100755 --- a/server/lib/22-claude-config.sh +++ b/server/lib/22-claude-config.sh @@ -46,6 +46,18 @@ DST="$H/.claude" # absolute laptop path arrives verbatim and is wrong on the box — keep those # paths relative or in settings.local.json. CONTENT=(CLAUDE.md statusline.sh agents commands docs hooks skills project-config) + +# What Claude Code writes at runtime beside a project's configuration, as +# opposed to the configuration itself: the machine-local settings file, the +# scheduler's lock, and the directory it makes agent worktrees in. All three +# belong to the machine that wrote them. +# +# Declared once because two things need the same list and rsync and diff spell +# it the same way: the delivery, which must not carry these or delete the box's, +# and the divergence test below, which must not read them as configuration this +# box holds and the payload lacks. Two hand-written copies would be the drift +# §13.2 counts. +LOCAL_ONLY=(--exclude=settings.local.json --exclude='*.lock' --exclude=worktrees) if [[ $PRINT_ONLY -eq 1 ]]; then for entry in "${CONTENT[@]}"; do [[ -e "$SRC/$entry" ]] || continue @@ -62,11 +74,11 @@ else # per-project machine-local file, which is the one file here that holds # box-specific state and can capture credentials from an approved command # line. - # *.lock is runtime state Claude Code writes beside a project's - # configuration; it belongs to the machine that wrote it, and is excluded - # here for the same two reasons as settings.local.json. - rsync -a --delete --exclude=settings.local.json --exclude='*.lock' \ - "$SRC/$entry" "$DST/" \ + # Excluding the machine-local set does two things at once under --delete: it + # declines to send files the payload should never have carried, and it + # protects the box's own from deletion, since rsync does not delete an + # excluded file on the receiving side. + rsync -a --delete "${LOCAL_ONLY[@]}" "$SRC/$entry" "$DST/" \ || die "could not sync $entry into $DST" done fi @@ -233,8 +245,7 @@ for cfg in "$DST"/project-config/*/; do # # Payload-only files are expected and do not block: the payload is the # source, and the link is what makes the box read it. - extra=$(diff -rq --exclude=settings.local.json --exclude='*.lock' \ - "$link" "$cfg" 2>&1 \ + extra=$(diff -rq "${LOCAL_ONLY[@]}" "$link" "$cfg" 2>&1 \ | grep -e "^Only in $link" -e ' differ$' | head -3) if [[ -n "$extra" ]]; then defer "$link" \