[object Object]

← back to Designerwallcoverings

TK-11471: every create payload carries a guard-sourced weight

231a90eadf9e03a2ec9c8d636ee1f8efb667f617 · 2026-09-11 11:30:32 -0700 · Steve Abrams

(B) four sites shipped variants with NO weight field at all — a product created there was born
at zero weight and could only be saved by a downstream heal:
  artmura/push-artmura-live.js, fallingstar/push-fallingstar-live.js,
  tres-tintas/push-tres-tintas-live.js, zoffany/push-zoffany-live.js

(C) eight build-payloads.mjs carried the literals weight: 3 / weight: 0.25 (2be0da4). Same
numbers, but a magic number cannot follow TYPE_DEFAULT_LB when it changes:
  greenland, knoll, maharam, muralsource, osborne, rigo, stout, stroheim

All twelve now emit weight: resolveWeightLb(variant, { productType }), weight_unit: 'lb' from
scripts/lib/weight-guard.mjs. Values are unchanged at 3.0 lb sellable / 0.25 lb sample — this is
a source-of-truth change, not a repricing.

verification/tk11471/create-weight-wiring.test.mjs proves it three ways (static wiring, a drift
check that the productType handed to resolveWeightLb matches each payload's own product_type
literal, and BEHAVIOURAL evaluation of all 20 extracted expressions against the real module),
plus negative tests for a dropped weight, a returning magic number and a dropped import.

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

Files touched

Diff

commit 231a90eadf9e03a2ec9c8d636ee1f8efb667f617
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Fri Sep 11 11:30:32 2026 -0700

    TK-11471: every create payload carries a guard-sourced weight
    
    (B) four sites shipped variants with NO weight field at all — a product created there was born
    at zero weight and could only be saved by a downstream heal:
      artmura/push-artmura-live.js, fallingstar/push-fallingstar-live.js,
      tres-tintas/push-tres-tintas-live.js, zoffany/push-zoffany-live.js
    
    (C) eight build-payloads.mjs carried the literals weight: 3 / weight: 0.25 (2be0da4). Same
    numbers, but a magic number cannot follow TYPE_DEFAULT_LB when it changes:
      greenland, knoll, maharam, muralsource, osborne, rigo, stout, stroheim
    
    All twelve now emit weight: resolveWeightLb(variant, { productType }), weight_unit: 'lb' from
    scripts/lib/weight-guard.mjs. Values are unchanged at 3.0 lb sellable / 0.25 lb sample — this is
    a source-of-truth change, not a repricing.
    
    verification/tk11471/create-weight-wiring.test.mjs proves it three ways (static wiring, a drift
    check that the productType handed to resolveWeightLb matches each payload's own product_type
    literal, and BEHAVIOURAL evaluation of all 20 extracted expressions against the real module),
    plus negative tests for a dropped weight, a returning magic number and a dropped import.
    
    Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01FJHxAzaEMMxado57mFjiCk
---
 scripts/artmura-onboard/push-artmura-live.js       |   5 +-
 .../fallingstar-onboard/push-fallingstar-live.js   |   5 +-
 scripts/greenland-onboard/build-payloads.mjs       |   3 +-
 scripts/knoll-onboard/build-payloads.mjs           |   5 +-
 scripts/maharam-onboard/build-payloads.mjs         |   3 +-
 scripts/muralsource-onboard/build-payloads.mjs     |   5 +-
 scripts/osborne-onboard/build-payloads.mjs         |   5 +-
 scripts/rigo-onboard/build-payloads.mjs            |   3 +-
 scripts/stout-onboard/build-payloads.mjs           |   5 +-
 scripts/stroheim-onboard/build-payloads.mjs        |   5 +-
 .../tres-tintas-onboard/push-tres-tintas-live.js   |   5 +-
 scripts/zoffany-onboard/push-zoffany-live.js       |   2 +
 verification/tk11471/create-weight-wiring.test.mjs | 155 +++++++++++++++++++++
 13 files changed, 187 insertions(+), 19 deletions(-)

diff --git a/scripts/artmura-onboard/push-artmura-live.js b/scripts/artmura-onboard/push-artmura-live.js
index 2894a8d..020bec3 100644
--- a/scripts/artmura-onboard/push-artmura-live.js
+++ b/scripts/artmura-onboard/push-artmura-live.js
@@ -17,6 +17,7 @@ const https = require('https');
 const fs = require('fs');
 const path = require('path');
 const { Pool } = require('pg');
+const { resolveWeightLb } = require('../lib/weight-guard.mjs');  // GUARD TK-11471 (create-side weight: no magic numbers)
 
 const STORE = process.env.SHOPIFY_STORE || 'designer-laboratory-sandbox.myshopify.com';
 const TOKEN = process.env.SHOPIFY_ADMIN_TOKEN || process.env.SHOPIFY_PRODUCT_TOKEN;
