[object Object]

← back to Gmc Titlefix

gmc mdc-title: fix 404 — bare-variant offerId, en~US~ name, reuse linked datasource (TK-11307)

8f147e652e773afe7abc4292eb5587f937ee8286 · 2026-09-09 08:22:17 -0700 · steve

Root cause of the live 404 'Product not found': (1) MDC offers are on the LEGACY
bare-variant-id GMC feed (offerId = bare Shopify variant id, name en~US~<offerId>),
not the compound shopify_US_<pid>_<vid>/online~ format the pusher constructed; (2)
step-a created NEW unbound supplemental datasources (the two empty dupes) instead of
reusing the existing primary-LINKED 'DW Sample Title Overrides' (10677010255).

Fix: builder now pulls the real GMC offerId set and includes ONLY MDC offers that
actually exist on GMC (offerId = bare sample-variant legacyResourceId) — the push can
never 404 on a missing product. resolveSampleTitleDS() reuses the single existing
datasource referenced by a primary's takeFromDataSources (idempotent — never creates a
dupe). Validated by a SINGLE-OFFER live insert (offer 44595192496179 'Lisbon Hard Rock',
HTTP 200; supplemental confirmed linked to primary 180695450). 13/13 tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JDeaFL4eeREBZX79tc4cnr

Files touched

Diff

commit 8f147e652e773afe7abc4292eb5587f937ee8286
Author: steve <steve@designerwallcoverings.com>
Date:   Wed Sep 9 08:22:17 2026 -0700

    gmc mdc-title: fix 404 — bare-variant offerId, en~US~ name, reuse linked datasource (TK-11307)
    
    Root cause of the live 404 'Product not found': (1) MDC offers are on the LEGACY
    bare-variant-id GMC feed (offerId = bare Shopify variant id, name en~US~<offerId>),
    not the compound shopify_US_<pid>_<vid>/online~ format the pusher constructed; (2)
    step-a created NEW unbound supplemental datasources (the two empty dupes) instead of
    reusing the existing primary-LINKED 'DW Sample Title Overrides' (10677010255).
    
    Fix: builder now pulls the real GMC offerId set and includes ONLY MDC offers that
    actually exist on GMC (offerId = bare sample-variant legacyResourceId) — the push can
    never 404 on a missing product. resolveSampleTitleDS() reuses the single existing
    datasource referenced by a primary's takeFromDataSources (idempotent — never creates a
    dupe). Validated by a SINGLE-OFFER live insert (offer 44595192496179 'Lisbon Hard Rock',
    HTTP 200; supplemental confirmed linked to primary 180695450). 13/13 tests.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01JDeaFL4eeREBZX79tc4cnr
---
 build-mdc-sample-titles.mjs | 123 ++++++++++++++++++++++++++++----------------
 push-mdc-sample-titles.mjs  |  56 ++++++++++----------
 test-mdc-sample-titles.mjs  |   9 ++++
 3 files changed, 117 insertions(+), 71 deletions(-)

diff --git a/build-mdc-sample-titles.mjs b/build-mdc-sample-titles.mjs
index f112490..5d5b4ac 100644
--- a/build-mdc-sample-titles.mjs
+++ b/build-mdc-sample-titles.mjs
@@ -5,20 +5,23 @@
  *
  * WHY: MDC products carry a single $4.25 memo-sample variant (no roll). On Google the
  * OFFER PRICE is a compliant $4.25, but the TITLE reads "<Pattern> Wallcovering | Phillipe
- * Romano" — so Google sees a $4.25-priced wallcovering (a ~$300 item at $4.25) and flags a
- * price-mismatch. Per Google's sample policy the title must BEGIN with "Sample". This tool
- * builds a supplemental-feed title-override list (offerId -> "Sample — …") that changes ONLY
- * the GOOGLE-facing title via a GMC supplemental data source — the on-site Shopify PDP title
- * is NEVER touched (shoppers still see the wallcovering name).
+ * Romano" — so Google sees a $4.25-priced wallcovering and flags a price-mismatch. Per
+ * Google's sample policy the title must BEGIN with "Sample". This builds a supplemental-feed
+ * title-override list that changes ONLY the GOOGLE title via a GMC supplemental data source
+ * — the on-site Shopify PDP title is NEVER touched.
  *
