HomeZero · Agent Model on Flue · 2026-06-23
One agent per market. It gathers everything knowable about the homes there — listings, parcels, sales, distress, water — into a structured brain and a vector mind, weaves a living wiki of briefs, and serves it to the advisor and the app. This is the full capability, captured.
If the advisor is one buyer's relationship, the market agent is one place's memory. It's the only thing in the system that scrapes, ingests, and reasons over the raw facts of a market — and the advisor reads its brain rather than ever touching a source itself.
It already does the thin slice (Zillow listings → Turso → per-home verdicts in Convex). This spec captures the parity rebuild of everything the old Delta agent did, on Flue: county records, a vector mind, and a wiki — all on the full storage stack every agent gets.
Unlike the advisor, the market agent is real end-to-end for its current scope (flue/src/agents/market.ts + flue/src/market/{brightdata,publish,tools,store}.ts, 74 vitest specs).
listings → publish_cards (clobber-safe). Fired by the 0 9 * * * cron via {kind:'sweep'}.{kind:'drain'} doorbell → per-home verdict / valueFactors / deepDive → Convex listings:upsert / setDeepDive.getTursoClient(), env TURSO_DATABASE_URL / TURSO_AUTH_TOKEN; tables listings + ingest_runs.listings is scalar only; no embeddings, no vector_top_k.Every agent in HomeZero gets the same five places to keep things. The point of the market agent is that it's the one that uses all of them, hard. The advisor uses a lean slice of the same stack — shown alongside so the two specs read as one model.
SKILL.md), study notes & source playbooks, wiki source it can edit./IDENTITY.md, /JOURNAL.md, its own skill files.defineTools.files table (its filesystem: skills, wiki source, study notes), its structured + vector brain, and a session log — keyed 1:1 to the market (north-fork-valley) and reachable from any libSQL client (SSH/TUI operability). Flue's flue_* engine tables stay in the DO.
The market uses the identical skill model proven in the advisor spec: a skill is a SKILL.md in .agents/skills/, discovered from the workspace, loaded on demand via activate_skill (progressive disclosure), reusable across agents. Tools stay agent-level code; each tool is paired to a skill by convention — the tool is the hands, the skill is the know-how that teaches its use.
Self-improvement is built the same way (Flue has no native hook): a skill carries a metric from real signal and tunable state (params in Turso and/or its own SKILL.md body), and the nightly dream rewrites the playbook. The market is where this pattern is most obviously native — its missing "metric skill" is a self-improving skill:
// market's daily billboard / metric beat const snap = brain.computeMarketMetrics() // median asking, DOM, verdict-mix, honest-deal count brain.writeMetrics(snap) // → skill_metrics (Turso) const drift = compare(snap, lastWeek) // what's moving in this market fs.writeFile('.agents/skills/comps/SKILL.md', retune(body, drift)) // the comps playbook adapts
The same loop tightens the comps skill (do its comps agree with what actually sold?), the county-ingest skills (did a source's parse drift?), and the study cadence (which yearning is worth a pulse). Every market skill measures itself.
listings mapping must be checked against one real record before the metric skill computes medians off it — a silent alias drift would poison every metric.A parcel-centred schema, jurisdiction-agnostic via an EAV facts table so a new county doesn't need new columns. Vectors are native libsql — F32_BLOB(n) + libsql_vector_idx (DiskANN) + vector_top_k — no Stoolap, no new dependency, just an ensureSchema add.
parcels( pin PRIMARY, geom, acres, owner_inst, … ) // the spine (ArcGIS + assessor) parcel_facts( pin, key, value, source, observed_at ) // EAV — water, zoning, soil, CDL… parcel_listings( pin, zpid ) // bridge scalar listings → the spine sales( pin, price, date, doc_ref ) // real comps, not estimates deeds · foreclosures · distress · permits · tax_history listing_vectors( zpid, embedding F32_BLOB(1024) ) // the vector mind brief_vectors( slug, embedding F32_BLOB(1024) ) wiki_links( from_slug, to_slug, kind, weight ) // the comps graph (§06) yearnings · skill_metrics · skill_params // shared brain shape
Pete's nudge — "a vector we can think of creatively." A few ways the market puts it to work beyond plain similarity:
vector_top_k over listing_vectors + brief_vectors, then graph traversal of wiki_links — neighbours by meaning, cross-checked against real sales.The wiki is the market's edge that grows with every zero. It's not a separate store — it's the wiki_links edge table over the briefs, woven at publish time. Each new brief is linked into its neighbours (same street, same band, substitute, comp, shares-a-risk), so comps become a graph traversal seeded by a vector search, not a SQL WHERE. Built fresh — the old Delta agent never had it.
publish_briefing — authoring a brief also writes its edges, so the graph is always current.Same primitive as the advisor (extend + scheduleEvery over DO alarms, cron as the floor). The market's daily study is bounded — one yearning per pulse on a study budget — which doubles as the source rate-limiter so it never hammers a county site.
live A home is zeroed → produce verdict / valueFactors / deepDive → Convex.
live Pull current listings (Bright Data) → Turso → publish cards. Lifecycle reconcile.
One demand-weighted standing question → ingest a county source via its pattern adapter → into the brain.
Compute & publish market metrics (the metric skill): median asking, DOM, verdict-mix, honest-deal count.
Weave the wiki, refresh embeddings, reweight yearnings by demand, rewrite skill playbooks from their metrics.
Market tool↔skill pairs. live = the tool exists today; new = parity rebuild. shared = same skill object as the advisor.
The egress question is settled (spike-verified): no county or state source needs a residential IP — that was a Zillow-only artifact. 8 of 9 sources are a direct datacenter Worker fetch(); the lone exception has a clean replacement. Residential egress stays scoped to Zillow.
| Source | Endpoint | Access |
|---|---|---|
| Parcel spine | services3.arcgis.com/…/Delta_County_Parcels/FeatureServer/0/query | direct · public (TaxPIN_GIS join) |
| Assessor / deeds | assessor.deltacountyco.gov/assessor/web/ | direct · server-HTML, no wall |
| Permits | apps2.colorado.gov/dora/… | direct · Tyler eLicense HTML |
| Foreclosure docs | deltacoco.api.civicclerk.com/v1/Events | direct · OData JSON |
| Public Trustee | ptrustee.deltacounty.com | direct · HTTP-only ASP.NET |
| Water rights | dwr.state.co.us/Rest/GET/api/v2/structures?county=DELTA | direct · open REST |
| Soil / crops | SSURGO SDA · CDL/CropScape | direct · open REST |
| qPublic (fallback) | qpublic.schneidercorp.com | Browser Rendering · optional — EagleWeb replaces it |
So Phase 2 = direct fetch + an optional Cloudflare Browser Rendering binding for the one bot-walled source. No wells, no proxy. Each source is a pattern adapter sized to fit one study pulse.
The market agent is not in the current build. We ship the advisor first (1A–1D). This spec is the capture so the parity rebuild is ready to lift when its turn comes. When it does, the order roughly mirrors the advisor's:
| Phase 2 milestone | Delivers |
|---|---|
| 2A · Spine | Parcel schema + EAV facts + the structured brain tables; bridge listings to parcels. |
| 2B · Records | County-ingest skills on the study beat (assessor, distress, permits, water) via pattern adapters. |
| 2C · Vector mind | Native libsql vectors + embeddings; comps via vector_top_k. |
| 2D · Wiki + metrics | Wiki-weave on publish; the metric skill + billboard; market dream (reconcile, reweight, self-tune). |
Same skill model, same rhythm, the shared brain skill already authored in the advisor build. Nothing here is orphaned — it's queued.