@@ -172,8 +173,8 @@ function buildPayload(r, dwSku) {
     ...(metafields.length ? { metafields } : {}),
     options: [{ name: 'Size' }],
     variants: [
-      { sku: dwSku, price: String(r.price_newwall_retail), option1: 'Yard', taxable: true, requires_shipping: true },
-      { sku: `${dwSku}-Sample`, price: String(r.sample_price || 5), option1: 'Sample', taxable: true, requires_shipping: true },
+      { sku: dwSku, price: String(r.price_newwall_retail), option1: 'Yard', taxable: true, requires_shipping: true, weight: resolveWeightLb({ sku: dwSku, option1: 'Yard' }, { productType: r.product_type || 'Wallcoverings' }), weight_unit: 'lb' /* TK-11471 */ },
+      { sku: `${dwSku}-Sample`, price: String(r.sample_price || 5), option1: 'Sample', taxable: true, requires_shipping: true, weight: resolveWeightLb({ sku: `${dwSku}-Sample`, option1: 'Sample' }, { productType: r.product_type || 'Wallcoverings' }), weight_unit: 'lb' /* TK-11471 */ },
     ],
   } };
 }
diff --git a/scripts/fallingstar-onboard/push-fallingstar-live.js b/scripts/fallingstar-onboard/push-fallingstar-live.js
index 1ee8c5b..e0bfb70 100644
--- a/scripts/fallingstar-onboard/push-fallingstar-live.js
+++ b/scripts/fallingstar-onboard/push-fallingstar-live.js
@@ -16,6 +16,7 @@
 const https = require('https');
 const fs = require('fs');
 const path = require('path');
+const { resolveWeightLb } = require('../lib/weight-guard.mjs');  // GUARD TK-11471 (create-side weight: no magic numbers)
 
 const STORE  = process.env.SHOPIFY_STORE || 'designer-laboratory-sandbox.myshopify.com';
 const TOKEN  = process.env.SHOPIFY_ADMIN_TOKEN || process.env.SHOPIFY_PRODUCT_TOKEN;
@@ -128,8 +129,8 @@ function buildPayload(r) {
     status: STATUS, tags: r.tags.join(', '), images, body_html: specBody(r),
     options: [{ name: 'Size' }],
     variants: [
-      { sku: r.dw_sku, price: String(r.price_retail), option1: 'Roll', taxable: true, requires_shipping: true },
-      { sku: `${r.dw_sku}-Sample`, price: String(r.sample_price || 5), option1: 'Sample', taxable: true, requires_shipping: true },
+      { sku: r.dw_sku, price: String(r.price_retail), option1: 'Roll', taxable: true, requires_shipping: true, weight: resolveWeightLb({ sku: r.dw_sku, option1: 'Roll' }, { productType: 'Wallcoverings' }), weight_unit: 'lb' /* TK-11471 */ },
+      { sku: `${r.dw_sku}-Sample`, price: String(r.sample_price || 5), option1: 'Sample', taxable: true, requires_shipping: true, weight: resolveWeightLb({ sku: `${r.dw_sku}-Sample`, option1: 'Sample' }, { productType: 'Wallcoverings' }), weight_unit: 'lb' /* TK-11471 */ },
     ],
     metafields,
   } };
diff --git a/scripts/greenland-onboard/build-payloads.mjs b/scripts/greenland-onboard/build-payloads.mjs
index 58e0c6c..5303ce0 100644
--- a/scripts/greenland-onboard/build-payloads.mjs
+++ b/scripts/greenland-onboard/build-payloads.mjs
@@ -13,6 +13,7 @@ import path from 'node:path';
 import { fileURLToPath } from 'node:url';
 import { execSync } from 'node:child_process';
 import { specMetafields } from '../_spec-to-metafields.mjs';
+import { resolveWeightLb } from '../lib/weight-guard.mjs'; // GUARD TK-11471 (create-side weight: no magic numbers)
 
 const HERE = path.dirname(fileURLToPath(import.meta.url));
 const OUT = path.join(HERE, 'out');
@@ -86,7 +87,7 @@ function main() {
         // Image is downloaded + base64-attached post-create by create-drafts.mjs.
         variants: [
           // TK-11357 Fix A: sample-only until a real cost list lands (launch-pricing ADDs the priced Roll).
-          { sku: `${sku}-Sample`, price: SAMPLE_PRICE, option1: 'Sample', taxable: true, requires_shipping: true, inventory_management: null, weight: 0.25, weight_unit: 'lb' /* TK-11414 */ },
+          { sku: `${sku}-Sample`, price: SAMPLE_PRICE, option1: 'Sample', taxable: true, requires_shipping: true, inventory_management: null, weight: resolveWeightLb({ sku: `${sku}-Sample`, option1: 'Sample' }, { productType: 'Wallcovering' }), weight_unit: 'lb' /* TK-11471: shared weight-guard, no magic number */ },
         ],
       },
       metafields: metafields(r),
