This is a submission for the Hacktoberfest Weekend Challenge: Build for a Friend
What I Built
I'm going away for a few weeks, and I asked a friend to keep my plants alive while I'm gone. This is the friend who once killed a cactus. A cactus. The plant you're supposed to be able to forget in a corner for a month. So "sure, I'll water them" was not the reassurance she thought it was.
The real problem isn't that she doesn't care. It's that my shelf has three plants that want three different things, and from the outside they all just look like "plants." Water them all on the same day and the fern's still thirsty while the succulent's drowning. I needed something that tells her, each day, exactly which plant needs what, so she doesn't have to guess and I don't have to text from another timezone.
So I built the plants a bodyguard. Plant Parent reads the state of each plant and gives one clear answer per plant, sorted by who's in trouble first:
- 🔴 Fern: water today (overdue by 3 days)
- 🟠 Succulent: move to brighter light (fine on water for 13 days)
- 🟢 Snake plant: happy, fine for 16 days
That's the whole idea. One glance says what to touch and what to leave alone. The snake plant being green is as useful as the fern being red, because the thing that kills plants in someone else's hands is fussing over the wrong one. The fern needs her today. The cactus-killer can ignore the snake plant for two weeks with a clear conscience.
And because I won't be there to run it, it has to run itself, every morning, without me. That part matters more than it looks, and it's where most of the real engineering went.
Demo
The friend-facing view is a single web page. Each plant is a card: a status color, the one action to take, and how long until it's a problem. The thirstiest plant sorts first, so the fern that needs water today sits where you can't miss it, and the snake plant that's fine for two weeks stays quietly out of the way.
Marking a plant done is a one-tap thing, and the card responds: the plant perks up, its water gauge fills, and the status settles to "all good." It's meant to feel satisfying, so a forgetful caretaker actually does it.
There's also a CLI view that prints the same triage as a table, which is what I used while building.
Code
lewisawe
/
plant-parent
A houseplant triage board for a friend who kills every plant. TabPFN predicts which plant needs help today; a Temporal workflow runs the daily check durably.
Plant Parent — "Will my plant survive my friend?"
A tiny houseplant-care triage tool for Mesh, a lightly fictional friend who kills every plant. It looks at each plant's state (species, pot size, light days since watered, room temp, humidity) and tells Mesh, per plant, the next action and how long until trouble — as a simple triage dashboard:
🔴 Fern — water today | 🟢 Succulent — move to light | 🟢 Snake plant — fine for 16 days
Three plants with genuinely different needs drive the demo: a snake plant (dry, infrequent), a fern (consistently moist), and a succulent (dry).
What's under the hood
-
TabPFN (Prior Labs' tabular foundation model) is the prediction core. Each
plant is one feature row; TabPFN predicts the next action (classifier) and
days-until-trouble (regressor) in a single forward pass — no gradient training
no hyperparameter search. A thin abstraction (
src/predict.py) runs…
Built during the challenge window.
How I Built It
The core: TabPFN reading a tiny table
Plant care is a table pretending to be a vibe. Each plant is a row: species, pot size, light level, days since watered, temperature, humidity. The thing you want to predict (what to do, how many days until trouble) is just two more columns. That shape is exactly what TabPFN is for.
TabPFN is a foundation model for tabular data. You hand it your labeled rows and the rows you want answered, and it predicts in a single forward pass. No training loop, no hyperparameter search, no model file to manage. For a shelf of plants that is wildly over-powered, and that's the point: the whole prediction core is a few lines, and it stays correct if I add a monstera next month.
I split it into a classifier (what's the next action) and a regressor (how many days until trouble), both pointed at the same feature table. The three plants come back with three different answers because their rows are different, not because I wrote three if statements.
The cold-start problem, handled honestly
Here's the catch nobody mentions in the demo: my plants have no history. I never logged a watering in my life. A model with no data has nothing to say.
So the seed data is synthetic, and I'm not going to pretend otherwise. I wrote down the care rules everyone already knows (snake plants and succulents ride out long dry spells, ferns sulk and brown within days, bright light and small pots dry soil faster, warm dry air shortens the window) and generated a few hundred rows from them. TabPFN reads those as its examples. The rules are the honest part; TabPFN's job is to generalize them to a plant in a specific spot with specific numbers, which is more than a lookup table does.
The design makes this a feature, not a fib. Every time a watering gets logged, that's a real row to append. The synthetic rules are the prior it starts with, and the real shelf slowly takes over. The app never dresses up the rule output as something it isn't. When it can't reach the real model it says so on the page, in plain text, instead of guessing.
Keeping the morning check alive with Temporal
While I'm away, nobody is babysitting this. No terminal I'm watching, no "oh, the script died on day three and I didn't notice." The check has to fire every morning on its own and survive whatever a laptop left running for weeks does to a process. That's where Temporal comes in.
The daily pipeline is three steps: read each plant's current state, run the prediction, send the one notification. Each step is a Temporal activity, which means the orchestration survives things going wrong. If the prediction call times out, Temporal retries it. If the machine falls over mid-run, the workflow picks up where it stopped instead of starting over. The "check every morning" part is a workflow that sleeps and wakes, and it keeps its place across restarts.
The part I most wanted to get right was no double-nagging. If the process dies after sending the fern alert but before finishing, the resume must not send that alert again. So the notify step is idempotent, keyed per plant per day. I proved it rather than hoped it: run the check, it sends three alerts; run it again (the resume case), it sends zero and skips three, and the notification log is unchanged. The workflow shows up as DailyPlantCheck with status Completed in Temporal's own UI, and the result it returns is the real TabPFN output (source: tabpfn), so the whole chain, prediction included, ran durably end to end.
Why Does Open Innovation Matter?
This project only makes sense because the model is open.
Plant Parent has to keep running for weeks, unattended, for free, without me wiring it to a paid API that bills per prediction or quietly rate-limits me while I'm on a plane. TabPFN is an open model you can run yourself, so the prediction core costs nothing per use and isn't hostage to anyone's product roadmap or uptime. For a thing whose entire job is to run while I'm not looking, "no external dependency to fail" is the feature.
It also keeps the data close. The whole input is a quiet inventory of my home: how warm the flat is, which window gets light, how often someone's around to water. That's exactly the kind of small personal detail that shouldn't need a round trip to a company's servers just to decide whether to water a fern. An open model lets the sensitive part stay on the machine in my living room.
And I could inspect and shape it instead of accepting a black box. I know what the seed rules are because I wrote them. I can see why a plant got the advice it got. For something I'm trusting with my plants while a self-confessed cactus-killer is in charge, being able to open the box matters more than a few points of benchmark score.
My Agent Session
I built this with an AI coding agent (Kiro), and the process is worth showing because it shaped the result.
The honest part the agent caught early: TabPFN needs rows to learn from, and my plants have no logged history. We stopped and dealt with that instead of papering over it, which is where the synthetic-from-rules seed came from. The agent also flagged the real auth wrinkle, that the local TabPFN package wants an interactive license step a terminal can't do, and switched the code to the hosted backend with my token. The Temporal idempotency ("don't nag twice on resume") came out of asking the agent to prove the resume case, not just claim it.
Prize Categories
TabPFN: TabPFN is the prediction core. Each plant is a feature row, and TabPFN predicts the next action and days-until-trouble in a single forward pass over a small synthetic-from-rules table, with no training step.
Temporal: The daily check is a Temporal workflow with each step as an activity, so it retries failures, resumes after a crash, and (via a per-plant per-day idempotency key) never sends the same alert twice.



Top comments (1)
hey great work , really like your idea !