What the parity pass found
In about ten days almost the entire app got rebuilt from a design prototype: the shell, six tools, the marketing site, Learn, Study, certificates, the dashboard and two labs. The prototype lived in a hosted design tool, so the rule on BMU-104 was to archive it into the repo first: 88 files pulled into docs/studio/prototype/ before any porting started, so the spec lived in the repo instead of in a tool. By the 13th, all 88 were archived and most of the app matched them.
Matching the prototype screen for screen is not the same as the app being internally consistent. The parity pass that followed went and checked, and it found the app telling itself different things in different places.
The route matcher that gated nothing
The sharpest one: middleware.ts's matcher was supposed to exclude API routes, static assets and anything with a file extension, and run on everything else. What it actually had was a trailing . where .* was meant:
# before: matches only single-character path segments
/((?!api|_next/static|...).)
# after: segment-anchored exclusions, .* restored
/((?!api/|_next/|og/|.*\..*).*)
A bare . matches exactly one character. /dashboard, /account, /media, /certificates and /learn all answered 200 to an anonymous request, because middleware never ran on any of them. The protected-routes list existed. It just never got a chance to protect anything.
Two more bugs came out of the same fix. The route check used startsWith, so /learner-survey would have been gated as if it were /learn and /media-kit as /media, and both now match on segment boundaries. And ?redirect= was dropping its query string on the way through, so /learn?course=hls sent you to bare /learn after signing in.
Two prices, both live
/pricing showed Free and Pro. The account page's billing tab, which the Pro plan's upgrade button linked to, still rendered a stale three-tier table ($19 and $39 plans) from a data file I had kept alive the day before because the billing tab still imported it. Both surfaces now read from one function, at one price: $12.99 a month or $129 a year. The stale file is gone.
Roadmap said five, app had one
The roadmap page claimed "five courses with live graphs" while HLS was listed on the same page as in progress and Media Pipelines as next. Only the original FFMPEG course had lesson content behind it. Corrected to claim one until the other four were real.
Certificates nobody could verify
/verify/[certificateId] looked certificates up in the viewer's own local list, so a shared verification link always failed, even though the underlying table had a public read policy that would have answered the query fine. The deeper cause: certificates were being minted client-side for anonymous users (a random id plus localStorage), which meant they were unverifiable by anyone but the browser that made them. Fixed by requiring sign-in to mint one.
Fixing that surfaced a second bug in the same code path: a score was being read before the state update that set it had landed, so every first certificate issued had been stamped averageScore: 0.
A demo that looked like a tool
Corruption Check first shipped exactly as the prototype had it: a drop always selected demo file 0 regardless of what you dropped, and the check ran a scripted timeline over fixed findings. Its commit message said so. The next day it became a real analyzer, lib/corrupt/analyze.ts over the same MP4 box parser what-the-ffmpeg uses, with six checks derived from the actual file: moov after mdat, samples that run past the end of the file, an H.264 track whose parameter set yields no SPS or PPS, A/V start drift from the leading edit list, decreasing DTS or a sample-count mismatch, and NAL length prefixes that don't span their sample.
Building the real version caught a live false positive: the parser mistypes QuickTime .mov tracks as data, because a second hdlr box nested under minf silently overwrites the first one. Parameter-set and NAL checks now report skip rather than pass for a .mov file, since an absent SPS there means "not read," not "not there."
Social cards pointing at localhost
SITE_URL fell back to http://127.0.0.1:3000, and that variable was unset on both the production and preview Vercel environments, so every deployed Open Graph image would have pointed at a machine nobody else can reach. The URL got a real fallback chain. The brand mark inside the cards took two dead ends first: reading the brand-mark asset with fs.readFileSync(new URL(...)) works in dev but breaks the production webpack build, and building the path with process.cwd() builds fine but throws at cold start because public/ isn't traced into the serverless bundle. The asset ended up checked in as a base64 data URI instead.
One course, 101 unlinkable lessons
Every lesson in the FFMPEG course canonicalized to /learn. None of the 101 could be shared or indexed on its own, and listing them in the sitemap would have been worse than listing none. Moved to /learn/[moduleId]/[lessonId]. The sitemap went from around 15 URLs to 116, and then, the same day the auth gate above actually started running, back down to 7, since the lesson pages go noindex behind sign-in.
Two days later a second audit compared every shipped screen with the prototype again, after re-syncing the archive. It came back with 60 findings, and those became the next week.