diff --git a/scripts/knoll-onboard/build-payloads.mjs b/scripts/knoll-onboard/build-payloads.mjs
index ae0cf34..3a85ce1 100644
--- a/scripts/knoll-onboard/build-payloads.mjs
+++ b/scripts/knoll-onboard/build-payloads.mjs
@@ -15,6 +15,7 @@ import { fileURLToPath } from 'node:url';
 import { execSync } from 'node:child_process';
 import { specMetafields } from '../_spec-to-metafields.mjs';
 import { keepOwnImages } from '../_own-image-filter.mjs';   // TK-11073 Wave 3 defensive image filter
+import { resolveWeightLb } from '../lib/weight-guard.mjs'; // GUARD TK-11471 (create-side weight: no magic numbers)
 
 const HERE = path.dirname(fileURLToPath(import.meta.url));
 const OUT = path.join(HERE, 'out');
@@ -110,8 +111,8 @@ function main() {
         status: 'draft', tags: tags(r) + (priced ? '' : ', quotes, Needs-Price'), body_html: bodyHtml(r),
         options: [{ name: 'Format' }],
         variants: [
-          { sku, price: rollPrice, option1: 'Roll', taxable: true, requires_shipping: true, inventory_management: null, weight: 3, weight_unit: 'lb' /* TK-11414 */ },
-          { sku: `${sku}-Sample`, price: SAMPLE_PRICE, option1: 'Sample', taxable: true, requires_shipping: true, inventory_management: null, weight: 0.25, weight_unit: 'lb' /* TK-11414 */ },
+          { sku, price: rollPrice, option1: 'Roll', taxable: true, requires_shipping: true, inventory_management: null, weight: resolveWeightLb({ sku: sku, option1: 'Roll' }, { productType: 'Wallcovering' }), weight_unit: 'lb' /* TK-11471: shared weight-guard, no magic number */ },
+          { sku: `${sku}-Sample`, price: SAMPLE_PRICE, option1: 'Sample', taxable: true, requires_shipping: true, inventory_management: null, weight: resolveWeightLb({ sku: `${sku}-Sample`, option1: 'Sample' }, { productType: 'Wallcovering' }), weight_unit: 'lb' /* TK-11471: shared weight-guard, no magic number */ },
         ],
       },
       metafields: metafields(r),
diff --git a/scripts/maharam-onboard/build-payloads.mjs b/scripts/maharam-onboard/build-payloads.mjs
index 05f8d96..4ef182a 100644
--- a/scripts/maharam-onboard/build-payloads.mjs
+++ b/scripts/maharam-onboard/build-payloads.mjs
@@ -24,6 +24,7 @@ import { fileURLToPath } from 'node:url';
 import { execSync } from 'node:child_process';
 import { specMetafields } from '../_spec-to-metafields.mjs';
 import { keepOwnImages } from '../_own-image-filter.mjs';   // TK-11073 Wave 3 defensive image filter
+import { resolveWeightLb } from '../lib/weight-guard.mjs'; // GUARD TK-11471 (create-side weight: no magic numbers)
 
 const HERE = path.dirname(fileURLToPath(import.meta.url));
 const OUT = path.join(HERE, 'out');
@@ -132,7 +133,7 @@ function main() {
         variants: [
           // SAMPLE-ONLY: the sole sellable variant is the $4.25 memo. No inventory at
           // draft time (inventory_management:null) — go-live enables tracking + qty.
-          { sku: `${sku}-Sample`, price: samplePrice, option1: 'Sample', taxable: true, requires_shipping: true, inventory_management: null, weight: 0.25, weight_unit: 'lb' /* TK-11414 */ },
+          { sku: `${sku}-Sample`, price: samplePrice, option1: 'Sample', taxable: true, requires_shipping: true, inventory_management: null, weight: resolveWeightLb({ sku: `${sku}-Sample`, option1: 'Sample' }, { productType: 'Wallcovering' }), weight_unit: 'lb' /* TK-11471: shared weight-guard, no magic number */ },
         ],
       },
       metafields: metafields(r),
diff --git a/scripts/muralsource-onboard/build-payloads.mjs b/scripts/muralsource-onboard/build-payloads.mjs
index c46652d..38b8c33 100644
--- a/scripts/muralsource-onboard/build-payloads.mjs
+++ b/scripts/muralsource-onboard/build-payloads.mjs
@@ -25,6 +25,7 @@ import { fileURLToPath } from 'node:url';
 import { execSync } from 'node:child_process';
 import { specMetafields } from '../_spec-to-metafields.mjs';
 import { keepOwnImages } from '../_own-image-filter.mjs';   // TK-11073 Wave 3 defensive image filter
