hosette.
← Back to projects

2026 · planning tool · AI assistant · horticulture

GardenIQ

A garden planner that explains why, not just what

Role
Product manager, designer, builder
Stack
ReactTypeScriptViteKonvaAnthropic SDKLovable AIreact-pdfTailwindZustand
Live
gardeniq.hosette.net ↗
GardenIQ: A garden planner that explains why, not just what
An early Lovable project, currently on hiatus. Enjoy the imperfections while they last.
Status
hiatus
Plants
50
Pairings
267
Hard part
rules before AI

why

My dad is a gardener with the kind of spatial reasoning I’d call savant-level if I weren’t his daughter. He looks at a bed and knows where the tomatoes go, why the squash needs more room than the seed packet says, which sprout isn’t a weed yet. I’ve tried gardening twice and failed twice. The thing I lost wasn’t motivation or the right tools; it was the silent decisions in his head, the ones he doesn’t bother explaining because they’re so obvious to him.

GardenIQ is the tool I wished I’d had: an attempt to encode the kind of reasoning my dad does for free, for the gardener who doesn’t have him to call. Most tools in this space chase the absolute beginner with surface-level companion advice (tomatoes love basil, hate fennel). Easy to ship; doesn’t help. GardenIQ goes the other way: companion conflicts flagged with their mechanism, spacing warnings drawn on a canvas, timing anchored to your zone’s last frost rather than the wall calendar. The why on every recommendation is the part a gardener-without-an-expert is actually missing.

what I built

A single-page planner with no account and no backend. Four pieces:

  • Garden canvas (Konva): drag-and-drop, grid overlay, zoom and pan, multiple named beds. Every plant carries its own spacing radius; the canvas warns you when two plants overlap.
  • Companion-planting layer: ranks pairings on a 1–3 strength scale, deduplicates reverse pairs (tomato-basil and basil-tomato are the same recommendation, not two), and surfaces why each pairing helps or hurts.
  • Succession timeline: converts “start indoors 6 weeks before last frost” into an actual calendar date for your zone. Offset and clone successive sowings without rebuilding the schedule.
  • Climate layer: zip-code lookup pulls USDA zone, first and last frost, average rainfall. Shapes every other recommendation downstream.

An export pipeline snapshots the canvas, renders the companion matrix, and assembles a printable plan: PDF for your fridge, email for your collaborator, a shareable link that carries the whole plan in the URL. No account, no backend, no plan-retention policy to write: your plan is the link, the same way it is for Office Hours.

GardenIQ onboarding screen on a warm dark-brown ground. Left panel: a small green sprout logo, 'GardenIQ' in display serif, the line 'Start with your climate, then sketch beds and plant with guidance that fits your season,' and three feature cards: Local timing ('Use your zip once and keep frost dates tied to your plan'), Starter layout ('Pick a garden size and get a clean starter setup you can edit later'), and Season-aware planting ('Recommendations and timelines adapt to your location automatically'). Right panel: a cream card titled 'Set your garden home' with a zip-code field showing 10001, three garden-size pills (Small / Medium / Large) with Medium highlighted, a note that starter beds can be reshaped later, a sage 'Start planning' button, and a footer line: 'Your zip is kept locally on this device so you don't need to enter it again.'
The entry point. Climate first, then layout, then season: the three pillars on the left are the same three pillars holding up every recommendation downstream.

the database

The rule layer is fifty plants and 267 companion pairings, hand-curated from horticultural references. Each plant entry carries:

  • Identity: scientific name, family, category
  • Spacing: minimum, recommended, row-spacing in inches
  • Frost-relative timing: indoor start, transplant, direct sow, days to maturity
  • Climate: USDA zones, sun, water, soil pH ranges
  • Pest and disease relationships
  • Companions: typed beneficial / neutral / antagonist, ranked 1–3, each with a one-sentence mechanism

The mechanism is the load-bearing part. Repels aphids and whiteflies is doing different work than both heavy feeders, compete for nutrients. A tool that flattens both into a green check or a red X has thrown away the part the gardener was supposed to learn.

