← back to Hollywood Optc

build-restore-map.mjs

129 lines

#!/usr/bin/env node
// TK-10633 Option C — REVERSIBLE PREP. Build the HW-<hash> restore-map for the 646
// ACTIVE Momentum-real DWHD-* products. READ-ONLY: reads the local dw_unified mirror,
// writes ONLY a local restore file. Fires NOTHING at Shopify or dw_unified.
//
// Hash REUSED VERBATIM from hollywood-import/momentum-feed/assign-sku.mjs (canary-steve's
// forward-fix): HW-<sha256(String(number)).slice(0,12).toUpperCase()>. Must match so
// generator-minted codes and these retroactive codes agree.
import { createRequire } from 'module';
import { createHash } from 'node:crypto';
import { writeFileSync } from 'node:fs';
const require = createRequire(import.meta.url);
const { Pool } = require('pg');

// --- canary-steve's canonical hash (assign-sku.mjs lines 70-72) ---
const PL_PREFIX = process.env.HW_PL_PREFIX || 'HW';
const HASH_LEN  = Number(process.env.HW_HASH_LEN || 12);
const plSku = num => `${PL_PREFIX}-${createHash('sha256').update(String(num)).digest('hex').slice(0, HASH_LEN).toUpperCase()}`;

const pool = new Pool({ connectionString: (process.env.PGCONNSTRING || 'postgresql://dw_admin@127.0.0.1:5432/dw_unified?host=/tmp') });

const BASE = `status='ACTIVE' AND variant_sku ~ '^DWHD' AND (metafields->'dwc'->'real_vendor'->>'value')='Momentum'`;

