build-logfestival-of-snowai-agents6 min read

The Week I Almost Flipped Festival of Snow to Production

1 June 2026

Festival of Snow is live. The coming-soon page is doing exactly what it should be doing.

That's the headline. Everything else this week was about almost doing it wrong.


The Setup

I sat down on Monday night wanting to flip Festival of Snow from the coming-soon page to the real application. The code is there. It builds. The Vite bundle is in dist/. Sixteen components, three services, 2,911 lines of code, the full PulseMap, the passport system, the dual-lens SOUL/SHRED view. It's all in the repo. It works locally.

The instinct is to push the button.

The instinct was wrong.


What I Almost Shipped

Festival of Snow calls the Google Gemini API to score images for authenticity, run the bot scout protocols, and generate "Field Reports" for the SOUL mode. That code lives in two files: services/geminiService.ts and services/botScoutService.ts.

Both files have a line near the top that looks like this:

const ai = new GoogleGenAI({ apiKey: process.env.API_KEY });

That line is at module scope. It runs the moment the page loads — before React mounts, before the first component renders, before anything else has a chance to happen. If process.env.API_KEY is undefined (because the GEMINI_API_KEY environment variable isn't set in Vercel), the constructor throws, the module fails to evaluate, and the whole bundle dies before the app can render.

The page goes blank. No error in the user-facing UI. Nothing in the Vercel deployment logs that says "your app is broken." Just a blank screen.

I almost shipped a blank screen to festivalofsnow.com.


The Fix That Already Existed

Here's the part that made me stop and write this down.

A previous Brumalia session had already diagnosed this exact bug. The fix was committed to a branch called preview/fos-review. The commit message: fix: avoid boot crash when gemini key is missing. The fix replaced the top-level instantiation with a lazy getter that returns null if the key is missing, and every caller checks for null and returns a safe fallback.

The fix is correct. It's tested. It builds.

The branch is dead. origin/preview/fos-review is gone. The fix is reachable via git log --all but never landed on main.

So the bug is known. The fix exists. The work is done. The deploy button is right there.

But the fix isn't where it needs to be.


Why I Stopped

The fix is one cherry-pick away. It would take about thirty seconds. git cherry-pick 9e964df, push, Vercel auto-deploys, we're done.

I didn't do it. Not because the fix is wrong, but because the decision to flip the production app from the coming-soon page to the real application is a different decision than the technical decision to apply the fix.

The PRD calls for V1 to be real. Real weather data. Real authentication. Real resort/region intelligence. Real passport progression. Real commerce flows. The current implementation is a polished concept prototype, not a production application. The PRD says so explicitly:

The current repository should be treated as a polished concept prototype, not yet as the production application.

Flipping the production app to the real SPA would be presenting the prototype as the product. It would feel like the product. It would look like the product. But it would not be the product, because the product is real, and the prototype is simulated.

The coming-soon page is the honest version of the project right now. It says: this is taking shape. The full app exists. But the full app isn't the product yet.


What I Did Instead

I sat down with Brumalia and did the work that needed to be done tonight.

I documented the real state of Festival of Snow in a brain page. The full file structure, the components, the services, the four V1 pillars (Mountain Intelligence, Editorial/Cultural Discovery, Passport Retention, Gear Commerce), the open decisions around business model and AI positioning.

I wrote a skill for the repo. The single most important rule in the skill: do not flip App.tsx from the coming-soon page to the real application until the boot-crash fix is on main and the production environment has the required credentials. Future-me will load that skill before touching the project and remember why the flip is a decision, not a button press.

I committed the .gitignore change for .vercel that had been sitting in the working tree. I reviewed the existing coming-soon page and confirmed it's the correct production surface.

I did not flip the production app.


The Two Things I Learned

1. "It builds" is not "it's ready."

The Festival of Snow app builds cleanly. It runs locally. The components render. The PulseMap spins up the Three.js scene. The passport system tracks state. The dual-lens view toggles between SOUL and SHRED.

It does not survive contact with a production environment that doesn't have every secret it expects to find. That's not a build problem. That's a deployment readiness problem. And it's a different problem than the one a green build solves.

2. The fix is not the same as the decision.

The boot-crash fix is one cherry-pick. That's not what I was deciding tonight. I was deciding whether to present the prototype as the product. The fix doesn't answer that question. Only the PRD, the open decisions around business model and AI positioning, and the V1 strategy do. And they all say: not yet.

The discipline is knowing which decision you're actually making.


What's Next

The Festival of Snow PRD is at v4.2. The V1 decision record is at v1. The V1 product strategy is at v1. The Supabase backend is specified but not built. The OAuth flow is designed but not implemented. The real weather integrations are planned but not started.

The next big push for Festival of Snow isn't a code push. It's the rebuild phase. The current implementation is the reference; the production application is what comes after. The PRD says so. The decision record says so. The strategy says so.

The coming-soon page stays. The blizzard is coming. The work continues.

Matty Horne Founder, Brumalia Studios