← back to Dw Marketing Reels
Fix publish-status unwrap: read Norma's .result envelope + explicit r.simulated (kills false 'posted' on no-creds + false 'simulated' on live post) + no-creds badge — contrarian gate
d02c9bb0a94ed092f9612bac938381cd9f2d7ce5 · 2026-07-13 10:29:15 -0700 · Steve Abrams
Files touched
M public/index.htmlM scripts/publish-social.mjs
Diff
commit d02c9bb0a94ed092f9612bac938381cd9f2d7ce5
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon Jul 13 10:29:15 2026 -0700
Fix publish-status unwrap: read Norma's .result envelope + explicit r.simulated (kills false 'posted' on no-creds + false 'simulated' on live post) + no-creds badge — contrarian gate
---
public/index.html | 1 +
scripts/publish-social.mjs | 12 ++++++++++--
2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/public/index.html b/public/index.html
index 606d9da..e0dd198 100644
--- a/public/index.html
+++ b/public/index.html
@@ -100,6 +100,7 @@ const escapeHtml = s => String(s||'').replace(/[&<>]/g,c=>({'&':'&','<':'<
const escapeAttr = s => String(s||'').replace(/"/g,'"').replace(/\n/g,' ');
const STMAP = { posted:['✓ posted','ok'], simulated:['◐ simulated','sim'], 'pending-creds':['◷ needs creds','wait'],
'pending-reconnect':['◷ reconnect','wait'], 'ready-pending-arm':['🔒 armed?','sim'], 'pending-review':['👁 review','sim'],
+ 'no-creds-for-account':['⚠ no acct creds','err'],
pending:['◷ pending','wait'], error:['✗ error','err'], skipped:['– skipped','wait'] };
function pubBadges(pub){
if(!pub) return '';
diff --git a/scripts/publish-social.mjs b/scripts/publish-social.mjs
index 84ffb7e..645b858 100644
--- a/scripts/publish-social.mjs
+++ b/scripts/publish-social.mjs
@@ -67,8 +67,16 @@ async function postInstagram(reel) {
const txt = await res.text().catch(() => '');
let body = {}; try { body = JSON.parse(txt); } catch {}
if (!res.ok) return { status: res.status === 401 ? 'pending-creds' : 'error', account: acc.id, http: res.status, at: now() };
- const simulated = body.simulated || /simulat/i.test(txt);
- return { status: simulated ? 'simulated' : 'posted', account: acc.id, postId: body.id || body.postId || null, at: now() };
+ // Norma wraps the skill output as { success, skill, result } — the real status/ids
+ // live on .result, NOT the envelope. Reading the envelope reported a false 'posted'
+ // for a no-creds account (contrarian-caught). Unwrap, then honor the skill's verdict.
+ const r = body.result || body;
+ if (r.status === 'no-creds-for-account') return { status: 'no-creds-for-account', account: acc.id, note: r.note, at: now() };
+ // r.simulated is an explicit boolean from Norma's reel skill — read it directly.
+ // (The old /simulat/i text-regex falsely matched `"simulated":false`, mislabeling
+ // a real live post as 'simulated' — contrarian-adjacent bug, fixed with the unwrap.)
+ const simulated = r.simulated === true;
+ return { status: simulated ? 'simulated' : 'posted', account: acc.id, postId: r.media_id || r.id || r.postId || null, at: now() };
} catch (e) { return { status: 'error', account: acc.id, note: e.message, at: now() }; }
}
← fea25bd chore: v0.3.0 — multi-account IG picker + meta-token canary
·
back to Dw Marketing Reels
·
Fix console port default 9846->9848 (matches .deploy.conf + 66cb716 →