A labeled record card showing the structure of one plant in the GardenIQ database: Cherry Tomato. Identity: Solanum lycopersicum var. cerasiforme, Solanaceae. Spacing in inches: minimum 18, recommended 24, row 36. Timing relative to last frost: indoor start six weeks before, transplant two weeks after, harvest 60 to 80 days. Companions, five of six listed: Sweet Basil beneficial strength three (repels aphids and whiteflies); Marigold beneficial strength three (deters nematodes); Carrot beneficial strength two (loosens soil around roots); Cabbage antagonist strength two (both heavy feeders, compete for nutrients); Fennel antagonist strength three (inhibits tomato growth). Footer: fifty plants, two hundred sixty-seven pairings, every entry carries its mechanism.
One plant entry, real data from the database. Beneficial pairings in sage; antagonists in rust. The mechanisms cluster into four kinds the rule engine reads: *allelopathy* (one plant releases chemicals that suppress another, the fennel-tomato problem), *pest confusion* (a scented neighbor masks the host, the basil-tomato story), *nutrient competition* (two heavy feeders draining the same reserves, the cabbage-tomato case), and *beneficial-insect attraction* (one plant calls in pollinators or predators that help the other, the marigold-and-everything case). Each is a different *why*, surfaced on the canvas tooltip at the moment of placement.

decisions I’d defend

Rule-first, AI-second. The companion-planting database is curated. The spacing math is deterministic. The timeline rules come from horticultural references, not the model. The Anthropic API enters as a second layer: an Optimize My Garden call that explains tradeoffs, suggests substitutions, and flags risks the rule database might have missed. AI as a second pair of eyes, not a first-draft generator. Same architecture under Pocketbook (model captions, human edits) and Celine AI (statutes as ground truth, model as pattern-matcher).

Encode expert reasoning, don’t simplify it down. Most beginner-targeted tools translate a savant’s mental model into yes/no labels and lose the part that actually teaches: the why. GardenIQ keeps the why on every recommendation. A tooltip that names the strength score, sources the mechanism, and flags airflow risk at close spacings is the kind of detail beginner tools strip out. The struggling gardener doesn’t need fewer details. She needs the right ones, surfaced when the decision is in front of her.

URL is the whole plan. No accounts in the MVP. Plans encode into a compressed URL parameter, share like a Spotify link, reopen on any device. Costs me persistence (no plant-tracking-over-time, no notifications, no social) and buys me a deploy story that survives me: same shape that lets Office Hours keep working without a server behind it.

Frost-date-relative scheduling. Every recommendation in the timeline anchors to your last frost, not the wall calendar. The same plan works for a Wisconsin gardener and a Texas gardener: absolute dates resolve differently, relative spacing of indoor-start, transplant, direct-sow, and harvest stays correct.

the receipts

The rule engine is one TypeScript function. The antagonist branch, verbatim from companion.ts: the part that fires the X conflicts with nearby plant warning when a placement matches:

for (const comp of plant.companions) {
  if (!placedPlantIds.has(comp.plantId)) continue;

  if (comp.type === 'antagonist') {
    recommendations.push({
      type: 'conflict_warning',
      severity: 'critical',
      title: `${plant.commonNames[0]} conflicts with nearby plant`,
      body: comp.reason,
      affectedPlantIds: [plant.id, comp.plantId],
      bedId: placement.bedId,
      source: 'rule_based',
    });
  }
}

// Deduplicate: A→B and B→A are the same conflict
return recommendations.filter(/* sort + dedup */);

No model in the loop. The function reads the static plant database, walks every placement on the canvas, and emits a warning whenever an antagonist pair lands within range. The mechanism (Fennel inhibits tomato growth, Both heavy feeders, compete for nutrients) comes straight from the data record’s comp.reason field. The model arrives later, on the Optimize My Garden call, with the rule output already in hand.

what I learned

