← back to Homesonspec
collectors/BUILDERS.md
277 lines
# Builder adapter recon (CA inventory) — 2026-07-23
All 6 top CA builders recon'd. **All are plain-curl, HTTP 200, no anti-bot, no browser needed.**
Each carries a per-community sales phone. Adapters are CA-scoped via an env var.
| Builder | key | status | source shape |
|---|---|---|---|
| Lennar | lennar-site | ✅ built | per-community `__NEXT_DATA__` Apollo (LENNAR_STATE_FILTER) |
| D.R. Horton | dr-horton-site | ✅ built+verified | per-community `var model = {…}` blob (DRHORTON_STATE) |
| KB Home | kb-home-site | ✅ built+verified | ONE feed `/move-in-ready` → `var allMIRs=JSON.parse("…")` double-decode |
| Pulte | pultegroup-site | ✅ built+live | clean JSON API (has lat/lon!) |
| Tri Pointe | tri-pointe-site | ✅ built+live | Next.js RSC `__next_f` `"display_price"` objects |
| Taylor Morrison | taylor-morrison-site | ⚠️ built+verified, BLOCKED at egress | Sitecore fedmodel (scan 13 blocks for `availableHomesList`); adapter correct (3 recs/0 err on fixture) but Akamai bot-blocks the Kamatera datacenter IP (37KB html "404" challenge on /ca/sitemap.xml under load). Needs residential/allowlisted egress — NOT circumvented per standing rule. Source `active=false, health=DEGRADED`. |
| Meritage | meritage-homes-site | ⛔ NEEDS BROWSER-TRACE RECON | Sitecore XM Cloud headless — inventory (QMIs) is NOT in server HTML (state/community/search pages all render empty shells; only plan ranges + community lat/lon in `__NEXT_DATA__`). QMI spec homes load via a runtime XHR (`community-qmi-filters` → XM Cloud edge API). NEXT STEP: capture the XHR endpoint+params via Playwright/Browserbase, then build a Pulte-style JSON-API adapter. Egress is CLEAN (200s) — only the endpoint is unknown. |
**Live builder counts (2026-07-23):** Lennar 38,800+ (TX drain running) · D.R. Horton 513 (full CA) · PulteGroup 180 · KB Home 143 · Tri Pointe 98. **32 states, 7,464 TX homes** (TX-zero fixed).
## Pulte (pultegroup-site) — JSON API, CLEANEST (do next)
- Communities: `GET https://www.pulte.com/api/community/getcommunities?brand=Pulte&state=California&pageSize=1000000&pageNumber=0`
- Homes per region: `POST https://www.pulte.com/api/qmi/inventorysearch` body `{"StateName":"California","RegionName":"<region>","BrandName":"Pulte"}`. CA regions: Bay Area, Los Angeles, Orange County, Palm Springs, Riverside County, Sacramento, San Bernardino County.
- Home fields: `price/priceDiscount/finalPrice, bedrooms, bathrooms/halfBaths/totalBaths, squareFeet, garages, floors, address.{street1,city,state,zipCode} (strip whitespace), dateAvailable, salesPhoneNumber, communityName, communityId, communityLatitude/Longitude, inventoryPageURL`.
- Status: `dateAvailable` "Available Now" → MOVE_IN_READY. HAS lat/lon (community-level). Adapter fetch() should POST per region (LiveFetcher may need a POST variant — or fetch the getcommunities GET + per-community `GET /api/plan/qmiplans?communityId=`).
## Tri Pointe (tri-pointe-site) — Next.js RSC
- Sitemap index `https://www.tripointehomes.com/sitemap.xml` → `sitemap-communities.xml`; CA communities = `<loc>` paths starting `/ca/` (73). URL: `/ca/{county}/{community}`.
- Per community page: unescape RSC (`.replace('\\"','"')`), brace-match each object containing `"display_price":\d+`.
- Fields: `display_price/min_price, min_bedrooms, min_bathrooms, min_sq_feet, min_garage, min_stories, home_status/availability_status/tpg_status, move_in_date, floor_plan, homesite, plan_type, address, cities[], zip_code, submarket, state_alpha_2_code`.
- Phone: community `"phone":"949.432.9284"` or JSON-LD `LocalBusiness.telephone`.
## Taylor Morrison (taylor-morrison-site) — Sitecore fedmodel
- Sitemap index `https://www.taylormorrison.com/sitemap-index.xml` → CA `https://www.taylormorrison.com/ca/sitemap.xml` (601). Community = 4-seg `/ca/{metro}/{city}/{community}`; drop suffix pages + `*-community-group-page`.
- Per community: fetch `{community}/available-homes`, regex `data-fed-ref="fedmodel">(\{…\})</script>`, JSON.parse, walk `availableHomesList.sections[*].homes[]`.
- Fields: `address, city, state, zip, homeSite, price, wasPrice, bed, fullBath, halfBath, totalBath, garages, sqft, availabilityStatus ("1"=available), readyDate, isComingSoon, floorPlan, viewHomeLink.Url, photo.Src`.
- Phone: community JSON-LD `telephone`. (SoCal grouped communities 302-redirect → resolve leaf.)
## Meritage (meritage-homes-site) — Next.js __NEXT_DATA__
- Sitemap `https://www.meritagehomes.com/sitemap.xml`; CA = `/state/ca/` (403), 3 regions. 2-seg `/state/ca/{region}/{slug}`: slug=street-address → HOME page; slug=name → COMMUNITY page.
- Home page `__NEXT_DATA__` path `props.pageProps.componentProps.{guid}.data.lotSearch.lotData` (find the componentProps child with `.data.lotSearch.lotData`).
- Home fields: `priceSpec, monthlyPrice, bedrooms, fullBathrooms, halfBathrooms, squareFootage, garages, stories, address1/city/state/zip, latitude/longitude, status ("Inventory"), constructionStage, completionEstimated, lot_number, imageUrls, communities.targetItems[].communityId`. HAS lat/lon.
- Community page path `...data.communitySearch.communityData` → `metroPhoneNumber` (sales phone), lat/long, price/bed/bath ranges, totalQmis.
## Notes
- Register each in `apps/workers/src/cli.ts` ADAPTERS + add workspace dep in `apps/workers/package.json`.
- SourceRegistry rows exist for all (inactive). To ingest: activate + set collectionMethod=CRAWL, deploy adapters to Kamatera, run `pnpm --filter @homesonspec/workers pipeline -- --adapter=<key>` with the state env.
- Pulte/Meritage give lat/lon → homes appear on the map. KB/DR-Horton/TriPointe/TaylorMorrison lack per-home geo in v1 (search/lists only) — geocoding is a follow-up.
---
# Regional-builder feasibility recon — 2026-07-27 (plain-fetch pass)
Probed 8 registered "adapter pending" regional builders for **plain-curl, facts-only** inventory
feasibility (respecting robots; no bot-wall circumvention). Finding: modern regional builders
overwhelmingly render per-home inventory **client-side (XHR)**, so plain-fetch adapters like
Discovery/Pulte are the exception, not the rule. Classifications written to `SourceRegistry.notes`.
| Builder | key | verdict | evidence |
|---|---|---|---|
| **Highland Homes** | highland-homes-site | **XHR-TRACE — best next** | Fully-open robots (no disallows) + rich sitemap (`/{metro}/{city}/{community}/homesites`, `/quick-move-in/{metro}`). jQuery + svg-pan-zoom site plan; facts load via AJAX (no facts/JSON-LD/`__NEXT_DATA__` in server HTML). Large TX/AZ/CO inventory. **Next: browser-trace the homesites/QMI AJAX endpoint → Pulte-style JSON-API adapter.** |
| Signature Homes | signature-homes-site | FEED-EXISTS-BUT-THIN | WordPress; permitted WP REST CPT `/wp/v2/move-in-home` (X-WP-Total=4). ACF facts NOT `show_in_rest`; move-in-home page HTML carries no price/beds/sqft (image-baked). Only breadcrumb JSON-LD community name + homesite label extractable. Revisit if catalog grows or ACF is REST-exposed. |
| Van Daele | van-daele-homes-site | SPA | WordPress, open robots, `/{region}-available-homes/` pages JS-hydrated (inline `$` values are filter dropdowns, not listings). Next: trace available-homes XHR / admin-ajax action. |
| SummerHill | summerhill-homes-site | SPA | AngularJS (`ng-app="app"`, `HomesNowReadyCtrl`); `/quick-move-in` facts client-side. Next: trace QMI XHR. |
| DeNova | denova-homes-site | ROBOTS-LIMITED | Per-lot inventory hydrates from robots-DISALLOWED `/*sitesearch.json*`; community pages are server-rendered but show only plan RANGES. Per-home OFF-LIMITS w/o permission → partner-feed outreach. Community-level metadata OK. |
| Landsea | landsea-homes-site | BOT-WALL | Cloudflare bot challenge (redirects → risewellhomes.com "Sorry you have been blocked"). Datacenter egress blocked — NOT circumvented. Needs allowlisted egress or partner feed. |
| Fulton | fulton-homes-site | thin/defer | 93KB homepage, no inventory-feed markers on landing; likely SPA/XHR. Defer behind Highland. |
**Buildable-now shortlist (plain-fetch):** none in this batch. **Best ROI next:** Highland Homes
(clean robots + big inventory) via a browser-trace of its AJAX endpoint — the same proven next-step
as Meritage. SPA/XHR builders (Highland, SummerHill, Van Daele) all converge on the same tool:
capture the inventory XHR once (Playwright/Browserbase), then a JSON-API adapter.
---
# 2026-07-27 (PASS 2) — Highland RE-VERDICT + David Weekley adapter BUILT
**Highland Homes — DOWNGRADED from "best next" to BOT-WALLED (not plain-fetch buildable).**
Browser-traced the QMI page (`/quick-move-in/{metro}`) with headless Playwright: the Algolia
inventory search **never fires** — Cloudflare `challenge-platform` intervenes before the Vue app
runs its query (0 `*.algolia.net` requests captured across a 13s load + scroll). The only 32-hex
key embedded in the server HTML (`dd383f7c31fb03968005570eff61eff7`, appId `RYAJNPEGOZ`) is the
**Insights** key — it returns `403 "Index not allowed"` on index `highland` and 14 name guesses, so
the real search key is fetched at runtime *behind* the CF challenge. Net: Highland needs a
challenge-solving browser (Browserbase/2captcha) to reach both the runtime key and the index —
same class as Landsea, NOT the clean XHR-trace PASS 1 hoped for. Not circumvented (standing rule).
**David Weekley (`david-weekley-site`) — ADAPTER BUILT (this pass).** Pivoted to a national
builder whose 2026-07-22 recon was "HTML_PARSE diff-2 clean" and re-verified it holds:
- Sitemap `https://www.davidweekleyhomes.com/sitemap.xml` -> ~855 real
`/new-homes/{st}/{market}/homes-ready-soon` list pages (drop `%7B...%7D` template rows).
- robots.txt only blocks querystring dupes (`?planId=`/`?communityId=`) — **list pages are allowed**.
- Each page is **fully server-rendered** (no browser). Per-home QMI card = `.single-tabbed-listing`
showcase block: url `/homes-ready-soon/{st}/{mkt}/{city}/{community}/{jobNumber}` (jobNumber =
builderInventoryId), `span.address` (street+city+ST+zip), `.starting-price .value` (price),
`.square-footage .value` (sqft), `.feature.story-count/.bedroom-count/.number-baths/.garage-count
.value`, `.plan-title`+`.plan-id`, `.label.ready` -> "Ready Now" (MOVE_IN_READY) or "Ready m/d/yyyy"
(UNDER_CONSTRUCTION + est completion date).
- **Live-verified read-only:** 3 TX pages -> 131 communities + **284 inventory_home records, 0 errors**,
full facts (e.g. `840 Perry Pass, Round Rock TX 78664 · $397,990 · 3bd/2ba · 1713sqft · MOVE_IN_READY`).
Fixture-parse matches source exactly (The Heron job 19370084: 3bd/3ba/2gar/2story/$362,990/2190sqft).
- Env scope: `DAVIDWEEKLEY_STATE` (2-letter), `DAVIDWEEKLEY_PAGE_LIMIT`. No per-home geo in v1
(list pages carry no lat/lon — geocoding is the same follow-up as KB/DR-Horton/TriPointe).
- Facts-only (images omitted, mediaRights=NONE). 6/6 vitest + typecheck green. Registered in
`apps/workers/src/cli.ts`. **To ingest:** activate `david-weekley-site` + set collectionMethod=CRAWL,
deploy, run `pnpm --filter @homesonspec/workers pipeline -- --adapter=david-weekley-site`.
**National adapter-pending backlog (all HEALTHY recon, plain-fetch, higher ROI than the SPA regionals):**
~~ashton-woods (ld+json QMI sitemaps, diff-2)~~ **✅ BUILT (PASS 3, below)** · beazer (open robots,
`__next_f` prices, diff-3) · mattamy (Sitecore JSS state, richest fields, diff-2) · ryan-homes
(data-* cards, diff-3) · lgi-homes (`__NEXT_DATA__` plan-priced) · mi-homes (data-price cards,
diff-2/3). Do these next before the bot-walled regionals.
---
# 2026-07-28 (PASS 3) — Ashton Woods adapter BUILT (JSON-LD QMI, HAS geo)
**Ashton Woods (`ashton-woods-site`) — ADAPTER BUILT (this pass).** The next national
plain-fetch target off the backlog. Confirmed the 2026-07-22 "ld+json QMI sitemaps" recon holds
and it is the RICHEST national source yet (per-home geo → homes appear on the map):
- Sitemap index `https://www.ashtonwoods.com/sitemap.xml` → 13 per-metro `{metro}-sitemap-qmi.xml`
files (atlanta, austin, charleston, dallas, houston, jacksonville, myrtle-beach, nashville,
orlando, phoenix, raleigh, san-antonio, tampa) → each lists per-home QMI URLs
`/{metro}/{community}/{plan}/{street-address}`.
- robots.txt disallows only /content/ /input/ /walk-in/ /sales_center/ /interactive-floorplan
/realtor-program/ /welcome — **the metro QMI pages we read are allowed.**
- Each QMI page is **fully server-rendered** (no browser) with a clean JSON-LD pair:
- `SingleFamilyResidence`: name, address{streetAddress/addressLocality/addressRegion/postalCode},
`ContainedIn` (community), `Floorsize` ("2260FTK" → sqft), `AmenityFeature`
["4 Bedrooms","3 Baths | 1 Half Baths","2 Stories","2 Car Garage"], `geo{latitude,longitude}`,
`telephone` (community sales phone).
- `Product.offers`: `price` (number), `availability` (schema.org/InStock → for-sale gate).
- One page == one `inventory_home` + its `community` (community carries the same per-home geo so it
also maps). Baths = full + half·0.5 ("3 Baths | 1 Half Baths" → 3.5). **coord() helper preserves
negative longitudes** (US lon < 0 — same class of bug caught overnight in the Discovery adapter;
a naive num() would strip the minus). builderInventoryId = the stable per-home page URL (AW has no
numeric job id). Extract skips sold/unpriced pages (no price or availability≠InStock).
- **Live-verified read-only** (3 metros/states through the real adapter `extract()`, 0 errors):
`5712 Terrace Drive, Watauga TX 76148 · $340,200 · 3bd/2.5ba · 2050sqft · geo(32.879,-97.260)`;
Houston TX `$274,990 · 4bd/2ba`; Queen Creek AZ `$599,990 · 3bd/2.5ba`. Fixture parse matches
source exactly (321 Possumhaw Lane, San Marcos TX: 4bd/3.5ba/2gar/2story/$369,636/2260sqft).
- Env scope: `ASHTONWOODS_METRO` (comma-list of metros, default all), `ASHTONWOODS_PAGE_LIMIT`.
- Facts-only (image omitted, mediaRights=NONE). 6/6 vitest + typecheck green (workers CLI typecheck
green too). Registered in `apps/workers/src/cli.ts`. **To ingest (GATED go-live):** activate
`ashton-woods-site` + set collectionMethod=CRAWL, deploy, run
`pnpm --filter @homesonspec/workers pipeline -- --adapter=ashton-woods-site`.
**Remaining national backlog (do next):** beazer · mattamy (Sitecore JSS, richest) · ryan-homes ·
lgi-homes · mi-homes — then the bot-walled regionals (Highland/Landsea) which need allowlisted egress.
---
# 2026-08-11 (TK-10001 CLOSE-OUT) — national-30 roster fully accounted for
Contrarian red-team of the "all 30 builders" DONE claim caught 4 seeded national builders in
`packages/database/prisma/seed-national-builders.ts` with **no adapter and no documented deferral**
(mi-homes, stanley-martin, smith-douglas, clayton-properties). Built via 4 parallel isolated
subagents + serial gated integration. Result — **every one of the 30 seeded national builders now
has either a built adapter OR a documented-deferral RECON.md:**
| builder | slug | outcome | source shape |
|---|---|---|---|
| M/I Homes | mi-homes-site | ✅ **BUILT** | Sitemap → 2,707 per-home pages, SSR `Product` JSON-LD (addr/geo/price/InStock) + `plan-specification__list` (sqft/beds/baths/garage/stories) + Ready-date. Self-test 14/14, 100% beds/baths/sqft/addr, 93% price/geo. robots `Allow:/`. |
| Smith Douglas | smith-douglas-site | ✅ **BUILT** | Sitemap → 752 per-home React-SSR detail pages, facts in HTML (`HomeOverview_*`). Self-test 25/25 distinct, 100% addr/beds/baths, 96% sqft, 92% price. robots `Allow:/`. |
| Stanley Martin | stanley-martin | ⛔ **DEFERRED** (RECON.md) | Vite/React SPA, empty 1.6KB app-shell, facts only via runtime XHR (endpoint runtime-resolved). 200 (not bot-walled) — same class as richmond-american. Sitemap 1,400 per-home URLs documented; needs the shared RenderFetcher or a green-lit XHR capture. |
| Clayton Properties | clayton-properties | ⛔ **DEFERRED** (RECON.md) | SCOPE MISMATCH: claytonhomes.com inventory is 100% manufactured/mobile (wrong product); genuine site-built spec inventory is federated across ~15 independent CPG brand domains (goodall/brohn/oakwood/mungo/…) with no single feed → should be per-brand adapters, not one `clayton-properties` adapter. |
**Deferred count = 3** (richmond-american [Blazor WS], stanley-martin [SPA XHR], clayton-properties
[scope]) — all documented with the exact future build path; none circumvented (standing rule).
**Built adapters registered in `apps/workers/src/cli.ts` = 31** (29 prior incl. `meridian-homes-fixtures`
reference + mi-homes + smith-douglas). Whole workspace typecheck exit 0 / 0 TS errors; workers + collector
tests green. **Go-live (activate + deploy + ingest to Kamatera) stays Steve-gated** — this pass is
build-out engineering only.
---
# 2026-08-11 (GO-LIVE) — mi-homes + smith-douglas activated + ingested (Steve-approved)
Steve approved full go-live. Both new adapters activated (`SourceRegistry.active=true`) and ingested
into the Mac2-local DB via the real pipeline, 0 rejected / 0 errors:
- **mi-homes-site**: 25 pages → 50 staged → **25 published homes** (92% price, 100% beds/sqft, 25 distinct addr). TX sample $429k–$462k.
- **smith-douglas-site**: 25 pages → **25 published homes** (48% price = honest "Contact For Pricing" nulls, 100% sqft, 25 distinct addr).
**FOOTGUN (fixed):** M/I Homes URLs use the FULL state name — `MI_STATE=texas`, NOT `MI_STATE=tx`
(a `tx` filter matches `/new-homes/tx/` which doesn't exist → 0 pages). Use the full state word.
**Deploy hardening (deploy-kamatera.sh):** added a pre-drop `pg_dump` backup (aborts if the dump is
suspiciously small) + `WORKER_HEALTH_PORT=9966` in the remote `.env` (worker was defaulting to 9976,
colliding with the admin app). Also fixed `apps/workers` `pipeline`/`verify` scripts to load the root
`.env` via `tsx --env-file` (was failing with "DATABASE_URL not found" on manual runs).
**Remaining:** re-run `deploy-kamatera.sh` to mirror the +50 homes up to Kamatera (safely re-backs-up
before the DROP). Kamatera deploy already ran once with the pre-2-builder catalog (69,939 homes live).
---
# 2026-08-12 (TK-10487) — Clayton Properties Group per-brand builders (batch 1)
The clayton-properties deferral's future path = one adapter per CPG brand domain. Batch 1 built via
3 parallel isolated subagents + serial gated integration, all plain-fetch/facts-only/robots-open,
live-verified 100% core coverage, typecheck green:
| brand | slug | source shape | self-test |
|---|---|---|---|
| Goodall Homes (TN/AL) | goodall-homes-site | React SSR sitemap→113 per-home; JSON-LD SingleFamilyResidence+Product (addr/geo/price) + HomeDetails DOM (beds/baths/sqft/stories/garage/status) | 14/14 distinct, 100% incl geo |
| Brohn Homes (Austin/Houston/SA TX) | brohn-homes-site | WordPress SSR sitemap→247 per-home; .home-price + .brohn-home-stats DOM; ZIP from maps-link query; TOWNHOME vs SFR | 8/8 distinct, 100% price/beds/baths/sqft/addr |
| Oakwood Homes (CO/UT/AZ/NC) | oakwood-homes-site | Nuxt 3 SSR devalue payload → KOVA ERP sales-order (addr/geo/price/beds/baths/sqft/plan/est-compl) | 12/12 distinct, 100% + geo, 92% plan |
All 3 registered in apps/workers/src/cli.ts + workers/package.json; whole-workspace typecheck exit 0.
Facts-only (mediaRights=NONE), honest nulls, no fabrication, no bot-wall circumvention. Remaining CPG
brands (mungo, chafin, summit-kc, legacy-al, elite-built, berkeley, silverthorne, harris-doyle,
your-arbor…) = batch 2. Activation + ingest + deploy = Steve-gated (same as the national 30).
# 2026-08-30 (TK-10487) — Clayton Properties Group per-brand builders (batch 2 continuation)
Summit Homes KC is committed and worker-registered. Mungo Homes and Chafin Communities now have
fixture-backed, schema-valid collectors and are registered in the local worker CLI + CPG seed list.
Mungo's 8 detail-page fixtures cover distinct homes across VA/SC/NC with 100% core facts + geo;
Chafin's 8 community-page fixtures yield 40+ distinct inventory homes with honest nulls for source-
absent sqft/geo. All are facts-only (`mediaRights=NONE`). Seed execution, activation, ingest, deploy,
and DNS remain explicitly gated and were not performed.
# 2026-08-30 (TK-10487) — CPG per-brand builders COMPLETE (13/13) + AUTHORITATIVE ROSTER
The speculative brand list above ("elite-built, berkeley, your-arbor…") was CORRECTED against the
authoritative source (claytonhomebuildinggroup.com/clayton-properties-group). The real CPG site-built
roster is **13 brands**, and ALL now have worker-registered collectors (whole-workspace typecheck exit 0):
| brand | slug/key | source / status source | committed self-test |
|---|---|---|---|
| Goodall Homes | goodall-homes-site | React SSR JSON-LD + HomeDetails DOM | ✅ vitest (14 fixtures) |
| Brohn Homes | brohn-homes-site | WordPress SSR DOM | live-verified (no committed fixtures) |
| Oakwood Homes | oakwood-homes-site | Nuxt3 devalue → KOVA ERP | live-verified (no committed fixtures) |
| Summit Homes (KC) | summit-homes-kc-site | React SSR JSON-LD + HomeOverview | live-verified (no committed fixtures) |
| Mungo Homes | mungo-homes-site | sitemap → JSON-LD SingleFamilyResidence | ✅ vitest (8 fixtures) |
| Chafin Communities | chafin-communities-site | community pages → per-home cards | ✅ vitest (8 fixtures → 75 homes) |
| Harris Doyle Homes | harris-doyle-site | JSON-LD Org + DOM + **dataLayer** (community/city/state) | ✅ vitest (14 fixtures) |
| Legacy Homes | legacy-al-site | (concurrent session build) | ✅ vitest (4) |
| Arbor Homes | arbor-site | **yourarborhome.com** (NOT arborhomes.com=Oregon namesake) JSON-LD | ✅ vitest (12 fixtures) |
| Silverthorne Homes | silverthorne-site | Arbor's luxury sister; JSON-LD; `--compressed` | ✅ vitest (1 real home, snapshot-pinned) |
| Elite Homes | elite-homes-site | JSON-LD + **Carousel_h2Lead status banner** | ✅ vitest (10 fixtures) |
| Berkeley Building Co. | berkeley-building-site | Boise ID; JSON-LD + **self-card exact-path status** | ✅ vitest (12 fixtures) |
| Highland Homes (FL) | highland-homes-fl-site | highlandhomes.**org** (concurrent; distinct from TX `highland-homes`=highlandhomes.**com**) | ✅ vitest (4) |
Key lesson (every real bug traced to it): **read the source's own machine-readable truth (JSON-LD /
dataLayer / status banner / the home's own grid card), not its rendering.** Real defects caught +
fixed by the DTD→Cody→verify loop: mungo (0 homes, wrong site model), chafin (0 homes regex +
fabricated 32.5-bath + `??`/`?:` dedupe-key bug), harris-doyle (dead entity-encoded regex →
mis-cased community), elite (8/10 wrong-null status), arbor (wrong-company Oregon build discarded).
REMAINING (all GATED, drafted to pending-approval): CPG seed run, per-adapter ingest, activation,
images, deploy. NON-GATED polish outstanding: committed fixture-backed selftests for the 3 batch-1
collectors (brohn/oakwood/summit) that are typecheck-clean + live-verified but lack them (backfill
attempted 2026-08-30 but blocked by a transient Anthropic API rate-limit; retry in a fresh session).
Harris Doyle is now fixture-backed and locally registered. Fourteen AL/FL detail fixtures produce
distinct addresses and builder inventory IDs with complete core facts; status comes only from the
explicit first-party `dataLayer.pageType` signal, while absent geo/stories remain honest nulls.
Legacy Homes Alabama is now fixture-backed and locally registered. Eight North Alabama detail-page
fixtures produce eight distinct addresses and builder inventory IDs from first-party Product JSON-LD;
primary DetailOverview markup supplies beds/baths/sqft and explicitly published plan/story facts.
Missing price/plan/stories/garage/lot/construction status remain honest nulls. Seed execution, ingest,
activation, deploy, DNS, and remote push were not performed.
Berkeley Building Co. is fixture-backed and locally registered. Twelve Idaho detail fixtures span
six Treasure Valley cities with 12 distinct addresses and inventory IDs. Product JSON-LD supplies
identity/address/geo/price while the primary HomeOverview block supplies structural facts; the exact
self-card supplies construction status without reading sibling cards. Four model homes retain honest
null prices, and source-absent garage/lot/completion remain null. No seed execution, ingest, activation,
deploy, DNS, or remote push was performed.
Highland Homes Florida is fixture-backed and locally registered under the collision-safe
`highland-homes-fl-site` key. Its authoritative domain is `highlandhomes.org`: the brand's own About
page says it joined Clayton Properties Group in 2019, its current brochure says "A Clayton Company",
and its Organization JSON-LD identifies Lakeland, Florida. Eight first-party per-home JSON-LD fixture
excerpts produce eight distinct Florida addresses and inventory IDs with complete core facts. The
existing `highland-homes-site` Texas collector (`highlandhomes.com`) remains unchanged. No seed
execution, ingest, activation, deploy, DNS, or remote push was performed.