[object Object]

← back to Dw Marketing Reels

reels IG roster: demote unreachable @grassclothwallpaper to needs-relink (TK-10447)

099834dc92cb029f10700e22dc4aa8c355b75921 · 2026-08-25 11:38:32 -0700 · Steve Abrams

Empirical Graph API verification (read-only, $0) of all 35 'ready' accounts on the
shared durable META token found 34 resolve and 1 does not: ig_user_id 17841408868631110
(@grassclothwallpaper) returns 'Object does not exist / missing permissions' while the
account still exists publicly (HTTP 200) — unlinked from the Page/Business or stale ID.
Added a NEEDS_RELINK manifest to the generator so it's durably demoted to status
'needs-relink' (was falsely 'ready'), preventing a silent publish failure. Roster is now
34 verified-postable + 1 needs-relink + 5 aspirational. Re-link is a gated Meta action.

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

Files touched

Diff

commit 099834dc92cb029f10700e22dc4aa8c355b75921
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Aug 25 11:38:32 2026 -0700

    reels IG roster: demote unreachable @grassclothwallpaper to needs-relink (TK-10447)
    
    Empirical Graph API verification (read-only, $0) of all 35 'ready' accounts on the
    shared durable META token found 34 resolve and 1 does not: ig_user_id 17841408868631110
    (@grassclothwallpaper) returns 'Object does not exist / missing permissions' while the
    account still exists publicly (HTTP 200) — unlinked from the Page/Business or stale ID.
    Added a NEEDS_RELINK manifest to the generator so it's durably demoted to status
    'needs-relink' (was falsely 'ready'), preventing a silent publish failure. Roster is now
    34 verified-postable + 1 needs-relink + 5 aspirational. Re-link is a gated Meta action.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 data/ig-accounts.json        |  4 ++--
 scripts/build-ig-accounts.js | 46 +++++++++++++++++++++++++++++++-------------
 2 files changed, 35 insertions(+), 15 deletions(-)

diff --git a/data/ig-accounts.json b/data/ig-accounts.json
index 1ae3468..1cb3d4a 100644
--- a/data/ig-accounts.json
+++ b/data/ig-accounts.json
@@ -151,9 +151,9 @@
     "ig_user_id": "17841408868631110",
     "agentUrl": null,
     "token_source": "shared-meta-durable",
-    "status": "ready",
+    "status": "needs-relink",
     "source": "canonical-postable",
-    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+    "note": "Graph GET of stored ig_user_id 17841408868631110 fails on the shared token (unlinked from Page/Business or stale ID); @grassclothwallpaper still exists publicly (HTTP 200). Re-link in Meta Business Manager to restore."
   },
   {
     "id": "ig-hollywoodwallcoverings",
diff --git a/scripts/build-ig-accounts.js b/scripts/build-ig-accounts.js
index e46a9d9..1efbf5b 100644
--- a/scripts/build-ig-accounts.js
+++ b/scripts/build-ig-accounts.js
@@ -30,6 +30,19 @@ const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
 const CANON = join(ROOT, 'data', 'dw-canonical-accounts.json');
 const OUT = join(ROOT, 'data', 'ig-accounts.json');
 
+// Postable-BY-HANDLE accounts whose IG account is NO LONGER reachable on the shared
+// durable page token — the account still exists publicly but is not (or no longer)
+// linked to the Page/Business this token authorizes, OR the recorded ig_user_id is
+// stale. Empirically confirmed 2026-08-25 (TK-10447): a read-only Graph GET of the
+// stored ig_user_id returns "Object does not exist / missing permissions" while the
+// other 34 resolve on the SAME token. Demoted to status:'needs-relink' so the console
+// does not advertise it as postable and publish-ig.mjs won't silently fail on it.
+// Fix = re-link the IG account to the Page in Meta Business Manager (or capture the
+// correct linked ig_user_id) — a gated Meta identity action. Keyed by handle.
+const NEEDS_RELINK = {
+  grassclothwallpaper: 'Graph GET of stored ig_user_id 17841408868631110 fails on the shared token (unlinked from Page/Business or stale ID); @grassclothwallpaper still exists publicly (HTTP 200). Re-link in Meta Business Manager to restore.',
+};
+
 // Brands with NO postable Instagram account yet (handle NOT among the 35 postable).
 // These stay pending-creds — the gated work is: create an IG Business account + link
 // it to the Meta Business so the shared token covers it. Keyed by handle (or a synthetic
@@ -59,18 +72,23 @@ const idFor = handle => handle === 'designerwallcoverings'
   ? 'dw'
   : 'ig-' + String(handle).toLowerCase().replace(/[^a-z0-9-]+/g, '-');
 
-const ready = postable.map(a => ({
-  id: idFor(a.handle),
-  label: a.name || a.handle,
-  handle: a.handle,
-  business: `${a.handle} · Instagram (Meta Graph)`,
-  ig_user_id: a.ig_user_id || null,
-  agentUrl: null,                       // published via scripts/publish-ig.mjs, not the HTTP agent
-  token_source: 'shared-meta-durable',  // one never-expiring META page token covers all 35
-  status: a.ig_user_id ? 'ready' : 'pending-creds',
-  source: 'canonical-postable',
-  note: 'Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed.',
-}));
+const ready = postable.map(a => {
+  const relink = NEEDS_RELINK[String(a.handle).toLowerCase()];
+  return {
+    id: idFor(a.handle),
+    label: a.name || a.handle,
+    handle: a.handle,
+    business: `${a.handle} · Instagram (Meta Graph)`,
+    ig_user_id: a.ig_user_id || null,
+    agentUrl: null,                       // published via scripts/publish-ig.mjs, not the HTTP agent
+    token_source: 'shared-meta-durable',  // one never-expiring META page token covers the postable set
+    status: relink ? 'needs-relink' : (a.ig_user_id ? 'ready' : 'pending-creds'),
+    source: 'canonical-postable',
+    note: relink
+      ? relink
+      : 'Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed.',
+  };
+});
 
 // Keep aspirational placeholders that don't collide (by handle) with a postable account.
 const postableHandles = new Set(postable.map(a => a.handle.toLowerCase()));
@@ -82,4 +100,6 @@ const out = [...ready, ...pending];
 const tmp = OUT + '.tmp';
 writeFileSync(tmp, JSON.stringify(out, null, 2) + '\n');
 renameSync(tmp, OUT);
-console.log(`wrote data/ig-accounts.json — ${ready.length} postable (ready) + ${pending.length} aspirational (pending-creds)`);
+const readyCount = out.filter(a => a.status === 'ready').length;
+const relinkCount = out.filter(a => a.status === 'needs-relink').length;
+console.log(`wrote data/ig-accounts.json — ${readyCount} postable (ready) + ${relinkCount} needs-relink + ${pending.length} aspirational (pending-creds)`);

← 71d633f chore: reels.json sync + version bump (session close: IG acc  ·  back to Dw Marketing Reels  ·  auto-data-snapshot: 2026-08-25T14:34:54 (1 data files) — dat 0537a8a →