← back to Dw Contact Us Pages

docs-memo-copy.md

71 lines

# TK-11925 — Designers Guild + Ralph Lauren + Christian Lacroix → "contact-us" product pages (785 ACTIVE)

**Ticket:** TK-11925-designers-guild-contact-us-pages-568-act · **Owner:** vp-dw-commerce (executor: claude-run-11925) · **Drafted:** 2026-09-19
**Store:** designer-laboratory-sandbox.myshopify.com = the LIVE DW store · **Live theme:** 145556635699 (role main)
**Scope (Steve, in-session 2026-09-19):** ALL ACTIVE products of vendors "Designers Guild", "Ralph Lauren", "Christian Lacroix Europe". NOT Quadrille.
**Steve's CLARIFY answers (2026-09-19):** (1) Hide + HARDEN · (2) Unpublish from Google & YouTube + Shop + Buy Button only · (3) Boost grid cards show "Contact us for pricing".

## What the customer sees after approval
- Product page: image gallery → **contact block directly below the image** (phone (888) 373-4564 · info@ mailto with SKU prefilled · inline "request pricing & availability" form posting to the existing /api/sku-inquiry pipeline) · title/vendor/SKU/description on the right. **No price, no add-to-cart, no $4.25 sample button.**
- Collection / search cards (Boost): price replaced by "Contact us for pricing" for the 3 vendors.
- Roll/yard variants: inventory policy DENY + on-hand 0, so a cart permalink or the public .json cannot complete a purchase (the price STRING remains visible in /products/<handle>.json — Shopify cannot hide it; see residuals).
- Google & YouTube / Shop / Buy Button: products unpublished → no GMC price/availability mismatch. Online Store, FB/IG, Pinterest, TikTok, Houzz, POS unchanged.

## Blast radius (measured live 2026-09-19)
| Item | Count |
|---|---|
| Products (DG / RL / CL) | 785 (568 / 140 / 77) |
| Non-sample variants to harden (DENY + 0) | 737 (598 currently UNTRACKED + CONTINUE → tracking enabled then on_hand 0; 139 tracked with stock → on_hand 0; 48 products are sample-only, nothing to harden) |
| Sample variants (untouched) | 785 (783 × $4.25, 1 × $3.50, 1 × $5.00) |
| Publications to remove (products × 3 channels, only where currently published) | 2,355 (785 × 3, all currently published) |
| Theme assets written on 145556635699 | 7 (new: templates/product.contact-us.json, snippets/dw-contact-us-block.liquid, assets/dw-contact-us-cards.js · edited: snippets/product.liquid (+1 gated render line), snippets/hide-browse-hidden.liquid (+1 script tag + vendor list), snippets/product-list-item.liquid (+1 template_suffix branch), layout/theme.liquid (JSON-LD Offer carve-out extended from the existing Newmor condition to product.template_suffix == contact-us); live preimage of all 4 already captured in data/theme-preimage/145556635699/) |

## Mechanism
1. **Alternate template** `templates/product.contact-us.json` — the live product template minus the `price` and `form` blocks (the buy box iterates `section.blocks`, so no price / cart / sample markup is emitted at all — not hidden, absent). `description` block enabled.
2. **Contact block** `snippets/dw-contact-us-block.liquid`, rendered by a one-line additive edit in `snippets/product.liquid` immediately after the gallery, gated on `template.suffix == 'contact-us'`. Zero effect on any other template. Belt-and-braces CSS under `body.template-suffix-contact-us` (theme.liquid already stamps that body class).
3. **Cards** `assets/dw-contact-us-cards.js` (MutationObserver over `.boost-sd__product-item`, same pattern as the live dw-showroom-hide.js) + a `template_suffix` branch in `snippets/product-list-item.liquid` for the liquid-rendered path. Vendor list data-driven (shop metafield → theme setting → literal fallback), mirroring `showroom_vendors`.
4. **Product writes** (scripts, preimage-first, ledgered): `assign-template` (templateSuffix → contact-us) · `harden-variants` (non-sample: DENY + 0 at every location) · `unpublish-channels` (3 publication ids).

