← back to Dw Unbuyable Recovery Pilot

tk11041-innovations-reconcile/lib/provenance.mjs

61 lines

// TK-11041 — catalog-source provenance guard (the narrow scraper/test hardening).
//
// PURPOSE: encode the TK-10981 lesson as an executable gate. Before any *_catalog
// staging row is TRUSTED as an authoritative recovery SOURCE (to backfill a real
// mfr item# or a net price onto a customer-facing product), it must pass this
// guard. A plausible code SHAPE is NOT proof of provenance — the innovations_catalog
// scrape produced 81 rows whose mfr_sku is just the *tail of the pattern name*
// (Sum`atra` -> `atra-1`, Maz`arin` -> `arin-1`) and 236 rows with no product_url
// at all. Backfilling from those would fabricate identity/price data.
//
// Pure functions, no I/O — unit-testable in isolation (see ../test/provenance.test.mjs).

// A real Innovations-style vendor code: 2–4 uppercase letters + digits (ZIO-88, SOA008).
const CODE_SHAPE = /^[A-Z]{2,4}-?\d{1,5}$/;
// The truncation signature: an all-lowercase letter run + digits (arin-1, atra-3).
const LOWERCASE_TAIL = /^[a-z]{2,6}-\d+$/;
// Bare placeholders that leaked in from boolean/null scrapes.
const PLACEHOLDER = /^(TRUE|FALSE|NULL|N\/?A|-|\?)$/i;

/** Does the code's letter run appear as the TAIL of any word of the pattern name? */
function codeIsPatternTail(code, patternName) {
  const letters = (code || '').replace(/[^a-z]/gi, '').toLowerCase();
  if (letters.length < 3) return false;
  const words = (patternName || '')
    .toLowerCase()
    .split(/[^a-z]+/)
    .filter(w => w && w !== 'wallcovering' && w !== 'wallpaper');
  // truncation match: a pattern word ENDS WITH the code letters but is not equal
  // to them (i.e. the code is a suffix chopped off the real name).
  return words.some(w => w.length > letters.length && w.endsWith(letters));
}

/**
 * Classify a staging-catalog row as a recovery source.
 * @param {{mfr_sku?:string, product_url?:string, pattern_name?:string, price_trade?:number|string}} row
 * @returns {{code:string, trustworthy:boolean, reasons:string[]}}
 *   trustworthy === true  => the row may be used to backfill identity/price.
 *   reasons lists every disqualifier found (empty when trustworthy).
 */
export function classifyCatalogSource(row = {}) {
  const code = (row.mfr_sku ?? '').trim();
  const reasons = [];

  if ((row.product_url ?? '').trim() === '') reasons.push('no_provenance_url');
  if (PLACEHOLDER.test(code)) reasons.push('placeholder_code');
  if (LOWERCASE_TAIL.test(code)) reasons.push('corrupted_lowercase_tail');
  if (codeIsPatternTail(code, row.pattern_name)) reasons.push('code_is_pattern_tail');
  if (!CODE_SHAPE.test(code) && !LOWERCASE_TAIL.test(code) && !PLACEHOLDER.test(code)) {
    reasons.push('unexpected_shape');
  }

  // de-dupe while preserving order
  const unique = [...new Set(reasons)];
  return { code, trustworthy: unique.length === 0, reasons: unique };
}

/** Convenience boolean. */
export function isTrustworthySource(row) {
  return classifyCatalogSource(row).trustworthy;
}