← back to Designerwallcoverings
TK-11758: reversible CONTINUE->DENY fix tool for 6 $0 Latigo cork variants (dry-run verified)
6def336c2bfeaa6069d960bc2ca25913a04717ef · 2026-09-16 09:44:03 -0700 · Steve Abrams
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013Szengr4hMUDtYdw3jSCfA
Files touched
A scripts/tk-11758-cork-continue-fix/.gitignoreA scripts/tk-11758-cork-continue-fix/fix.mjs
Diff
commit 6def336c2bfeaa6069d960bc2ca25913a04717ef
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Sep 16 09:44:03 2026 -0700
TK-11758: reversible CONTINUE->DENY fix tool for 6 $0 Latigo cork variants (dry-run verified)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013Szengr4hMUDtYdw3jSCfA
---
scripts/tk-11758-cork-continue-fix/.gitignore | 1 +
scripts/tk-11758-cork-continue-fix/fix.mjs | 103 ++++++++++++++++++++++++++
2 files changed, 104 insertions(+)
diff --git a/scripts/tk-11758-cork-continue-fix/.gitignore b/scripts/tk-11758-cork-continue-fix/.gitignore
new file mode 100644
index 0000000..373f5f3
--- /dev/null
+++ b/scripts/tk-11758-cork-continue-fix/.gitignore
@@ -0,0 +1 @@
+snapshots/
diff --git a/scripts/tk-11758-cork-continue-fix/fix.mjs b/scripts/tk-11758-cork-continue-fix/fix.mjs
new file mode 100644
index 0000000..849634c
--- /dev/null
+++ b/scripts/tk-11758-cork-continue-fix/fix.mjs
@@ -0,0 +1,103 @@
+#!/usr/bin/env node
+/**
+ * TK-11758 — Latigo Real Cork | Phillipe Romano: 6 variants are $0.00 + inventoryPolicy=CONTINUE
+ * + availableForSale=true, i.e. checkout-orderable for FREE. Remedy per the canary's guidance:
+ * flip inventoryPolicy CONTINUE -> DENY on these 6 variants ONLY. Does NOT touch price.
+ * These products carry the `contact-for-price` tag — they are quote-only, not meant to be orderable.
+ *
+ * Modes:
+ * (no flag) DRY-RUN — read current state of the 6 variants, write a snapshot/restore-map,
+ * print exactly what --apply would change. Read-only. Safe. $0.
+ * --apply GATED — flip the 6 CONTINUE variants to DENY (canonical customer-facing Shopify write).
+ * Writes the snapshot FIRST, then mutates. Reversible via --undo.
+ * --undo Restore each variant's inventoryPolicy to the value recorded in the newest snapshot.
+ *
+ * Blast radius: exactly 6 variants, pinned by GID below. No price change, no publish, no send.
+ */
+import fs from 'node:fs';
+import path from 'node:path';
+import { gql } from '../lib/shopify.mjs';
+
+const VARIANT_GIDS = [
+ 'gid://shopify/ProductVariant/17829266522177',
+ 'gid://shopify/ProductVariant/17829266587713',
+ 'gid://shopify/ProductVariant/17829266653249',
+ 'gid://shopify/ProductVariant/17829266686017',
+ 'gid://shopify/ProductVariant/17829266751553',
+ 'gid://shopify/ProductVariant/17829267243073',
+];
+
+const MODE = process.argv.includes('--apply') ? 'apply'
+ : process.argv.includes('--undo') ? 'undo'
+ : 'dry-run';
+
+const HERE = path.dirname(new URL(import.meta.url).pathname);
+const SNAP_DIR = path.join(HERE, 'snapshots');
+fs.mkdirSync(SNAP_DIR, { recursive: true });
+
+async function readState() {
+ const q = `query($ids:[ID!]!){ nodes(ids:$ids){ ... on ProductVariant {
+ id sku price inventoryPolicy availableForSale product { id title status } } } }`;
+ const d = await gql(q, { ids: VARIANT_GIDS });
+ if (d?.__err) throw new Error('read failed: ' + JSON.stringify(d.__err).slice(0, 300));
+ return d.nodes.filter(Boolean);
+}
+
+function newestSnapshot() {
+ const files = fs.readdirSync(SNAP_DIR).filter(f => f.endsWith('.json')).sort();
+ if (!files.length) throw new Error('no snapshot to undo from — run dry-run or --apply first');
+ return JSON.parse(fs.readFileSync(path.join(SNAP_DIR, files.at(-1)), 'utf8'));
+}
+
+async function setPolicy(productId, variantId, policy) {
+ const m = `mutation($productId:ID!,$variants:[ProductVariantsBulkInput!]!){
+ productVariantsBulkUpdate(productId:$productId, variants:$variants){
+ productVariants { id inventoryPolicy } userErrors { field message } } }`;
+ const d = await gql(m, { productId, variants: [{ id: variantId, inventoryPolicy: policy }] });
+ if (d?.__err) throw new Error('mutation transport error: ' + JSON.stringify(d.__err).slice(0, 300));
+ const ue = d.productVariantsBulkUpdate.userErrors;
+ if (ue.length) throw new Error('userErrors: ' + JSON.stringify(ue));
+ return d.productVariantsBulkUpdate.productVariants[0].inventoryPolicy;
+}
+
+(async () => {
+ if (MODE === 'undo') {
+ const snap = newestSnapshot();
+ console.log(`UNDO — restoring inventoryPolicy from snapshot ${snap.ts}`);
+ for (const v of snap.variants) {
+ const now = await setPolicy(v.product.id, v.id, v.inventoryPolicy);
+ console.log(` ${v.id.split('/').pop()} -> restored ${now}`);
+ }
+ console.log('undo complete.');
+ return;
+ }
+
+ const state = await readState();
+ const ts = new Date().toISOString().replace(/[:.]/g, '-');
+ const snapFile = path.join(SNAP_DIR, `snapshot-${ts}.json`);
+ fs.writeFileSync(snapFile, JSON.stringify({ ts, mode: MODE, variants: state }, null, 2));
+
+ console.log(`TK-11758 — ${MODE.toUpperCase()} — 6 Latigo Real Cork variants`);
+ console.log(`snapshot saved: ${snapFile}\n`);
+ const willChange = state.filter(v => v.inventoryPolicy === 'CONTINUE');
+ for (const v of state) {
+ const id = v.id.split('/').pop();
+ const flag = v.inventoryPolicy === 'CONTINUE' ? 'CONTINUE -> DENY' : `${v.inventoryPolicy} (leave)`;
+ console.log(` ${id} price=$${v.price} forSale=${v.availableForSale} status=${v.product.status} | ${flag}`);
+ }
+ console.log(`\n${willChange.length} of ${state.length} variants would flip to DENY (price untouched).`);
+
+ if (MODE === 'dry-run') {
+ console.log('\nDRY-RUN only. No writes performed. To apply (GATED):');
+ console.log(' node scripts/tk-11758-cork-continue-fix/fix.mjs --apply');
+ return;
+ }
+
+ // --apply
+ console.log('\nAPPLYING (gated write)…');
+ for (const v of willChange) {
+ const now = await setPolicy(v.product.id, v.id, 'DENY');
+ console.log(` ${v.id.split('/').pop()} -> ${now}`);
+ }
+ console.log('\napply complete. Reversible: node scripts/tk-11758-cork-continue-fix/fix.mjs --undo');
+})().catch(e => { console.error('FATAL:', e.message); process.exit(1); });
← 6eab755 auto-data-snapshot: 2026-09-16T09:33:20 (1 data files) — dat
·
back to Designerwallcoverings
·
WM/SDG reprice pt2: +36 William Morris (exact-code), Morris c843942 →