[object Object]

← back to Commercialrealestate

TK-10703 cycle7: contrarian-gate fixes — robust address normalizer (split on comma OR 2+ spaces so LA Assessor double-space addresses join; property contact strip was always-off), all-digit multi-address guard (no wrong-join), linkedin esc() escapes ' and >

1d2233c616103812c14c10b05de9795533e23535 · 2026-08-21 07:07:46 -0700 · Steve Abrams

Files touched

Diff

commit 1d2233c616103812c14c10b05de9795533e23535
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Fri Aug 21 07:07:46 2026 -0700

    TK-10703 cycle7: contrarian-gate fixes — robust address normalizer (split on comma OR 2+ spaces so LA Assessor double-space addresses join; property contact strip was always-off), all-digit multi-address guard (no wrong-join), linkedin esc() escapes ' and >
---
 public/direct-listings.html |  9 +++++++--
 public/linkedin.html        |  2 +-
 public/property.html        | 12 +++++++++---
 3 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/public/direct-listings.html b/public/direct-listings.html
index 9d9384f..b6a5ff4 100644
--- a/public/direct-listings.html
+++ b/public/direct-listings.html
@@ -141,12 +141,17 @@ const tog=(s,v)=>{s.has(v)?s.delete(v):s.add(v);};
 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;};
 // address normalizer — street-only (drop the ", City, CA ZIP" tail) then strip street-type + directional words