- * SCOPE — STRICTLY tag:Showroom, ACTIVE, actually published to the Google & YouTube channel.
- * Belt-and-suspenders: re-affirms hasShowroomTag per product, so a sellable Phillipe Romano
- * product (Spazzolato etc., no Showroom tag) is NEVER in the list and its title is unchanged.
+ * OFFER IDENTITY (fixed 2026-09-09 after a 404 diagnosis): MDC offers live on GMC under the
+ * LEGACY BARE-VARIANT-ID feed — offerId = the bare Shopify variant legacyResourceId (e.g.
+ * "44595192496179"), product resource name = "en~US~<offerId>" (NO "online~" prefix, NO
+ * "shopify_US_" compound). Not every ACTIVE tag:Showroom product is actually a live GMC
+ * product, so this builder pulls the REAL set of GMC offerIds and includes ONLY MDC offers
+ * that already exist on GMC — so the push can never 404 on a missing product.
  *
- * READ-ONLY against Shopify (query only). Writes one local JSON. Nothing is pushed to GMC
- * here — push-mdc-sample-titles.mjs (Steve-gated) does that.
+ * SCOPE — STRICTLY tag:Showroom, ACTIVE. Belt-and-suspenders: re-affirms hasShowroomTag per
+ * product, so a sellable Phillipe Romano product (Spazzolato etc.) is never included.
  *
- * OUTPUT: data/mdc-sample-title-overrides.json  (rows: offerId, gmcId, productId, currentTitle, proposedTitle)
+ * READ-ONLY (Shopify GET + GMC products.list GET). Writes one local JSON. Nothing pushed.
+ * OUTPUT: data/mdc-sample-title-overrides.json  (offerId, gmcName, productId, sku, currentTitle, proposedTitle)
  * USAGE:  node build-mdc-sample-titles.mjs
  */
 import fs from 'node:fs';
@@ -27,17 +30,17 @@ import { fileURLToPath } from 'node:url';
 import { createRequire } from 'node:module';
 const require = createRequire(import.meta.url);
 const { hasShowroomTag } = require(process.env.HOME + '/Projects/fix-live-board/config/showroom-vendor.cjs');
+const { token, MERCHANT } = require('./_auth');
 const __dirname = path.dirname(fileURLToPath(import.meta.url));
 
 const SHOP = 'designer-laboratory-sandbox.myshopify.com';
 const VER = '2024-10';
