Skip to content
Development

Harvest shelf: giving Greenhouse's released items somewhere to land

By Victor Da Luz
rusttaurisveltedev-loggreenhouse

Greenhouse is my opinionated creative-project manager. Ideas get captured, mature, get worked on through a pipeline of stages, and eventually either get vaulted (shelved, reversible) or released (published, done). I built the vault side of that a while back: a footer counter, a browse view, a rescue button. The release side never got the same treatment.

A recent full-app review caught it: releasing a project was write-only. The database marked an item released and stamped a timestamp, but no command ever listed released items back out. ItemCard’s “Released” badge had a whole switch-case branch for it that could never actually render, because nothing ever handed it a released item. The Publish confirmation dialog told the user where their project was leaving from, but not where it went. Functionally: nowhere. The entire payoff of the pipeline - the thing the plant metaphor calls Fruiting, Harvest - was invisible.

Why this shape

The vault view was already the right template. Same problem shape: a status that filters items out of the main worklist, a count that needs a footer indicator, a full-window browse view that mirrors the dashboard’s own shell instead of popping a modal. I didn’t want to invent a second pattern when one already existed and worked. So the plan was mechanical: copy the vault’s wiring, swap the status and the sort direction.

One real design decision: sort order. The vault view sorts oldest-dormant-first, because the whole point is surfacing the thing that’s been sitting the longest for a Rescue-or-Keep decision. Harvest has no decision to make - it’s a trophy shelf, not a queue - so most-recent-release-first made more sense. You want to see what you just shipped at the top.

Implementation

On the Rust side: a new query, list_released_by_recency, ordering by released_at DESC instead of the vault’s vaulted_at ASC. A released_count field on the daily state struct, computed the same way as the existing vaulted_count. Two new Tauri commands - get_released_items and open_harvest_folder - both near-identical copies of their vault counterparts.

The one place I had to actually think instead of copy: releasing a project is a status transition only, it never moves the item’s folder on disk. A released item just stays wherever the pipeline’s terminal stage put it (its stage folder, e.g. 60-released/). So “open the whole harvest folder” can’t reuse the vault’s fixed 90-vault/ path - it has to resolve whichever folder the last configured pipeline stage points at, since that’s config-driven and not a hardcoded constant.

On the frontend: a new HarvestView.svelte, close to a straight copy of VaultView.svelte, minus the Rescue button - there’s no un-releasing a project, so the only per-item action is Show in Finder. Added a released_at field alongside the existing vaulted_at so ItemCard could show a “Released 3d ago” duration next to the badge, the same way it already shows “Dormant 12d” for vaulted items.

Gotchas

Adding a field to a shared struct that’s serialized straight to the frontend (DerivedItemState) means every test fixture that builds one by hand needs the new field too, or TypeScript’s strict object literal checking flags it. Fifteen files touched, and the actual feature logic lived in maybe six of them. Not a complaint, just a reminder that “add a field” is rarely a one-line change once test coverage is real.

I also drove the built app end-to-end with the project’s WebDriver harness rather than trusting the test suite alone. Seeded a released item straight into a throwaway vault’s SQLite file, relaunched the real app, and clicked through: footer said “Harvest: 1 release”, the view opened, the item showed up with its release duration, both Show-in-Finder buttons were there. Mocked IPC in a test suite proves the wiring is internally consistent; it doesn’t prove a real click through a real webview actually renders the thing. Worth the extra ten minutes.

Result

Releasing a project now goes somewhere. The dead badge branch does something. And there’s a small, deliberately cheap “look what you shipped” counter sitting right next to the vault indicator - which, for a tool whose whole design premise is fighting the instinct to abandon projects, felt like the right kind of payoff to make visible.

Related reading