Garden plans that nobody checks

A model designs the garden and code decides whether the plan goes out. No person reads it first.

It started with the garden for my own plot. The 3D viewer was working within the first few days - you scroll through the months and watch the hedges grow - and the planting plan for the whole plot took about five weeks.

Then I wanted to see if someone else could get the same thing from a form - draw the plot, describe the house, say what you want, get a plan back. I decided early that no person would review the plans; tests would have to show whether the tool works on its own. I set what the product should do and what it must never do.

The first test run looked fine. The model wrote the plan and, next to it, the checks for it. At 21 of 21 passing it still had sun-loving plants under a birch, which only showed up when the numbers behind the pass were read; it fixed its checks and its plan and finished at 23 of 23.

So the checks became product code, written before any plan and the same for every customer. Run through that suite, the 23-of-23 plan got 11 passes, 3 warnings and 4 failures: sun plants in real shade, perennials planted alone or in pairs, an allium 30 cm from the middle of a hydrangea, and ornamental allium in a house with a dog.

Who does what

The model writes plan.json: zones, where each tree and shrub goes, a recipe for each bed (which species, in what share) and a text for the customer that explains the choices. It can only use plants from a database of about 400 species, which it reads through a small query tool and never as a whole file.

Top-down plan of an invented test plot: a house turned at an angle, a garage, a hedge along one side, a large walnut tree, beds and a lawn
Test plot E from above. The model placed the trees and hedges and wrote the bed recipes; code laid out every plant in the beds.

Code does the rest. It lays out the beds from the recipes (tall plants at the back, drifts, groups of at least three), builds the scene for the viewer, counts plants and prices, and runs the checks; there are about thirty of them now. A tree with its crown over the house fails, so does a woody plant over a gas line, a poisonous plant in a garden with a child under seven, or a terrace the neighbor can still see from an upstairs window ten years after planting. The light check uses the same sun model as the viewer, so a plant is judged by the shade the customer will see.

To prove each check works, the repo has 64 planted faults, each a plan or a small scene with exactly one thing wrong, and every one has to be caught by its check. They run after every change to the pipeline.

If the customer didn't give us something, for example the soil or the children's ages, the check that needs it answers not_checked. It doesn't guess, and the model is told not to make it up either.

The gate

Passing checks isn't enough to send a plan. After every model session a script, deliver.py, reads the files and picks the next step:

  • the first designer session leaves something failing: a fresh session gets the list of failures and one try to fix them;
  • that fails too: the order is not delivered, and the customer gets a plain note on what couldn't be reconciled (the plan was a full refund, but payments were never built);
  • the plan passes: a different session reads it the way a gardener would and writes comments;
  • the comments get one revision round, and if that round breaks the plan, the version from before it goes out.

That makes at most four model sessions per order. Warnings never stop delivery, but the text has to mention every one of them.

The second look is a model reading another model's plan. On its first plan it found that "338 m² of connected lawn" was all the grass on the plot joined through a one-meter neck, with a rowan standing in it; by its own count the lawn you can play on was about 120 m². It noticed that the privacy figure in the text was the ten-year one and the text didn't say so, and that two hydrangeas less than a meter from the path would flop onto it. The designer had also told the customer to buy sand for a meadow because "no moist-meadow mix exists", while the database had one, added after the text was written. None of the checks caught any of this.

The text has its own check too. rationale_check.py holds the figures and plant names in the text against the plan; run over the 15 texts we had, it failed 14, mostly because the checks had changed after those texts were written.

Everything the customer types is screened at the intake (length, markup, invisible characters) and passed to the models as the customer's words. The prompts say it's never an instruction, and the sessions can only read and write inside the order's folder.

What it costs, and what's missing

On Opus 5.5 a first plan took 8 to 15 minutes and 2 to 4 dollars at API list prices, measured on four invented plots, and all four ended with no failed checks. A whole order through the gate, with the second look and a revision round, took about 27 minutes.

The garden planner's 3D viewer showing test plot E: a house with a garage, a walnut tree, beds and a lawn, with month, hour and growth sliders
The same plot in the 3D viewer after a full order: designer, second look, one revision. October, year five.

I also went through it the way a customer would and filled in the order form for my own plot. The layout checks passed all three layouts that came back, and I still found plenty to fix: there was no preview of the house as I typed it in and no way to upload a plan, the layouts had too few trees for a plot that size, one layout's cost estimate ran from 10 to 52 thousand zł, and the evenly spread dots on the plans looked like far too many plants. The house editor and the plan upload are in the public code. The changes to costs and to the plan view stayed on a branch that was never merged, and more trees on big plots is still an open item.

The light model treats every plot as flat, so sun on a slope is wrong. The species data hasn't been checked record by record, and 280 records still have no soil check. The designer aims the midpoint of its cost estimate at the budget, so in three of the four test plans the upper estimate went over it. And the second look is still a model; it found real problems, but I have no measure of what it misses.

I don't have time to keep building it, so the code is on GitHub under MIT: plrojek/garden-planner. The viewer on its own is a Claude Code skill in plrojek/agent-skills.

← All writing Get in touch