At first glance, Events in Vacaville is a pretty simple website.

You pick a date. You find something happening nearby. Maybe you add it to your calendar, visit the organizer's website, or decide where you're taking the family this weekend.

That's more or less what I wanted it to be.

What visitors don't see is everything required to keep that simple experience useful: gathering event information from dozens of inconsistent sources, turning it into structured data, reviewing community submissions, understanding what people are searching for, preparing weekly social content, drafting a newsletter, and keeping up with email.

And because Events in Vacaville is a side project run by one person, I can't solve those problems by throwing more people at them.

I've had to engineer away the work.

But that wasn't the original plan.

It started with my wife trying to get me out of the house

I'm a homebody.

My wife is not.

She loves going out and planning things for us to do as a family, and she's usually the one who discovers interesting events and sends me ideas.

The problem was that those outings rarely seemed to be in Vacaville.

We'd end up going to Berkeley, Oakland, San Francisco, or somewhere else around the Bay Area. I love visiting those places, but sometimes weekend traffic is horrible, and it felt strange that we had to drive that far just to find something interesting to do.

Every once in a while, my wife would text me something closer to home.

Maybe it was a link to an Instagram post. Or something she found on Facebook. Or a local business promoting an event.

Eventually, that got me wondering:

Are there really not that many things happening in Vacaville, or are they just hard to find?

So I started looking.

Honestly, I got a headache.

The information existed. It was just everywhere.

I'm someone who likes being able to centralize information. Give me one place to look---a single source of truth---and I'm happy.

That's not what I found.

There were events on Eventbrite. Downtown organizations had their own calendars. Businesses had events on their websites. Organizers posted flyers to Instagram and Facebook. Other events lived on completely different community websites.

There wasn't necessarily a shortage of things happening.

There was a discovery problem.

You could find plenty of information if you already knew which businesses, organizers, venues, Instagram accounts, Facebook pages, and websites to follow.

But that's backwards from how I wanted to search.

I didn't necessarily know who was hosting something.

I just wanted to know:

What's happening in Vacaville this weekend?

I'd actually thought about this kind of problem before. At one point, I considered building an app where mobile food vendors could quickly post where they would be that day.

It's the same problem.

If you already know the food truck, you can follow them on social media and figure out where they'll be.

But what if you just want to know which food trucks are nearby tonight?

Local information often exists. Discovery depends on knowing who to follow.

Building something to improve that sounded like a fun challenge.

And I like building things that help people.

So I decided to see what I could make.

I didn't start by designing the website

I've been developing software for more than 25 years.

At this point, building something isn't particularly hard.

Building something useful is.

So when I started working on Events in Vacaville in January 2026, I wasn't particularly worried about whether I could build an events calendar.

I was worried about whether I could keep it full of useful events.

What good is a pretty design if there's no data to drive it?

So I started with the data.

I searched for events in Vacaville and started identifying potential sources. I looked at sites like Downtown Vacaville, Journey Downtown, Eventbrite, individual businesses, and other local organizations.

Then I started looking under the hood.

Did their pages contain structured data?

Could I find JSON-LD?

How consistently were events represented?

Could the information be gathered reliably?

Eventually I started building source-specific data gatherers. AI coding tools such as Codex and Claude helped me accelerate that work, but each source still needed to be understood individually.

That was an early lesson that has stuck with the project:

There wasn't going to be one magical event scraper.

Different sources describe events differently. Some provide clean structured data. Some don't. Some need source-specific handling.

So each source gets a gatherer appropriate for that source.

And importantly, those gatherers don't blindly write to production.

They produce data for review.

The first source of truth

The initial product requirements document described the goal pretty plainly:

"I want this app to be the central hub for all events in Vacaville CA. I want people to be able to come here, search for and find things to do."

The original MVP included event lists, filtering, a calendar, event details, community submissions, search, analytics, SEO pages and an approval workflow.

The KPI was refreshingly simple:

Traffic.

