First-pass structural reference for the Vite SPA migration (apps/web): dual-app tree during migration, shared lib/ networking layer, origin-relative/same-origin serving model, build scripts, the P1-P6 increment map, and the #1145 P5 blocker. Living doc — details to be fleshed out by follow-up. Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_01ESFAnh2t9HmLwng8oW95St
6.8 KiB
WebUI Phase P — File / Folder Structure & Migration Map
Status: living document — first pass. Structure and increment status are verified against
nextas of merge8c27024d. Details (per-surface component inventories, exact route tables, test matrices) are still being fleshed out; extend the stub sections below rather than rewriting the verified structure.
1. What Phase P is
Phase P migrates the Mosaic web UI (apps/web) from the legacy Next.js App Router app to a
Vite + React Router single-page app (SPA) that the Gateway serves same-origin on
:14242. The RFC splits the work into six increments (P1–P6); the P1 PR title records this as
"increment 1/6".
The migration is deliberately incremental and non-destructive: the new SPA is built up
beside the existing Next app, sharing one apps/web/src/lib networking/auth layer, until the
final cutover (P5) removes the Next tree. At every point in between, both app trees exist in the
same package — this is intentional, not drift.
2. Current tree on next (dual-app, transitional)
apps/web/
├── next.config.ts # legacy Next.js config (removed at P5)
├── vite.config.ts # SPA build + DEV proxy config (canonical from P5)
├── package.json # dev/build default to NEXT today; :vite variants opt in
└── src/
├── main.tsx # ── SPA entry (Vite)
├── routes.tsx # ── SPA React Router route table
├── spa/ # ── NEW SPA surfaces
│ ├── guards.tsx # guest / authenticated route guards
│ ├── pages/ # login, register, sso-callback (P2); chat + error boundary (P3)
│ └── chat/ # P3 typed chat: use-chat-connection, commands-panel,
│ # session-panel, message-transcript, tool-call-list, composer
│
├── lib/ # ── SHARED by BOTH trees (origin-relative networking + auth)
│ ├── api.ts # fetch wrapper — relative /api/...
│ ├── socket.ts # Socket.IO singleton — relative /chat
│ ├── auth-client.ts # BetterAuth client — relative /api/auth/...
│ ├── auth-redirect.ts # post-auth redirect resolution (protocol-relative rejected)
│ ├── chat-contract.ts # P3 typed chat wire contract (runtime-guarded)
│ ├── sso.ts · types.ts · cn.ts
│
├── app/ # ══ LEGACY Next.js App Router (removed at P5)
│ ├── (auth)/{login,register}/
│ ├── (dashboard)/{admin,chat,projects,projects/[id],settings,tasks}/
│ ├── auth/provider/[provider]/
│ └── layout.tsx · page.tsx · globals.css
│
├── components/ # ══ LEGACY Next component library (auth, chat, layout,
│ # projects, settings, tasks, ui) — ported into spa/ across P3/P4
└── providers/ # ══ theme-provider (legacy; SPA equivalent under providers)
Legend: ── new SPA (keep), ══ legacy Next (removed at P5), shared lib/ in the middle.
3. Networking / serving model (why it's same-origin)
- The SPA speaks origin-relative paths only:
/api/...,/api/auth/...,/chat. NoNEXT_PUBLIC_*/VITE_*origin var, no hard-codedhttp://localhost:14242underapps/web/src. - Dev:
vite.config.tsruns a dev-only proxy that forwards those paths to the Gateway (so the SPA on its dev port and the Gateway on:14242behave as one origin). - Prod (target): the SPA is same-origin with the Gateway — the Gateway serves the built
static bundle and the API/WS on
:14242, so no proxy and no CORS. (The Gateway does not serve the webdistyet — adding that is the core of P5; see §5.)
4. Build scripts (apps/web/package.json)
| Script | Today | Notes |
|---|---|---|
dev |
next dev |
legacy dev server |
dev:vite |
vite |
SPA dev server (+ dev proxy) |
build |
node ../../scripts/build-web.mjs |
currently a Next build |
build:vite |
vite build |
SPA production build → dist/ |
lint / typecheck / test |
eslint src / tsc --noEmit / vitest run |
tree-agnostic |
At P5 the :vite variants become the defaults (dev→vite, build→vite build) and the Next
build path is retired.
5. Increment map (P1–P6)
| # | Increment | Branch | Status |
|---|---|---|---|
| P1 | Vite + React Router skeleton beside Next (entry, router, guards, vitest) | feat/webui-p1-vite-skeleton |
✅ merged — PR #1143 |
| P2 | SPA data layer + same-origin auth (login/register/SSO pages, guards, relative api/socket/auth-client) | feat/webui-p2-data-auth |
✅ merged — PR #1144 |
| P3 | Typed SPA chat (spa/chat/*, chat-contract.ts, chat page + error boundary) |
feat/webui-p3-chat |
🚧 in progress (unmerged) |
| P4 | Port projects / tasks / settings / admin dashboard surfaces into the SPA | tbd | ⏳ not started |
| P5 | Cutover: Gateway serves the Vite dist on :14242; flip dev/build to vite; remove the legacy Next app/ tree + next.config.ts |
tbd | ⏳ not started |
| P6 | CI / images (trails): build the SPA in CI, ship images | tbd | ⏳ trails |
Each increment follows the same delivery pipeline: brief traceable to the RFC → author →
independent integrator verification (build+test+typecheck+lint) → independent code + security
review (author ≠ reviewer) → author remediates → branch + PR to next → independent merge-gate
merges. Author self-reports are not trusted; every gate is re-derived independently.
6. Known dependency / blocker
- Issue #1145 — Gateway
distboot is broken (DI failure on a defaulted constructor param); the Gateway currently runs dev-mode only. This is a hard precondition for P5: the Gateway cannot serve the SPAdiston:14242untildistboot works. P3/P4 remain on the dev-proxy topology meanwhile.
7. Not part of Phase P (disambiguation)
docs/plans/2026-08-09-webui-fleet-claude-bridge.md and
docs/scratchpads/webui-fleet-bridge-plan.md describe a separate WebUI ↔ fleet/Claude bridge
effort. They are not the Phase P SPA migration and should not be conflated with the increments
above.
8. Where the detail lives (extend these)
- Per-increment working notes:
docs/scratchpads/webui-p*-*.md(e.g.webui-p2-data-auth.md). - Stub — to flesh out: per-surface component inventory (which
components/*port to whichspa/*), the full SPA route table, the P5 cutover checklist, and the P6 CI/image plan.