[object Object]

← back to Games Agentabrams

sync guard: deploy re-syncs from source first + sync.sh --check drift detector

686fd16f09cf7895ae3cc121c810555eedbe17e9 · 2026-07-25 11:33:48 -0700 · Steve

Closes the silent-drift hole where editing a source build (e.g. particle-text)
and forgetting ./sync.sh would rsync a stale games/<id>/ copy to the live site.
deploy.sh now runs sync.sh before rsync; sync.sh gains a non-mutating --check
mode (exit 3 on drift) for canary/CI use.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 686fd16f09cf7895ae3cc121c810555eedbe17e9
Author: Steve <steve@designerwallcoverings.com>
Date:   Sat Jul 25 11:33:48 2026 -0700

    sync guard: deploy re-syncs from source first + sync.sh --check drift detector
    
    Closes the silent-drift hole where editing a source build (e.g. particle-text)
    and forgetting ./sync.sh would rsync a stale games/<id>/ copy to the live site.
    deploy.sh now runs sync.sh before rsync; sync.sh gains a non-mutating --check
    mode (exit 3 on drift) for canary/CI use.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 deploy/deploy.sh |  6 ++++++
 sync.sh          | 41 ++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 46 insertions(+), 1 deletion(-)

diff --git a/deploy/deploy.sh b/deploy/deploy.sh
index bc2a20e..2393bba 100755
--- a/deploy/deploy.sh
+++ b/deploy/deploy.sh
@@ -6,6 +6,12 @@ KAMATERA=root@45.61.58.125
 SITE=games.agentabrams.com
 HERE="$(cd "$(dirname "$0")/.." && pwd)"
 
+# 0. Sync guard: rebuild games/<id>/ from their source projects BEFORE rsync so
+#    the live site can never ship a stale copy (sync.sh is otherwise manual —
+#    edit a source, forget to run it, and the old build would deploy). Fail-closed
+#    via set -e; ships whatever the current sources say.
+"$HERE/sync.sh"
+
 # 1. DNS: A record games -> Kamatera (DNS-only, matches fleet pattern)
 export $(grep -E "^CLOUDFLARE_API_TOKEN=" ~/Projects/secrets-manager/.env)
 ZONE=$(curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
diff --git a/sync.sh b/sync.sh
index e39fb0b..bdf6424 100755
--- a/sync.sh
+++ b/sync.sh
@@ -1,11 +1,50 @@
 #!/bin/bash
 # Reconcile standalone source builds -> this topic hub (games/<id>/).
 # Mapping lives in sources.tsv: "<hub-id>\t<abs-source-project-path>".
-# Usage: ./sync.sh [--commit]
+# Usage: ./sync.sh [--commit|--check]
+#   (no flag)  copy source builds into games/<id>/ and regenerate games-data.js
+#   --commit   same, then git-commit the hub if anything changed
+#   --check    NON-MUTATING drift detector: exit 3 if any deployed copy or the
+#              games-data.js mirror is out of sync with source (for canaries/CI)
 set -euo pipefail
 HUB="$(cd "$(dirname "$0")" && pwd)"
 MAP="$HUB/sources.tsv"
 [ -f "$MAP" ] || { echo "no sources.tsv"; exit 0; }
+
+if [ "${1:-}" = "--check" ]; then
+  drift=0
+  while IFS=$'\t' read -r id src; do
+    [ -z "${id:-}" ] && continue
+    case "$id" in \#*) continue;; esac
+    src="${src/#\~/$HOME}"
+    dest="$HUB/games/$id"
+    [ -d "$src" ] || { echo "! source missing for $id: $src"; drift=1; continue; }
+    if ! cmp -s "$src/index.html" "$dest/index.html" 2>/dev/null; then
+      echo "DRIFT: games/$id/index.html differs from $src/index.html"; drift=1
+    fi
+  done < "$MAP"
+  # games-data.js must equal a fresh regen of games.json (lockstep mirror)
+  if [ -f "$HUB/games.json" ]; then
+    tmp="$(mktemp)"
+    python3 - "$HUB" "$tmp" <<'PY'
+import json, os, sys
+hub, out = sys.argv[1], sys.argv[2]
+d = json.load(open(os.path.join(hub, 'games.json')))
+with open(out, 'w') as f:
+    f.write('/* AUTO-GENERATED from games.json by sync.sh — do not edit by hand.\n')
+    f.write('   file:// fallback: <script src> works from a local file, fetch() does not. */\n')
+    f.write('window.__GAMES_DATA__ = ')
+    json.dump(d, f, ensure_ascii=False, indent=2)
+    f.write(';\n')
+PY
+    if ! cmp -s "$tmp" "$HUB/games-data.js"; then
+      echo "DRIFT: games-data.js is stale vs games.json (run ./sync.sh)"; drift=1
+    fi
+    rm -f "$tmp"
+  fi
+  [ "$drift" = 0 ] && { echo "check: all in sync"; exit 0; } || { echo "check: DRIFT detected"; exit 3; }
+fi
+
 changed=0
 while IFS=$'\t' read -r id src; do
   [ -z "${id:-}" ] && continue

← c13d678 hopper + tower-stack: fix desktop scale  ·  back to Games Agentabrams  ·  auto-save: 2026-07-25T11:35:57 (1 files) — sync.sh ddb5d46 →