← back to Rebel Walls Push
scripts/revalidate-descG.js
105 lines
'use strict';
/* READ-ONLY revalidation for TK-11248 action G (6 settlement-CLEARED Rebel Walls
* descriptionHtml writes). Also confirms the HOLDs (R20805 blocked + 5 counsel)
* remain untouched. NO WRITES. $0.
*
* For each of the 12 HELD murals it GETs live: id, status, vendor, mfr metafield,
* current descriptionHtml length + first 80 chars. Compares the 6 target handles
* to the drafted copy and reports NEEDS-WRITE / ALREADY-CORRECT / DRIFT.
*/
const fs = require('fs');
const path = require('path');
const { gqlRetry, DOMAIN } = require('./_shop');
// The 6 CLEAR-TO-PUBLISH (action G) — handle -> {mfr, expected descriptionHtml}
const DRAFTED = require('./descG-copy.json'); // { handle: {mfr, html} }
const CLEARED = Object.keys(DRAFTED);
// HOLDS — must stay untouched
const HOLD_BLOCKED = ['birds-and-cages-petrol-rebel-walls']; // R20805
const HOLD_COUNSEL = [
'monstera-leaves-beige-rebel-walls', // R19778
'elephant-and-friends-blue-rebel-walls', // R20227
'bear-friend-blue-rebel-walls', // R21071
'happy-bear-beige-rebel-walls', // R21072
'twilight-bouquet-dark-rebel-walls', // R21637
];
const Q = `query($h: String!){ productByHandle(handle:$h){
id legacyResourceId handle title status vendor descriptionHtml
mfr: metafield(namespace:"custom", key:"mfr_sku"){ value }
global_mfr: metafield(namespace:"global", key:"mfr_sku"){ value }
} }`;
const norm = s => (s || '').replace(/\s+/g, ' ').trim();
async function getOne(handle) {
const r = await gqlRetry(Q, { h: handle }, `h:${handle}`);
const p = r.json && r.json.data && r.json.data.productByHandle;
return p || null;
}
async function main() {
const ts = new Date().toISOString().replace(/[:.]/g, '-');
const report = { ts, store: DOMAIN, cleared: [], hold_blocked: [], hold_counsel: [],
gate: { ready: false, needs_write: 0, already_correct: 0, drift: [] } };
console.log(`\n=== TK-11248 action G REVALIDATION (READ-ONLY, $0) @ ${ts} ===`);
console.log(`store: ${DOMAIN}\n`);
console.log(`[6 CLEAR-TO-PUBLISH — action G targets]`);
for (const h of CLEARED) {
const p = await getOne(h);
const want = DRAFTED[h];
if (!p) { report.gate.drift.push(`${h}: NOT FOUND`); console.log(` ${h} DRIFT: not found`); continue; }
const curLen = (p.descriptionHtml || '').length;
const isDraft = p.status === 'DRAFT';
const vendorOk = p.vendor === 'Rebel Walls';
const mfrLive = (p.mfr && p.mfr.value) || (p.global_mfr && p.global_mfr.value) || '';
const mfrOk = !mfrLive || mfrLive === want.mfr; // tolerate missing metafield
const already = norm(p.descriptionHtml) === norm(want.html);
const emptyNow = curLen === 0;
let state;
if (!isDraft) state = 'DRIFT-NOT-DRAFT';
else if (!vendorOk) state = 'DRIFT-VENDOR';
else if (!mfrOk) state = 'DRIFT-MFR';
else if (already) state = 'ALREADY-CORRECT';
else if (emptyNow) state = 'NEEDS-WRITE';
else state = 'DRIFT-UNEXPECTED-CONTENT'; // has some other description
const row = { handle: h, mfr: want.mfr, id: p.id, legacyId: p.legacyResourceId,
status: p.status, vendor: p.vendor, mfr_live: mfrLive, cur_len: curLen,
cur_preview: norm(p.descriptionHtml).slice(0, 80), state };
report.cleared.push(row);
if (state === 'NEEDS-WRITE') report.gate.needs_write++;
else if (state === 'ALREADY-CORRECT') report.gate.already_correct++;
else report.gate.drift.push(`${h}: ${state} (len=${curLen}, status=${p.status})`);
console.log(` ${h.padEnd(40)} ${state.padEnd(24)} status=${p.status} len=${curLen} mfr=${mfrLive||'-'}`);
}
console.log(`\n[HOLD — must stay untouched]`);
for (const h of [...HOLD_BLOCKED, ...HOLD_COUNSEL]) {
const p = await getOne(h);
if (!p) { console.log(` ${h.padEnd(40)} (not found)`); continue; }
const bucket = HOLD_BLOCKED.includes(h) ? report.hold_blocked : report.hold_counsel;
const row = { handle: h, id: p.id, status: p.status, cur_len: (p.descriptionHtml||'').length,
cur_preview: norm(p.descriptionHtml).slice(0,60) };
bucket.push(row);
console.log(` ${h.padEnd(40)} status=${p.status} len=${row.cur_len}${row.cur_len? ' ⚠ has description':''}`);
}
report.gate.ready = report.gate.drift.length === 0 && report.gate.needs_write > 0;
const outFile = path.join(__dirname, '..', 'data', `TK11248-descG-revalidation-${ts}.json`);
fs.writeFileSync(outFile, JSON.stringify(report, null, 2));
console.log(`\n--- SUMMARY ---`);
console.log(` NEEDS-WRITE: ${report.gate.needs_write}`);
console.log(` ALREADY-CORRECT: ${report.gate.already_correct}`);
console.log(` DRIFT: ${report.gate.drift.length}${report.gate.drift.length? ' -> ' + report.gate.drift.join(' | '):''}`);
console.log(` GATE ready_to_write=${report.gate.ready}`);
console.log(`\nEvidence -> ${outFile}`);
console.log(`NO WRITES FIRED.\n`);
// exit 0 if clean (either all already-correct, or a clean needs-write set); 5 on drift
process.exit(report.gate.drift.length ? 5 : 0);
}
main().catch(e => { console.error('FATAL', e); process.exit(2); });