Product Designer / Design Engineer
Back to work

VegAvengers Marketing

Designed and shipped the content platform behind vegavengers.com — a Payload CMS system of record with an editorial dashboard, publish-triggered rebuilds, and an AI freshness checker whose every proposal waits for editor approval.

  • Payload CMS
  • Next.js
  • PostgreSQL
  • Astro
  • +2

Highlights

  • Designed and shipped the system of record for a nonprofit's content — collections, editorial workflow, and admin experience on Payload 3

  • Modeled messy real-world festival data honestly, keeping original date text verbatim alongside parsed fields and provenance

  • Built the editorial dashboard two non-technical editors use daily, from trimmed rich-text controls to per-photo credit tooling

  • Shipped an AI freshness checker that proposes content changes as drafts, so nothing goes live without an editor's approval

  • Designed the publish pipeline so a CMS outage can never break the public site

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, until 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.

The real product problem wasn’t discovery anymore. It was content operations: who owns the data, how it gets updated, and how much of that work should require me. So I designed and shipped a second product — the system of record — as a Payload CMS the organization’s two editors run themselves.

Modeling Messy Reality

Festival data does not arrive as clean timestamps. It arrives as “6/5-6/7/26”, “TBD”, or “sometime in the fall.” The content model honors that instead of fighting it:

One document per festival edition. A festival’s 2026 edition is its own record — “roll into next year” is just duplicate-and-bump, and history stays intact.

Verbatim text beside parsed fields. displayDate keeps the original free-text date exactly as published; dateStart and dateEnd hold parsed values where parsing is possible. The site shows people what the festival actually said, while search still works on real dates.

Provenance and confidence as first-class fields. Every record knows whether it came from a CSV import, a human, or AI discovery — and the fields the AI checker maintains are read-only in the admin, so the boundary between machine bookkeeping and editorial judgment is visible in the interface itself.

An Admin Two Editors Actually Use

Payload gives you a capable admin for free; the design work was deciding what the editors should not have to see. Lynn and Harris are not developers, and the dashboard treats that as a requirement rather than a limitation.

  • Rich-text controls are deliberately trimmed: editors mark meaning — emphasis, links, structure — while templates own presentation. Nobody fights alignment buttons at midnight.
  • Photo galleries carry per-image credits through a custom field editor, because attribution is an editorial value, not a nice-to-have. The first real post (a photo essay) drove this design.
  • The admin is branded, welcoming, and grouped by mental model — “Festival Database” is a different place than the blog, because it is 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 selectively — alt-text changes rebuild, focal-point nudges don’t — and 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 degrades freshness, never availability.
  • A nightly rebuild acts as a backstop, so the site can never drift more than a day from the system of record.

The AI Checker: Automation Under Approval

The climax of the platform is the piece that attacks the original rot problem. 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.

Drafts as approval
  1. Scrape

    Fetch the festival's own website — the primary source, not a model's memory.

  2. Extract

    One bounded model call answers one question, and its JSON is validated in code before anything is trusted.

  3. Diff

    Compare against the published record. No difference means a metadata stamp, not a proposal.

  4. Draft

    A change lands as a Payload draft — proposed status, proposed date, and the evidence quoted in the notes.

  5. 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 native version UI — no bolt-on approval queue to learn.

The design decision I care most about is where approval lives. Instead of building a separate review dashboard, the checker writes ordinary Payload drafts — so the approval surface is the one editors already trust, and an AI proposal looks exactly like a colleague’s unpublished edit, evidence attached.

Dead links get their own path: flagged as a draft and handed to a follow-up job that re-probes the URL but never fabricates a replacement. And when nothing changed, the checker stamps bookkeeping fields on the published record without creating noise for anyone to review.

Before it touched production, the checker went through an adversarial review that surfaced eleven real defects — the sharpest being a case where 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. The whole pipeline was then proven against production data: a dead-link festival produced its draft, the follow-up probe appended its evidence, and the published record stayed untouched until a human said otherwise.

Search Designed For Partial Memory

The discovery experience the platform serves stayed a product surface in its own right. People rarely search with 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 is shown rather than hidden — and the AI checker now works to shrink the TBD group at the source. Search queries Payload live, with the pre-CMS dataset retained as a fallback.

Outcome And Learning

VegAvengers went from a site I maintained to a platform its owners operate. The editors publish independently; the data has an owner, a provenance trail, and an automated assistant that proposes but never decides.

It also sharpened a thesis I keep returning 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 human approval step exactly where the consequences are. In a CMS, that place is publish — so that’s where this system puts it.