feat(m18): bootstrap apps/web on Vite + React 19 + TanStack + Base UI + Tailwind 4 #172
No reviewers
Labels
No labels
area:agents
area:dashboard
area:database
area:design
area:design-review
area:flows
area:infra
area:meta
area:security
area:sessions
area:webhook
area:workdir
security
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/claude-hooks!172
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "boss/163"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Stands up the t3code stack in
apps/webso M18-3+ have a real frontend tobuild against. Closes #163.
Stack
@tailwindcss/vite@base-ui-components/react)src/routes/) + TanStack QueryPath aliases:
@/*→src/*,@shared/*→../../packages/shared/src/*—wired in both
tsconfig.jsonandvite.config.ts/vitest.config.ts.Design tokens
Tokyo Night Storm palette mirrored from
design/tokens.json::theme-darkinto
src/styles/tokens.css, then mapped onto Tailwind@themenames(
bg-bg,text-accent,border-border, …) so utility classes resolvethrough the same CSS variables — no raw hex in downstream components.
Dev loop
cd apps/web && bun run dev→ Vite on127.0.0.1:5173with a proxyto
http://localhost:4500for/task,/queue,/history,/events,/stats,/agents,/usage,/storage,/breakdown.just devfans out server + web in parallel via Turbo.apps/webemits todist/—bun run buildworks.Production serving
apps/servergrowshandleWebAppwhich servesapps/web/dist/at/app/*(e.g./app/monitor,/app/planner) with:index.html..segments rejected with 403)/assets/(Vite fingerprintseverything there); short
max-age=300on unhashed filesbun run buildhint when the bundle is missing— silent-404ing would be confusing
Legacy
/still servesdashboard.htmluntil #M18-9.Linter choice
Biome for the web app, same as the server — the one-linter
workspace rule stays intact (no Oxlint). Documented in
apps/web/README.md.Tests
happy-dom. One smokespec per route (
routes/index.test.tsx) asserting it renders.in #M18-3).
just testswitched from a barebun test(which would try toexecute web
.test.tsxfiles under Bun's native runner and fail)to
bun x turbo run test, so each package'stestscript picksthe right runner.
Out of scope
Real UI beyond the styled "Hello" landing, multi-page routing, and
auth — all belong to later M18 stories.
Test plan
bun run qapasses (typecheck + lint + format + tests across the monorepo)bun run buildinsideapps/webproducesdist/cleanlymain.test.tsasserts/app/*returns 503 with a build-hint whendist/is absent and 200 HTML when built; path-traversal is rejected🤖 Generated with Claude Code
Review — M18-2 web bootstrap
CI: green ✅ (run #1766, 5m8s)
Round: 1 (first review)
All acceptance criteria from issue #163 are met. No blocking issues found.
Acceptance criteria — ✅ all green
@/*+@shared/*in bothtsconfig.jsonandvite.config.tsdesign/tokens.jsonintokens.css, mapped to@themeinindex.css— no raw hex downstreambun run dev→ Vite on 5173 with proxy to 4500 for all 9 API pathsjust devfans out both apps via Turbobun run build→dist/apps/servergains/app/*static-file route with SPA fallback, immutable caching on/assets/,max-age=300on unhashed files, 503+hint whendist/absentjust testswitched tobun x turbo run test(avoids Bun picking up.test.tsxunder its native runner)Code correctness
Path traversal guard (
handleWebApp): correct.join(WEB_DIST_DIR, normalised)resolves all..segments before thestartsWithcheck, so any payload that escapesdist/fails the prefix check and gets a 403. Thecandidate === WEB_DIST_DIRcarve-out cleanly handles/app(no trailing slash) without a 403.Cache-Control strategy:
no-cacheon the SPAindex.htmlshell (correct — content changes on every deploy),immutableon Vite-fingerprinted/assets/files (correct — content-hashed names),max-age=300on everything else (favicon, manifest). Solid.503 hint: the operator-facing message includes both
apps/webandbun run build, which is exactly what the PR description promised and what the test asserts.Test isolation in
index.test.tsx: each test spins up its own in-memory router +QueryClientrather than importing the module-scoped singletons frommain.tsx. This is the right pattern — no shared state bleed between tests._agentTypeProbeprobe inindex.tsx: intentional and documented.void _agentTypeProbesuppresses the lint warning cleanly. If the@claude-hooks/sharedworkspace alias ever breaks, typecheck fails before runtime does. Fine.Minor observations (non-blocking)
@base-ui-components/react@^1.0.0-beta.5is pre-release, but that's expected for M18-2 scope and is what the t3code stack ships with at this stage.routeTree.gen.tscommitted — standard TanStack Router pattern; needed for TS type-safety without requiring the dev server to be running.apps/web/package.json'sqascript (typecheck && test, nolint) is slightly inconsistent with the rootjust qa(which covers lint workspace-wide via Biome). Not a gap in coverage, just a cosmetic inconsistency; acceptable to address in a later cleanup.