Twenty One Media
webJuly 27, 2026

The Build Passed and the Page Didn't Exist

We shipped the /book scheduling page last weekend. Not the functionality — that had been live for days. The actual file.

Here's what happened: the route src/app/book/page.tsx existed on Isaac's laptop. The Header, CityLanding, and ServiceHub components had all been updated to point their "Book a call" CTAs at /book. The site.ts config had the real Google Calendar embed URL. npm run build ran clean. Everything looked ready.

The file had never been committed to git.

Why the Build Doesn't Save You

Next.js builds from the filesystem, not from git. If a file exists on your machine and is referenced at build time, it compiles. The build doesn't know or care that the file isn't tracked. Green local build, green local preview, file ships to production via... nothing, because it's not in the repository.

In a solo dev workflow this is easy to miss. You create a file, wire it up, test it locally, confirm it looks right in the browser, and move on. The commit happens later, maybe in a different session. If "later" never comes before you push the surrounding work, you've got CTAs pointing at a 404 on every production page.

The Specific Failure Mode

The three components that pointed at /book were committed and pushed. The configuration in site.ts was committed and pushed. The sitemap didn't include /book yet, but that's a softer miss. The page itself: not in the repo, not deployed.

If we'd pushed to production in that state, every "Book a call" button on the site would have 404'd. That's the primary conversion path for the agency. The embed URL was ready. The Calendar was configured. The CTA copy was live. The page was a ghost.

We caught it by auditing the handoff doc before the final push and cross-referencing what the build had compiled versus what was actually tracked.

What We're Doing About It

Two guard rails are now on the backlog. Neither is complicated.

The first is a reachability check: scan the navItems array and every hardcoded href in the three layout components, then verify each route has a corresponding directory under src/app/. This catches the linked-but-missing case. A simple script, runs before push.

The second is a broader untracked-file check: flag any file under src/ that git status reports as untracked. This catches the inverse, files that exist locally but haven't been staged. The check doesn't block — it just prints a warning so you can decide whether the file is intentionally local or an accidental omission.

Neither of these would have required much effort to write. The mistake cost more time than the prevention would have.

The Broader Pattern

We build in sessions. One session wires up the CTAs and config. Another session builds the actual page. A third session deploys. If the second session's output doesn't get committed before the third session pushes, you ship a broken experience that looks complete from the inside.

The fix isn't to be more careful. It's to make the gap visible. A pre-push hook that surfaces untracked files in src/ would have caught this in under a second. We're adding it.

The build passed. The page is committed now. And the Calendar embed is live at /book if you want to grab 30 minutes.