[object Object]

← back to Imageless Rescue

governance(TK-11370): make live-by-default Shopify writers dry-run by default

bdc58840cce34f6f7f04fd37ea956d3beeb58aa9 · 2026-09-10 17:52:31 -0700 · Steve Abrams

Flip `DRY = argv.includes('--dry')` -> `DRY = !argv.includes('--apply')` so a
zero-argument run no longer fires customer-facing Shopify writes.

Backward compatible: `--dry` still means dry; `--apply` now required to write.
Only zero-arg invocations change behaviour, and every candidate was checked
against its launchd/cron/pm2/wrapper invocation sites first (4 scripts that a
cadence job runs flagless were EXCLUDED so this cannot silently disable them).

Verified: node --check/py_compile 87/87 with a positive control proving the
checker rejects an injected fault; argv truth-table negative test 5/5.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L53y4oP2itY3qeJ8WQgMzc

Files touched

Diff

commit bdc58840cce34f6f7f04fd37ea956d3beeb58aa9
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Sep 10 17:52:31 2026 -0700

    governance(TK-11370): make live-by-default Shopify writers dry-run by default
    
    Flip `DRY = argv.includes('--dry')` -> `DRY = !argv.includes('--apply')` so a
    zero-argument run no longer fires customer-facing Shopify writes.
    
    Backward compatible: `--dry` still means dry; `--apply` now required to write.
    Only zero-arg invocations change behaviour, and every candidate was checked
    against its launchd/cron/pm2/wrapper invocation sites first (4 scripts that a
    cadence job runs flagless were EXCLUDED so this cannot silently disable them).
    
    Verified: node --check/py_compile 87/87 with a positive control proving the
    checker rejects an injected fault; argv truth-table negative test 5/5.
    
    Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01L53y4oP2itY3qeJ8WQgMzc
---
 scripts/fill-descriptions.js       | 2 +-
 scripts/material-default-widths.js | 2 +-
 scripts/width-from-catalog.js      | 2 +-
 scripts/width-from-sibling.js      | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/scripts/fill-descriptions.js b/scripts/fill-descriptions.js
index e585503..46accd4 100644
--- a/scripts/fill-descriptions.js
+++ b/scripts/fill-descriptions.js
@@ -2,7 +2,7 @@
 // Fill body_html for active-gate description-violators still missing one. Deterministic,
 // title/vendor-derived (NO metered LLM, NO legal text). Additive: only sets if missing/placeholder.
 const fs=require('fs'),os=require('os'),path=require('path'),https=require('https');
