joey.hersh

systems / secondhome

SecondHome

Source-to-score qualification with a reason on every record

A score without a reason is just a guess with confidence.

Period
July 2026
My role
Creator and lead engineer. Designed and built the data connectors, research workflows, scoring engine, and review app for a real-estate operator.
State
Working MVP with a demo, an acceptance packet, and a thousand passing tests.
  • 20+County connectorsscraped portals and open-data feeds
  • 1,039Tests passingreal fixtures from captured portal payloads
  • 23Schema migrations
  • 2Independent signals before a contact counts
  • Python
  • Postgres
  • FastAPI
  • Windmill
  • Next.js
  • MCP
  • Anthropic API
  • Playwright
an isometric county map tile with property parcels outlined, a magnifying glass hovering, small pins with tiny glow
Twenty county sources, one schema, a written reason on every score.
source-to-score.flow: architecture flowadjustmentsCounty recordsforeclosure · tax · evictionforeclosure · tax · evict…Normalizeone schemaone schemaEnrichmentownership · equity · skip traceownership · equity · skip…Agent skills17 bounded tasks17 bounded tasksContact confidenceidentity · authority · reachabilityidentity · authority · re…Scoring engineversioned weightsversioned weightsCall rationalefrom evidencefrom evidenceCaller briefsources · confidence · whysources · confidence · whyOperator reviewcapturedcaptured

Diagram nodes, in flow order: County records (foreclosure · tax · eviction); Normalize (one schema); Enrichment (ownership · equity · skip trace); Agent skills (17 bounded tasks); Contact confidence (identity · authority · reachability); Scoring engine (versioned weights); Call rationale (from evidence); Caller brief (sources · confidence · why); Operator review (captured).

deterministic code model judgment human durable state tool / API

The problem

Distressed-property leads are cheap to list and expensive to qualify. A broker can't research eight hundred foreclosure filings a month. The economics only work if a machine does an unreasonable amount of work per lead and then tells a human, honestly, which ones deserve a phone call and why.

The architecture

Public records come in through county-specific connectors, twenty-plus of them, some of which reverse-engineer ancient government web portals and work around their bugs. Everything is normalized into one schema that keeps the original source on every field. Enrichment providers add ownership, equity, event timing, and contact data behind a shared interface, so a vendor can be swapped without touching the scoring.

Seventeen bounded agent skills do the fuzzy parts: extraction, maturity inference, decision-maker ranking, street-view condition, contradiction detection, and the caller brief itself. Each skill is a versioned task spec loaded by content hash, and the agent's system prompt has hard rules against foreclosure-rescue language.

Every distress signal carries a source class, official or vendor or agent, and every field carries an epistemic label: hard, inferred, estimated, or agent judgment. Contact channels are scored on identity, authority, reachability, and eligibility with named floors, and a channel needs at least two independent signals before it's promoted to primary.

The score is a deterministic, versioned formula over persisted evidence: opportunity value times distress severity times urgency, adjusted by confidence, contactability, and freshness decay. It does no I/O, so the number on the screen composes exactly from the components you can inspect. Then a model writes a plain-language rationale from that evidence, and the whole thing lands in a caller brief served to the operator's app and, through an MCP server, to their agent.

I set three boundaries early. Downstream access is read-only. Scoring is client-specific, not a universal number. Nothing transfers a score without its provenance.

SecondHome, in twenty-five seconds

SecondHome reads twenty county sources, keeps the source on every field, scores each lead with a formula you can inspect, and writes the reason to call before anyone dials.