Some days the work trickles. This weekend it poured. I sat down with Ceres, the multi-agent assistant I’m building for our raised-bed garden, when it was still a mostly empty repo: a few placeholders, some specs, and a database schema. By Saturday night 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. By Sunday, I had a roster of specialist agents wired to an orchestrator, a scheduled daily briefing, and live garden data on a dashboard in the app. All told it was 74 commits across the weekend: 51 on Saturday, 23 on Sunday. It uses the Anthropic API for the heavy tasks and delegates to local models like Qwen, Whisper, and YOLO where it makes sense.

Saturday 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.
Saturday 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, not to mention the constant notifications to upgrade to Business for needed features. 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.
Saturday 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 the UI to the root of the site and tucked the API under /api, so we just visit the address and see the garden, not a developer’s API. Soil sensors got a cell-level binding and their own management page. The Home Assistant 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.
Saturday 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”).
Saturday 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 hadn’t been before.
To cap the night, I gave the whole thing a face-lift: a real Today dashboard, a modern design system, and a bottom-tab nav on mobile that becomes a left rail on the desktop.
Sunday Morning: the camera learns the garden
Saturday’s vision service could tell a person from a tomato plant and measure brightness. Sunday it learned the actual garden. I built a bed-polygon authoring tool, then wired the vision service to read those bed shapes straight from the database so it knows which pixels belong to which bed. I added scheduled brightness captures and a daily snapshot of the garden, which now surfaces in the app.

A handful of smaller fixes made it feel more like ours: temperatures in Fahrenheit, cells labeled by what’s growing in them (“Roma Tomato (G-1)” instead of a bare grid code), and voice notes that gracefully handle a remark about one bed, several beds, or the whole garden at once.
Sunday Midday: an orchestrator and a roster of specialists
This is the part that makes Ceres “multi-agent” rather than “an app with one clever function.” I defined a uniform contract every specialist speaks, then built a Python orchestrator that dispatches work to them in parallel and merges the results. On top of it came three new specialists: phenology (what to anticipate as the season moves), pest (pressure to watch for), and a plant specialist that answers questions, joining Saturday’s watering agent.
Those agents now compose a real, multi-section daily briefing that runs on a 06:00 schedule (plus ad-hoc re-runs): today’s watering plan, today’s tasks, what to anticipate this week, and recent journal entries. I also added a proper in-app task list with quick-add, so a spoken “remind me to stake the tomatoes” becomes a checkable to-do.
And here’s the weekend’s second “delete what you planned” moment. The original design leaned on n8n for orchestration. Once the Python orchestrator existed, n8n was just a second system to babysit, so I cut it. Orchestration is Python plus cron, not n8n. Same lesson as the NocoDB pivot: the right tool was the one I controlled directly.
What’s live, and how it runs. Three specialists run on the daily schedule, dispatched in parallel by the orchestrator:
- Watering — the per-cell watering plan and summary you see on Today and the dashboard.
- Phenology — the briefing’s “what to anticipate” section.
- Pest — pest pressure, riding along in the anticipate section.
Two more answer on demand:
- Plant — behind the Ask box: ask a question and it answers from your live plantings, soil, weather, and the plant’s care profile.
- Diagnose — behind photo capture: snap a plant and the vision agent returns a health assessment.
More are still to come, a dedicated bed specialist and the year-over-year intelligence, chief among them.
Sunday Afternoon: a dashboard, taps that do things, and a guide
The last push was about turning data into something Kim can act on at a glance. I built a live dashboard: current conditions, a soil-moisture tile per plant (green/amber/red, with a “stale” state if a sensor goes quiet), the latest garden snapshot, and the day’s watering progress. All reachable from anywhere in the app without burying the day’s actions behind it.


Watering became actionable: instead of just reading “water beds A and C,” you tap a bed to mark it watered, and a progress bar tracks how many cells are done for the day. I also wrote a plain-language guide to what Ceres does, how to use it, and what happens automatically so Kim has a user guide that lives right in the app. A round of per-screen polish (collapsing duplicate briefings, a one-line answer preview in the history feed, a mobile thumbnail fix) closed out the day.

Where it stands
Ceres went from a scaffold to something we genuinely use, in a single weekend: we can talk to it, show it a sick leaf, ask it questions, and get a real watering plan now backed by an orchestrator coordinating a roster of specialists, a daily briefing on a schedule, and a live dashboard, with a history of all of it. There’s plenty left: more specialist agents, the year-over-year intelligence, and hardening. But this was the weekend it stopped being plumbing and started being an assistant.
Not a bad weekend in the garden.
