← back to Dw Yolo Loop
scripts/google-feed/SCOPE.md
87 lines
# Clean Google Merchant Center feed — scope & plan
**Merchant Center ID:** 146735262
**Trigger:** Google's "automatic product additions" will auto-list ~30,229 crawled
storefront products on **2026-06-18** unless opted out by **2026-06-17**.
**Decision (Steve, 2026-06-15):** opt out of the blanket auto-add; build a
deliberate, price-validated feed instead.
---
## 1. The core problem the feed solves
Google's crawl-based auto-add reads the storefront product page. Every DW product
carries two variants:
| variant | example price | role |
|---|---|---|
| roll / sellable | $34.55–$205 | the real product |
| `*-sample` | **$4.25** (memo) | swatch only |
Shopify renders the page headline as "from $4.25", so a blanket crawl-add would
**advertise wallcoverings at $4.25** — and for Kravet-umbrella lines that is also a
**MAP violation** (Kravet enforces MAP = wholesale × 1.5; advertising below it risks
the dealer account). The clean feed sends Google the **roll price**, never the sample.
## 2. Architecture decision: LIVE-Shopify-driven (not the mirror)
`scripts/google-feed/calibrate-price.mjs` proved the `dw_unified` mirror `price`
column is stale/sample-contaminated:
| mirror says | live Shopify roll price |
|---|---|
| NULL (41,987 active rows) | $34.55 … |
| $4.25 (12,106 active rows) | $81–$205 … |
15/15 of each sampled group had a correct live roll price. **Scoping off the mirror
would wrongly drop ~54k correctly-priced products.** Therefore the feed price is read
**live from Shopify** (the roll variant's price). The mirror is still used for the one
thing it's good at: the Kravet **cost** for the MAP floor check (and even that is
sparse → flagged "unverified", never a false exclude).
## 3. Eligibility gate (per product)
Built on the existing fail-closed validator (`scripts/preflight/rules.json`). A
product is **eligible** iff ALL pass:
- has a product image
- has a **roll** (non-`-sample`) variant with a price **≥ floor ($10)**
- roll price is not exactly $4.25 (sample-trap)
- no **private-label upstream** name in title/vendor/tags/handle
(`command54, wallquest, chesapeake, nextwall, seabrook, brewster, desima, carlsten,
nicolette mayer`) — note: repped brand names (Kravet, Thibaut, Schumacher…) ARE
allowed; DW is an authorized dealer, so they belong in the `brand` field.
- title contains no `wallpaper` / `unknown`
- Kravet-family: roll price ≥ wholesale×1.5 when cost is known (else flagged, not dropped)
**Sample-only products** (only a `-sample` variant, no roll) are correctly **excluded** —
there is no sellable unit to advertise. This is the single largest exclusion class and
it is *by design* (see MEMORY `425-sample-price-policy`).
## 4. Output (local artifacts — nothing submitted)
`node scripts/google-feed/feed-eligibility.mjs --floor=10` (read-only) writes:
- `data/google-feed/feed-clean.tsv` — Merchant Center primary feed of eligible products
(roll price, public DW link, featured image, brand, condition, GTIN-less mpn).
- `data/google-feed/exclusions.json` — every excluded product + reason (auditable).
- `data/google-feed/report.json` — counts + reason breakdown + top eligible vendors.
## 5. Recommended delivery path (Steve-gated — NOT yet done)
Two options to get the clean set to Google:
- **A — Shopify-native (recommended).** Keep the existing *Google & YouTube* sales
channel as the single feed; do not upload a competing custom feed (avoids duplicate
items). Use the eligibility report to **unpublish the excluded set from the Google
channel** (sample-only + sub-floor + leaks). Least new infra; uses authoritative live
data; inventory/availability stay automatic. Bulk channel-publish changes are Shopify
**writes → require Steve sign-off.**
- **B — Supplemental TSV feed.** Upload `feed-clean.tsv` to Merchant Center as a feed.
Full control over exactly what lists, but must be hosted/refreshed and reconciled
against the Shopify app feed to avoid duplicates.
**Status:** opt-out (the urgent 06-17 action) is Steve's UI step. This feed work is the
deliberate follow-on. Submitting/publishing anything to Google is **gated** — these
scripts only read Shopify and write local files.