← back to Dw Yolo Loop
fence-strip DRY-RUN (c52): 61 code-fence leaks, review-ready, all gates pass (read-only)
1dc6ff61d4789d16fdee3b68b65a16fd697d80ee · 2026-06-16 20:53:06 -0700 · Steve Abrams
Staged the c51-officer-approved fix as read-only dry-run. Self-corrected mid-cycle:
v1 only matched complete fenced blocks (1/61); real leaks are UNCLOSED leading fences,
patched unwrap to strip leading/trailing fence markers -> v2 strips all 61 cleanly.
Gates: 0 banned-word Wallpaper after strip; all 61 need derived meta/OG regen (null
seo.description); 3 extra from c51 64 were bold false-positives, excluded. Vendors
Kravet 49 / Scalamandre 8. Worklist before/after x61 in yolo-queue. NO write, apply Steve-gated.
Files touched
A scripts/fence-strip/fence-strip-dryrun.mjs
Diff
commit 1dc6ff61d4789d16fdee3b68b65a16fd697d80ee
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Jun 16 20:53:06 2026 -0700
fence-strip DRY-RUN (c52): 61 code-fence leaks, review-ready, all gates pass (read-only)
Staged the c51-officer-approved fix as read-only dry-run. Self-corrected mid-cycle:
v1 only matched complete fenced blocks (1/61); real leaks are UNCLOSED leading fences,
patched unwrap to strip leading/trailing fence markers -> v2 strips all 61 cleanly.
Gates: 0 banned-word Wallpaper after strip; all 61 need derived meta/OG regen (null
seo.description); 3 extra from c51 64 were bold false-positives, excluded. Vendors
Kravet 49 / Scalamandre 8. Worklist before/after x61 in yolo-queue. NO write, apply Steve-gated.
---
scripts/fence-strip/fence-strip-dryrun.mjs | 50 ++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
diff --git a/scripts/fence-strip/fence-strip-dryrun.mjs b/scripts/fence-strip/fence-strip-dryrun.mjs
new file mode 100644
index 0000000..2b43398
--- /dev/null
+++ b/scripts/fence-strip/fence-strip-dryrun.mjs
@@ -0,0 +1,50 @@
+// 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(/```/.test(b) || /^\s*<code>[\s\S]*<\/code>\s*$/i.test(b)){
+ 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.`);
← b5a6309 body-quality scan (c51): 187 empty + 64 code-fence leaks; 14
·
back to Dw Yolo Loop
·
fence-strip dry-run: tighten to leading-fence-only per offic 6c53615 →