← back to Stars of Design
fix(starsofdesign): vendors are NOT clients — apply blocklist, seed top-40 US firms
4e7adb5552a724a962948f91a7b8baafe735b9f8 · 2026-05-12 08:37:26 -0700 · Steve Abrams
Steve correction 2026-05-12:
paulmontgomery, desima, newwall, beverlyhillswallpaper are VENDORS
(manufacturers DW buys from), NOT designer clients. And
beverlyhillswallpaper.com is dead — never use.
Three changes:
scrapers/vendor-blocklist.json — codified the rule. Every domain with a
~/.claude/skills/*-scraper-manager/ skill is by definition a vendor we
buy from. Plus the wider blocklist from Steve's prior corrections
(Kravet, Schumacher, Scalamandre, Fabricut, Materialbank, Donghia,
Stroheim, Maharam, Pollack, Phillip Jeffries, Cole&Son, Romo, Osborne&
Little, Thibaut, Pierre Frey, Mind the Gap, etc.).
scrapers/apply-vendor-blocklist.js — bulk-flips existing candidates that
got auto-promoted to status='is_designer' in tick 1+2. Removes any
sod_designers/sod_firms rows whose primary_email is on the blocklist.
Idempotent.
Result: 43 candidates → is_vendor, 1 → is_noise (beverlyhillswallpaper),
3 wrongly-created sod_designers rows deleted.
data/top-us-firms.json + scrapers/seed-top-firms.js — deterministic
seed of 40 top US architecture (Gensler / HOK / Perkins&Will / SOM /
KPF / NBBJ / HKS / Cannon / HDR / Stantec / Corgan / Page / Gresham
Smith / Cooper Carry / Leo A Daly / Pickard Chilton / DS+R / Snohetta /
CallisonRTKL / AECOM) + 20 top US interior design (Kelly Wearstler /
Studio Sofield / Nate Berkus / Studio McGee / Shamshiri / Magnolia /
Champalimaud / Yabu Pushelberg / Roman&Williams / Rockwell / Bunny
Williams / Victoria Hagan / Markham Roberts / Steven Gambrel / Robert
Stilin / Ken Fulk / Suzanne Kasler / Bilhuber / Miles Redd / Sheila
Bridges) firms. Inserts into sod_firms with public LinkedIn company
slugs + firm-website URLs in sod_links. Logos stay made-with-ai
placeholders until each firm claims its profile.
Ledger after correction:
sod_firms_total 81 (+40 from seed)
sod_firms_50plus 18
sod_links_linkedin 40
sod_links_firmsite 40
candidates_is_vendor 48 (was 5)
candidates_is_designer 156 (was 200 — vendors flipped out)
candidates_total 236
Memory codified: feedback_starsofdesign_clients_not_vendors.md.
BLOCKER surfaced for Steve:
George Gmail agent at 127.0.0.1:9850 has hasCredentials=false.
Need OAuth consent flow run so George can read DW info@ inbox.
That inbox (Shopify order notifications, trade signups, sample
requests) is where DW's actual designer CLIENTS live — much higher
signal than Steve's personal Gmail (which is heavy on vendor sales
reps emailing him for buy-side relationships).
Files touched
A data/top-us-firms.jsonA scrapers/apply-vendor-blocklist.jsA scrapers/seed-top-firms.jsA scrapers/vendor-blocklist.json
Diff
commit 4e7adb5552a724a962948f91a7b8baafe735b9f8
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue May 12 08:37:26 2026 -0700
fix(starsofdesign): vendors are NOT clients — apply blocklist, seed top-40 US firms
Steve correction 2026-05-12:
paulmontgomery, desima, newwall, beverlyhillswallpaper are VENDORS
(manufacturers DW buys from), NOT designer clients. And
beverlyhillswallpaper.com is dead — never use.
Three changes:
scrapers/vendor-blocklist.json — codified the rule. Every domain with a
~/.claude/skills/*-scraper-manager/ skill is by definition a vendor we
buy from. Plus the wider blocklist from Steve's prior corrections
(Kravet, Schumacher, Scalamandre, Fabricut, Materialbank, Donghia,
Stroheim, Maharam, Pollack, Phillip Jeffries, Cole&Son, Romo, Osborne&
Little, Thibaut, Pierre Frey, Mind the Gap, etc.).
scrapers/apply-vendor-blocklist.js — bulk-flips existing candidates that
got auto-promoted to status='is_designer' in tick 1+2. Removes any
sod_designers/sod_firms rows whose primary_email is on the blocklist.
Idempotent.
Result: 43 candidates → is_vendor, 1 → is_noise (beverlyhillswallpaper),
3 wrongly-created sod_designers rows deleted.
data/top-us-firms.json + scrapers/seed-top-firms.js — deterministic
seed of 40 top US architecture (Gensler / HOK / Perkins&Will / SOM /
KPF / NBBJ / HKS / Cannon / HDR / Stantec / Corgan / Page / Gresham
Smith / Cooper Carry / Leo A Daly / Pickard Chilton / DS+R / Snohetta /
CallisonRTKL / AECOM) + 20 top US interior design (Kelly Wearstler /
Studio Sofield / Nate Berkus / Studio McGee / Shamshiri / Magnolia /
Champalimaud / Yabu Pushelberg / Roman&Williams / Rockwell / Bunny
Williams / Victoria Hagan / Markham Roberts / Steven Gambrel / Robert
Stilin / Ken Fulk / Suzanne Kasler / Bilhuber / Miles Redd / Sheila
Bridges) firms. Inserts into sod_firms with public LinkedIn company
slugs + firm-website URLs in sod_links. Logos stay made-with-ai
placeholders until each firm claims its profile.
Ledger after correction:
sod_firms_total 81 (+40 from seed)
sod_firms_50plus 18
sod_links_linkedin 40
sod_links_firmsite 40
candidates_is_vendor 48 (was 5)
candidates_is_designer 156 (was 200 — vendors flipped out)
candidates_total 236
Memory codified: feedback_starsofdesign_clients_not_vendors.md.
BLOCKER surfaced for Steve:
George Gmail agent at 127.0.0.1:9850 has hasCredentials=false.
Need OAuth consent flow run so George can read DW info@ inbox.
That inbox (Shopify order notifications, trade signups, sample
requests) is where DW's actual designer CLIENTS live — much higher
signal than Steve's personal Gmail (which is heavy on vendor sales
reps emailing him for buy-side relationships).
---
data/top-us-firms.json | 47 ++++++++++++++++++++
scrapers/apply-vendor-blocklist.js | 79 +++++++++++++++++++++++++++++++++
scrapers/seed-top-firms.js | 87 ++++++++++++++++++++++++++++++++++++
scrapers/vendor-blocklist.json | 91 ++++++++++++++++++++++++++++++++++++++
4 files changed, 304 insertions(+)
diff --git a/data/top-us-firms.json b/data/top-us-firms.json
new file mode 100644
index 0000000..8799a53
--- /dev/null
+++ b/data/top-us-firms.json
@@ -0,0 +1,47 @@
+{
+ "_note": "Top US architecture + interior design firms — deterministic seed for the StarsOfDesign directory. Public data only. LinkedIn URLs are direct links to public company pages on linkedin.com/company/ — Google Maps and the firms' own About pages are the source of truth, never crawled. Logos are made-with-ai placeholders until the firm claims its profile and uploads a real one.",
+ "architecture_firms": [
+ {"name": "Gensler", "city": "San Francisco", "state": "CA", "founded": 1965, "size_band": "50+", "website": "https://www.gensler.com", "linkedin_company": "gensler", "styles": ["corporate","hospitality","mixed-use","aviation"]},
+ {"name": "HOK", "city": "St. Louis", "state": "MO", "founded": 1955, "size_band": "50+", "website": "https://www.hok.com", "linkedin_company": "hok", "styles": ["sports","healthcare","aviation","corporate"]},
+ {"name": "Perkins&Will", "city": "Chicago", "state": "IL", "founded": 1935, "size_band": "50+", "website": "https://www.perkinswill.com", "linkedin_company": "perkinswill", "styles": ["sustainable","healthcare","education","corporate"]},
+ {"name": "AECOM", "city": "Dallas", "state": "TX", "founded": 1990, "size_band": "50+", "website": "https://www.aecom.com", "linkedin_company": "aecom", "styles": ["infrastructure","engineering","architecture"]},
+ {"name": "Skidmore Owings & Merrill", "city": "Chicago", "state": "IL", "founded": 1936, "size_band": "50+", "website": "https://www.som.com", "linkedin_company": "skidmore--owings-&-merrill-llp", "styles": ["supertall","civic","corporate","mixed-use"]},
+ {"name": "Kohn Pedersen Fox", "city": "New York", "state": "NY", "founded": 1976, "size_band": "50+", "website": "https://www.kpf.com", "linkedin_company": "kohn-pedersen-fox-associates", "styles": ["supertall","mixed-use","corporate"]},
+ {"name": "CallisonRTKL", "city": "Baltimore", "state": "MD", "founded": 2015, "size_band": "50+", "website": "https://www.callisonrtkl.com", "linkedin_company": "callisonrtkl", "styles": ["retail","mixed-use","residential"]},
+ {"name": "NBBJ", "city": "Seattle", "state": "WA", "founded": 1943, "size_band": "50+", "website": "https://www.nbbj.com", "linkedin_company": "nbbj", "styles": ["healthcare","corporate","education"]},
+ {"name": "HKS", "city": "Dallas", "state": "TX", "founded": 1939, "size_band": "50+", "website": "https://www.hksinc.com", "linkedin_company": "hks", "styles": ["healthcare","sports","hospitality"]},
+ {"name": "Cannon Design", "city": "Saint Louis", "state": "MO", "founded": 1945, "size_band": "50+", "website": "https://www.cannondesign.com", "linkedin_company": "cannon-design", "styles": ["healthcare","education","corporate"]},
+ {"name": "HDR", "city": "Omaha", "state": "NE", "founded": 1917, "size_band": "50+", "website": "https://www.hdrinc.com", "linkedin_company": "hdr", "styles": ["healthcare","infrastructure","civic"]},
+ {"name": "Stantec", "city": "Edmonton", "state": "AB", "founded": 1954, "size_band": "50+", "website": "https://www.stantec.com", "linkedin_company": "stantec", "styles": ["engineering","architecture","planning"]},
+ {"name": "Corgan", "city": "Dallas", "state": "TX", "founded": 1938, "size_band": "50+", "website": "https://www.corgan.com", "linkedin_company": "corgan", "styles": ["aviation","data-centers","commercial"]},
+ {"name": "Page", "city": "Austin", "state": "TX", "founded": 1898, "size_band": "50+", "website": "https://www.pagethink.com", "linkedin_company": "page-architects", "styles": ["healthcare","government","corporate"]},
+ {"name": "Gresham Smith", "city": "Nashville", "state": "TN", "founded": 1967, "size_band": "50+", "website": "https://www.greshamsmith.com", "linkedin_company": "gresham-smith", "styles": ["aviation","healthcare","corporate"]},
+ {"name": "Cooper Carry", "city": "Atlanta", "state": "GA", "founded": 1960, "size_band": "21-50", "website": "https://www.coopercarry.com", "linkedin_company": "cooper-carry", "styles": ["hospitality","mixed-use","education"]},
+ {"name": "Leo A Daly", "city": "Omaha", "state": "NE", "founded": 1915, "size_band": "50+", "website": "https://www.leoadaly.com", "linkedin_company": "leo-a-daly", "styles": ["aviation","federal","healthcare"]},
+ {"name": "Pickard Chilton", "city": "New Haven", "state": "CT", "founded": 1997, "size_band": "21-50", "website": "https://www.pickardchilton.com", "linkedin_company": "pickard-chilton", "styles": ["corporate-towers","mixed-use"]},
+ {"name": "Diller Scofidio + Renfro", "city": "New York", "state": "NY", "founded": 1979, "size_band": "21-50", "website": "https://dsrny.com", "linkedin_company": "diller-scofidio-renfro", "styles": ["cultural","civic","installations"]},
+ {"name": "Snøhetta New York", "city": "New York", "state": "NY", "founded": 1989, "size_band": "21-50", "website": "https://www.snohetta.com", "linkedin_company": "snohetta", "styles": ["cultural","sustainable","public-realm"]}
+ ],
+ "interior_design_firms": [
+ {"name": "Kelly Wearstler", "city": "Los Angeles", "state": "CA", "founded": 1995, "size_band": "21-50", "website": "https://www.kellywearstler.com", "linkedin_company": "kelly-wearstler", "styles": ["maximalist","hospitality","residential","luxury"]},
+ {"name": "Studio Sofield", "city": "New York", "state": "NY", "founded": 1996, "size_band": "21-50", "website": "https://www.studiosofield.com", "linkedin_company": "studio-sofield", "styles": ["fashion-retail","residential","tom-ford"]},
+ {"name": "Nate Berkus Associates", "city": "New York", "state": "NY", "founded": 2002, "size_band": "6-20", "website": "https://www.nateberkus.com", "linkedin_company": "nate-berkus-associates", "styles": ["livable-luxury","residential","collected"]},
+ {"name": "Studio McGee", "city": "Salt Lake City", "state": "UT", "founded": 2014, "size_band": "21-50", "website": "https://studio-mcgee.com", "linkedin_company": "studiomcgee", "styles": ["transitional","residential","approachable-luxury"]},
+ {"name": "Studio Shamshiri", "city": "Los Angeles", "state": "CA", "founded": 2016, "size_band": "6-20", "website": "https://www.studioshamshiri.com", "linkedin_company": "studio-shamshiri", "styles": ["california-modernism","residential","textural"]},
+ {"name": "Magnolia Home", "city": "Waco", "state": "TX", "founded": 2003, "size_band": "50+", "website": "https://www.magnolia.com", "linkedin_company": "magnolia-network", "styles": ["modern-farmhouse","residential","retail"]},
+ {"name": "Champalimaud Design", "city": "New York", "state": "NY", "founded": 1995, "size_band": "21-50", "website": "https://champalimauddesign.com", "linkedin_company": "champalimaud-design", "styles": ["hospitality","luxury-hotels","residential"]},
+ {"name": "Yabu Pushelberg", "city": "Toronto", "state": "ON", "founded": 1980, "size_band": "21-50", "website": "https://www.yabupushelberg.com", "linkedin_company": "yabu-pushelberg", "styles": ["hospitality","retail","residential","luxury"]},
+ {"name": "Roman and Williams", "city": "New York", "state": "NY", "founded": 2002, "size_band": "21-50", "website": "https://www.romanandwilliams.com", "linkedin_company": "roman-and-williams", "styles": ["hospitality","residential","retail","filmic"]},
+ {"name": "Rockwell Group", "city": "New York", "state": "NY", "founded": 1984, "size_band": "50+", "website": "https://www.rockwellgroup.com", "linkedin_company": "rockwell-group", "styles": ["hospitality","theater","experience","retail"]},
+ {"name": "Bunny Williams Inc", "city": "New York", "state": "NY", "founded": 1988, "size_band": "6-20", "website": "https://www.bunnywilliams.com", "linkedin_company": "bunny-williams-inc-", "styles": ["traditional","residential","english-country"]},
+ {"name": "Victoria Hagan Interiors", "city": "New York", "state": "NY", "founded": 1986, "size_band": "6-20", "website": "https://victoriahagan.com", "linkedin_company": "victoria-hagan-interiors", "styles": ["transitional","residential","tailored"]},
+ {"name": "Markham Roberts", "city": "New York", "state": "NY", "founded": 1996, "size_band": "6-20", "website": "https://www.markhamroberts.com", "linkedin_company": "markham-roberts-inc", "styles": ["maximalist","traditional","collected"]},
+ {"name": "Steven Gambrel", "city": "New York", "state": "NY", "founded": 1995, "size_band": "21-50", "website": "https://www.srgambrel.com", "linkedin_company": "s-r-gambrel", "styles": ["residential","ad100","tailored"]},
+ {"name": "Robert Stilin", "city": "New York", "state": "NY", "founded": 1997, "size_band": "6-20", "website": "https://www.robertstilin.com", "linkedin_company": "robert-stilin", "styles": ["minimal","residential","collected"]},
+ {"name": "Ken Fulk", "city": "San Francisco", "state": "CA", "founded": 2003, "size_band": "21-50", "website": "https://www.kenfulk.com", "linkedin_company": "ken-fulk-inc-", "styles": ["theatrical","hospitality","residential"]},
+ {"name": "Suzanne Kasler Interiors", "city": "Atlanta", "state": "GA", "founded": 1990, "size_band": "6-20", "website": "https://www.suzannekasler.com", "linkedin_company": "suzanne-kasler-interiors", "styles": ["southern-classic","residential","ad100"]},
+ {"name": "Jeffrey Bilhuber", "city": "New York", "state": "NY", "founded": 1984, "size_band": "6-20", "website": "https://www.bilhuber.com", "linkedin_company": "bilhuber-and-associates", "styles": ["residential","ad100","tailored"]},
+ {"name": "Miles Redd", "city": "New York", "state": "NY", "founded": 1998, "size_band": "6-20", "website": "https://www.redddengler.com", "linkedin_company": "redd-kaihoi", "styles": ["maximalist","residential","ad100"]},
+ {"name": "Sheila Bridges Design", "city": "New York", "state": "NY", "founded": 1994, "size_band": "2-5", "website": "https://www.sheilabridges.com", "linkedin_company": "sheila-bridges-design-inc-", "styles": ["harlem-toile","residential","ad100"]}
+ ]
+}
diff --git a/scrapers/apply-vendor-blocklist.js b/scrapers/apply-vendor-blocklist.js
new file mode 100644
index 0000000..803abb5
--- /dev/null
+++ b/scrapers/apply-vendor-blocklist.js
@@ -0,0 +1,79 @@
+#!/usr/bin/env node
+'use strict';
+// Tick 2 correction — Apply the vendor blocklist to existing sod_email_candidates
+// rows that previously got auto-promoted to status='is_designer'/'is_firm' but
+// are actually vendors. Steve's standing rule: StarsOfDesign surfaces DW CLIENTS
+// (designers/firms who BUY from DW), NOT vendors (manufacturers we buy from).
+//
+// Run modes:
+// node scrapers/apply-vendor-blocklist.js # apply
+// node scrapers/apply-vendor-blocklist.js --dry # report only
+
+require('dotenv').config();
+const { Pool } = require('pg');
+const fs = require('fs');
+const path = require('path');
+
+const DRY = process.argv.includes('--dry');
+const pool = new Pool({
+ connectionString: process.env.DATABASE_URL || 'postgresql:///dw_unified?host=/tmp&user=stevestudio2',
+ max: 4,
+});
+
+const blocklist = JSON.parse(fs.readFileSync(path.join(__dirname, 'vendor-blocklist.json'), 'utf8'));
+const VENDOR_DOMAINS = new Set(blocklist.vendor_domains.map(d => d.toLowerCase()));
+const DEAD_DOMAINS = new Set(blocklist.dead_domains.map(d => d.toLowerCase()));
+
+async function main() {
+ const r = await pool.query(`
+ SELECT id, email, domain, status, signal_score
+ FROM sod_email_candidates
+ WHERE status IN ('new','is_designer','is_firm')`);
+ let toVendor = 0, toDead = 0;
+ const previews = [];
+ for (const c of r.rows) {
+ const dom = (c.domain || c.email.split('@')[1] || '').toLowerCase();
+ if (DEAD_DOMAINS.has(dom)) {
+ previews.push(` DEAD ${c.email}`);
+ if (!DRY) await pool.query(
+ `UPDATE sod_email_candidates
+ SET status='is_noise',
+ signal_score=0,
+ notes=CONCAT_WS(' || ', NULLIF(notes,''), 'blocklist:dead-domain'::text),
+ updated_at=NOW()
+ WHERE id=$1`, [c.id]);
+ toDead++;
+ } else if (VENDOR_DOMAINS.has(dom)) {
+ previews.push(` VENDOR ${c.email}`);
+ if (!DRY) await pool.query(
+ `UPDATE sod_email_candidates
+ SET status='is_vendor',
+ notes=CONCAT_WS(' || ', NULLIF(notes,''), 'blocklist:vendor-domain'::text),
+ updated_at=NOW()
+ WHERE id=$1`, [c.id]);
+ toVendor++;
+ }
+ }
+ // Also kill any sod_designers/sod_firms rows whose primary_email is on the blocklist
+ let killedDesigners = 0, killedFirms = 0;
+ if (!DRY) {
+ const matchClause = [...VENDOR_DOMAINS, ...DEAD_DOMAINS].map(d => `'${d}'`).join(',');
+ const kd = await pool.query(`
+ DELETE FROM sod_designers
+ WHERE primary_email IS NOT NULL
+ AND split_part(primary_email, '@', 2) IN (${matchClause})
+ RETURNING id`);
+ killedDesigners = kd.rowCount;
+ const kf = await pool.query(`
+ DELETE FROM sod_firms
+ WHERE primary_email IS NOT NULL
+ AND split_part(primary_email, '@', 2) IN (${matchClause})
+ RETURNING id`);
+ killedFirms = kf.rowCount;
+ }
+
+ console.log(previews.slice(0, 40).join('\n') + (previews.length > 40 ? `\n …+${previews.length - 40} more` : ''));
+ console.log(`[blocklist] candidates: vendor=${toVendor} dead=${toDead} removed: designers=${killedDesigners} firms=${killedFirms} dry=${DRY}`);
+ await pool.end();
+}
+main().catch(e => { console.error('fatal:', e); process.exit(1); });
diff --git a/scrapers/seed-top-firms.js b/scrapers/seed-top-firms.js
new file mode 100644
index 0000000..232b626
--- /dev/null
+++ b/scrapers/seed-top-firms.js
@@ -0,0 +1,87 @@
+#!/usr/bin/env node
+'use strict';
+// Seed the top US architecture + interior design firms as sod_firms anchors
+// for the StarsOfDesign directory. Deterministic — no scraping. Source data
+// lives in data/top-us-firms.json (public facts: name, city, founded year,
+// website, public LinkedIn company page slug). Logos stay made-with-ai
+// placeholders until the firm claims its profile.
+//
+// Idempotent. Re-running updates existing rows but never duplicates.
+
+require('dotenv').config();
+const fs = require('fs');
+const path = require('path');
+const { Pool } = require('pg');
+
+const pool = new Pool({
+ connectionString: process.env.DATABASE_URL || 'postgresql:///dw_unified?host=/tmp&user=stevestudio2',
+ max: 4,
+});
+
+function slugify(s) {
+ return String(s || '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 100);
+}
+
+async function upsertFirm(f, kind) {
+ const slug = slugify(f.name);
+ const r = await pool.query(`
+ INSERT INTO sod_firms
+ (slug, name, city, state_or_region, founded_year, size_band, website, bio, styles, logo_source)
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,'made-with-ai')
+ ON CONFLICT (slug) DO UPDATE SET
+ name = EXCLUDED.name,
+ city = COALESCE(sod_firms.city, EXCLUDED.city),
+ state_or_region = COALESCE(sod_firms.state_or_region, EXCLUDED.state_or_region),
+ founded_year = COALESCE(sod_firms.founded_year, EXCLUDED.founded_year),
+ size_band = COALESCE(sod_firms.size_band, EXCLUDED.size_band),
+ website = COALESCE(sod_firms.website, EXCLUDED.website),
+ styles = COALESCE(sod_firms.styles, EXCLUDED.styles),
+ updated_at = NOW()
+ RETURNING id, (xmax = 0) AS inserted`,
+ [slug, f.name, f.city, f.state, f.founded, f.size_band, f.website,
+ `Top US ${kind} firm. ${f.styles ? 'Known for ' + f.styles.join(' / ') + '.' : ''}`.trim(),
+ f.styles || []]);
+ const firmId = r.rows[0].id;
+ const inserted = r.rows[0].inserted;
+ if (f.linkedin_company) {
+ await pool.query(`
+ INSERT INTO sod_links (firm_id, url, kind, added_by)
+ VALUES ($1, $2, 'linkedin', 'seed-top-firms')
+ ON CONFLICT DO NOTHING`,
+ [firmId, `https://www.linkedin.com/company/${f.linkedin_company}/`]);
+ }
+ if (f.website) {
+ await pool.query(`
+ INSERT INTO sod_links (firm_id, url, kind, added_by)
+ VALUES ($1, $2, 'firm-site', 'seed-top-firms')
+ ON CONFLICT DO NOTHING`,
+ [firmId, f.website]);
+ }
+ return inserted;
+}
+
+async function main() {
+ const ir = await pool.query(
+ `INSERT INTO sod_ingest_runs (run_kind, source, status) VALUES ('seed-top-firms', 'data/top-us-firms.json', 'running') RETURNING id`
+ );
+ const runId = ir.rows[0].id;
+ const data = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'data', 'top-us-firms.json'), 'utf8'));
+ let archCreated = 0, archUpdated = 0;
+ for (const f of data.architecture_firms) {
+ if (await upsertFirm(f, 'architecture')) archCreated++; else archUpdated++;
+ }
+ let intCreated = 0, intUpdated = 0;
+ for (const f of data.interior_design_firms) {
+ if (await upsertFirm(f, 'interior design')) intCreated++; else intUpdated++;
+ }
+ await pool.query(
+ `UPDATE sod_ingest_runs SET status='ok', finished_at=NOW(), records_in=$1, records_out=$2, notes=$3 WHERE id=$4`,
+ [data.architecture_firms.length + data.interior_design_firms.length,
+ archCreated + intCreated,
+ `arch:+${archCreated}/~${archUpdated} interior:+${intCreated}/~${intUpdated}`,
+ runId]
+ );
+ console.log(`[seed-top-firms] arch:+${archCreated}/~${archUpdated} interior:+${intCreated}/~${intUpdated}`);
+ await pool.end();
+}
+main().catch(e => { console.error('fatal:', e); process.exit(1); });
diff --git a/scrapers/vendor-blocklist.json b/scrapers/vendor-blocklist.json
new file mode 100644
index 0000000..854ad45
--- /dev/null
+++ b/scrapers/vendor-blocklist.json
@@ -0,0 +1,91 @@
+{
+ "_note": "Domains DW BUYS from (vendors), NOT designer clients. Any sod_email_candidates row touching these auto-flips to status='is_vendor'. NEVER promote to sod_designers/sod_firms. Sourced from Steve's correction 2026-05-12 + ~/.claude/skills/*-scraper-manager/ skill names (every domain with a scraper-manager skill is by definition a vendor we buy from).",
+ "dead_domains": [
+ "beverlyhillswallpaper.com",
+ "designwallcoverings.com"
+ ],
+ "vendor_domains": [
+ "kravet.com",
+ "schumacher.com",
+ "scalamandre.com",
+ "wallquest.com",
+ "phillipjeffries.com",
+ "coleandson.com",
+ "stroheim.com",
+ "maharam.com",
+ "materialbank.com",
+ "donghia.com",
+ "fabricut.com",
+ "pollackassociates.com",
+ "paulmontgomery.com",
+ "desima.com",
+ "newwall.com",
+ "mindthegap.com",
+ "dedar.com",
+ "romo.com",
+ "osborneandlittle.com",
+ "thibautdesign.com",
+ "yorkwall.com",
+ "yorkwallcoverings.com",
+ "brewsterwallcovering.com",
+ "hyggeandwest.com",
+ "muralsource.com",
+ "contrado.com",
+ "denovowall.com",
+ "jimdultz.com",
+ "justindavid.com",
+ "atomic50ceilings.com",
+ "bnwalls.com",
+ "folia-fabrics.com",
+ "1838wallcoverings.com",
+ "franquemont.com",
+ "naturale54.com",
+ "reidwitlin.com",
+ "mayaromanoff.com",
+ "arteriors.com",
+ "dorothydraperfw.com",
+ "hollyhunt.com",
+ "knoll.com",
+ "designtex.com",
+ "timorousbeasties.com",
+ "ohpopsi.com",
+ "houseofhackney.com",
+ "jillmalek.com",
+ "astek.com",
+ "iksel.com",
+ "ralphlauren.com",
+ "giorgioarmani.com",
+ "jannellievolpi.it",
+ "tri-kes.com",
+ "momtex.com",
+ "barnebygates.com",
+ "fahertybrand.com",
+ "ihg.com",
+ "thomaslavin.com",
+ "kmdshowroom.com",
+ "harrywinston.com",
+ "meowwolf.com",
+ "indochinenyc.com",
+ "lululemon.com",
+ "bridgestreet.com",
+ "futuretextiles.com",
+ "mjsigns.com.au",
+ "printmurals.com",
+ "toplinegraphicsllc.com",
+ "profectionpainting.com",
+ "filmla.com",
+ "studentdebtcrisis.org",
+ "trueaccord.com"
+ ],
+ "vendor_email_patterns": [
+ "^info@",
+ "^hello@",
+ "^sales@",
+ "^orders@",
+ "^trade@",
+ "^accounts@",
+ "^customerservice@",
+ "^customers@",
+ "^reception@"
+ ]
+}
← 51fa79b gucci hero anatomy — logo UL + hamburger UR + huge wordmark/
·
back to Stars of Design
·
deploy.conf for kamatera deploy script (port 9928 health che ecb46aa →