Problem
The Next.js app router resolves to app/app — a directory whose only tracked file is a build artifact, app/app/.next/analyze/summary.json — so every real page and route handler in the repository compiles to nothing. Verified with Next.js's own resolver from app/:
findPagesDir: { pagesDir: undefined, appDir: '/workspaces/XStreamRoll/app/app' }
next/dist/lib/find-pages-dir.js resolves the app directory with findDir(dir, 'app'), which prefers ./app over ./src/app:
function findDir(dir, name) {
// prioritize ./${name} over ./src/${name}
let curDir = _path.default.join(dir, name);
if (_fs.default.existsSync(curDir)) return curDir;
curDir = _path.default.join(dir, 'src', name);
...
}
The app/app directory exists (its only tracked content is the committed bundle-analyzer baseline app/app/.next/analyze/summary.json), so Next never reaches app/src/app, where the real pages live. app/app contains no page.tsx, no layout.tsx, and no route handlers.
The consequence is total: next build emits only Next's auto-generated _not-found and _global-error slots — app/.next/server/app-paths-manifest.json contains exactly those two entries — so the built frontend serves a blank app. Every tracked page is outside the compiled router:
app/page.tsx, app/layout.tsx, app/auth/login/page.tsx, app/auth/register/page.tsx, app/api/auth/* route handlers
app/src/app/page.tsx, app/src/app/dashboard/**, app/src/app/admin/**, app/src/app/api/health/route.ts, app/src/app/providers.tsx
The CI bundle-analysis job (bundle-analysis in .github/workflows/ci.yml) builds the app and checks bundle sizes with node .github/scripts/check-bundle-size.js — but the budgets are upper bounds (BUNDLE_BUDGET_TOTAL / BUNDLE_BUDGET_PER_LARGEST), so an empty build of ~0 bytes passes. The regression is invisible to CI.
Root cause
app/ # Next project root (next.config.mjs, package.json)
├── app/ # ← resolves as the app router; contains only .next/
│ └── .next/analyze/summary.json # committed bundle-analyzer baseline
├── src/app/ # ← the real pages (dashboard, admin, auth-adjacent, api/health)
└── page.tsx, layout.tsx, auth/, api/ # ← pre-src layout leftovers, also outside the router
Why this is architecturally hard
- There are two incompatible layout generations in the tree: the pre-
src layout (app/page.tsx, app/layout.tsx, app/auth/*, app/api/auth/* at the project root) and the src/app layout (app/src/app/**). Fixing the resolution is not enough — the two must be reconciled into one canonical layout, or the fix lands on top of a still-inconsistent tree.
app/layout.tsx imports ./src/app/providers — a cross-directory dependency that only makes sense in the pre-src world. If src/app becomes the router, a root layout.tsx must be created inside src/app (there is none today), and the pre-src leftovers (page.tsx, layout.tsx, auth/, api/auth/*) must be moved or deleted; if the project root becomes the router instead, src/app/** must be moved up. The issue's deliverable is a single, buildable layout — the direction is a design decision.
- The committed artifact
app/app/.next/analyze/summary.json is what creates the shadowing directory in the first place. Whatever the chosen layout, the stray app/app directory must not exist at build time, and the bundle-analysis baseline (which .github/scripts/check-bundle-size.js writes into <appDir>/.next/analyze/summary.json) needs a home that cannot shadow the router.
- CI cannot catch this today (empty bundle passes the budget check). Part of the fix is making the build gate real: assert a minimum bundle size or assert that the expected routes exist in the build output.
Proposed design
Choose one canonical layout and migrate everything into it, e.g.:
- Delete the stray
app/app directory (including the committed .next/analyze/summary.json baseline) so findDir falls through to app/src/app; create app/src/app/layout.tsx (moving the root layout's providers/theme/toaster wiring); move app/auth/* and app/api/auth/* into app/src/app/ (or delete them if superseded); delete the orphaned app/page.tsx and app/layout.tsx.
Or keep the root as the router and move src/app/** up — but the src/app layout is where the recent work lives, so consolidating on src/app is the lower-friction direction.
Acceptance criteria
Build
CI
Tests
Documentation
Out of scope
Rebuilding the auth flow or any individual page — those are separate issues (the auth flow is tracked separately and depends on this one landing first). This issue is only about making the frontend build and serve real routes again.
Getting started
Real files in scope: app/app/.next/analyze/summary.json (delete or relocate), app/src/app/ (add layout.tsx, move auth/ pages and api/auth/* handlers in), app/page.tsx, app/layout.tsx (remove or move), .github/scripts/check-bundle-size.js (add a lower bound), .github/workflows/ci.yml (bundle-analysis job).
Verify with:
cd app && npm run typecheck && npm test && npm run build
ls app/.next/server/app-paths-manifest.json # must list the real routes
Good first files to read: app/layout.tsx, app/src/app/providers.tsx, app/src/app/dashboard/streams/page.tsx, .github/scripts/check-bundle-size.js.
Problem
The Next.js app router resolves to
app/app— a directory whose only tracked file is a build artifact,app/app/.next/analyze/summary.json— so every real page and route handler in the repository compiles to nothing. Verified with Next.js's own resolver fromapp/:next/dist/lib/find-pages-dir.jsresolves the app directory withfindDir(dir, 'app'), which prefers./appover./src/app:The
app/appdirectory exists (its only tracked content is the committed bundle-analyzer baselineapp/app/.next/analyze/summary.json), so Next never reachesapp/src/app, where the real pages live.app/appcontains nopage.tsx, nolayout.tsx, and no route handlers.The consequence is total:
next buildemits only Next's auto-generated_not-foundand_global-errorslots —app/.next/server/app-paths-manifest.jsoncontains exactly those two entries — so the built frontend serves a blank app. Every tracked page is outside the compiled router:app/page.tsx,app/layout.tsx,app/auth/login/page.tsx,app/auth/register/page.tsx,app/api/auth/*route handlersapp/src/app/page.tsx,app/src/app/dashboard/**,app/src/app/admin/**,app/src/app/api/health/route.ts,app/src/app/providers.tsxThe CI bundle-analysis job (
bundle-analysisin.github/workflows/ci.yml) builds the app and checks bundle sizes withnode .github/scripts/check-bundle-size.js— but the budgets are upper bounds (BUNDLE_BUDGET_TOTAL/BUNDLE_BUDGET_PER_LARGEST), so an empty build of ~0 bytes passes. The regression is invisible to CI.Root cause
Why this is architecturally hard
srclayout (app/page.tsx,app/layout.tsx,app/auth/*,app/api/auth/*at the project root) and thesrc/applayout (app/src/app/**). Fixing the resolution is not enough — the two must be reconciled into one canonical layout, or the fix lands on top of a still-inconsistent tree.app/layout.tsximports./src/app/providers— a cross-directory dependency that only makes sense in the pre-srcworld. Ifsrc/appbecomes the router, a rootlayout.tsxmust be created insidesrc/app(there is none today), and the pre-srcleftovers (page.tsx,layout.tsx,auth/,api/auth/*) must be moved or deleted; if the project root becomes the router instead,src/app/**must be moved up. The issue's deliverable is a single, buildable layout — the direction is a design decision.app/app/.next/analyze/summary.jsonis what creates the shadowing directory in the first place. Whatever the chosen layout, the strayapp/appdirectory must not exist at build time, and the bundle-analysis baseline (which.github/scripts/check-bundle-size.jswrites into<appDir>/.next/analyze/summary.json) needs a home that cannot shadow the router.Proposed design
Choose one canonical layout and migrate everything into it, e.g.:
app/appdirectory (including the committed.next/analyze/summary.jsonbaseline) sofindDirfalls through toapp/src/app; createapp/src/app/layout.tsx(moving the root layout's providers/theme/toaster wiring); moveapp/auth/*andapp/api/auth/*intoapp/src/app/(or delete them if superseded); delete the orphanedapp/page.tsxandapp/layout.tsx.Or keep the root as the router and move
src/app/**up — but thesrc/applayout is where the recent work lives, so consolidating onsrc/appis the lower-friction direction.Acceptance criteria
Build
cd app && npm run buildsucceeds andapp/.next/server/app-paths-manifest.jsoncontains real routes:/,/auth/login,/auth/register,/dashboard/streams,/admin, and the/api/healthroute handler.node -e "require('next/dist/lib/find-pages-dir').findPagesDir(process.cwd())"fromapp/) returns a directory that containspage.tsxandlayout.tsx.app/appexists in the repository after the fix.CI
bundle-analysisjob fails (or a new assertion fails) when the build emits zero pages — add a minimum-total-bytes assertion in.github/scripts/check-bundle-size.jsor an equivalent route-presence check.npm run analyzeoutput is written to a location that cannot shadow the app router.Tests
cd app && npm run testpasses after the layout consolidation (any page that referenced@/lib/...or@/components/...from the moved layout keeps working).Documentation
README.md's "Package Breakdown" / run instructions still match the layout actually built (e.g. the app runs fromapp/src/approutes).Out of scope
Rebuilding the auth flow or any individual page — those are separate issues (the auth flow is tracked separately and depends on this one landing first). This issue is only about making the frontend build and serve real routes again.
Getting started
Real files in scope:
app/app/.next/analyze/summary.json(delete or relocate),app/src/app/(addlayout.tsx, moveauth/pages andapi/auth/*handlers in),app/page.tsx,app/layout.tsx(remove or move),.github/scripts/check-bundle-size.js(add a lower bound),.github/workflows/ci.yml(bundle-analysis job).Verify with:
Good first files to read:
app/layout.tsx,app/src/app/providers.tsx,app/src/app/dashboard/streams/page.tsx,.github/scripts/check-bundle-size.js.