[object Object]

← back to Rentv

fix(deals): drop an implausibly small SF (<100 sf/unit) next to a unit count — a body-leaked per-unit avg/amenity size, not the asset total ('406 units · 669 SF' → '406 units')

72561ee456f887e8b7c42d1ae1c7112b76641758 · 2026-08-06 07:58:56 -0700 · Steve

Files touched

Diff

commit 72561ee456f887e8b7c42d1ae1c7112b76641758
Author: Steve <steve@designerwallcoverings.com>
Date:   Thu Aug 6 07:58:56 2026 -0700

    fix(deals): drop an implausibly small SF (<100 sf/unit) next to a unit count — a body-leaked per-unit avg/amenity size, not the asset total ('406 units · 669 SF' → '406 units')
---
 scripts/lib/deal-parse.mjs | 12 ++++++++----
 test/deals/parse.test.mjs  | 10 ++++++++++
 2 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/scripts/lib/deal-parse.mjs b/scripts/lib/deal-parse.mjs
index add84472..a4e8e952 100644
--- a/scripts/lib/deal-parse.mjs
+++ b/scripts/lib/deal-parse.mjs
@@ -71,14 +71,18 @@ export function parseSize(text) {
     mK && { idx: mK.index, val: Math.round(parseFloat(mK[1].replace(/,/g, '')) * 1e3) },
     mPl && { idx: mPl.index, val: +mPl[1].replace(/,/g, '') },
   ].filter(Boolean).sort((a, b) => a.idx - b.idx);
-  const sqft = cands.length ? cands[0].val : null;
+  let sqft = cands.length ? cands[0].val : null;
+  const unitsNum = units ? +units[1].replace(/,/g, '') : null;
+  // Plausibility guard: a multifamily asset is measured in UNITS, not SF, so a small "N sf" next to a
+  // unit count is a body leak — a per-unit average or an amenity/clubhouse size, NOT the total. No
+  // residential building runs under ~100 sf/unit (micro-units start ~250), and real data shows a clean
+  // gap (leaks < 11 sf/unit, legit > 790), so this drops "406 units · 669 SF" → "406 units" safely.
+  if (unitsNum && sqft && sqft < unitsNum * 100) sqft = null;
   const parts = [];
   if (units) parts.push(`${units[1]} units`);
   if (sqft) parts.push(`${sqft.toLocaleString()} SF`);
   if (acres) parts.push(`${acres[1]} acres`);
-  return { size_label: parts.join(' · ') || null,
-    units: units ? +units[1].replace(/,/g, '') : null,
-    sqft };
+  return { size_label: parts.join(' · ') || null, units: unitsNum, sqft };
 }
 
 export function parseAddress(body) {
diff --git a/test/deals/parse.test.mjs b/test/deals/parse.test.mjs
index 7887b569..0410f722 100644
--- a/test/deals/parse.test.mjs
+++ b/test/deals/parse.test.mjs
@@ -99,6 +99,16 @@ test('parseSize: magnitude-aware (k = thousand, msf = million) and title-first-w
   assert.equal(parseSize('104k sf Office Sells. Property is adjacent to 2.5 msf distribution hub.').sqft, 104000);
   assert.equal(parseSize('400k sf campus is part of a 3.2 million square feet master plan').sqft, 400000);
 });
+test('parseSize: an implausibly small SF next to a unit count is dropped (multifamily body leak)', () => {
+  // a per-unit average / amenity size leaked from the body → drop it, keep the unit count
+  assert.deepEqual(parseSize('Step Up Acquires 406-Unit Portfolio. Each unit averages 669 sf.'),
+    { size_label: '406 units', units: 406, sqft: null });
+  assert.deepEqual(parseSize('BlackRock Acquires 3,620-Unit Portfolio. A 39,000 sf clubhouse anchors it.'),
+    { size_label: '3,620 units', units: 3620, sqft: null });
+  // a plausible large SF next to units is KEPT (not dropped)
+  assert.deepEqual(parseSize('Meta Housing 300-Unit Community spanning 238,000 sf'),
+    { size_label: '300 units · 238,000 SF', units: 300, sqft: 238000 });
+});
 
 // ── parseOccupancy / parseYearBuilt ───────────────────────────────────────────
 test('parseOccupancy + parseYearBuilt', () => {

← fa57d846 auto-save: 2026-08-06T07:49:47 (7 files) — data/deals-regist  ·  back to Rentv  ·  harden(deals): Cody gate — lower SF/unit plausibility thresh f6e330bb →