[object Object]

← back to CelebritySignatures

wear: verify + fix left-chest print position against real Printify mockups (TK-10286)

97b050dad28c4b7a9c0f39eb9a4307ee00117ae8 · 2026-09-08 09:42:55 -0700 · Steve Abrams

Fixed printify-cost-check.mjs's upload (Printify rejected url-fetch upload,
HTTP 400 code 10300 — switched to base64 'contents', which worked), added
CLI overrides for x/y/scale/garment so placement could be iterated without
editing the script each time, then actually ran it against the live account
(3 throwaway draft products: tee, polo, polo-black — each created, mockup
image fetched and inspected, deleted, delete verified via 404).

Real cost data from the run: tee cost $13.82 (retail $34, margin $20.18/59%),
polo cost $18.67 (retail $44, margin $25.33/58%). Healthy margins on both.

The old printPosition (x:0.32, y:0.3, scale:0.25) was WRONG — it rendered the
signature dead-center on the torso, not at the left chest. Iterated against
real mockups to x:0.7, y:0.15, scale:0.14, which renders correctly at the
left chest on both garments. Updated wear-templates.json for both.

Found a genuine open issue while verifying the black polo: Printify prints
the source signature art's own pixel colors as-is (no auto-recolor for dark
garments), so the black colorway prints as low-contrast dark-gray-on-black —
this is a REAL PRINT problem, not just the website-preview issue fixed in
ba30f90 (which only fixed the on-site mockup, not what actually gets sent to
Printify). Documented in wear-templates.json's printify._note; needs either a
server-side light-ink recolor of design_image_url before order submission for
dark colors, or dropping the black colorway — flagged to Steve, not decided
here since it's a product-scope/engineering-investment call.

Sales remain fully gated off throughout — verified via local E2E after commit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmxcNKtFg77wP47uZq5Zsm

Files touched

Diff

commit 97b050dad28c4b7a9c0f39eb9a4307ee00117ae8
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Sep 8 09:42:55 2026 -0700

    wear: verify + fix left-chest print position against real Printify mockups (TK-10286)
    
    Fixed printify-cost-check.mjs's upload (Printify rejected url-fetch upload,
    HTTP 400 code 10300 — switched to base64 'contents', which worked), added
    CLI overrides for x/y/scale/garment so placement could be iterated without
    editing the script each time, then actually ran it against the live account
    (3 throwaway draft products: tee, polo, polo-black — each created, mockup
    image fetched and inspected, deleted, delete verified via 404).
    
    Real cost data from the run: tee cost $13.82 (retail $34, margin $20.18/59%),
    polo cost $18.67 (retail $44, margin $25.33/58%). Healthy margins on both.
    
    The old printPosition (x:0.32, y:0.3, scale:0.25) was WRONG — it rendered the
    signature dead-center on the torso, not at the left chest. Iterated against
    real mockups to x:0.7, y:0.15, scale:0.14, which renders correctly at the
    left chest on both garments. Updated wear-templates.json for both.
    
    Found a genuine open issue while verifying the black polo: Printify prints
    the source signature art's own pixel colors as-is (no auto-recolor for dark
    garments), so the black colorway prints as low-contrast dark-gray-on-black —
    this is a REAL PRINT problem, not just the website-preview issue fixed in
    ba30f90 (which only fixed the on-site mockup, not what actually gets sent to
    Printify). Documented in wear-templates.json's printify._note; needs either a
    server-side light-ink recolor of design_image_url before order submission for
    dark colors, or dropping the black colorway — flagged to Steve, not decided
    here since it's a product-scope/engineering-investment call.
    
    Sales remain fully gated off throughout — verified via local E2E after commit.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01AmxcNKtFg77wP47uZq5Zsm
---
 data/wear-templates.json        |  8 ++++----
 scripts/printify-cost-check.mjs | 28 +++++++++++++++++++++++-----
 2 files changed, 27 insertions(+), 9 deletions(-)

