[object Object]

← back to Newmor Onboard

Newmor onboard cycle 1: reconciliation + metafield map artifacts (TK-10670)

a9b82b85f56564909c55ef742b9f4fcf863c5217 · 2026-08-18 09:21:58 -0700 · steve@designerwallcoverings.com

Files touched

Diff

commit a9b82b85f56564909c55ef742b9f4fcf863c5217
Author: steve@designerwallcoverings.com <steve@designerwallcoverings.com>
Date:   Tue Aug 18 09:21:58 2026 -0700

    Newmor onboard cycle 1: reconciliation + metafield map artifacts (TK-10670)
---
 .gitignore                  |  7 +++++++
 README.md                   |  5 +++++
 artifacts/METAFIELD-MAP.md  | 41 +++++++++++++++++++++++++++++++++++++++++
 artifacts/RECONCILIATION.md | 43 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 96 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..08240ae
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+node_modules/
+.env*
+tmp/
+*.log
+.DS_Store
+dist/
+build/
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d4cdd3a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,5 @@
+# newmor-onboard (TK-10670)
+Refresh + complete the existing Newmor line (registry id 56) under series DWNM-.
+Distributor: LBI Boyd. All specs -> Shopify metafields, never body_html.
+See artifacts/RECONCILIATION.md + artifacts/METAFIELD-MAP.md.
+Pricing PARKED (no cost basis). Catalog refresh BLOCKED (Norman/newmor-agent down).
diff --git a/artifacts/METAFIELD-MAP.md b/artifacts/METAFIELD-MAP.md
new file mode 100644
index 0000000..0fcdcfc
--- /dev/null
+++ b/artifacts/METAFIELD-MAP.md
@@ -0,0 +1,41 @@
+# Newmor → Shopify spec→METAFIELD mapping (TK-10670)
+
+HARD RULE (Steve, 2026-08-18): **all spec/metafield info goes into Shopify METAFIELDS, never body_html.**
+`body_html` stays clean marketing prose only. This map is the enforcement contract for the
+(gated) publish/backfill.
+
+Source: `newmor_catalog` columns → Shopify metafield. Namespace defaults to `custom.*`;
+FINAL namespace/keys must be reconciled against the existing DW live metafield schema
+(verify from a live Newmor/commercial product in cycle 2 before any write) so we don't fork keys.
+
+| catalog column        | Shopify target                    | metafield type          | notes |
+|-----------------------|-----------------------------------|-------------------------|-------|
+| width / width_inches  | `custom.width`                    | dimension               | coverage 787/1294 — gap |
+| length                | `custom.roll_length`              | dimension               | coverage 1045/1294 |
+| coverage              | `custom.coverage`                 | single_line_text_field  | EMPTY (0) — needs refresh |
+| repeat_v              | `custom.pattern_repeat_vertical`  | dimension               | EMPTY (0) — needs refresh |
+| repeat_h              | `custom.pattern_repeat_horizontal`| dimension               | EMPTY (0) — needs refresh |
+| match_type            | `custom.pattern_match`            | single_line_text_field  | EMPTY (0) — needs refresh |
+| material              | `custom.material`                 | single_line_text_field  | coverage 617/1294 |
+| finish                | `custom.finish`                   | single_line_text_field  | EMPTY (0) — needs refresh |
+| fire_rating           | `custom.fire_rating`              | single_line_text_field  | EMPTY (0) — commercial line, IMPORTANT |
+| application           | `custom.application`              | single_line_text_field  | EMPTY (0) — needs refresh |
+| features              | `custom.features`                 | list.single_line_text   | |
+| color_primary         | `custom.color_primary`            | single_line_text_field  | |
+| color_secondary       | `custom.color_secondary`          | single_line_text_field  | |
+| color_hex / dominant_color_hex | `custom.color_hex`       | color                   | |
+| us_distributor        | `custom.us_distributor`           | single_line_text_field  | = "LBI Boyd" (Steve-confirmed) |
+| product_type          | Shopify **product_type** (native) | —                       | NOT a metafield |
+| collection            | Shopify **tag/collection**        | —                       | NOT a metafield |
+| product_url           | `custom.vendor_url`               | url                     | |
+
+## body_html policy
+- KEEP: marketing prose (pattern story, designer, base options). Current bodies are clean
+  (only 46/1284 mention a spec token) — light cleanup, not systemic.
+- STRIP from body if present: width/repeat/fire-rating/match/coverage → move to the metafields above.
+
+## Data-gap flag (feeds refresh branch)
+repeat_v/repeat_h, finish, fire_rating, application, match_type, coverage are ALL empty in
+`newmor_catalog`. For a **commercial** line, fire_rating + application + match are material specs.
+→ the catalog REFRESH (via Norman/newmor-agent, currently DOWN) must recapture these before
+the metafield backfill is meaningful.
diff --git a/artifacts/RECONCILIATION.md b/artifacts/RECONCILIATION.md
new file mode 100644
index 0000000..f985bde
--- /dev/null
+++ b/artifacts/RECONCILIATION.md
@@ -0,0 +1,43 @@
+# Newmor SKU normalization reconciliation (TK-10670)
+
+Goal: bring the 543 mfr-coded LIVE Newmor products onto the canonical `DWNM-58xxxx` series
+(Steve-ruled), deduping against the existing 1,294-row `newmor_catalog` (which already holds
+the DWNM codes). NO new series. NO duplicates.
+
+## Data topology (verified 2026-08-18, cycle 1)
+- `newmor_catalog`: 1,294 rows; **1,285 already carry a distinct `DWNM-58xxxx` `dw_sku`** (9 lack one).
+  Keyed internally by `mfr_sku` + legacy numeric REST id (`shopify_product_id`, e.g. 7810541092915).
+- `shopify_products` (live mirror): 544 Newmor rows; **543 still mfr-coded** (`Brushed Steel BST-008`),
+  only 1 already DWNM. Its `dw_sku` column is ALSO mfr-coded (not the DWNM code). Keyed by
+  GraphQL gid (`gid://shopify/Product/4351...`) — a DIFFERENT id space than the catalog.
+- ⇒ catalog↔live can only be bridged by `mfr_sku`. The numeric-REST-id ↔ gid map is absent.
+
+## Match quality (the blocker)
+- Raw exact mfr_sku join: 126/544.
+- Normalized (upper+trim) join: **126/544 = 23.2%** — no better.
+- Root cause: many LIVE `mfr_sku` are malformed collection-level junk
+  (`ARONA-COMMERCIAL-WALLCOVERING-ARONA`, sku `Arona -`) while catalog codes are clean (`AR-103`).
+
+## Decision (DTD-committed, cycle 1)
+A 543-row live SKU rewrite off a 23%-confident map is unsafe → **HOLD the gated rewrite.**
+Cycle 2+ must raise match confidence first:
+1. Fuzzy pattern-code extraction: pull the trailing `[A-Z]{2,4}-\d{2,4}` code from both live sku
+   and catalog mfr_sku, match on that; then match residuals by pattern_name + color_name.
+2. Re-measure; only when a high-confidence tier (target ≥ ~90%) exists do we draft the gated rewrite.
+3. Assign DWNM to the 9 catalog rows lacking `dw_sku`.
+
+## Staged artifact (reversible)
+`newmor_sku_reconcile` table in dw_unified (Mac2 staging). Drop-to-undo. Columns:
+`shopify_id, handle, live_sku, mfr_sku, k(normalized), target_dwnm, has_target, live_already_ok`.
+Current: 126 matched / 418 unmatched-flagged / 0 already-ok.
+
+## Gated (NOT executed — pending-approval when ready)
+- Live variant-SKU rewrite on the 543 products (customer-facing).
+- Publish of the ~751 catalog rows not yet on Shopify.
+- Any live price write (BLOCKED separately — see pricing branch).
+
+## Branch status
+- SKU normalization: IN PROGRESS (match rate too low → cycle-2 fuzzy pass).
+- Metafield map: DEFINED (see METAFIELD-MAP.md); backfill gated + depends on refresh.
+- Catalog refresh: BLOCKED — newmor-agent (Norman, :9695) is DOWN in pm2.
+- Pricing: PARKED — `newmor_catalog` has 0/1294 cost; needs LBI Boyd cost list (vendor-price-ingest).

(oldest)  ·  back to Newmor Onboard  ·  Cycle1 fix: 3-bucket disposition + never-overwrite metafield b4d774a →