[object Object]

← back to Designerwallcoverings

TK-11238: staged reversible Matka stray-orphan fixes (archive + title-suffix, dry-run verified, gated apply)

b90d16f53be6117ff5821520c171da00a7ad8341 · 2026-09-10 06:00:06 -0700 · Steve Abrams

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JNgURsoJm4vppNLpW3B8WT

Files touched

Diff

commit b90d16f53be6117ff5821520c171da00a7ad8341
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Sep 10 06:00:06 2026 -0700

    TK-11238: staged reversible Matka stray-orphan fixes (archive + title-suffix, dry-run verified, gated apply)
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01JNgURsoJm4vppNLpW3B8WT
---
 scripts/tk11238-matka-archive-stray.mjs | 37 +++++++++++++++++++++++++++++++++
 scripts/tk11238-matka-title-fix.mjs     | 34 ++++++++++++++++++++++++++++++
 2 files changed, 71 insertions(+)

diff --git a/scripts/tk11238-matka-archive-stray.mjs b/scripts/tk11238-matka-archive-stray.mjs
new file mode 100644
index 0000000..46bc455
--- /dev/null
+++ b/scripts/tk11238-matka-archive-stray.mjs
@@ -0,0 +1,37 @@
+#!/usr/bin/env node
+// TK-11238 — archive the sourceless/colorless/redundant "Matka - Single Color" orphan (7800007786547).
+// The 12 real Matka colorways (A187-XXX) are already ACTIVE + live; this bare-A187 base has no source
+// row and no color, so it's a stray. Reversible: saves prev status; rollback re-activates.
+// SAFE: dry-run by default; customer-facing status write only with --apply (Steve gated). --rollback reverts.
+import { readFileSync, writeFileSync, existsSync } from 'node:fs';
+const PID = '7800007786547';
+const SHOP = 'designer-laboratory-sandbox.myshopify.com';
+const API = '2024-10';
+const UNDO = `${import.meta.dirname}/tk11238-matka-archive-undo.json`;
+const TOKEN = (readFileSync(`${process.env.HOME}/Projects/secrets-manager/.env`,'utf8')
+  .match(/^SHOPIFY_FULL_ACCESS_TOKEN=(.+)$/m)||[])[1]?.trim().replace(/^['"]|['"]$/g,'');
+if (!TOKEN) { console.error('no SHOPIFY_FULL_ACCESS_TOKEN'); process.exit(2); }
+const H = { 'X-Shopify-Access-Token': TOKEN, 'Content-Type':'application/json' };
+const ROLLBACK = process.argv.includes('--rollback');
+const APPLY = process.argv.includes('--apply');
+
+const cur = await (await fetch(`https://${SHOP}/admin/api/${API}/products/${PID}.json?fields=id,title,status`, {headers:H})).json();
+if (!cur.product) { console.error('product not found'); process.exit(2); }
+console.log('product:', PID, '|', JSON.stringify(cur.product.title), '| status:', cur.product.status);
+
+if (ROLLBACK) {
+  if (!existsSync(UNDO)) { console.error('no undo file'); process.exit(2); }
+  const prev = JSON.parse(readFileSync(UNDO,'utf8')).prev_status || 'active';
+  if (!APPLY) { console.log(`DRY-RUN rollback → would set status=${prev}. Re-run with --apply.`); process.exit(0); }
+  const r = await (await fetch(`https://${SHOP}/admin/api/${API}/products/${PID}.json`, {method:'PUT',headers:H,body:JSON.stringify({product:{id:PID,status:prev}})})).json();
+  console.log('rolled back → status:', r.product?.status); process.exit(0);
+}
+
+const TARGET = 'archived';
+if (cur.product.status === TARGET) { console.log('already archived — nothing to do.'); process.exit(0); }
+console.log('FROM status:', cur.product.status, '→ TO:', TARGET);
+if (!APPLY) { console.log('\nDRY-RUN — nothing written. Re-run with --apply (Steve gated).'); process.exit(0); }
+writeFileSync(UNDO, JSON.stringify({pid:PID, prev_status:cur.product.status, ts:new Date().toISOString()}, null, 2));
+const r = await (await fetch(`https://${SHOP}/admin/api/${API}/products/${PID}.json`, {method:'PUT',headers:H,body:JSON.stringify({product:{id:PID,status:TARGET}})})).json();
+if (r.product?.status === TARGET) console.log('\nARCHIVED. undo saved:', UNDO, '\nrollback: node', import.meta.filename, '--rollback --apply');
+else { console.error('WRITE FAILED:', JSON.stringify(r).slice(0,300)); process.exit(1); }
diff --git a/scripts/tk11238-matka-title-fix.mjs b/scripts/tk11238-matka-title-fix.mjs
new file mode 100644
index 0000000..c4e1238
--- /dev/null
+++ b/scripts/tk11238-matka-title-fix.mjs
@@ -0,0 +1,34 @@
+#!/usr/bin/env node
+// TK-11238 — fix the ONE remaining defective Hollywood title (Matka twin, product 7800007786547).
+// Suffix-only per memo revised rec: fix the wrong brand suffix + match sibling "Type II Vinyl"
+// convention. Colorway placeholder "Single Color" is LEFT AS-IS (real color not sourceable without
+// fabrication — needs a separate data lookup). Reversible: prints + saves the old title first.
+// SAFE: dry-run by default; customer-facing Shopify write only with --apply (Steve's gated ! run).
+import { readFileSync, writeFileSync } from 'node:fs';
+const PID = '7800007786547';
+const SHOP = 'designer-laboratory-sandbox.myshopify.com';
+const API = '2024-10';
+const NEW_TITLE = 'Matka - Single Color Type II Vinyl Wallcovering | Hollywood Wallcoverings';
+const TOKEN = (readFileSync(`${process.env.HOME}/Projects/secrets-manager/.env`,'utf8')
+  .match(/^SHOPIFY_FULL_ACCESS_TOKEN=(.+)$/m)||[])[1]?.trim().replace(/^['"]|['"]$/g,'');
+if (!TOKEN) { console.error('no SHOPIFY_FULL_ACCESS_TOKEN'); process.exit(2); }
+const H = { 'X-Shopify-Access-Token': TOKEN, 'Content-Type':'application/json' };
+const APPLY = process.argv.includes('--apply');
+
+const cur = await (await fetch(`https://${SHOP}/admin/api/${API}/products/${PID}.json?fields=id,title,vendor`, {headers:H})).json();
+const old = cur.product?.title;
+if (!old) { console.error('product not found'); process.exit(2); }
+console.log('product :', PID, '| vendor:', cur.product.vendor);
+console.log('FROM    :', JSON.stringify(old));
+console.log('TO      :', JSON.stringify(NEW_TITLE));
+if (old === NEW_TITLE) { console.log('already correct — nothing to do.'); process.exit(0); }
+if (!APPLY) { console.log('\nDRY-RUN — nothing written. Re-run with --apply (Steve gated).'); process.exit(0); }
+
+// reversibility: save old title before writing
+const undo = `${import.meta.dirname}/tk11238-matka-title-undo.json`;
+writeFileSync(undo, JSON.stringify({pid:PID, old_title:old, ts:new Date().toISOString()}, null, 2));
+const r = await fetch(`https://${SHOP}/admin/api/${API}/products/${PID}.json`, {
+  method:'PUT', headers:H, body: JSON.stringify({product:{id:PID, title:NEW_TITLE}}) });
+const j = await r.json();
+if (j.product?.title === NEW_TITLE) console.log('\nAPPLIED. undo saved:', undo, '\nrollback: node', import.meta.filename, '--rollback');
+else { console.error('WRITE FAILED:', JSON.stringify(j).slice(0,300)); process.exit(1); }

← 53a3f8b auto-data-snapshot: 2026-09-10T05:55:20 (1 data files) — dat  ·  back to Designerwallcoverings  ·  TK-11357: reversible qty->0 stopgap for NEW canary-flagged 1 250a2bf →