Client · Since February 2025 · Product Designer / Design Engineer
VegAvengers Marketing
A content platform two non-technical editors can run themselves. I designed the CMS, publishing workflow, and AI review tools behind vegavengers.com.
- The problem
- Festival information was going stale, and every correction landed on a developer.
- The decision
- Give editors their own publishing workflow, and have AI suggestions show up as ordinary drafts with evidence.
- What changed
- Two non-technical editors run the live CMS. AI proposes updates; an editor decides what gets published.
My role. Solo product designer and design engineer, in a client engagement since February 2025: the content model, the editorial workflow, the admin experience, and the automation around them. The editors own editorial judgment and what gets published.
Context
VegAvengers connects people with vegan festivals, animal sanctuaries and advocacy organizations. The first version I built was a fast, searchable Astro site, and it worked.
Then the data underneath it started to rot. Festival dates changed, websites died, and every correction was a developer task. Of 115 festivals with unconfirmed dates, 26 had links that had quietly gone dead.
At that point the problem wasn’t really discovery anymore. It was content operations: who owns the data, how it gets updated, and how much of that work should need me at all. So I designed and shipped a second product, the system of record: a Payload CMS the organization’s two editors run themselves.
Modeling messy reality
Festival data doesn’t show up as clean timestamps. It shows up as “6/5-6/7/26”, “TBD” or “sometime in the fall.” I wanted the content model to work with that instead of fighting it.
One document per festival edition. A festival’s 2026 edition is its own record. Rolling into next year is just duplicate and bump, and the history stays intact.
The original text beside the parsed fields. displayDate keeps the free-text date exactly as it was published. dateStart and dateEnd hold parsed values where parsing is possible. People see what the festival actually said, and search still works on real dates.
Provenance and confidence as real fields. Every record knows whether it came from a CSV import, a person or AI discovery. The fields the AI checker maintains are read-only in the admin, so you can see the line between machine bookkeeping and editorial judgment right in the interface.
An admin two editors actually use
Payload gives you a capable admin for free. The design work was deciding what the editors shouldn’t have to see. Lynn and Harris aren’t developers, and I treated that as a requirement, not a limitation.
- I trimmed the rich-text controls on purpose. Editors mark meaning (emphasis, links, structure), and the templates handle presentation. Nobody should be fighting alignment buttons at midnight.
- Photo galleries carry a credit on every image through a custom field editor, because attribution is an editorial value, not a nice-to-have. The first real post, a photo essay, drove that design.
- The admin is branded and welcoming, and it’s grouped the way the editors think about the work. “Festival Database” is a different place from the blog, because it’s a different job.
Publishing without fear
The public site prerenders from the CMS, which raises an obvious question. What happens when the CMS is down, or an editor publishes at 2 a.m.?
- A publish triggers a debounced build hook, so a burst of edits becomes one rebuild. Media edits trigger it selectively: alt-text changes rebuild, focal-point nudges don’t. That policy has its own test suite.
- Builds fall back to fixtures if the CMS is unreachable, and live search falls back to a local dataset. An outage can make the site less fresh, but it can’t take it down.
- A nightly rebuild is the backstop, so the site never drifts more than a day from the system of record.
The AI checker: automation under approval
This is the piece that goes after the original problem, the data going stale. A Payload jobs-queue task checks each unconfirmed festival. It scrapes the festival’s website, asks a model one bounded question (is a new-season date published here?) and then diffs the answer against the published record.
- Scrape
Fetch the festival's own website: the primary source, not a model's memory.
- Extract
One bounded model call answers one question, and its JSON is validated in code before anything is trusted.
- Diff
Compare against the published record. No difference means a metadata stamp, not a proposal.
- Draft
A change lands as a Payload draft, with the proposed status, the proposed date, and the evidence quoted in the notes.
- Editor publishes
The editor reviews the version diff in the admin they already know, and approval is one click.
The AI never publishes. Every content proposal becomes a draft on the record itself, reviewed in Payload's own version UI, so there's no bolt-on approval queue to learn.
The decision I care about most is where approval lives. I could have built a separate review dashboard. Instead, the checker writes ordinary Payload drafts. The approval screen is one the editors already trust, and an AI proposal looks exactly like a colleague’s unpublished edit, with the evidence attached.
Dead links get their own path. They’re flagged as a draft and handed to a follow-up job that checks the URL again but never makes up a replacement. And when nothing changed, the checker stamps bookkeeping fields on the published record without creating anything for anyone to review.
Before it touched production, the checker went through an adversarial review that turned up eleven real defects. The sharpest one: a routine metadata stamp could silently bury an editor’s pending draft. That bug is why the system reads the draft state before every write.
Then I proved the whole pipeline against production data. A dead-link festival produced its draft, the follow-up probe added its evidence, and the published record stayed untouched until a person said otherwise.
Search designed for partial memory
The discovery side stayed a product surface in its own right. People rarely search in clean database terms. They remember “a VegFest in March,” or type “UK” and expect England to count.
Results are grouped into upcoming, TBD and past, so uncertainty shows instead of hiding. Now the AI checker also works on shrinking the TBD group at the source. Search queries Payload live and keeps the pre-CMS dataset as a fallback.
Outcome and learning
VegAvengers went from a site I maintained to a platform its owners run. The editors publish on their own. The data has an owner, a trail showing where it came from, and an automated assistant that proposes but never decides.
It also sharpened an idea I keep coming back to, the same one Ghosted taught me from the other direction. Give deterministic code everything it can do. Give the model the smallest job it can be trusted with. And put a person’s approval exactly where the consequences are.
In a CMS, that place is publish, so that’s where this system puts it.