Would anyone actually use this thing?

The MVP target was March 1, 2026.

Events in Vacaville went live on February 1.

Supabase became the structured source of truth I couldn't find when I originally started searching for events.

But getting data into that source of truth became one of the most important parts of the system.

Today, the basic data pipeline looks something like this:

Source → gatherer → JSON → semantic validation/normalization → human review → editorial enrichment → Supabase

Some of that work is deterministic code.

Some of it uses a local LLM because event sources don't always describe the same concepts the same way, and interpretation is sometimes necessary.

And some of it remains deliberately human.

That's a pattern that would eventually spread across almost everything I built around EIV.

Then I had to operate the thing

A PRD can tell you what you think you'll need.

Operating the product tells you what you actually need.

The original requirements included an event submission form.

Great.

Then people can submit events.

But that creates another problem:

Someone has to manage those submissions.

I needed to review events, correct information, approve submissions, cancel events and remove bad entries.

That pain led to EIV Admin.

Then Google started sending people to the site.

That was great too.

But now I wanted to understand why.

What are people searching for?

Which pages are appearing?

Are people searching for individual venues? Recurring event series? Things happening this weekend?

That led to a Google Search Console integration and an SEO dashboard inside EIV Admin.

Not because I wanted another dashboard.

Because I had questions I needed it to answer.

That's generally how I've approached EIV ever since.

I build things when the problem asks for them.

And then there were flyers

This one became a rabbit hole.

A lot of local event organizers advertise with flyers on Instagram and Facebook.

I'd find one while using the EIV social accounts, take a screenshot, and then manually turn everything on that flyer into clean structured JSON.

Event name.

Organizer.

Date.

Start time.

End time.

Location.

Price.

Description.

And so on.

Each flyer took me about 5--10 minutes.

Do that 20 or 30 times and suddenly maintaining a "simple" community website has eaten several hours.

So I built an agent.

The current workflow is almost comically easy from my perspective:

See relevant flyer → screenshot → email it → move on with my day.

An always-on agent watches the mailbox, recognizes the subject, extracts the image, processes the flyer using a local vision model, creates the structured event data, and sends me a Slack message when it's ready.

I review the results.

The machine doesn't decide whether an event belongs on EIV. I do.

It doesn't get to blindly publish whatever it extracts. I review it.

But the part where I spend hours transcribing information from flyers?

The machine can have that job.

Today, reviewing a batch of 20--30 processed flyers takes me roughly five minutes.

Getting that system working reliably involved local vision models, golden datasets, evals, autoresearch, switching inference runtimes, and an experiment that eventually took extraction accuracy from 79% to 98%.

That's its own story.

The important part here is why I built it.

I didn't wake up wanting to build an AI flyer-processing agent.

I got tired of spending 5--10 minutes typing information from every flyer.

The same thing happened with email

My first email agent was one big agent.

As I added responsibilities, its instructions grew.

If this is a website question, do this.

If it's a bug, do that.

If an organizer is sending an event, do something else.

More instructions. More conditional branches. More context.

Eventually I looked at it and realized I had created the AI equivalent of code I already hate maintaining.

So I refactored it.

Today, one agent categorizes incoming email and hands it to smaller specialist agents.

A website question goes to an agent grounded with the EIV FAQ.

A bug follows a different workflow.

An organizer submitting an event follows another.

The agents can prepare responses.

They don't send them.

When something needs my attention, Slack tells me.

Again:

Machine prepares. Human decides.

Then I got tired of running the same SQL every week

Every week I create an EIV Weekend Roundup for social media.

Originally, that meant figuring out the dates I needed, running a Supabase query, copying the data, saving it to a text file, sorting through it, and then putting on my creative hat to write social copy.

The following week?

Do it all again.

So now an agent runs Monday evening.

It determines the upcoming Friday-through-Sunday window, queries Supabase, selects the fields I need, sorts everything chronologically, generates a CSV, and drafts separate Instagram and Facebook copy using the actual events as context.

