Some days the work trickles. Today it poured. I sat down with Ceres, the multi-agent assistant I’m building for our raised-bed garden, as a mostly-empty repo: a few placeholders, some specs, and a database schema. By tonight it was transcribing voice notes, diagnosing plant photos, answering questions about what’s actually growing out back, and telling us which beds to water tomorrow. Forty-nine commits. Let me walk through how it came together.

Morning: pouring the foundation
The day started unglamorously, which is usually how the good days start. I stood up the FastAPI backend shell with health and version endpoints, and wired in a pytest config so the services could actually be tested.
Then came the supporting cast that makes the rest possible:
- A speech-to-text service using faster-whisper.
- A vision service with YOLO person-detection and a brightness check, pointed at the real Frigate camera feed.
- A PWA static-hosting placeholder with an NGINX Unit config, and a daily-briefing placeholder page to render into later.
- Database backup, retention, and media-cleanup scripts, because a garden assistant that loses three years of history is worse than no assistant at all.
A chunk of the morning was just GPU wrangling: getting the STT and vision containers to target the right CUDA versions and land on Nova’s 2080Ti instead of fighting over the wrong device.
Midday: capture, a model router, and a hard pivot
This is where it started to feel alive. I built the voice-capture pipeline: record in the browser, POST the audio, run it through speech-to-text, then a local parser, then bed-inference from the camera, and finally write a structured event to the garden database.
The most important piece from this stretch is the model router. Ceres isn’t one model; it’s a policy table that dispatches each task to the right brain. Cheap, high-volume parsing runs on a local Ollama model; the judgment-heavy work routes to Claude. Every call logs its cost. Flipping a task from local to cloud is a one-row change, no code edits.
Then I made a decision I’d been circling for a while. I’d been building the data-entry surface in NocoDB, fighting its dropdown columns and constraints all morning. Midday I stopped and replaced the whole NocoDB editing surface with server-rendered forms I control directly. Sometimes the right move is to delete the thing you spent three hours on. The forms are plain HTML that work without JavaScript, which is exactly what I want for something we will use from our phones in the garden.
Afternoon: the garden takes shape on screen
With forms in place, the UI filled in fast. I seeded the beds from our garden.yaml layout, built a garden overview page showing every bed and cell and what’s planted in each, and reworked the schema so plantings live at the cell level instead of the whole bed (so “the broccoli in J-2” is a real, addressable thing).
I moved Kim’s UI to the root of the site and tucked the API under /api, so she just visits the address and sees her garden, not a developer’s API. Soil sensors got a cell-level binding and their own management page. The HA telemetry ingest went live on an hourly cron with freshness checks, pulling calibrated soil-moisture readings into the database. Tempest weather data started flowing in, with proper handling of cumulative rainfall.
And then the first real agent: the watering specialist. It gathers the live garden state (what’s planted, latest soil moisture, recent rain, the forecast) and decides, cell by cell, what needs water today, with a confidence level on each call. That output renders into the daily briefing page. Ceres was no longer just storing data; it was reasoning about it.
Evening: the agents come alive
The late-afternoon run was the fun part, the payoff for all the plumbing:


- A voice-note capture page so we can just talk to the garden log.
- Ask-the-agent: a question box grounded in the live garden state. Ask “when do I harvest the broccoli in bed J?” and it answers from your actual plantings, soil, weather, and the plant’s care profile, not generic internet advice.
- A photo-diagnosis pipeline: snap a plant, optionally tag the cell, and a vision agent returns a health assessment and findings.
- Weather forecast ingest, so the watering agent can reason about rain that hasn’t fallen yet (“skip today, it’s coming Thursday”).
Night: polish, a history feed, and a sneaky bug
The last stretch was making it feel finished. The agents return Markdown, but the pages were showing the raw # and ** characters, so Claude built a small, safe Markdown renderer (and later taught it to handle tables) and shared it across the ask page and elsewhere.
Then the feature I realized I needed quickly: a history feed. One page to scroll back through everything Ceres has done with you, the questions you asked, the notes and photos you logged, the briefings it produced, all in one reverse-chronological timeline. That meant persisting the question-and-answer exchanges too, which they weren’t before.
And then the bug I’d planted that morning came home. I asked for “things to do tomorrow” and got a list for the day after tomorrow. The culprit: the agents were reckoning “today” in UTC. In the evening here in Pacific time, UTC has already rolled over to the next day, so “today” was wrong and “tomorrow” was wrong by one more. I gave Ceres a proper sense of garden-local time, and while I was at it, made the history feed show timestamps in friendly 12-hour local time (“Jun 13, 2026 · 8:14 PM PDT”) instead of 24-hour UTC.
Where it stands



Ceres went from a scaffold to something we could genuinely use in a day: we can talk to it, show it a sick leaf, ask it questions, and get a real watering plan, with a history of all of it. There’s a lot left (more specialist agents, the year-over-year intelligence, hardening), but today was the day it stopped being plumbing and started being an assistant.
Not a bad day in the garden.