+import { resolveWeightLb } from '../lib/weight-guard.mjs'; // GUARD TK-11471 (create-side weight: no magic numbers)
 
 const HERE = path.dirname(fileURLToPath(import.meta.url));
 const OUT = path.join(HERE, 'out');
@@ -157,8 +158,8 @@ function main() {
         variants: [
           // Sellable roll variant at price_retail + $4.25 memo Sample. inventory_management:null
           // at draft time; go-live enables tracking + on_hand qty on BOTH variants.
-          { sku, price, option1: 'Priced Per Single Roll', taxable: true, requires_shipping: true, inventory_policy: 'deny', inventory_management: null, weight: 3, weight_unit: 'lb' /* TK-11414 */ },
-          { sku: `${sku}-Sample`, price: SAMPLE_PRICE, option1: 'Sample', taxable: true, requires_shipping: true, inventory_policy: 'deny', inventory_management: null, weight: 0.25, weight_unit: 'lb' /* TK-11414 */ },
+          { sku, price, option1: 'Priced Per Single Roll', taxable: true, requires_shipping: true, inventory_policy: 'deny', inventory_management: null, weight: resolveWeightLb({ sku: sku, option1: 'Priced Per Single Roll' }, { productType: 'Wallcovering' }), weight_unit: 'lb' /* TK-11471: shared weight-guard, no magic number */ },
+          { sku: `${sku}-Sample`, price: SAMPLE_PRICE, option1: 'Sample', taxable: true, requires_shipping: true, inventory_policy: 'deny', inventory_management: null, weight: resolveWeightLb({ sku: `${sku}-Sample`, option1: 'Sample' }, { productType: 'Wallcovering' }), weight_unit: 'lb' /* TK-11471: shared weight-guard, no magic number */ },
         ],
       },
       metafields: metafields(r),
diff --git a/scripts/osborne-onboard/build-payloads.mjs b/scripts/osborne-onboard/build-payloads.mjs
index c90d058..7876aa3 100644
--- a/scripts/osborne-onboard/build-payloads.mjs
+++ b/scripts/osborne-onboard/build-payloads.mjs
@@ -21,6 +21,7 @@ import path from 'node:path';
 import { fileURLToPath } from 'node:url';
 import { execSync } from 'node:child_process';
 import { specMetafields } from '../_spec-to-metafields.mjs';
+import { resolveWeightLb } from '../lib/weight-guard.mjs'; // GUARD TK-11471 (create-side weight: no magic numbers)
 
 const DB = 'postgresql:///dw_unified?host=/tmp';
 
@@ -151,8 +152,8 @@ function main() {
         images: r.image_url ? [{ src: r.image_url, alt: esc(r.title_core) }] : [],
         options: [{ name: 'Size' }],
         variants: [
-          { sku, price: String(r.retail), option1: 'Roll', taxable: true, requires_shipping: true, weight: 3, weight_unit: 'lb' /* TK-11414 */ },
-          { sku: `${sku}-Sample`, price: SAMPLE_PRICE, option1: 'Sample', taxable: true, requires_shipping: true, weight: 0.25, weight_unit: 'lb' /* TK-11414 */ },
+          { sku, price: String(r.retail), option1: 'Roll', taxable: true, requires_shipping: true, weight: resolveWeightLb({ sku: sku, option1: 'Roll' }, { productType: 'Wallcovering' }), weight_unit: 'lb' /* TK-11471: shared weight-guard, no magic number */ },
+          { sku: `${sku}-Sample`, price: SAMPLE_PRICE, option1: 'Sample', taxable: true, requires_shipping: true, weight: resolveWeightLb({ sku: `${sku}-Sample`, option1: 'Sample' }, { productType: 'Wallcovering' }), weight_unit: 'lb' /* TK-11471: shared weight-guard, no magic number */ },
         ],
       },
       metafields: metafields(r, e),
diff --git a/scripts/rigo-onboard/build-payloads.mjs b/scripts/rigo-onboard/build-payloads.mjs
index b116aa0..8d1a14a 100644
--- a/scripts/rigo-onboard/build-payloads.mjs
+++ b/scripts/rigo-onboard/build-payloads.mjs
@@ -20,6 +20,7 @@ import path from 'node:path';
 import { fileURLToPath } from 'node:url';
 import { execFileSync } from 'node:child_process';
 import { specMetafields } from '../_spec-to-metafields.mjs';
