[object Object]

← back to Govarbitrage

agents: drop financial-advisory/wealth firms from CRE finder

2d441932f06e4cb83cb5bc1de3506105fac18593 · 2026-07-27 21:23:40 -0700 · Steve Abrams

The broker/finance verticals overlap, so a Places text search for
'commercial real estate broker' surfaces wealth-management, financial-
advisory and securities firms. Their names carry finance-ambiguous
tokens (investments/advisors/capital markets) that also satisfy the
generic COMMERCIAL_RE keep, so 'Sterling Financial Advisors',
'Vanguard Investment Advisors', 'Edward Jones Investments' etc. were
wrongly kept as CRE brokers (the finance type-gate only catches
Google-tagged finance; many wealth firms come back as establishment/POI).

Add a financial-advisory name gate that runs before the commercial keep:
a wealth/securities/financial firm is dropped UNLESS it carries an
unambiguous real-estate anchor (real estate/realty/CRE/properties/
leasing/net lease/investment sales/commercial) or an allowlisted CRE
brand. 'investment sales' stays a CRE anchor so legit investment-sales
brokers survive. +18 offline regression tests, 131/131, tsc+eslint clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 2d441932f06e4cb83cb5bc1de3506105fac18593
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Mon Jul 27 21:23:40 2026 -0700

    agents: drop financial-advisory/wealth firms from CRE finder
    
    The broker/finance verticals overlap, so a Places text search for
    'commercial real estate broker' surfaces wealth-management, financial-
    advisory and securities firms. Their names carry finance-ambiguous
    tokens (investments/advisors/capital markets) that also satisfy the
    generic COMMERCIAL_RE keep, so 'Sterling Financial Advisors',
    'Vanguard Investment Advisors', 'Edward Jones Investments' etc. were
    wrongly kept as CRE brokers (the finance type-gate only catches
    Google-tagged finance; many wealth firms come back as establishment/POI).
    
    Add a financial-advisory name gate that runs before the commercial keep:
    a wealth/securities/financial firm is dropped UNLESS it carries an
    unambiguous real-estate anchor (real estate/realty/CRE/properties/
    leasing/net lease/investment sales/commercial) or an allowlisted CRE
    brand. 'investment sales' stays a CRE anchor so legit investment-sales
    brokers survive. +18 offline regression tests, 131/131, tsc+eslint clean.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 src/lib/places.test.ts | 48 +++++++++++++++++++++++++++++++++++++++
 src/lib/places.ts      | 61 ++++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 102 insertions(+), 7 deletions(-)

diff --git a/src/lib/places.test.ts b/src/lib/places.test.ts
index abb9f35..b6daebc 100644
--- a/src/lib/places.test.ts
+++ b/src/lib/places.test.ts
@@ -153,6 +153,54 @@ describe("isCommercial — DROP on off-topic Google place types (regardless of n
   });
 });
 
