← back to Dw Sku Integrity
TK-10900: contrarian-2 fix — guard title-mode cross-class hole (keep single-class, defer multi-class)
0d133cadbf0478b3fd289b590af5b837e40a59f3 · 2026-08-31 04:41:41 -0700 · codex-10896
Second contrarian pass on the FINAL tool (unreviewed since Defect B): title-mode
catalogs lack product_type, so isCrossClass can't run -> latent wrong-class write.
Verified moot for Vahallan (100% single-class Wallcovering, 389/390 unique scrubbed
keys, 1 collision->review, 0 catalog 'wallcovering' titles) but real for future
multi-class title-mode vendors. Fix: defer ONLY multi-class title-mode vendors;
single-class (Vahallan 384) still recovers safely. 61 tests green, Vahallan unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
Diff
commit 0d133cadbf0478b3fd289b590af5b837e40a59f3
Author: codex-10896 <steve@designerwallcoverings.com>
Date: Mon Aug 31 04:41:41 2026 -0700
TK-10900: contrarian-2 fix — guard title-mode cross-class hole (keep single-class, defer multi-class)
Second contrarian pass on the FINAL tool (unreviewed since Defect B): title-mode
catalogs lack product_type, so isCrossClass can't run -> latent wrong-class write.
Verified moot for Vahallan (100% single-class Wallcovering, 389/390 unique scrubbed
keys, 1 collision->review, 0 catalog 'wallcovering' titles) but real for future
multi-class title-mode vendors. Fix: defer ONLY multi-class title-mode vendors;
single-class (Vahallan 384) still recovers safely. 61 tests green, Vahallan unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
content-match-gen.mjs | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/content-match-gen.mjs b/content-match-gen.mjs
index e380847..6028400 100644
--- a/content-match-gen.mjs
+++ b/content-match-gen.mjs
@@ -98,6 +98,16 @@ const shopRows = runPsql(KAM,
`WHERE lower(coalesce(status,''))='active' AND (dw_sku IS NULL OR btrim(dw_sku)='') ` +
`AND vendor ILIKE '${sqlEscape(VENDOR)}%';`);
+// Contrarian pass-2 guard: title-mode catalogs have no product_type, so the cross-class bucket guard
+// (isCrossClass) can't run. That is only class-SAFE when the vendor's blank rows are a single product
+// class (no fabric-vs-wall ambiguity possible — e.g. Vahallan, 100% Wallcovering). A MULTI-class
+// title-mode vendor could silently take a wrong-class code, so defer it to a fitted/manual path.
+if (keyMode === 'title' && !hasProductType) {
+ const shopBuckets = new Set(shopRows.map(([, , pt]) => bucket(pt)).filter((b) => b !== 'other'));
+ if (shopBuckets.size > 1) deferUnsupported('title_mode_multiclass_no_bucket_guard',
+ `title-mode w/o catalog product_type but Shopify rows span ${shopBuckets.size} classes (${[...shopBuckets].join(',')}) — cross-class guard cannot run; not safe to auto-emit`);
+}
+
const entries = [], review = [];
const stat = { shopify_blank: shopRows.length, no_shopify_id: 0, no_map: 0, ambiguous: 0, cross_class: 0, matched: 0 };
for (const [sid, title, ptype] of shopRows) {
← 0f0dacf TK-10900: extract pure match helpers into tested module (47-
·
back to Dw Sku Integrity
·
TK-11002: Harlequin DWHQ-->DWHF- prefix re-stamp (Option A) 289793b →