Merging six per-market duplicate products into one canonical reusable-shopping-bags — staged invisibly, switched on by merging to the GitHub-connected live theme (translations preserved).
Today Nanobag sells the same physical bags through six separate Shopify products — one per market (US, EU, Canada, UK, Singapore, International). This split hurts us everywhere: Google sees duplicates and ranks none of them well, reviews are scattered six ways, every content or price change is done six times, and the cart breaks when a shopper crosses markets.
Shopify shipped a feature in July 2026 (variant-level publishing) that lets one product show a different selection of colors/styles per market. That removes the original reason the duplicates were created. This plan merges everything into the main US product, reusable-shopping-bags, while keeping each market's exact current assortment and prices.
main branch — the theme updates in place, keeping all its translations and settings. Rolling back is reverting that merge. Prices stay exactly as they are today in every market.
Not touched at all: the Meta-catalog and TikTok ad products (ads are running on them), free-gift products, and the Daypack/Standard standalone US products.
Shubham branch. Full QA of every market on an unpublished preview theme before anything goes live.main — the live GitHub-connected theme updates in place, translations intact. Watch orders, checkout, gifts, and reviews for 48–72 hours. Rollback = revert the merge.| Shubham (Shopify admin / accounts) | Claude (scripts & theme code) |
|---|---|
| Create the Admin API token (one-off, scoped) | Variant mapping matrix — done (consolidation-variant-matrix.csv) |
| Sign off the Phase 0 gates: live feature test, price-list check, inventory pooling, SKU audit | All Admin GraphQL scripts (variant creation, market publishing, price lists), validated before handover |
| Google Search Console check; Judge.me review grouping; nav menus | Theme repoint edits (~25 templates + gallery snippet) on a branch |
| Run the scripts (or hand over the token); push & publish themes; every go/no-go decision | Per-market QA checklists, redirect CSV, monitoring queries |
| Check ads/emails for links to old products before retiring them | Post-cutover verification support |
An independent adversarial review (Opus 4.8) produced 15 findings — all folded into the phases below. The headline items:
| Severity | Risk | How the plan handles it |
|---|---|---|
| Blocker | Non-US prices may be fixed lists today, not currency conversion — trusting conversion would silently change EU/CA/SG/INT prices | Phase 0 confirms the live config; plan fix-prices every market from the matrix regardless |
| Blocker | Old + new products both sellable during transition → overselling the same physical stock | Inventory pooling is a hard precondition before cutover, not an afterthought |
| Blocker | The new Shopify feature might not filter hidden variants everywhere in the theme (custom variant picker, deep links, structured data) | Phase 0.3 live test is the go/no-go gate for the whole architecture |
| Blocker | Collections, search & recommendations are product-level — canonical could be invisible in market listings even with a working PDP | Phase 2.3 adds the canonical to every collection + discovery rules before cutover |
| Major | All non-US orders switch to SB02- SKUs — 3PL, accounting and Amazon mappings can break | Phase 0.7 SKU-continuity audit before cutover |
| Major | Free-gift rules & mini-cart metafields point at old product IDs | Repointed (not copied) in Phases 1 & 3, verified in QA |
| Major | Old product URLs going dead before redirects exist | Phase 6 archives + creates 301s in the same step |
Verified data facts (2026-07-06): Style vocabulary identical across all 7 products (daypack, micro, pack, sling, standard, xl); SG/INT differ only in option name (Type vs Style). SKU scheme = market prefix + shared barcode (SB02- US, OP01- EU, CA01- CA, SBUK02- UK, SG01- SG, bare = INT). Canonical (94 keys) is a strict subset of the 133-key union. UK prices +16–29%, non-uniform. Zero compare_at prices exist anywhere. Known data defects: micro/psychedelic has SKU added_later (no barcode) in both sources; sling/petaline CA 15.95 vs 18.95 elsewhere; blank SKUs on CA/UK porcelain pack/sling.
Assortment rule: a variant is published to a market's catalog iff that market's dup has that Style×Color today (matrix column presence). Targets: US 94 · EU 98 · CA 96 · UK 105 · SG 110 · INT 100 of 114 canonical variants post-gap-fill.
read/write_products, read/write_publications, read_markets, read/write_price_lists.product.variants) excludes it — the custom controller is the real rendering path;?variant=<id> deep-link and /cart/<variant_id>:1 permalink blocked or fall back safely;selected_or_first_available_variant never resolves to a market-hidden variant;/products/<handle>.js, /cart/add, checkout all block it.test-us-nanobag-2x-weight.reusable-shopping-bags out-ranks the dup PDPs; if not, reopen canonical choice.SB02- mappings or re-key on barcode.?variant= deep links (not just handles).micro/psychedelic; script validates every source SKU matches <prefix>-<barcode>, halts on exceptions).productSet with published: false per variant; SKU SB02-<barcode>; price per matrix.productCreateMedia + attach).custom.minicart_variant contextual values): repoint, don't copy — any value referencing a dup product/variant ID is rewritten to the canonical ID.translatableResource(s), register the union onto canonical via translationsRegister per locale.Rollback: delete the 20 variants and their created media / copied metafields.
publishableUnpublish the variants not in that market's assortment), THEN publish the product to the catalog — documented safe order, no wrong-assortment window.Rollback: unpublish canonical from the new market catalogs; remove from added collections. Dups still serve all traffic.
reusable-shopping-bags: ~25 contextual templates (index.context.*, cart.context.*, page.ebfcm-template.context.*, index.sl-531E49B0.context.*, overlay-group.context.*), snippets/product-media-gallery-new.liquid:212,373, nav menus (USER, admin). All edits stay on the Shubham branch — nothing touches main (the live theme) until cutover.new-main-product for Type option-name or dup-handle assumptions (expected none — keys on Style).nb-gift-rules qualifying product IDs + EG app config must include the canonical product ID per market.Shubham branch (or CLI-push it). A fresh theme has no theme-content translations — fine for functional QA; for language-fidelity QA run scripts/copy-theme-translations.mjs onto it.Rollback: don't merge. Delete the QA theme when done.
Shubham with latest main; re-verify the repoint diff on fresh templates; never merge unintentional settings_data.json changes; merge promptly.Shubham → main. The GitHub integration updates the live theme in place (~1 min); translations/settings/app embeds untouched (theme ID unchanged). Dups remain published with zeroed/pooled inventory — no oversell. Update nav menus now; add canonical to market collections a few hours before the merge, not days.Rollback: git revert the merge on main and push — the previous live theme code is restored. Reverse the inventory move, revert nav menus. Emergency fallback: publish the step-3 duplicate.
/products/nanobag-eu → /products/reusable-shopping-bags etc.). No unpublish-and-wait window. Claude generates the redirect CSV; user imports (admin → Navigation → URL redirects) immediately after archiving.Rollback: delete redirects, unarchive, republish.
sling/petaline CA 15.95 / UK 19.35 anomaly — intentional promo or data error?micro/psychedelic (needed for its SKU).nanobag-daypack-us / nanobag-standard-us — fold into canonical in a later pass?*-meta-catalog, TikTok-US) are running live ads and are never archived or repointed in this project. The user pushes and publishes all themes; scripts are run only after user sign-off.