← back to Gmc 425 Supplemental Feed
reorder-canary-apply.mjs
29 lines
#!/usr/bin/env node
/** reorder-canary-apply.mjs — LIVE (Steve-approved) but CANARY-scoped + reversible.
* Reorders Roll->pos1 for the first N SUPP_CANT_HELP products, recording a rollback ledger FIRST. */
import fs from 'node:fs';
const HOME=process.env.HOME, SHOP='designer-laboratory-sandbox';
const API=`https://${SHOP}.myshopify.com/admin/api/2024-10/graphql.json`;
const T=(fs.readFileSync(HOME+'/Projects/secrets-manager/.env','utf8').match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m)||[])[1].replace(/['"]/g,'').trim();
const N=parseInt(process.argv[2]||'10',10);
const LEDGER=HOME+'/.claude/yolo-queue/restore-maps/reorder-canary-rollback.jsonl';
async function gql(q,v){const r=await fetch(API,{method:'POST',headers:{'X-Shopify-Access-Token':T,'Content-Type':'application/json'},body:JSON.stringify({query:q,variables:v||{}})});return r.json();}
const rows=JSON.parse(fs.readFileSync(new URL('./out/supp-cant-help.json',import.meta.url),'utf8')).slice(0,N);
const CUR=`query($id:ID!){product(id:$id){title variants(first:20){nodes{id title position price}}}}`;
const MUT=`mutation($productId:ID!,$positions:[ProductVariantPositionInput!]!){productVariantsBulkReorder(productId:$productId,positions:$positions){userErrors{field message}}}`;
let ok=0,fail=0;
for(const r of rows){
const pid=`gid://shopify/Product/${r.pid}`;
const cj=await gql(CUR,{id:pid}); const p=cj.data?.product; if(!p){console.log(`SKIP ${r.pid} (not found)`);continue;}
const before=p.variants.nodes.map(v=>({id:v.id,title:v.title,position:v.position,price:v.price}));
fs.appendFileSync(LEDGER,JSON.stringify({ts:new Date().toISOString(),pid:r.pid,title:p.title,before})+'\n');
const positions=[{id:`gid://shopify/ProductVariant/${r.rollVid}`,position:1},{id:`gid://shopify/ProductVariant/${r.sampleVid}`,position:2}];
const mj=await gql(MUT,{productId:pid,positions});
const errs=mj.data?.productVariantsBulkReorder?.userErrors||[];
if(errs.length){fail++;console.log(`ERR ${r.title||p.title}: ${JSON.stringify(errs)}`);continue;}
const vj=await gql(CUR,{id:pid}); const roll=vj.data.product.variants.nodes.find(v=>v.id.endsWith(r.rollVid));
console.log(`OK ${p.title.slice(0,40).padEnd(40)} Roll now pos${roll?.position} ($${roll?.price})`); ok++;
}
console.log(`\nCANARY DONE: ${ok} reordered OK, ${fail} failed. Rollback ledger: ${LEDGER}`);
console.log(`Next: Google re-sync (hours) -> re-run classify on these ${ok} to confirm the Roll offer appears + $4.25 drops.`);