HomeZero · Agent Model on Flue · 2026-06-23

The Advisor Agent, made operational.

A spec for getting one buyer's advisor off the birth table and onto a living loop — native Flue skills it can rewrite on itself, a private brain of yearnings and journal, and five beats of rhythm that make it get better every day.

Flue · Cloudflare DO advisor.homezero.md Opus 4.8 · medium status: APPROVED — plan next
00 · In one breath

What we're building

Today the advisor is born but mute: it self-configures an identity at birth, then chats — and writes nothing to the buyer's board. New users get an empty board. This spec turns it into a working agent.

Four things make the advisor operational, and each maps to a layer it doesn't have yet:

The keystone idea: skills are files the agent owns, and Flue re-reads them every session — so a skill can measure itself and rewrite its own playbook overnight. That gives us self-improving skills with no framework support, just writeFile and a metric.

01 · Ground truth

Where we are — the as-built shell

Verified in code (flue/src/agents/advisor.ts, flue/src/advisor/*) on 2026-06-23. Everything the loop needs is genuinely net-new — there's nothing to retrofit.

What exists live

  • Birth self-config. A model-driven ladder via 4 tools (check_identity, set_step, write_identity, finish_birth) that writes /IDENTITY.md to a Durable Workspace and marks the advisor born in Convex.
  • Workspace fs. WorkspaceFileSystem(getDefaultWorkspace()) over the DO's SQLite — but the wrapper only exposes exists / readFile / writeFile (no directory listing).
  • Model. anthropic/claude-opus-4-8, thinking medium, direct API (no gateway). GPT-5.5 registered, unused.
  • Auth. Per-agent route middleware gating on AUTH_PASSWORD (Bearer or ?key=).
  • Convex hand. callConvex / callConvexQuery with a deploy-key admin client — can call internal mutations.

What's missing net-new

  • No board-write hand. The entire toolset is birth-only — the advisor cannot author a card. board.authorHomeCard exists; nothing in the advisor calls it. the real risk
  • No loop. Zero Flue scheduling anywhere (extend / schedule / scheduleEvery / onStart / queue all absent — grep-verified). The advisor DO is pure request/response. The only cron wakes the market.
  • No brain. The advisor uses no Turso. Yearnings don't exist as a table anywhere.
  • No skills. No .agents/skills/ — only /IDENTITY.md on disk.
  • Identity gap. Browser chat mints a random web-xxxx instance id; the doorbell uses the real userId. Different DOs. The loop must key on userId.
02 · The shape

Architecture — four layers

One DO per buyer (/agents/advisor/:userId). Inside it, four layers with clean boundaries. The first column is the layer; the second is where it physically lives.

Hands tools · code
author_home_card, pin_ask, read_board, read_reactions, brain_read/write, journal_append, scout_query — agent-level defineTools in flue/src/advisor/. The actual capability.
Know-how skills · SKILL.md
One native Flue skill per capability, in .agents/skills/<name>/SKILL.md in the Workspace. Teaches the model how/when to use its paired tool. Loaded on demand via activate_skill.
Brain per-buyer Turso db
yearnings (what it's learning) + per-skill metrics + tuned params + a sessions log (the transcript). Private learned state. A public-safe confidence subset is projected to Convex for the board's "what I'm learning" panel.
Filesystem same db · files table
IDENTITY.md, JOURNAL.md (relationship, tagged by home slug), and each skill's PLAYBOOK.md — all rows in a files table in the same per-buyer Turso db. One addressable mind: brain + filesystem + session log together.
The boundary that keeps it honest: the market owns objective home knowledge (briefs, comps, the wiki) in its Turso; the advisor keeps only this buyer's relationship and subjective reactions in its brain + journal. The advisor reads the market's Turso (scout) but never duplicates it. Convex stays the shared, app-served projection both write into.
03 · Spike-verified

Flue skills — the real primitive

We read the installed runtime (@flue/runtime@1.0.0-beta.2). Skills are real and fully implemented, not a stub. Here's exactly what they are, so we build on truth.

FactWhat it means for us
A skill is a SKILL.md — frontmatter (name, description) + a markdown body.Each capability gets a plain file. The body is the playbook the model reads.
Discovered from .agents/skills/<name>/SKILL.md in the agent's own workspace (or registered as metadata in createAgent({skills})).We use workspace skills — files the advisor owns and can rewrite. That's the whole game (see §04).
Progressive disclosure. The system prompt lists only names + descriptions; the model calls a native activate_skill tool to load the full body on demand.Skill instructions don't burn context until needed. The catalog can be large and cheap.
Reusable across agents — the same skill object drops into both the advisor and the market.The shared Brain skill (how to use Turso) is written once, used by both.
Skills do not bundle tools. Tools stay agent-level; allowed-tools frontmatter is parsed but not enforced.The tool↔skill pairing is our convention, by discipline — exactly the model "a tool needs a skill so it knows how to use it."
session.skill(name, {args, result}) can invoke a skill programmatically, with structured output.Beats can run a skill in code (e.g. the dream calls a reweight skill and gets back a typed result).
The one correction to the mental model: Flue doesn't weld the tool to the skill. The tool is the hands (code); the skill is the know-how (SKILL.md); we pair them ourselves and reference the tool inside the skill's body. That's lighter and more flexible than a welded primitive — and it's what makes self-rewriting possible.
04 · The keystone

Self-improving skills

Flue has no native eval / metric / tune hook — grep-verified (the only version machinery is schema-versioning). So self-improvement is ours to build. But it falls out almost for free, because of one fact: workspace skills are files, and Flue re-discovers them every session.

So a skill is not static know-how. It's a unit with three parts and a loop:

1 · Hands
its tool(s) — code.
2 · Know-how
its SKILL.md — the playbook.
3 · Metric
a number read from real signal (did the buyer engage?), stored in the brain.
4 · Tunable state
params in Turso and/or the SKILL.md body itself.

The dream closes the loop overnight:

// nightly, per skill
const m = brain.metric(skill)         // e.g. board: 👍-rate & answer-rate on cards it authored
const verdict = reflect(skill, m)      // what's working, what isn't (a reasoning turn)
brain.tuneParams(skill, verdict)        // numeric weights/thresholds → Turso
fs.writeFile(`.agents/skills/${skill}/SKILL.md`, rewrite(body, verdict))  // the playbook edits itself
// next session: Flue re-discovers the improved SKILL.md automatically
ours, not flue's  This single pattern subsumes three things we'd otherwise build separately: the market's missing "metric skill," the yearnings-confidence reweighting, and "the agent gets better every day." Every skill is a metric skill.

Build-time check (in the plan): confirm the Cloudflare DO adapter runs workspace skill-discovery against getDefaultWorkspace() — the primitive is built for it, but we prove the self-rewrite takes effect on our substrate before leaning on it.

05 · Private state

The brain DB — one addressable mind

Every agent's whole mind is one per-agent Turso db, addressed by its id. For the advisor that's a per-buyer db named from the userId (1:1 — the db IS the user), provisioned at birth. Because it's libSQL, you can connect to it from anywhere — the basis for the SSH/TUI operability below. It holds three things:

// the FILESYSTEM
files( path PRIMARY KEY, content, updated_at )   // IDENTITY.md · JOURNAL.md · <skill>/PLAYBOOK.md
// the BRAIN
yearnings( key PRIMARY KEY, prompt, confidence REAL, weight REAL, status, source, last_touched )
skill_metrics( skill, metric_key, value REAL, n, window, updated_at )
skill_params(  skill, param_key, value, updated_at )   // tuned by the dream
// the SESSION LOG (mirror of the DO's transcript, so the dream reads full history)
sessions( turn_id PRIMARY KEY, started_at, role, content )

The filesystem is a table. IDENTITY.md (who I am), JOURNAL.md (the relationship — entries tagged by home slug, so the advisor's "wiki" grows out of the journal), and each skill's learned PLAYBOOK.md are all rows in files, read/written via fsRead/fsWrite. Nothing of the agent's lives in the DO workspace anymore.

Yearnings = the advisor's standing questions about the buyer (budget comfort, town-vs-land, fixer appetite, owned water, timeline, who it's for, commute), seeded at birth, plus custom ones. One drives each card's question; the dream reweights confidence from answers. A public-safe confidence subset is projected to Convex for the board's "what I'm learning about you" panel — the brain db stays source of truth.

What stays in the DO: Flue's own engine tables (flue_sessions, flue_session_entries, flue_runs…) — how the agent runs (fiber recovery, replay). The brain db is what it knows. A turn-end hook mirrors the DO transcript into the brain's sessions table so the nightly dream consolidates the full history before Flue compaction drops it.

06 · Rhythm

The loop — five beats

The DO gets a rhythm via extend({ base }) from @flue/runtime/cloudflare: onStart() registers the recurring beats with scheduleEvery; one-shots use schedule. Backed by DO alarms (Flue owns alarm() — we never set it directly). The 0 9 * * * Worker cron stays as a billing-safe floor.

react
event · per zero

A buyer zeroes a home → doorbell. Brief it through the buyer's lens, author a card, ask one good question, set 👍/👎.

tend
every 60 min

Board housekeeping. Append to clusters, narrate shifts, keep the board coherent. Cheap.

morning brief
daily · AM

A short WhatsApp check-in — greeting + one overnight highlight + a gentle question + board link. Simple now, tuned later (§09 B). Board-card fallback if WhatsApp unwired.

scout
nightly · ≤1/day

Read the market's Turso, rank unzeroed homes by active yearnings, author scout cards. Never scrapes Zillow.

dream
nightly

Consolidate. Answers → yearning confidence; per-skill metric → rewrite each skill's SKILL.md + params; summarize the journal; re-org the board.

Wake-intent rides inside the message (Flue convention): body: { message: JSON.stringify({ kind:'zero', slug }) } — the model parses the kind. The react beat is fired by a new advisor doorbell (sibling of the market's ringMarket), rung from stars.ts at the same two if (created) branches that ring the market, scoped to the zeroing buyer's userId.
07 · The pairs

The skill catalog

Each row is a tool ↔ skill pair: the hands, the playbook that teaches their use, the metric that measures the pair, and the state the dream tunes. brain is the one shared with the market.

board net-new
toolsauthor_home_card · pin_ask · read_board
know-howCompose a card from the home + the buyer's lens; place it; ask exactly one question worth answering.
metric👍-rate & answer-rate on the cards it authored.
tunesQuestion style, card framing, which yearning to probe.
reactions net-new
toolsread_reactions (off ask.reactedAt / answers)
know-howTurn a 👍/👎 or an answer into a yearning update — which signal moves which belief.
metricHow often its yearning-guess predicted the next thumb.
tunesSignal→belief mapping; confidence step size.
brain shared with market
toolsbrain_read · brain_write (Turso)
know-howWhen to read vs write; the yearnings/metrics/params schema; how to query.
metric—(infrastructure skill; correctness, not engagement).
tunesMostly static; the shared contract.
memory net-new
toolsjournal_append · journal_read
know-howWhat's worth journaling; tag entries by home slug; prune in the dream.
metricRetrieval usefulness — did recalled entries shape a good card?
tunesWhat to keep vs prune; summary granularity.
scout net-new
toolsscout_query (reads market's Turso)
know-howQuery the market brain, rank by active yearnings, exclude already-zeroed, pick what to surface.
metricSurfaced → zeroed / engaged conversion.
tunesRanking weights; how many to surface; freshness vs fit.

Phase 1 scout ranks structurally over the market's listings. Phase 2 swaps to vector_top_k once the market lands brief_vectors / listing_vectors — same skill, better query.

08 · First boot

Birth & environment setup

"Set up its local environment" has a precise meaning now: at birth the advisor provisions its own brain, memory, and skill files — then reconciles the identity it already self-authors. The existing birth ladder (waking → studying_market → authoring → scouting → ready) extends to do it:

StepWhat gets provisioned
wakingensureAdvisorSchema() on the advisor Turso (yearnings, skill_metrics, skill_params). Seed the skill files into .agents/skills/ from the birth template.
studying_marketRead the market's Turso to ground itself in the place it serves.
authoringWrite /IDENTITY.md (today) + seed yearnings from onboarding (universal core + custom). Start /JOURNAL.md.
scoutingFirst scout pass → the buyer's first board cards (no longer an empty board).
ready → finish_birthonStart registers the recurring beats. The loop is live.

Because skills are seeded as files, every newborn advisor starts with the current best playbook — and then diverges as it tunes itself for its own buyer.

09 · For your sign-off

Open decisions & my calls

A · Skill = a SKILL.md the agent rewrites on itself ✓ confirmed

Locked (Pete, 2026-06-23): skills that rewrite themselves. Flue gives us discovery + progressive disclosure + re-read-per-session; we add the metric and the overnight rewrite. Every skill is a metric skill.

B · WhatsApp morning briefing — ship a simple one now ✓ confirmed

Locked (Pete, 2026-06-23): a real daily morning briefing on WhatsApp — stubbed and simple to start; we tune the content later. Pete's framing: "remember our audience — someone shopping for a home. A little morning check-in. Don't overwhelm. They have the board."

What it is (winged, v0):

  • One short WhatsApp message, once each morning. A few lines, plain text — not a digest.
  • Shape: warm one-line greeting → the single most relevant thing overnight (one scout find or one board change) → one gentle question or nudge → a link to the board.
  • Tone: a friend keeping an eye on the market for you. Calm, never salesy, never a wall of listings.
  • One focus per morning. On a genuinely quiet night, a one-liner ("quiet morning — still watching") or skip, rather than manufacture noise.
  • Delivery is pluggable. WhatsApp send via a new flue/src/advisor/whatsapp.ts; if a buyer has no number/opt-in yet, fall back to a "good morning" board card so it always works. Meta template compliance is a known follow-up — fine to wing the first cut.

Example: "Morning 👋 Found one new place overnight that fits your 'space without the upkeep' lean — a 3-bed with a flat lot. Want me to dig in, or skip it? Your board's here: [link]"

Already locked earlier and not re-opened here: advisor brain = journal + yearnings; first-class native Flue skills; scout reads the market's Turso; Turso = private brain, Convex = shared projection; build on the latest @flue (pin the version, confirm the tool shape).

10 · Sequencing

What ships first

The advisor becomes useful the moment it can write to the board — so that's the first shippable cut, before the full rhythm.

MilestoneDeliversBeats live
1A · It writes@flue pinned + tools confirmed · board tool + skill · advisor doorbell · react beat · chat keyed to userId. New users get a real, agent-authored board.react
1B · It has a brainAdvisor Turso + yearnings + birth seeding · the brain & memory & reactions skills · learning-panel projection.react
1C · It has rhythmextend/scheduleEvery · tend · scout · dream (incl. the self-rewrite loop).+ tend · scout · dream
1D · It greetsMorning-brief beat → a simple daily WhatsApp check-in (whatsapp.ts), board-card fallback if a buyer isn't WhatsApp-wired. Content tuned later.+ morning brief

Then the market agent spec (depth: county records, Turso vectors, the wiki) — same skill model, same rhythm primitive, the shared brain skill already written here.