Then:

Slack: it's ready.

I review it and make whatever changes I want.

I don't have an AI system generating the entire social carousel because I don't need one.

I already use Adobe Express.

I built a template there that can bulk-create the carousel from the CSV my agent generates.

That's an important part of my approach too:

Automation doesn't mean rebuilding everything yourself.

Sometimes the right solution is simply getting information from Tool A into exactly the shape Tool B needs.

My newsletter works the same way

Monday night, another agent queries Supabase for events from Thursday through the following Wednesday.

It orders the events, gathers the fields needed for the newsletter, and drafts a subject line, intro and outro based on what's happening that week.

Then it connects to Kit and creates the newsletter.

But it creates it as an unscheduled draft.

Slack tells me it's ready.

I log into Kit, read everything, make edits, and when I'm satisfied, I schedule the delivery.

Could I automate that final click?

Probably.

I don't want to.

The goal isn't to see how much control I can give an agent.

The goal is to decide where automation is valuable and where human judgment is valuable.

Somehow, my simple website became this

At this point, Events in Vacaville has several distinct pieces working together.

The architecture looks roughly like this:

Public Web
The thing people actually see: event discovery, event pages, submissions and newsletter signup.

Supabase
The structured source of truth underneath the system.

Data Collection & Skills
Source-specific gatherers, validation, normalization, editorial enrichment and importing.

EIV Admin
The tools I need to operate the product: event management and search/SEO visibility.

Agents
Always-on workers handling flyer intake, email triage, the Weekend Roundup and newsletter preparation.

Agent Dashboard
A control surface where I can see what agents are running, when they're scheduled, manually trigger them, and inspect what happened.

Slack
My attention layer.

And then there are existing products around the edges---Google Search Console, Kit, Adobe Express, email---that already solve problems well enough that I have no interest in rebuilding them.

The current Events in Vacaville architecture. None of this was designed up front—the system grew as operating the site exposed new problems to solve.

The interesting part is that I didn't sit down in January and design all of this.

Most of it wasn't supposed to exist.

The architecture followed the pain

That's probably the biggest thing Events in Vacaville has reinforced for me.

I don't like building things simply because I can.

The original PRD contains plenty of ideas I haven't built.

Some may eventually make sense.

Some may never make sense.

That's fine.

A roadmap is a hypothesis, not a commitment.

Instead, most of the systems surrounding EIV have come from using the product and paying attention to friction.

Community submissions became painful to manage.

Build the minimum Admin tooling I need.

I needed better visibility into search behavior.

Add the SEO dashboard.

Flyer transcription consumed hours.

Build the Flyer Intake agent.

The email agent became complicated.

Refactor it into specialists.

Weekly social preparation became repetitive.

Automate the preparation.

Newsletter creation became repetitive.

Automate that too.

Agents started running continuously.

Build enough of a dashboard to operate them.

Then stop.

Until another real problem appears.

I'm not trying to automate myself out of EIV

There's a lot of discussion right now about autonomous AI agents.

That's not particularly interesting to me as a goal by itself.

I don't want AI deciding which events are appropriate for my community site.

I don't want it blindly publishing event information.

I don't want it automatically emailing people as me.

I don't want it sending a newsletter I haven't read.

Those are places where my judgment matters.

What I don't need to personally do is spend three hours transcribing flyers, repeatedly calculate date ranges for SQL queries, copy database results into files, or assemble the same newsletter structure every week.

So I've ended up with a fairly simple rule:

Let the machines do the waiting and repetitive preparation. Save the human for judgment.

Sometimes that machine is an LLM.

Sometimes it's a cron job.

Sometimes it's a Python script.

Sometimes the solution is just a better admin screen.

And sometimes the best engineering decision is to use Adobe Express, Kit, or another tool that already solves the problem and move on.

The technology isn't the point.

The time it gives me back is.

Events in Vacaville still looks like a simple local events website.

I hope it always does.

The complicated part is my problem.