[object Object]

← back to Rentv 2026

harden(deals): parseAmount \s* binds magnitude across a typographic double space ($52 million→$52M, was $52); document range + billionaire as zero-occurrence follow-ons. Pure superset — real corpus uses ≤1 space, no existing input changes. +1 test, 60/0

971e9e4b1d8e5a6966c103a19bb4ea6e973fa9c3 · 2026-08-06 13:57:29 -0700 · Steve

Files touched

Diff

commit 971e9e4b1d8e5a6966c103a19bb4ea6e973fa9c3
Author: Steve <steve@designerwallcoverings.com>
Date:   Thu Aug 6 13:57:29 2026 -0700

    harden(deals): parseAmount \s* binds magnitude across a typographic double space ($52  million→$52M, was $52); document range + billionaire as zero-occurrence follow-ons. Pure superset — real corpus uses ≤1 space, no existing input changes. +1 test, 60/0
---
 scripts/lib/deal-parse.mjs | 9 ++++++++-
 test/deals/parse.test.mjs  | 8 ++++++++
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/scripts/lib/deal-parse.mjs b/scripts/lib/deal-parse.mjs
index fa870839..8c693f85 100644
--- a/scripts/lib/deal-parse.mjs
+++ b/scripts/lib/deal-parse.mjs
@@ -12,7 +12,14 @@
 // as "$153"). If a title states only a per-X price, the total is unknown → null (honest-empty).
 export function parseAmount(text) {
   // Spelled words (mil/billion) tried before the bare single-letter M/B so "$52 Mil" → mil, "$150M" → m.
-  const re = /\$\s?([\d,]+(?:\.\d+)?)\s?(bil|billion|mil|million|[mb]|k)?/gi;
+  // \s* (not \s?) before the magnitude so a typographic double space ("$52  million") still binds the
+  // suffix — \s? spanned only one space and silently dropped the magnitude ($52M → $52). Pure superset:
+  // every real-corpus amount uses ≤1 space, so this changes no existing input.
+  // KNOWN LIMITATIONS (zero occurrence in the live corpus — logged follow-ons, not fixed here to avoid
+  // regressing legit inputs): (a) a shared-magnitude range "$40-50 million" reads only the low bound
+  // unscaled ($40) — needs true range semantics; (b) the spelled word matches inside a larger word, so
+  // "$5 billionaire" would fabricate $5B — can't be tightened without breaking "$1.5MM" finance notation.
+  const re = /\$\s?([\d,]+(?:\.\d+)?)\s*(bil|billion|mil|million|[mb]|k)?/gi;
   let m;
   while ((m = re.exec(text)) !== null) {
     const after = text.slice(m.index + m[0].length);
diff --git a/test/deals/parse.test.mjs b/test/deals/parse.test.mjs
index 82d610c5..feb8904c 100644
--- a/test/deals/parse.test.mjs
+++ b/test/deals/parse.test.mjs
@@ -98,6 +98,14 @@ test('parseAmount: broadened per-sf spellings (psf / sqft / per sq ft / per foot
   for (const t of ['$153 psf', '$153 per sq ft', '$153 sqft', '$153 per foot'])
     assert.equal(parseAmount(t).amount, null, t);
 });
+test('parseAmount: a typographic double space still binds the magnitude (\\s* not \\s?)', () => {
+  // "$52  million" (two spaces) was dropping the suffix → $52; the magnitude must still attach.
+  assert.equal(parseAmount('$52  million deal').amount, 52000000);
+  assert.equal(parseAmount('$1.2   billion recap').amount, 1200000000);
+  // regression guard: the normal single-space and no-space forms are unchanged
+  assert.equal(parseAmount('$52 million').amount, 52000000);
+  assert.equal(parseAmount('$150M').amount, 150000000);
+});
 
 // ── parseSize ─────────────────────────────────────────────────────────────────
 test('parseSize: units / sf / acres', () => {

← 206b9954 Gate E (TK-10076): /services degraded-state banner on reales  ·  back to Rentv 2026  ·  auto-data-snapshot: 2026-08-06T13:58:45 (7 data files) — dat d0acfe5e →