[object Object]

← back to Designer Wallcoverings

fix(TK-11357/TK-11403/TK-11539): create DRAFT then gate before ACTIVE (activate-then-price reorder)

624ad3ff931e0c7e85763ca33a1c237d85a0b0de · 2026-09-14 15:26:18 -0700 · Steve

createProductWithMetafield no longer decides ACTIVE from a pre-gate price guess
(which let Shopify auto-create a $0 sellable variant that stayed ACTIVE +
published on a gate block / infra error — the TK-11357 recurrence). It now
always creates DRAFT and returns baseline eligibility flags; addSampleVariant
runs the price-integrity gate BEFORE the price write and returns {priced,
gateBlocked}; createOrGetProduct promotes to ACTIVE + publishes ONLY when
eligibleForActive && priced && !gateBlocked, else keeps DRAFT + Needs-Price-Review.
The catch path keeps the $0-orderable check fail-CLOSED even on gate infra error.

Includes the shared gate library it depends on (price-integrity-gate/cost/record,
weight-guard) and its negative tests (inject bad input -> gate goes red -> green).

KNOWN-OPEN (ticket stays open, see review clippings): gate returns ok:true on an
empty variants array (fail-open in the shared fn), and the bulk-import batch route
still bypasses the gate/weight/sample. Do not mark TK-11357/TK-11414 closed yet.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 624ad3ff931e0c7e85763ca33a1c237d85a0b0de
Author: Steve <steve@designerwallcoverings.com>
Date:   Mon Sep 14 15:26:18 2026 -0700

    fix(TK-11357/TK-11403/TK-11539): create DRAFT then gate before ACTIVE (activate-then-price reorder)
    
    createProductWithMetafield no longer decides ACTIVE from a pre-gate price guess
    (which let Shopify auto-create a $0 sellable variant that stayed ACTIVE +
    published on a gate block / infra error — the TK-11357 recurrence). It now
    always creates DRAFT and returns baseline eligibility flags; addSampleVariant
    runs the price-integrity gate BEFORE the price write and returns {priced,
    gateBlocked}; createOrGetProduct promotes to ACTIVE + publishes ONLY when
    eligibleForActive && priced && !gateBlocked, else keeps DRAFT + Needs-Price-Review.
    The catch path keeps the $0-orderable check fail-CLOSED even on gate infra error.
    
    Includes the shared gate library it depends on (price-integrity-gate/cost/record,
    weight-guard) and its negative tests (inject bad input -> gate goes red -> green).
    
    KNOWN-OPEN (ticket stays open, see review clippings): gate returns ok:true on an
    empty variants array (fail-open in the shared fn), and the bulk-import batch route
    still bypasses the gate/weight/sample. Do not mark TK-11357/TK-11414 closed yet.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 .../__tests__/price-integrity-gate.test.ts         | 226 ++++++++++
 .../__tests__/shopify-activation-order.test.ts     | 436 +++++++++++++++++++
 .../__tests__/weight-guard.test.ts                 |  96 +++++
 DW-Programming/ImportNewSkufromURL/gate-negtest.ts |  47 ++
 .../lib/price-integrity-cost.ts                    |  84 ++++
 .../lib/price-integrity-gate.ts                    | 476 +++++++++++++++++++++
 .../lib/price-integrity-record.ts                  |  57 +++
 DW-Programming/ImportNewSkufromURL/lib/shopify.ts  |  53 ++-
 .../ImportNewSkufromURL/lib/weight-guard.ts        |  74 ++++
 9 files changed, 1541 insertions(+), 8 deletions(-)

