← back to Commercialrealestate
licensed-agents + licensed: require name+firm match for broker contact (Cody gate) — name-alone join published the WRONG person's phone as fact; unconfirmed now falls back to DRE lookup
132b8b762f4bd4ae49fa1bde72aff77b2c3e5302 · 2026-08-18 18:35:06 -0700 · Steve Abrams
Files touched
M public/licensed-agents.htmlM public/licensed.html
Diff
commit 132b8b762f4bd4ae49fa1bde72aff77b2c3e5302
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Aug 18 18:35:06 2026 -0700
licensed-agents + licensed: require name+firm match for broker contact (Cody gate) — name-alone join published the WRONG person's phone as fact; unconfirmed now falls back to DRE lookup
---
public/licensed-agents.html | 17 ++++++++++-------
public/licensed.html | 14 +++++++++++---
2 files changed, 21 insertions(+), 10 deletions(-)
diff --git a/public/licensed-agents.html b/public/licensed-agents.html
index 9d27a2a..4aae10a 100644
--- a/public/licensed-agents.html
+++ b/public/licensed-agents.html
@@ -102,8 +102,9 @@ const esc=s=>String(s??'').replace(/[&<>"]/g,c=>({'&':'&','<':'<','>':'&g
// ── Agent CONTACT join (Steve HARD RULE 2026-08-18): the /api/agents/licensed feed carries NO
// phone/email, so join client-side against /api/brokers/all ({name,firm,phone,email,website})
-// by agent NAME (precise — that's the set these agents were derived from), firm as fallback.
-// Mirrors just-listed.html's brokerFor() + clean()/weburl() helpers. Never fabricates contact.
+// by agent NAME **confirmed by matching FIRM** — the agents (CA DRE) and brokers (portal
+// graph) are DIFFERENT populations that merely overlap on names, so name-alone mis-joins.
+// Mirrors just-listed.html's helpers. Never fabricates OR mis-attributes a contact.
const clean=v=>{const s=(v==null?'':String(v)).trim();return(!s||/^null/i.test(s))?null:s;}; // drop stringified "null"
const weburl=w=>{w=clean(w);return w?(/^https?:\/\//i.test(w)?w:'http://'+w):null;};
const nn=s=>String(s||'').toLowerCase().replace(/[^a-z0-9]+/g,''); // name key
@@ -125,14 +126,16 @@ async function loadContacts(){
});
}catch(e){}
}
-// Return the best contact record for an agent (prefer a name match that actually has contact).
+// Return a contact ONLY when we can trust it belongs to THIS agent (Cody gate 2026-08-18):
+// a name match is trustworthy only if the matched broker's FIRM also equals the agent's
+// brokerage — a name-alone match routinely hits a DIFFERENT person who shares the name, and
+// the old firm-only fallback glued one broker's number onto every agent at that firm. Both
+// published wrong contacts as fact. If the firm doesn't confirm, return null -> DRE fallback.
function contactFor(a){
if(!a) return null;
const bn=NAMEIDX[nn(a.agent)];
- if(bn && (bn.phone||bn.email)) return bn;
- const bf=FIRMIDX[normFirm(a.brokerage)];
- if(bf && (bf.phone||bf.email)) return bf;
- return bn||bf||null;
+ if(bn && (bn.phone||bn.email) && bn.firm && normFirm(bn.firm)===normFirm(a.brokerage)) return bn;
+ return null; // no firm-confirmed personal contact — caller falls back to the agent's DRE profile
}
// Always-visible phone + email markup. Falls back to the agent's DRE profile / directory when no contact exists.
function contactHtml(a){
diff --git a/public/licensed.html b/public/licensed.html
index 0beb703..472d646 100644
--- a/public/licensed.html
+++ b/public/licensed.html
@@ -94,11 +94,19 @@ let BROKERS={}; // normalized firm/name -> {name,firm,phone,email,website} from
const clean=v=>{const s=(v==null?'':String(v)).trim();return(!s||/^null/i.test(s))?null:s;};
const weburl=w=>{w=clean(w);return w?(/^https?:\/\//i.test(w)?w:'http://'+w):null;};
const normBk=s=>String(s||'').toLowerCase().replace(/[^a-z0-9]+/g,'').replace(/(inc|llc|corp|corporation|company|co|realty|realestate|group|properties|partners|associates)$/,'');
-// Match a directory row to a broker record: agents by firm then name; firm/title/escrow by name.
+// Match a directory row to a broker record. Cody gate 2026-08-18: an AGENT contact is trusted
+// ONLY when a broker's NAME **and** FIRM both agree — firm-first matching glued one broker's
+// personal number onto every agent at that firm (published wrong contact as fact). Unconfirmed
+// -> null -> DRE fallback. Firm/title/escrow rows are orgs: a broker at that org is a valid contact.
function brokerFor(r){
if(!r) return null;
- const keys = r.kind==='agent' ? [r.firm, r.name] : [r.name];
- for(const key of keys){ const k=normBk(key); if(k && BROKERS[k] && (BROKERS[k].phone||BROKERS[k].email)) return BROKERS[k]; }
+ if(r.kind==='agent'){
+ const b=BROKERS[normBk(r.name)];
+ if(b && (b.phone||b.email) && normBk(b.name)===normBk(r.name) && b.firm && normBk(b.firm)===normBk(r.firm)) return b;
+ return null;
+ }
+ const b=BROKERS[normBk(r.name)];
+ if(b && (b.phone||b.email)) return b;
return null;
}
// ALWAYS-visible phone + email. Real tel:/mailto: when we have a broker match; otherwise a
← 5d50017 CRCP filters: fan responsive filter-modal to deals-flow + co
·
back to Commercialrealestate
·
residential-brokers: add 3-view (grid/list/table) + all-fiel 3971e26 →