-const na=s=>String(s||'').split(',')[0].toLowerCase().replace(/\b(street|st|avenue|ave|boulevard|blvd|road|rd|drive|dr|lane|ln|place|pl|court|ct|suite|ste|unit|#|north|south|east|west|n|s|e|w)\b/g,'').replace(/[^a-z0-9]+/g,'');
+// Split on the FIRST comma OR run of 2+ spaces (robust to both the listing ", City, CA ZIP" tail and
+// the Assessor "6651 BECK AVE  LOS ANGELES CA  91606" double-space tail), then strip street-type words.
+const na=s=>String(s||'').split(/,|\s{2,}/)[0].toLowerCase().replace(/\b(street|st|avenue|ave|boulevard|blvd|road|rd|drive|dr|lane|ln|place|pl|court|ct|suite|ste|unit|#|north|south|east|west|n|s|e|w)\b/g,'').replace(/[^a-z0-9]+/g,'');
+// A multi-address range ("1704, 1710, 1728 N Sierra Bonita St…") truncates to a bare number → ambiguous,
+// would wrong-join. Reject all-digit keys (better a "via source" fallback than the WRONG agent — TK-10703).
+const naOk=k=>!!k && !/^\d+$/.test(k);
 const srcClass=s=>String(s||'').replace(/[^a-z]/g,'');
 
 // ── broker contact join (Steve HARD RULE: always show broker phone + email) ──
 // The authoritative, NON-FABRICATED source is /api/listing-brokers joined by property address.
-function brokerFor(r){ if(!r) return null; return ADDRC[na(r.address)]||null; }
+function brokerFor(r){ if(!r) return null; const k=na(r.address); return naOk(k)?(ADDRC[k]||null):null; }
 
 // ── COLUMN MODEL — header, cells, sort comparator, picker, + ThreeView fields ──
 const COLS=[
diff --git a/public/linkedin.html b/public/linkedin.html
index 2358312..eef78cb 100644
--- a/public/linkedin.html
+++ b/public/linkedin.html
@@ -117,7 +117,7 @@ try{const _o=localStorage.getItem('frank_li_followed');if(_o!==null&&localStorag
 let followed=new Set(JSON.parse(localStorage.getItem(FOLLOW_KEY)||'[]'));
 let people=[], filt='all', q='', sortKey='listings', sortDir=-1;
 const atype=t=>t==='residential'?'<span class="atype res">Residential</span>':'<span class="atype com">Commercial</span>';
-const esc=s=>(s==null?'':String(s)).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/"/g,'&quot;');
+const esc=s=>(s==null?'':String(s)).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;');
 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 bnorm=s=>String(s||'').toLowerCase().replace(/[^a-z0-9]+/g,'').replace(/(inc|llc|corp|corporation|company|co|realty|realestate|group|properties|partners|associates)$/,'');
diff --git a/public/property.html b/public/property.html
index f2f681a..075fd8d 100644
--- a/public/property.html
+++ b/public/property.html
@@ -54,7 +54,13 @@ let _seq=0;   // guards a stale slow response from overwriting a newer lookup
 //    real listing agent exists for this address). NON-FABRICATION: the ONLY source is
 //    /api/listing-brokers → rows[]{address,name,phone,email,website}, joined on the NORMALIZED
 //    address (authoritative). No address match → source/DRE fallback link, NEVER an invented number.
-const na=s=>String(s||'').toLowerCase().replace(/\b(street|st|avenue|ave|boulevard|blvd|road|rd|drive|dr|lane|ln|place|pl|court|ct|suite|ste|unit|#|north|south|east|west|n|s|e|w)\b/g,'').replace(/[^a-z0-9]+/g,'');
+// Normalize to a street-only key. Split on the FIRST comma OR run of 2+ spaces so it handles BOTH
+// the listing-brokers "6651 Beck" form AND the LA Assessor "6651 BECK AVE  LOS ANGELES CA  91606"
+// double-space form (comma-only stripping never matched the Assessor tail — TK-10703 contrarian fix).
+const na=s=>String(s||'').split(/,|\s{2,}/)[0].toLowerCase().replace(/\b(street|st|avenue|ave|boulevard|blvd|road|rd|drive|dr|lane|ln|place|pl|court|ct|suite|ste|unit|#|north|south|east|west|n|s|e|w)\b/g,'').replace(/[^a-z0-9]+/g,'');
+// A multi-address range ("1704, 1710, 1728 N Sierra Bonita St…") truncates to a bare number → ambiguous,
+// would wrong-join. Reject all-digit keys (better a missing contact than the WRONG agent's number).
+const naOk=k=>!!k && !/^\d+$/.test(k);
 const cleanv=v=>{const s=(v==null?'':String(v)).trim();return(!s||/^null/i.test(s))?null:s;};
 const weburl=w=>{w=cleanv(w);return w?(/^https?:\/\//i.test(w)?w:'http://'+w):null;};
 const ADDRC={};   // normalized address -> {name,phone,email,website}
@@ -63,7 +69,7 @@ window.ADDRC=ADDRC; window.na=na;   // exposed for the TK-10703 verification har
   try{
     const lb=await api('/api/listing-brokers');
     ((lb&&lb.rows)||[]).forEach(row=>{
-      const k=na(row.address); if(!k)return;
+      const k=na(row.address); if(!naOk(k))return;
       const ph=cleanv(row.phone),em=cleanv(row.email);
       if(!ADDRC[k] || ((ph||em) && !(ADDRC[k].phone||ADDRC[k].email)))
         ADDRC[k]={name:cleanv(row.name),phone:ph,email:em,website:weburl(row.website)};
@@ -72,7 +78,7 @@ window.ADDRC=ADDRC; window.na=na;   // exposed for the TK-10703 verification har
 })();
 // Build the always-on contact strip for a rendered property (never fabricates).
 function contactStrip(p){
-  const b=ADDRC[na(p&&p.address)];
+  const pk=na(p&&p.address); const b=naOk(pk)?ADDRC[pk]:null;
   if(b && (b.phone||b.email)){
     const ph=b.phone?`<a href="tel:${esc(b.phone.replace(/[^0-9+]/g,''))}">📞 ${esc(b.phone)}</a>`:'<span class="mut">phone via source ↗</span>';
     const em=b.email?`<a href="mailto:${esc(b.email)}">✉️ ${esc(b.email)}</a>`:'<span class="mut">email via source ↗</span>';

← 05883e8 TK-10703 cycle7: linkedin → CRCP standard (List/Grid view to  ·  back to Commercialrealestate  ·  TK-10703 cycle7: commit the direct-listings + linkedin verif 0cff5ac →