[object Object]

← back to Dw Marketing Reels

publish reel media before Instagram handoff

fab601724631140d53bf353d6b33440bd4c37e77 · 2026-08-29 17:29:34 -0700 · Steve Abrams

Files touched

Diff

commit fab601724631140d53bf353d6b33440bd4c37e77
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sat Aug 29 17:29:34 2026 -0700

    publish reel media before Instagram handoff
---
 scripts/cron-run.sh         | 18 ++++++++++++++++--
 scripts/publish-social.mjs  |  7 ++++++-
 verification/e2e-proof.json | 38 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 60 insertions(+), 3 deletions(-)

diff --git a/scripts/cron-run.sh b/scripts/cron-run.sh
index 9a6ea6f..bef6187 100755
--- a/scripts/cron-run.sh
+++ b/scripts/cron-run.sh
@@ -14,6 +14,22 @@ node scripts/fetch-new-arrivals.mjs && node scripts/build-reel.mjs || exit 1
 # leaks" check into a standing gate.
 node scripts/scan-leaks.mjs || { echo "LEAK GATE FAILED — aborting publish+push"; exit 1; }
 
+# Meta pulls `video_url` from the public marketing host while processing the container. The old
+# order called Norma BEFORE rsync, so every newly-rendered filename was still a public 404 and Meta
+# returned 2207076. When live IG is armed, fail closed unless the media is public before publish.
+PUSH_HOST="${PUSH_HOST:-root@45.61.58.125}"
+PUSH_PATH="${PUSH_PATH:-/root/public-projects/dw-marketing-reels}"
+if [ "${SOCIAL_LIVE_ARMED:-0}" = "1" ]; then
+  if ssh -o BatchMode=yes -o ConnectTimeout=6 "$PUSH_HOST" "test -d $PUSH_PATH" 2>/dev/null; then
+    timeout 120 rsync -az reels/ "$PUSH_HOST:$PUSH_PATH/reels/" \
+      || { echo "PRE-PUBLISH MEDIA PUSH FAILED — refusing live post with a 404 URL"; exit 1; }
+    echo "pre-published reel media -> $PUSH_HOST:$PUSH_PATH/reels/"
+  else
+    echo "PRE-PUBLISH HOST UNREACHABLE — refusing live post with a 404 URL"
+    exit 1
+  fi
+fi
+
 # auto-post (Steve: auto-post on the nightly schedule). Runs on Mac3 where Norma/tiktok live.
 SOCIAL_AUTOPOST="${SOCIAL_AUTOPOST:-1}" node scripts/publish-social.mjs
 
@@ -43,8 +59,6 @@ fi
 
 # Push to the Kamatera console. Bounded so a full/hung host can't wedge the whole job (and can't
 # suppress the gate above, which already ran). rsync gets its own timeout + non-fatal guard.
-PUSH_HOST="${PUSH_HOST:-root@45.61.58.125}"
-PUSH_PATH="${PUSH_PATH:-/root/public-projects/dw-marketing-reels}"
 if ssh -o BatchMode=yes -o ConnectTimeout=6 "$PUSH_HOST" "test -d $PUSH_PATH" 2>/dev/null; then
   timeout 120 rsync -az reels/ "$PUSH_HOST:$PUSH_PATH/reels/" || echo "reels rsync failed/timed out (non-fatal)"
   timeout 120 rsync -az data/  "$PUSH_HOST:$PUSH_PATH/data/"  || echo "data rsync failed/timed out (non-fatal)"
