HomeZero · Design spec
The kanban built the surface. This builds the gardener — the advisor cell that places zeroed homes, writes the take when a Brief lands, digests every tap, re-ranks, and asks the next question.
The one rule
The wake is gated. The judgment is not.
The advisor runs at most once every ~30 minutes — cost is bounded by how often it wakes, never by throttling its thinking. Within a wake, every call is the agent's own reasoning: which column a home belongs in, what the take says, what to re-rank, which question to ask. The single thing that happens with no agent in the loop is the instant inbox receipt the moment a home is zeroed.
01
A zeroed home travels three hops to become a tended card. The first two already exist — the star path and the market cell's per-home Brief. This spec adds the inbox card in hop 1 and all of hop 3.
stage:inbox card appears at once — titled from the Zillow capture, reading “Analyzing…”. No agent fires. Zero ten homes, the advisor never wakes.zero-created doorbell); it drains its queue and writes a covered listing + verdict. The Brief is its deep read of the county data — parcels, deeds, permits, water rights, distress — not Zillow. Zillow is only the capture that triggers it.Live by default
Every hop is a real Convex write to boardCards, and the board subscribes with a reactive query — so a buyer sitting on /board watches it happen: the card appears in Inbox the instant they zero, the take writes itself in and the card slides to Worth-seeing when the advisor tends, re-ranks and the new question land — no refresh. The design rule that buys this: nothing lives only on the cell; state is always a write Convex can push.
02
The buyer should see a zeroed home immediately — not up to 30 minutes later, when the advisor next wakes. So the zero path (hop 1) writes the inbox card directly, with no agent involved.
Where: starHome in convex/stars.ts already creates the saved row. Extend it to also upsert a type:"home" card at stage:"inbox" — title, price and thumbnail pulled from the capture (or the listing, for an already-covered save), body empty until there's a take. A plain write inside an existing mutation: no scheduler, no doorbell.
The inbox card is born before a listing exists, so it can't key on a slug. Every home card keys on home:zpid:<zpid> instead — stable from the moment of zero through coverage. When the advisor later authors the same home, it reuses that key, so the “Analyzing…” card is patched in place into a placed, commented card. One card, never two. The backfill adopts the same key (covered listings always carry a zpid).
03
One heartbeat is one pass. The agent reads what changed since its cursor and works the batch in order. Every step is judgment expressed through the Convex surface — there is no scoring formula in code.
Idempotent, ≤1 in flight. The pass only touches what changed since the cursor, so a re-run with no new signals and no newly-landed Briefs is a no-op. The heartbeat is serial; a manual trigger fired mid-pass just folds into the running one — no second pass.
This engine authors only the entry — inbox → worth-seeing / passed. The faded columns (scheduled, seen, shortlist) are buyer- and staff-driven states the kanban already tracks; the tend pass doesn't move cards there.
04
A lastTendedAt field on the buyers table — one advisor serves one buyer, so one cursor each. A pass reads since = lastTendedAt and stamps its start time. On completion it sets lastTendedAt to that start time — not the end — so any signal that arrived mid-pass is re-read next time instead of being skipped. Stored in Convex so it survives a cell restart or rebuild.
05
One new read, one cursor write, and the receipt. Everything else reuses what the kanban MVP already shipped.
New
tendDigest — internal readthe board + signals since the cursor + the inbox homes now ready to placemarkTended — internal writestamps lastTendedAt at pass endstarHomeextends an existing mutationReused
addCardplace a home, refresh the note, author the asksetStageInternalthe agent's own sort (no buyer signal)highlightCardelevate the one to look at firstdismissCardretire a stale card — never one the buyer pinnedThe agent writes with the advisor cell's Convex deploy key, as agent actions following the playbook — no sync script on the Mac.
06
The tend logic is prose the pi agent follows, not a script — the same shape as the market cell's daily loop. It ships in the advisor cell seed, delivered through cellTemplateFiles:
tendDigest, work the batch, call markTended. Carries the cost discipline (one ask max, silence is allowed) and the “forgive a verdict” latitude.cells talk <advisor> "tend now" runs the same pass on demand. Staff / dev only in the MVP — no buyer-facing refresh button.The agent picks from its own uncertainty — the widest gap between what it knows and what it needs to serve this buyer (a yearning it can't yet weigh, a budget ceiling it's guessing at, a trade-off the signals left ambiguous). Bounded by two rules: at most one open question, and silence beats a low-value tap.
The buyer model is the advisor's on-cell memory (BUYER.md / yearnings) — the store it reasons from, updated each pass. The Convex buyers.profile stays the public-safe mirror for the app to read; this engine doesn't change that contract.
07
stage:inbox card keyed by zpid with a capture-derived title; a covered save sets listingId; re-zeroing doesn't duplicate.since; the ready set includes a now-covered inbox home and excludes a still-uncovered one.08
tendDigest, markTended, the cursor, the zpid rekey, and tests. Ships on its own — zeros land in the inbox column live, before the cell adopts the playbook.home:<slug> cards (pre-launch, ~one live buyer) so they don't double with the new zpid cards.The buyer-facing refresh button; a per-Brief doorbell (commentary waits for the heartbeat by design); tour booking and outbound to listing agents; multi-buyer advisors; any scoring formula in code.