← back to Tk10895 GroupB
TK-10895: mill reply resolves the slash-pair by plane (fabric/wallpaper)
d51d455110970a8046d7b5df6553e3f6ab833c7d · 2026-09-10 16:43:55 -0700 · Steve Abrams
Erica Nyarko (Quadrille) 2026-09-10: 'First price is the fabric, the second is
the wallpaper $Fabric/$WP.' The mill sheet's own Fabric/Wallpaper column is the
selector, so the token is chosen per-plane and not per-pattern.
- live-by-pid.mjs read-only audit of all 31 adjudicated SKUs, keyed by product id
- reprice-erica.mjs reversible fix for the 11 live overpriced rows (dry-run default)
- erica-live-audit.json verified live state
Overturns today's fill-down ruling: the 6 Wildflowers II WALLPAPERS took the
fabric token ($172) and are live $54.30/roll over. The corroborating siblings
that appeared to confirm $172 are both F/W=Fabric — right evidence, wrong plane.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016hho8N5fqKPc14Ec64LW9A
Files touched
A build-wf6.mjsA erica/erica-live-audit.jsonA erica/live-by-pid.mjsA erica/pids.txtA erica/reprice-erica.mjsA reprice-volpi.mjs
Diff
commit d51d455110970a8046d7b5df6553e3f6ab833c7d
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Sep 10 16:43:55 2026 -0700
TK-10895: mill reply resolves the slash-pair by plane (fabric/wallpaper)
Erica Nyarko (Quadrille) 2026-09-10: 'First price is the fabric, the second is
the wallpaper $Fabric/$WP.' The mill sheet's own Fabric/Wallpaper column is the
selector, so the token is chosen per-plane and not per-pattern.
- live-by-pid.mjs read-only audit of all 31 adjudicated SKUs, keyed by product id
- reprice-erica.mjs reversible fix for the 11 live overpriced rows (dry-run default)
- erica-live-audit.json verified live state
Overturns today's fill-down ruling: the 6 Wildflowers II WALLPAPERS took the
fabric token ($172) and are live $54.30/roll over. The corroborating siblings
that appeared to confirm $172 are both F/W=Fabric — right evidence, wrong plane.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016hho8N5fqKPc14Ec64LW9A
---
build-wf6.mjs | 108 ++++++
erica/erica-live-audit.json | 833 ++++++++++++++++++++++++++++++++++++++++++++
erica/live-by-pid.mjs | 46 +++
erica/pids.txt | 31 ++
erica/reprice-erica.mjs | 78 +++++
reprice-volpi.mjs | 100 ++++++
6 files changed, 1196 insertions(+)
diff --git a/build-wf6.mjs b/build-wf6.mjs
new file mode 100755
index 0000000..36b7705
--- /dev/null
+++ b/build-wf6.mjs
@@ -0,0 +1,108 @@
+#!/usr/bin/env node
+// TK-10895 — build the side files that release the 6 Wildflowers II wallpapers.
+//
+// It does NOT mutate the canonical evidence CSV. The executor supports --evidence and
+// --worklist overrides, so this writes SIDE COPIES and leaves the canonical file untouched:
+// wf6-evidence.csv — the full evidence CSV with ONLY those 6 rows flipped PULL->GO and
+// cost_cell_clean NO->YES, with the resolution proof recorded in pull_reason
+// wf6-worklist.json — the 6 rows in the worklist shape the executor expects
+//
+// Why those 6 are releasable: their rows ALREADY pass G1 (5yd/PER_ROLL), G3 (cost_row_type_clean)
+// and width_match. The ONLY blocker was G2 cost_cell_clean=NO. That is resolved — see NOTE below.
+import fs from 'node:fs';
+
+const EV_DIR = process.env.HOME + '/.claude/yolo-queue/evidence/TK-10895';
+const SRC = EV_DIR + '/TrancheB-per-sku-evidence-20260910.csv';
+const OUT_EV = process.env.HOME + '/Projects/tk10895-groupB/wf6-evidence.csv';
+const OUT_WL = process.env.HOME + '/Projects/tk10895-groupB/wf6-worklist.json';
+
+const WF = new Set(['785121', '785221', '785321', '785421', '785521', '785621']);
+const WP_COST = 172; // the real cost — first token, constant across all 8 rows
+const PRICE = (WP_COST / 0.65 / 0.85).toFixed(2); // 311.31
+
+// SCOPE (contrarian correction, 2026-09-10): $172 is the cost of THESE 8 mfr_skus
+// (785121/785221/785321/785421/785521/785621 wallpaper + 982022/982122 fabric), NOT of the
+// pattern "Wildflowers II". Rows 6380-15WP / 6380-03WP / 6380-11WP are also Wildflowers II and
+// carry $178/$198 — pricing them off $172 would underprice by $10.69/unit. Never restate this
+// ruling at pattern level.
+const NOTE = 'G2 RESOLVED 2026-09-10 without the vendor: the second token is an Excel FILL-DOWN '
+ + 'COUNTER, not a price. The 8 Wildflowers II rows occupy a contiguous sheet block (3110-3117); '
+ + 'their second token runs 142,143,144,145,146,147,148,149 (strictly +1, no gaps) while the first '
+ + 'token stays constant at $172; and the sequence CROSSES the product-type boundary, running '
+ + '142-147 on six 27in WALLPAPER rows and continuing into 148-149 on two 48in FABRIC rows. A cost '
+ + 'cannot increment $1 per colorway and carry on across a wallpaper->fabric boundary. No other '
+ + 'column carries those values, so it is not a copy of a visible field. CORROBORATION (independent, '
+ + 'predates this ruling by 6 days): the two FABRIC SKUs in this same 8-row block, 982022 and 982122, '
+ + 'have been ACTIVE and selling at exactly $311.31 since 2026-09-04 — a pipeline already priced them '
+ + 'off $172, not off $148/$149. RARITY: across ~40 slash-Cost rows in the whole 4,592-row sheet this '
+ + '8-row block is the ONLY incrementing case (Silhouette Reverse, Volpi and Ziggurat all hold their '
+ + 'second token CONSTANT) — a systemic price field would recur; a one-time fill-handle leak looks '
+ + 'exactly like this. SCOPE: applies to these 8 mfr_skus only, NOT to pattern "Wildflowers II" '
+ + '(6380-15WP/03WP/11WP are $178) and NOT to Silhouette Reverse or Volpi, whose constant second '
+ + 'tokens are more consistent with a real List/Net pair and remain UNRESOLVED. Cost basis = $172.';
+
+// --- RFC4180-ish parse/serialise (matches the executor's own parser) ---
+function parseCsv(txt) {
+ const rows = []; let row = [], f = '', q = false;
+ for (let i = 0; i < txt.length; i++) {
+ const c = txt[i];
+ if (q) { if (c === '"' && txt[i + 1] === '"') { f += '"'; i++; } else if (c === '"') q = false; else f += c; }
+ else if (c === '"') q = true;
+ else if (c === ',') { row.push(f); f = ''; }
+ else if (c === '\n') { row.push(f); rows.push(row); row = []; f = ''; }
+ else if (c !== '\r') f += c;
+ }
+ if (f || row.length) { row.push(f); rows.push(row); }
+ return rows;
+}
+const esc = v => /[",\n]/.test(v) ? '"' + String(v).replace(/"/g, '""') + '"' : String(v);
+
+const rows = parseCsv(fs.readFileSync(SRC, 'utf8'));
+const hdr = rows.shift();
+const ix = Object.fromEntries(hdr.map((h, i) => [h, i]));
+for (const c of ['decision', 'cost_cell_clean', 'cost_row_type_clean', 'mill_yards_per_roll', 'mill_unit', 'pull_reason', 'mfr_sku', 'dw_sku', 'shopify_id', 'our_width_in']) {
+ if (!(c in ix)) { console.error('evidence CSV missing column: ' + c); process.exit(1); }
+}
+
+let flipped = 0; const wl = [];
+for (const r of rows) {
+ if (r.length !== hdr.length) continue;
+ const mfr = r[ix.mfr_sku];
+ if (!WF.has(mfr)) continue;
+
+ // FAIL CLOSED: only touch rows whose ONLY defect was G2. If anything else is off, refuse.
+ if (r[ix.mill_yards_per_roll] !== '5' || r[ix.mill_unit] !== 'PER_ROLL') {
+ console.error(`REFUSING ${mfr}: G1 not satisfied (${r[ix.mill_yards_per_roll]}/${r[ix.mill_unit]})`); process.exit(1);
+ }
+ if (r[ix.cost_row_type_clean] !== 'YES') {
+ console.error(`REFUSING ${mfr}: G3 cost_row_type_clean=${r[ix.cost_row_type_clean]}`); process.exit(1);
+ }
+ r[ix.decision] = 'GO';
+ r[ix.cost_cell_clean] = 'YES';
+ r[ix.pull_reason] = NOTE;
+ flipped++;
+
+ const width = (r[ix.our_width_in] || '').replace(/\.00$/, '');
+ wl.push({
+ mfr,
+ dw_sku: r[ix.dw_sku],
+ pid: String(r[ix.shopify_id]).split('/').pop(),
+ price: PRICE,
+ width,
+ // Label convention: the LIVE catalog is split — the 226 batch shipped
+ // "Sold Per Roll - <W>In Wide" (double space) while the 55 batch shipped
+ // "Sold Per Single Roll - 5 Yards - <W>In Wide". Use the LATTER: it is the
+ // deliberate later choice, it is what the executor's guard enforces, and it
+ // states the 5-yard put-up that is the whole point of this tranche.
+ label: `Sold Per Single Roll - 5 Yards - ${width}In Wide`,
+ });
+}
+
+if (flipped !== WF.size) { console.error(`REFUSING: flipped ${flipped}, expected ${WF.size}`); process.exit(1); }
+
+fs.writeFileSync(OUT_EV, [hdr, ...rows].map(r => r.map(esc).join(',')).join('\n') + '\n');
+fs.writeFileSync(OUT_WL, JSON.stringify(wl, null, 1));
+console.log(`flipped ${flipped} rows PULL->GO (G2 resolved), price $${PRICE} each`);
+for (const w of wl) console.log(` ${w.mfr} ${w.dw_sku} pid=${w.pid} ${w.label} $${w.price}`);
+console.log(`\nevidence -> ${OUT_EV}\nworklist -> ${OUT_WL}`);
+console.log('canonical evidence CSV NOT modified.');
diff --git a/erica/erica-live-audit.json b/erica/erica-live-audit.json
new file mode 100644
index 0000000..962c74d
--- /dev/null
+++ b/erica/erica-live-audit.json
@@ -0,0 +1,833 @@
+[
+ {
+ "mfr": "718121",
+ "pid": "6604493258803",
+ "title": "Club Cane Cream Forest Screen Printed - Mauve Wallcovering | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Wallcovering",
+ "db_cost": 216,
+ "db_our": 390.95,
+ "mill_cost": 174,
+ "expected": 314.93,
+ "live": 390.95,
+ "verdict": "❌ OVERPRICED +$76.02",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Single Roll - 5 Yards - 27In Wide",
+ "price": "390.95",
+ "sku": "DWCW-718146"
+ },
+ {
+ "pos": 2,
+ "title": "Sold Per Unit",
+ "price": "4.25",
+ "sku": "DWCW-718146-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "718221",
+ "pid": "6604493291571",
+ "title": "Club Cane Cream Taupe Turquoise Screen Printed | China Seas",
+ "status": "DRAFT",
+ "ptype": "Wallcovering",
+ "db_cost": 216,
+ "db_our": 390.95,
+ "mill_cost": 174,
+ "expected": 314.93,
+ "live": null,
+ "verdict": "SAMPLE-ONLY (not sellable)",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Unit",
+ "price": "4.25",
+ "sku": "DWCW-718246-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "718321",
+ "pid": "6604493324339",
+ "title": "Club Cane Cream Taupe Brown Screen Printed - Dark Brown Wallcovering | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Wallcovering",
+ "db_cost": 216,
+ "db_our": 390.95,
+ "mill_cost": 174,
+ "expected": 314.93,
+ "live": 390.95,
+ "verdict": "❌ OVERPRICED +$76.02",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Single Roll - 5 Yards - 27In Wide",
+ "price": "390.95",
+ "sku": "DWCW-718346"
+ },
+ {
+ "pos": 2,
+ "title": "Sold Per Unit",
+ "price": "4.25",
+ "sku": "DWCW-718346-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "718421",
+ "pid": "6604493357107",
+ "title": "Club Cane Cream Taupe Eggplant Screen Printed - Mauve Wallcovering | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Wallcovering",
+ "db_cost": 216,
+ "db_our": 390.95,
+ "mill_cost": 174,
+ "expected": 314.93,
+ "live": 390.95,
+ "verdict": "❌ OVERPRICED +$76.02",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Single Roll - 5 Yards - 27In Wide",
+ "price": "390.95",
+ "sku": "DWCW-718446"
+ },
+ {
+ "pos": 2,
+ "title": "Sold Per Unit",
+ "price": "4.25",
+ "sku": "DWCW-718446-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "718521",
+ "pid": "6604493389875",
+ "title": "Club Cane Cream Taupe Orange Screen Printed - Rust Wallcovering | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Wallcovering",
+ "db_cost": 216,
+ "db_our": 390.95,
+ "mill_cost": 174,
+ "expected": 314.93,
+ "live": 390.95,
+ "verdict": "❌ OVERPRICED +$76.02",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Single Roll - 5 Yards - 27In Wide",
+ "price": "390.95",
+ "sku": "DWCW-718546"
+ },
+ {
+ "pos": 2,
+ "title": "Sold Per Unit",
+ "price": "4.25",
+ "sku": "DWCW-718546-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "718621",
+ "pid": "6604493422643",
+ "title": "Club Cane Cream Taupe Peach Screen Printed - Mauve Wallcovering | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Wallcovering",
+ "db_cost": 216,
+ "db_our": 390.95,
+ "mill_cost": 174,
+ "expected": 314.93,
+ "live": 390.95,
+ "verdict": "❌ OVERPRICED +$76.02",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Single Roll - 5 Yards - 27In Wide",
+ "price": "390.95",
+ "sku": "DWCW-718646"
+ },
+ {
+ "pos": 2,
+ "title": "Sold Per Unit",
+ "price": "4.25",
+ "sku": "DWCW-718646-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "784821",
+ "pid": "6604519112755",
+ "title": "Volpi Gray on Off White Screen Printed - Off-White Wallcovering | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Wallcovering",
+ "db_cost": 178,
+ "db_our": 322.17,
+ "mill_cost": 198,
+ "expected": 358.37,
+ "live": 358.37,
+ "verdict": "✅ MATCHES mill",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Single Roll - 5 Yards - 27In Wide",
+ "price": "358.37",
+ "sku": "DWCW-784846"
+ },
+ {
+ "pos": 2,
+ "title": "Sold Per Unit",
+ "price": "4.25",
+ "sku": "DWCW-784846-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "784921",
+ "pid": "6604519178291",
+ "title": "Volpi Langdon Yellow on Almost White Screen Printed | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Wallcovering",
+ "db_cost": 178,
+ "db_our": 322.17,
+ "mill_cost": 198,
+ "expected": 358.37,
+ "live": 358.37,
+ "verdict": "✅ MATCHES mill",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Single Roll - 5 Yards - 27In Wide",
+ "price": "358.37",
+ "sku": "DWCW-784946"
+ },
+ {
+ "pos": 2,
+ "title": "Sold Per Unit",
+ "price": "4.25",
+ "sku": "DWCW-784946-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "785021",
+ "pid": "6604519211059",
+ "title": "Volpi New Navy on Off White Screen Printed - Off-White Wallcovering | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Wallcovering",
+ "db_cost": 178,
+ "db_our": 322.17,
+ "mill_cost": 198,
+ "expected": 358.37,
+ "live": 358.37,
+ "verdict": "✅ MATCHES mill",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Single Roll - 5 Yards - 27In Wide",
+ "price": "358.37",
+ "sku": "DWCW-785046"
+ },
+ {
+ "pos": 2,
+ "title": "Sold Per Unit",
+ "price": "4.25",
+ "sku": "DWCW-785046-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "785121",
+ "pid": "6604519243827",
+ "title": "Wildflowers Ii Black on White Screen Printed - White Wallcovering | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Wallcovering",
+ "db_cost": 172,
+ "db_our": 311.31,
+ "mill_cost": 142,
+ "expected": 257.01,
+ "live": 311.31,
+ "verdict": "❌ OVERPRICED +$54.30",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Single Roll - 5 Yards - 27In Wide",
+ "price": "311.31",
+ "sku": "DWCW-785146"
+ },
+ {
+ "pos": 2,
+ "title": "Sold Per Unit",
+ "price": "4.25",
+ "sku": "DWCW-785146-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "785221",
+ "pid": "6604519276595",
+ "title": "Wildflowers Ii Brown on White Screen Printed - White Wallcovering | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Wallcovering",
+ "db_cost": 172,
+ "db_our": 311.31,
+ "mill_cost": 142,
+ "expected": 257.01,
+ "live": 311.31,
+ "verdict": "❌ OVERPRICED +$54.30",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Single Roll - 5 Yards - 27In Wide",
+ "price": "311.31",
+ "sku": "DWCW-785246"
+ },
+ {
+ "pos": 2,
+ "title": "Sold Per Unit",
+ "price": "4.25",
+ "sku": "DWCW-785246-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "785321",
+ "pid": "6604519309363",
+ "title": "Wildflowers II Camel II on White Screen Printed | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Wallcovering",
+ "db_cost": 172,
+ "db_our": 311.31,
+ "mill_cost": 142,
+ "expected": 257.01,
+ "live": 311.31,
+ "verdict": "❌ OVERPRICED +$54.30",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Single Roll - 5 Yards - 27In Wide",
+ "price": "311.31",
+ "sku": "DWCW-785346"
+ },
+ {
+ "pos": 2,
+ "title": "Sold Per Unit",
+ "price": "4.25",
+ "sku": "DWCW-785346-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "785421",
+ "pid": "6604519342131",
+ "title": "Wildflowers Ii Gray on White Screen Printed - White Wallcovering | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Wallcovering",
+ "db_cost": 172,
+ "db_our": 311.31,
+ "mill_cost": 142,
+ "expected": 257.01,
+ "live": 311.31,
+ "verdict": "❌ OVERPRICED +$54.30",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Single Roll - 5 Yards - 27In Wide",
+ "price": "311.31",
+ "sku": "DWCW-785446"
+ },
+ {
+ "pos": 2,
+ "title": "Sold Per Unit",
+ "price": "4.25",
+ "sku": "DWCW-785446-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "785521",
+ "pid": "6604519374899",
+ "title": "Wildflowers Ii Purple on White Screen Printed - White Wallcovering | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Wallcovering",
+ "db_cost": 172,
+ "db_our": 311.31,
+ "mill_cost": 142,
+ "expected": 257.01,
+ "live": 311.31,
+ "verdict": "❌ OVERPRICED +$54.30",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Single Roll - 5 Yards - 27In Wide",
+ "price": "311.31",
+ "sku": "DWCW-785546"
+ },
+ {
+ "pos": 2,
+ "title": "Sold Per Unit",
+ "price": "4.25",
+ "sku": "DWCW-785546-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "785621",
+ "pid": "6604519407667",
+ "title": "Wildflowers II Royal Blue on White Screen Printed | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Wallcovering",
+ "db_cost": 172,
+ "db_our": 311.31,
+ "mill_cost": 142,
+ "expected": 257.01,
+ "live": 311.31,
+ "verdict": "❌ OVERPRICED +$54.30",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Single Roll - 5 Yards - 27In Wide",
+ "price": "311.31",
+ "sku": "DWCW-785646"
+ },
+ {
+ "pos": 2,
+ "title": "Sold Per Unit",
+ "price": "4.25",
+ "sku": "DWCW-785646-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "975722",
+ "pid": "6604476121139",
+ "title": "Silhouette Reverse Camel on Tint Screen Printed Fabric | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Fabric",
+ "db_cost": 172,
+ "db_our": 311.31,
+ "mill_cost": 172,
+ "expected": 311.31,
+ "live": 311.31,
+ "verdict": "✅ MATCHES mill",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Yard",
+ "price": "311.31",
+ "sku": "DWCS-975748"
+ },
+ {
+ "pos": 2,
+ "title": "Sample",
+ "price": "4.25",
+ "sku": "DWCS-975748-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "975822",
+ "pid": "6604476153907",
+ "title": "Silhouette Reverse Coral on Tint Screen Printed Fabric | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Fabric",
+ "db_cost": 172,
+ "db_our": 311.31,
+ "mill_cost": 172,
+ "expected": 311.31,
+ "live": 311.31,
+ "verdict": "✅ MATCHES mill",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Yard",
+ "price": "311.31",
+ "sku": "DWCS-975848"
+ },
+ {
+ "pos": 2,
+ "title": "Sample",
+ "price": "4.25",
+ "sku": "DWCS-975848-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "975922",
+ "pid": "6604476186675",
+ "title": "Silhouette Reverse French Green on Tint Screen Printed Fabric | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Fabric",
+ "db_cost": 172,
+ "db_our": 311.31,
+ "mill_cost": 172,
+ "expected": 311.31,
+ "live": 311.31,
+ "verdict": "✅ MATCHES mill",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Yard",
+ "price": "311.31",
+ "sku": "DWCS-975948"
+ },
+ {
+ "pos": 2,
+ "title": "Sample",
+ "price": "4.25",
+ "sku": "DWCS-975948-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "976022",
+ "pid": "6604476219443",
+ "title": "Silhouette Reverse Gray on Tint Screen Printed Fabric | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Fabric",
+ "db_cost": 172,
+ "db_our": 311.31,
+ "mill_cost": 172,
+ "expected": 311.31,
+ "live": 311.31,
+ "verdict": "✅ MATCHES mill",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Yard",
+ "price": "311.31",
+ "sku": "DWCS-976048"
+ },
+ {
+ "pos": 2,
+ "title": "Sample",
+ "price": "4.25",
+ "sku": "DWCS-976048-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "976222",
+ "pid": "6604476317747",
+ "title": "Silhouette Reverse Lavender on Tan Screen Printed Fabric | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Fabric",
+ "db_cost": 172,
+ "db_our": 311.31,
+ "mill_cost": 172,
+ "expected": 311.31,
+ "live": 311.31,
+ "verdict": "✅ MATCHES mill",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Yard",
+ "price": "311.31",
+ "sku": "DWCS-976248"
+ },
+ {
+ "pos": 2,
+ "title": "Sample",
+ "price": "4.25",
+ "sku": "DWCS-976248-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "976322",
+ "pid": "6604476350515",
+ "title": "Silhouette Reverse Lavender on Tint Screen Printed Fabric | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Fabric",
+ "db_cost": 172,
+ "db_our": 311.31,
+ "mill_cost": 172,
+ "expected": 311.31,
+ "live": 311.31,
+ "verdict": "✅ MATCHES mill",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Yard",
+ "price": "311.31",
+ "sku": "DWCS-976348"
+ },
+ {
+ "pos": 2,
+ "title": "Sample",
+ "price": "4.25",
+ "sku": "DWCS-976348-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "976422",
+ "pid": "6604476383283",
+ "title": "Silhouette Reverse New Blue on Tan Screen Printed Fabric | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Fabric",
+ "db_cost": 172,
+ "db_our": 311.31,
+ "mill_cost": 172,
+ "expected": 311.31,
+ "live": 311.31,
+ "verdict": "✅ MATCHES mill",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Yard",
+ "price": "311.31",
+ "sku": "DWCS-976448"
+ },
+ {
+ "pos": 2,
+ "title": "Sample",
+ "price": "4.25",
+ "sku": "DWCS-976448-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "976522",
+ "pid": "6604476416051",
+ "title": "Silhouette Reverse New Jungle on Tan Screen Printed Fabric | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Fabric",
+ "db_cost": 172,
+ "db_our": 311.31,
+ "mill_cost": 172,
+ "expected": 311.31,
+ "live": 311.31,
+ "verdict": "✅ MATCHES mill",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Yard",
+ "price": "311.31",
+ "sku": "DWCS-976548"
+ },
+ {
+ "pos": 2,
+ "title": "Sample",
+ "price": "4.25",
+ "sku": "DWCS-976548-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "976622",
+ "pid": "6604476448819",
+ "title": "Silhouette Reverse New Navy on Tan Screen Printed Fabric | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Fabric",
+ "db_cost": 172,
+ "db_our": 311.31,
+ "mill_cost": 172,
+ "expected": 311.31,
+ "live": 311.31,
+ "verdict": "✅ MATCHES mill",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Yard",
+ "price": "311.31",
+ "sku": "DWCS-976648"
+ },
+ {
+ "pos": 2,
+ "title": "Sample",
+ "price": "4.25",
+ "sku": "DWCS-976648-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "976722",
+ "pid": "6604476481587",
+ "title": "Silhouette Reverse New Navy on Tint Screen Printed Fabric | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Fabric",
+ "db_cost": 172,
+ "db_our": 311.31,
+ "mill_cost": 172,
+ "expected": 311.31,
+ "live": 311.31,
+ "verdict": "✅ MATCHES mill",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Yard",
+ "price": "311.31",
+ "sku": "DWCS-976748"
+ },
+ {
+ "pos": 2,
+ "title": "Sample",
+ "price": "4.25",
+ "sku": "DWCS-976748-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "976922",
+ "pid": "6604476547123",
+ "title": "Silhouette Reverse Pink on Tan Screen Printed - Off-White Fabric | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Fabric",
+ "db_cost": 172,
+ "db_our": 311.31,
+ "mill_cost": 172,
+ "expected": 311.31,
+ "live": 311.31,
+ "verdict": "✅ MATCHES mill",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Yard",
+ "price": "311.31",
+ "sku": "DWCS-976948"
+ },
+ {
+ "pos": 2,
+ "title": "Sample",
+ "price": "4.25",
+ "sku": "DWCS-976948-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "977122",
+ "pid": "6604476612659",
+ "title": "Silhouette Reverse Taxicab on Tint Screen Printed Fabric | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Fabric",
+ "db_cost": 172,
+ "db_our": 311.31,
+ "mill_cost": 172,
+ "expected": 311.31,
+ "live": 311.31,
+ "verdict": "✅ MATCHES mill",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Yard",
+ "price": "311.31",
+ "sku": "DWCS-977148"
+ },
+ {
+ "pos": 2,
+ "title": "Sample",
+ "price": "4.25",
+ "sku": "DWCS-977148-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "977222",
+ "pid": "6604476678195",
+ "title": "Silhouette Reverse Turquoise on Tan Screen Printed Fabric | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Fabric",
+ "db_cost": 172,
+ "db_our": 311.31,
+ "mill_cost": 172,
+ "expected": 311.31,
+ "live": 311.31,
+ "verdict": "✅ MATCHES mill",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Yard",
+ "price": "311.31",
+ "sku": "DWCS-977248"
+ },
+ {
+ "pos": 2,
+ "title": "Sample",
+ "price": "4.25",
+ "sku": "DWCS-977248-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "977322",
+ "pid": "6604476710963",
+ "title": "Silhouette Reverse Windsor Blue on Tint Screen Printed Fabric | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Fabric",
+ "db_cost": 172,
+ "db_our": 311.31,
+ "mill_cost": 172,
+ "expected": 311.31,
+ "live": 311.31,
+ "verdict": "✅ MATCHES mill",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Yard",
+ "price": "311.31",
+ "sku": "DWCS-977348"
+ },
+ {
+ "pos": 2,
+ "title": "Sample",
+ "price": "4.25",
+ "sku": "DWCS-977348-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "982022",
+ "pid": "6604478677043",
+ "title": "Wildflowers Ii Black on White Screen Printed - White Fabric | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Fabric",
+ "db_cost": 172,
+ "db_our": 311.31,
+ "mill_cost": 172,
+ "expected": 311.31,
+ "live": 311.31,
+ "verdict": "✅ MATCHES mill",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Yard - 48In Wide",
+ "price": "311.31",
+ "sku": "DWCS-982048"
+ },
+ {
+ "pos": 2,
+ "title": "Sample",
+ "price": "4.25",
+ "sku": "DWCS-982048-Sample"
+ }
+ ]
+ },
+ {
+ "mfr": "982122",
+ "pid": "6604478709811",
+ "title": "Wildflowers Ii Brown on White Screen Printed - White Fabric | China Seas",
+ "status": "ACTIVE",
+ "ptype": "Fabric",
+ "db_cost": 172,
+ "db_our": 311.31,
+ "mill_cost": 172,
+ "expected": 311.31,
+ "live": 311.31,
+ "verdict": "✅ MATCHES mill",
+ "variants": [
+ {
+ "pos": 1,
+ "title": "Sold Per Yard - 48In Wide",
+ "price": "311.31",
+ "sku": "DWCS-982148"
+ },
+ {
+ "pos": 2,
+ "title": "Sample",
+ "price": "4.25",
+ "sku": "DWCS-982148-Sample"
+ }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/erica/live-by-pid.mjs b/erica/live-by-pid.mjs
new file mode 100644
index 0000000..97e46bc
--- /dev/null
+++ b/erica/live-by-pid.mjs
@@ -0,0 +1,46 @@
+import fs from 'node:fs';
+const TOK = fs.readFileSync(process.env.HOME+'/Projects/secrets-manager/.env','utf8')
+ .split('\n').find(l=>l.startsWith('SHOPIFY_ADMIN_TOKEN='))?.split('=').slice(1).join('=').trim();
+const SHOP='designer-laboratory-sandbox.myshopify.com', API='2024-10';
+const retail=c=>Math.round((c/0.65/0.85)*100)/100;
+// Erica Nyarko, Quadrille, 2026-09-10: "First price is the fabric, the second is the wallpaper $Fabric/$WP."
+const MILL={ // pattern-plane -> mill-stated net cost
+ 'Wildflowers II|Wallcovering':142, 'Wildflowers II|Fabric':172,
+ 'Silhouette Reverse|Fabric':172, 'Volpi|Wallcovering':198,
+ 'Club Cane|Wallcovering':174,
+};
+const rows=fs.readFileSync('pids.txt','utf8').trim().split('\n').map(l=>{
+ const [mfr,pid,ptype,cost,our]=l.split('|'); return {mfr,pid,ptype,cost:+cost,our:+our}; });
+const ids=rows.map(r=>`gid://shopify/Product/${r.pid}`);
+const q=`query($ids:[ID!]!){ nodes(ids:$ids){ ... on Product { id legacyResourceId title status
+ variants(first:20){ edges{ node{ id title price position sku inventoryQuantity } } } } } }`;
+const r=await fetch(`https://${SHOP}/admin/api/${API}/graphql.json`,{method:'POST',
+ headers:{'X-Shopify-Access-Token':TOK,'Content-Type':'application/json'},
+ body:JSON.stringify({query:q,variables:{ids}})});
+const j=await r.json();
+if(j.errors){console.error(JSON.stringify(j.errors));process.exit(1);}
+const byPid=Object.fromEntries(j.data.nodes.filter(Boolean).map(n=>[n.legacyResourceId,n]));
+const out=[];
+for(const row of rows){
+ const n=byPid[row.pid];
+ if(!n){ console.log(`${row.mfr} :: PRODUCT NOT FOUND (pid ${row.pid})`); continue; }
+ const pat=n.title.replace(/ (Screen Printed|Hand Print).*$/,'').replace(/^(.*?II|.*?Reverse|Volpi|Club Cane).*$/,'$1');
+ const vs=n.variants.edges.map(e=>e.node).sort((a,b)=>a.position-b.position);
+ const sell=vs.filter(v=>parseFloat(v.price)>10);
+ const T=n.title.toLowerCase();
+ const key=Object.keys(MILL).find(k=>T.startsWith(k.split('|')[0].toLowerCase()) && k.split('|')[1]===row.ptype);
+ const millCost=MILL[key]; const exp=millCost?retail(millCost):null;
+ const livePrice=sell.length?parseFloat(sell[0].price):null;
+ let verdict;
+ if(livePrice===null) verdict='SAMPLE-ONLY (not sellable)';
+ else if(exp===null) verdict='no mill basis';
+ else if(Math.abs(livePrice-exp)<0.02) verdict='✅ MATCHES mill';
+ else if(livePrice>exp) verdict=`❌ OVERPRICED +$${(livePrice-exp).toFixed(2)}`;
+ else verdict=`❌ UNDERPRICED -$${(exp-livePrice).toFixed(2)}`;
+ out.push({mfr:row.mfr,pid:row.pid,title:n.title,status:n.status,ptype:row.ptype,
+ db_cost:row.cost,db_our:row.our,mill_cost:millCost??null,expected:exp,live:livePrice,verdict,
+ variants:vs.map(v=>({pos:v.position,title:v.title,price:v.price,sku:v.sku}))});
+ console.log(`${row.mfr} ${row.ptype.padEnd(12)} ${n.status.padEnd(7)} live=${livePrice===null?'—':'$'+livePrice} exp=${exp?'$'+exp:'—'} ${verdict} ${n.title.slice(0,52)}`);
+}
+fs.writeFileSync('erica-live-audit.json',JSON.stringify(out,null,2));
+console.log('\nrows='+out.length+' -> erica-live-audit.json');
diff --git a/erica/pids.txt b/erica/pids.txt
new file mode 100644
index 0000000..c162de1
--- /dev/null
+++ b/erica/pids.txt
@@ -0,0 +1,31 @@
+718121|6604493258803|Wallcovering|216.00|390.95
+718221|6604493291571|Wallcovering|216.00|390.95
+718321|6604493324339|Wallcovering|216.00|390.95
+718421|6604493357107|Wallcovering|216.00|390.95
+718521|6604493389875|Wallcovering|216.00|390.95
+718621|6604493422643|Wallcovering|216.00|390.95
+784821|6604519112755|Wallcovering|178.00|322.17
+784921|6604519178291|Wallcovering|178.00|322.17
+785021|6604519211059|Wallcovering|178.00|322.17
+785121|6604519243827|Wallcovering|172.00|311.31
+785221|6604519276595|Wallcovering|172.00|311.31
+785321|6604519309363|Wallcovering|172.00|311.31
+785421|6604519342131|Wallcovering|172.00|311.31
+785521|6604519374899|Wallcovering|172.00|311.31
+785621|6604519407667|Wallcovering|172.00|311.31
+975722|6604476121139|Fabric|172.00|311.31
+975822|6604476153907|Fabric|172.00|311.31
+975922|6604476186675|Fabric|172.00|311.31
+976022|6604476219443|Fabric|172.00|311.31
+976222|6604476317747|Fabric|172.00|311.31
+976322|6604476350515|Fabric|172.00|311.31
+976422|6604476383283|Fabric|172.00|311.31
+976522|6604476416051|Fabric|172.00|311.31
+976622|6604476448819|Fabric|172.00|311.31
+976722|6604476481587|Fabric|172.00|311.31
+976922|6604476547123|Fabric|172.00|311.31
+977122|6604476612659|Fabric|172.00|311.31
+977222|6604476678195|Fabric|172.00|311.31
+977322|6604476710963|Fabric|172.00|311.31
+982022|6604478677043|Fabric|172.00|311.31
+982122|6604478709811|Fabric|172.00|311.31
diff --git a/erica/reprice-erica.mjs b/erica/reprice-erica.mjs
new file mode 100644
index 0000000..3114912
--- /dev/null
+++ b/erica/reprice-erica.mjs
@@ -0,0 +1,78 @@
+#!/usr/bin/env node
+// TK-10895 — reprice the 11 LIVE products whose cost token Erica Nyarko (Quadrille) corrected in writing.
+//
+// BASIS (not an inference — the mill said it): msg 1a08d0a98d30b250, 2026-09-10 20:37Z —
+// "First price is the fabric, the second is the wallpaper $Fabric/$WP."
+// "Wildflowers II Fabric $172 | Wallpaper $142"
+// "Club cane stating Custom was an error from my new assistant ... the accurate standard roll price of $174"
+// msg 1a08d46c0609ead2, 21:43Z — "In wallpaper we have 7 colorways priced at $174 ... not a custom."
+// The mill sheet's own Fabric/Wallpaper column selects the token. All 11 targets are F/W=Wallpaper,
+// so they take the WALLPAPER cost. Both moves are price REDUCTIONS (we were overcharging).
+//
+// Dry-run by default. Rollback map written BEFORE any write. Fails closed on any price drift.
+import fs from 'node:fs';
+const TOK = fs.readFileSync(process.env.HOME+'/Projects/secrets-manager/.env','utf8')
+ .split('\n').find(l=>l.startsWith('SHOPIFY_ADMIN_TOKEN='))?.split('=').slice(1).join('=').trim();
+if(!TOK){console.error('no SHOPIFY_ADMIN_TOKEN');process.exit(1);}
+const SHOP='designer-laboratory-sandbox.myshopify.com', API='2024-10';
+const APPLY=process.argv.includes('--apply');
+const EV=process.env.HOME+'/.claude/yolo-queue/evidence/TK-10895';
+const SELLABLE_FLOOR=10, PACE_MS=600;
+const retail=c=>(Math.round((c/0.65/0.85)*100)/100).toFixed(2);
+
+// mfr -> {before: the ONLY price we will overwrite, cost_before, cost_after, pat}
+const T={};
+for(const m of ['785121','785221','785321','785421','785521','785621'])
+ T[m]={before:'311.31',cost_before:172,cost_after:142,pat:'Wildflowers II'};
+for(const m of ['718121','718321','718421','718521','718621'])
+ T[m]={before:'390.95',cost_before:216,cost_after:174,pat:'Club Cane'};
+// 718221 is DRAFT / sample-only — releasing it is a CREATE, not a reprice. Deliberately excluded.
+
+async function api(path,opt={}){
+ const r=await fetch(`https://${SHOP}/admin/api/${API}/${path}`,{
+ ...opt, headers:{'X-Shopify-Access-Token':TOK,'Content-Type':'application/json',...(opt.headers||{})}});
+ if(!r.ok) throw new Error(`${r.status} ${await r.text()}`);
+ return r.json();
+}
+const pidOf=Object.fromEntries(fs.readFileSync('pids.txt','utf8').trim().split('\n')
+ .map(l=>{const [mfr,pid]=l.split('|');return [mfr,pid];}));
+
+const map=[];
+for(const [mfr,t] of Object.entries(T)){
+ const pid=pidOf[mfr];
+ if(!pid){console.log(` ${mfr} no pid — skip`);continue;}
+ const p=(await api(`products/${pid}.json?fields=id,handle,status,variants`)).product;
+ const v=p.variants.find(v=>parseFloat(v.price)>SELLABLE_FLOOR);
+ const want=retail(t.cost_after);
+ if(!v){console.log(` ${mfr} ${p.handle} sample-only — skip`);continue;}
+ if(v.price===want){console.log(` ${mfr} ${p.handle} already $${want} — skip (idempotent)`);continue;}
+ if(v.price!==t.before){
+ console.error(` REFUSING ${mfr} ${p.handle}: live $${v.price}, expected $${t.before} — drifted, not touching it`);
+ continue;
+ }
+ map.push({mfr,pid,handle:p.handle,status:p.status,variant_id:v.id,variant_title:v.title,
+ pattern:t.pat,price_before:v.price,price_after:want,
+ cost_before:t.cost_before,cost_after:t.cost_after});
+ console.log(` ${mfr} ${t.pat.padEnd(16)} ${p.handle.slice(0,30).padEnd(31)} $${v.price} -> $${want} (cost $${t.cost_before} -> $${t.cost_after})`);
+}
+if(!map.length){console.log('\nnothing to do.');process.exit(0);}
+const RB=EV+'/erica-reprice-rollback-20260910.json';
+fs.writeFileSync(RB,JSON.stringify(map,null,1));
+const delta=map.reduce((s,m)=>s+(parseFloat(m.price_before)-parseFloat(m.price_after)),0);
+console.log(`\nrollback map -> ${RB} (${map.length} rows)`);
+console.log(`total customer-facing reduction: $${delta.toFixed(2)} across ${map.length} products`);
+if(!APPLY){console.log('\nDRY RUN — nothing written. Re-run with --apply.');process.exit(0);}
+
+let ok=0,fail=0;
+for(const m of map){
+ try{
+ const r=await api(`variants/${m.variant_id}.json`,{method:'PUT',
+ body:JSON.stringify({variant:{id:m.variant_id,price:m.price_after}})});
+ if(r.variant.price!==m.price_after) throw new Error(`read-back ${r.variant.price} != ${m.price_after}`);
+ console.log(` OK ${m.mfr} ${m.handle} now $${r.variant.price}`); ok++;
+ }catch(e){console.log(` FAIL ${m.mfr} ${m.handle} :: ${e.message}`);fail++;}
+ await new Promise(r=>setTimeout(r,PACE_MS));
+}
+if(fail) process.exitCode=1;
+console.log(`\napplied=${ok} failed=${fail}`);
+console.log('Verify from the STOREFRONT after ~30s, never the Admin API (its variant order is stale).');
diff --git a/reprice-volpi.mjs b/reprice-volpi.mjs
new file mode 100644
index 0000000..5e66457
--- /dev/null
+++ b/reprice-volpi.mjs
@@ -0,0 +1,100 @@
+#!/usr/bin/env node
+// TK-10895 — Volpi reprice: 3 LIVE wallpapers priced off the FABRIC token of a fabric/wallpaper pair.
+//
+// The mill sheet cell reads "$178/$198". Which token is which is PINNED by the sheet itself, not
+// inferred: 15 standalone Volpi FABRIC rows carry a CLEAN single "$178.00" (Material "100% linen
+// fabric"), while all 14 WALLPAPER rows carry the pair. So token 1 = fabric cost, token 2 = WALLPAPER
+// cost. Our three SKUs are Wallpaper (F/W=Wallpaper, 27in) -> correct cost $198, not $178.
+//
+// This is the MONEY-LOSING direction: live at $322.17 (from $178) vs correct $358.37 (from $198),
+// i.e. under by $36.20/roll — 1.63x cost against our 1.81x target.
+//
+// Dry-run by default. --apply to write. Rollback map written BEFORE any write.
+import fs from 'node:fs';
+
+const TOK = fs.readFileSync(process.env.HOME + '/Projects/secrets-manager/.env', 'utf8')
+ .split('\n').find(l => l.startsWith('SHOPIFY_ADMIN_TOKEN='))
+ ?.split('=').slice(1).join('=').trim();
+if (!TOK) { console.error('no SHOPIFY_ADMIN_TOKEN'); process.exit(1); }
+
+const SHOP = 'designer-laboratory-sandbox.myshopify.com';
+const API = '2024-10';
+const APPLY = process.argv.includes('--apply');
+const EV = process.env.HOME + '/.claude/yolo-queue/evidence/TK-10895';
+
+const WALLPAPER_COST = 198; // token 2 — pinned by the 15 clean $178.00 FABRIC rows
+const WHOLESALE_MARGIN = 0.65, RETAIL_MARGIN = 0.85;
+const WANT = (WALLPAPER_COST / WHOLESALE_MARGIN / RETAIL_MARGIN).toFixed(2); // 358.37
+const EXPECT_BEFORE = '322.17'; // what token-1 ($178) produced — refuse anything else
+const SELLABLE_FLOOR = 10, PACE_MS = 600;
+
+const TARGETS = ['784821', '784921', '785021'];
+
+async function api(path, opt = {}) {
+ const r = await fetch(`https://${SHOP}/admin/api/${API}/${path}`, {
+ ...opt,
+ headers: { 'X-Shopify-Access-Token': TOK, 'Content-Type': 'application/json', ...(opt.headers || {}) },
+ });
+ if (!r.ok) throw new Error(`${r.status} ${path} :: ${(await r.text()).slice(0, 200)}`);
+ return r.json();
+}
+
+// resolve pids from the per-SKU evidence CSV rather than hardcoding them.
+// NOTE: a naive split(',') is WRONG here — pull_reason is a quoted field containing commas, which
+// shifts every column after it. Use a real RFC4180-ish parse.
+function parseCsv(txt) {
+ const rows = []; let row = [], f = '', q = false;
+ for (let i = 0; i < txt.length; i++) {
+ const c = txt[i];
+ if (q) { if (c === '"' && txt[i + 1] === '"') { f += '"'; i++; } else if (c === '"') q = false; else f += c; }
+ else if (c === '"') q = true;
+ else if (c === ',') { row.push(f); f = ''; }
+ else if (c === '\n') { row.push(f); rows.push(row); row = []; f = ''; }
+ else if (c !== '\r') f += c;
+ }
+ if (f || row.length) { row.push(f); rows.push(row); }
+ const h = rows.shift();
+ return rows.filter(r => r.length === h.length).map(r => Object.fromEntries(h.map((k, i) => [k, r[i]])));
+}
+const pidOf = {};
+for (const r of parseCsv(fs.readFileSync(EV + '/TrancheB-per-sku-evidence-20260910.csv', 'utf8'))) {
+ pidOf[r.mfr_sku] = { pid: String(r.shopify_id).split('/').pop(), dw: r.dw_sku };
+}
+
+const map = [];
+for (const mfr of TARGETS) {
+ const rec = pidOf[mfr];
+ if (!rec?.pid) { console.log(` ${mfr} no pid in evidence CSV — skip`); continue; }
+ const p = (await api(`products/${rec.pid}.json?fields=id,handle,variants`)).product;
+ const v = p.variants.find(v => parseFloat(v.price) > SELLABLE_FLOOR);
+ if (!v) { console.log(` ${mfr} ${p.handle} no sellable variant (still sample-only) — skip`); continue; }
+ if (v.price === WANT) { console.log(` ${mfr} ${p.handle} already $${WANT} — skip (idempotent)`); continue; }
+ // FAIL CLOSED: only correct the exact known-bad price. Anything else means someone changed it.
+ if (v.price !== EXPECT_BEFORE) {
+ console.error(` REFUSING ${mfr} ${p.handle}: live $${v.price}, expected $${EXPECT_BEFORE} — drifted, not touching it`);
+ continue;
+ }
+ map.push({ mfr, dw_sku: rec.dw, pid: rec.pid, handle: p.handle, variant_id: v.id,
+ title: v.title, price_before: v.price, price_after: WANT, cost_before: 178, cost_after: WALLPAPER_COST });
+ console.log(` ${mfr} ${p.handle.padEnd(14)} $${v.price} -> $${WANT} (cost $178 fabric-token -> $198 wallpaper)`);
+}
+
+if (!map.length) { console.log('\nnothing to do.'); process.exit(0); }
+fs.writeFileSync(EV + '/volpi-reprice-rollback-20260910.json', JSON.stringify(map, null, 1));
+console.log(`\nrollback map -> ${EV}/volpi-reprice-rollback-20260910.json (${map.length})`);
+if (!APPLY) { console.log('\nDRY RUN — nothing written. Re-run with --apply.'); process.exit(0); }
+
+let ok = 0, fail = 0;
+for (const m of map) {
+ try {
+ const r = await api(`variants/${m.variant_id}.json`, {
+ method: 'PUT', body: JSON.stringify({ variant: { id: m.variant_id, price: m.price_after } }),
+ });
+ if (r.variant.price !== m.price_after) throw new Error(`read-back ${r.variant.price} != ${m.price_after}`);
+ console.log(` OK ${m.mfr} ${m.handle} now $${r.variant.price}`); ok++;
+ } catch (e) { console.log(` FAIL ${m.mfr} ${m.handle} :: ${e.message}`); fail++; }
+ await new Promise(r => setTimeout(r, PACE_MS));
+}
+if (fail) process.exitCode = 1;
+console.log(`\napplied=${ok} failed=${fail}`);
+console.log('Verify from the STOREFRONT after ~30s, never the Admin API (its variant order is stale).');
← c5cad53 auto-data-snapshot: 2026-09-10T13:02:09 (2 data files) — wf6
·
back to Tk10895 GroupB
·
auto-data-snapshot: 2026-09-11T10:49:53 (4 data files) — eri 75499b6 →