Files
stack/docs/webui/PHASE-P-STRUCTURE.md
T
shaggy (mosaic-dev box)andClaude Opus 4.8 a45f53071a
ci/woodpecker/pr/ci Pipeline failed
docs(webui): add Phase P structure & migration map
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
2026-08-10 14:34:57 -05:00

116 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 (P1P6)**; 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 (P1P6)
| # | 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.