← 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
M scripts/sanderson-onboard/out/pdp-verify-FINAL-20260916.jsonM scripts/sanderson-onboard/pdp-verify-final.mjs
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 →