[object Object]

← back to Designerwallcoverings

TK-00038: harden the Sanderson PDP verifier (2 measurement holes from 2nd-model review)

85ec636a793b5d18bd3f7225179cb7393c4c8e95 · 2026-09-16 14:17:56 -0700 · Steve Abrams

Second-model review (grok-4.5; codex path is spend-capped) agreed the 09-14
report was a false green and named two ways a broken product could still
read PASS. Both closed:

1. Variant page truncation. variants(first:20) silently treated anything
   beyond page 1 as fine. A full page now returns NOT-MEASURED — we cannot
   prove we saw every variant. Live max is 2/502 so this is a future guard,
   not a live fix.
2. Storefront identity join. The .json we measure must BE the product we
   asked about; a mismatched id is now NOT-MEASURED, so a wrong-handle
   orphan can't be counted as someone else's pass.

Negative test extended 20 -> 23 assertions, both new guards covered with
a passing control beside each.

Re-ran live with the guards on: 502/502 MEASURED-OK on both planes, 0 bad,
0 not-measured => PASS, identity join enforced on all 502.

Scope note: this verifies catalog invariants + storefront data plane, NOT
the full buy path (image bytes, add-to-cart, checkout) — stated rather
than implied.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P9JdVA5oKccJQWkG5Qwiz5

Files touched

Diff

commit 85ec636a793b5d18bd3f7225179cb7393c4c8e95
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Sep 16 14:17:56 2026 -0700

    TK-00038: harden the Sanderson PDP verifier (2 measurement holes from 2nd-model review)
    
    Second-model review (grok-4.5; codex path is spend-capped) agreed the 09-14
    report was a false green and named two ways a broken product could still
    read PASS. Both closed:
    
    1. Variant page truncation. variants(first:20) silently treated anything
       beyond page 1 as fine. A full page now returns NOT-MEASURED — we cannot
       prove we saw every variant. Live max is 2/502 so this is a future guard,
       not a live fix.
    2. Storefront identity join. The .json we measure must BE the product we
       asked about; a mismatched id is now NOT-MEASURED, so a wrong-handle
       orphan can't be counted as someone else's pass.
    
    Negative test extended 20 -> 23 assertions, both new guards covered with
    a passing control beside each.
    
    Re-ran live with the guards on: 502/502 MEASURED-OK on both planes, 0 bad,
    0 not-measured => PASS, identity join enforced on all 502.
    
    Scope note: this verifies catalog invariants + storefront data plane, NOT
    the full buy path (image bytes, add-to-cart, checkout) — stated rather
    than implied.
    
    Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01P9JdVA5oKccJQWkG5Qwiz5
---
 .../out/pdp-verify-FINAL-20260916.json             |  2 +-
 scripts/sanderson-onboard/pdp-verify-final.mjs     | 25 +++++++++++++++++++---
 2 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/scripts/sanderson-onboard/out/pdp-verify-FINAL-20260916.json b/scripts/sanderson-onboard/out/pdp-verify-FINAL-20260916.json
