diff --git a/docs/webui/PHASE-P-STRUCTURE.md b/docs/webui/PHASE-P-STRUCTURE.md new file mode 100644 index 00000000..b82475fb --- /dev/null +++ b/docs/webui/PHASE-P-STRUCTURE.md @@ -0,0 +1,115 @@ +# WebUI Phase P — File / Folder Structure & Migration Map + +> **Status:** living document — first pass. Structure and increment status are verified against +> `next` as of merge `8c27024d`. 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`. No + `NEXT_PUBLIC_*` / `VITE_*` origin var, no hard-coded `http://localhost:14242` under + `apps/web/src`. +- **Dev:** `vite.config.ts` runs a dev-only proxy that forwards those paths to the Gateway (so the + SPA on its dev port and the Gateway on `:14242` behave 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 web `dist` yet — 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 `dist` boot 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 SPA `dist` on `:14242` until `dist` boot 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 which + `spa/*`), the full SPA route table, the P5 cutover checklist, and the P6 CI/image plan.