## Order of operations (each step dry-runs first; every step writes its preimage BEFORE the first write)
```
cd ~/Projects/dw-contact-us-pages
node scripts/enumerate.mjs                                   # refresh data/targets.json (read-only)
node scripts/push-theme.mjs --theme 145556635699 --allow-main --apply   # theme preimage → data/theme-preimage/145556635699/
node scripts/assign-template.mjs --apply                     # ledger: data/ledger-template.jsonl
# RECOMMENDED: run harden once with --limit 1 (one RL product) and eyeball Shopify admin before the full run
# (inventorySetQuantities right after tracked:true is the one call that could not be dry-run-proven)
node scripts/harden-variants.mjs --apply                     # ledger: data/ledger-variants.jsonl
node scripts/unpublish-channels.mjs --apply                  # ledger: data/ledger-channels.jsonl
node scripts/verify.mjs                                      # PASS/FAIL table: PDP has no cart/price, block present, .json roll unavailable
```
Optional safer sequencing: run push-theme against an unpublished DUPLICATE of the live theme first (Admin → Themes → Duplicate), preview a DG product with `?preview_theme_id=<id>&view=contact-us`, then run against main.

## Rollback (exact, per step, independent)
```
node scripts/unpublish-channels.mjs --rollback --apply      # republish exactly what was published
node scripts/harden-variants.mjs --rollback --apply          # restore policy + per-location quantities from ledger
node scripts/assign-template.mjs --rollback --apply          # restore old templateSuffix per product
node scripts/rollback-theme.mjs --theme 145556635699 --apply # restore edited assets from preimage, delete new ones
```

## Gate classification
Customer-facing Shopify writes (theme publish on the live main theme + 785 product/variant/channel writes) → **HARD GATE, Steve's yes required.** Reversible (recorded undo for every write), no spend, no send-to-list, no DNS, no remote push. Blast radius > 500 items → flagged.

## Residual risks / things Steve should know
- `/products/<handle>.json` and `.js` still expose the roll variant's price STRING (available:false after hardening). Shopify has no switch for this. Only removing the variant would hide it — not proposed (destroys pricing data).
- Boost cards depend on a JS hook; if Boost changes its card markup the price could reappear on grids (page stays clean because the template has no price block). verify.mjs checks a live grid.
- Canary interactions: dw-five-field-canary counts "has sellable variant / complete pricing" — these products still have priced variants so it should not regress; the zero-price tripwires watch $0 variants with qty>0 — we set qty 0, the opposite direction. dw-active-weight-canary unaffected.
- GMC: unpublishing from Google & YouTube removes the offers on the next channel sync (gmc-push-freshness-canary may show a one-time drop of ~785 offers).
- Sample orders: customers can no longer order a $4.25 sample of these lines online; the contact block invites a request instead. (If Steve wants samples back for one vendor, the `form` block could be restored in a per-vendor template variant.)
- Quick-shop modal: opening a DG/RL/CL card's quick view would have rendered the quickshop template (price + cart) — mitigated three ways (block/CSS gated on product.template_suffix, .dw-contact-us-product wrapper class, card JS removes quick-view triggers). NOT browser-verified before apply; verify.mjs cannot see Boost's client-side DOM — eyeball one collection after apply.
- Ralph Lauren "Minimum order: N yards" notice renders outside the block loop; hidden by CSS only (verify reports WARN, not FAIL).
- Reversing the vendor→contact-us decision later = the four rollback commands above; the template files can stay in the theme harmlessly.

## Files
Repo: ~/Projects/dw-contact-us-pages (git, local only) — commits: 0158453 … b8b6045 (14 commits; scripts dry-run verified, push-theme write to main REFUSED without --allow-main in a negative test)
Preimage snapshot (read-only capture before any write): /private/tmp/claude-501/-Users-macstudio3/209bfc7d-5bb8-4376-a8c3-a55dd4e3b2b3/scratchpad/preimage/preimage.json (785 products, captured 2026-09-19T17:02Z, copied into the repo data/ dir)
Contrarian review: Cody (contrarian agent) verdict FIX FIRST → all 3 HIGH findings fixed in 942c738 + 2b12c68 (ledger-before-write, ledger-only-on-truth, JSON-LD Offer carve-out); MED findings #4 min-order notice (CSS-hidden + verify WARN), #5 quick-shop (gated on product.template_suffix + wrapper-class CSS + card JS strips quick-view triggers), #6 stale preimage (2h freshness guard, enumerate is step 1) folded in; #7 eur-90463 (onlineStoreUrl null) = manual eyeball after apply · Second-model check: Codex spend-capped (429) → Kimi kimi-k3 review ($0.019): confirmed 2024-10 mutation shapes; flagged null-payload-as-success, 200-row qty batches (cap 100), template rollback '' vs null, HTTP-throw aborting the run — all fixed in 2b12c68

## APPROVE / REVISE / BLOCK
vp-dw-commerce recommendation: **APPROVE** — vendor-policy-driven, fully reversible, preimage-first, matches Steve's three CLARIFY answers.