-const GOOGLE_PUBLICATION = 'gid://shopify/Publication/29646651457'; // Google & YouTube
 const GTITLE_MAX = 150; // Google title max length
 
 /**
  * The Google-facing sample title transform (exported for tests). Strips a trailing
- * "| Phillipe Romano" / "- Phillipe Romano" vendor suffix, wraps as a memo sample, and
- * ALWAYS begins with "Sample". Truncation is end-only so the "Sample" prefix survives.
+ * "| Phillipe Romano" vendor suffix, wraps as a memo sample, ALWAYS begins with "Sample".
+ * Truncation is end-only so the "Sample" prefix survives.
  */
 export function mdcSampleTitle(title) {
   const clean = String(title == null ? '' : title)
@@ -54,53 +57,83 @@ export function sampleVariant(variants) {
   return variants.find(v => /-sample$/i.test(v.sku || '')) || variants[0];
 }
 
-// main (skipped when imported for tests)
+/**
+ * Resolve the EXISTING, PRIMARY-LINKED "DW Sample Title Overrides" supplemental data source
+ * (never create a new one — that spawned the empty-duplicate 404 mess). Returns the data
+ * source RESOURCE NAME that is actually referenced by a primary feed's takeFromDataSources.
+ * (exported so the pusher reuses the identical resolution.)
+ */
+export async function resolveSampleTitleDS(tok) {
+  const rd = await fetch(`https://merchantapi.googleapis.com/datasources/v1/accounts/${MERCHANT}/dataSources`, { headers: { Authorization: 'Bearer ' + tok } });
+  const jd = await rd.json();
+  const all = jd.dataSources || [];
+  // candidate supplementals named "DW Sample Title Overrides"
+  const named = all.filter(d => d.supplementalProductDataSource && /DW Sample Title Overrides/i.test(d.displayName || ''));
+  // linked set = every supplemental referenced by any primary's defaultRule.takeFromDataSources
+  const linked = new Set();
+  for (const d of all) {
+    const takes = d.primaryProductDataSource?.defaultRule?.takeFromDataSources || [];
+    for (const t of takes) if (t.supplementalDataSourceName) linked.add(t.supplementalDataSourceName);
+  }
+  const chosen = named.find(d => linked.has(d.name)) || named[0];
+  if (!chosen) throw new Error('no "DW Sample Title Overrides" supplemental data source found — create ONE and link it to the primary first');
+  return { name: chosen.name, linked: linked.has(chosen.name), duplicates: named.filter(d => d.name !== chosen.name).map(d => d.name) };
+}
+
 const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
 if (isMain) {
   const TOKEN = (fs.readFileSync(process.env.HOME + '/Projects/secrets-manager/.env', 'utf8')
     .match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m) || [])[1]?.trim();
-  if (!TOKEN) { console.error('no token'); process.exit(1); }
-  const ENDPOINT = `https://${SHOP}/admin/api/${VER}/graphql.json`;
+  if (!TOKEN) { console.error('no Shopify token'); process.exit(1); }
+  const SEP = `https://${SHOP}/admin/api/${VER}/graphql.json`;
   const sleep = ms => new Promise(r => setTimeout(r, ms));
-  const gql = async (query, variables) => {
+  const sgql = async (query, variables) => {
     for (let a = 0; a < 8; a++) {
       let j;
-      try {
-        const r = await fetch(ENDPOINT, { method: 'POST',
-          headers: { 'X-Shopify-Access-Token': TOKEN, 'Content-Type': 'application/json' },
-          body: JSON.stringify({ query, variables }) });
-        j = await r.json();
-      } catch { await sleep(1500 * (a + 1)); continue; }
+      try { const r = await fetch(SEP, { method: 'POST', headers: { 'X-Shopify-Access-Token': TOKEN, 'Content-Type': 'application/json' }, body: JSON.stringify({ query, variables }) }); j = await r.json(); }
+      catch { await sleep(1500 * (a + 1)); continue; }
       if (j.errors) { if (JSON.stringify(j.errors).includes('THROTTLED')) { await sleep(2000 * (a + 1)); continue; } throw new Error(JSON.stringify(j.errors)); }
       const t = j.extensions?.cost?.throttleStatus; if (t && t.currentlyAvailable < 400) await sleep(1200);
       return j.data;
     }
-    throw new Error('retries');
+    throw new Error('shopify retries');
   };
 
-  const Q = `query($cursor:String){
-    products(first:100, after:$cursor, query:"status:active tag:'Showroom'"){
-      pageInfo{ hasNextPage endCursor }
-      nodes{ legacyResourceId title tags
-        onGoogle:publishedOnPublication(publicationId:"${GOOGLE_PUBLICATION}")
-        variants(first:10){ nodes{ legacyResourceId sku } } }
-    }
-  }`;
-
   (async () => {
-    const rows = []; let cursor = null, has = true, seen = 0, offCh = 0, notag = 0;
+    const tok = await token();
+    // 1. Real GMC offerId set (paginate products.list) — so we only override offers that EXIST.
+    process.stderr.write('pulling GMC offerId set…\n');
+    const gmcOffers = new Set();
+    let pt = null, gpages = 0;
+    do {
+      const url = `https://merchantapi.googleapis.com/products/v1/accounts/${MERCHANT}/products?pageSize=250` + (pt ? `&pageToken=${pt}` : '');
+      const r = await fetch(url, { headers: { Authorization: 'Bearer ' + tok } });
+      const j = await r.json();
+      for (const p of (j.products || [])) if (p.offerId) gmcOffers.add(String(p.offerId));
+      pt = j.nextPageToken; gpages++;
+    } while (pt && gpages < 400);
+    process.stderr.write(`  GMC offers: ${gmcOffers.size} (pages ${gpages})\n`);
+
+    // 2. Enumerate ACTIVE tag:Showroom MDC products; offerId = bare sample-variant legacyId.
+    const Q = `query($cursor:String){
+      products(first:100, after:$cursor, query:"status:active tag:'Showroom'"){
+        pageInfo{ hasNextPage endCursor }
+        nodes{ legacyResourceId title tags variants(first:10){ nodes{ legacyResourceId sku } } }
+      }
+    }`;
+    const rows = []; let cursor = null, has = true, seen = 0, notag = 0, notOnGmc = 0;
     while (has) {
-      const d = await gql(Q, { cursor });
+      const d = await sgql(Q, { cursor });
       for (const p of d.products.nodes) {
         seen++;
-        if (!hasShowroomTag(p.tags)) { notag++; continue; } // re-affirm the tag — never trust the query
-        if (!p.onGoogle) { offCh++; continue; }             // only offers actually on Google
+        if (!hasShowroomTag(p.tags)) { notag++; continue; }
         const v = sampleVariant(p.variants.nodes);
         if (!v) continue;
-        const offerId = `shopify_US_${p.legacyResourceId}_${v.legacyResourceId}`;
+        const offerId = String(v.legacyResourceId); // BARE variant id — the legacy GMC feed identity
+        if (!gmcOffers.has(offerId)) { notOnGmc++; continue; } // not a live GMC product → skip (no 404)
         rows.push({
           offerId,
-          gmcId: `online:en:US:${offerId}`,
+          gmcName: `accounts/${MERCHANT}/products/en~US~${offerId}`,
           productId: String(p.legacyResourceId),
           sku: v.sku,
           currentTitle: p.title,
@@ -109,14 +142,18 @@ if (isMain) {
       }
       has = d.products.pageInfo.hasNextPage; cursor = d.products.pageInfo.endCursor;
     }
+
+    const ds = await resolveSampleTitleDS(tok).catch(e => ({ error: e.message }));
     const DIR = path.join(__dirname, 'data'); fs.mkdirSync(DIR, { recursive: true });
     const out = path.join(DIR, 'mdc-sample-title-overrides.json');
     fs.writeFileSync(out, JSON.stringify(rows, null, 1));
     console.log('MDC (tag:Showroom) Google sample-title override list');
-    console.log(`  active tag:'Showroom' seen: ${seen} | skipped(no real tag): ${notag} | skipped(off Google): ${offCh}`);
+    console.log(`  active tag:'Showroom' seen: ${seen} | skipped(no real tag): ${notag} | skipped(not a live GMC offer): ${notOnGmc}`);
     console.log(`  overrides written: ${rows.length}`);
-    if (rows[0]) { console.log('  sample row:'); console.log('    ', rows[0].offerId); console.log('    ', rows[0].currentTitle, '->', rows[0].proposedTitle); }
+    if (rows[0]) { console.log('  sample:', rows[0].offerId, '|', rows[0].currentTitle, '->', rows[0].proposedTitle); }
+    console.log('  reuse datasource:', ds.name || ds.error, ds.linked ? '(linked to primary ✓)' : '');
+    if (ds.duplicates?.length) console.log('  EMPTY DUPLICATE title datasources to delete:', ds.duplicates.join(', '));
     console.log('  wrote:', out);
-    console.log('\n  Fire (Steve only): node push-mdc-sample-titles.mjs --apply --i-am-steve <dataSourceName>');
+    console.log('\n  Fire (Steve only): node push-mdc-sample-titles.mjs --apply --i-am-steve');
   })();
 }
diff --git a/push-mdc-sample-titles.mjs b/push-mdc-sample-titles.mjs
index 394de81..478d44b 100644
--- a/push-mdc-sample-titles.mjs
+++ b/push-mdc-sample-titles.mjs
@@ -1,20 +1,20 @@
 #!/usr/bin/env node
 /**
  * push-mdc-sample-titles.mjs — GATED WRITE. Push the MDC "Sample —" Google-title overrides
- * to a GMC supplemental data source (Merchant API v1), keyed by offerId. (TK-11307 verdict B)
+ * to the EXISTING, primary-linked "DW Sample Title Overrides" supplemental data source,
+ * keyed by offerId. (TK-11307 verdict B)
  *
  * ⚠️  STEVE-GATED, customer-facing (Google listing). DEFAULT = DRY-RUN (no writes).
- *     A real run requires BOTH:  --apply  AND  --i-am-steve  AND a <dataSourceName>.
- *     It overrides ONLY the GOOGLE title via the supplemental source — it NEVER touches
- *     Shopify, price, availability, or the on-site PDP. Fully reversible: delete the
- *     supplemental data source (removes every override at once) — see the memo.
+ *     A real run requires BOTH:  --apply  AND  --i-am-steve.
+ *     Overrides ONLY the GOOGLE title via the supplemental source — NEVER Shopify, price,
+ *     availability, or the on-site PDP. Idempotent: re-inserting the same offerId just
+ *     re-writes the same override (Google merges by offerId). Reversible: delete the
+ *     productInputs (or drop the supplemental source's overrides) — see the memo.
  *
- * Reads: data/mdc-sample-title-overrides.json (from build-mdc-sample-titles.mjs).
- * GUARD: HARD-ABORTS unless EVERY row's proposedTitle begins with "Sample" (the whole point).
- *
- * Create the data source first (dedicated, so rollback = delete just this source):
- *   node step-a-create-datasource.js     # (rename displayName to 'DW MDC Sample Title Overrides' first)
- * then: node push-mdc-sample-titles.mjs --apply --i-am-steve accounts/146735262/dataSources/<id>
+ * IDEMPOTENCY: reuses the SINGLE existing linked datasource resolved by displayName — it does
+ *     NOT create a datasource, so a re-fire can never spawn duplicates.
+ * GUARD: HARD-ABORTS unless EVERY row's proposedTitle begins with "Sample".
+ * Optional: pass an explicit `accounts/146735262/dataSources/<id>` to override the resolver.
  */
 import fs from 'node:fs';
 import path from 'node:path';
@@ -22,42 +22,42 @@ import { fileURLToPath } from 'node:url';
 import { createRequire } from 'node:module';
 const require = createRequire(import.meta.url);
 const { token, MERCHANT } = require('./_auth');
+const { resolveSampleTitleDS } = await import('./build-mdc-sample-titles.mjs');
 const __dirname = path.dirname(fileURLToPath(import.meta.url));
 
-const args = Object.fromEntries(process.argv.slice(2).map(a => { const [k, v] = a.replace(/^--/, '').split('='); return [k, v === undefined ? true : v]; }));
-const DS = process.argv.slice(2).find(a => a.startsWith('accounts/'));
+const argv = process.argv.slice(2);
+const args = Object.fromEntries(argv.map(a => { const [k, v] = a.replace(/^--/, '').split('='); return [k, v === undefined ? true : v]; }));
+const DS_ARG = argv.find(a => a.startsWith('accounts/'));
 const APPLY = args.apply === true && args['i-am-steve'] === true;
 
 const LIST_PATH = path.join(__dirname, 'data', 'mdc-sample-title-overrides.json');
 const list = JSON.parse(fs.readFileSync(LIST_PATH, 'utf8'));
 
-// HARD GUARD — every proposed Google title MUST begin with "Sample".
 const bad = list.filter(r => !/^Sample\b/.test(String(r.proposedTitle || '')));
 if (bad.length) {
   console.error(`⛔ ABORT: ${bad.length}/${list.length} proposed titles do NOT begin with "Sample". Rebuild the list.`);
-  console.error(`   e.g. ${bad.slice(0, 3).map(r => r.offerId + ': ' + r.proposedTitle).join(' | ')}`);
   process.exit(1);
 }
 
-console.log(`push-mdc-sample-titles — mode: ${APPLY ? '⚠️  LIVE APPLY' : 'DRY-RUN (no writes)'}`);
-console.log(`overrides: ${list.length} (all begin with "Sample" ✓)`);
-if (!APPLY || !DS) {
-  console.log('\nDRY-RUN or no data source. To execute (Steve only):');
-  console.log('  1) node step-a-create-datasource.js   # create the supplemental source, copy its name');
-  console.log('  2) node push-mdc-sample-titles.mjs --apply --i-am-steve accounts/146735262/dataSources/<id>');
-  if (list[0]) console.log(`\n  first override: ${list[0].offerId}\n    ${list[0].currentTitle}\n    -> ${list[0].proposedTitle}`);
-  process.exit(0);
-}
-
 const sleep = ms => new Promise(r => setTimeout(r, ms));
 (async () => {
-  let tok = await token(), tokAt = Date.now(), ok = 0, fail = 0;
+  const tok = await token();
+  let DS = DS_ARG;
+  if (!DS) { const r = await resolveSampleTitleDS(tok); DS = r.name; console.log(`resolved datasource: ${DS} ${r.linked ? '(linked to primary ✓)' : '(NOT LINKED — override will not merge!)'}`); }
+  console.log(`push-mdc-sample-titles — mode: ${APPLY ? '⚠️  LIVE APPLY' : 'DRY-RUN (no writes)'}`);
+  console.log(`overrides: ${list.length} (all begin with "Sample" ✓) → ${DS}`);
+  if (!APPLY) {
+    console.log('\nDRY-RUN. To execute (Steve only): node push-mdc-sample-titles.mjs --apply --i-am-steve');
+    if (list[0]) console.log(`  first: ${list[0].offerId}\n    ${list[0].currentTitle}\n    -> ${list[0].proposedTitle}`);
+    process.exit(0);
+  }
+  let tokAt = Date.now(), tk = tok, ok = 0, fail = 0;
   for (let i = 0; i < list.length; i++) {
-    if (Date.now() - tokAt > 50 * 60 * 1000) { tok = await token(); tokAt = Date.now(); }
+    if (Date.now() - tokAt > 50 * 60 * 1000) { tk = await token(); tokAt = Date.now(); }
     const row = list[i];
     const url = `https://merchantapi.googleapis.com/products/v1/accounts/${MERCHANT}/productInputs:insert?dataSource=${encodeURIComponent(DS)}`;
     const body = { offerId: row.offerId, contentLanguage: 'en', feedLabel: 'US', productAttributes: { title: row.proposedTitle } };
-    const r = await fetch(url, { method: 'POST', headers: { Authorization: 'Bearer ' + tok, 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
+    const r = await fetch(url, { method: 'POST', headers: { Authorization: 'Bearer ' + tk, 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
     if (r.ok) ok++; else { fail++; if (fail <= 10) console.error('FAIL', row.offerId, r.status, (await r.text()).slice(0, 120)); }
     if (i % 500 === 0) console.log(`  ${i}/${list.length} | ok ${ok} fail ${fail}`);
     if (r.status === 429) await sleep(3000);
diff --git a/test-mdc-sample-titles.mjs b/test-mdc-sample-titles.mjs
index 21a1520..6581449 100644
--- a/test-mdc-sample-titles.mjs
+++ b/test-mdc-sample-titles.mjs
@@ -16,7 +16,16 @@ const MDC  = { title: 'London All About Big Geo Platinum White Wallcovering | Ph
 const SELL = { title: 'Spazzolato Bronze Wallcovering | Phillipe Romano', tags: ['Type II', 'Vinyl'], variants: [{ legacyResourceId: '1', sku: 'DWPX-500001-Yard' }, { legacyResourceId: '2', sku: 'DWPX-500001-Sample' }] };
 const PJ   = { title: 'Untethered Wallcovering by Phillip Jeffries', tags: ['Grasscloth', 'Showroom'], variants: [{ legacyResourceId: '9', sku: 'PJ-Sample' }] };
 
+// Offer identity the builder constructs: offerId = BARE sample-variant legacyResourceId,
+// GMC product name = "accounts/<merchant>/products/en~US~<offerId>" (legacy bare-id feed).
+const MERCHANT = '146735262';
+const mdcVar = sampleVariant(MDC.variants);
+const mdcOfferId = String(mdcVar.legacyResourceId);
+const mdcGmcName = `accounts/${MERCHANT}/products/en~US~${mdcOfferId}`;
+
 const cases = [
+  ['offerId is the BARE variant id (not compound shopify_US_)', mdcOfferId, '44594816745523'],
+  ['GMC name uses en~US~ (no online~ prefix, no shopify_US_ compound)', mdcGmcName, 'accounts/146735262/products/en~US~44594816745523'],
   ['MDC Google title BEGINS with "Sample"', mdcSampleTitle(MDC.title).startsWith('Sample'), true],
   ['MDC Google title drops the "| Phillipe Romano" suffix', /Phillipe Romano/.test(mdcSampleTitle(MDC.title)), false],
   ['MDC Google title is a memo swatch', /Memo Swatch$/.test(mdcSampleTitle(MDC.title)), true],

← 37f1e17 gmc: MDC Google sample-title override (DTD verdict B, TK-113  ·  back to Gmc Titlefix  ·  auto-data-snapshot: 2026-09-09T08:26:36 (1 data files) — dat 6b3b6dd →