feat: Tier B operator scaffolding — bundle, copy SoT, posts, emails

Pick up and finish yesterday's cut-off Tier B pass.

- build/: PyInstaller scaffold (datatools.spec + launcher.py +
  hook-streamlit.py + README) — folder-mode bundle, locked
  127.0.0.1, per-OS recipe
- marketing/COPY.md: single source of truth for every customer-facing
  string — landing H1/sub/CTAs, demo CTAs, email subjects, Gumroad
  listing, banned phrases
- marketing/community-posts/: 9 drafts (3 posts × 3 niches:
  bookkeeper, revops, shopify-pet) — story / tip / soft-offer
- marketing/emails/: 18 drafts (Gumroad delivery + 5-touch
  onboarding × 3 niches), per-niche segmentation guidance
- docs/NEXT-STEPS.md: flip 2.2 / 2.4 / 3.1 / 3.4 to done with
  pointers to the new assets; add Phase 0 inventory rows
- .gitignore: narrow `build/` ignore so PyInstaller spec + launcher
  + hooks get tracked, only generated artifacts (build/build/,
  build/__pycache__/, build/dist/) stay ignored

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-02 14:04:37 +00:00
parent 966af8ef94
commit e1f364f010
36 changed files with 1741 additions and 15 deletions

View File

@@ -0,0 +1,34 @@
# Shopify-pet · Day 0 — Delivery email
**Subject:** Your DataTools download (start here)
**Send:** immediately on Gumroad purchase confirmation
**Goal:** download + first run within 24h
---
Hi {{first_name}},
Thanks for buying DataTools. Your download:
**{{download_url}}**
Three things to do in the next 5 minutes:
**1. Download the installer for your OS** (Mac `.dmg`, Windows `.exe`, or Linux `.tar.gz`). About 280 MB. The link auto-detects.
**2. Run it.** First launch takes ~5 seconds; a browser tab opens to `127.0.0.1:8501`. That's the app — running locally on your machine. No data leaves the box. Your customer list never goes to a server.
**3. Drop in a real Shopify customer export.** Don't bother with the bundled samples. Customers > Export > "All customers" > CSV in Shopify admin. Drag it into DataTools' analyzer, click **"Run all"**. You'll see what it catches — typically a few hundred phone-format issues, some hidden-character emails, and a handful of cross-row duplicates — in about 30 seconds.
If something doesn't work: reply to this email. Goes to my inbox.
Refund: also reply. 30-day no-questions; no form.
Tomorrow I'll send a sample Shopify customer export with the tricky cases pre-built in, so you can see what the cleanup catches on a known input. After that you'll get one email a week for the next month with one tip each. Unsubscribe at the bottom of any of them.
Welcome aboard.
— Michael
{{support_email}}
P.S. Got a fellow store owner who'd find this useful? {{landing_page}}.

View File

@@ -0,0 +1,32 @@
# Shopify-pet · Day 1 — Try it on this Shopify customer export first
**Subject:** Try it on this Shopify customer export first
**Send:** Day 1, ~9am buyer-local-time
---
Hi {{first_name}},
Yesterday's email had your download. Today's email has a *file* — a synthetic Shopify customer export I built specifically to break things Klaviyo silently chokes on.
**{{sample_file_url}}** (480 KB CSV, 2,200 rows — fully synthetic, no real customer data)
What's hidden in there:
- Phone numbers in 6 different formats (`(415) 555-0143`, `415.555.0143`, `4155550143`, `+44 20 7946 0958` without country field, `+1-415-555-0143 ext 12`, `415 555 0143`)
- Email addresses with embedded zero-width spaces (looks identical to a clean email; Klaviyo treats as different addresses)
- ~80 obvious customer duplicates (same email, different case)
- ~40 cross-row duplicates (different email, same name + same shipping address — usually the same person ordering with two emails)
- Shipping addresses with mixed `St.` / `Street` / `St` / `STREET` for the same street name
- 12 customers from outside North America with country field blank
Drop it into DataTools. Click **"Run all"** in the analyzer. Then run **format → dedupe → text-clean → gate** in that order.
Look at the **gate report** at the end — it'll tell you exactly which rows would have broken Klaviyo, with a one-line "why" per row.
If you want to see the difference: import the **raw** file to a test Klaviyo list, then import the **cleaned** file to a different test list. Compare the SMS-deliverable count. The delta is what you've been losing every month.
Reply and tell me what it caught (or missed) — v1.1 detector improvements come from real-world feedback.
— Michael
{{support_email}}

View File