+import { resolveWeightLb } from '../lib/weight-guard.mjs'; // GUARD TK-11471 (create-side weight: no magic numbers)
 
 const HERE = path.dirname(fileURLToPath(import.meta.url));
 const OUT = path.join(HERE, 'out');
@@ -119,7 +120,7 @@ for (const r of rows) {
       options: [{ name: 'Format' }],
       variants: [
         // TK-11357 Fix A: sample-only until a real cost list lands (launch-pricing ADDs the priced Yard).
-        { sku: `${r.dw_sku}-Sample`, price: SAMPLE_PRICE, option1: 'Sample', taxable: true, requires_shipping: true, inventory_management: null, weight: 0.25, weight_unit: 'lb' /* TK-11414 */ },
+        { sku: `${r.dw_sku}-Sample`, price: SAMPLE_PRICE, option1: 'Sample', taxable: true, requires_shipping: true, inventory_management: null, weight: resolveWeightLb({ sku: `${r.dw_sku}-Sample`, option1: 'Sample' }, { productType: 'Wallcovering' }), weight_unit: 'lb' /* TK-11471: shared weight-guard, no magic number */ },
       ],
     },
     metafields: metafields(r),
diff --git a/scripts/stout-onboard/build-payloads.mjs b/scripts/stout-onboard/build-payloads.mjs
index 21954dd..f2afbbe 100644
--- a/scripts/stout-onboard/build-payloads.mjs
+++ b/scripts/stout-onboard/build-payloads.mjs
@@ -28,6 +28,7 @@ import { fileURLToPath } from 'node:url';
 import { execSync } from 'node:child_process';
 import { specMetafields } from '../_spec-to-metafields.mjs';
 import { keepOwnImages } from '../_own-image-filter.mjs';   // TK-11073 Wave 3 defensive image filter
+import { resolveWeightLb } from '../lib/weight-guard.mjs'; // GUARD TK-11471 (create-side weight: no magic numbers)
 
 const HERE = path.dirname(fileURLToPath(import.meta.url));
 const OUT = path.join(HERE, 'out');
@@ -164,8 +165,8 @@ function main() {
         variants: [
           // Sellable roll variant at price_retail + $4.25 memo Sample. inventory_management:null
           // at draft time; go-live enables tracking + on_hand qty on BOTH variants.
-          { sku, price, option1: 'Double Roll', taxable: true, requires_shipping: true, inventory_policy: 'deny', inventory_management: null, weight: 3, weight_unit: 'lb' /* TK-11414 */ },
-          { sku: `${sku}-Sample`, price: SAMPLE_PRICE, option1: 'Sample', taxable: true, requires_shipping: true, inventory_policy: 'deny', inventory_management: null, weight: 0.25, weight_unit: 'lb' /* TK-11414 */ },
+          { sku, price, option1: 'Double Roll', taxable: true, requires_shipping: true, inventory_policy: 'deny', inventory_management: null, weight: resolveWeightLb({ sku: sku, option1: 'Double Roll' }, { productType: 'Wallcovering' }), weight_unit: 'lb' /* TK-11471: shared weight-guard, no magic number */ },
+          { sku: `${sku}-Sample`, price: SAMPLE_PRICE, option1: 'Sample', taxable: true, requires_shipping: true, inventory_policy: 'deny', inventory_management: null, weight: resolveWeightLb({ sku: `${sku}-Sample`, option1: 'Sample' }, { productType: 'Wallcovering' }), weight_unit: 'lb' /* TK-11471: shared weight-guard, no magic number */ },
         ],
       },
       metafields: metafields(r),
diff --git a/scripts/stroheim-onboard/build-payloads.mjs b/scripts/stroheim-onboard/build-payloads.mjs
index 19e64e3..82e8f36 100644
--- a/scripts/stroheim-onboard/build-payloads.mjs
+++ b/scripts/stroheim-onboard/build-payloads.mjs
@@ -23,6 +23,7 @@ import path from 'node:path';
 import { fileURLToPath } from 'node:url';
 import { execFileSync } from 'node:child_process';
 import { specMetafields } from '../_spec-to-metafields.mjs';
+import { resolveWeightLb } from '../lib/weight-guard.mjs'; // GUARD TK-11471 (create-side weight: no magic numbers)
 
 const HERE = path.dirname(fileURLToPath(import.meta.url));
 const OUT = path.join(HERE, 'out');
