A garden grow log built around pinching — every plant carries the reason nipping out its tip pays off.

The problem

Gardening advice is written for a month. Your plant went in on a Tuesday.

Every guide tells you what to do in May. None of them know when you actually planted anything, so the advice arrives at the wrong time for your garden and you either act too early or find out too late. A grow log that only records the past is a diary; the useful part is what it tells you to do next week.

The other half is the photograph. Point a camera at a sick leaf and most tools hand back one confident species name and a treatment, which is the failure mode — a plant disease looks like three other plant diseases, and acting on the wrong one costs you the plant. Pinch is built so that its answer arrives with the things that would prove it wrong.

Never claim certainty from a single photo.

How it works

The pipeline, step by step

Nine steps, and the seventh is the one the app is really about. The rest is a grow log; that one is the reason to trust it.

  1. 01

    Add the plant, and the day it went out

    Ninety-seven plant types across ten categories, ten locations from garden bed to cold frame to allotment, four sun exposures and eight growth stages. The planting date is the field everything else keys off.

    plant_types.dart
  2. 02

    Every plant says why you'd pinch it

    Not a species list with a name and a photo. Each type carries what pinching that particular plant buys you — more blooms and bushier growth on a dahlia, delayed flowering on basil — and it is shown in the picker, at the moment you are choosing.

    pinchBenefit
  3. 03

    Tasks come off your date, not a calendar

    A nightly job walks every growing plant and materialises its care tasks from the day you planted it plus an offset. The reminder to stake a dahlia lands three weeks after it went into your soil, not in a generic article about June.

    plantOutAt + offsetDays
  4. 04

    One digest, at an hour you chose

    An hourly job sends only to people whose chosen hour it is, skips their quiet hours, and writes a per-day marker so it cannot send twice. A gardening app that pings you at ten at night is one you delete.

    withinQuietHours()
  5. 05

    Photograph it, and the phone stays out of the analysis

    The photo uploads once. A storage trigger derives a thumbnail and a medium copy server-side, and the model is handed a bucket reference rather than the image being sent a second time from the handset.

    onUserPhotoUploaded
  6. 06

    The model is asked for candidates, not an answer

    Identification returns the top three with the reasoning behind each, a confidence band written in words rather than a percentage, and three questions it wants you to answer before it commits any further.

    candidates (top 3), questions (3)
  7. 07

    The checks that would disprove it come before the treatment

    The model is asked for the likely issues and, alongside them, `confirm` — the signs you have to go outside and look for yourself. The app renders those as their own card, and its position is the point: it sits between the diagnosis and the treatment, so the doubt reaches you before the action does.

    confirm[] rendered before actions[]
  8. 08

    Log what you actually did

    Eight event types, and they are the ones that matter to a grower rather than a generic note field: pinched, staked, fed, deadheaded, pest, disease, bud set, first bloom.

    add_event_page.dart
  9. 09

    Or let it draft the entries for you

    Photograph the plant and it proposes up to five timeline entries, each with the reason it thinks so and a plain-word confidence. You accept the ones that are right. It never writes to your log on its own.

    ≤5 suggestions, each with a reason

Honest by design

A plant doctor that hands you the checks, not the verdict.

The tempting product is the one that names the disease and tells you what to spray. Pinch will not do that, because a photograph of a leaf genuinely is not enough to be sure, and a confident wrong answer costs you the plant. Every answer it gives is shaped so you can go and test it.

  • Answers arrive as a shortlist with reasoning — never one confident name
  • Every diagnosis names the signs you must confirm yourself — never just a treatment
  • Confidence is written in words — never a meter that implies precision
  • It asks three questions back — never assumes it has enough to go on

To Confirm

Check the undersides of the leaves for colonies, and whether the spots spread after rain. Both would point away from the first candidate.

Before you treat anything

Illustrative

Everything inside

A whole world in one app

Ninety-seven plants, each with a reason to pinch it

The pinch benefit is the app's whole argument, and it is surfaced where the decision is made rather than buried in a help page.

  • Ten categoriesCut flowers, herbs, fruiting and leafy vegetables, beans and peas, houseplants, perennials, annuals, fruit and berries — and a tenth for the rest.
  • Named plant by plantBasil says it prevents flowering; aster says compact growth and delayed flowering. The reason differs per plant, not per category.
  • Read while you chooseThe benefit is the subtitle on every row of the picker, and a tip card again once the type is chosen.
  • Where it is growingTen locations from garden bed to cold frame, four sun exposures, five watering rhythms and eight growth stages.

Every plant says what pinching buys you — never a bare species list.

plant_types.dart

A plant doctor that shows its working

Identify a plant, diagnose a problem, or have it suggest what to log. Every mode answers with candidates, reasoning and the checks that would disprove it, and there is a free-text box for the context a photo cannot carry.

  • Identify a plantThree candidates, each with the reasoning behind it, a confidence written in words, and three questions it wants answered.
  • Diagnose a problemThe likely issues, then the signs to confirm, then the low-risk first actions, and when to escalate.
  • Suggest what to logUp to five timeline entries, each with the reason it inferred them and a high, medium or low confidence.
  • A disclaimer it cannot dropAll three endpoints must return the same line: this is best-effort from a photo, and it can be wrong.

Answers come with the signs to confirm — never a single confident verdict.

likely_issues + confirm + escalate

Everything inside

The rest of it