@@ -0,0 +1,33 @@
# Shopify-pet · Day 3 — The phone-format step Klaviyo cares about
**Subject:** The phone-format step Klaviyo cares about
**Send:** Day 3
**Goal:** deepen feature understanding around the format standardizer
---
Hi {{first_name}},
The single biggest source of "Klaviyo dropped this customer silently" is phone formatting. DataTools fixes this in one tool — the **format standardizer** — but the *settings* matter.
Klaviyo (and basically every modern SMS platform) wants phones in **E.164** format: `+` then country code then number, no spaces, no dashes, no extension. Like: `+14155550143`.
Three settings in DataTools' format standardizer that get this right:
**1. Set "Phone output format" to `E.164`.** Default is `national` (`(415) 555-0143`) — fine for display, broken for Klaviyo. Change it once; the preset remembers.
**2. Set "Default country" per row, not per file.** This is the non-obvious one. For each customer:
- If the `country` field has a value (e.g., "Canada", "CA", "Canadá"), use it.
- If blank, fall back to the country in the *shipping address*.
- If still blank, fall back to the file-level default (you set this — typically your store's primary market).
DataTools does this automatically when you check "Use per-row country detection". *Skip this and ~30% of international customers will end up with US country codes prepended to their numbers — which Klaviyo accepts but routes wrong, and your SMS never arrives.*
**3. Set "Quarantine un-parseable phones" to ON.** Don't drop them silently; don't pass them to Klaviyo broken. Send them to `<filename>.quarantine.csv` so you can fix the worst 10-20 by hand and re-include them.
The combination — E.164 + per-row country + quarantine — typically takes a Shopify export from "60-70% of phones survive Klaviyo's import" to "97-99%". On a 10,000-customer list, that's 2,500 - 3,500 more customers reachable per campaign.
Reply if you want me to walk through these settings on a screen-share — happy to do this for any buyer in the first 30 days.
— Michael
{{support_email}}

View File

@@ -0,0 +1,35 @@
# Shopify-pet · Day 7 — Run it before every Klaviyo sync
**Subject:** Run it before every Klaviyo sync
**Send:** Day 7
**Goal:** reframe from one-off tool to per-sync workflow
---
Hi {{first_name}},
A week in. By now you've probably run DataTools on a real customer export once or twice and seen the cleanup catch things you'd been losing in Klaviyo for months.
The thing that turns DataTools into a recurring win instead of a one-off purchase: **run it before every sync, not just the first time.**
The pattern that works for most stores:
**1. Pick a cadence.** Most stores I talk to do this monthly; high-volume stores do it weekly. The cadence should match your "I'm planning a campaign" rhythm.
**2. The Sunday-morning ritual:**
- Pull a fresh customer export from Shopify (Customers > Export > "All customers")
- Drop into DataTools
- Run the pipeline (analyzer → format → text-clean → dedupe → gate)
- Review the gate quarantine file (typically 0.5-2% of rows)
- Push the cleaned CSV to Klaviyo (their CSV import or via their API)
**3. Save your settings as a preset.** The "Save settings" button writes a `.datatools-preset.json`. Keep it in your store's Drive / Notion / wherever your shop docs live. Next month, load preset, run pipeline, done in 4 minutes.
**4. After 3 months, retune the preset.** Look at your manual-review queue across the 3 runs. If you're consistently approving 0.86-confidence merges, drop the auto-merge threshold to 0.85. If you're rejecting 0.92 merges, raise it to 0.94. The preset improves with use.
The store owners doing this monthly tell me their open rates go up 8-15% in the first 90 days — not from new content, just from the email actually reaching the inbox.
If you want, reply with a sanitized export and I'll suggest a starting preset for your store — happy to do this for the first 50 buyers.
— Michael
{{support_email}}

View File

@@ -0,0 +1,32 @@
# Shopify-pet · Day 14 — Two-minute trick: hidden-character cleanup
**Subject:** Two-minute trick: hidden-character cleanup
**Send:** Day 14
**Goal:** surface the text cleaner — non-obvious, high-value
---
Hi {{first_name}},
The tool inside DataTools that buyers find last is the **text cleaner** — and on Shopify customer exports it's usually the one with the most "wait, that was a problem?" moments.
What it catches: invisible characters that got into your customer data when customers typed on their phones. The most common offenders:
- **Zero-width space** (`U+200B`) inside emails — Klaviyo treats `sarah@acme.com` (with hidden char) and `sarah@acme.com` (without) as different addresses
- **Non-breaking space** (`U+00A0`) inside addresses — Shopify accepts it, Klaviyo accepts it, but USPS address validation fails on it
- **BOM marker** (`U+FEFF`) at the start of CSV cells — usually from a customer pasting from Word or a PDF
- **Right-to-left mark** (`U+200F`) — rare, but appears in customer names from Hebrew/Arabic locales
The 2-minute workflow:
1. After the format standardizer pass, run the text cleaner.
2. It produces an additional sidecar file: `<filename>.hidden-chars.csv` — every cell where it found a hidden char, with a "what was hidden where" annotation.
3. Skim it. Most are fine to silently strip (zero-width spaces, BOMs). For rare ones (right-to-left marks in a name), confirm before stripping — sometimes they're load-bearing.
4. Click "Apply cleanup". The text cleaner replaces the hidden chars in the cleaned CSV.
The reason this matters: **dedupe runs after text-clean.** Two emails with a hidden char difference look identical in the GUI but get treated as two separate customers — and your dedupe pass won't catch them unless the text cleaner ran first.
The pipeline order baked into the GUI is: `analyzer → format → text-clean → dedupe → gate`. Stick to it; per-tool runs out of order are the most common source of "wait, why didn't dedupe catch this?".
— Michael
{{support_email}}

View File

@@ -0,0 +1,26 @@
# Shopify-pet · Day 30 — Heard from another store owner?
**Subject:** Heard from another store owner?
**Send:** Day 30
**Goal:** referral / review ask
---
Hi {{first_name}},
A month in. If DataTools earned its $49 — would you do me one small favor?
**Pick the one that's easiest.**
1. **Gumroad review** (60 seconds): {{download_url}}#reviews — every line helps the next Shopify owner trust the listing enough to click "buy".
2. **Reply to this email with one sentence I can quote** on the landing page. Anonymous if you prefer; I'll never use a name without explicit permission.
3. **Share the landing page** with one fellow store owner who'd benefit: {{landing_page}}. No referral commission, just a link.
If DataTools *didn't* earn its $49 — also reply. Tell me what's missing or broken. The 30-day refund window is still open and I'd rather refund than have an unhappy customer in the wild.
Either way, this is the last automated email you'll get from me. After this you only hear from me when there's a v1.x update or if you reply to one of the previous emails.
Thanks for being an early buyer — the first 50 customers shape the next 5,000.
— Michael
{{support_email}}