async function main() {
  // The 646 targets. variant_sku in the mirror is the SAMPLE variant (DWHD-#####-Sample);
  // the customer-facing BASE sku is that minus '-Sample'. mfr number = custom.manufacturer_sku.
  const { rows } = await pool.query(
    `SELECT variant_id, variant_sku, title, handle,
            (metafields->'custom'->'manufacturer_sku'->>'value') AS mfr_custom,
            (metafields->'dwc'->'manufacturer_sku'->>'value')    AS mfr_dwc,
            (metafields->'global'->'dw_sku'->>'value') AS gdw,
            (metafields->'dwc'->'dw_sku'->>'value')    AS ddw,
            (metafields->'custom'->'dw_sku'->>'value') AS cdw
       FROM shopify_products WHERE ${BASE}
       ORDER BY variant_sku`);

  if (rows.length !== 646) console.warn(`WARN: expected 646, got ${rows.length}`);

  const map = [];
  const problems = [];
  for (const r of rows) {
    const mfr = r.mfr_custom || r.mfr_dwc;
    if (!mfr) { problems.push({ variant_sku: r.variant_sku, why: 'no manufacturer_sku' }); continue; }
    // base customer-facing sku = sample sku minus trailing -Sample (case-insensitive)
    const oldBase = r.variant_sku.replace(/-sample$/i, '');
    const newBase = plSku(mfr);
    const newSample = `${newBase}-Sample`;
    // which dw_sku metafields are polluted with a DWH* code (need HW-hash too)
    const polluted = [];
    if ((r.gdw || '').match(/^DWH/)) polluted.push('global.dw_sku');
    if ((r.ddw || '').match(/^DWH/)) polluted.push('dwc.dw_sku');
    if ((r.cdw || '').match(/^DWH/)) polluted.push('custom.dw_sku');
    map.push({
      variant_id: r.variant_id,
      old_variant_sku: r.variant_sku,        // DWHD-#####-Sample (the actual stored/sellable-sample sku)
      old_base_sku: oldBase,                  // DWHD-#####
      manufacturer_sku: mfr,                  // UNTOUCHED internal Momentum number
      new_base_sku: newBase,                  // HW-<hash>
      new_variant_sku: newSample,             // HW-<hash>-Sample
      mfr_is_bare_number: /^[0-9]+$/.test(mfr),
      polluted_dw_sku_metafields: polluted,   // which dw_sku mfs to rewrite HW-<hash>
      dw_sku_old: { global: r.gdw, dwc: r.ddw, custom: r.cdw },
      title: r.title, handle: r.handle,
    });
  }

  // --- VERIFY (a): HW-hash collisions across all 646 ---
  // A collision here is NOT a hash weakness (48-bit @ n=646 is astronomically safe) but a
  // SHARED manufacturer_sku: Momentum gave two distinct colorways the same supplier number,
  // so the deterministic hash collapses them. We must NOT collapse two live products to one
  // SKU, and we must NOT fabricate a distinguishing number. So: split the set into a CLEAN
  // apply-set (unique mfr number) and a HELD collision-exception set (stays DWHD until the
  // true distinct Momentum numbers are recovered — same "never fabricate" discipline as the
  // whole Hollywood reconciliation).
  const codeCount = {};
  for (const m of map) codeCount[m.new_base_sku] = (codeCount[m.new_base_sku] || 0) + 1;
  const collidingCodes = new Set(Object.entries(codeCount).filter(([, n]) => n > 1).map(([c]) => c));
  const collisionExceptions = map.filter(m => collidingCodes.has(m.new_base_sku));
  const applySet = map.filter(m => !collidingCodes.has(m.new_base_sku));
  const collisionsWithin = [...collidingCodes];

  // --- VERIFY (a2): zero collision against EXISTING live SKUs (any product NOT in this set) ---
  const targetVids = new Set(map.map(m => m.variant_id));
  const { rows: existing } = await pool.query(
    `SELECT variant_id, variant_sku FROM shopify_products
      WHERE variant_sku ~* '^HW-' `);
  const existingHW = existing.filter(e => !targetVids.has(e.variant_id))
    .map(e => e.variant_sku.replace(/-sample$/i, ''));
  const existingHWset = new Set(existingHW.map(s => s.toUpperCase()));
  // check the APPLY-SET codes against existing live HW SKUs
  const applyCodes = applySet.map(m => m.new_base_sku);
  const collisionsExisting = [...new Set(applyCodes.filter(c => existingHWset.has(c.toUpperCase())))];

  // --- VERIFY (b): zero DWHD/DWHW residue in the NEW codes ---
  const residue = map.filter(m => /^DWH/i.test(m.new_base_sku) || /DWHD|DWHW/i.test(m.new_variant_sku));

  // --- VERIFY (c): no HW code accidentally equals a real Momentum (bare) number ---
  // HW codes are prefixed 'HW-' + hex; a bare Momentum number is all digits. Structurally
  // impossible to equal, but assert anyway.
  const equalsMomentum = map.filter(m => /^[0-9]+$/.test(m.new_base_sku.replace(/^HW-/, '')) === false ? false : true)
                            .filter(m => m.new_base_sku === m.manufacturer_sku);

  const summary = {
    generated_at: new Date().toISOString(),
    ticket: 'TK-10633',
    hash_source: 'hollywood-import/momentum-feed/assign-sku.mjs (HW-<sha256(String(num)).slice(0,12).toUpperCase()>)',
    hash_prefix: PL_PREFIX, hash_len: HASH_LEN,
    total_targeted: map.length,
    apply_set_count: applySet.length,                        // clean, unique-mfr rows to remap
    held_collision_rows: collisionExceptions.length,         // shared-mfr rows held at DWHD
    skipped_no_mfr: problems.length,
    polluted_dw_sku_metafield_rows: applySet.filter(m => m.polluted_dw_sku_metafields.length).length,
    verify: {
      collisions_within_apply_set: 0,                        // by construction (collisions removed)
      shared_mfr_collision_groups: collisionsWithin.length,  // held out, NOT applied
      collisions_vs_existing_live_HW: collisionsExisting.length, // MUST be 0
      dwhd_dwhw_residue_in_new_applyset: applySet.filter(m => /^DWH/i.test(m.new_base_sku) || /DWHD|DWHW/i.test(m.new_variant_sku)).length, // MUST be 0
      new_code_equals_momentum_number: equalsMomentum.length, // MUST be 0
      collision_codes: collisionsWithin,
      collision_rows: collisionExceptions.map(m => ({ old: m.old_variant_sku, mfr: m.manufacturer_sku, title: m.title })),
    },
  };

  writeFileSync('data/hw-restore-map.json', JSON.stringify({ summary, problems, apply_set: applySet, held_collision_rows: collisionExceptions, full_map: map }, null, 2));
  console.log(JSON.stringify(summary, null, 2));
  await pool.end();
}
main().catch(e => { console.error(e); process.exit(1); });