@@ -132,8 +133,8 @@ function main() {
         status: 'draft', tags: tags(r), body_html: bodyHtml(r),
         options: [{ name: 'Size' }],
         variants: [
-          { sku, price, option1: 'Priced Per Single Roll', taxable: true, requires_shipping: true, inventory_policy: 'deny', inventory_management: null, weight: 3, weight_unit: 'lb' /* TK-11414 */ },
-          { sku: `${sku}-Sample`, price: SAMPLE_PRICE, option1: 'Sample', taxable: true, requires_shipping: true, inventory_policy: 'deny', inventory_management: null, weight: 0.25, weight_unit: 'lb' /* TK-11414 */ },
+          { sku, price, option1: 'Priced Per Single Roll', taxable: true, requires_shipping: true, inventory_policy: 'deny', inventory_management: null, weight: resolveWeightLb({ sku: sku, option1: 'Priced Per Single Roll' }, { productType: 'Wallcovering' }), weight_unit: 'lb' /* TK-11471: shared weight-guard, no magic number */ },
+          { sku: `${sku}-Sample`, price: SAMPLE_PRICE, option1: 'Sample', taxable: true, requires_shipping: true, inventory_policy: 'deny', inventory_management: null, weight: resolveWeightLb({ sku: `${sku}-Sample`, option1: 'Sample' }, { productType: 'Wallcovering' }), weight_unit: 'lb' /* TK-11471: shared weight-guard, no magic number */ },
         ],
       },
       metafields: metafields(r),
diff --git a/scripts/tres-tintas-onboard/push-tres-tintas-live.js b/scripts/tres-tintas-onboard/push-tres-tintas-live.js
index 81af3e1..4c20768 100644
--- a/scripts/tres-tintas-onboard/push-tres-tintas-live.js
+++ b/scripts/tres-tintas-onboard/push-tres-tintas-live.js
@@ -15,6 +15,7 @@ const https = require('https');
 const fs = require('fs');
 const path = require('path');
 const { Client } = require('pg');
+const { resolveWeightLb } = require('../lib/weight-guard.mjs');  // GUARD TK-11471 (create-side weight: no magic numbers)
 
 const STORE = process.env.SHOPIFY_STORE;
 const TOKEN = process.env.SHOPIFY_ADMIN_TOKEN;
@@ -124,8 +125,8 @@ function buildPayload(r) {
     options: [{ name: 'Size' }],
     variants: [
       { sku: r.dw_sku, price: String(r.dw_retail), option1: 'Roll', taxable: true, requires_shipping: true,
-        inventory_management: 'shopify', cost: String(r.cost_newwall) },
-      { sku: `${r.dw_sku}-Sample`, price: SAMPLE_PRICE, option1: 'Sample', taxable: true, requires_shipping: true },
+        inventory_management: 'shopify', cost: String(r.cost_newwall), weight: resolveWeightLb({ sku: r.dw_sku, option1: 'Roll' }, { productType: 'Wallcoverings' }), weight_unit: 'lb' /* TK-11471 */ },
+      { sku: `${r.dw_sku}-Sample`, price: SAMPLE_PRICE, option1: 'Sample', taxable: true, requires_shipping: true, weight: resolveWeightLb({ sku: `${r.dw_sku}-Sample`, option1: 'Sample' }, { productType: 'Wallcoverings' }), weight_unit: 'lb' /* TK-11471 */ },
     ],
   } };
 }
diff --git a/scripts/zoffany-onboard/push-zoffany-live.js b/scripts/zoffany-onboard/push-zoffany-live.js
index ff673a9..2bba2b5 100644
--- a/scripts/zoffany-onboard/push-zoffany-live.js
+++ b/scripts/zoffany-onboard/push-zoffany-live.js
@@ -19,6 +19,7 @@
  */
 const https = require('https');
 const { Pool } = require('pg');
+const { resolveWeightLb } = require('../lib/weight-guard.mjs');  // GUARD TK-11471 (create-side weight: no magic numbers)
 
 const STORE = process.env.SHOPIFY_STORE || 'designer-laboratory-sandbox.myshopify.com';
 const TOKEN = process.env.SHOPIFY_ADMIN_TOKEN || process.env.SHOPIFY_PRODUCT_TOKEN;
@@ -166,6 +167,7 @@ function buildPayload(r) {
       sku: r.dw_sku, price: String(r.price_retail), option1: 'Default Title',
       taxable: true, requires_shipping: true,
       inventory_management: null, inventory_policy: 'continue',
+      weight: resolveWeightLb({ sku: r.dw_sku, option1: 'Default Title' }, { productType: r.is_fabric ? 'Fabric' : 'Wallcovering' }), weight_unit: 'lb' /* TK-11471 */,
     }],
   };
   // Spec metafields (TK-10039): preserve any already on the payload, no duplicate namespace.key.
