[object Object]

← 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

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 →