[object Object]

← back to Designerwallcoverings

go-live gate: require live manufacturer_sku before ACTIVE (TK-10303)

50f482be2480916b5ec0588500d3759c03bf2c7a · 2026-08-06 12:30:46 -0700 · steve

Every onboard go-live.mjs flipped products ACTIVE without verifying a real
manufacturer_sku metafield — the latent hole behind the no-mfr go-live breach
class (TK-10291, 10 Romo products live with 0 mfr#). create-drafts logs a failed
metafieldsSet (mfFail++) but doesn't de-list, and go-live trusted it blindly.

Add a self-contained, idempotent, fail-safe guard immediately before the
status:ACTIVE mutation in all 7 gates (knoll/maharam/muralsource/osborne/
sanderson/stout/stroheim): query custom/dwc.manufacturer_sku live; if empty,
HOLD the product as draft (return {heldNoMfr}) instead of activating.

The ad-hoc-import vector remains covered by the hardened dw-golive-gate-canary
(live-verify, this session). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 50f482be2480916b5ec0588500d3759c03bf2c7a
Author: steve <steve@designerwallcoverings.com>
Date:   Thu Aug 6 12:30:46 2026 -0700

    go-live gate: require live manufacturer_sku before ACTIVE (TK-10303)
    
    Every onboard go-live.mjs flipped products ACTIVE without verifying a real
    manufacturer_sku metafield — the latent hole behind the no-mfr go-live breach
    class (TK-10291, 10 Romo products live with 0 mfr#). create-drafts logs a failed
    metafieldsSet (mfFail++) but doesn't de-list, and go-live trusted it blindly.
    
    Add a self-contained, idempotent, fail-safe guard immediately before the
    status:ACTIVE mutation in all 7 gates (knoll/maharam/muralsource/osborne/
    sanderson/stout/stroheim): query custom/dwc.manufacturer_sku live; if empty,
    HOLD the product as draft (return {heldNoMfr}) instead of activating.
    
    The ad-hoc-import vector remains covered by the hardened dw-golive-gate-canary
    (live-verify, this session). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 scripts/knoll-onboard/go-live.mjs       | 7 +++++++
 scripts/maharam-onboard/go-live.mjs     | 7 +++++++
 scripts/muralsource-onboard/go-live.mjs | 7 +++++++
 scripts/osborne-onboard/go-live.mjs     | 7 +++++++
 scripts/sanderson-onboard/go-live.mjs   | 7 +++++++
 scripts/stout-onboard/go-live.mjs       | 7 +++++++
 scripts/stroheim-onboard/go-live.mjs    | 7 +++++++
 7 files changed, 49 insertions(+)

diff --git a/scripts/knoll-onboard/go-live.mjs b/scripts/knoll-onboard/go-live.mjs
index ec6d1af..9f11f6e 100644
--- a/scripts/knoll-onboard/go-live.mjs
+++ b/scripts/knoll-onboard/go-live.mjs
@@ -75,6 +75,13 @@ async function goLive(pid, sku) {
   (r2.inventorySetQuantities?.userErrors || []).forEach(e => errs.push('setqty:' + e.message));
   const r3 = await gql(M_PUBLISH, { id: gidP, pubs: PUBLICATIONS.map(p => ({ publicationId: p })) });
   (r3.publishablePublish?.userErrors || []).forEach(e => errs.push('publish:' + e.message));
+  // TK-10303 go-live gate: never flip a product ACTIVE without a real manufacturer_sku metafield.
+  // Fail-safe backstop for the no-mfr go-live breach class (TK-10291): even if
+  // create-drafts' metafieldsSet silently failed, this HOLDS the product as draft.
+  { const _mf = await gql('query($id:ID!){product(id:$id){c:metafield(namespace:"custom",key:"manufacturer_sku"){value} d:metafield(namespace:"dwc",key:"manufacturer_sku"){value}}}', { id: gidP });
+    if (!((_mf?.product?.c?.value || _mf?.product?.d?.value || '').trim())) {
+      console.error('  ⛔ HELD (no manufacturer_sku metafield — TK-10303 go-live gate): ' + gidP);
+      return { heldNoMfr: true }; } }
   const r4 = await gql(M_ACTIVE, { id: gidP });
   (r4.productUpdate?.userErrors || []).forEach(e => errs.push('active:' + e.message));
   const status = r4.productUpdate?.product?.status;
diff --git a/scripts/maharam-onboard/go-live.mjs b/scripts/maharam-onboard/go-live.mjs
index 2c0a71a..5e16768 100644
--- a/scripts/maharam-onboard/go-live.mjs
+++ b/scripts/maharam-onboard/go-live.mjs
@@ -74,6 +74,13 @@ async function goLive(pid, sku) {
   (r2.inventorySetQuantities?.userErrors || []).forEach(e => errs.push('setqty:' + e.message));
   const r3 = await gql(M_PUBLISH, { id: gidP, pubs: PUBLICATIONS.map(p => ({ publicationId: p })) });
   (r3.publishablePublish?.userErrors || []).forEach(e => errs.push('publish:' + e.message));
+  // TK-10303 go-live gate: never flip a product ACTIVE without a real manufacturer_sku metafield.
+  // Fail-safe backstop for the no-mfr go-live breach class (TK-10291): even if
+  // create-drafts' metafieldsSet silently failed, this HOLDS the product as draft.
+  { const _mf = await gql('query($id:ID!){product(id:$id){c:metafield(namespace:"custom",key:"manufacturer_sku"){value} d:metafield(namespace:"dwc",key:"manufacturer_sku"){value}}}', { id: gidP });
+    if (!((_mf?.product?.c?.value || _mf?.product?.d?.value || '').trim())) {
+      console.error('  ⛔ HELD (no manufacturer_sku metafield — TK-10303 go-live gate): ' + gidP);
+      return { heldNoMfr: true }; } }
   const r4 = await gql(M_ACTIVE, { id: gidP });
   (r4.productUpdate?.userErrors || []).forEach(e => errs.push('active:' + e.message));
   const status = r4.productUpdate?.product?.status;
diff --git a/scripts/muralsource-onboard/go-live.mjs b/scripts/muralsource-onboard/go-live.mjs
index d7bf61c..f4e5893 100644
--- a/scripts/muralsource-onboard/go-live.mjs
+++ b/scripts/muralsource-onboard/go-live.mjs
@@ -100,6 +100,13 @@ async function goLive(pid, sku, mfrSku) {
   (r2.inventorySetQuantities?.userErrors || []).forEach(e => errs.push('setqty:' + e.message));
   const r3 = await gql(M_PUBLISH, { id: gidP, pubs: PUBLICATIONS.map(p => ({ publicationId: p })) });
   (r3.publishablePublish?.userErrors || []).forEach(e => errs.push('publish:' + e.message));
+  // TK-10303 go-live gate: never flip a product ACTIVE without a real manufacturer_sku metafield.
+  // Fail-safe backstop for the no-mfr go-live breach class (TK-10291): even if
+  // create-drafts' metafieldsSet silently failed, this HOLDS the product as draft.
+  { const _mf = await gql('query($id:ID!){product(id:$id){c:metafield(namespace:"custom",key:"manufacturer_sku"){value} d:metafield(namespace:"dwc",key:"manufacturer_sku"){value}}}', { id: gidP });
+    if (!((_mf?.product?.c?.value || _mf?.product?.d?.value || '').trim())) {
+      console.error('  ⛔ HELD (no manufacturer_sku metafield — TK-10303 go-live gate): ' + gidP);
+      return { heldNoMfr: true }; } }
   const r4 = await gql(M_ACTIVE, { id: gidP });
   (r4.productUpdate?.userErrors || []).forEach(e => errs.push('active:' + e.message));
   const status = r4.productUpdate?.product?.status;
diff --git a/scripts/osborne-onboard/go-live.mjs b/scripts/osborne-onboard/go-live.mjs
index 30e2c97..8806de1 100644
--- a/scripts/osborne-onboard/go-live.mjs
+++ b/scripts/osborne-onboard/go-live.mjs
@@ -73,6 +73,13 @@ async function goLive(pid) {
   (r2.inventorySetQuantities?.userErrors || []).forEach(e => errs.push('setqty:' + e.message));
   const r3 = await gql(M_PUBLISH, { id: gidP, pubs: PUBLICATIONS.map(p => ({ publicationId: p })) });
   (r3.publishablePublish?.userErrors || []).forEach(e => errs.push('publish:' + e.message));
+  // TK-10303 go-live gate: never flip a product ACTIVE without a real manufacturer_sku metafield.
+  // Fail-safe backstop for the no-mfr go-live breach class (TK-10291): even if
+  // create-drafts' metafieldsSet silently failed, this HOLDS the product as draft.
+  { const _mf = await gql('query($id:ID!){product(id:$id){c:metafield(namespace:"custom",key:"manufacturer_sku"){value} d:metafield(namespace:"dwc",key:"manufacturer_sku"){value}}}', { id: gidP });
+    if (!((_mf?.product?.c?.value || _mf?.product?.d?.value || '').trim())) {
+      console.error('  ⛔ HELD (no manufacturer_sku metafield — TK-10303 go-live gate): ' + gidP);
+      return { heldNoMfr: true }; } }
   const r4 = await gql(M_ACTIVE, { id: gidP });
   (r4.productUpdate?.userErrors || []).forEach(e => errs.push('active:' + e.message));
   return { status: r4.productUpdate?.product?.status, errs };
diff --git a/scripts/sanderson-onboard/go-live.mjs b/scripts/sanderson-onboard/go-live.mjs
index 2bc7c7a..0c5efc6 100644
--- a/scripts/sanderson-onboard/go-live.mjs
+++ b/scripts/sanderson-onboard/go-live.mjs
@@ -102,6 +102,13 @@ async function goLive(rec) {
   }
   const r3 = await gql(M_PUBLISH, { id: gidP, pubs: PUBLICATIONS.map(p => ({ publicationId: p })) });
   (r3.publishablePublish?.userErrors || []).forEach(e => errs.push('publish:' + e.message));
+  // TK-10303 go-live gate: never flip a product ACTIVE without a real manufacturer_sku metafield.
+  // Fail-safe backstop for the no-mfr go-live breach class (TK-10291): even if
+  // create-drafts' metafieldsSet silently failed, this HOLDS the product as draft.
+  { const _mf = await gql('query($id:ID!){product(id:$id){c:metafield(namespace:"custom",key:"manufacturer_sku"){value} d:metafield(namespace:"dwc",key:"manufacturer_sku"){value}}}', { id: gidP });
+    if (!((_mf?.product?.c?.value || _mf?.product?.d?.value || '').trim())) {
+      console.error('  ⛔ HELD (no manufacturer_sku metafield — TK-10303 go-live gate): ' + gidP);
+      return { heldNoMfr: true }; } }
   const r4 = await gql(M_ACTIVE, { id: gidP });
   (r4.productUpdate?.userErrors || []).forEach(e => errs.push('active:' + e.message));
   const status = r4.productUpdate?.product?.status;
diff --git a/scripts/stout-onboard/go-live.mjs b/scripts/stout-onboard/go-live.mjs
index 5506553..d7ff0a2 100644
--- a/scripts/stout-onboard/go-live.mjs
+++ b/scripts/stout-onboard/go-live.mjs
@@ -103,6 +103,13 @@ async function goLive(pid, sku, mfrSku) {
   (r2.inventorySetQuantities?.userErrors || []).forEach(e => errs.push('setqty:' + e.message));
   const r3 = await gql(M_PUBLISH, { id: gidP, pubs: PUBLICATIONS.map(p => ({ publicationId: p })) });
   (r3.publishablePublish?.userErrors || []).forEach(e => errs.push('publish:' + e.message));
+  // TK-10303 go-live gate: never flip a product ACTIVE without a real manufacturer_sku metafield.
+  // Fail-safe backstop for the no-mfr go-live breach class (TK-10291): even if
+  // create-drafts' metafieldsSet silently failed, this HOLDS the product as draft.
+  { const _mf = await gql('query($id:ID!){product(id:$id){c:metafield(namespace:"custom",key:"manufacturer_sku"){value} d:metafield(namespace:"dwc",key:"manufacturer_sku"){value}}}', { id: gidP });
+    if (!((_mf?.product?.c?.value || _mf?.product?.d?.value || '').trim())) {
+      console.error('  ⛔ HELD (no manufacturer_sku metafield — TK-10303 go-live gate): ' + gidP);
+      return { heldNoMfr: true }; } }
   const r4 = await gql(M_ACTIVE, { id: gidP });
   (r4.productUpdate?.userErrors || []).forEach(e => errs.push('active:' + e.message));
   const status = r4.productUpdate?.product?.status;
diff --git a/scripts/stroheim-onboard/go-live.mjs b/scripts/stroheim-onboard/go-live.mjs
index 8aea4fb..123bd7e 100644
--- a/scripts/stroheim-onboard/go-live.mjs
+++ b/scripts/stroheim-onboard/go-live.mjs
@@ -104,6 +104,13 @@ async function goLive(pid, sku, mfrSku) {
   (r2.inventorySetQuantities?.userErrors || []).forEach(e => errs.push('setqty:' + e.message));
   const r3 = await gql(M_PUBLISH, { id: gidP, pubs: PUBLICATIONS.map(p => ({ publicationId: p })) });
   (r3.publishablePublish?.userErrors || []).forEach(e => errs.push('publish:' + e.message));
+  // TK-10303 go-live gate: never flip a product ACTIVE without a real manufacturer_sku metafield.
+  // Fail-safe backstop for the no-mfr go-live breach class (TK-10291): even if
+  // create-drafts' metafieldsSet silently failed, this HOLDS the product as draft.
+  { const _mf = await gql('query($id:ID!){product(id:$id){c:metafield(namespace:"custom",key:"manufacturer_sku"){value} d:metafield(namespace:"dwc",key:"manufacturer_sku"){value}}}', { id: gidP });
+    if (!((_mf?.product?.c?.value || _mf?.product?.d?.value || '').trim())) {
+      console.error('  ⛔ HELD (no manufacturer_sku metafield — TK-10303 go-live gate): ' + gidP);
+      return { heldNoMfr: true }; } }
   const r4 = await gql(M_ACTIVE, { id: gidP });
   (r4.productUpdate?.userErrors || []).forEach(e => errs.push('active:' + e.message));
   const status = r4.productUpdate?.product?.status;

← 77b3068 auto-data-snapshot: 2026-08-06T12:25:51 (2 data files) — dat  ·  back to Designerwallcoverings  ·  Add scoped orphan drafter (draft only reviewed IDs, dry-run f39ee23 →