Skip to content

Model changelog

Every output is stamped model_version. When the version bumps, active listings scored under the old model get rescored (stale_model_rows in /api/health tracks the backlog). Releases that change numbers get an entry here. Serving/platform releases are stamped separately as model_versions.serving.

p5v4 / p5s11-2026.07 — unreleased audit stop-loss

Section titled “p5v4 / p5s11-2026.07 — unreleased audit stop-loss”

Working tree only as of 2026-07-12; no deployment or inventory rescore is claimed.

  • Range-only, hedonic, area-context, stale, and suppressed valuation states cannot emit exact offers, profit, NOI, DSCR, cash-on-cash, loan limits, cashflow projections, or property strategy scores. All 39 current calibration segments remain range/suppress.
  • Suggested offer on any future point-actionable result is capped at the analyzed price.
  • Canonical subject/provenance is carried identically on valuation and artifact records; nearest-comp facts cannot become subject GLA/year/beds. Input fingerprints now cover the local evidence/asset projection and are reproducibility metadata, not anonymization.
  • Anonymous MCP callers cannot invoke write-state or UI-controller mutations; OAuth usage is identity-rate-limited and token exchange no longer creates a durable API key.
  • Underwrite REST/MCP inputs are closed and strictly typed; unknown, duplicate, non-finite, malformed, and out-of-range values are rejected.
  • Applicant/household income, crime trend/mix, and the current income-contaminated capital-flow series are removed from public scores, risk, payloads, and lenses. This is a stop-loss, not fairness clearance: the current fairness artifact still fails its error-level differential gate.
  • Public inventory now fails closed to licensed RentCast/MRED rows across map, property, MCP, alerts, digests, health, and cron. Legacy scrape lanes require an explicit internal-only override.
  • The published scorecard now uses exact sale dates: every historical comp must predate its holdout sale. Same-year future sales can no longer leak into a backtest pool.
  • The map deal lab no longer shows legacy client-side draft offers while the API is pending or unavailable. Editable assumptions are limited to fields that round-trip through the closed underwriting schema; visible dollar outputs are engine results or explicit refusals.

What changed for your numbers: current valuation evidence remains visible as a range or refusal, but exact deal recommendations are intentionally absent until calibration and the remaining financial-data phases clear their gates.

Released 2026-07-12. Another platform release — no deal-math change (model and underwrite stay p1d-2026.07); this one closes gaps the 2026-07 audit register flagged and gets the public docs caught up with the API. Serving version is p6-2026.07.

  • Interim licensed listings source. A RentCast-licensed feed is activating alongside the Redfin-derived daily pull. It closes coverage holes Redfin’s polygons never reached (Waukegan, central Lake County) and surfaces condos and townhomes for the first time — visible in search, but unscored, since the valuation engine models sfh/mf2_4 only. See data coverage for the full matrix.
  • Score-consistency fix across surfaces. The map card, the underwrite response, the comps view, and the MCP tools now return identical numbers for the same address, sampled and checked nightly (INV-2). A same-address mismatch is a regression, not a rounding difference.
  • One-verdict responses. A response could previously carry two disagreeing read strings — a top-level verdict keyed to the default strategy and a different one nested under underwrite.read. Every response now carries exactly one read, scoped to the requested strategy; per-strategy verdicts are still available under strategy_scores.*.read.
  • Coverage page published. Data coverage is live: the honest per-source matrix — what’s fully valued (Cook SFH and 2–4 flats), what’s context-only (Lake County, pending its sales corpus), and what’s simply not tracked outside Chicago (permits, 311, crime trend).
  • llms.txt v2. llms.txt leads with the v2 tool names, lists the legacy names as deprecated aliases, swaps the Waukegan example address (zero listings coverage there) for three addresses with live coverage, and links every machine-readable spec contract.
  • /api/openapi.json is real JSON. It previously served with the wrong content type and carried float-cast sale years ("2021.0"); both are fixed. The developers page now links openapi.json alongside the MCP manifest and the four spec contracts (valuation, scoring, constants, error taxonomy).
  • Report a value. A new endpoint lets a caller flag a specific valuation as wrong with a short reason; flagged addresses queue for review — the first piece of the full “challenge this number” pipeline described on accuracy.

What changed for your numbers: nothing in the math — ARV, rent, DSCR, and the four strategy scores are computed exactly as before. What changed is that every surface now agrees with every other surface, and a response never contradicts itself.

