← back to Flock Fix Viewer
TK-11122: reversible archive script for 5 residual unbuyable flock listings
e3508077fdf0c0f678a9e41071121a166350b788 · 2026-09-03 13:32:07 -0700 · Steve Abrams
Files touched
A archive-flock-residual.mjsA mfr-8004-snapshot.jsonA priceup-windsor.mjs
Diff
commit e3508077fdf0c0f678a9e41071121a166350b788
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Sep 3 13:32:07 2026 -0700
TK-11122: reversible archive script for 5 residual unbuyable flock listings
---
archive-flock-residual.mjs | 45 ++++++++++++++++++++++++++++++++
mfr-8004-snapshot.json | 14 ++++++++++
priceup-windsor.mjs | 64 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 123 insertions(+)
diff --git a/archive-flock-residual.mjs b/archive-flock-residual.mjs
new file mode 100644
index 0000000..5d753df
--- /dev/null
+++ b/archive-flock-residual.mjs
@@ -0,0 +1,45 @@
+// TK-11122 — archive the 5 residual active-but-UNBUYABLE flock listings.
+// Steve approved 2026-09-03 (AskUserQuestion): Retro Velvet 8003/8004 = archive both;
+// Monkey Madness 876555/6/7 = archive all 3. Each is sample-only, no sellable roll,
+// no recoverable mfr# (8004 excepted — it now carries W5725-04 but still no roll).
+// Reversible: snapshots each product's current status, ledgers undo (status->active).
+// DRY default; --apply writes. Live Shopify write => run via Steve's `!` paste.
+import fs from 'fs'; import os from 'os'; import path from 'path';
+const SHOP='designer-laboratory-sandbox.myshopify.com';
+const TOKEN=fs.readFileSync(new URL('./.token',import.meta.url),'utf8').trim();
+const LEDGER=path.join(os.homedir(),'.claude/yolo-queue/executed-reversible/ledger.jsonl');
+const SNAP=new URL('./archive-flock-residual-snapshot.json',import.meta.url);
+const DRY=!process.argv.includes('--apply');
+const API=`https://${SHOP}/admin/api/2024-10`;
+const H={'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'};
+
+const TARGETS=[
+ {id:1494878158960, sku:'FLOCK-8003', note:'Retro Velvet paisley (mfr# unknown)'},
+ {id:1494878388336, sku:'FLOCK-8004', note:'Retro Velvet hot pink (O&L W5725-04, no roll)'},
+ {id:1499354464368, sku:'Flock-876555', note:'Monkey Madness Ebony & Cream'},
+ {id:1499355807856, sku:'Flock-876556', note:'Monkey Madness Ivory & Bone'},
+ {id:1499355676784, sku:'Flock-876557', note:'Monkey Madness Natural & Beige'},
+];
+
+console.log(`Archive ${TARGETS.length} residual flock listings APPLY=${!DRY}\n`);
+const snap=[];
+for(const t of TARGETS){
+ const r=await (await fetch(`${API}/products/${t.id}.json?fields=id,status,title`,{headers:H})).json();
+ const p=r.product;
+ console.log(` ${t.sku.padEnd(14)} ${t.id} status: "${p.status}" -> "archived" (${t.note})`);
+ snap.push({id:t.id,sku:t.sku,oldStatus:p.status});
+}
+if(DRY){console.log('\nDRY RUN — nothing written. Re-run with --apply.');process.exit(0);}
+
+for(const t of TARGETS){
+ const res=await (await fetch(`${API}/products/${t.id}.json`,{method:'PUT',headers:H,
+ body:JSON.stringify({product:{id:t.id,status:'archived'}})})).json();
+ const ok=res?.product?.status==='archived';
+ console.log(` ${ok?'✓':'FAIL'} ${t.sku} -> ${res?.product?.status||JSON.stringify(res).slice(0,120)}`);
+}
+fs.writeFileSync(SNAP,JSON.stringify(snap,null,1));
+fs.appendFileSync(LEDGER,JSON.stringify({ts:new Date().toISOString(),agent:'vp-dw-commerce',ticket:'TK-11122',
+ action:`archived 5 residual active-but-unbuyable flock listings (8003/8004 + Monkey Madness 876555/6/7)`,
+ blast_radius:5,undo_cmd:`restore archive-flock-residual-snapshot.json: PUT each product id status back to oldStatus (active)`,
+ verify:'GET each product .json -> status==archived'})+'\n');
+console.log('\nDONE — snapshot: archive-flock-residual-snapshot.json, ledgered.');
diff --git a/mfr-8004-snapshot.json b/mfr-8004-snapshot.json
new file mode 100644
index 0000000..e90d473
--- /dev/null
+++ b/mfr-8004-snapshot.json
@@ -0,0 +1,14 @@
+[
+ {
+ "id": 34800269426739,
+ "namespace": "custom",
+ "key": "manufacturer_sku",
+ "oldValue": "FLOCK-8004"
+ },
+ {
+ "id": 36114493964339,
+ "namespace": "dwc",
+ "key": "manufacturer_sku",
+ "oldValue": "FLOCK-8004"
+ }
+]
\ No newline at end of file
diff --git a/priceup-windsor.mjs b/priceup-windsor.mjs
new file mode 100644
index 0000000..5d0fe44
--- /dev/null
+++ b/priceup-windsor.mjs
@@ -0,0 +1,64 @@
+// Price up the 20.5" WINDSOR flock sample-shells into 2-variant products, modeled on the
+// active Bella's Windsor (FLOCK-1011): roll 20.5"x5.5yd @ $190.99 + sample $4.25. Reversible + ledgered.
+import fs from 'fs'; import os from 'os'; import path from 'path';
+const SHOP='designer-laboratory-sandbox.myshopify.com';
+const TOKEN=fs.readFileSync(new URL('./.token',import.meta.url),'utf8').trim();
+const LEDGER=path.join(os.homedir(),'.claude/yolo-queue/executed-reversible/ledger.jsonl');
+const SNAPDIR=new URL('./windsor-snapshots/',import.meta.url); try{fs.mkdirSync(SNAPDIR,{recursive:true});}catch(e){}
+const DRY=!process.argv.includes('--apply');
+const API=`https://${SHOP}/admin/api/2024-10`, H={'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'};
+const gql=async(q,v={})=>(await (await fetch(`${API}/graphql.json`,{method:'POST',headers:H,body:JSON.stringify({query:q,variables:v})})).json());
+const sleep=ms=>new Promise(r=>setTimeout(r,ms));
+const ROLL_OPT='Sold per single roll (20.5" x 5.5 yards)', ROLL_PRICE='190.99', SAMPLE_PRICE='4.25';
+
+const SET=`mutation($input:ProductSetInput!){ productSet(synchronous:true, input:$input){ product{id handle variants(first:6){edges{node{title price sku}}}} userErrors{field message} } }`;
+function buildInput(id, rollSku, sampleSku){
+ return { id, productType:'Wallcovering',
+ productOptions:[{name:'Size', values:[{name:ROLL_OPT},{name:'Sample'}]}],
+ variants:[
+ {optionValues:[{optionName:'Size',name:ROLL_OPT}], price:ROLL_PRICE, sku:rollSku, inventoryPolicy:'CONTINUE'},
+ {optionValues:[{optionName:'Size',name:'Sample'}], price:SAMPLE_PRICE, sku:sampleSku, inventoryPolicy:'CONTINUE'}
+ ],
+ metafields:[
+ {namespace:'global',key:'v_prods_quantity_order_min',type:'single_line_text_field',value:'2'},
+ {namespace:'global',key:'v_prods_quantity_order_units',type:'single_line_text_field',value:'2'},
+ {namespace:'global',key:'unit_of_measure',type:'single_line_text_field',value:'Priced Per Single Roll. Ships untrimmed (30" untrimmed / 20.5" trimmed width).'},
+ {namespace:'global',key:'width',type:'single_line_text_field',value:'20.5 Inches Wide'},
+ {namespace:'custom',key:'width',type:'single_line_text_field',value:'20.5 Inches Wide'},
+ {namespace:'global',key:'length',type:'single_line_text_field',value:'5.5 Yards'},
+ {namespace:'global',key:'sqft_single_roll',type:'single_line_text_field',value:'14.09 Sq Ft'},
+ {namespace:'global',key:'sqft_double_roll',type:'single_line_text_field',value:'28.19 Sq Ft'},
+ {namespace:'custom',key:'sqft_single_roll',type:'single_line_text_field',value:'14.09 Sq Ft'},
+ {namespace:'custom',key:'sqft_double_roll',type:'single_line_text_field',value:'28.19 Sq Ft'},
+ {namespace:'global',key:'print_type',type:'single_line_text_field',value:'Flock'},
+ {namespace:'custom',key:'print_type',type:'single_line_text_field',value:'Flock'}
+ ]
+ };
+}
+const maxRoll=n=>n.variants.edges.map(e=>e.node).filter(v=>!/sample/i.test(v.title||'')).reduce((m,v)=>Math.max(m,parseFloat(v.price)||0),0);
+
+(async()=>{
+ let cur=null,all=[];
+ const Q=`query($c:String){ products(first:100, query:"windsor flock", after:$c){ pageInfo{hasNextPage endCursor} edges{node{ id handle title status variants(first:8){edges{node{title price sku}}} }}}}`;
+ do{const r=await gql(Q,{c:cur});const p=r?.data?.products;if(!p)break;all=all.concat(p.edges);cur=p.pageInfo.hasNextPage?p.pageInfo.endCursor:null;}while(cur);
+ const targets=all.map(e=>e.node).filter(n=>{
+ if(n.status==='ARCHIVED') return false;
+ if(!/windsor/i.test(n.handle+' '+n.title)) return false;
+ return maxRoll(n)<=10; // sample-only shells only
+ });
+ console.log(`Windsor sample-only shells: ${targets.length}`);
+ const list = DRY ? targets : targets;
+ for(const n of list){
+ const base=(n.variants.edges.map(e=>e.node).find(v=>v.sku)?.sku||n.handle).replace(/-sample$/i,'').replace(/-Sample$/,'');
+ if(DRY){ console.log(' would price up',n.status,n.handle.slice(0,46),'-> roll',base,'@ $190.99'); continue; }
+ fs.writeFileSync(new URL(n.handle.replace(/[^a-z0-9-]/gi,'_')+'.json',SNAPDIR), JSON.stringify(n,null,2));
+ const r=await gql(SET,{input:buildInput(n.id, base, base+'-Sample')});
+ const errs=r?.data?.productSet?.userErrors;
+ if(errs&&errs.length){console.log(' FAIL',n.handle,JSON.stringify(errs));continue;}
+ const vs=r.data.productSet.product.variants.edges.map(e=>e.node);
+ console.log(' OK',n.handle.slice(0,46),'->',vs.map(v=>v.title.slice(0,20)+'=$'+v.price).join(' | '));
+ fs.appendFileSync(LEDGER,JSON.stringify({ts:new Date().toISOString(),agent:'main:priceup-windsor',ticket:'flock-windsor-205',action:`price up ${n.handle} to 20.5" roll $190.99 + sample`,blast_radius:1,undo_cmd:`restore windsor-snapshots/${n.handle}.json`,verify:`2 variants roll $190.99 + sample $4.25, width 20.5"`})+'\n');
+ await sleep(400);
+ }
+ console.log(`${DRY?'DRY':'DONE'} · ${targets.length} Windsor shells`);
+})();
← e920313 add reversible mfr# fix for FLOCK-8004 -> Osborne & Little W
·
back to Flock Fix Viewer
·
auto-data-snapshot: 2026-09-03T13:47:49 (1 data files) — arc 1b9bb1d →