[object Object]

← back to Homesonspec

fix(pulte): use real state code instead of hardcoded CA (enables multi-state)

55372bc1aff42f892b79aa8a50c6b784d42857fa · 2026-07-23 07:25:54 -0700 · Steve Abrams

The community + home state value was pinned to 'CA' while raw evidence
held the true stateAbbreviation — a silent data-integrity bug that would
mislabel every non-CA home as California on a multi-state drain. Now
normalizes stateAbbreviation (or the PULTE_STATE full-name fallback) to
the real 2-letter code. Prereq for the authorized national expansion.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 55372bc1aff42f892b79aa8a50c6b784d42857fa
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Jul 23 07:25:54 2026 -0700

    fix(pulte): use real state code instead of hardcoded CA (enables multi-state)
    
    The community + home state value was pinned to 'CA' while raw evidence
    held the true stateAbbreviation — a silent data-integrity bug that would
    mislabel every non-CA home as California on a multi-state drain. Now
    normalizes stateAbbreviation (or the PULTE_STATE full-name fallback) to
    the real 2-letter code. Prereq for the authorized national expansion.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 collectors/pulte/src/index.ts | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/collectors/pulte/src/index.ts b/collectors/pulte/src/index.ts
index ca90fad..1ce1105 100644
--- a/collectors/pulte/src/index.ts
+++ b/collectors/pulte/src/index.ts
@@ -22,6 +22,23 @@ const STATE = process.env.PULTE_STATE ?? "California";
 const BRAND = process.env.PULTE_BRAND ?? "Pulte";
 const PAGE_LIMIT = Number(process.env.PULTE_PAGE_LIMIT ?? 200);
 
+// Full state name → 2-letter code, so a multi-state drain labels homes with
+// their REAL state instead of a hardcoded "CA". stateAbbreviation from the API
+// is already 2-letter; this is the fallback when it's missing.
+const US_STATE: Record<string, string> = {
+  california: "CA", texas: "TX", arizona: "AZ", colorado: "CO", florida: "FL",
+  nevada: "NV", "new mexico": "NM", washington: "WA", oregon: "OR", georgia: "GA",
+  "north carolina": "NC", "south carolina": "SC", tennessee: "TN", indiana: "IN",
+  illinois: "IL", minnesota: "MN", michigan: "MI", ohio: "OH", "new york": "NY",
+  massachusetts: "MA", maryland: "MD", virginia: "VA", pennsylvania: "PA",
+  "new jersey": "NJ", connecticut: "CT", missouri: "MO", kentucky: "KY",
+};
+function stateCode(abbr: unknown, fallbackFullName: string): string | null {
+  const a = typeof abbr === "string" ? abbr.trim() : "";
+  if (/^[A-Za-z]{2}$/.test(a)) return a.toUpperCase();
+  return US_STATE[fallbackFullName.toLowerCase()] ?? null;
+}
+
 function fv<T>(value: T | null, raw: string | null, sourceUrl: string, evidenceText?: string | null): FieldValue<T> {
   return { value, raw, evidenceText: evidenceText ?? raw, sourceUrl, confidence: value === null ? 0 : 1 };
 }
@@ -107,7 +124,7 @@ export const pulteAdapter: SourceAdapter = {
           name: fv(communityName, communityName, page.url),
           street: fv<string>(null, null, page.url),
           city: fv(str(addr.city), str(addr.city), page.url),
-          state: fv("CA" as const, str(addr.stateAbbreviation) ?? STATE, page.url),
+          state: fv(stateCode(addr.stateAbbreviation, STATE), str(addr.stateAbbreviation) ?? STATE, page.url),
           zip: fv(str(addr.zipCode), str(addr.zipCode), page.url),
           county: fv<string>(null, null, page.url),
           metro: fv<string>(null, null, page.url),
@@ -143,7 +160,7 @@ export const pulteAdapter: SourceAdapter = {
           fields: {
             street: fv(address, address, page.url),
             city: fv(str(a.city), str(a.city), page.url),
-            state: fv("CA" as const, str(a.stateAbbreviation) ?? STATE, page.url),
+            state: fv(stateCode(a.stateAbbreviation, STATE), str(a.stateAbbreviation) ?? STATE, page.url),
             zip: fv(str(a.zipCode), str(a.zipCode), page.url),
             price: fv(price, price === null ? null : String(price), page.url, price ? `finalPrice ${price}` : null),
             beds: fv(num(h.bedrooms), null, page.url),

← ac8dff8 docs(collectors): update builder status — 5 live, TM egress-  ·  back to Homesonspec  ·  fix(dr-horton,tri-pointe): use real parsed state instead of 6612fe5 →