← back to Dw Yolo Loop

scripts/fence-strip/fence-strip-dryrun.mjs

51 lines

// fence-strip-dryrun (c52) — READ-ONLY preview of the c51-officer-APPROVED fix:
// unwrap leaked markdown code-fence wrappers from ACTIVE body_html (strip ONLY the
// ```html ... ``` / <code> fence, keep inner HTML verbatim). Runs vp-dw-marketing's
// 3 gates: (a) fence-only guard (skip non-fenced, never touch bold/strong),
// (b) banned-word 'Wallpaper' check on each post-strip body, (c) flag meta/OG regen.
// DRY-RUN ONLY — writes a review worklist, NO Shopify write (live apply Steve-gated).
import fs from 'fs';
const T=(fs.readFileSync(process.env.HOME+'/Projects/secrets-manager/.env','utf8').match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m)||[])[1];
const URL='https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-10/graphql.json';
const sleep=ms=>new Promise(r=>setTimeout(r,ms));
async function gql(q,v){for(let a=0;a<6;a++){const r=await fetch(URL,{method:'POST',headers:{'X-Shopify-Access-Token':T,'Content-Type':'application/json'},body:JSON.stringify({query:q,variables:v||{}})});if(r.status===429){await sleep(2500);continue;}const j=await r.json();if(j.errors&&JSON.stringify(j.errors).match(/THROTTLED/)){await sleep(2500);continue;}return j;}throw new Error('throttled');}
const FENCE_RE=/```[a-z]*\s*([\s\S]*?)\s*```/i;        // ```html ... ``` (lang optional)
const txt=h=>(h||'').replace(/<[^>]+>/g,'').replace(/&[a-z]+;/g,' ').replace(/\s+/g,' ').trim();
// gate-a: deterministic unwrap, ONLY if a real fence wrapper is present
function unwrap(b){
  let body=b;
  body=body.replace(/^\s*```[a-z]*\s*\n?/i,'');        // (a) leading fence marker (```html / ```) — the unclosed-leak case
  body=body.replace(/\n?\s*```\s*$/,'');               // (c) trailing fence marker
  body=body.replace(/```[a-z]*\s*([\s\S]*?)\s*```/gi,'$1'); // (b) any complete inner block — keep inner
  body=body.replace(/^\s*<code>([\s\S]*?)<\/code>\s*$/i,'$1'); // whole-body <code> wrap
  return body.trim();
}
// find the 64: re-scan ACTIVE bodies for an actual ``` fence (strict, not bold)
let cur=null,total=0; const items=[];
do{
  const j=await gql(`query($c:String){products(first:100,after:$c,query:"status:active"){pageInfo{hasNextPage endCursor} nodes{id handle vendor descriptionHtml seo{description}}}}`,{c:cur});
  const pg=j.data.products;
  for(const n of pg.nodes){ total++; const b=n.descriptionHtml||'';
    if(/^\s*```/.test(b) || /^\s*<code>[\s\S]*<\/code>\s*$/i.test(b)){  // leading-fence-only (officer gate-A: don't touch non-leaked products)
      const after=unwrap(b);
      const changed = after!==b && txt(after).length>=3;          // gate-a: actually changed + non-empty inner
      const bannedWord = /\bwallpaper\b/i.test(txt(after));         // gate-b
      const metaDerived = !n.seo?.description;                      // gate-c: null seo → meta derives from body → needs regen awareness
      items.push({id:n.id, handle:n.handle, vendor:n.vendor, changed, bannedWord, metaNeedsRegen:metaDerived,
        before:txt(b).slice(0,70), after:txt(after).slice(0,70)});
    }
  }
  cur=pg.pageInfo.hasNextPage?pg.pageInfo.endCursor:null;
}while(cur);
const willStrip=items.filter(i=>i.changed);
const skipNoChange=items.filter(i=>!i.changed);
const bannedHits=willStrip.filter(i=>i.bannedWord);
const metaRegen=willStrip.filter(i=>i.metaNeedsRegen);
fs.writeFileSync(process.env.HOME+'/.claude/yolo-queue/fence-strip-dryrun-worklist.json',JSON.stringify(items,null,2));
console.log(`scanned ${total} ACTIVE | fence-flagged ${items.length} | WILL-STRIP ${willStrip.length} | skip(no real change) ${skipNoChange.length}`);
console.log(`gate-b banned-word 'Wallpaper' AFTER strip: ${bannedHits.length} → ${bannedHits.length?'FLAG (do not auto-rewrite): '+bannedHits.slice(0,6).map(x=>x.handle).join(', '):'none ✅'}`);
console.log(`gate-c meta/OG would need regen (null seo.description): ${metaRegen.length}`);
console.log('\n=== sample before → after (6) ===');
for(const i of willStrip.slice(0,6)){ console.log(`\n• ${i.handle} [${i.vendor}]`); console.log(`  BEFORE: ${i.before}`); console.log(`  AFTER : ${i.after}`); }
console.log(`\nDRY-RUN only — worklist: ~/.claude/yolo-queue/fence-strip-dryrun-worklist.json. Live apply Steve-gated.`);