← back to Designerwallcoverings
Correct Thibaut source-gap evidence and stage specifications request
3e683ff02c6f08e2ca1e2dcf4163726826f3f850 · 2026-09-10 20:19:49 -0700 · Steve Abrams
Files touched
M scripts/thibaut-wide-width-length-gap/diagnose.mjsA scripts/thibaut-wide-width-length-gap/source-status.mjsA scripts/thibaut-wide-width-length-gap/source-status.test.mjs
Diff
commit 3e683ff02c6f08e2ca1e2dcf4163726826f3f850
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Sep 10 20:19:49 2026 -0700
Correct Thibaut source-gap evidence and stage specifications request
---
scripts/thibaut-wide-width-length-gap/diagnose.mjs | 29 +++++++++++++---------
.../source-status.mjs | 9 +++++++
.../source-status.test.mjs | 16 ++++++++++++
3 files changed, 42 insertions(+), 12 deletions(-)
diff --git a/scripts/thibaut-wide-width-length-gap/diagnose.mjs b/scripts/thibaut-wide-width-length-gap/diagnose.mjs
index 9be4306..7153627 100644
--- a/scripts/thibaut-wide-width-length-gap/diagnose.mjs
+++ b/scripts/thibaut-wide-width-length-gap/diagnose.mjs
@@ -2,14 +2,14 @@
/**
* TK-11358 — Thibaut "Wide Width" (TWW-prefix) roll-length data-sourcing gap diagnostic.
* READ-ONLY. Confirms live Shopify state + joins against the local dw_unified thibaut_catalog
- * staging table to show that the SOURCE has no roll-length data for these SKUs (not a
- * carry-through/backfill bug — a genuine vendor-PDP data gap, verified via the raw scraped
- * `specs.source_url` + `specs.pdp_single_roll_yd: 0` captured at scrape time).
+ * staging table. Blank/zero staging values and missing joins are separate evidence classes.
+ * This query does not verify current vendor PDPs or cover the full four-vendor canary.
*
- * Usage: node diagnose.mjs > out/report.json
+ * Usage: node diagnose.mjs (writes out/report.json; prints a summary)
*/
import { readFileSync, writeFileSync } from 'node:fs';
-import { execSync } from 'node:child_process';
+import { execFileSync } from 'node:child_process';
+import { sourceStatus } from './source-status.mjs';
const envTxt = readFileSync('/Users/macstudio3/Projects/secrets-manager/.env', 'utf8');
const TOKEN = envTxt.split('\n').find(l => l.startsWith('SHOPIFY_ADMIN_TOKEN='))?.split('=').slice(1).join('=').trim();
@@ -64,28 +64,32 @@ async function main() {
let catalogRows = [];
if (skus.length) {
const sqlList = skus.map(s => `'${s.replace(/'/g,"''")}'`).join(',');
- const sql = `SELECT dw_sku, mfr_sku, pattern_name, color_name, width, roll_length FROM thibaut_catalog WHERE dw_sku IN (${sqlList});`;
- const out = execSync(`psql "host=/tmp dbname=dw_unified" -t -A -F'|' -c "${sql.replace(/"/g,'\\"')}"`, { encoding: 'utf8' });
- catalogRows = out.trim().split('\n').filter(Boolean).map(l => {
- const [dw_sku, mfr_sku, pattern_name, color_name, width, roll_length] = l.split('|');
- return { dw_sku, mfr_sku, pattern_name, color_name, width, roll_length };
- });
+ const sql = `SELECT COALESCE(json_agg(t), '[]'::json) FROM (SELECT dw_sku, mfr_sku, pattern_name, color_name, width, roll_length FROM thibaut_catalog WHERE dw_sku IN (${sqlList})) t;`;
+ const out = execFileSync('psql', ['host=/tmp dbname=dw_unified', '-X', '-t', '-A', '-v', 'ON_ERROR_STOP=1', '-c', sql], { encoding: 'utf8' });
+ catalogRows = JSON.parse(out);
}
const catalogBySku = Object.fromEntries(catalogRows.map(r => [r.dw_sku, r]));
const emptyAtSource = lenLess.filter(p => {
const c = catalogBySku[p.dwsku?.value];
- return !c || !c.roll_length || /^0/.test(c.roll_length.trim()) || c.roll_length.trim() === '';
+ return ['blank', 'zero'].includes(sourceStatus(c));
});
const report = {
generated_at: new Date().toISOString(),
+ scope: 'Thibaut active Wide Width title query; not the complete four-vendor canary population',
+ source_evidence: 'Local thibaut_catalog snapshot; current vendor PDPs not checked',
active_wide_width_thibaut: active.length,
draft_wide_width_thibaut_still_queued: draft.length,
active_wide_width_length_less: lenLess.length,
length_less_confirmed_empty_at_source: emptyAtSource.length,
length_less_NOT_confirmed_empty_at_source: lenLess.length - emptyAtSource.length,
+ source_status_counts: Object.fromEntries(['blank', 'zero', 'unmatched', 'positive', 'unparsed'].map(status => [status,
+ lenLess.filter(p => sourceStatus(catalogBySku[p.dwsku?.value]) === status).length])),
rows: lenLess.map(p => ({
+ product_id: p.id,
handle: p.handle, dw_sku: p.dwsku?.value, title: p.title,
+ single_roll_length: p.sr?.value ?? null, double_roll_length: p.dr?.value ?? null,
+ source_status: sourceStatus(catalogBySku[p.dwsku?.value]),
status: p.status, createdAt: p.createdAt, updatedAt: p.updatedAt,
catalog: catalogBySku[p.dwsku?.value] || null,
})),
@@ -97,6 +101,7 @@ async function main() {
active_wide_width_length_less: report.active_wide_width_length_less,
length_less_confirmed_empty_at_source: report.length_less_confirmed_empty_at_source,
length_less_NOT_confirmed_empty_at_source: report.length_less_NOT_confirmed_empty_at_source,
+ source_status_counts: report.source_status_counts,
}, null, 2));
}
main().catch(e => { console.error('ERROR', e); process.exit(1); });
diff --git a/scripts/thibaut-wide-width-length-gap/source-status.mjs b/scripts/thibaut-wide-width-length-gap/source-status.mjs
new file mode 100644
index 0000000..9a2e323
--- /dev/null
+++ b/scripts/thibaut-wide-width-length-gap/source-status.mjs
@@ -0,0 +1,9 @@
+// Missing joins are unknown; only actual staging values can be classified.
+export function sourceStatus(catalog) {
+ if (!catalog) return 'unmatched';
+ const length = String(catalog.roll_length ?? '').trim();
+ if (!length) return 'blank';
+ const number = length.match(/^(\d+(?:\.\d+)?)(?=\s|$)/);
+ if (!number) return 'unparsed';
+ return Number(number[1]) === 0 ? 'zero' : 'positive';
+}
diff --git a/scripts/thibaut-wide-width-length-gap/source-status.test.mjs b/scripts/thibaut-wide-width-length-gap/source-status.test.mjs
new file mode 100644
index 0000000..b6829da
--- /dev/null
+++ b/scripts/thibaut-wide-width-length-gap/source-status.test.mjs
@@ -0,0 +1,16 @@
+import test from 'node:test';
+import assert from 'node:assert/strict';
+import { sourceStatus } from './source-status.mjs';
+
+test('unknown joins and invalid source text are not confirmed empty', () => {
+ assert.equal(sourceStatus(null), 'unmatched');
+ assert.equal(sourceStatus(undefined), 'unmatched');
+ assert.equal(sourceStatus({ roll_length: 'TBD' }), 'unparsed');
+ assert.equal(sourceStatus({ roll_length: '0 unknown label' }), 'zero');
+});
+
+test('blank and numeric zero differ from positive lengths with leading zero', () => {
+ for (const roll_length of ['', ' ', null]) assert.equal(sourceStatus({ roll_length }), 'blank');
+ for (const roll_length of ['0 yd (0.00 m)', '0.00 m', '0']) assert.equal(sourceStatus({ roll_length }), 'zero');
+ for (const roll_length of ['0.5 yd', '09 yd', '9 yd (8.23 m)']) assert.equal(sourceStatus({ roll_length }), 'positive');
+});
← 1a161c2 auto-data-snapshot: 2026-09-10T18:07:04 (15 data files) — da
·
back to Designerwallcoverings
·
auto-data-snapshot: 2026-09-10T21:50:55 (1 data files) — scr 768920f →