+// Regression: the "broker" and "finance" verticals overlap, so a Places text
+// search for "commercial real estate broker" surfaces WEALTH / FINANCIAL-
+// ADVISORY / securities firms. Their names carry finance-ambiguous tokens
+// ("investments", "advisors", "capital markets") that ALSO satisfy the generic
+// COMMERCIAL_RE keep — so before the fix, "Sterling Financial Advisors" and
+// "Vanguard Investment Advisors" were WRONGLY kept as CRE brokers. Now a
+// financial-advisory name with NO real-estate anchor is dropped, while a legit
+// CRE broker (which carries an RE anchor or an allowlisted brand) is preserved.
+describe("isCommercial — DROP on financial-advisory / wealth firms with no RE anchor", () => {
+  it.each([
+    "Morgan Stanley Wealth Investments",
+    "Sterling Financial Advisors",
+    "Vanguard Investment Advisors",
+    "Edward Jones Investments",
+    "Pinnacle Wealth Advisors",
+    "Fidelity Asset Management",
+    "Prudential Insurance Advisors",
+    "Cornerstone Wealth Management",
+    "Cambridge Investment Management",
+    "Heritage Retirement Advisors",
+  ])("drops finance firm %s (no real-estate anchor)", (name) => {
+    expect(isCommercial(place(name))).toBe(false);
+  });
+
+  it.each([
+    // A real-estate anchor rescues a finance-ambiguous name — these are real CRE.
+    "Net Lease Advisors", // "net lease" + "leasing" anchor
+    "Metro CRE Advisors", // "CRE" anchor
+    "Apex Investment Sales", // "investment sales" is the CRE-specific phrase
+    "Harbor Income Properties", // "properties" anchor
+    "Commercial Real Estate Investment Advisors", // "real estate" anchor
+    "Summit Capital Markets", // capital markets alone isn't a finance-drop token
+  ])("keeps CRE broker %s despite a finance-ambiguous word", (name) => {
+    expect(isCommercial(place(name))).toBe(true);
+  });
+
+  it("keeps an allowlisted CRE brand even when the name carries a finance word", () => {
+    // Brand allowlist overrides the financial-advisory drop.
+    expect(isCommercial(place("CBRE Investment Management"))).toBe(true);
+    expect(isCommercial(place("JLL Capital Markets"))).toBe(true);
+  });
+
+  it("still keeps a plainly-commercial broker with no finance word at all", () => {
+    // Guard against the finance gate over-reaching into ordinary CRE names.
+    expect(isCommercial(place("Downtown Commercial Leasing"))).toBe(true);
+  });
+});
+
 describe("isCommercial — DROP on residential-flavored place types", () => {
   it.each(["apartment_complex", "home_goods_store"])("drops type %s with no commercial name", (type) => {
     expect(isCommercial(place("Parkview", [type]))).toBe(false);
diff --git a/src/lib/places.ts b/src/lib/places.ts
index 962f3f6..7c4b9bf 100644
--- a/src/lib/places.ts
+++ b/src/lib/places.ts
@@ -163,11 +163,48 @@ function isOffTopicType(t: string): boolean {
 // reason as above). Complements the off-topic blocklist for apartment/home mis-tags.
 const RESIDENTIAL_TYPES = new Set(["apartment_complex", "home_goods_store"]);
 
+// FINANCIAL-ADVISORY / wealth / securities name signals — the "broker" and
+// "finance" verticals overlap, so a Places text search for "commercial real
+// estate broker" reliably surfaces wealth-management, financial-advisory and
+// securities firms. Google's `finance` type-gate only catches the ones Google
+// explicitly tagged finance; many advisory firms come back tagged
+// `establishment`/`point_of_interest` and slip past it. These names carry the
+// finance-ambiguous tokens ("investments", "advisors", "capital markets") that
+// ALSO appear in legit CRE names, so a firm matching this is dropped UNLESS it
+// also carries an unambiguous real-estate anchor (RE_ANCHOR_RE, below).
+// Note: "investment sales" is a CRE-specific phrase and is EXCLUDED here (it's a
+// real-estate anchor, below) — only bare "investment(s)" / "investment advisors"
+// (with no RE anchor) reads as a wealth firm. The negative lookahead on
+// "investment" avoids matching "investment sales".
+const FINANCIAL_ADVISORY_RE =
+  /\b(wealth|financial\s+advisors?|financial\s+planning|financial\s+services|financial\s+group|securities|asset\s+management|wealth\s+management|investment\s+management|investment\s+advisors?|investments?(?!\s+sales)|private\s+equity|hedge\s+fund|insurance|retirement|portfolio\s+management|mutual\s+funds?|stockbrokers?|401k?)\b/i;
+
+// Unambiguous REAL-ESTATE anchor words. Presence of one proves a
+// finance-ambiguous name (e.g. "Investment Sales Advisors") is genuinely a
+// commercial-RE broker and not a wealth firm, so it survives the financial-
+// advisory drop. "real estate" / "realty" / "property/properties" / "leasing"
+// are the reliable RE anchors; "CRE" and "commercial real estate" too.
+const RE_ANCHOR_RE =
+  /\b(real\s+estate|realty|CRE|propert(?:y|ies)|leasing|net\s+lease|tenant\s+rep(?:resentation)?|multifamily|land\s+brokerage|investment\s+sales|commercial)\b/i;
+
 function hasCommercialBrand(name: string): boolean {
   const n = name.toLowerCase();
   return COMMERCIAL_BRANDS.some((b) => n.includes(b));
 }
 
+// True when the name reads as a financial-advisory / wealth firm that lacks a
+// real-estate anchor — i.e. a finance false-positive we must drop even though a
+// bare finance-ambiguous token (investments/advisors/capital markets) would
+// otherwise satisfy COMMERCIAL_RE. An allowlisted CRE brand always overrides
+// (a real brokerage that happens to include "insurance" etc. in its name stays).
+function isFinancialAdvisoryNoRE(name: string): boolean {
+  return (
+    FINANCIAL_ADVISORY_RE.test(name) &&
+    !RE_ANCHOR_RE.test(name) &&
+    !hasCommercialBrand(name)
+  );
+}
+
 // --- Commercial-relevance ranking weight ----------------------------------
 // Every result in the ranked list has ALREADY passed isCommercial() (precision
 // gate), but they're not equally strong CRE-broker matches. A listing that is
@@ -312,12 +349,17 @@ function locationString(city?: string, state?: string, zip?: string): string {
 // evidence (a strong name signal or an allowlisted brokerage brand) to keep a
 // result, instead of defaulting to keep. Apply order:
 //   1. Off-topic Google type (travel/lodging/storage/…) → DROP regardless.
-//   2. Strong commercial name signal OR allowlisted brand → KEEP (wins over
+//   2. Financial-advisory / wealth firm WITHOUT a real-estate anchor → DROP.
+//      (Runs before the commercial keep because finance names carry ambiguous
+//      "investments"/"advisors"/"capital markets" tokens that would otherwise
+//      satisfy COMMERCIAL_RE — e.g. "Sterling Financial Advisors" is a wealth
+//      firm, not a CRE broker. An allowlisted CRE brand overrides this.)
+//   3. Strong commercial name signal OR allowlisted brand → KEEP (wins over
 //      any residential signal, e.g. "Commercial Realty Advisors").
-//   3. Residential / irrelevant name signal → DROP.
-//   4. Residential-flavored place type → DROP.
-//   5. Generic real-estate result with no commercial evidence → DROP.
-//   6. Non-real-estate result with no signal either way → DROP (nothing kept it).
+//   4. Residential / irrelevant name signal → DROP.
+//   5. Residential-flavored place type → DROP.
+//   6. Generic real-estate result with no commercial evidence → DROP.
+//   7. Non-real-estate result with no signal either way → DROP (nothing kept it).
 export function isCommercial(p: RawPlace): boolean {
   const name = p.displayName?.text ?? "";
   const types = [p.primaryType ?? "", ...(p.types ?? [])];
@@ -328,7 +370,12 @@ export function isCommercial(p: RawPlace): boolean {
 
   const hasResidentialType = types.some((t) => RESIDENTIAL_TYPES.has(t.toLowerCase()));
 
-  // 2. Positive commercial evidence wins over everything below.
+  // 2. Financial-advisory / wealth / securities firm with no real-estate anchor →
+  //    drop. Guards against the broker↔finance vertical overlap that surfaces
+  //    wealth firms on a "commercial real estate broker" text search.
+  if (isFinancialAdvisoryNoRE(name)) return false;
+
+  // 3. Positive commercial evidence wins over everything below.
   if (COMMERCIAL_RE.test(name) || hasCommercialBrand(name)) return true;
 
   // 3. Explicit residential / irrelevant name → drop.
@@ -337,7 +384,7 @@ export function isCommercial(p: RawPlace): boolean {
   // 4. Residential-flavored place type with no commercial evidence → drop.
   if (hasResidentialType) return false;
 
-  // 5 & 6. No commercial evidence at all — whether or not it's a generic
+  // 6 & 7. No commercial evidence at all — whether or not it's a generic
   // real_estate_agency, we can't confirm it's commercial, so drop it.
   // (Precision > recall: the page promises commercial-only.)
   return false;

← c53b1f2 agents: relevance-weighted ranking — surface stronger CRE ma  ·  back to Govarbitrage  ·  auto-save: 2026-07-27T21:24:43 (1 files) — tsconfig.tsbuildi a2ed0e6 →