diff --git a/DW-Programming/ImportNewSkufromURL/__tests__/price-integrity-gate.test.ts b/DW-Programming/ImportNewSkufromURL/__tests__/price-integrity-gate.test.ts
new file mode 100644
index 00000000..cb1b8ce1
--- /dev/null
+++ b/DW-Programming/ImportNewSkufromURL/__tests__/price-integrity-gate.test.ts
@@ -0,0 +1,226 @@
+/**
+ * price-integrity-gate.test.ts - TK-11403 / TK-11431 rule 3 proof suite.
+ *
+ * Written for JEST (describe/it/expect) so it runs under the repo's real test
+ * command (npm test -> jest; jest.config.js testMatch covers this file):
+ *     npx jest __tests__/price-integrity-gate.test.ts
+ *
+ * Imports ONLY the pure gate (no DB, no cost adapter). Every bug class has a
+ * NEGATIVE test that proves the gate goes RED on an injected fault, plus the
+ * headline sample/default-price LEAK cases (the bug this gate exists to stop).
+ */
+import { describe, it, expect } from '@jest/globals';
+import {
+  assertPriceIntegrity,
+  enforcePriceIntegrity,
+  PriceIntegrityError,
+  type PriceIntegrityInput,
+} from '../lib/price-integrity-gate';
+
+const clean: PriceIntegrityInput = {
+  dwSku: 'DWX-1',
+  netCost: 30,
+  variants: [
+    { role: 'sample', price: 4.25, orderable: false },
+    { role: 'sellable', price: 59, orderable: true },
+  ],
+};
+
+describe('assertPriceIntegrity', () => {
+  // ---- 1. POSITIVE clean ----
+  it('1. clean input → ok:true, 0 violations', () => {
+    const r = assertPriceIntegrity(clean);
+    expect(r.ok).toBe(true);
+    expect(r.violations).toHaveLength(0);
+  });
+
+  // ---- 2. NEGATIVE class A (sample mis-price) ----
+  // TK-11447: the old per-variant `sample === 4.25` equality was REPLACED by the
+  // product-level OUTCOME test, so the code changed (`sample-not-expected` →
+  // `no-sample-in-band`). The INTENT is unchanged: a $59 "sample" is outside the
+  // acceptable band [4.25, 50], so no customer can buy a memo sample at a sample
+  // price, and the product must still be BLOCKED.
+  it('2. sample priced 59 → ok:false, class A no-sample-in-band', () => {
+    const r = assertPriceIntegrity({
+      dwSku: 'DWX-1',
+      netCost: 30,
+      variants: [
+        { role: 'sample', price: 59, orderable: false },
+        { role: 'sellable', price: 59, orderable: true },
+      ],
+    });
+    expect(r.ok).toBe(false);
+    expect(r.violations.some(v => v.class === 'A' && v.code === 'no-sample-in-band')).toBe(true);
+  });
+
+  // ---- 3. NEGATIVE class B (sellable at cost, below 1.5x) ----
+  it('3. sellable 30 at netCost 30 (1.0x) → ok:false, class B below-markup-floor', () => {
+    const r = assertPriceIntegrity({
+      dwSku: 'DWX-1',
+      netCost: 30,
+      variants: [
+        { role: 'sample', price: 4.25, orderable: false },
+        { role: 'sellable', price: 30, orderable: true },
+      ],
+    });
+    expect(r.ok).toBe(false);
+    expect(r.violations.some(v => v.class === 'B' && v.code === 'below-markup-floor')).toBe(true);
+  });
+
+  // ---- 4. A-with-teeth: cost UNKNOWN + plausible price WARNs, does NOT block ----
+  //         (updated from the old fail-closed 'cost-unknown' block per the
+  //         2026-09-11 DTD verdict — blocking on unknown cost would halt ~97 vendors.)
+  it('4. sellable 59 with netCost:null → ok:true AND warnings has cost-unverified (no block)', () => {
+    const r = assertPriceIntegrity({
+      dwSku: 'DWX-1',
+      netCost: null,
+      variants: [
+        { role: 'sample', price: 4.25, orderable: false },
+        { role: 'sellable', price: 59, orderable: true },
+      ],
+    });
+    expect(r.ok).toBe(true);
+    expect(r.violations).toHaveLength(0);
+    expect(r.warnings.some(w => w.class === 'B' && w.code === 'cost-unverified' && w.severity === 'warn')).toBe(true);
+  });
+
+  // ---- 5. NEGATIVE class C ($0 orderable) ----
+  it('5. sellable 0 orderable:true → ok:false, class C zero-price-orderable', () => {
+    const r = assertPriceIntegrity({
+      dwSku: 'DWX-1',
+      netCost: 30,
+      variants: [
+        { role: 'sample', price: 4.25, orderable: false },
+        { role: 'sellable', price: 0, orderable: true },
+      ],
+    });
+    expect(r.ok).toBe(false);
+    expect(r.violations.some(v => v.class === 'C' && v.code === 'zero-price-orderable')).toBe(true);
+  });
+
+  // ---- 6. HEADLINE LEAK (numeric backstop): $4.25 sample price leaks as the
+  //         sellable/roll price. This is the exact bug the gate exists to stop,
+  //         and it used to pass clean when 4.25 >= 1.5x cost. ----
+  it('6. sellable 4.25 == sample price (netCost 2.50) → ok:false, class A sellable-equals-sample', () => {
+    const r = assertPriceIntegrity({
+      dwSku: 'DWX-1',
+      netCost: 2.5, // 1.5x = 3.75, so 4.25 clears Class B — magnitude alone misses it
+      variants: [
+        { role: 'sample', price: 4.25, orderable: false },
+        { role: 'sellable', price: 4.25, orderable: true },
+      ],
+    });
+    expect(r.ok).toBe(false);
+    expect(r.violations.some(v => v.class === 'A' && v.code === 'sellable-equals-sample')).toBe(true);
+  });
+
+  // ---- 7. HEADLINE LEAK (provenance): a sellable whose price was DEFAULTED. ----
+  it("7. sellable priceSource:'defaulted' → ok:false, class A sellable-defaulted-price", () => {
+    const r = assertPriceIntegrity({
+      dwSku: 'DWX-1',
+      netCost: 30,
+      variants: [
+        { role: 'sample', price: 4.25, orderable: false },
+        { role: 'sellable', price: 4.25, orderable: true, priceSource: 'defaulted' },
+      ],
+    });
+    expect(r.ok).toBe(false);
+    expect(r.violations.some(v => v.class === 'A' && v.code === 'sellable-defaulted-price')).toBe(true);
+  });
+
+  // ---- 8. NEGATIVE class C (negative price gets its own honest code) ----
+  it('8. sellable -5 orderable:true → ok:false, class C negative-price (not zero-price)', () => {
+    const r = assertPriceIntegrity({
+      dwSku: 'DWX-1',
+      netCost: 30,
+      variants: [
+        { role: 'sample', price: 4.25, orderable: false },
+        { role: 'sellable', price: -5, orderable: true },
+      ],
+    });
+    expect(r.ok).toBe(false);
+    expect(r.violations.some(v => v.class === 'C' && v.code === 'negative-price')).toBe(true);
+    expect(r.violations.some(v => v.code === 'zero-price-orderable')).toBe(false);
+  });
+
+  // ---- 9. POSITIVE: a legitimately cheap-ish product still passes ----
+  //         (guards against the backstop over-blocking low-priced real goods.)
+  it('9. cheap-but-legit sellable 10 at netCost 2.50 (scraped) → ok:true', () => {
+    const r = assertPriceIntegrity({
+      dwSku: 'DWX-1',
+      netCost: 2.5,
+      variants: [
+        { role: 'sample', price: 4.25, orderable: false },
+        { role: 'sellable', price: 10, orderable: true, priceSource: 'scraped' },
+      ],
+    });
+    expect(r.ok).toBe(true);
+    expect(r.violations).toHaveLength(0);
+  });
+
+  // ---- 11. cost-FREE absolute-dollar floor: $2 orderable with UNKNOWN cost blocks ----
+  it('11. cost-unknown + $2 orderable → ok:false, block B below-absolute-floor', () => {
+    const r = assertPriceIntegrity({
+      dwSku: 'DWX-1',
+      netCost: null,
+      variants: [
+        { role: 'sample', price: 4.25, orderable: false },
+        { role: 'sellable', price: 2, orderable: true },
+      ],
+    });
+    expect(r.ok).toBe(false);
+    expect(r.violations.some(v => v.class === 'B' && v.code === 'below-absolute-floor')).toBe(true);
+  });
+
+  // ---- 12. a $4 orderable with cost unknown is blocked by the floor (default 5.00) ----
+  it('12. cost-unknown + $4 orderable → blocked by absolute floor', () => {
+    const r = assertPriceIntegrity({
+      dwSku: 'DWX-1',
+      netCost: null,
+      variants: [{ role: 'sellable', price: 4, orderable: true }],
+    });
+    expect(r.ok).toBe(false);
+    expect(r.violations.some(v => v.code === 'below-absolute-floor')).toBe(true);
+  });
+
+  // ---- 13. cost KNOWN, sellable at cost → still the markup-floor BLOCK (unchanged) ----
+  it('13. netCost 30 + sellable 30 → still ok:false below-markup-floor', () => {
+    const r = assertPriceIntegrity({
+      dwSku: 'DWX-1',
+      netCost: 30,
+      variants: [
+        { role: 'sample', price: 4.25, orderable: false },
+        { role: 'sellable', price: 30, orderable: true },
+      ],
+    });
+    expect(r.ok).toBe(false);
+    expect(r.violations.some(v => v.class === 'B' && v.code === 'below-markup-floor')).toBe(true);
+  });
+
+  // ---- 14. clean input carries no warnings (shape guard) ----
+  it('14. clean input → warnings is empty', () => {
+    const r = assertPriceIntegrity(clean);
+    expect(r.warnings).toHaveLength(0);
+  });
+});
+
+describe('enforcePriceIntegrity', () => {
+  // ---- 10. throws on fail, not on clean ----
+  it('10. throws PriceIntegrityError (with .violations) on failing input, not on clean', () => {
+    expect(() => enforcePriceIntegrity(clean)).not.toThrow();
+
+    const failing: PriceIntegrityInput = {
+      dwSku: 'DWX-1',
+      netCost: 30,
+      variants: [{ role: 'sellable', price: 0, orderable: true }],
+    };
+    let caught: unknown;
+    try {
+      enforcePriceIntegrity(failing);
+    } catch (e) {
+      caught = e;
+    }
+    expect(caught).toBeInstanceOf(PriceIntegrityError);
+    expect((caught as PriceIntegrityError).violations.length).toBeGreaterThan(0);
+  });
+});
diff --git a/DW-Programming/ImportNewSkufromURL/__tests__/shopify-activation-order.test.ts b/DW-Programming/ImportNewSkufromURL/__tests__/shopify-activation-order.test.ts
new file mode 100644
index 00000000..4cf7dbb0
--- /dev/null
+++ b/DW-Programming/ImportNewSkufromURL/__tests__/shopify-activation-order.test.ts
@@ -0,0 +1,436 @@
+/**
+ * shopify-activation-order.test.ts
+ * ============================================================================
+ * Regression coverage for the activate-then-price ordering bug (2026-09-14 fix).
+ *
+ * BEFORE this fix: createProductWithMetafield() decided ACTIVE from a PRE-gate
+ * price guess (`numericUnitPrice > 4.25`) and productCreate() shipped NO priced
+ * variants, so Shopify auto-created the sellable variant at $0. The REAL price
+ * (after the price-integrity gate) was only written later in addSampleVariant(),
+ * which sat AFTER `if (gateBlocked) return;` and returned void — so a gate
+ * BLOCK, a gate infra error, or a failed price write all left an ACTIVE,
+ * all-channels-published $0 product live (TK-11357 recurrence).
+ *
+ * AFTER the fix: createProductWithMetafield() ALWAYS creates DRAFT.
+ * createOrGetProduct() promotes to ACTIVE + publishes ONLY when BOTH:
+ *   (1) the baseline eligibility gate passed (hasImage && hasWidth && hasPrice)
+ *   (2) addSampleVariant()'s result confirms priced:true AND gateBlocked:false
+ * Otherwise the product stays DRAFT, gets tagged 'Needs-Price-Review', and
+ * publishToAllChannels() is never called.
+ *
+ * MOCKING STRATEGY (call-site level, no real network / DB):
+ *   - global.fetch is replaced with a single mock that inspects each GraphQL
+ *     request's `query` text and returns a canned response per mutation/query
+ *     name. This lets createOrGetProduct()'s REAL code run end-to-end (the
+ *     exact code path that had the bug) while making zero real Shopify calls.
+ *   - lib/dw-sku-registry, lib/sku-counter, lib/sku-history, and
+ *     lib/price-integrity-record are jest.mock()'d — they are DB/file-writing
+ *     adapters unrelated to the bug under test, and touching real Postgres or
+ *     the shared price-integrity telemetry JSONL from a unit test would be
+ *     unsafe/non-hermetic.
+ *   - lib/price-integrity-gate and lib/price-integrity-cost are left REAL —
+ *     the whole point of this suite is to prove the gate's real outcome is
+ *     what decides ACTIVE vs DRAFT, not a mock of it. resolveNetCost() is
+ *     pointed (via PRICE_FINDER_VERIFY_SCRIPT) at a nonexistent script so it
+ *     fails fast/closed to null (a legitimate, already-covered fail-safe path
+ *     in the gate: unknown cost = non-blocking WARN, never a block) instead of
+ *     shelling out to the real price-finder skill against production data.
+ *
+ * LIMITATION (noted per the task's instruction to be explicit rather than ship
+ * a vacuous test): addProductImages()/uploadImageToShopify() — which runs
+ * AFTER the activation decision this suite covers — is exercised for real
+ * (dto.images is non-empty, to legitimately satisfy `hasImage`), but its
+ * network calls are NOT modeled beyond "return 404" for any non-GraphQL fetch.
+ * That is sufficient because (a) it only affects logging/best-effort image
+ * attachment, not the ACTIVE/DRAFT/publish decision under test, and (b) it
+ * already fails gracefully (try/catch, never throws) by design. It is not
+ * asserted on directly in this suite.
+ */
+import { describe, it, expect, beforeAll, beforeEach, jest } from '@jest/globals';
+import type { ProductDTO } from '../lib/types';
+
+// ---- jest.mock() calls are hoisted above imports/requires by Jest's babel
+// plugin, so these register before lib/shopify.ts (which imports these same
+// modules) is ever loaded — regardless of textual position in this file. ----
+jest.mock('../lib/dw-sku-registry', () => ({
+  assignDwSku: jest.fn(async (_vendorName: string, _mfrSku: string | null) => ({
+    dwSku: 'DWXX-999999',
+    vendorPrefix: 'DWXX',
+    isNew: true,
+  })),
+  registerShopifyInfo: jest.fn(async () => {}),
+}));
+
+jest.mock('../lib/sku-counter', () => ({
+  getNextSKU: jest.fn(async () => 'DWWC-500000'),
+  getCurrentSKU: jest.fn(async () => 'DWWC-500000'),
+}));
+
+jest.mock('../lib/sku-history', () => ({
+  addToHistory: jest.fn(async () => {}),
+  getHistory: jest.fn(async () => []),
+}));
+
+jest.mock('../lib/price-integrity-record', () => ({
+  recordOutcome: jest.fn(),
+}));
+
+const GRAPHQL_URL = 'https://test-store.myshopify.com/admin/api/2024-07/graphql.json';
+
+function graphqlOk(data: unknown) {
+  return {
+    ok: true,
+    status: 200,
+    json: async () => ({ data }),
+  };
+}
+
+let productCounter = 0;
+// (review 2026-09-14) When true, the productVariantsBulkCreate branch (the Sample
+// variant create) returns a real userError so the sample-create-failure path can be
+// exercised — proving a failed Sample keeps the product DRAFT (sampleOk fix).
+let FAIL_SAMPLE_CREATE = false;
+
+/** Builds a fresh fetch mock. One instance per test so call logs don't bleed across tests. */
+function makeFetchMock() {
+  return jest.fn(async (url: any, opts: any = {}) => {
+    if (url === GRAPHQL_URL) {
+      const body = JSON.parse(opts.body);
+      const q: string = body.query || '';
+      const vars = body.variables || {};
+
+      if (q.includes('mutation ProductCreate')) {
+        productCounter++;
+        return graphqlOk({
+          productCreate: {
+            product: {
+              id: `gid://shopify/Product/${productCounter}`,
+              handle: `test-handle-${productCounter}`,
+              status: 'DRAFT',
+            },
+            userErrors: [],
+          },
+        });
+      }
+      if (q.includes('query getProductVariants')) {
+        // A single existing variant that never matches unitLabel by name, so the
+        // real code's `findBySize(unitLabel) || existingVariants[0]` fallback
+        // exercises the same path regardless of the computed unit label.
+        return graphqlOk({
+          product: {
+            variants: {
+              edges: [
+                {
+                  node: {
+                    id: 'gid://shopify/ProductVariant/1',
+                    title: 'Default Title',
+                    selectedOptions: [{ name: 'Size', value: '__no_match__' }],
+                  },
+                },
+              ],
+            },
+          },
+        });
+      }
+      if (q.includes('productVariantsBulkUpdate')) {
+        return graphqlOk({
+          productVariantsBulkUpdate: {
+            productVariants: [
+              {
+                id: vars.variants?.[0]?.id || 'gid://shopify/ProductVariant/1',
+                title: 'Roll',
+                price: vars.variants?.[0]?.price,
+                inventoryItem: {
+                  id: 'gid://shopify/InventoryItem/1',
+                  sku: vars.variants?.[0]?.inventoryItem?.sku,
+                },
+              },
+            ],
+            userErrors: [],
+          },
+        });
+      }
+      if (q.includes('productVariantsBulkCreate')) {
+        if (FAIL_SAMPLE_CREATE) {
+          // A real (non-"already exists") userError — the Sample variant was NOT created.
+          return graphqlOk({
+            productVariantsBulkCreate: {
+              productVariants: [],
+              userErrors: [{ field: ['variants'], message: 'Something went wrong creating the variant' }],
+            },
+          });
+        }
+        return graphqlOk({
+          productVariantsBulkCreate: {
+            productVariants: [
+              {
+                id: 'gid://shopify/ProductVariant/2',
+                title: 'Sample',
+                inventoryItem: {
+                  id: 'gid://shopify/InventoryItem/2',
+                  sku: vars.variants?.[0]?.inventoryItem?.sku,
+                },
+              },
+            ],
+            userErrors: [],
+          },
+        });
+      }
+      if (q.includes('getInventoryLevels')) {
+        return graphqlOk({
+          inventoryItem: {
+            id: vars.id,
+            inventoryLevels: {
+              edges: [
+                {
+                  node: {
+                    id: 'gid://shopify/InventoryLevel/1',
+                    location: { id: 'gid://shopify/Location/1', name: 'Shop' },
+                  },
+                },
+              ],
+            },
+          },
+        });
+      }
+      if (q.includes('inventoryAdjustQuantities')) {
+        return graphqlOk({
+          inventoryAdjustQuantities: {
+            inventoryAdjustmentGroup: { createdAt: new Date().toISOString(), reason: 'correction', changes: [] },
+            userErrors: [],
+          },
+        });
+      }
+      if (q.includes('mutation productUpdateStatus')) {
+        return graphqlOk({
+          productUpdate: {
+            product: { id: vars.input.id, status: vars.input.status },
+            userErrors: [],
+          },
+        });
+      }
+      if (q.includes('mutation tagsAdd')) {
+        return graphqlOk({
+          tagsAdd: { node: { id: vars.id }, userErrors: [] },
+        });
+      }
+      if (q.includes('publications(')) {
+        return graphqlOk({
+          publications: { edges: [{ node: { id: 'gid://shopify/Publication/1', name: 'Online Store' } }] },
+        });
+      }
+      if (q.includes('mutation productPublish')) {
+        return graphqlOk({
+          productPublish: { product: { id: vars.input.id }, userErrors: [] },
+        });
+      }
+      // Unknown GraphQL op — never throw, just return empty data.
+      return graphqlOk({});
+    }
+    // Non-GraphQL fetch (image download in addProductImages) — fail gracefully.
+    // addProductImages runs AFTER the activation decision under test and is
+    // already best-effort (try/catch, never throws) by design; see LIMITATION.
+    return { ok: false, status: 404, statusText: 'Not Found' };
+  });
+}
+
+/** Filters a fetch mock's calls to those whose GraphQL `query` contains `substr`. */
+function callsMatching(fetchMock: ReturnType<typeof makeFetchMock>, substr: string) {
+  return (fetchMock.mock.calls as any[]).filter(([url, opts]) => {
+    if (url !== GRAPHQL_URL) return false;
+    try {
+      return JSON.parse((opts as any).body).query.includes(substr);
+    } catch {
+      return false;
+    }
+  });
+}
+
+function baseDto(overrides: Partial<ProductDTO> = {}): ProductDTO {
+  return {
+    source_url: 'https://example.com/products/test-pattern',
+    fingerprint: `fp-${Math.random().toString(36).slice(2)}`,
+    title: 'Test Pattern Blue',
+    bodyHtml: '<p>A lovely wallcovering.</p>',
+    tags: ['blue', 'geometric'],
+    images: ['https://cdn.example.com/img1.jpg'],
+    specs: {
+      sku: 'TP-001',
+      brand: 'Test Vendor',
+      width: '27 Inches',
+      length: '27 Feet',
+    },
+    price: '54.00',
+    vendor: 'Test Vendor',
+    ...overrides,
+  } as ProductDTO;
+}
+
+let createOrGetProduct: (dto: ProductDTO) => Promise<any>;
+
+beforeAll(() => {
+  // Set BEFORE requiring lib/shopify.ts — it reads these at module top-level.
+  process.env.SHOPIFY_STORE_DOMAIN = 'test-store.myshopify.com';
+  process.env.SHOPIFY_ADMIN_ACCESS_TOKEN = 'test-token';
+  process.env.SHOPIFY_ADMIN_API_VERSION = '2024-07';
+  // Force resolveNetCost() to fail fast/closed to null (spawn ENOENT) instead
+  // of shelling out to the real price-finder skill against production data.
+  process.env.PRICE_FINDER_VERIFY_SCRIPT = '/nonexistent/verify-price-for-tests.js';
+
+  // Require (not import) so it loads AFTER the env vars above are set and
+  // AFTER the jest.mock() calls (hoisted) have registered.
+  // eslint-disable-next-line @typescript-eslint/no-var-requires
+  createOrGetProduct = require('../lib/shopify').createOrGetProduct;
+});
+
+beforeEach(() => {
+  jest.clearAllMocks();
+  FAIL_SAMPLE_CREATE = false;
+});
+
+describe('activate-then-price ordering fix (2026-09-14)', () => {
+  it('gate BLOCK: sub-$5-absolute-floor sellable never goes ACTIVE, never publishes, tagged for review', async () => {
+    // $4.50 clears the OLD pre-gate guess (`> 4.25`) so hasPrice=true and the
+    // baseline eligibility gate passes — but the REAL price-integrity gate's
+    // absolute-dollar floor ($5.00 default) blocks it. This is exactly the
+    // ordering bug: eligible-looking, but the real gate says no.
+    const fetchMock = makeFetchMock();
+    global.fetch = fetchMock as any;
+
+    const dto = baseDto({ price: '4.50', vendor: 'Test Vendor' });
+    const result = await createOrGetProduct(dto);
+
+    expect(result.product).toBeDefined();
+
+    // The sellable price write must NEVER have been attempted — the gate
+    // blocked before addSampleVariant's write step.
+    expect(callsMatching(fetchMock, 'productVariantsBulkUpdate')).toHaveLength(0);
+    expect(callsMatching(fetchMock, 'productVariantsBulkCreate')).toHaveLength(0);
+
+    // The product must NEVER be promoted to ACTIVE...
+    const statusCalls = callsMatching(fetchMock, 'mutation productUpdateStatus');
+    expect(statusCalls.every(([, opts]: any) => JSON.parse(opts.body).variables.input.status !== 'ACTIVE')).toBe(
+      true
+    );
+
+    // ...and must NEVER be published to any sales channel.
+    expect(callsMatching(fetchMock, 'mutation productPublish')).toHaveLength(0);
+
+    // It must be tagged for manual review.
+    const tagCalls = callsMatching(fetchMock, 'mutation tagsAdd');
+    expect(tagCalls.length).toBeGreaterThan(0);
+    const taggedWith = tagCalls.map(([, opts]: any) => JSON.parse(opts.body).variables.tags).flat();
+    expect(taggedWith).toContain('Needs-Price-Review');
+  }, 15000);
+
+  it('happy path: well-priced product with image+width promotes to ACTIVE + publishes with the real price written', async () => {
+    const fetchMock = makeFetchMock();
+    global.fetch = fetchMock as any;
+
+    const dto = baseDto({ price: '54.00', vendor: 'Test Vendor' });
+    const result = await createOrGetProduct(dto);
+
+    expect(result.product).toBeDefined();
+
+    // The real sellable price must have been written.
+    const priceWriteCalls = callsMatching(fetchMock, 'productVariantsBulkUpdate');
+    expect(priceWriteCalls).toHaveLength(1);
+    expect(JSON.parse(priceWriteCalls[0][1].body).variables.variants[0].price).toBe('54.00');
+
+    // The product must be promoted to ACTIVE...
+    const statusCalls = callsMatching(fetchMock, 'mutation productUpdateStatus');
+    expect(statusCalls).toHaveLength(1);
+    expect(JSON.parse(statusCalls[0][1].body).variables.input.status).toBe('ACTIVE');
+
+    // ...and published to all channels.
+    expect(callsMatching(fetchMock, 'mutation productPublish')).toHaveLength(1);
+
+    // No review tag on the happy path.
+    const tagCalls = callsMatching(fetchMock, 'mutation tagsAdd');
+    const taggedWith = tagCalls.map(([, opts]: any) => JSON.parse(opts.body).variables.tags).flat();
+    expect(taggedWith).not.toContain('Needs-Price-Review');
+  }, 15000);
+
+  it("declared-floor vendor ('DW Bespoke Studio'): sample is written at the declared $12 floor and passes", async () => {
+    const fetchMock = makeFetchMock();
+    global.fetch = fetchMock as any;
+
+    const dto = baseDto({ price: '60.00', vendor: 'DW Bespoke Studio' });
+    const result = await createOrGetProduct(dto);
+
+    expect(result.product).toBeDefined();
+
+    // resolveSampleFloor('DW Bespoke Studio') === 12 (VENDOR_DECLARED_SAMPLE_PRICE),
+    // and addSampleVariant writes the sample at String(sampleFloor) — not the
+    // generic $4.25 — so the sample-create call must carry price '12'.
+    const sampleCreateCalls = callsMatching(fetchMock, 'productVariantsBulkCreate');
+    expect(sampleCreateCalls).toHaveLength(1);
+    expect(JSON.parse(sampleCreateCalls[0][1].body).variables.variants[0].price).toBe('12');
+
+    // A sample correctly priced at its own declared floor passes the gate —
+    // eligible + gate-passed ⇒ promoted to ACTIVE + published.
+    const statusCalls = callsMatching(fetchMock, 'mutation productUpdateStatus');
+    expect(statusCalls).toHaveLength(1);
+    expect(JSON.parse(statusCalls[0][1].body).variables.input.status).toBe('ACTIVE');
+    expect(callsMatching(fetchMock, 'mutation productPublish')).toHaveLength(1);
+  }, 15000);
+
+  it("declared-floor vendor ('DW Bespoke Studio'): a $4.25 sample (the generic default) would BLOCK the gate", async () => {
+    // This proves the WIRING matters: addSampleVariant() always writes+checks the
+    // sample at String(sampleFloor) — never a hardcoded $4.25 — for a declared
+    // vendor. Called directly against the real, pure gate (no network/DB) to
+    // demonstrate what protects against a future regression that reintroduces a
+    // hardcoded $4.25 sample write for a declared-floor vendor.
+    const { assertPriceIntegrity, resolveSampleFloor } = require('../lib/price-integrity-gate');
+    const sampleFloor = resolveSampleFloor('DW Bespoke Studio');
+    expect(sampleFloor).toBe(12.0);
+
+    const result = assertPriceIntegrity({
+      dwSku: 'DWXX-999999',
+      netCost: null,
+      sampleFloor,
+      variants: [
+        { role: 'sellable', price: 60, orderable: true, sku: 'DWXX-999999-Roll', priceSource: 'scraped' },
+        { role: 'sample', price: 4.25, orderable: false, sku: 'DWXX-999999-Sample' },
+      ],
+    });
+
+    expect(result.ok).toBe(false);
+    expect(
+      result.violations.some(
+        (v: any) => v.class === 'A' && (v.code === 'no-sample-in-band' || v.code === 'sample-below-vendor-floor')
+      )
+    ).toBe(true);
+  });
+
+  // NEGATIVE TEST (CLAUDE.md TK-11431 rule 3) for the sampleOk fix (review 2026-09-14):
+  // a well-priced, eligible product whose Sample-variant CREATE fails must NOT be
+  // promoted to ACTIVE (DW hard rule: every product has a memo Sample) — it stays DRAFT
+  // and is tagged Needs-Sample. Without the fix, priced+gate-passed alone promoted it.
+  it('sample-create failure: product with a good sellable but a failed Sample create stays DRAFT + Needs-Sample', async () => {
+    FAIL_SAMPLE_CREATE = true;
+    const fetchMock = makeFetchMock();
+    global.fetch = fetchMock as any;
+
+    const dto = baseDto({ price: '54.00', vendor: 'Test Vendor' });
+    const result = await createOrGetProduct(dto);
+    expect(result.product).toBeDefined();
+
+    // The sellable price DID write (this is not a price failure)...
+    expect(callsMatching(fetchMock, 'productVariantsBulkUpdate')).toHaveLength(1);
+    // ...and the Sample create WAS attempted and failed.
+    expect(callsMatching(fetchMock, 'productVariantsBulkCreate')).toHaveLength(1);
+
+    // Must NEVER be promoted to ACTIVE and NEVER published.
+    const statusCalls = callsMatching(fetchMock, 'mutation productUpdateStatus');
+    expect(
+      statusCalls.every(([, opts]: any) => JSON.parse(opts.body).variables.input.status !== 'ACTIVE')
+    ).toBe(true);
+    expect(callsMatching(fetchMock, 'mutation productPublish')).toHaveLength(0);
+
+    // Must be tagged Needs-Sample.
+    const tagCalls = callsMatching(fetchMock, 'mutation tagsAdd');
+    const taggedWith = tagCalls.map(([, opts]: any) => JSON.parse(opts.body).variables.tags).flat();
+    expect(taggedWith).toContain('Needs-Sample');
+  }, 15000);
+});
diff --git a/DW-Programming/ImportNewSkufromURL/__tests__/weight-guard.test.ts b/DW-Programming/ImportNewSkufromURL/__tests__/weight-guard.test.ts
new file mode 100644
index 00000000..0c6254b6
--- /dev/null
+++ b/DW-Programming/ImportNewSkufromURL/__tests__/weight-guard.test.ts
@@ -0,0 +1,96 @@
+// weight-guard.test.ts — TK-11539 negative test (CLAUDE.md TK-11431 amendment 3:
+// "a check ships with a negative test proving it goes red on an injected fault").
+//
+// The guard's job: no variant the shared import engine creates may carry a zero/missing
+// shipping weight. hasPositiveWeight() is the invariant; the tests prove it is RED on the
+// pre-fix (un-guarded) payload and GREEN on the guarded payload, and that the guard resolves
+// sane, positive, per-product-type weights.
+
+import {
+  inventoryItemWithWeight,
+  hasPositiveWeight,
+  sellableWeightLb,
+  sampleWeightLb,
+  SAMPLE_WEIGHT_LB,
+  FALLBACK_LB,
+  TYPE_DEFAULT_LB,
+} from '../lib/weight-guard';
+
+describe('TK-11539 weight guard — weight resolution', () => {
+  it('sellable Wallcovering resolves to the TK-11414 backfill default (3.0 lb)', () => {
+    expect(sellableWeightLb('Wallcovering')).toBe(TYPE_DEFAULT_LB['Wallcovering']);
+    expect(sellableWeightLb('Wallcovering')).toBe(3.0);
+  });
+
+  it('sellable resolution is case-insensitive', () => {
+    expect(sellableWeightLb('wallcovering')).toBe(3.0);
+    expect(sellableWeightLb('  FABRIC ')).toBe(1.0);
+  });
+
+  it('unknown/blank product_type falls back to FALLBACK_LB (never zero)', () => {
+    expect(sellableWeightLb('Nonexistent Type')).toBe(FALLBACK_LB);
+    expect(sellableWeightLb('')).toBe(FALLBACK_LB);
+    expect(sellableWeightLb(undefined)).toBe(FALLBACK_LB);
+    expect(sellableWeightLb(null)).toBe(FALLBACK_LB);
+    expect(FALLBACK_LB).toBeGreaterThan(0);
+  });
+
+  it('sample weight is the approved 0.25 lb', () => {
+    expect(sampleWeightLb()).toBe(SAMPLE_WEIGHT_LB);
+    expect(sampleWeightLb()).toBe(0.25);
+  });
+});
+
+describe('TK-11539 weight guard — inventoryItemWithWeight builder', () => {
+  it('sellable payload keeps base fields AND carries a positive weight in POUNDS', () => {
+    const out = inventoryItemWithWeight(
+      { sku: 'DWXX-1-Roll', tracked: true },
+      { role: 'sellable', productType: 'Wallcovering' }
+    );
+    expect(out.sku).toBe('DWXX-1-Roll');
+    expect(out.tracked).toBe(true);
+    expect(out.measurement.weight.unit).toBe('POUNDS');
+    expect(out.measurement.weight.value).toBe(3.0);
+    expect(hasPositiveWeight(out)).toBe(true);
+  });
+
+  it('sample payload keeps base fields AND carries 0.25 lb', () => {
+    const out = inventoryItemWithWeight(
+      { sku: 'DWXX-1-Sample', tracked: false },
+      { role: 'sample', productType: 'Wallcovering' }
+    );
+    expect(out.sku).toBe('DWXX-1-Sample');
+    expect(out.tracked).toBe(false);
+    expect(out.measurement.weight.value).toBe(0.25);
+    expect(hasPositiveWeight(out)).toBe(true);
+  });
+
+  it('guarantees a positive weight even for an unknown product_type', () => {
+    const out = inventoryItemWithWeight(
+      { sku: 'DWXX-2-Roll', tracked: true },
+      { role: 'sellable', productType: 'Totally Unknown' }
+    );
+    expect(out.measurement.weight.value).toBe(FALLBACK_LB);
+    expect(hasPositiveWeight(out)).toBe(true);
+  });
+});
+
+describe('TK-11539 weight guard — NEGATIVE test (proves the invariant has teeth)', () => {
+  it('REDDENS on the PRE-FIX payload (the exact zero-weight fault this guard removes)', () => {
+    // This is precisely what addSampleVariant wrote before TK-11539: no measurement.
+    const preFixUnguardedPayload = { sku: 'DWXX-1-Roll', tracked: true };
+    expect(hasPositiveWeight(preFixUnguardedPayload)).toBe(false);
+  });
+
+  it('REDDENS on an injected zero / negative / non-finite weight', () => {
+    expect(hasPositiveWeight({ sku: 'x', measurement: { weight: { value: 0, unit: 'POUNDS' } } })).toBe(false);
+    expect(hasPositiveWeight({ sku: 'x', measurement: { weight: { value: -3, unit: 'POUNDS' } } })).toBe(false);
+    expect(hasPositiveWeight({ sku: 'x', measurement: { weight: { value: NaN, unit: 'POUNDS' } } })).toBe(false);
+    expect(hasPositiveWeight({ sku: 'x', measurement: { weight: {} } })).toBe(false);
+  });
+
+  it('GREENS on the guarded payload — closing the loop against the pre-fix fault', () => {
+    const guarded = inventoryItemWithWeight({ sku: 'DWXX-1-Roll', tracked: true }, { role: 'sellable', productType: 'Wallcovering' });
+    expect(hasPositiveWeight(guarded)).toBe(true);
+  });
+});
diff --git a/DW-Programming/ImportNewSkufromURL/gate-negtest.ts b/DW-Programming/ImportNewSkufromURL/gate-negtest.ts
new file mode 100644
index 00000000..eb4f8b0e
--- /dev/null
+++ b/DW-Programming/ImportNewSkufromURL/gate-negtest.ts
@@ -0,0 +1,47 @@
+// TK-11447 negative tests: prove the reworked Class A goes RED on injected faults
+// AND green on the vendor prices that the old equality wrongly blocked.
+import { assertPriceIntegrity, resolveSampleFloor } from './lib/price-integrity-gate';
+const R:string[]=[]; let pass=0, fail=0;
+const t=(name:string, cond:boolean)=>{ (cond?pass++:fail++); R.push((cond?'  PASS  ':'  FAIL  ')+name); };
+const G=(vendor:string, variants:any[], netCost:number|null=null)=>assertPriceIntegrity({
+  dwSku:'T', netCost, sampleFloor:resolveSampleFloor(vendor), variants});
+const codes=(r:any)=>r.violations.map((v:any)=>v.code);
+
+// --- GREEN: the legitimate vendor sample prices the equality rule wrongly blocked
+for (const [vendor, p] of [['DW Bespoke Studio',12.00],['Sister Parish',8.24],['Fentucci',9.95],['Artmura',5.00]] as [string,number][]) {
+  const r=G(vendor,[{role:'sample',price:p,orderable:true},{role:'sellable',price:195,orderable:true,priceSource:'scraped'}]);
+  t(`LEGIT ${vendor} sample $${p} passes (equality rule would have BLOCKED)`, r.ok);
+}
+// house baseline still fine
+t('LEGIT house $4.25 sample passes', G('Thibaut',[{role:'sample',price:4.25,orderable:true},{role:'sellable',price:174.71,orderable:true,priceSource:'scraped'}]).ok);
+
+// --- RED: injected faults
+const f1=G('Thibaut',[{role:'sample',price:161.27,orderable:true},{role:'sellable',price:174.71,orderable:true,priceSource:'scraped'}]);
+t('FAULT sample priced as a roll ($161.27) BLOCKS', !f1.ok && codes(f1).includes('no-sample-in-band'));
+
+const f2=G('Thibaut',[{role:'sample',price:4.25,orderable:true},{role:'sellable',price:4.25,orderable:true,priceSource:'scraped'}]);
+t('FAULT sellable equals sample BLOCKS', !f2.ok && codes(f2).includes('sellable-equals-sample'));
+
+// the leak a GLOBAL 4.25 constant would have MISSED: vendor floor is 12.00
+const f3=G('DW Bespoke Studio',[{role:'sample',price:12.00,orderable:true},{role:'sellable',price:12.00,orderable:true,priceSource:'scraped'}]);
+t('FAULT sellable equals a NON-4.25 vendor sample ($12) BLOCKS (global-constant blind spot)', !f3.ok && codes(f3).includes('sellable-equals-sample'));
+
+const f4=G('Thibaut',[{role:'sample',price:4.25,orderable:true},{role:'sellable',price:0,orderable:true,priceSource:'scraped'}]);
+t('FAULT $0 orderable sellable BLOCKS', !f4.ok && codes(f4).includes('zero-price-orderable'));
+
+const f5=G('Innovations',[{role:'sample',price:3.50,orderable:true},{role:'sellable',price:195,orderable:true,priceSource:'scraped'}]);
+t('FAULT sample BELOW vendor floor ($3.50 vs $4.25) BLOCKS', !f5.ok && codes(f5).includes('sample-below-vendor-floor'));
+
+const f6=G('Thibaut',[{role:'sample',price:4.25,orderable:true},{role:'sellable',price:50,orderable:true,priceSource:'defaulted'}]);
+t('FAULT defaulted-source sellable BLOCKS', !f6.ok && codes(f6).includes('sellable-defaulted-price'));
+
+// --- the DTD verdict E behaviour: cost-unknown must WARN, never BLOCK
+const w=G('SomeVendorWithNoCostFeed',[{role:'sample',price:4.25,orderable:true},{role:'sellable',price:195,orderable:true,priceSource:'scraped'}], null);
+t('VERDICT-E cost-unknown WARNS and does NOT block', w.ok && w.warnings.map((x:any)=>x.code).includes('cost-unverified'));
+// and a KNOWN cost below markup still blocks
+const b=G('Thibaut',[{role:'sample',price:4.25,orderable:true},{role:'sellable',price:60,orderable:true,priceSource:'scraped'}], 100);
+t('cost KNOWN + below markup floor still BLOCKS', !b.ok && codes(b).includes('below-markup-floor'));
+
+console.log(R.join('\n'));
+console.log(`\n${pass} passed, ${fail} failed`);
+process.exit(fail?1:0);
diff --git a/DW-Programming/ImportNewSkufromURL/lib/price-integrity-cost.ts b/DW-Programming/ImportNewSkufromURL/lib/price-integrity-cost.ts
new file mode 100644
index 00000000..29020b3d
--- /dev/null
+++ b/DW-Programming/ImportNewSkufromURL/lib/price-integrity-cost.ts
@@ -0,0 +1,84 @@
+/**
+ * price-integrity-cost.ts — impure COST ADAPTER for the price-integrity gate.
+ * ============================================================================
+ * Ticket: TK-11403 (see lib/price-integrity-gate.ts header).
+ *
+ * The pure gate (lib/price-integrity-gate.ts) is DB-free and deterministic and
+ * MUST NOT import this file. This adapter is the ONLY place that resolves OUR
+ * net cost by shelling the price-finder authority:
+ *
+ *     node <price-finder>/scripts/verify-price.js --vendor <V> --dw-sku <S> --json
+ *
+ * That emits JSON with a `sample: [{ dw_sku, net_cost, our_price, ... }]` array.
+ * net_cost is OUR net cost in dollars. We return sample[0].net_cost as a number,
+ * or null on ANY error / missing value. Under the A-with-teeth contract
+ * (2026-09-11) a null cost makes the gate emit a NON-BLOCKING `cost-unverified`
+ * WARNING (not a block) — so a broken cost lookup does not halt the ~97 vendors
+ * with no cost feed, while the gate's cost-free absolute-dollar floor still
+ * blocks an obviously-mispriced (sub-$5 orderable) publish.
+ */
+
+import { execFile } from 'node:child_process';
+import { homedir } from 'node:os';
+import { join } from 'node:path';
+
+/** Location of the price-finder authority. Overridable via env for tests/CI. */
+const VERIFY_PRICE_SCRIPT =
+  process.env.PRICE_FINDER_VERIFY_SCRIPT ||
+  join(homedir(), '.claude', 'skills', 'price-finder', 'scripts', 'verify-price.js');
+
+interface VerifyPriceReport {
+  sample?: Array<{ dw_sku?: string; net_cost?: number | string | null }>;
+}
+
+/**
+ * Resolve OUR net cost (dollars) for a (vendor, dwSku) pair, or null on any
+ * failure/missing — fail-closed by design.
+ */
+export async function resolveNetCost(
+  vendor: string,
+  dwSku: string,
+  timeoutMs: number = 60_000,
+): Promise<number | null> {
+  if (!vendor || !dwSku) return null;
+
+  let stdout: string;
+  try {
+    stdout = await runVerifyPrice(vendor, dwSku, timeoutMs);
+  } catch {
+    return null; // spawn error, nonzero exit, timeout, etc. → fail-closed
+  }
+
+  let report: VerifyPriceReport;
+  try {
+    report = JSON.parse(stdout) as VerifyPriceReport;
+  } catch {
+    return null; // unparseable stdout → fail-closed
+  }
+
+  const first = report?.sample?.[0];
+  if (!first) return null;
+
+  const cost = typeof first.net_cost === 'string' ? Number(first.net_cost) : first.net_cost;
+  if (typeof cost !== 'number' || !isFinite(cost) || cost <= 0) return null;
+
+  return cost;
+}
+
+function runVerifyPrice(vendor: string, dwSku: string, timeoutMs: number = 60_000): Promise<string> {
+  // Clamp to a sane range: a 0/negative/NaN timeout would disable the kill-timer
+  // (execFile treats 0 as "no timeout"), which is exactly the unbounded-hang risk
+  // the batch path passes a short timeout to avoid.
+  const t = Number.isFinite(timeoutMs) && timeoutMs > 0 ? Math.min(timeoutMs, 60_000) : 60_000;
+  return new Promise((resolve, reject) => {
+    execFile(
+      process.execPath, // node
+      [VERIFY_PRICE_SCRIPT, '--vendor', vendor, '--dw-sku', dwSku, '--json'],
+      { timeout: t, maxBuffer: 8 * 1024 * 1024 },
+      (err, out) => {
+        if (err) return reject(err);
+        resolve(out ? out.toString() : '');
+      }
+    );
+  });
+}
diff --git a/DW-Programming/ImportNewSkufromURL/lib/price-integrity-gate.ts b/DW-Programming/ImportNewSkufromURL/lib/price-integrity-gate.ts
new file mode 100644
index 00000000..d579997a
--- /dev/null
+++ b/DW-Programming/ImportNewSkufromURL/lib/price-integrity-gate.ts
@@ -0,0 +1,476 @@
+/**
+ * price-integrity-gate.ts — SHARED WRITE-TIME PRICE-INTEGRITY GATE
+ * ============================================================================
+ * Ticket:  TK-11403 (root-cause prevention memo 2026-09-10-ROOTCAUSE-pricing-
+ *          integrity-recurrence) · closes the recurrence class under the
+ *          CLAUDE.md TK-11431 doctrine (rule 3: ships with a NEGATIVE TEST that
+ *          proves it goes RED on an injected fault).
+ *
+ * WHY THIS EXISTS
+ * ----------------------------------------------------------------------------
+ * Variant price writes for DW products go through addSampleVariant() in
+ * lib/shopify.ts AND at least two other choke points (schumacher's own
+ * addSampleVariant, scripts/import-queue-runner.js) — see PENDING-WIREIN memo;
+ * a truly "shared" gate must be wired into all of them. Each has three latent
+ * bug classes, each of which has shipped to the live store more than once:
+ *
+ *   Class A — SAMPLE OUTCOME + DEFAULT-PRICE LEAK. Two halves:
+ *             (i) OUTCOME (reworked TK-11447): the product must offer at least
+ *             ONE sample variant inside the band [sampleFloor, sampleCeiling] —
+ *             i.e. "can a customer buy a memo sample at a sample price?" This
+ *             REPLACES the old `samplePrice === 4.25` equality, which was MEASURED
+ *             WRONG for this catalog (see MEASUREMENT below).
+ *             (ii) LEAK: a sample/default price must never surface as the
+ *             sellable/roll price. Magnitude alone misses it (when cost is low,
+ *             4.25 clears the markup floor), so a sellable priced at one of this
+ *             product's own sample prices OR sourced from a 'defaulted' fallback
+ *             is a HARD BLOCK.
+ *
+ *             MEASUREMENT (bulk export, all 92,593 ACTIVE products, 2026-09-11):
+ *             a hard `samplePrice == 4.25` equality fail-CLOSES on ~1,287
+ *             legitimately-priced products whose vendor declares its own sample
+ *             price — DW Bespoke Studio $12.00 (a genuine 2ft x 1ft sample),
+ *             PS Removable / Artmura / PR Faux Leather $5.00, Fentucci $9.95 and
+ *             $4.95, Sister Parish $8.24 — and would HALT those imports, which is
+ *             exactly the "gate blocks a legitimate import" risk the wire-in memo
+ *             flagged. On the same data the SHAPE test ("sample priced above
+ *             $4.25") returned 4,301 hits, ~97% FALSE; the OUTCOME test ("offers
+ *             at least one sample-ish variant at or below a ~$50 ceiling")
+ *             returned 12, ALL TRUE on live confirm. Reference detector:
+ *             ~/.claude/yolo-queue/tk11403-residual/harm.mjs.
+ *
+ *   Class B — MARKUP + ANOMALY floors. When cost is KNOWN, the sellable must
+ *             clear the DW markup floor (retail = net_cost/0.65/0.85 ~1.81x;
+ *             gate floor a conservative 1.5x) — below → BLOCK. When cost is
+ *             UNKNOWN, that is a NON-BLOCKING WARNING ('cost-unverified'), so the
+ *             ~97 vendors with no cost feed still import (DTD "A-with-teeth"
+ *             verdict 2026-09-11). A cost-FREE absolute-dollar floor (default
+ *             $5, tunable) BLOCKS any orderable variant priced under it, so
+ *             unknown-cost is never a blind allow.
+ *
+ *   Class C — $0-ORDERABLE. addSampleVariant sets `numericUnitPrice =
+ *             numericPrice || '0.00'`, then inventoryPolicy:'CONTINUE' +
+ *             setInventoryQuantity(...,2025) — so a MISSING scraped price
+ *             silently becomes a $0 variant that is orderable at checkout. This
+ *             exact defect ($0 + orderable) has recurred 6 times. $0/NaN and a
+ *             negative price are separate HARD BLOCKS.
+ *
+ * This module is the PURE, DETERMINISTIC, ZERO-DEPENDENCY, DB-FREE gate that
+ * every price-writer can call to BLOCK a bad publish. It knows nothing about
+ * Shopify, Postgres, or the filesystem — cost is resolved by the separate,
+ * impure adapter lib/price-integrity-cost.ts and passed in as `netCost`.
+ *
+ * SEVERITY CONTRACT (A-with-teeth)
+ * ----------------------------------------------------------------------------
+ * assertPriceIntegrity returns { ok, violations, warnings }. `violations` are
+ * block-severity (fail ok, thrown by enforcePriceIntegrity); `warnings` are
+ * warn-severity (recorded, non-blocking). ok = violations.length === 0. Warnings
+ * (notably 'cost-unverified') MUST be surfaced by the caller to a monitored
+ * channel — a swallowed warning is a false green (TK-11431 amendment 2).
+ */
+
+/** One variant, described in the gate's own vocabulary (role, not option string). */
+export interface VariantCheck {
+  /** 'sample' = the $4.25 memo variant; 'sellable' = the real per-unit (Per Roll / Per Yard) variant. */
+  role: 'sample' | 'sellable';
+  /** Numeric price in dollars (NOT a "$54.00 USD" string — resolve that upstream). */
+  price: number;
+  /** True when the variant can be added to cart / checked out (CONTINUE policy or in-stock). */
+  orderable: boolean;
+  /** Optional variant SKU, echoed into violations for traceability. */
+  sku?: string;
+  /**
+   * PROVENANCE of the price (the headline-bug fix, TK-11403).
+   * The recurring live bug is the $4.25 SAMPLE DEFAULT leaking out as the
+   * sellable/roll price. Magnitude alone can't catch it — when net cost is low
+   * enough that 4.25 >= 1.5x cost, Class B and Class C both pass and the leak
+   * clears clean. So we ALSO track how the price got here:
+   *   'scraped'   — parsed from the vendor page (trustworthy)
+   *   'vendor'    — from a vendor cost/price feed (trustworthy)
+   *   'defaulted' — the code fell back to a literal (e.g. ProductDTO.price
+   *                 defaults to '4.25', route.ts hardcodes '4.25' on fallback
+   *                 paths) → a sellable with this source is BLOCKED, class A.
+   * OPTIONAL: when absent, the numeric sellable-equals-sample backstop still
+   * catches the same leak. Provenance is the belt; the backstop is the braces.
+   */
+  priceSource?: 'scraped' | 'vendor' | 'defaulted';
+}
+
+export interface PriceIntegrityInput {
+  /** The DW SKU under test (for logging / violation context). */
+  dwSku: string;
+  /**
+   * OUR net cost in dollars. null/undefined/<=0 ⇒ cost UNVERIFIED. As of the
+   * DTD "A-with-teeth" verdict (2026-09-11) this is a NON-BLOCKING WARNING
+   * ('cost-unverified'), NOT a hard block — blocking on unknown cost would halt
+   * ~97 of 108 vendors that have no cost feed. The absolute-dollar floor (below)
+   * keeps unknown-cost from being a blind allow.
+   */
+  netCost: number | null;
+  /**
+   * The PER-VENDOR DECLARED sample price in dollars — the FLOOR of the acceptable
+   * sample band, and the input that replaced the old global `sampleExpected`
+   * equality (TK-11447). Default 4.25 (the DW house baseline). Resolve it for a
+   * vendor with `resolveSampleFloor(vendor)`.
+   */
+  sampleFloor?: number;
+  /**
+   * Upper bound of the sample band in dollars. Default 50. This is the OUTCOME
+   * bound: a product passes Class A's outcome test when at least one sample
+   * variant is priced within [sampleFloor, sampleCeiling]. Measured 100% precise
+   * on the 92,593-product export (12/12 true positives) where the shape test was
+   * ~97% false.
+   */
+  sampleCeiling?: number;
+  /** Minimum sellable markup multiple over netCost (only checked when cost is KNOWN). Default 1.5. */
+  markupFloor?: number;
+  /**
+   * Cost-FREE absolute-dollar floor. Any ORDERABLE variant priced > 0 but below
+   * this → BLOCK ('below-absolute-floor'), whether or not cost is known — a
+   * conservative anomaly guard so an unknown-cost item can't ship at $2. Default
+   * 5.00 (Cody floated $15 as aggressive; 5.00 avoids false-blocking a legit
+   * cheap item). Tunable per caller.
+   */
+  absoluteFloor?: number;
+  /** Every variant being written for this product. */
+  variants: VariantCheck[];
+}
+
+export interface Violation {
+  class: 'A' | 'B' | 'C';
+  code: string;
+  /**
+   * 'block' → fails the gate (ok:false) and is thrown by enforcePriceIntegrity.
+   * 'warn'  → recorded but does NOT fail the gate; the caller MUST surface it to
+   *           a monitored channel (see recordOutcome note in the wire-in memo).
+   */
+  severity: 'block' | 'warn';
+  sku?: string;
+  message: string;
+  /** The offending observed value (price). */
+  observed: number;
+  /** Human-readable description of what was expected. */
+  expected: string;
+}
+
+export interface GateResult {
+  /** True iff there are ZERO block-severity violations. Warnings do NOT fail ok. */
+  ok: boolean;
+  /** Block-severity findings (fail the gate). */
+  violations: Violation[];
+  /** Warn-severity findings (recorded, non-blocking — MUST be surfaced by the caller). */
+  warnings: Violation[];
+}
+
+/** Custom error thrown by enforcePriceIntegrity so callers can catch + inspect .violations. */
+export class PriceIntegrityError extends Error {
+  violations: Violation[];
+  constructor(violations: Violation[]) {
+    super(
+      `price integrity gate BLOCKED: ${violations.length} violation(s) — ` +
+        violations.map(v => `[${v.class}:${v.code}${v.sku ? ' ' + v.sku : ''}]`).join(' ')
+    );
+    this.name = 'PriceIntegrityError';
+    this.violations = violations;
+  }
+}
+
+/** DW house baseline sample price, used when a vendor declares none. */
+const DEFAULT_SAMPLE_FLOOR = 4.25;
+/** Outcome bound: above this, a "sample" is not a sample price any customer would pay. */
+const DEFAULT_SAMPLE_CEILING = 50.0;
+const DEFAULT_MARKUP_FLOOR = 1.5;
+const DEFAULT_ABSOLUTE_FLOOR = 5.0;
+/** Epsilon for float comparison on sample-price bands. */
+const SAMPLE_EPSILON = 0.005;
+
+/**
+ * Vendors MEASURED (2026-09-11 bulk export of all 92,593 ACTIVE products) to
+ * declare their own sample price. Keyed case-insensitively on the Shopify vendor
+ * string. Where a vendor declares more than one, the MINIMUM is the floor, so
+ * this map can only reflect what the vendor actually charges — it never invents
+ * a tighter floor than the catalog shows.
+ */
+const VENDOR_DECLARED_SAMPLE_PRICE: Record<string, number> = {
+  'dw bespoke studio': 12.0,
+  'ps removable': 5.0,
+  artmura: 5.0,
+  'pr faux leather': 5.0,
+  fentucci: 4.95, // declares both $9.95 and $4.95 — floor is the minimum
+  'sister parish': 8.24,
+};
+
+/**
+ * Resolve the per-vendor declared sample price (the Class-A floor) for a vendor
+ * string. Falls back to the DW house baseline $4.25 for any vendor with no
+ * measured declared price. Pure — no I/O, safe for the gate's zero-dep contract.
+ */
+export function resolveSampleFloor(vendor: string | null | undefined): number {
+  if (!vendor) return DEFAULT_SAMPLE_FLOOR;
+  const declared = VENDOR_DECLARED_SAMPLE_PRICE[vendor.trim().toLowerCase()];
+  return typeof declared === 'number' && isFinite(declared) && declared > 0
+    ? declared
+    : DEFAULT_SAMPLE_FLOOR;
+}
+
+/**
+ * Pure gate. Returns { ok, violations, warnings }. NEVER throws. A single
+ * variant may produce multiple findings (e.g. a $0 sellable is both Class B and
+ * Class C). `violations` = block-severity (fail ok); `warnings` = warn-severity
+ * (recorded, non-blocking). ok = violations.length === 0.
+ */
+export function assertPriceIntegrity(input: PriceIntegrityInput): GateResult {
+  // Collect everything here with severity, then split at the end.
+  const findings: Violation[] = [];
+
+  const sampleFloor =
+    typeof input.sampleFloor === 'number' && isFinite(input.sampleFloor) && input.sampleFloor > 0
+      ? input.sampleFloor
+      : DEFAULT_SAMPLE_FLOOR;
+  const rawCeiling =
+    typeof input.sampleCeiling === 'number' && isFinite(input.sampleCeiling)
+      ? input.sampleCeiling
+      : DEFAULT_SAMPLE_CEILING;
+  // An incoherent caller (ceiling below floor) must not produce an empty band that
+  // blocks every product; the floor always sits inside its own band.
+  const sampleCeiling = Math.max(rawCeiling, sampleFloor);
+  const markupFloor =
+    typeof input.markupFloor === 'number' && isFinite(input.markupFloor)
+      ? input.markupFloor
+      : DEFAULT_MARKUP_FLOOR;
+  const absoluteFloor =
+    typeof input.absoluteFloor === 'number' && isFinite(input.absoluteFloor)
+      ? input.absoluteFloor
+      : DEFAULT_ABSOLUTE_FLOOR;
+
+  const variants = Array.isArray(input.variants) ? input.variants : [];
+
+  // Cost known & positive? Needed to PROVE the Class B MARKUP floor. When
+  // unknown, markup is a non-blocking WARNING (A-with-teeth), and the cost-free
+  // absolute-dollar floor below is what keeps unknown-cost from being a blind allow.
+  const costKnown =
+    typeof input.netCost === 'number' && isFinite(input.netCost) && input.netCost > 0;
+  const floorPrice = costKnown ? markupFloor * (input.netCost as number) : NaN;
+
+  // ---- Class A (i): the PRODUCT-LEVEL OUTCOME test (TK-11447) ----------------
+  // Replaces the old per-variant `samplePrice === 4.25` equality, MEASURED WRONG
+  // on 2026-09-11: the equality fail-CLOSES on ~1,287 legitimately-priced
+  // products whose vendor declares its own sample price. The honest question is
+  // an OUTCOME, asked ONCE per product, not per variant:
+  //     "can a customer buy a memo sample of this product at a sample price?"
+  // A product passes when AT LEAST ONE sample variant is priced inside the band
+  // [sampleFloor, sampleCeiling]. Measured 12/12 true positives where the shape
+  // test was ~97% false.
+  //
+  // Deliberately NOT a finding when the product ships zero sample variants: a
+  // sellable-only product is a catalog-completeness question, not a MIS-PRICING
+  // one, and this gate must not block an import for it.
+  const sampleVariants = variants.filter(v => v.role === 'sample');
+  const samplePrices = sampleVariants
+    .map(v => (typeof v.price === 'number' && isFinite(v.price) ? v.price : NaN))
+    .filter(p => isFinite(p));
+  const sampleInBand = samplePrices.some(
+    p => p >= sampleFloor - SAMPLE_EPSILON && p <= sampleCeiling + SAMPLE_EPSILON
+  );
+  if (sampleVariants.length > 0 && !sampleInBand) {
+    const cheapest = samplePrices.length ? Math.min(...samplePrices) : NaN;
+    findings.push({
+      class: 'A',
+      code: 'no-sample-in-band',
+      severity: 'block',
+      sku: sampleVariants[0]?.sku,
+      message:
+        `product offers NO sample inside the acceptable band — cheapest sample-role variant is ` +
+        `${fmt(cheapest)}, outside [${fmt(sampleFloor)}, ${fmt(sampleCeiling)}]. ` +
+        `A customer cannot buy a memo sample at a sample price.`,
+      observed: numOrZero(cheapest),
+      expected: `at least one sample variant within [${round2(sampleFloor)}, ${round2(sampleCeiling)}]`,
+    });
+  }
+
+  for (const v of variants) {
+    const price = typeof v.price === 'number' && isFinite(v.price) ? v.price : NaN;
+
+    // ---- Class A: per-sample sanity — BLOCK only BELOW the vendor's own floor ----
+    // The OUTCOME test above already decides whether the product offers a usable
+    // sample. What remains per-variant is the one case the outcome test cannot
+    // express: a sample priced UNDER the vendor's declared floor is a mis-price
+    // (e.g. the $3.50 rows found on Innovations vs their $4.25 house price), and
+    // an under-priced sample is a real money leak. A sample ABOVE the ceiling is
+    // NOT blocked here — that is the outcome test's job, and blocking it per
+    // variant would re-introduce the equality behaviour this ticket removed
+    // (a product may legitimately carry several sample sizes at several prices).
+    if (v.role === 'sample') {
+      if (isFinite(price) && price < sampleFloor - SAMPLE_EPSILON) {
+        findings.push({
+          class: 'A',
+          code: 'sample-below-vendor-floor',
+          severity: 'block',
+          sku: v.sku,
+          message: `sample variant priced ${fmt(price)} is BELOW this vendor's declared sample floor ${fmt(sampleFloor)}`,
+          observed: numOrZero(price),
+          expected: `>= ${round2(sampleFloor)} (vendor-declared sample floor)`,
+        });
+      }
+    }
+
+    // ---- Class A (sellable side): the SAMPLE/DEFAULT-PRICE LEAK guard — BLOCK ----
+    // The headline bug: the $4.25 sample default surfaces as the sellable price.
+    // Two independent hard-block catches, both class A:
+    if (v.role === 'sellable') {
+      // (b) PROVENANCE — a sellable whose price was defaulted (never scraped)
+      //     is a leak by construction, regardless of magnitude.
+      if (v.priceSource === 'defaulted') {
+        findings.push({
+          class: 'A',
+          code: 'sellable-defaulted-price',
+          severity: 'block',
+          sku: v.sku,
+          message: `sellable variant priced ${fmt(
+            price
+          )} from a DEFAULTED source (never scraped/vendor) — suspected sample/default-price leak`,
+          observed: numOrZero(price),
+          expected: "priceSource 'scraped' or 'vendor' for a sellable",
+        });
+      }
+      // (a) NUMERIC BACKSTOP — a sellable priced at (≈) the sample price is
+      //     essentially always the leak (DW sellables are $30–$2000+). Catches
+      //     it even when priceSource is absent.
+      // Compare against THIS PRODUCT'S OWN sample prices (not a global constant),
+      // falling back to the vendor floor when the product ships no sample variant.
+      // A global 4.25 would have missed the leak entirely on any vendor whose
+      // declared sample price is not 4.25 — the same blind spot the equality had.
+      const leakTargets = samplePrices.length ? samplePrices : [sampleFloor];
+      const matched = leakTargets.find(t => Math.abs(price - t) <= SAMPLE_EPSILON);
+      if (matched !== undefined) {
+        findings.push({
+          class: 'A',
+          code: 'sellable-equals-sample',
+          severity: 'block',
+          sku: v.sku,
+          message: `sellable variant priced ${fmt(
+            price
+          )} equals this product's sample price ${fmt(matched)} — suspected sample/default-price leak`,
+          observed: numOrZero(price),
+          expected: `sellable price != any sample price on this product (±${SAMPLE_EPSILON})`,
+        });
+      }
+    }
+
+    // ---- Class B: the MARKUP floor (cost-based) ----
+    if (v.role === 'sellable') {
+      if (!costKnown) {
+        // A-with-teeth: cost unknown ⇒ WARN (allow import, counted), NOT a block.
+        // The caller MUST surface this warning to a monitored channel.
+        findings.push({
+          class: 'B',
+          code: 'cost-unverified',
+          severity: 'warn',
+          sku: v.sku,
+          message: `markup UNVERIFIED — netCost unknown/invalid (${String(
+            input.netCost
+          )}); imported with a warning (magnitude not proven against cost)`,
+          observed: numOrZero(price),
+          expected: `netCost > 0 to verify price >= ${markupFloor}x cost`,
+        });
+      } else if (!(price >= floorPrice)) {
+        findings.push({
+          class: 'B',
+          code: 'below-markup-floor',
+          severity: 'block',
+          sku: v.sku,
+          message: `sellable variant ${fmt(price)} is below the ${markupFloor}x markup floor of ${fmt(
+            floorPrice
+          )} (netCost ${fmt(input.netCost as number)})`,
+          observed: numOrZero(price),
+          expected: `>= ${round2(floorPrice)} (${markupFloor}x netCost ${input.netCost})`,
+        });
+      }
+    }
+
+    // ---- Class B: cost-FREE absolute-dollar floor — BLOCK ----
+    // Fires for ANY orderable variant priced > 0 but under absoluteFloor,
+    // regardless of whether cost is known. $0/negative are Class C, not here.
+    // SAMPLE-ROLE EXEMPTION (TK-11447, caught by the negative test): a sample is
+    // legitimately cheap — the DW house sample is $4.25, BELOW the $5 default
+    // absolute floor — so applying this guard to samples blocks essentially every
+    // standard DW import. Samples already have their own floor enforced above
+    // ('sample-below-vendor-floor', the vendor-declared price). The absolute
+    // floor is an anomaly guard for SELLABLES, where no legitimate item is $2.
+    if (v.role === 'sellable' && v.orderable === true && price > 0 && price < absoluteFloor) {
+      findings.push({
+        class: 'B',
+        code: 'below-absolute-floor',
+        severity: 'block',
+        sku: v.sku,
+        message: `orderable ${v.role} variant ${fmt(price)} is below the absolute floor ${fmt(
+          absoluteFloor
+        )} — suspected mis-price (cost-free anomaly guard)`,
+        observed: numOrZero(price),
+        expected: `>= ${round2(absoluteFloor)} while orderable`,
+      });
+    }
+
+    // ---- Class C: any orderable variant (any role) MUST be priced > 0 — BLOCK ----
+    // Diagnose honestly: a NEGATIVE price is a different fault than a $0/NaN
+    // price and gets its own code so the report isn't misleading.
+    if (v.orderable === true && !(price > 0)) {
+      if (price < 0) {
+        findings.push({
+          class: 'C',
+          code: 'negative-price',
+          severity: 'block',
+          sku: v.sku,
+          message: `${v.role} variant is ORDERABLE at a NEGATIVE price ${fmt(
+            price
+          )} — invalid price`,
+          observed: numOrZero(price),
+          expected: '> 0 while orderable',
+        });
+      } else {
+        // price === 0 or NaN/invalid
+        findings.push({
+          class: 'C',
+          code: 'zero-price-orderable',
+          severity: 'block',
+          sku: v.sku,
+          message: `${v.role} variant is ORDERABLE at ${fmt(
+            price
+          )} — a $0 orderable variant is a checkout money-leak`,
+          observed: numOrZero(price),
+          expected: '> 0 while orderable',
+        });
+      }
+    }
+  }
+
+  const violations = findings.filter(f => f.severity === 'block');
+  const warnings = findings.filter(f => f.severity === 'warn');
+  return { ok: violations.length === 0, violations, warnings };
+}
+
+/**
+ * Fail-closed helper callers use to BLOCK a publish. Runs the pure gate and
+ * THROWS PriceIntegrityError (carrying the block-severity .violations) ONLY when
+ * a block-severity violation exists. Warn-severity findings (e.g. 'cost-unverified')
+ * do NOT throw — the caller must still surface them via assertPriceIntegrity(...).warnings
+ * to a monitored channel (see the wire-in memo's recordOutcome note). Returns
+ * void on a clean-or-warn-only input.
+ */
+export function enforcePriceIntegrity(input: PriceIntegrityInput): void {
+  const result = assertPriceIntegrity(input);
+  if (!result.ok) {
+    throw new PriceIntegrityError(result.violations);
+  }
+}
+
+// ---- small pure helpers (no deps) ----
+function numOrZero(n: number): number {
+  return typeof n === 'number' && isFinite(n) ? n : 0;
+}
+function round2(n: number): number {
+  return Math.round(n * 100) / 100;
+}
+function fmt(n: number): string {
+  return isFinite(n) ? `$${round2(n).toFixed(2)}` : `$NaN`;
+}
diff --git a/DW-Programming/ImportNewSkufromURL/lib/price-integrity-record.ts b/DW-Programming/ImportNewSkufromURL/lib/price-integrity-record.ts
new file mode 100644
index 00000000..cbef5285
--- /dev/null
+++ b/DW-Programming/ImportNewSkufromURL/lib/price-integrity-record.ts
@@ -0,0 +1,57 @@
+/**
+ * price-integrity-record.ts — BEST-EFFORT telemetry sink for the price gate.
+ * ============================================================================
+ * Ticket: TK-11403 (see lib/price-integrity-gate.ts header).
+ *
+ * The gate's WARN findings (notably 'cost-unverified') must be surfaced to a
+ * MONITORED channel, not swallowed in a console.log — a swallowed warning is a
+ * false green (TK-11431 amendment 2). This appends one JSONL line per outcome to
+ *   ~/.claude/skills/dw-price-integrity-gate/data/runtime-events.jsonl
+ * which the dw-price-integrity-gate skill's report.mjs folds into its
+ * fleet-health data/latest.json.
+ *
+ * HARD FAIL-SAFE: telemetry MUST NEVER crash or slow an import. The whole body
+ * is wrapped in try/catch that SWALLOWS every error — if the dir/path doesn't
+ * exist on the host it is created; if creation fails, it silently no-ops.
+ */
+
+import { appendFileSync, mkdirSync } from 'node:fs';
+import { homedir } from 'node:os';
+import { join } from 'node:path';
+
+export interface OutcomeRecord {
+  vendor?: string | null;
+  dwSku?: string | null;
+  /** 'warn' = imported with warnings; 'block' = publish blocked by a violation. */
+  outcome: 'warn' | 'block';
+  /** The violation/warning codes involved (e.g. ['cost-unverified'] or ['below-absolute-floor']). */
+  codes?: string[];
+}
+
+const EVENTS_PATH =
+  process.env.PRICE_INTEGRITY_EVENTS_PATH ||
+  join(homedir(), '.claude', 'skills', 'dw-price-integrity-gate', 'data', 'runtime-events.jsonl');
+
+/**
+ * Append one telemetry line. Best-effort, fully swallowed — never throws.
+ */
+export function recordOutcome(rec: OutcomeRecord): void {
+  try {
+    const line =
+      JSON.stringify({
+        ts: new Date().toISOString(),
+        vendor: rec.vendor ?? null,
+        dwSku: rec.dwSku ?? null,
+        outcome: rec.outcome,
+        codes: Array.isArray(rec.codes) ? rec.codes : [],
+      }) + '\n';
+    try {
+      mkdirSync(join(EVENTS_PATH, '..'), { recursive: true });
+    } catch {
+      /* dir may already exist or be uncreatable — ignore */
+    }
+    appendFileSync(EVENTS_PATH, line);
+  } catch {
+    /* telemetry MUST NEVER crash or slow an import — swallow everything */
+  }
+}
diff --git a/DW-Programming/ImportNewSkufromURL/lib/shopify.ts b/DW-Programming/ImportNewSkufromURL/lib/shopify.ts
index c04f6b09..60d47cb1 100644
--- a/DW-Programming/ImportNewSkufromURL/lib/shopify.ts
+++ b/DW-Programming/ImportNewSkufromURL/lib/shopify.ts
@@ -952,13 +952,19 @@ async function createProductWithMetafield(dto: ProductDTO, priceUnit: string, un
 export interface AddSampleVariantResult {
   priced: boolean;
   gateBlocked: boolean;
+  // sampleOk:false when the Sample variant could NOT be established (a real,
+  // non-"already exists" productVariantsBulkCreate userError, or a path that
+  // never reached the sample create). The caller must NOT promote a product to
+  // ACTIVE without a memo sample (DW hard rule: every product has a Sample),
+  // so ACTIVE promotion ANDs this with priced && !gateBlocked.
+  sampleOk: boolean;
 }
 
 // Price BOTH the per-unit (Per Roll / Per Yard) variant AND the Sample variant that
 // were auto-created from the two Size option values. The per-unit variant carries the
 // real scraped vendor price (standing rule: every new SKU must have a price per unit);
 // the Sample variant is fixed at $4.25 (standing rule: every product has a Sample).
-async function addSampleVariant(productId: string, sku: string, price: string, unitLabel: string, unitKind: string, vendor: string = '', productType: string = 'Wallcovering'): Promise<AddSampleVariantResult> {
+async function addSampleVariant(productId: string, sku: string, price: string, unitLabel: string, unitKind: string, vendor: string = '', productType: string = 'Wallcovering', priceDefaulted: boolean = false): Promise<AddSampleVariantResult> {
   // First, get the product's variants (Shopify auto-created them from productOptions)
   const getVariantsQuery = `
     query getProductVariants($id: ID!) {
@@ -1033,7 +1039,10 @@ async function addSampleVariant(productId: string, sku: string, price: string, u
             price: Number(numericUnitPrice),
             orderable: true, // CONTINUE + setInventoryQuantity(...,2025) below make it orderable
             sku: `${sku}-${unitKind}`,
-            priceSource: numericPrice ? 'scraped' : 'defaulted',
+            // Provenance is 'scraped' ONLY when the caller had a real dto.price that also
+            // parsed to a number; a defaulted literal (e.g. '4.25') or a non-numeric price
+            // is 'defaulted' so the gate's Class-A provenance defense can actually fire.
+            priceSource: (!priceDefaulted && numericPrice) ? 'scraped' : 'defaulted',
           },
           { role: 'sample', price: sampleFloor, orderable: false, sku: `${sku}-Sample` },
         ],
@@ -1066,10 +1075,15 @@ async function addSampleVariant(productId: string, sku: string, price: string, u
     // (2026-09-14 fix) do NOT write the bad price — publish blocked. Return gateBlocked
     // so createOrGetProduct() force-keeps the product DRAFT and skips publish, instead
     // of relying on the caller to notice a swallowed void return.
-    if (gateBlocked) return { priced: false, gateBlocked: true };
+    if (gateBlocked) return { priced: false, gateBlocked: true, sampleOk: false };
 
     // 1) Update the per-unit variant
     let priced = false;
+    // Sample establishment tracker (review 2026-09-14): stays true when a Sample
+    // variant already exists or is created cleanly; flipped false on a real
+    // create failure so the caller keeps the product DRAFT instead of shipping
+    // an ACTIVE product with no memo sample.
+    let sampleOk = true;
     if (unitVariant) {
       const upd = await shopifyGraphQL<{
         productVariantsBulkUpdate: {
@@ -1096,7 +1110,10 @@ async function addSampleVariant(productId: string, sku: string, price: string, u
         priced = true;
       }
       const iid = upd.productVariantsBulkUpdate.productVariants?.[0]?.inventoryItem?.id;
-      if (iid) await setInventoryQuantity(iid, 2025);
+      // Only stock the variant when the price write CONFIRMED error-free. A partial
+      // success (variants populated alongside userErrors) must NOT leave a variant
+      // stocked+orderable at an unconfirmed/failed price (the $0-orderable class).
+      if (iid && !uerr?.length) await setInventoryQuantity(iid, 2025);
     }
 
     // 2) Create the Sample variant (idempotent — skip if one already exists)
@@ -1121,6 +1138,17 @@ async function addSampleVariant(productId: string, sku: string, price: string, u
       const cerr = cre.productVariantsBulkCreate.userErrors;
       if (cerr?.length && !JSON.stringify(cerr).toLowerCase().includes('already exists')) {
         console.error(JSON.stringify({ event: 'shopify_sample_variant_error', productId, errors: cerr }));
+        // (review 2026-09-14) A real Sample-create failure must block ACTIVE
+        // promotion — otherwise the product goes live with a sellable but NO memo
+        // sample (DW hard rule violation the price gate can't see, since it only
+        // validated the INTENDED sample pre-write).
+        sampleOk = false;
+      } else if (!cre.productVariantsBulkCreate.productVariants?.length &&
+                 !JSON.stringify(cerr || []).toLowerCase().includes('already exists')) {
+        // No variant returned and no "already exists" error => the sample was not
+        // actually created. Treat as not-established so we don't promote blind.
+        console.error(JSON.stringify({ event: 'shopify_sample_variant_missing', productId }));
+        sampleOk = false;
       }
     }
 
@@ -1131,7 +1159,7 @@ async function addSampleVariant(productId: string, sku: string, price: string, u
       unitLabel,
       unitPrice: numericUnitPrice,
     }));
-    return { priced, gateBlocked: false };
+    return { priced, gateBlocked: false, sampleOk };
   } catch (error) {
     console.error(JSON.stringify({
       timestamp: new Date().toISOString(),
@@ -1143,7 +1171,7 @@ async function addSampleVariant(productId: string, sku: string, price: string, u
     // caller does not promote an unpriced/unwritten product to ACTIVE + published
     // (2026-09-14 fix — this swallowed exception used to leave the caller with no
     // way to know the price write never landed).
-    return { priced: false, gateBlocked: false };
+    return { priced: false, gateBlocked: false, sampleOk: false };
   }
 }
 
@@ -1997,7 +2025,8 @@ export async function createOrGetProduct(dto: ProductDTO): Promise<ShopifyProduc
   // back to 'Wallcovering' (the DW house default) only when it is absent, preserving prior
   // behavior for typeless imports. Carnegie and other fabric lines route through this engine.
   const resolvedProductType = dto.specs?.product_type || 'Wallcovering';
-  const variantResult = await addSampleVariant(product.id, sku, price, unitLabel, unitKind, vendorName, resolvedProductType);
+  // price === dto.price || '4.25'; pass whether it was DEFAULTED so the gate sees true provenance.
+  const variantResult = await addSampleVariant(product.id, sku, price, unitLabel, unitKind, vendorName, resolvedProductType, !dto.price);
 
   // Link the DW SKU to its Shopify product in the registry (audit + future dedup).
   if (assignment) {
@@ -2014,7 +2043,10 @@ export async function createOrGetProduct(dto: ProductDTO): Promise<ShopifyProduc
   // !variantResult.gateBlocked). A gate BLOCK, gate infra error, or failed price write
   // now leaves the product DRAFT + tagged, never ACTIVE + all-channels-published at $0
   // (TK-11357 recurrence this fix closes).
-  const priceWriteOk = variantResult.priced && !variantResult.gateBlocked;
+  // (review 2026-09-14) sampleOk ANDed in: a product with no established memo
+  // sample must stay DRAFT (DW hard rule: every product has a Sample). A failed
+  // Sample create used to be logged-and-ignored while the product still went ACTIVE.
+  const priceWriteOk = variantResult.priced && !variantResult.gateBlocked && variantResult.sampleOk;
   if (eligibleForActive && priceWriteOk) {
     const promoted = await setProductStatus(product.id, 'ACTIVE');
     if (promoted) {
@@ -2036,6 +2068,10 @@ export async function createOrGetProduct(dto: ProductDTO): Promise<ShopifyProduc
     if (variantResult.gateBlocked || !variantResult.priced) {
       await addProductTags(product.id, ['Needs-Price-Review']);
     }
+    // (review 2026-09-14) A missing/failed Sample is its own kept-DRAFT reason.
+    if (!variantResult.sampleOk) {
+      await addProductTags(product.id, ['Needs-Sample']);
+    }
     console.warn(JSON.stringify({
       event: 'product_kept_draft',
       productId: product.id,
@@ -2043,6 +2079,7 @@ export async function createOrGetProduct(dto: ProductDTO): Promise<ShopifyProduc
       eligibleForActive,
       gateBlocked: variantResult.gateBlocked,
       priced: variantResult.priced,
+      sampleOk: variantResult.sampleOk,
     }));
   }
 
diff --git a/DW-Programming/ImportNewSkufromURL/lib/weight-guard.ts b/DW-Programming/ImportNewSkufromURL/lib/weight-guard.ts
new file mode 100644
index 00000000..6f46a611
--- /dev/null
+++ b/DW-Programming/ImportNewSkufromURL/lib/weight-guard.ts
@@ -0,0 +1,74 @@
+// weight-guard.ts — TK-11539 (2026-09-12) CREATE-PATH weight guard for the shared
+// ImportNewSkufromURL engine (Carnegie + every vendor routed through /api/create-product).
+//
+// Prevents a recurrence of the TK-11414 zero-weight gap: createOrGetProduct/addSampleVariant
+// created variants with inventoryItem:{sku,tracked} but NEVER set measurement.weight, so every
+// imported product shipped at ZERO weight. Zero weight collapses orders into the lowest
+// freight tier / the free-shipping band and mis-costs DW shipping (Steve's rule: NO product
+// may go ACTIVE with a missing/zero product WEIGHT).
+//
+// Values mirror the canonical backfill primitive:
+//   ~/Projects/designerwallcoverings/scripts/lib/weight-guard.mjs
+// (SAMPLE_WEIGHT_LB, FALLBACK_LB, TYPE_DEFAULT_LB). Keep this in sync with that file and the
+// approved TK-11414 backfill. Unit is always POUNDS (Shopify WeightUnit enum).
+
+export const SAMPLE_WEIGHT_LB = 0.25;
+export const FALLBACK_LB = 2.0;
+
+// product_type -> sellable default weight (POUNDS). Mirrors weight-guard.mjs.
+export const TYPE_DEFAULT_LB: Record<string, number> = {
+  'Wallcovering': 3.0, 'Wallcoverings': 3.0, 'Wallpaper': 3.0,
+  'Metallic Wallcovering': 3.0, 'Commercial Wallcovering': 3.0,
+  'Mural': 4.0,
+  'Fabric': 1.0, 'Commercial Fabric': 1.0, 'Commercial Drapery': 1.0,
+  'Trim': 0.5, 'Acoustic Panel': 6.0, 'Pillow': 1.5,
+  'Upholstered Walls/Panels': 6.0, 'Tin Ceiling Tile': 2.0,
+  'Hardware': 1.0, 'Furniture': 15.0, 'Memo Sample': 0.25,
+};
+
+export type WeightUnit = 'POUNDS';
+
+export interface WeightMeasurement {
+  measurement: { weight: { value: number; unit: WeightUnit } };
+}
+
+const norm = (t?: string | null): string => String(t ?? '').trim().toLowerCase();
+
+/** Sellable default weight (lb) for a product_type; unknown/blank -> FALLBACK_LB. Case-insensitive. */
+export function sellableWeightLb(productType?: string | null): number {
+  const key = Object.keys(TYPE_DEFAULT_LB).find(k => norm(k) === norm(productType));
+  const w = key ? TYPE_DEFAULT_LB[key] : FALLBACK_LB;
+  return (Number.isFinite(w) && w > 0) ? w : FALLBACK_LB;
+}
+
+/** Sample-variant weight (lb) — always the approved 0.25 lb. */
+export function sampleWeightLb(): number {
+  return SAMPLE_WEIGHT_LB;
+}
+
+/**
+ * THE CREATE-SIDE GUARD. Merge a positive measurement.weight into a
+ * ProductVariantsBulkInput.inventoryItem input so no variant is ever created zero-weight.
+ *   role 'sample'   -> SAMPLE_WEIGHT_LB (0.25 lb)
+ *   role 'sellable' -> per-productType default (FALLBACK_LB when unknown)
+ * The returned inventoryItem ALWAYS carries weight.value > 0.
+ * Valid on ProductVariantsBulkInput.inventoryItem (InventoryItemInput.measurement) in the
+ * Shopify Admin API 2024-07+.
+ */
+export function inventoryItemWithWeight<T extends object>(
+  base: T,
+  opts: { role: 'sellable' | 'sample'; productType?: string | null }
+): T & WeightMeasurement {
+  const value = opts.role === 'sample' ? sampleWeightLb() : sellableWeightLb(opts.productType);
+  return { ...base, measurement: { weight: { value, unit: 'POUNDS' } } };
+}
+
+/**
+ * Invariant used by the negative test and callable as a runtime assert: a variant's
+ * inventoryItem input must carry a finite, positive measurement.weight. Returns false for
+ * the pre-fix payload (no measurement), which is exactly the fault the guard removes.
+ */
+export function hasPositiveWeight(inventoryItemInput: any): boolean {
+  const v = inventoryItemInput?.measurement?.weight?.value;
+  return typeof v === 'number' && Number.isFinite(v) && v > 0;
+}

← 25dadd6f fix(TK-11503): validate Shopify token via read_products prob  ·  back to Designer Wallcoverings  ·  fix(rimg): drop placeholder srcset + use native loading=lazy e0bf2137 →