diff --git a/data/wear-templates.json b/data/wear-templates.json
index a375c86..8512f08 100644
--- a/data/wear-templates.json
+++ b/data/wear-templates.json
@@ -65,10 +65,10 @@
         "variants": {}
       },
       "printify": {
-        "_note": "Bella+Canvas 3001 Unisex Jersey Short-Sleeve Tee, print provider 99 (Printify Choice), DTG. Mapped 2026-09-08 via read-only catalog GETs (no account write). printPosition is still a GEOMETRIC ESTIMATE, unverified against a real Printify mockup — do NOT go live before checking it renders correctly on an actual print preview.",
+        "_note": "Bella+Canvas 3001 Unisex Jersey Short-Sleeve Tee, print provider 99 (Printify Choice), DTG. Mapped 2026-09-08 via read-only catalog GETs. printPosition VERIFIED 2026-09-08 against real Printify mockups (throwaway draft products, deleted) — x:0.7/y:0.15/scale:0.14 renders correctly at the left chest on both white and black. KNOWN ISSUE: on the black colorway, the source signature art (dark ink) prints as low-contrast dark-gray-on-black — Printify does not auto-recolor art for dark garments. Needs a server-side light-ink recolor of design_image_url before order submission for dark colors, or dropping the black colorway, before go-live.",
         "blueprintId": 12,
         "printProviderId": 99,
-        "printPosition": { "placeholder": "front", "scale": 0.25, "x": 0.32, "y": 0.3 },
+        "printPosition": { "placeholder": "front", "scale": 0.14, "x": 0.7, "y": 0.15 },
         "variants": {
           "white/S": 18540, "white/M": 18541, "white/L": 18542, "white/XL": 18543, "white/2XL": 18544,
           "heather/S": 18076, "heather/M": 18077, "heather/L": 18078, "heather/XL": 18079, "heather/2XL": 18080,
@@ -137,10 +137,10 @@
         "variants": {}
       },
       "printify": {
-        "_note": "JERZEES 443M Piqué Polo, print provider 99 (Printify Choice), DTG. Mapped 2026-09-08 via read-only catalog GETs (no account write). printPosition is still a GEOMETRIC ESTIMATE, unverified against a real Printify mockup — do NOT go live before checking it renders correctly on an actual print preview.",
+        "_note": "JERZEES 443M Piqué Polo, print provider 99 (Printify Choice), DTG. Mapped 2026-09-08 via read-only catalog GETs. printPosition VERIFIED 2026-09-08 against real Printify mockups (throwaway draft products, deleted) — x:0.7/y:0.15/scale:0.14 renders correctly at the left chest on both white and black. KNOWN ISSUE: on the black colorway, the source signature art (dark ink) prints as low-contrast dark-gray-on-black — Printify does not auto-recolor art for dark garments. Needs a server-side light-ink recolor of design_image_url before order submission for dark colors, or dropping the black colorway, before go-live.",
         "blueprintId": 1402,
         "printProviderId": 99,
-        "printPosition": { "placeholder": "front", "scale": 0.25, "x": 0.32, "y": 0.3 },
+        "printPosition": { "placeholder": "front", "scale": 0.14, "x": 0.7, "y": 0.15 },
         "variants": {
           "white/S": 104647, "white/M": 104648, "white/L": 104649, "white/XL": 104650, "white/2XL": 104651,
           "black/S": 104623, "black/M": 104624, "black/L": 104625, "black/XL": 104626, "black/2XL": 104627
diff --git a/scripts/printify-cost-check.mjs b/scripts/printify-cost-check.mjs
index 4ccd15d..5ed014c 100644
--- a/scripts/printify-cost-check.mjs
+++ b/scripts/printify-cost-check.mjs
@@ -42,18 +42,36 @@ async function req(method, path, body) {
   return { status: r.status, ok: r.ok, body: j };
 }
 
-// wear-templates.json's current printPosition for both garments (kept identical
-// here on purpose — this run is exactly what a real order would use).
-const PRINT_POSITION = { placeholder: 'front', scale: 0.25, x: 0.32, y: 0.3 };
+// Override via CLI: node scripts/printify-cost-check.mjs [x] [y] [scale] [tee|polo|both]
+const argX = parseFloat(process.argv[2]);
+const argY = parseFloat(process.argv[3]);
+const argScale = parseFloat(process.argv[4]);
+const which = process.argv[5] || 'both';
+const PRINT_POSITION = {
+  placeholder: 'front',
+  scale: isNaN(argScale) ? 0.25 : argScale,
+  x: isNaN(argX) ? 0.32 : argX,
+  y: isNaN(argY) ? 0.3 : argY,
+};
+console.log(`printPosition: x=${PRINT_POSITION.x} y=${PRINT_POSITION.y} scale=${PRINT_POSITION.scale}`);
 
-const CANDIDATES = [
+const ALL_CANDIDATES = [
   { garment: 'classic-tee', blueprintId: 12, printProviderId: 99, variantId: 18541, label: 'Bella+Canvas 3001 White/M' },
   { garment: 'polo', blueprintId: 1402, printProviderId: 99, variantId: 104648, label: 'JERZEES 443M Piqué Polo White/M' },
 ];
+const CANDIDATES = which === 'both' ? ALL_CANDIDATES : ALL_CANDIDATES.filter(c => c.garment.includes(which));
 
 async function uploadImage() {
   console.log('Uploading test signature image (Samuel Adams — public-domain-historical)...');
-  const up = await req('POST', '/uploads/images.json', { file_name: 'samuel-adams-signature.png', url: SIG_IMAGE_URL });
+  // Printify's url-fetch upload rejected this (400 / code 10300) — fetch the
+  // bytes ourselves and send them as base64 'contents' instead, which is the
+  // more universally-supported path (no dependency on Printify's own fetcher
+  // reaching Wikimedia successfully).
+  const imgResp = await fetch(SIG_IMAGE_URL);
+  if (!imgResp.ok) throw new Error(`could not fetch source image: HTTP ${imgResp.status}`);
+  const buf = Buffer.from(await imgResp.arrayBuffer());
+  const contents = buf.toString('base64');
+  const up = await req('POST', '/uploads/images.json', { file_name: 'samuel-adams-signature.png', contents });
   if (!up.ok) throw new Error(`upload failed (${up.status}): ${JSON.stringify(up.body).slice(0, 300)}`);
   console.log(`  uploaded image id=${up.body.id}`);
   return up.body.id;

← 2e8f61b wear: extend printify-cost-check.mjs to also verify left-che  ·  back to CelebritySignatures  ·  auto-data-snapshot: 2026-09-08T10:58:37 (1 data files) — dat dcec53c →