diff --git a/verification/tk11471/create-weight-wiring.test.mjs b/verification/tk11471/create-weight-wiring.test.mjs
new file mode 100644
index 0000000..7e0e38a
--- /dev/null
+++ b/verification/tk11471/create-weight-wiring.test.mjs
@@ -0,0 +1,155 @@
+#!/usr/bin/env node
+/**
+ * create-weight-wiring.test.mjs — TK-11471. Offline proof that every create-payload site emits a
+ * real positive weight from the SHARED module.  node verification/tk11471/create-weight-wiring.test.mjs
+ *
+ * Three layers, because a static grep alone would have accepted `weight: 0`:
+ *   1. STATIC  — every variant literal carries weight + weight_unit:'lb', sourced from
+ *                weight-guard (no magic numbers left).
+ *   2. DRIFT   — the productType handed to resolveWeightLb matches the payload's own product_type
+ *                literal, so the two cannot silently diverge.
+ *   3. BEHAVIOUR — each resolveWeightLb(...) expression is lifted out of the source and EVALUATED
+ *                against the real module with realistic bindings; the result must be > 0 and equal
+ *                the approved default for that product type / sample.
+ * No network, no DB, no Shopify.
+ */
+import fs from 'node:fs';
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+import * as WG from '../../scripts/lib/weight-guard.mjs';
+
+const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../scripts');
+
+export const CREATE_SITES = [
+  // (B) had NO weight at all before TK-11471
+  'artmura-onboard/push-artmura-live.js',
+  'fallingstar-onboard/push-fallingstar-live.js',
+  'tres-tintas-onboard/push-tres-tintas-live.js',
+  'zoffany-onboard/push-zoffany-live.js',
+  // (C) carried hardcoded literal weights (3 / 0.25), now the shared module
+  'greenland-onboard/build-payloads.mjs',
+  'knoll-onboard/build-payloads.mjs',
+  'maharam-onboard/build-payloads.mjs',
+  'muralsource-onboard/build-payloads.mjs',
+  'osborne-onboard/build-payloads.mjs',
+  'rigo-onboard/build-payloads.mjs',
+  'stout-onboard/build-payloads.mjs',
+  'stroheim-onboard/build-payloads.mjs',
+];
+
+let pass = 0, fail = 0;
+const ok = (n, c, d = '') => { if (c) { pass++; console.log(`  ok   ${n}`); } else { fail++; console.error(`  FAIL ${n}${d ? ' — ' + d : ''}`); } };
+
+/** Pull every `resolveWeightLb( ... )` call out of source, balanced-paren safe. */
+export function extractCalls(src) {
+  const out = [];
+  let i = 0;
+  while ((i = src.indexOf('resolveWeightLb(', i)) !== -1) {
+    const start = i + 'resolveWeightLb'.length;
+    let depth = 0, j = start;
+    for (; j < src.length; j++) {
+      if (src[j] === '(') depth++;
+      else if (src[j] === ')') { depth--; if (depth === 0) { j++; break; } }
+    }
+    out.push(src.slice(i, j));
+    i = j;
+  }
+  return out;
+}
+
+/** Balanced-brace extraction of every `{ sku ... }` variant literal (multi-line safe). */
+export function extractVariantLiterals(src) {
+  const out = [];
+  const re = /\{\s*sku[\s:,]/g;
+  let m;
+  while ((m = re.exec(src))) {
+    let depth = 0, j = m.index;
+    for (; j < src.length; j++) {
+      if (src[j] === '{') depth++;
+      else if (src[j] === '}') { depth--; if (depth === 0) { j++; break; } }
+    }
+    out.push(src.slice(m.index, j));
+    re.lastIndex = j;
+  }
+  return out;
+}
+
+/** Does this variant literal carry a weight sourced from the guard? */
+export function auditCreateSource(src) {
+  const missing = [];
+  const importsGuard = /weight-guard\.mjs/.test(src);
+  if (!importsGuard) missing.push('imports weight-guard');
+  // Any variant OBJECT LITERAL that sets a sku must also set weight + weight_unit. Scan balanced
+  // braces, not lines — tres-tintas' sellable variant spans two lines, and a line-based check
+  // reported it as weightless when it was fine (caught by this suite on the first run).
+  const variantLines = extractVariantLiterals(src).filter(v => /option1:/.test(v));
+  for (const l of variantLines) {
+    if (!/weight:/.test(l)) missing.push('variant literal with no weight');
+    else if (!/weight_unit: 'lb'/.test(l)) missing.push('variant weight with no lb unit');
+    else if (/weight:\s*[\d.]+\s*,/.test(l)) missing.push('magic-number weight');
+  }
+  if (!variantLines.length && !/resolveWeightLb\(/.test(src)) missing.push('no weighted variant found');
+  return { ok: missing.length === 0, missing: [...new Set(missing)], variantLines: variantLines.length };
+}
+
+console.log('── (1) STATIC: every create site emits a guard-sourced weight');
+for (const rel of CREATE_SITES) {
+  const p = path.join(ROOT, rel);
+  if (!fs.existsSync(p)) { fail++; console.error(`  FAIL ${rel} — missing`); continue; }
+  const r = auditCreateSource(fs.readFileSync(p, 'utf8'));
+  ok(rel, r.ok, r.missing.join(', '));
+}
+
+console.log('\n── (2) DRIFT: resolveWeightLb productType matches the payload product_type literal');
+for (const rel of CREATE_SITES.filter(f => f.endsWith('build-payloads.mjs'))) {
+  const src = fs.readFileSync(path.join(ROOT, rel), 'utf8');
+  const declared = (src.match(/product_type: '([^']+)'/) || [])[1];
+  const used = [...new Set(extractCalls(src).map(c => (c.match(/productType: '([^']+)'/) || [])[1]))];
+  ok(`${rel} (${declared})`, used.length === 1 && used[0] === declared, `declared=${declared} used=${JSON.stringify(used)}`);
+}
+
+console.log('\n── (3) BEHAVIOUR: each expression really evaluates to a positive, correct weight');
+{
+  // Realistic bindings for the identifiers the extracted expressions close over.
+  const bind = {
+    sku: 'DWX-1001', dwSku: 'DWX-1001',
+    r: { dw_sku: 'DWX-1001', product_type: null, is_fabric: false },
+  };
+  let evaluated = 0;
+  for (const rel of CREATE_SITES) {
+    const src = fs.readFileSync(path.join(ROOT, rel), 'utf8');
+    for (const call of extractCalls(src)) {
+      const fn = new Function('resolveWeightLb', 'sku', 'dwSku', 'r', `return ${call};`);
+      const lb = fn(WG.resolveWeightLb, bind.sku, bind.dwSku, bind.r);
+      const isSample = /-Sample`|option1: 'Sample'/.test(call);
+      const pt = (call.match(/productType: '([^']+)'/) || [])[1]
+             || (/Wallcoverings'/.test(call) ? 'Wallcoverings' : 'Wallcovering');
+      const want = isSample ? WG.SAMPLE_WEIGHT_LB : (WG.TYPE_DEFAULT_LB[pt] ?? WG.FALLBACK_LB);
+      ok(`${rel} :: ${isSample ? 'sample' : 'sellable'} => ${lb} lb`, lb > 0 && Math.abs(lb - want) < 1e-9, `want ${want}`);
+      evaluated++;
+    }
+  }
+  ok(`evaluated ${evaluated} expressions across ${CREATE_SITES.length} sites`, evaluated >= 20, `only ${evaluated}`);
+}
+
+console.log('\n── (4) NEGATIVE: the detector goes red on each injected fault');
+{
+  const good = fs.readFileSync(path.join(ROOT, 'knoll-onboard/build-payloads.mjs'), 'utf8');
+  ok('control: the real file passes', auditCreateSource(good).ok, JSON.stringify(auditCreateSource(good).missing));
+
+  const f1 = good.replace(/, weight: resolveWeightLb\([^\n]*?weight_unit: 'lb'[^\n]*?\*\//, '');
+  ok('a variant loses its weight => red', !auditCreateSource(f1).ok && auditCreateSource(f1).missing.includes('variant literal with no weight'));
+
+  const f2 = good.replace(/weight: resolveWeightLb\(\{ sku: sku[^\n]*?\}\), /, 'weight: 3, ');
+  ok('a magic-number weight returns => red', !auditCreateSource(f2).ok && auditCreateSource(f2).missing.includes('magic-number weight'), JSON.stringify(auditCreateSource(f2).missing));
+
+  const f3 = good.replace(/^import \{ resolveWeightLb \}.*$/m, '');
+  ok('the guard import is dropped => red', !auditCreateSource(f3).ok && auditCreateSource(f3).missing.includes('imports weight-guard'));
+
+  // BEHAVIOURAL negative: a zero weight must never be accepted as "wired"
+  ok('resolveWeightLb can never return 0 for a sellable', WG.resolveWeightLb({ sku: 'X', option1: 'Roll', weight: 0 }, { productType: 'Wallcovering' }) > 0);
+  ok('resolveWeightLb can never return 0 for a sample', WG.resolveWeightLb({ sku: 'X-Sample', option1: 'Sample' }, { productType: 'Wallcovering' }) > 0);
+}
+
+console.log(`\n${fail === 0 ? 'PASS' : 'FAIL'} — ${pass} passed, ${fail} failed`);
+process.exit(fail === 0 ? 0 : 1);

← 6a9e28e TK-11471: weight go-live gate on the 9 unguarded ACTIVATE si  ·  back to Designerwallcoverings  ·  TK-11471: Python twin of the weight guard (lib/weight_guard. e5b2795 →