Tasks off your planting date

Care, growth, problem and overwintering tasks generated from the day the plant went in. Snooze a day, three days, a week, or until a date you pick.

  • Four kinds of taskCare, growth updates, problems and overwintering — each with its own notification setting: push, digest or off.
  • Snooze like a gardenerA day, three days, a week, or a date you choose, and the task comes back rather than quietly disappearing.
  • It cannot double upA task's id is its plant, its template and its due date, so re-running the nightly job rewrites instead of duplicating.
  • Schedules are dataCare templates live in a reference collection every client can read and none can write, keyed by plant type.

A digest that respects your evening

One push at the hour you chose, capped, quiet-hours aware, and deduplicated so a retry cannot send it twice.

  • Your hourThe job wakes every hour and sends only to people whose chosen hour it is; the default is eight in the evening, UTC.
  • Quiet hoursA window you set, ten at night to seven by default, and a digest falling inside it is skipped rather than queued.
  • Eight tasks, soonest firstThe push carries the eight open tasks due first, not everything outstanding — a list you can actually act on.
  • Once a day, provablyA per-day marker is written on send, so a retry or an overlapping run cannot deliver the same digest twice.

A photo timeline per plant

Every photo kept at three sizes generated server-side, threaded onto one timeline with the events you logged, so a season reads as a sequence rather than a folder.

  • Three sizes, server-sideA storage trigger derives a 400px thumbnail and a 1600px copy; the grid loads the small one, the viewer the larger.
  • Three tabs per plantEvents in date order under a running count, every photograph of that plant, and its saved analyses — each its own tab.
  • AI reads are filedSave an analysis and it becomes its own event on that plant's timeline, alongside the photo it was read from.
  • Delete means deleteRemoving a plant takes its events, photos and tasks with it rather than leaving orphans behind in your record.

Loaders built rather than downloaded

Facet Bloom, Growth Ring and Facet Skeleton — three hand-written painters that carry the low-poly identity into the waiting states, plus a wind sway deliberately capped at a few degrees so it reads as breeze rather than animation.

  • Facet BloomA low-poly bud that grows and shifts its facets on a slow loop — the app's stand-in for a spinner, everywhere it waits.
  • Growth RingProgress drawn as a ring of small triangular facets rather than a sweeping arc, for uploads and longer jobs.
  • Facet SkeletonGrey faceted tiles that snap into place where a shimmer skeleton would go; the brief rules shimmer out by name.

Groups, with reporting that only goes one way

Public or private groups with posts, photos and comments. Reports are write-only by design — a member can file one and nobody, including other members, can read, edit or delete it.

  • Public or privatePublic groups are discoverable and you join by adding yourself. Posts in either kind are readable only once you are a member.
  • Posts, photos and commentsMembers only, and only the author may edit or delete their own — enforced in the security rules, not just in the UI.
  • Reports go one wayAnyone signed in can file one; reading, editing and deleting are all denied outright, so no report can be pulled back out.
  • The owner owns itOnly the owner can change or delete a group; everyone else may move the member count and nothing else.

Architecture

How it's put together

A Flutter client of roughly 23,000 lines, a small TypeScript Functions surface for anything scheduled or privileged, and a Python service that is the only thing permitted to talk to the model. Everything runs in London except the model itself.

App

Web and Android from one codebase
Flutter
Dart, web and Android from a single source
Riverpod
State, with the theme and accent persisted locally
GoRouter
Routing
CustomPainter
The three hand-built loaders and the sway animations

Backend

Cloud Functions, London region
Task generation
Nightly, materialising each plant's schedule
Digest
Hourly, quiet-hours aware, deduplicated per day
Image derivatives
Storage-triggered thumbnail and medium copies
Purchase check
Server-side entitlement, not yet wired to a store

AI

A separate service, so the app never holds a model credential
FastAPI on Cloud Run
Identify, triage and auto-log endpoints
Vertex AI
Gemini, asked for JSON and prompted never to claim certainty
Firebase ID token
Verified server-side on every call
Low temperature
So the same photo answers with the same structure

Data

Firestore
Per-user subtrees; plant reference data is read-only
Cloud Storage
Originals plus the two derived sizes
FCM
The digest, and nothing else

Background

The story

Pinching is the horticultural act of nipping out a growing tip so the plant branches instead of running to a single stem. Do it to a dahlia and you get more blooms on a bushier plant; do it to basil and you delay it bolting. It is one of the highest-return things a gardener can do and it is almost never explained at the moment you are stood over the plant wondering whether to.

So that became the app. Every one of the ninety-seven plant types carries its own pinch benefit, shown in the picker rather than in documentation, and the name of the app is the thing it is trying to teach.

The task engine was the second decision. Anchoring care to a real planting date instead of a month is more work — it needs a nightly job walking every growing plant and materialising its schedule — but it is the difference between a log and something that earns its place on your phone.

The plant doctor was the hardest thing to get right, and the work went into making it less confident rather than more. It is prompted never to claim certainty from a single photograph, and the signs that would disprove it are rendered as their own card in front of the treatment rather than after it. That is slower to read than a species name and a spray, and it is the honest shape of the answer.

The look came from a written art direction rather than a component library — a low-poly, faceted identity carried down into three hand-built loaders and a wind sway capped at a few degrees, so the app moves like weather rather than like software.

Next

QR Protector

Check the code before you trust it