Naming the right unit makes the rest of the schema small. Once last-frost-relative dates were the calendar primitive, succession planting fell out almost for free: every successive sowing is just an offset off the same anchor. Most garden-planning code I read was wrestling with absolute calendar dates and re-deriving zone offsets at every render. Pinning the data layer to the relative anchor made the rest of the code uneventful.

Companion scoring is a deduplication problem in horticulture clothing. The N×N companion matrix is symmetric: tomato-basil and basil-tomato are the same recommendation, not two. A tool that doesn’t dedupe surfaces the same advice twice, which erodes trust in the entire database. Unglamorous and load-bearing.

AI is most useful when the deterministic core is already correct. The model sees the user’s plan, the climate context, and the rule output, and adds analysis the rules can’t easily encode (variety substitutions, rotation suggestions, pest-window calls). The shortcut would have been a chat surface over a horticulture prompt: fluent, fast, mostly wrong. The questions a serious gardener asks have right answers and wrong ones, and fluent-sounding answers from a model unanchored from data are exactly how trust gets lost. Get the deterministic core right, then let the model help.

what would prove it

A garden tests its own rules slowly, season by season. Three claims the rule layer makes, and two risks the deploy story lives with.

  • The four mechanism categories generalize past 50 plants. Allelopathy, pest confusion, nutrient competition, beneficial-insect attraction are the buckets every pairing in the database resolves into. The hypothesis: a gardener adding a 51st plant (heirloom, regional, off-list) writes companion entries that fit the same four. Proof: open the database to a small batch of contributors, watch whether they reach for a fifth category or stay inside the four.
  • Frost-relative scheduling holds across zones. A Wisconsin gardener and a Texas gardener using the same plan should land equivalent yields once you adjust for zone and rainfall. Proof: two seasons of paired use across at least one northern and one southern user, comparing germination and harvest dates against the relative anchor instead of the calendar. The relative-date claim is the schema’s load-bearing one; if it doesn’t survive zone diversity, the calendar primitive is wrong.
  • Trust order matches design order. Users open Optimize My Garden after the rule output reads, not before; rules-first / AI-second at the architecture level mirrored in the user’s actual sequence. Proof: a small instrumentation pass on the call (which I haven’t shipped, because the no-account ethic), or a friend-test that watches the order in which a first-time user reads recommendations.

Risks:

  • The 50-plant set is curator-shaped, not gardener-shaped. A real gardener wants the heirloom her grandmother grew, the regional cultivar her neighbor swears by, the variety that survived her zone last summer. Fifty plants curated by me is a starting library, not a landscape, and the gap between the two is exactly the gap that makes the tool feel like a demo to anyone serious.
  • The next feature competes with the architecture. A what I planted vs. planned second-season view requires durable state, which the URL-as-config deploy story wasn’t built to extend. Either the architecture changes (account, backend, persistence: the things the project refused on purpose), or the feature doesn’t ship. Both options cost something the project decided not to spend.

method extracted: Rules before AI

GardenIQ is the project. Rules-before-AI is the reusable architecture. Recommendations need a deterministic core. AI can explain, translate, and help users navigate the rule system. AI should not invent the rule system.

Related methods: Rules before AI. See also Celine.

what’s next

GardenIQ is live at gardeniq.hosette.net and currently on hiatus: an early Lovable build I shipped enough of to use, then set down so other work could move. The canvas works, the companion library is in, the AI recommendations land. The polish backlog is real:

  • URL versioning
  • Print-PDF flow on letter and A4
  • Rough edges around bed rotation and the auto-plant prompt

I’ll get back to it when the next gardening season makes the missing pieces obvious. Plant-tracking-over-time is the longer arc: a what I actually planted vs. what I planned view is the bridge to seasons two and three, and that arc only matters if I’m using the tool for real. For now: the rough edges are honest about where the project is, and the project is honest about where the gardener is.

See also

All projects

Working on something similar?

I take a small handful of consulting briefs a year and am always up for trading notes with anyone shipping in this space — send a note.

Or: values behind the work · obsessions that shape it · other projects.