← back to Majilite Jewelry Cases
scripts/tk11086-ig-delete-heldset2.mjs
105 lines
#!/usr/bin/env node
// TK-11086 HELD SET #2 — IRREVERSIBLE Instagram deletion of the 7 Rodeo Drive
// jewelry-store reels on @beverlyhillsvideos that matched ONLY on the word
// "jewelry" (why_flagged == ["caption:jewelry-terms"]).
// Steve answered the final clarify gate: NO to keeping them -> DELETE.
//
// Deletes ONLY the exact 7 media IDs in data/tk11086-ig-heldset2-targets.jsonl.
// DELETE graph.facebook.com/v21.0/{ig-media-id}; verifies each with a follow-up
// GET (expects "Unsupported get request"). Every deletion logged to:
// - data/tk11086-ig-deleted-audit.jsonl (per-delete audit record)
// - ~/.claude/yolo-queue/executed-reversible/ledger.jsonl
// (undo = "IRREVERSIBLE — Steve-authorized IG deletion TK-11086 held-set-2")
// SAFETY: DRY-RUN by default; real deletes require --apply. Hard-caps at 7 rows.
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { homedir } from 'node:os';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.resolve(__dirname, '..');
const V = 'https://graph.facebook.com/v21.0';
const args = process.argv.slice(2);
const APPLY = args.includes('--apply');
function token() {
if (process.env.IG_ACCESS_TOKEN) return process.env.IG_ACCESS_TOKEN;
const p = path.join(homedir(), 'Projects/Norma/agents/instagram-agent/.env');
const m = fs.readFileSync(p, 'utf8').match(/^IG_ACCESS_TOKEN=(.+)$/m);
return m ? m[1].trim() : null;
}
const TOK = token();
if (!TOK) { console.error('no IG_ACCESS_TOKEN'); process.exit(1); }
const TARGETS = path.join(ROOT, 'data/tk11086-ig-heldset2-targets.jsonl');
const AUDIT = path.join(ROOT, 'data/tk11086-ig-deleted-audit.jsonl');
const LEDGER = path.join(homedir(), '.claude/yolo-queue/executed-reversible/ledger.jsonl');
const rows = fs.readFileSync(TARGETS, 'utf8').trim().split('\n').map(l => JSON.parse(l));
// RAIL: exact held set only — refuse to widen.
if (rows.length !== 7) { console.error(`REFUSING: expected 7 targets, got ${rows.length}`); process.exit(1); }
for (const r of rows) {
if (r.account !== 'beverlyhillsvideos' || JSON.stringify(r.why_flagged) !== JSON.stringify(['caption:jewelry-terms'])) {
console.error(`REFUSING: target ${r.ig_media_id} is not a held-set-2 jewelry reel`); process.exit(1);
}
}
const sleep = ms => new Promise(r => setTimeout(r, ms));
console.log(`Held-set-2 targets: ${rows.length} | mode: ${APPLY ? 'APPLY (LIVE IRREVERSIBLE DELETE)' : 'DRY-RUN (no deletes)'}`);
let deleted = 0, gone = 0, err = 0;
for (const r of rows) {
if (!APPLY) { console.log(` + WOULD delete ${r.account} ${r.ig_media_id} — ${r.caption_snippet.slice(0,40)}`); continue; }
const url = `${V}/${r.ig_media_id}?access_token=${encodeURIComponent(TOK)}`;
let res, body;
try {
res = await fetch(url, { method: 'DELETE' });
body = await res.text();
} catch (e) { console.log(` ! ${r.account} ${r.ig_media_id}: fetch error ${e.message}`); err++; await sleep(400); continue; }
let d; try { d = JSON.parse(body); } catch { d = { raw: body }; }
const success = d && d.success === true;
const alreadyGone = !success && /does not exist|Unsupported.*request|cannot be loaded/i.test(body);
// Follow-up GET verify (expects "Unsupported get request" once gone)
let verifyResult = 'skipped';
if (success || alreadyGone) {
await sleep(300);
try {
const gr = await fetch(`${V}/${r.ig_media_id}?fields=id&access_token=${encodeURIComponent(TOK)}`);
const gt = await gr.text();
verifyResult = /Unsupported get request|does not exist|cannot be loaded/i.test(gt) ? 'confirmed-gone' : `still-present:${gt.slice(0,80)}`;
} catch (e) { verifyResult = `verify-fetch-error:${e.message}`; }
}
const rec = {
ts: new Date().toISOString(),
account: r.account,
ig_user_id: r.ig_user_id,
ig_media_id: r.ig_media_id,
posted_date: r.posted_date,
media_type: r.media_type,
caption_snippet: r.caption_snippet,
permalink: r.permalink,
why_flagged: r.why_flagged,
held_set: 2,
delete_result: success ? 'success' : (alreadyGone ? 'already-gone' : 'error'),
get_verify: verifyResult,
graph_response: d,
};
fs.appendFileSync(AUDIT, JSON.stringify(rec) + '\n');
if (success) {
deleted++;
console.log(` ✓ deleted ${r.account} ${r.ig_media_id} | GET-verify: ${verifyResult}`);
fs.appendFileSync(LEDGER, JSON.stringify({
ts: rec.ts, agent: 'vp-dw-marketing', ticket: 'TK-11086',
action: `IG DELETE ${r.account} media ${r.ig_media_id} (${r.posted_date}) — Rodeo Drive jewelry reel (held-set-2)`,
blast_radius: 1,
undo_cmd: 'IRREVERSIBLE — Steve-authorized IG deletion TK-11086 held-set-2',
verify: `GET ${V}/${r.ig_media_id} returns "Unsupported get request" (${verifyResult})`,
}) + '\n');
} else if (alreadyGone) { gone++; console.log(` = ${r.account} ${r.ig_media_id}: already gone | GET-verify: ${verifyResult}`); }
else { err++; console.log(` ! ${r.account} ${r.ig_media_id}: ${JSON.stringify(d).slice(0,140)}`); }
await sleep(400);
}
console.log(`\n=== HELD-SET-2 RESULT === deleted=${deleted} already-gone=${gone} errors=${err} audit=${AUDIT}`);