-const DRY=process.argv.includes('--dry-run'),LIMIT=process.argv.includes('--limit')?parseInt(process.argv[process.argv.indexOf('--limit')+1],10):Infinity;
+const DRY=!process.argv.includes('--apply'),LIMIT=process.argv.includes('--limit')?parseInt(process.argv[process.argv.indexOf('--limit')+1],10):Infinity;
 function tok(){if(process.env.SHOPIFY_ADMIN_TOKEN)return process.env.SHOPIFY_ADMIN_TOKEN.replace(/['"]/g,'').trim();try{return fs.readFileSync(path.join(os.homedir(),'Projects/secrets-manager/.env'),'utf8').match(/^SHOPIFY_ADMIN_TOKEN=(.*)$/m)[1].replace(/['"]/g,'').trim()}catch{return null}}
 const T=tok(),STORE='designer-laboratory-sandbox.myshopify.com',API='2024-10',sleep=ms=>new Promise(r=>setTimeout(r,ms));
 function gql(q,v){return new Promise(res=>{const b=JSON.stringify({query:q,variables:v});const r=https.request({hostname:STORE,path:`/admin/api/${API}/graphql.json`,method:'POST',headers:{'X-Shopify-Access-Token':T,'Content-Type':'application/json','Content-Length':Buffer.byteLength(b)}},x=>{let d='';x.on('data',c=>d+=c);x.on('end',()=>{try{res(JSON.parse(d))}catch{res({})}})});r.on('error',()=>res({}));r.write(b);r.end()})}
diff --git a/scripts/material-default-widths.js b/scripts/material-default-widths.js
index 4c61a76..04cdf43 100644
--- a/scripts/material-default-widths.js
+++ b/scripts/material-default-widths.js
@@ -3,7 +3,7 @@
 // (Steve-approved 2026-06-21). Skips ambiguous "standard" + murals. Additive: checks
 // live width first and never overwrites. Idempotent, resumable, dry-run.
 const fs=require('fs'),os=require('os'),path=require('path'),https=require('https');
-const DRY=process.argv.includes('--dry-run');
+const DRY=!process.argv.includes('--apply');
 const LIMIT=process.argv.includes('--limit')?parseInt(process.argv[process.argv.indexOf('--limit')+1],10):Infinity;
 function tok(){if(process.env.SHOPIFY_ADMIN_TOKEN)return process.env.SHOPIFY_ADMIN_TOKEN.replace(/['"]/g,'').trim();try{return fs.readFileSync(path.join(os.homedir(),'Projects/secrets-manager/.env'),'utf8').match(/^SHOPIFY_ADMIN_TOKEN=(.*)$/m)[1].replace(/['"]/g,'').trim()}catch{return null}}
 const T=tok(),STORE='designer-laboratory-sandbox.myshopify.com',API='2024-10',sleep=ms=>new Promise(r=>setTimeout(r,ms));
diff --git a/scripts/width-from-catalog.js b/scripts/width-from-catalog.js
index 443fddf..d1f2f24 100644
--- a/scripts/width-from-catalog.js
+++ b/scripts/width-from-catalog.js
@@ -2,7 +2,7 @@
 // Fill global.width for active-gate width-violators from the per-vendor *_catalog
 // tables (DB lookup, NO scraping, NO images). Additive, idempotent, resumable, dry-run.
 const fs=require('fs'),os=require('os'),path=require('path'),https=require('https');
-const DRY=process.argv.includes('--dry-run');
+const DRY=!process.argv.includes('--apply');
 const LIMIT=process.argv.includes('--limit')?parseInt(process.argv[process.argv.indexOf('--limit')+1],10):Infinity;
 function tok(){if(process.env.SHOPIFY_ADMIN_TOKEN)return process.env.SHOPIFY_ADMIN_TOKEN.replace(/['"]/g,'').trim();try{return fs.readFileSync(path.join(os.homedir(),'Projects/secrets-manager/.env'),'utf8').match(/^SHOPIFY_ADMIN_TOKEN=(.*)$/m)[1].replace(/['"]/g,'').trim()}catch{return null}}
 const T=tok(),STORE='designer-laboratory-sandbox.myshopify.com',API='2024-10',sleep=ms=>new Promise(r=>setTimeout(r,ms));
diff --git a/scripts/width-from-sibling.js b/scripts/width-from-sibling.js
index 95ae8ab..077ea96 100644
--- a/scripts/width-from-sibling.js
+++ b/scripts/width-from-sibling.js
@@ -3,7 +3,7 @@
 // sibling colorway that already has one (exact pattern_name match ⇒ same width).
 // Additive, idempotent, resumable, dry-run. No guessing.
 const fs=require('fs'),os=require('os'),path=require('path'),https=require('https');
-const DRY=process.argv.includes('--dry-run');
+const DRY=!process.argv.includes('--apply');
 const LIMIT=process.argv.includes('--limit')?parseInt(process.argv[process.argv.indexOf('--limit')+1],10):Infinity;
 function tok(){if(process.env.SHOPIFY_ADMIN_TOKEN)return process.env.SHOPIFY_ADMIN_TOKEN.replace(/['"]/g,'').trim();for(const p of['Projects/secrets-manager/.env']){try{const m=fs.readFileSync(path.join(os.homedir(),p),'utf8').match(/^SHOPIFY_ADMIN_TOKEN=(.*)$/m);if(m)return m[1].replace(/['"]/g,'').trim()}catch{}}return null}
 const T=tok(),STORE='designer-laboratory-sandbox.myshopify.com',API='2024-10';

← fefec7c auto-save: 2026-08-02T00:10:36 (1 files) — dw-vendor-cost-pr  ·  back to Imageless Rescue  ·  (newest)