← back to Dw Marketing Reels
publish reel media before Instagram handoff
fab601724631140d53bf353d6b33440bd4c37e77 · 2026-08-29 17:29:34 -0700 · Steve Abrams
Files touched
M scripts/cron-run.shM scripts/publish-social.mjsA verification/e2e-proof.json
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 →