index beb46b2..b00beb7 100644
--- a/scripts/sanderson-onboard/out/pdp-verify-FINAL-20260916.json
+++ b/scripts/sanderson-onboard/out/pdp-verify-FINAL-20260916.json
@@ -1,7 +1,7 @@
 {
   "ticket": "TK-00038 / TK-11046",
   "purpose": "FINAL Sanderson PDP verification — completes the 09-14 pass that measured only 60/502 (442 x HTTP 429).",
-  "built_at": "2026-09-16T21:10:59.230Z",
+  "built_at": "2026-09-16T21:17:39.649Z",
   "population": 502,
   "tombstoned_excluded": [
     "7938828533811",
diff --git a/scripts/sanderson-onboard/pdp-verify-final.mjs b/scripts/sanderson-onboard/pdp-verify-final.mjs
index 41b7e4d..144a382 100644
--- a/scripts/sanderson-onboard/pdp-verify-final.mjs
+++ b/scripts/sanderson-onboard/pdp-verify-final.mjs
@@ -22,6 +22,9 @@ const outDir = path.join(__dir, 'out');
 const PUBLIC = 'designerwallcoverings.com';
 const sleep = ms => new Promise(r => setTimeout(r, ms));
 const SAMPLE_PRICE = 4.25;
+// If a product returns a FULL variant page we cannot prove we saw every variant, so the
+// row is NOT-MEASURED rather than silently OK (live max is 2, so this is a future guard).
+const VAR_PAGE = 20;
 
 const map = JSON.parse(fs.readFileSync(path.join(outDir, 'rollback-map-TK-11046.json'), 'utf8'));
 const targets = map.products.filter(p => p.current_status === 'ACTIVE');
@@ -79,6 +82,17 @@ if (process.argv.includes('--test')) {
   globalThis.fetch = realFetch;
   t('storefront 429 after retries => NOT-MEASURED (the 09-14 bug)', sf429.state === 'NOT-MEASURED');
   t('storefront 404 => MEASURED-BAD', sf404.state === 'MEASURED-BAD');
+  // a full variant page means we cannot prove we saw every variant => NOT-MEASURED, not OK
+  const trunc = clean();
+  while (trunc.variants.edges.length < VAR_PAGE) trunc.variants.edges.push(JSON.parse(JSON.stringify(trunc.variants.edges[0])));
+  t(`full variant page (${VAR_PAGE}) => NOT-MEASURED (truncation is not "fine")`, checkAdminNode(trunc).state === 'NOT-MEASURED');
+  // measuring a DIFFERENT product's storefront page must not count as a pass
+  globalThis.fetch = async () => ({ status: 200, ok: true, json: async () => ({ product: { id: 999, images: [{ src: 'x' }], variants: [{ title: 'Roll', price: '134.01' }] } }) });
+  const sfWrong = await storefront('some-handle', 2, 111);
+  const sfRight = await storefront('some-handle', 2, 999);
+  globalThis.fetch = realFetch;
+  t('storefront identity mismatch => NOT-MEASURED', sfWrong.state === 'NOT-MEASURED');
+  t('storefront identity match => MEASURED-OK (control)', sfRight.state === 'MEASURED-OK');
   // verdict mapping: unmeasured can never be PASS
   t('verdict: 0 bad / 0 unmeasured => PASS', verdictOf({ nBad: 0, nUnmeasured: 0 }) === 'PASS');
   t('verdict: 0 bad / 1 unmeasured => WARN (never PASS)', verdictOf({ nBad: 0, nUnmeasured: 1 }) === 'WARN');
@@ -94,7 +108,7 @@ const chunk = (a, n) => a.reduce((o, _, i) => (i % n ? o : [...o, a.slice(i, i +
 const Q = `query($ids:[ID!]!){ nodes(ids:$ids){ ... on Product {
   id status handle title vendor descriptionHtml tags publishedAt
   media(first:1){ edges{ node{ id } } }
-  variants(first:20){ edges{ node{ id sku title price inventoryItem{ measurement{ weight{ value unit } } } } } }
+  variants(first:${VAR_PAGE}){ edges{ node{ id sku title price inventoryItem{ measurement{ weight{ value unit } } } } } }
   mfCustom: metafield(namespace:"custom", key:"manufacturer_sku"){ value }
   mfDwc:    metafield(namespace:"dwc",    key:"manufacturer_sku"){ value }
 } } }`;
@@ -126,6 +140,7 @@ function checkAdminNode(n) {
   if (!n) return { state: 'NOT-MEASURED', reasons: ['admin read failed'] };
   const bad = [];
   const vs = (n.variants?.edges || []).map(e => e.node);
+  if (vs.length >= VAR_PAGE) return { state: 'NOT-MEASURED', reasons: [`variant page full (${vs.length}>=${VAR_PAGE}) — cannot prove all variants seen`], handle: n.handle, title: n.title };
   const samples = vs.filter(v => /sample/i.test(v.title) || /-sample$/i.test(v.sku || ''));
   const sellable = vs.filter(v => !samples.includes(v));
   const prices = sellable.map(v => Number(v.price));
@@ -157,7 +172,7 @@ export function verdictOf({ nBad, nUnmeasured }) {
 }
 
 /* ---------- Plane B: public storefront PDP (paced, retrying) ---------- */
-async function storefront(handle, tries = 4) {
+async function storefront(handle, tries = 4, expectId = null) {
   for (let a = 0; a < tries; a++) {
     let r;
     try { r = await fetch(`https://${PUBLIC}/products/${handle}.json`, { headers: { 'User-Agent': 'DW-internal-pdp-verify/1.0' } }); }
@@ -168,6 +183,10 @@ async function storefront(handle, tries = 4) {
     let j; try { j = await r.json(); } catch { return { state: 'NOT-MEASURED', reasons: ['unparseable body'] }; }
     const pr = j.product;
     if (!pr) return { state: 'NOT-MEASURED', reasons: ['no product in body'] };
+    // identity join — guard against measuring a DIFFERENT product's page (wrong-handle orphan)
+    if (expectId && String(pr.id) !== String(expectId)) {
+      return { state: 'NOT-MEASURED', reasons: [`storefront identity mismatch: got id ${pr.id}, expected ${expectId}`] };
+    }
     const bad = [];
     if (!(pr.images || []).length) bad.push('storefront: no image');
     const vs = pr.variants || [];
@@ -184,7 +203,7 @@ let i = 0;
 for (const p of targets) {
   const a = checkAdmin(p);
   const handle = a.handle || p.handle;
-  const b = handle ? await storefront(handle) : { state: 'NOT-MEASURED', reasons: ['no handle'] };
+  const b = handle ? await storefront(handle, 4, p.product_id) : { state: 'NOT-MEASURED', reasons: ['no handle'] };
   rows.push({ sku: p.sku, product_id: p.product_id, handle, admin: a, storefront: b });
   if (++i % 25 === 0) process.stdout.write(`  storefront: ${i}/${targets.length}\r`);
   await sleep(220);   // ~4.5 req/s — the pacing the 09-14 pass lacked

← ef068c8 TK-11357: stop the action-C tripwire fabricating success rec  ·  back to Designerwallcoverings  ·  chore: lint, refactor, v0.1.17 (TK-00038 session close) 0b26a67 →