Phase 16: Frontend Scaffold - 5 plans in 3 waves - 4 parallel-capable, 1 sequential with checkpoint - Ready for execution
10 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous
| phase | plan | type | wave | depends_on | files_modified | autonomous | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 16-frontend-scaffold | 04 | execute | 3 |
|
|
true |
Purpose: Establish the navigation structure and persistent layout that all subsequent screens plug into. Phase 17-19 will implement the actual page content; this plan creates the skeleton they slot into. TanStack Router provides fully type-safe routing with validated params and search params. Output: Working router with 3 page stubs (Dashboard, Initiative Detail, Inbox) and persistent nav header.
<execution_context>
@/.claude/get-shit-done/workflows/execute-plan.md
@/.claude/get-shit-done/templates/summary.md
</execution_context>
Wireframe navigation structure
@docs/wireframes/initiative-dashboard.md
tRPC client from Plan 03
@packages/web/src/lib/trpc.ts @packages/web/src/main.tsx
Task 1: Install TanStack Router and add shadcn/ui base components packages/web/package.json, packages/web/src/components/ui/button.tsx, packages/web/src/components/ui/badge.tsx, packages/web/src/components/ui/card.tsx, packages/web/src/components/ui/dropdown-menu.tsx 1. Install in packages/web: - Dependencies: `@tanstack/react-router` - Dev dependencies: `@tanstack/router-plugin` (Vite plugin for route generation), `@radix-ui/react-dropdown-menu`, `@radix-ui/react-slot` (shadcn/ui primitives)-
Update
packages/web/vite.config.tsto add the TanStack Router Vite plugin:import { TanStackRouterVite } from '@tanstack/router-plugin/vite' export default defineConfig({ plugins: [ TanStackRouterVite(), react(), ], // ... existing proxy config })The TanStack Router plugin must come BEFORE the react plugin. It auto-generates the route tree from the file-based routes in
src/routes/. -
Add the 4 most-needed shadcn/ui components. Use
npx shadcn@latest add button badge card dropdown-menufrom packages/web directory. If the CLI doesn't work with this directory structure, manually create the component files following the shadcn/ui source (available at ui.shadcn.com).The components go in
packages/web/src/components/ui/:button.tsx— needed by every page (View, Spawn, Queue, etc.)badge.tsx— needed for StatusBadge componentcard.tsx— needed for InitiativeCarddropdown-menu.tsx— needed for ActionMenu and SpawnArchitectDropdown
These are the foundation components. Additional shadcn components will be added in later phases as needed.
-
Run
npm installfrom root. cd packages/web && npx vite build succeeds. Component files exist in packages/web/src/components/ui/. TanStack Router and shadcn/ui base components installed.
TanStack Router uses file-based routing. The Vite plugin generates routeTree.gen.ts automatically from the file structure:
__root.tsx— Root layout route (wraps all pages with AppLayout)index.tsx—/route, redirects to/initiativesinitiatives/index.tsx—/initiativesDashboardPageinitiatives/$id.tsx—/initiatives/$idInitiativeDetailPage (type-safe param)inbox.tsx—/inboxInboxPage
-
Create
packages/web/src/routes/__root.tsx: The root route wraps all pages with the AppLayout shell:import { createRootRoute, Outlet } from '@tanstack/react-router' import { AppLayout } from '../layouts/AppLayout' export const Route = createRootRoute({ component: () => ( <AppLayout> <Outlet /> </AppLayout> ), notFoundComponent: () => ( <div className="p-8 text-center"> <h1 className="text-2xl font-bold">Page not found</h1> <p className="text-muted-foreground mt-2">The page you're looking for doesn't exist.</p> </div> ), }) -
Create
packages/web/src/routes/index.tsx: Redirect root/to/initiatives:import { createFileRoute, redirect } from '@tanstack/react-router' export const Route = createFileRoute('/')({ beforeLoad: () => { throw redirect({ to: '/initiatives' }) }, }) -
Create
packages/web/src/routes/initiatives/index.tsx: Dashboard stub:import { createFileRoute } from '@tanstack/react-router' import { trpc } from '../../lib/trpc' export const Route = createFileRoute('/initiatives/')({ component: DashboardPage, }) function DashboardPage() { const health = trpc.health.useQuery() return ( <div> <h1 className="text-2xl font-bold">Initiative Dashboard</h1> <p className="text-muted-foreground mt-2"> Server: {health.data?.status ?? 'connecting...'} </p> <p className="text-muted-foreground mt-1">Content coming in Phase 17</p> </div> ) } -
Create
packages/web/src/routes/initiatives/$id.tsx: Detail stub with type-safe params:import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/initiatives/$id')({ component: InitiativeDetailPage, }) function InitiativeDetailPage() { const { id } = Route.useParams() return ( <div> <h1 className="text-2xl font-bold">Initiative Detail</h1> <p className="text-muted-foreground mt-2">Initiative ID: {id}</p> <p className="text-muted-foreground mt-1">Content coming in Phase 18</p> </div> ) } -
Create
packages/web/src/routes/inbox.tsx: Inbox stub:import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/inbox')({ component: InboxPage, }) function InboxPage() { return ( <div> <h1 className="text-2xl font-bold">Agent Inbox</h1> <p className="text-muted-foreground mt-1">Content coming in Phase 19</p> </div> ) } -
Create
packages/web/src/layouts/AppLayout.tsx: The persistent navigation shell from the wireframe header:CODEWALK DISTRICT [+ New Initiative] Initiatives Agents Tasks SettingsImplementation:
- Header with app title "Codewalk District" on the left
- "New Initiative" button on the right (placeholder handler for now)
- Navigation tabs: Initiatives (links to /initiatives), Inbox (links to /inbox)
- Skip "Agents", "Tasks", "Settings" tabs for now — not in Phase 16-19 scope. Include them as disabled/greyed out.
- Use
Linkfrom@tanstack/react-routerfor navigation links - Active nav item highlighted using
activePropsoractiveOptionson Link - Children rendered via props.children (passed from root route Outlet)
- Minimal Tailwind styling: fixed header, content below with padding
-
Create
packages/web/src/router.tsx:import { createRouter } from '@tanstack/react-router' import { routeTree } from './routeTree.gen' export const router = createRouter({ routeTree }) declare module '@tanstack/react-router' { interface Register { router: typeof router } } -
Update
packages/web/src/App.tsx: Replace the temporary health-check component with the router:import { RouterProvider } from '@tanstack/react-router' import { router } from './router' function App() { return <RouterProvider router={router} /> } export default App -
Run
cd packages/web && npx vite build— the TanStack Router plugin will generaterouteTree.gen.tsduring the build. The generated file should be committed to git (it's a type-safe route manifest).
Keep it simple. These are stubs — the real UI comes in Phases 17-19. The goal is a working navigation skeleton with the right route structure. cd packages/web && npx vite build succeeds. routeTree.gen.ts generated with routes: /, /initiatives, /initiatives/$id, /inbox. AppLayout renders header and child content. TanStack Router configured with file-based routes. App shell layout renders nav header. Page stubs in place for Phases 17-19 to fill in.
Before declaring plan complete: - [ ] `cd packages/web && npx vite build` succeeds - [ ] Route `/initiatives` renders DashboardPage stub - [ ] Route `/initiatives/some-id` renders InitiativeDetailPage stub with type-safe param - [ ] Route `/inbox` renders InboxPage stub - [ ] AppLayout nav header present on all pages - [ ] Active nav item highlighted - [ ] routeTree.gen.ts generated<success_criteria>
- TanStack Router configured with file-based routes
- Type-safe params on /initiatives/$id route
- AppLayout shell matches wireframe header structure
- Page stubs ready for Phase 17-19 implementation
- Navigation between pages works
- Build passes cleanly </success_criteria>