[object Object]

← back to Norma

IG cadence: close carousel room-image dedup gap (Cody gate) — ledger now records ALL slides + dedup skips candidates whose room OR swatch was already posted, so room photos no longer repeat across accounts

4b58e973dcb916d71572979f304aff57c2d99271 · 2026-08-15 08:05:04 -0700 · Steve

Files touched

Diff

commit 4b58e973dcb916d71572979f304aff57c2d99271
Author: Steve <steve@designerwallcoverings.com>
Date:   Sat Aug 15 08:05:04 2026 -0700

    IG cadence: close carousel room-image dedup gap (Cody gate) — ledger now records ALL slides + dedup skips candidates whose room OR swatch was already posted, so room photos no longer repeat across accounts
---
 agents/instagram-agent/daily-cadence.js | 12 ++++++++++--
 agents/instagram-agent/post-to.js       |  3 +++
 2 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/agents/instagram-agent/daily-cadence.js b/agents/instagram-agent/daily-cadence.js
index 9a082e7..6ca2ff5 100644
--- a/agents/instagram-agent/daily-cadence.js
+++ b/agents/instagram-agent/daily-cadence.js
@@ -57,7 +57,14 @@ const everPosted = new Set(rows.map((r) => r.product_handle)); // avoid reusing
 // Also dedup by IMAGE identity (filename), not just handle — several handles can share one image
 // (e.g. the "Quick Ship Durable Walls" variants), which was silently creating same-image duplicates.
 const imgKey = (u) => String(u || '').split('/').pop().split('?')[0].toLowerCase();
-const everPostedImg = new Set(rows.map((r) => imgKey(r.image_url)).filter(Boolean));
+// Build from ALL published slides (carousel room + swatch), not just the swatch — else the room
+// photo repeats across accounts, unguarded (Cody gate, /yoloforever cycle 1).
+const everPostedImg = new Set();
+for (const r of rows) {
+  everPostedImg.add(imgKey(r.image_url));
+  String(r.images || '').split(',').forEach((u) => everPostedImg.add(imgKey(u)));
+}
+everPostedImg.delete('');
 
 function themeFor(h) {
   const o = themes[h] || {};
@@ -79,7 +86,8 @@ async function tryCandidates(list) {
     if (!p.handle || everPosted.has(p.handle)) continue; // don't repost a product
     try {
       const r = await content.resolveProduct(p.handle); // throws on leak/no-image (guard) — UNCHANGED
-      if (everPostedImg.has(imgKey(r.image_url))) continue; // same image already posted — skip (no dup)
+      // Skip if ANY slide (room OR swatch) was already posted — not just the swatch.
+      if ((r.images || [r.image_url]).some((u) => everPostedImg.has(imgKey(u)))) continue;
       return { handle: p.handle, title: r.title, image_url: r.image_url };
     } catch { /* leak or imageless — next */ }
   }
diff --git a/agents/instagram-agent/post-to.js b/agents/instagram-agent/post-to.js
index bf91c37..ebe5f3e 100644
--- a/agents/instagram-agent/post-to.js
+++ b/agents/instagram-agent/post-to.js
@@ -254,6 +254,9 @@ async function main() {
             ts: new Date().toISOString(), handle: t.handle, page_name: t.page_name || null,
             product_handle: args._productHandle || null, product_title: args._productTitle || null,
             image_url: args._imageUrl || args.image || null,
+            // ALL published slides (carousel = room + swatch), so the cadence's image-dedup guard
+            // sees the room photo too — not just the swatch (Cody gate, /yoloforever cycle 1).
+            images: args.images || args._imageUrl || args.image || null,
             permalink: r.permalink || null, media_id: r.media_id || null, kind: r.kind || null,
           }) + '\n');
         } catch (_) { /* ledger is best-effort; never block a post */ }

← 43de9ea Norma IG cadence: fresh-source via Boost API (fixes clean-pr  ·  back to Norma  ·  Norma IG cadence: 8s timeout on Boost fetch (Cody FIX-FIRST, 0ff33f9 →