Platform release — no deal-math change (model and underwrite versions are unchanged). This is new serving surface, state, and the hardened MCP v2 tool layer.

  • Response envelope on every /api/* JSON. Additive: existing top-level fields are untouched; each response now also carries as_of, model_versions{model,underwrite,serving}, and warnings[]. Serving version is p5s8-2026.07.
  • Unified serving API. GET /api/map-pins (compressed pins + server-side clustering above ~400 in view), /api/listings gains bbox + filters + keyset pagination (cursor/next_cursor), GET /api/property/{id}, GET /api/comps/{id-or-latlon}, GET /api/tract/{geoid}, GET /api/property-history, GET /api/viewport-context, GET /api/og/{id}.svg.
  • MCP v2 tool surface. Plan-named tools — search_investment_inventory, deep_underwrite_asset (adds a 5-year cashflow projection), get_comps — with the old names (search_deals, underwrite, comps) kept as deprecated aliases that still work. Manifest at GET /mcp/v2/manifest. Strict input validation returns invalid_params{field,expected}; the error taxonomy is unified and closed.
  • Auth, scopes & metering. Authorization: Bearer pm_<key> carries scopes (read, underwrite, write-state, webhooks) and a per-key rate limit; anonymous keyless read stays at 30/min/IP. Per-key usage is metered (comps=5, underwrite=3, else 1).
  • Server-side state + real-time sync. /api/me/* (saved items, search profiles, underwriting presets, notes) + import-local, an append-only event log, and an SSE stream at GET /api/events?since=. MCP write tools (save_favorite_property, update_underwriting_preset, create_strategy_profile, add_note) and UI-controller tools sync live to the open map.
  • Alerts & signed webhooks. Strategy profiles matched inside the daily run; deduped alerts carry score + model-version provenance; delivery via Telegram and HMAC-signed webhooks (retries ×3, dead-lettering).

What changed for your numbers: nothing — ARV, rent, DSCR and the four strategy scores are identical. Only the envelope and the available surfaces changed.

Presentation and freshness release — the deal math is unchanged, but three things now match what a consumer expects:

  • Rank-transformed scores. Every served flip/BRRRR score is now a county percentile: the best tract reads 99–100, the median reads ~50. A 90 now means top decile, literally. Raw factor math is unchanged and stays internal; only the surfaced number moves onto a 0–100 scale you can read at a glance. The deal score for each listing is built from the rank-transformed tract score too, so a great location finally shows up near the top of the range.
  • Real listing dates. Listings now capture Redfin days-on-market, so “listed X days ago” reflects the actual on-market date instead of when we first saw it. Older listings where the source hides the day count still fall back to first-seen.
  • Photo service. A Street View photo service is wired in behind GOOGLE_SV_KEY; property and listing responses expose photo_url (a /api/photo?lat=&lon= link that never leaks the key). It ships dark — returns a 404 with a plain reason — until the key is set.

What changed for your numbers: scores spread across the full 0–100 range instead of bunching in the 40–85 band, so the same tract that read 85 may now read 99. Rankings and the underlying math are identical — this is a rescale, not a re-rank. Run a rescore after deploy so stored listings pick up the new scale.

Comp-engine guards and honesty fixes, following a 2026-07-08 audit that found a live $500k Logan Square 2-flat valued at ARV $1.37M from comps spanning $125k–$1.94M:

  • Similarity floor 0.45 and a minimum of 4 comps — dissimilar sales are rejected with reasons, not averaged in.
  • Dispersion gate: comp IQR > 45% of median → “insufficient comps,” not a number.
  • Dispersion penalty in the confidence formula uncapped (was capped at 35); comps drive ARV only at confidence ≥ 55 (was 35).
  • Income-approach cap re-anchored to the tract heuristic — a hot comp base can no longer float its own ceiling.
  • Circular ARV fallback removed: tract price null → unscored with a reason, never (ask + rehab) × 1.12.
  • Unscored states: deal_score: null with reason strings; null ≠ 0.
  • Client/worker parity: the in-map calculator now runs the same math as the API (it had a leftover 1.30 comp premium, 1.85× rent multiplier, and 2× income cap).
  • Public beta: reads and /mcp opened, 30 calls/min/IP.

What changed for your numbers: addresses with mixed-condition comp sets now refuse instead of returning an inflated ARV; the map popup and the API can no longer disagree; some listings that previously had scores are now honestly null.

  • Economics factor (0.35–1.0) multiplicatively gates the deal score.
  • Income-approach ARV capped at 1.5× (was 2× — too loose).
  • Bedroom-aware market rents: SAFMR ladder blended with ZORI, bounded 0.7–1.6× SAFMR.
  • Renovated rent multiplier 1.85× → 1.5× in the engine.

What changed for your numbers: deal scores collapsed on bad deal math even in good tracts; rents and income-approach ARVs dropped to defensible levels. Scores broadly moved down — that was the point.