← 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
M scripts/knoll-onboard/go-live.mjsM scripts/maharam-onboard/go-live.mjsM scripts/muralsource-onboard/go-live.mjsM scripts/osborne-onboard/go-live.mjsM scripts/sanderson-onboard/go-live.mjsM scripts/stout-onboard/go-live.mjsM scripts/stroheim-onboard/go-live.mjs
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 →