← back to Rebel Walls Push
scripts/revalidate-TK11248.js
134 lines
'use strict';
/* READ-ONLY revalidation for the TK-11248 batch (TK-11238 + TK-10405).
* Confirms both live targets still match their characterization and builds
* restore maps. NO WRITES. $0. Exit 0 only if BOTH targets are exactly as
* expected and safe to execute once approval is logged.
*
* Usage: node revalidate-TK11248.js
* Writes: data/TK11248-revalidation-<ts>.json (evidence + restore maps)
*/
const fs = require('fs');
const path = require('path');
const { gqlRetry, DOMAIN } = require('./_shop');
// ---- TK-11238: Aries title fix ------------------------------------------------
const ARIES_ID = '7800009752627';
const ARIES_GID = `gid://shopify/Product/${ARIES_ID}`;
const ARIES_OLD_TITLE = 'Aries - Single Color Commercial Wallcovering | DW Commercial Surfaces';
const ARIES_NEW_TITLE = 'Aries - Akoya Type II Vinyl Wallcovering | Hollywood Wallcoverings';
// ---- TK-10405: 12 Rebel Walls Default-Title variant deletes -------------------
const RW12 = [
['6679729078323', 'A City Rises (Class 2 $80.48 anomaly)'],
['6679739629619', 'A Priori'],
['7851164303411', '3D Art - Multi'],
['7851164336179', '3D Boxes - Gray'],
['7851164368947', '3D Wave - Gray'],
['7851164401715', '3D Wave - Marble'],
['7851164434483', '3D Wave - Sand'],
['7851164500019', '3D Wave - White'],
['7851164532787', '70S Retro - Beige'],
['7851164565555', 'A City Rises - Dark Blue'],
['7851164598323', 'A Priori - Taupe'],
['7851164631091', 'A Priori - Soft'],
];
const PRODUCT_Q = `query($id: ID!) {
product(id: $id) {
id legacyResourceId title handle status vendor
variants(first: 20) {
nodes { id legacyResourceId title sku price selectedOptions { name value } }
}
}
}`;
async function main() {
const ts = new Date().toISOString().replace(/[:.]/g, '-');
const report = { ts, store: DOMAIN, tk11238: null, tk10405: null, gate: { ready_11238: false, ready_10405: false } };
// ---- TK-11238 revalidation ----
const ar = await gqlRetry(PRODUCT_Q, { id: ARIES_GID }, 'aries');
const ap = ar.json && ar.json.data && ar.json.data.product;
if (!ap) {
report.tk11238 = { error: 'product not found', raw: ar.json };
} else {
const titleMatches = ap.title === ARIES_OLD_TITLE;
const isActive = ap.status === 'ACTIVE';
report.tk11238 = {
id: ap.legacyResourceId, gid: ap.id, handle: ap.handle, vendor: ap.vendor, status: ap.status,
current_title: ap.title,
expected_old_title: ARIES_OLD_TITLE,
proposed_new_title: ARIES_NEW_TITLE,
title_matches_expected: titleMatches,
is_active: isActive,
undo: { field: 'title', restore_to: ap.title },
};
report.gate.ready_11238 = titleMatches && isActive;
}
// ---- TK-10405 revalidation ----
const rows = [];
for (const [pid, label] of RW12) {
const r = await gqlRetry(PRODUCT_Q, { id: `gid://shopify/Product/${pid}` }, `rw:${pid}`);
const p = r.json && r.json.data && r.json.data.product;
if (!p) { rows.push({ pid, label, error: 'not found', raw: r.json }); continue; }
const vs = p.variants.nodes;
const byTitle = t => vs.filter(v => v.title === t);
const dt = byTitle('Default Title');
const mural = vs.find(v => /Mural \(per m²\)|Mural \(per m2\)/.test(v.title));
const sample = vs.find(v => v.title === 'Sample' || /-Sample$/i.test(v.sku || ''));
rows.push({
pid, label, product_status: p.status, handle: p.handle,
variant_count: vs.length,
default_title_variants: dt.map(v => ({ id: v.id, legacyId: v.legacyResourceId, sku: v.sku, price: v.price,
options: v.selectedOptions })),
has_mural_survivor: !!mural,
mural: mural ? { id: mural.id, sku: mural.sku, price: mural.price } : null,
has_sample_survivor: !!sample,
sample: sample ? { id: sample.id, sku: sample.sku, price: sample.price } : null,
all_variants: vs.map(v => ({ id: v.id, title: v.title, sku: v.sku, price: v.price })),
});
}
// safe iff: each product has exactly one Default Title variant to delete, AND both
// Mural + Sample survivors remain (so the delete cannot orphan the sellable/sample).
const perProductSafe = rows.map(row => ({
pid: row.pid, label: row.label,
ok: !row.error && Array.isArray(row.default_title_variants) && row.default_title_variants.length === 1 &&
row.has_mural_survivor && row.has_sample_survivor && row.variant_count === 3,
dt_count: row.default_title_variants ? row.default_title_variants.length : null,
variant_count: row.variant_count,
}));
report.tk10405 = { products: rows, per_product_safe: perProductSafe };
report.gate.ready_10405 = perProductSafe.length === 12 && perProductSafe.every(x => x.ok);
const outDir = path.join(__dirname, '..', 'data');
const outFile = path.join(outDir, `TK11248-revalidation-${ts}.json`);
fs.writeFileSync(outFile, JSON.stringify(report, null, 2));
// ---- console summary ----
console.log(`\n=== TK-11248 REVALIDATION (READ-ONLY, $0) @ ${ts} ===`);
console.log(`store: ${DOMAIN}`);
console.log(`\n[TK-11238 Aries title]`);
if (report.tk11238.error) console.log(` ERROR: ${report.tk11238.error}`);
else {
console.log(` product ${report.tk11238.id} status=${report.tk11238.status} handle=${report.tk11238.handle} vendor="${report.tk11238.vendor}"`);
console.log(` current title : "${report.tk11238.current_title}"`);
console.log(` title matches expected old? ${report.tk11238.title_matches_expected}`);
console.log(` is active? ${report.tk11238.is_active}`);
console.log(` -> proposed new title: "${report.tk11238.proposed_new_title}"`);
console.log(` READY_11238=${report.gate.ready_11238}`);
}
console.log(`\n[TK-10405 Rebel Walls 12 Default-Title deletes]`);
for (const s of perProductSafe) {
const row = rows.find(r => r.pid === s.pid);
const dt = row && row.default_title_variants && row.default_title_variants[0];
console.log(` ${s.pid} ${s.ok ? 'OK ' : 'HOLD'} variants=${s.variant_count} dt=${s.dt_count} ${dt ? `[del ${dt.sku} @ $${dt.price}]` : ''} mural=${row&&row.has_mural_survivor} sample=${row&&row.has_sample_survivor} ${s.label}`);
}
console.log(` READY_10405=${report.gate.ready_10405} (${perProductSafe.filter(x=>x.ok).length}/12 safe)`);
console.log(`\nEvidence -> ${outFile}`);
console.log(`\nGATE: ready_11238=${report.gate.ready_11238} ready_10405=${report.gate.ready_10405}`);
console.log(`NO WRITES FIRED. Awaiting explicit exact approval logged on TK-11248.\n`);
}
main().catch(e => { console.error('FATAL', e); process.exit(2); });