Skip to content
AI-Native PM
Field Notes

Field Note

Why We Run a Research Brief Before Every Migration

We wanted FuelTheFam's app on Cloudflare and its database with it, so before either migration we ran a research brief. The first told us to upgrade the app before moving it, and the second told us not to move the database at all. We spent the time we saved hardening the stack we already had.

· 5 min read

In late July we shipped the redesign of FuelTheFam, the family nutrition app we run, and the next morning we asked a bigger question. Everything else we operate lives on Cloudflare, while the app sat on Vercel with its database on Supabase, and we wanted everything in one place. That meant two moves: the app onto Cloudflare Workers, and, if it could be done well, the database onto something Cloudflare-native.

We did not begin either move as a migration. Before each one we ran a research brief: we sent a fleet of agents to verify the current facts of the move, with separate tracks checking the same claims independently against the primary sources, and we asked for one dated brief at the end that had to open with a verdict. We chose that format so that a no counted as a real answer: when the brief has to open with a verdict, "do not proceed" is as legitimate an opening as "go ahead".

Before any move you cannot easily undo, commission a research brief, and make it clear that "do not proceed" is an acceptable answer.

Two migrations, each decided by a research brief before it startedTwo rows. In the first, the ask to move the app to Cloudflare passes through a research brief and returns the verdict not yet, because the adapter refuses Next 14; the outcome is upgrade first, then move. In the second, the ask to leave Supabase returns the verdict no, because the target has no row-level security and no sign-in product; the outcome is stay, rotate keys, and harden.TWO MOVES, TWO BRIEFS, TWO ANSWERSMove the appto CloudflareRESEARCH6 agents, 5 tracksNOT YETthe adapterrefuses Next 14Upgrade to 15,then moveLeave Supabase fora Cloudflare DBRESEARCH8 agents, 4 tracksNOno row-level security,no sign-in productStay, rotatekeys, hardenBoth answers arrived before anything moved.

The first brief: upgrade the app before moving it

The verdict at the top of the Cloudflare brief was not yet. OpenNext, the adapter that runs Next.js apps on Cloudflare Workers, had dropped support for Next.js 14 months earlier, and its package declares the requirement outright: next >=15.5.21. Agents on two separate research tracks each confirmed that range against npm, while the adapter's own documentation still said Next 14 worked. Our app ran Next 14. Without the brief we would have started the migration and hit that wall an hour in, with the documentation still saying Next 14 was supported.

So we changed the order of the work and nothing else. We upgraded the app to Next.js 15 in one commit, and then finished the move in a day: we split the marketing page out as a static site, put the app on Workers, cut the domain over, and removed Sentry, our error monitoring, so the whole thing runs on the free plan.

The second brief: do not move the database

Halfway through the cutover we asked the more ambitious question: could we move the database off Supabase and into Cloudflare too? The verdict at the top of the second brief was do not move. Cloudflare has no database with row-level security (RLS), the database-side rule that keeps one family's rows unreadable to another, and no consumer sign-in product. Going all-Cloudflare would have meant hand-writing the authorization for a database that holds children's names, ages, and food logs, weeks after we had missed an authorization bug in our own review. If we ever do leave, the recommendation in the brief was a hosted Postgres such as Neon plus a dedicated sign-in service such as Clerk, and not D1, the database Cloudflare does offer.

We stayed, and we lost nothing by staying, because the wish to be all-Cloudflare was tidiness rather than a requirement. The price of that tidiness would have been hand-building the security layer for a product that stores information about children, and once we saw it put that way in the brief, declining was easy.

What we hardened with the time we saved

We did not get those days back by saying no; we spent them on the stack we kept instead. Straight after the cutover, we made the hardening pass we had queued behind the migration.

The hardening that landed in the days the migration did not consumeA ledger of five completed fixes: authorization checks before the RLS-bypassing client, email subject sanitization, the open nutrition proxy closed, per-family cache isolation, and secrets that fail loudly with a webhook that fails closed.WHAT THE FREED DAYS BOUGHTEvery route validates the user before the RLS-bypassing client runsAUTHORIZATIONThe notification email subject is sanitized against injectionINJECTIONThe open proxy to the nutrition database is closedEXPOSURECache tags scoped per family, and family rows made uniqueISOLATIONMissing secrets fail loudly, and the billing webhook fails closedSECRETSEvery fix landed on the stack the second brief told us to keep.

Every route that uses the database client capable of bypassing row-level security now validates the signed-in user first. The notification email's subject line is sanitized against injection. A proxy the app exposed in front of the public nutrition database, open to anyone who found it, is closed. Cache tags are scoped per family and family rows are unique, so one household's data cannot bleed into another's view. Secrets fail loudly when they are missing, the billing webhook fails closed instead of open, refusing what it cannot verify, and the database keys were rotated to Supabase's new key format on the way out. None of it was migration work, which is exactly what we wanted: we spent our risk budget closing real holes in the platform we already understood.

Moving platforms also means rewriting pages you rarely open. We had already made one pass through the legal pages for the rebrand, renaming the old working title, Family Nutrition Dashboard, to Fuel the Fam everywhere, including a terms opener that a literal find-and-replace would have turned into "Fuel the Fam, operated by Fuel the Fam," and which now defines who "we" and "us" bind instead. Now the platform change means we owe a second pass, because a privacy page lists the services that process user data, and that list has to match the infrastructure: we move the hosting entry off Vercel and take Sentry off the list entirely. That list is the slowest-moving part of any migration and the easiest to forget, and ours is mid-move as we write this.

Where this goes next

We examine the product all of this happened to, end to end, in the FuelTheFam case study. We describe the standing instruction behind this tactic in Why We Told the AI to Disagree With Us, and in How We Caught AI Mistakes With a Second Agent we point the same verification instinct at finished work instead of plans. The Frontier's Multi-Agent Orchestration part covers the mechanics of running research as a fleet, with independent tracks and adversarial checks.

Sources

  • The two decision briefs, both prepared 2026-07-25: "Cloudflare Migration: Decision Brief" (six agents, five research tracks) and "Decision brief: replacing Supabase in Fuel the Fam" (eight agents, four tracks). Internal documents.
  • The FuelTheFam repository, July 24 to 25, 2026: the redesign merge and the eight pull requests that followed it, covering the migration, the monitoring removal, and the secrets, authorization, injection, and proxy fixes.
  • The Workers adapter's requirement, next >=15.5.21, with Next.js 14 support removed in adapter v1.16.0 on 2026-01-26; verified against the npm registry on 2026-07-25.