From aa43bb7d1aba2127738176c03ebfab19435c6951 Mon Sep 17 00:00:00 2001 From: Mike Swanson Date: Thu, 23 Jul 2026 07:40:53 -0700 Subject: [PATCH] sync: auto-sync from GURU-5070 at 2026-07-23 07:40:10 Author: Mike Swanson Machine: GURU-5070 Timestamp: 2026-07-23 07:40:10 --- .../2026-07-23-mike-prospect-list-vetting.md | 219 ++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 session-logs/2026-07/2026-07-23-mike-prospect-list-vetting.md diff --git a/session-logs/2026-07/2026-07-23-mike-prospect-list-vetting.md b/session-logs/2026-07/2026-07-23-mike-prospect-list-vetting.md new file mode 100644 index 00000000..e8c8a8b3 --- /dev/null +++ b/session-logs/2026-07/2026-07-23-mike-prospect-list-vetting.md @@ -0,0 +1,219 @@ +# Prospect Call-List Vetting — "Arizona Computer Guru New List.csv" + +## User +- **User:** Mike Swanson (mike) +- **Machine:** GURU-5070 +- **Role:** admin + +--- + +## Session Summary + +Mike supplied a purchased 238-row Tucson-area lead list intended as a call list for an outside +sales / appointment-setting firm working on ACG's behalf. The task was to vet it: dedupe against +existing and former clients, and strip prospect categories ACG does not want to onboard. Mike +noted this had been done before, but no record of prior criteria existed anywhere in the repo +(grep over `wiki/`, `session-logs/`, `.claude/memory/` for "call list", "lead list", "ICP", +"do not onboard" returned nothing), so the criteria were re-established from scratch this session. + +The file was located at `C:\Users\guru\Documents\Arizona Computer Guru New List.csv`, not +Downloads as stated. First finding: the CSV is a **vendor preview export with no usable contact +data**. Columns `work_email`, `personal_email`, `work_phone`, `work_mobile`, `mobile`, +`alt_mobile`, `branch_phone`, `hq_phone`, `street1`, `street2`, `company_revenue`, +`company_employee_count`, `linkedin_url`, `primary_industry`, and all department-size and `hq_*` +fields contain the literal strings `Yes`/`No` (field-availability flags), not values. Verified +across all 238 rows. Only `company`, `domain`, `first_name`, `last_name`, `job_title`, +`job_level`, `job_department`, `city`, `state`, `zip`, `location_type`, and `matchId` hold real +data. A setting firm cannot dial this file as-is. + +Dedupe was done against Syncro rather than `wiki/clients/` — the wiki has ~75 client articles +while Syncro holds 3000 customers and 768 contacts. Both were paginated out to local TSVs and +matched three ways: email domain (customer `.email` plus all contact emails, free-mail domains +excluded), normalized business name, and difflib fuzzy. Domain matching produced every real hit; +fuzzy below 0.86 produced only false positives (TMM Family Services -> AMP Facility Services, +NTH Consulting -> SRK Consulting, Stark Electric -> Fiesta Electric). Sixteen leads matched; 13 +were genuine. Ticket and invoice counts plus max dates were then pulled per matched customer to +separate active from dormant: 4 active (Dataforth, SunDanzer, Patient Care Advocates, Ralph's +Transfer — all invoiced in 2026), 9 dormant former clients dating 2015–2022. + +Mike confirmed all four proposed exclusion categories (IT/MSP competitors, orgs with in-house IT +staff, micro/consumer-facing shops, healthcare/HIPAA-heavy), plus keep-and-flag handling for +former clients and for remote decision-makers. All 238 rows were then classified by hand into +call tiers A/B/C or an exclusion bucket, with 14 ambiguous companies researched via WebFetch +before classification. Output: two CSVs in `~/Documents` — 168 callable companies tier-sorted, +70 excluded with a reason per row. Criteria were saved as a repo memory so the next list runs the +same way without re-asking. + +## Key Decisions + +- **Deduped against Syncro, not the wiki.** `wiki/clients/` covers ~75 clients; Syncro has ~3000 + customers. Using the wiki would have missed every dormant/one-off record. +- **Included Syncro *contacts* (768) in the domain map, not just customer records.** This caught + three leads that exist only as contacts under a differently-named customer: SunDanzer (cust + 139935), Aviva Children's Services (cust 2122768), Dataforth's older record (cust 578095). +- **Treated a Syncro record alone as meaningless.** Several matched customers have zero tickets + and zero invoices (Ironwood Tree Experience, Marana Chamber). Activity counts + max dates were + required to call anything "active." +- **Did not cut on the CSV's city/state column.** `location_type` = `Remote` (77 rows) means the + *contact* works remotely; the company is usually still Tucson. Verified example: The Carlson + Company reads "Lakewood CA" but is a 60-year Tucson plumbing/HVAC shop. Cutting on the state + column would have dropped ~34 valid AZ prospects. Instead: verify HQ via the domain, keep, and + flag `remote_contact`. +- **Kept medical device manufacturers and distributors** despite the healthcare exclusion — + Wells Johnson, NeuroVRD, Medfix International, Avery Therapeutics are not covered entities. + Flagged to Mike as a judgment call open to reversal. +- **Excluded Bluespan as a competitor** — Tucson ISP with 15,000+ subscribers and its own network + staff, not obvious from the name. +- **Excluded 6 orgs on the contact's own job title** (Network Administrator, Sr Systems Engineer, + IT Coordinator, IT Administrator, Sr Systems Administrator) — self-identifying in-house IT. +- **Three tiers instead of a flat keep/drop**, so the setting firm has a dial order rather than + 168 undifferentiated rows. +- **Left 5 unverifiable companies in at Tier B/C rather than dropping them**, marked `UNVERIFIED` + for the setter to qualify — dropping on a failed HTTP fetch would discard real prospects. + +## Problems Encountered + +- **CSV not in Downloads.** Mike said Downloads; the file was in `~/Documents`. Resolved with a + `find` over `/c/Users/guru` maxdepth 4. +- **Syncro `/contacts` pagination caps below the requested `per_page`.** `per_page=100` over 8 + pages returned only 400 unique rows against `total_entries: 768` (effective cap ~50/page). + Resolved by looping 20 pages and `sort -u`; confirmed 768 unique. +- **Six WebFetch verifications failed** — TLS cert mismatches (ziprtechnologies.com and + openenclose.net both point at shared bluehost/wpengine certs), HTTP 403 anti-scraping + (gvfan.com, swstucson.com), TLS internal error (palletrecyclers.com), connection refused + (repicon.com). Not logged to errorlog — these are remote-site conditions, not harness failures. + Resolved by marking the 5 affected leads `UNVERIFIED` at Tier B/C rather than guessing. + ZIPR Technologies was still excluded as a competitor because Mike explicitly confirmed it in + that category when answering the criteria question. +- **`os.environ['SP']` KeyError** in the first extraction script — assumed a scratchpad env var + existed. Resolved by using the literal absolute scratchpad path. + +## Configuration Changes + +**Created (repo):** +- `.claude/memory/feedback_prospect_list_vetting.md` — durable vetting criteria (type: feedback) +- Appended index line to `.claude/memory/MEMORY.md` + +**Created (Mike's Documents, not in repo):** +- `C:\Users\guru\Documents\ACG Call List - VETTED.csv` — 168 rows, tier-sorted +- `C:\Users\guru\Documents\ACG Call List - EXCLUDED.csv` — 70 rows, reason per row + +**Scratchpad (session-local, not committed):** +- `scratchpad/leads.tsv` — extracted lead fields +- `scratchpad/syncro_customers.txt` — 3000 customers (id, business_name, contact name, email) +- `scratchpad/syncro_contacts.txt` — 768 contacts (customer_id, name, email) +- `scratchpad/matches.txt` — dedupe match report +- `scratchpad/classify.py` — full 238-row classification + CSV generator (rerunnable) + +## Credentials & Secrets + +None discovered, created, or rotated. Syncro API key was read via the standard path +(`source .claude/scripts/syncro-env.sh`, vault `msp-tools/syncro`, user `mike`, key length 51). +All Syncro calls were read-only (GET) — no writes, no bot alerts required. + +## Infrastructure & Servers + +- Syncro PSA: `https://computerguru.syncromsp.com/api/v1` — 3000 customers, 768 contacts +- No other infrastructure touched. + +## Commands & Outputs + +```bash +# Locate the file (was NOT in Downloads) +find /c/Users/guru -maxdepth 4 -iname "*New List*" +# -> /c/Users/guru/Documents/Arizona Computer Guru New List.csv + +# Prove the contact columns are availability flags, not data +python -c "import csv,io; r=list(csv.DictReader(io.open('Arizona Computer Guru New List.csv',encoding='utf-8-sig'))); print(sorted(set(x['work_phone'] for x in r)))" +# -> ['No', 'Yes'] (same for work_email, mobile, hq_phone, street1, company_revenue, +# company_employee_count, linkedin_url, primary_industry) + +# Syncro customer pull (30-page guard, 100/page) +source .claude/scripts/syncro-env.sh +curl -s "${SYNCRO_BASE}/customers?per_page=100&page=${P}&api_key=${SYNCRO_API_KEY}" | tr -d '\000-\037' \ + | jq -r '.customers[] | [.id, (.business_name // .fullname // ""), ...] | @tsv' +# -> 3000 rows + +# Contacts pull — per_page is capped ~50 despite per_page=100; loop 20 pages +for P in $(seq 1 20); do curl -s ".../contacts?per_page=100&page=${P}&api_key=..." ; done +# -> 768 unique (matches meta.total_entries) + +# Activity check per matched customer +curl -s "${B}/tickets?customer_id=${CID}&per_page=100&api_key=${K}" # .meta.total_entries + max(.tickets[].created_at) +curl -s "${B}/invoices?customer_id=${CID}&per_page=100&api_key=${K}" # .meta.total_entries + max(.invoices[].date) +curl -s "${B}/customer_assets?customer_id=${CID}&per_page=100&..." # .assets | length +``` + +**Activity results for the 13 genuine matches:** + +| Lead | Company | Syncro cust | Tickets (last) | Invoices (last) | Assets | Verdict | +|---|---|---|---|---|---|---| +| 209 | Dataforth Corporation | 578095 | 207 (2026-07-17) | 232 (2026-07-17) | 50 | ACTIVE | +| 9 | SunDanzer Refrigeration | 139935 | 153 (2026-03-31) | 286 (2026-07-19) | 0 | ACTIVE | +| 107 | Patient Care Advocates | 29907453 | 15 (2025-09-19) | 33 (2026-07-01) | 0 | ACTIVE | +| 135 | Ralph's Transfer | 28715601 | 10 (2025-10-16) | 9 (2026-01-25) | 0 | ACTIVE | +| 62 | Termagon | 26038848 | 1 (2022-02-14) | 1 (2022-02-18) | 1 | FORMER | +| 120 | Earth Savers Energy | 16378562 | 4 (2022-12-27) | 3 (2019-06-04) | 0 | FORMER | +| 23 | Serrato Corporation | 24410171 | 1 (2021-07-19) | 0 | 0 | FORMER | +| 78 | Saguaro Solar | 21754858 | 1 (2020-10-19) | 1 (2020-10-20) | 0 | FORMER | +| 230 | Swaim Associates | 18118621 | 1 (2019-10-16) | 0 | 0 | FORMER | +| 37 | Mission Mgmt & Trust | 9778876 | 1 (2017-07-31) | 0 | 0 | FORMER | +| 35 | Aviva Children's Svcs | 2122768 | 0 | 1 (2015-04-10) | 0 | FORMER | +| 38 | Ironwood Tree Experience | 19057730 | 0 | 0 | 0 | FORMER (record only) | +| 61 | Marana Chamber | 17767946 | 0 | 0 | 0 | FORMER (record only) | + +**False-positive fuzzy matches (rejected):** TMM Family Services ~0.80 AMP Facility Services; +NTH Consulting ~0.79 SRK Consulting; Stark Electric ~0.83 Fiesta Electric. + +**Final classification:** + +``` +CALL LIST: 168 EXCLUDED: 70 + Tier A: 87 HEALTHCARE 18 + Tier B: 57 IN-HOUSE-IT 18 + Tier C: 24 MICRO 13 + former client: 9 NOT-AZ 10 + remote contact: 48 COMPETITOR 7 + unverified: 5 ACTIVE-CLIENT 4 +``` + +**WebFetch verifications that changed a classification:** + +| Company | Finding | Effect | +|---|---|---| +| Artec Industries | Off-road parts mfr, HQ North Salt Lake UT | -> EXCLUDE NOT-AZ | +| Urban Moment | Multifamily developer, Austin TX, no AZ ops | -> EXCLUDE NOT-AZ | +| Bluespan | Tucson ISP, fiber/wireless, 15k+ subs, own staff | -> EXCLUDE COMPETITOR | +| TracTru | SaaS/marketing platform, own devs + QA lead | -> EXCLUDE IN-HOUSE-IT | +| NTH Consulting | Tucson mgmt/enterprise-risk consulting, NOT IT | -> KEEP Tier A | +| The Carlson Company | Tucson plumbing/HVAC/sheet metal, 60+ yrs | -> KEEP Tier A | +| SWTDT | Southwest Truck Driver Training, Tucson + Phoenix | -> KEEP Tier A | +| Termagon | Tucson family pest control since 1995, multi-county | -> KEEP Tier A (former) | +| NeuroVRD | Tucson surgical-AI medical DEVICE maker, not provider | -> KEEP Tier B | +| Quick & Associates | Tucson CPA, 1331 N Wilmot Rd | -> KEEP Tier A | +| Oremus Material | Concrete, Chicago HQ w/ Tucson branch | -> Tier C GEO | + +## Pending / Incomplete Tasks + +- **BLOCKER for the setting firm: the source CSV has no phone numbers or emails.** Mike needs the + vendor to release the unlocked export. The vetted CSV carries `matchId` and `domain` per row so + the unlocked data can be joined back without redoing the vetting. +- **Open judgment call:** medical device makers/distributors (Wells Johnson #197, NeuroVRD #65, + Medfix International #29, Avery Therapeutics #15) were kept as non-covered-entities. Mike may + want them moved to EXCLUDED if he wants all healthcare-adjacent gone. +- **5 UNVERIFIED leads** need qualification before dialing: Third Act LLC / gvfan.com (#68), + Pallet Recyclers (#115), REPICON (#151), Open Enclose (#175), Southwest Solutions (#159). +- No wiki article implied (root-level session log) — no `/wiki-compile` needed. + +## Reference Information + +- Source file: `C:\Users\guru\Documents\Arizona Computer Guru New List.csv` (238 rows, 56 cols) +- Deliverables: `C:\Users\guru\Documents\ACG Call List - VETTED.csv`, + `C:\Users\guru\Documents\ACG Call List - EXCLUDED.csv` +- Rerunnable classifier: `scratchpad/classify.py` — edit the `C = {...}` dict (idx -> tier/reason/note) + and rerun to regenerate both CSVs +- Memory: `.claude/memory/feedback_prospect_list_vetting.md` +- Syncro API: `https://computerguru.syncromsp.com/api/v1` (read-only this session) +- Tier legend: `A` = priority call (right size/industry, owner-exec contact, local); + `B` = fits, lower priority; `C` = qualify first (geography, size, or unverified) +- Exclusion reasons: `ACTIVE-CLIENT`, `COMPETITOR`, `IN-HOUSE-IT`, `MICRO`, `HEALTHCARE`, `NOT-AZ`