← back to Rentv 2026
harden(deals): Cody gate — parseSize picks the EARLIEST-in-text sqft match, not by magnitude precedence, so a body 'N msf' comp can't override the title's real 'Nk sf' (branch-order bug: million scanned whole string first)
2930a2089739a902abc971d1a3102f64c876bd55 · 2026-08-06 07:48:56 -0700 · Steve
Files touched
M scripts/lib/deal-parse.mjsM test/deals/parse.test.mjs
Diff
commit 2930a2089739a902abc971d1a3102f64c876bd55
Author: Steve <steve@designerwallcoverings.com>
Date: Thu Aug 6 07:48:56 2026 -0700
harden(deals): Cody gate — parseSize picks the EARLIEST-in-text sqft match, not by magnitude precedence, so a body 'N msf' comp can't override the title's real 'Nk sf' (branch-order bug: million scanned whole string first)
---
scripts/lib/deal-parse.mjs | 19 +++++++++++++------
test/deals/parse.test.mjs | 3 +++
2 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/scripts/lib/deal-parse.mjs b/scripts/lib/deal-parse.mjs
index c06cb28e..add84472 100644
--- a/scripts/lib/deal-parse.mjs
+++ b/scripts/lib/deal-parse.mjs
@@ -59,12 +59,19 @@ export function parseSize(text) {
// Square footage, MAGNITUDE-AWARE. CRE headlines write the asset size as "104k sf" (thousand) or
// "1.27 msf" (million square feet); the old plain "N sf" regex couldn't match those (the number isn't
// directly followed by "sf"), so it SKIPPED the title's real size and grabbed a stray small "N sf" from
- // the body — a 104,000 sf building rendered as "26 SF". Try million, then thousand, then plain: the
- // magnitude notations live in the title (front of the text) so matching them here wins over a body leak.
- let sqft = null, m;
- if ((m = text.match(/([\d,.]+)\s?(?:msf|m\s?sf|million\s?(?:sf|square[ -]feet))/i))) sqft = Math.round(parseFloat(m[1].replace(/,/g, '')) * 1e6);
- else if ((m = text.match(/([\d,.]+)k\s?(?:sf|sq\.?\s?ft|square[ -]feet|square[ -]foot)/i))) sqft = Math.round(parseFloat(m[1].replace(/,/g, '')) * 1e3);
- else if ((m = text.match(/([\d,]+)\s?(?:sf|sq\.?\s?ft|square[ -]feet|square[ -]foot)/i))) sqft = +m[1].replace(/,/g, '');
+ // the body — a 104,000 sf building rendered as "26 SF". We compute all three candidate matches and pick
+ // the one that occurs EARLIEST in the text (by .index), NOT by magnitude precedence: the title sits at
+ // the front of `hay`, so its size wins by position. Ordering by magnitude instead would let a BODY
+ // mention of a larger comp ("adjacent to a 2.5 msf campus") override the title's real "104k sf".
+ const mMil = text.match(/([\d,.]+)\s?(?:msf|m\s?sf|million\s?(?:sf|square[ -]feet))/i);
+ const mK = text.match(/([\d,.]+)k\s?(?:sf|sq\.?\s?ft|square[ -]feet|square[ -]foot)/i);
+ const mPl = text.match(/([\d,]+)\s?(?:sf|sq\.?\s?ft|square[ -]feet|square[ -]foot)/i);
+ const cands = [
+ mMil && { idx: mMil.index, val: Math.round(parseFloat(mMil[1].replace(/,/g, '')) * 1e6) },
+ 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;
const parts = [];
if (units) parts.push(`${units[1]} units`);
if (sqft) parts.push(`${sqft.toLocaleString()} SF`);
diff --git a/test/deals/parse.test.mjs b/test/deals/parse.test.mjs
index 8c8c156c..7887b569 100644
--- a/test/deals/parse.test.mjs
+++ b/test/deals/parse.test.mjs
@@ -95,6 +95,9 @@ test('parseSize: magnitude-aware (k = thousand, msf = million) and title-first-w
// the title's "104k sf" wins over a stray small "26 sf" leaked later in the body
assert.equal(parseSize('SFF Buys 104k sf office. The lobby has 26 sf of signage.').sqft, 104000);
assert.equal(parseSize('GO builds a 250,000 square feet facility').sqft, 250000);
+ // a BODY mention of a LARGER comp must NOT override the title's k-sf (earliest-in-text wins, not magnitude)
+ 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);
});
// ── parseOccupancy / parseYearBuilt ───────────────────────────────────────────
← 28068930 fix(deals): magnitude-aware parseSize — parse '104k sf'/'1.2
·
back to Rentv 2026
·
auto-save: 2026-08-06T07:49:47 (7 files) — data/deals-regist fa57d846 →