diff --git a/scripts/publish-social.mjs b/scripts/publish-social.mjs
index a8714a8..b8c0857 100644
--- a/scripts/publish-social.mjs
+++ b/scripts/publish-social.mjs
@@ -64,7 +64,12 @@ async function postInstagram(reel) {
   if (acc.status !== 'ready') return { status: 'pending-creds', account: acc.id, note: `${tag} not connected — wire creds + set status "ready" in ig-accounts.json`, at: now() };
   if (!auth) return { status: 'pending-creds', account: acc.id, note: `${tag}: IG_AGENT_AUTH${acc.id === 'dw' ? '' : '_' + acc.id.toUpperCase().replace(/-/g, '_')} not set (route via secrets skill)`, at: now() };
   if (!ARMED) return { status: 'ready-pending-arm', account: acc.id, note: `${tag} creds present; set SOCIAL_LIVE_ARMED=1 (Steve arm) to enable the live post`, at: now() };
-  const mediaUrl = `${PUBLIC_BASE}/reels/${encodeURIComponent(reel.file)}`;
+  // A newly rendered filename can be requested by Meta milliseconds before rsync completes. The
+  // public Cloudflare layer caches that 404 for hours, so even after the file is uploaded Meta's
+  // edge may keep failing container processing with 2207076. Version the fetch URL from the stable
+  // reel creation timestamp to bypass any negative-cache entry without changing the media asset.
+  const mediaVersion = encodeURIComponent(reel.created_at || reel.file);
+  const mediaUrl = `${PUBLIC_BASE}/reels/${encodeURIComponent(reel.file)}?v=${mediaVersion}`;
   try {
     const res = await withTimeout(fetch(`${acc.agentUrl || IG_URL}/api/skill/reel`, {
       method: 'POST',
diff --git a/verification/e2e-proof.json b/verification/e2e-proof.json
new file mode 100644
index 0000000..cabbd0b
--- /dev/null
+++ b/verification/e2e-proof.json
@@ -0,0 +1,38 @@
+{
+  "intent": "Publish the retained Designer Wallcoverings reel once through Norma to Instagram after proving the public media URL is fetchable.",
+  "risk_tier": "R4",
+  "environment": "production Instagram via Norma at 100.82.17.107:9810; public media at marketing.designerwallcoverings.com",
+  "build_identity": {
+    "baseline_commit": "78de2e06f41af1f5ab4b427a826b1975b8f71e12",
+    "reel": "new-arrivals-2026-08-29-21-07.mp4"
+  },
+  "timestamp": "2026-08-30T00:29:10Z",
+  "approval": "Steve explicitly approved the surfaced single live Instagram proof with 'go'.",
+  "baseline": {
+    "previous_state": "instagram error 2207076 after Meta fetched the media URL before rsync completed",
+    "public_asset": "HTTP 200, video/mp4, 33332040 bytes on a cache-busted Cloudflare MISS",
+    "auth": "Norma status authenticated successfully using the canonically routed IG_AGENT_AUTH secret"
+  },
+  "commands": [
+    "node --check scripts/publish-social.mjs",
+    "zsh -n scripts/cron-run.sh",
+    "git diff --check",
+    "curl -fsSI https://marketing.designerwallcoverings.com/reels/new-arrivals-2026-08-29-21-07.mp4?v=2026-08-29T21%3A07%3A47.172Z",
+    "SOCIAL_AUTOPOST=1 node scripts/publish-social.mjs",
+    "node scripts/check-publish-status.mjs"
+  ],
+  "assertions": {
+    "local_media": { "verdict": "PASS", "detail": "Existing H264/AAC 1080x1920 MP4 retained; no second render." },
+    "public_media": { "verdict": "PASS", "detail": "Cache-busted URL returned HTTP 200, video/mp4, 33332040 bytes, cf-cache-status MISS." },
+    "authentication": { "verdict": "PASS", "detail": "Norma accepted the shell-sourced Basic credential after secrets-manager quoting fix." },
+    "instagram_publish": { "verdict": "PASS", "detail": "Persisted status posted; postId 18121685866895707 at 2026-08-29T21:19:05.857Z." },
+    "independent_status": { "verdict": "PASS", "detail": "check-publish-status.mjs reported instagram: posted." },
+    "negative_gate": { "verdict": "PASS", "detail": "TikTok remained ready-pending-tiktok-arm; no TikTok live post." },
+    "duplicate_control": { "verdict": "PASS", "detail": "Exactly one publisher-only cache-busted retry was issued; no new reel was rendered." }
+  },
+  "side_effects": {
+    "retained": "Instagram post 18121685866895707 and the public reel asset are intentionally retained.",
+    "cleanup": "No temporary social post or duplicate container was created by this proof."
+  },
+  "verdict": "PASS"
+}

← 78de2e0 auto-data-snapshot: 2026-08-29T14:38:58 (1 data files) — dat  ·  back to Dw Marketing Reels  ·  auto-data-snapshot: 2026-08-30T07:15:22 (3 data files) — dat e1c1a20 →