← back to Dw Sku Integrity
TK-10900/A: fix cross-class wrong-write (contrarian Defect B) + harden base-strip
e47fdb55badbc0bedcce1ec04a130b6b2288e6d3 · 2026-08-31 02:19:07 -0700 · codex-10896
Contrarian caught it: the product_type bucket-guard only ran on MULTI-candidate
keys, so a single wall-only catalog code (e.g. Abbey 61 -> 7394A61 'Upholstered
Walls/Panels') was stamped blindly onto an Upholstery Shopify row. Blast radius
1,523 wall-only single-base keys. Fix: bucket-compatibility is now a hard
precondition on the single-candidate path too -> cross_class_mismatch -> review,
never apply. Also harden baseCode to strip only trailing -<alpha> suffixes so a
numeric-hyphenated real code (91026-10) is preserved, not truncated.
Carnegie honest rate: 87.7% -> 70.0% matched (2,745), 694 cross-class quarantined.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
Diff
commit e47fdb55badbc0bedcce1ec04a130b6b2288e6d3
Author: codex-10896 <steve@designerwallcoverings.com>
Date: Mon Aug 31 02:19:07 2026 -0700
TK-10900/A: fix cross-class wrong-write (contrarian Defect B) + harden base-strip
Contrarian caught it: the product_type bucket-guard only ran on MULTI-candidate
keys, so a single wall-only catalog code (e.g. Abbey 61 -> 7394A61 'Upholstered
Walls/Panels') was stamped blindly onto an Upholstery Shopify row. Blast radius
1,523 wall-only single-base keys. Fix: bucket-compatibility is now a hard
precondition on the single-candidate path too -> cross_class_mismatch -> review,
never apply. Also harden baseCode to strip only trailing -<alpha> suffixes so a
numeric-hyphenated real code (91026-10) is preserved, not truncated.
Carnegie honest rate: 87.7% -> 70.0% matched (2,745), 694 cross-class quarantined.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
content-match-gen.mjs | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/content-match-gen.mjs b/content-match-gen.mjs
index 11d24ca..c04a4c9 100644
--- a/content-match-gen.mjs
+++ b/content-match-gen.mjs
@@ -41,9 +41,10 @@ const MINT_CATALOG = new Set(['carnegie', 'maharam', 'cmo paris', 'cmo_paris', '
const sqlEscape = (v) => String(v).replace(/'/g, "''");
const norm = (s) => (s || '').trim().toLowerCase().replace(/\s+/g, ' ');
-// Base code = token before the first DW type-suffix ('-panels', '-wallcoverings', ...). Real codes with
-// no such suffix pass through unchanged. Only applied to mint-catalog mfr_sku (dw_sku is used verbatim).
-const baseCode = (c) => (c || '').trim().split('-')[0].toUpperCase();
+// Base code = strip trailing DW type-suffix groups ('-panels', '-panels-museums', '-dividers', ...).
+// Strips ONLY trailing '-<alpha>' tokens, so a genuinely hyphenated real code with a numeric segment
+// ('91026-10') is preserved rather than truncated. Only applied to mint-catalog mfr_sku (dw_sku verbatim).
+const baseCode = (c) => (c || '').trim().replace(/(-[A-Za-z][A-Za-z]*)+$/, '').toUpperCase();
// product_type semantic bucket so Shopify 'Upholstery' can match catalog 'Upholstery'/'Fabric' etc.
const bucket = (pt) => {
const s = norm(pt);
@@ -100,7 +101,7 @@ const shopRows = runPsql(KAM,
const vname = norm(VENDOR);
const entries = [], review = [];
-const stat = { shopify_blank: shopRows.length, no_shopify_id: 0, parse_miss: 0, no_map: 0, ambiguous: 0, matched: 0 };
+const stat = { shopify_blank: shopRows.length, no_shopify_id: 0, parse_miss: 0, no_map: 0, ambiguous: 0, cross_class: 0, matched: 0 };
for (const [sid, title, ptype] of shopRows) {
if (!sid) { stat.no_shopify_id++; continue; }
let body = norm(title);
@@ -117,6 +118,15 @@ for (const [sid, title, ptype] of shopRows) {
if (filtered.length === 1) cand = filtered;
}
if (cand.length !== 1) { stat.ambiguous++; review.push({ shopify_id: sid, title, key, candidates: [...codes.keys()], reason: 'ambiguous_multi_code' }); continue; }
+ // Bucket-compatibility precondition — applies to SINGLE-candidate keys too (the Abbey-61 class):
+ // never stamp a wall-only code onto a fabric row or vice versa. Shopify/catalog 'other' -> allow.
+ const shopBucket = bucket(ptype);
+ const catBuckets = codes.get(cand[0]);
+ if (shopBucket !== 'other' && !catBuckets.has(shopBucket) && !catBuckets.has('other')) {
+ stat.cross_class++;
+ review.push({ shopify_id: sid, title, key, candidate: cand[0], catalog_buckets: [...catBuckets], shopify_bucket: shopBucket, reason: 'cross_class_mismatch' });
+ continue;
+ }
entries.push({ shopify_id: sid, title, candidate: cand[0] });
stat.matched++;
}
← 0aa462b TK-10900/A: content-match-gen.mjs — schema-adaptive catalog
·
back to Dw Sku Integrity
·
TK-10900: plan evidence updated — content-match SHIPPED + co e5896e1 →