RMH Studios — Whole-Platform Improvement Plan¶
Document type: Cross-cutting engineering & product audit + roadmap
Prepared: 2026-06-30
Scope: The entire web application — public site (app/routes/_site/*), games, full-screen apps, shared UI primitives, server functions/API routes (app/routes/api/*, ~316 files), realtime hubs (server/*, go-services/*), build/deploy, and developer experience.
Stack of record: TanStack Start + Vite 8 + React 19 + Nitro SSR · PostgreSQL/Prisma 7 · Better Auth · Socket.io · Go services (Bazel) · Tailwind v4 · 32-locale i18n · ~2,250 TS/TSX files.
How to read this document. Findings are grouped by dimension. Each dimension opens with what is already strong (this is a mature, well-engineered codebase and the plan treats it as such), then a findings table with severity, a concrete
file:lineanchor, the current state, and a recommended change. A consolidated, phased roadmap is at the end, followed by KPIs, tooling, and a definition-of-done appendix.Confidence & method. Every finding is source-anchored. Where a claim drives a specific code change, it was spot-verified against the live tree on this branch. A handful of items surfaced from legacy audit documents were verified to be already remediated or not present in the current code; these are listed explicitly in §6 so they are not re-opened by mistake. Items that still need a runtime check (vs. source) are labeled
VERIFY.
Implementation status (branch claude/website-improvement-ideas-mq41th)¶
Shipped on this branch so far. Items that need a staging URL / seeded DB for visual/scroll QA, or a product/infra decision, are intentionally not shipped blind and are listed as deferred.
Phase |
Item |
Status |
|---|---|---|
0 |
Error tracking + route error boundaries + styled 404 |
✅ done |
0 |
Default OG image ( |
✅ done |
0 |
|
✅ done |
0 |
|
✅ done |
0 |
Security-header parity (Apache CSP Report-Only + Traefik middleware) |
✅ done |
0 |
Frontend CI gate (typecheck/lint/test/build) |
⏸️ skipped by request (no test gate for now) |
1 |
Client RUM (Core Web Vitals → |
✅ done |
1 |
JSON-LD structured data ( |
✅ done |
1 |
Canonical URLs on content routes |
✅ done |
1 |
|
⏸️ deferred — needs per-locale URLs (architectural); same-URL alternates would be invalid |
1 |
Security VERIFY sweep |
✅ done — Range bounds & Socket CORS confirmed already-fixed; PAT/AI quotas flagged for owner |
1 |
Form labels + Escape-to-close (first pass) |
✅ done (ComposeBox poll, ImageCropModal) |
1 |
Stripe payment tests |
⏸️ skipped by request (no tests) |
2 |
Reduced-motion global gate + |
✅ done |
2 |
High-Contrast theme (WCAG AAA) |
✅ done |
2 |
Skeletons + |
✅ done |
2 |
|
✅ done |
2 |
PWA manifest (PNG/maskable icons, shortcuts, categories) |
✅ done |
2 |
Feed list virtualization |
⏸️ deferred — needs scroll/visual QA on dynamic-height feeds |
2 |
Service worker + Web Push |
⏸️ deferred — a bad SW breaks prod caching; push needs VAPID keys + rollout |
2 |
Responsive-image rework |
⏸️ deferred — needs per-image above/below-fold judgment on the running app |
3 |
Dependabot |
✅ done |
3 |
Docs & DX (rewrite this overview, CONTRIBUTING, Prettier, PR template, |
✅ done |
3 |
CSP enforcement |
⏸️ deferred — promote from Report-Only after collecting real violation data |
3 |
Product features (referral, quote-repost, badges, creator coins, digests) |
⏸️ deferred — schema-changing features; need product sign-off + migration/QA |
3 |
Resilience (backoff/breaker/DB-retry) + migration/e2e tests |
⏸️ deferred — server behavior needs a runtime to validate; tests skipped by request |
Verification standard used: every code change was typechecked (tsc --noEmit
stays at the repo’s pre-existing baseline with zero new errors in changed files)
and linted, and generated assets were visually checked.
Table of contents¶
1. Executive summary¶
RMH Studios is an unusually ambitious single codebase: a social feed, ~20 browser games (several multiplayer, several 3D), a half-dozen full apps (RMHTube, RMHMusic, RMHType, RMHStudy, RMHCode), a blog/research system, a coin economy, a developer API, a Discord Activity build, and a Go microservice fleet behind it. The engineering bar is already high — manual vendor chunking, server-stub plugins for .server files, lazy-loaded locale bundles, intent-based route preloading, content-addressed immutable caching, a blue-green web hot-swap, a Bazel-built Go tier with e2e + Helm-lint CI, and an LLM “senior review” merge gate.
Because the foundations are strong, the highest-value work is no longer “build the basics” — it is closing the gaps between an excellent core and enterprise-grade consistency. Those gaps cluster in five places:
The frontend has no CI safety net. The only GitHub Actions workflows are Go-only (
go-microservices.yml) and an author-gated LLM review (senior-review.yml, gated to one user). A pure TypeScript/React PR can merge with no typecheck, no lint, no test run, and no build. This is the single biggest risk-to-effort item in the entire plan.Accessibility is good in primitives but inconsistent in composition — Radix-based dialogs and native form controls are solid, but the skip link is a no-op (no
#main-contenttarget, no<main>landmark), ~492onClickhandlers include non-keyboard div-buttons, many form fields lack programmatic labels, andprefers-reduced-motionis honored in only ~11 of the many Framer Motion/GSAP surfaces.SEO leaves structured discoverability on the table — SSR and clean URLs are excellent, but there is zero JSON-LD, no
hreflangdespite 32 locales, only ~49% of routes set unique titles, and the referenced default OG image (/images/og/default.png) 404s because it was never created.Observability is thin for the user-facing tier — no error tracking (Sentry-class), no client RUM/Web-Vitals reporting, and routes define no
errorComponent, so a render error in a route surfaces as a broken shell with no signal to ops.Security is solid where it was audited, but the deployment is mid-migration — Apache sets a thoughtful header set today, yet the Helm/Go-gateway path replicates none of it, and the CSP is
frame-ancestors-only (noscript-src/connect-src). Several legacy-audit “criticals” are in fact already fixed (see §6).
None of these are architectural rewrites. The plan below sequences them so that two weeks of work removes the most acute risk (a frontend CI gate + error tracking + the four “missing-asset/missing-tag” SEO fixes), and the remainder is steady, parallelizable refinement.
2. Platform scorecard¶
Dimension |
Grade |
One-line assessment |
|---|---|---|
Performance & Web Vitals |
A− |
Sophisticated build/splitting; gaps are list virtualization, hot-path memoization, and a few raw |
Accessibility |
C+ |
Great primitives, inconsistent composition; no a11y lint, skip-link no-op, motion prefs partial. |
Security & privacy |
B |
Strong auth/validation/upload checks; residual CSP depth, gateway header parity, IP-trust nuance. |
SEO & discoverability |
B− |
Excellent SSR/URLs/sitemap; missing JSON-LD, hreflang, broad unique titles, OG default asset. |
Reliability & observability |
C+ |
Excellent Go CI + blue-green deploy; no frontend CI, no error tracking, no RUM. |
UX, engagement & delight |
A− |
Deep gamification & social; refine onboarding, loading states, confetti coverage, PWA/offline. |
Internationalization |
B+ |
32 locales, RTL, SSR resolution; missing hreflang and a per-locale OG/QA story. |
Docs & DX |
B− |
Rich domain docs; the flagship |
Grades are relative to an enterprise bar, not to typical projects — most lines here would be an “A” against an average codebase.
3. Top 12 highest-leverage improvements¶
Ranked by (impact ÷ effort). Each links to its detailed finding.
# |
Improvement |
Dimension |
Effort |
Impact |
Why it’s top-ranked |
|---|---|---|---|---|---|
1 |
Add a frontend CI workflow (typecheck + lint + |
Reliability |
S |
★★★★★ |
Closes the largest hole: today FE code merges unchecked. → |
2 |
Integrate error tracking + route |
Observability |
S–M |
★★★★★ |
Production render/JS errors are currently invisible. → |
3 |
Create the missing |
SEO |
XS |
★★★★ |
A referenced asset 404s on every share without a custom card. → |
4 |
Add a real |
A11y |
XS |
★★★★ |
One-line fix that unbreaks keyboard/SR navigation site-wide. → |
5 |
Add |
A11y |
XS |
★★★★ |
Prevents the long tail from growing while you fix the backlog. → |
6 |
JSON-LD structured data (Article, Organization, Person, Book, Breadcrumb) |
SEO |
M |
★★★★ |
Unlocks rich results; pure additive |
7 |
Virtualize the long lists (feed threads, leaderboards, profile tabs) |
Performance |
M |
★★★★ |
Biggest runtime win at scale; DOM stays bounded. → |
8 |
Replicate security headers in the Helm ingress / Go gateway + deepen CSP |
Security |
S–M |
★★★★ |
The migration target currently ships no security headers. → |
9 |
|
i18n/SEO |
S |
★★★ |
You already do the hard SSR locale work; just signal it. → |
10 |
Client RUM: report Core Web Vitals + JS errors |
Observability |
S |
★★★ |
Turns “feels slow” into a measured, trendable number. → |
11 |
First-run onboarding checklist for new accounts (+ coin reward) |
UX |
M |
★★★ |
Converts the strong feature set into activated users. → |
12 |
Web Push + service worker (notifications + offline single-player games) |
UX/PWA |
M–L |
★★★ |
Real retention lever; |
Effort key: XS ≤ ½ day · S ≈ 1–2 days · M ≈ 3–5 days · L ≈ 1–2 weeks.
4. Dimension 1 — Performance & Core Web Vitals¶
Already strong¶
Build-level splitting is excellent.
vite.config.tsdefines deliberatemanualChunksfor three/monaco/tiptap/tone/pixi/recharts/motion, externalizes heavy client-only deps from the Nitro server bundle, and a customstubServerFiles()plugin prevents.servertrees (pg, Buffer) from leaking into the client (vite.config.ts:15-81,:148-201).Routing is tuned for perceived speed:
defaultPreload: "intent"with a 50ms delay and 30s preload stale-time (app/router.tsx).i18n is lazy — only
enships eagerly;zh/ar/etc. resources are serialized into the loader payload only when active (app/routes/__root.tsx:76-85).Caching is correct where it matters: content-addressed image/cover routes use
max-age=31536000, immutable; the image proxy usesstale-while-revalidate; sitemap is cache-revalidated.Console stripping (
esbuild.pure: ["console.log","console.debug"]) and deferred decorative fonts viarequestIdleCallback(__root.tsx:69) are already in place.
Findings¶
Sev |
Area |
Location |
Current state |
Recommendation |
|---|---|---|---|---|
High |
|
Long lists render every item; infinite-scroll accumulates items in state and keeps them all mounted. |
Introduce a virtualizer ( |
|
Med |
Hot-path memoization |
|
Only one |
|
Med |
Responsive user images |
|
|
Route remaining avatar/attachment |
Med |
Effect races on fast nav |
|
In-flight fetches aren’t aborted when |
Add |
Low |
List key stability |
|
|
Pass |
Low |
Font CLS |
|
Main Nunito/Inter link is render-blocking and Google’s default is |
Ensure every Google Fonts URL carries |
Info |
SSR payload trim |
|
Root loader serializes a moderate user object on every navigation. |
Optional: trim to |
Acceptance signal: a Lighthouse mobile run on the feed and a 500-item conversation stays in the green for CLS/INP; React Profiler shows bounded DOM node count while scrolling.
5. Dimension 2 — Accessibility (WCAG 2.2 AA)¶
Already strong¶
Native, accessible primitives:
components/ui/button.tsxuses real<button>withfocus-visible:ring; inputs/sliders use native elements with visible focus; dialogs are Radix (@radix-ui/react-dialog) and get focus trap/restore + Escape for free.407 ARIA attributes across 136 files,
role="status" aria-live="polite"on toasts/uploaders/spinner, decorative icons markedaria-hidden, RTLdirhandled at the document level (__root.tsx:60).A skip link exists (
app/routes/_site.tsx:28-33) — it just needs a target (below).
Findings¶
Sev |
Area |
Location |
Current state |
Recommendation |
|---|---|---|---|---|
High |
|
Skip link targets |
Wrap the content outlet in |
|
High |
|
No |
Add |
|
High |
Non-keyboard interactive divs |
~492 |
Several custom overlays/dropdowns are mouse-only (no |
Migrate custom modals to the shared Radix |
High |
Form label association |
|
Many fields rely on |
Add programmatic labels ( |
Med |
Motion preferences |
|
Framer Motion/GSAP UI animations (dialogs, reveals, page transitions) are not guarded. |
Export a shared |
Med |
Color contrast on muted text |
|
Secondary/helper/disabled text can drop below AA on some of the 20+ themes. |
Lift |
Med |
Image alt text on user content |
|
Avatars and uploaded images often have no |
Default avatar |
Low |
Toggle state + live game state |
like/follow buttons; minigame score/turn |
No |
|
Target: WCAG 2.2 AA. A pragmatic order is fix #1/#2 (landmark + lint) first, then forms and keyboard handlers, then contrast/motion, then a third-party audit before claiming conformance.
6. Dimension 3 — Security, Privacy & Abuse-resistance¶
⚠️ Accuracy note. Several items in the legacy reports under
docs/misc/were re-verified against the current tree and are already remediated or not present. They are listed first so they are not re-opened. The genuinely-open items follow.
✅ Verified already-fixed / not-applicable (do NOT re-open)¶
Webhook default secret — legacy reports flag
WEBHOOK_SECRET || 'change-me'as live. The currentwebhook-server.cjs:4-6already fails closed:const SECRET = process.env.WEBHOOK_SECRET; if (!SECRET || SECRET === 'change-me') { …FATAL… }. ✔ Fixed.SharedNoteViewXSS (href/src) — the file does not exist in the tree (components/rmh-notes/SharedNoteView.tsxnot found). ✔ N/A.formatProblemDescriptionXSS inOAEditor— no such function / nodangerouslySetInnerHTMLincomponents/rmh-jobs/OAEditor.tsx. ✔ N/A.XSS surface generally — only 2
dangerouslySetInnerHTMLsites exist site-wide, both controlled: the inline theme bootstrap (app/routes/__root.tsx) and Wikipedia content that is server-sanitized before render (components/rmhbox/minigames/wiki-race/WikiFrame.tsx+lib/rmhbox/wiki-race/wikipedia-proxy.ts). User prose renders viareact-markdown(escapes HTML by default). ✔ Low residual risk.Prior audit F1–F10 (comment/score rate limits, username sanitization, body-size caps, cover magic-byte checks) — present in the relevant
app/routes/api/**handlers. ✔ Fixed.Auth/session hardening — Better Auth with per-endpoint sign-in/sign-up/reset rate limits (
lib/auth.ts),httpOnly/secure/sameSitecookies, Prisma-parameterized queries throughout. ✔ Solid.
Genuinely open findings¶
Sev |
Area |
Location |
Current state |
Recommendation |
|---|---|---|---|---|
High |
|
Apache sets a thoughtful set (HSTS, |
Add the same headers at the ingress (nginx |
|
High |
CSP depth |
|
CSP is |
Roll out a real CSP in Report-Only first (collect violations), then enforce. Pair with a nonce/hash strategy for the small set of inline bootstrap scripts in |
Med |
Client-IP trust for rate limiting |
|
|
Document/confirm the proxy contract (Cloudflare → Apache/ingress). For authenticated endpoints, also key the limiter on |
Med |
AI-endpoint cost abuse |
routes using |
Generative endpoints are the highest-$$ abuse target. Per-IP limits exist broadly; per-user/льtier quotas for AI specifically need confirmation. |
Add per-user and per-tier quota + concurrency caps on AI routes; log spend per user; alert on anomalies. |
Med |
Range/stream input validation |
audio/asset stream routes (e.g. |
Legacy report flags unbounded |
Validate |
Med |
Socket.io CORS default |
|
An empty default may not fail closed depending on Socket.io semantics. |
Make startup throw if |
Med |
Secret-at-rest for 3rd-party tokens |
GitHub PAT flow ( |
The encryption key is declared, but it’s unconfirmed that stored PATs are actually AES-GCM encrypted. |
Confirm encrypt-on-write/decrypt-on-read is wired; if not, implement it. |
Med |
No security scanning in CI |
|
No dependency audit, secret scanning, SAST, or container scan runs on PRs. |
Add |
Low |
|
|
Disallows |
Add explicit |
Suggested first PR (½ day): ingress header parity + CSP in Report-Only. Then the VERIFY items as a single hardening sweep.
7. Dimension 4 — SEO & Discoverability¶
Already strong¶
Full SSR — crawlers receive complete HTML; per-route
head()+ server loaders fetch data before paint.Dynamic OG images via
satori+@resvg/resvg-jsfor social posts (app/routes/api/og/post/$id.ts,lib/og/post-image.server.tsx), privacy-aware and cached.Reusable SEO helpers
buildMeta()/buildCanonical()(lib/seo.ts).Dynamic sitemap covering static routes + up to ~7k blog/news/build URLs with
lastmod/priorityand graceful DB-failure fallback (app/routes/sitemap[.]xml.ts).Clean, semantic URLs and a correct
viewport/manifest baseline.
Findings¶
Sev |
Area |
Location |
Current state |
Recommendation |
|---|---|---|---|---|
High |
entire app (0 |
No schema.org markup anywhere → no rich results, no Organization knowledge-panel seed. |
Add a |
|
High |
referenced by |
Any route using the default card (or any non-custom share) points at a missing |
Create |
|
Med |
Unique titles/descriptions coverage |
~49% of routes set |
Game/app roots ( |
Add |
Med |
|
Crawlers can’t associate language variants; same URL serves different languages by cookie (duplicate-content risk). |
Emit |
|
Med |
Canonicals underused |
|
Dynamic content (blog/news/profiles/builds) sets no canonical; two profile routes ( |
Emit a canonical from every public route’s |
Low |
OG coverage for games/apps |
85+ routes set no OG tags |
Game/app pages don’t unfurl with a preview card. |
Use |
Low |
PWA manifest depth |
|
Only an SVG icon; no |
Add 192/512 PNG icons, |
8. Dimension 5 — Reliability, Observability & Quality Engineering¶
Already strong¶
Go tier is well-engineered for CI/CD: Bazel build+test with opt-in remote cache, a Postgres-backed e2e job, and Helm lint/template validation (
.github/workflows/go-microservices.yml).Deployment safety: blue-green web hot-swap with health-gated flip and rollback (
deploy/hotswap-web.sh), Helm--atomic --wait, SIGTERM graceful shutdown across services, liveness/readiness probes, a standalone status dashboard that survives outages.Structured JSON logging in services (
server/shared/logger.ts); connection pooling with timeouts (server/shared/prisma-client.ts);strict: trueTypeScript.
Findings¶
Sev |
Area |
Location |
Current state |
Recommendation |
|---|---|---|---|---|
High |
|
The only workflows are Go-path-scoped and an author-gated LLM review ( |
Add |
|
High |
|
A render/JS error surfaces as a broken shell with no signal to ops. |
Integrate a Sentry-class SDK (client + Nitro server); add a root error boundary in |
|
High |
Payments untested |
|
Subscription/checkout/webhook paths have zero automated coverage — highest blast-radius if it breaks. |
Add a Stripe test suite (mocked webhooks: checkout completed, subscription updated/canceled, payment failed) + an idempotency test. |
Med |
no analytics/RUM ( |
“Feels slow” can’t be measured or trended; no real-user INP/LCP/CLS. |
Add a lightweight, consent-aware RUM (Web-Vitals → a |
|
Med |
Component/e2e test coverage |
161 test files (~20% breadth); 0 React component tests; Playwright present but used only for app features, not QA |
Critical UI (auth, compose, multiplayer join) is unverified end-to-end. |
Add |
Med |
Lint is advisory-only; 285 |
|
Warnings never block; formatting drifts across 750+ components; 285 |
Once CI exists, fail on a non-regressing error set; add Prettier; ratchet |
Med |
Resilience patterns |
WebSocket reconnect uses fixed delay (e.g. |
Recovery can thunder-herd; a single DB blip fails queries; cascading failures aren’t isolated. |
Exponential backoff + jitter on socket reconnect; bounded DB connect-retry; a simple breaker around cross-service calls. |
Med |
Migration & deploy guards |
|
Breaking schema changes can ship; |
Run migrations against a throwaway DB in CI; add a post-deploy smoke check (probe |
Low |
Dependency automation |
no |
Stale/vulnerable deps aren’t surfaced. |
Add Renovate/Dependabot + |
The CI workflow is the keystone — items #2 (tests), the lint ratchet, and security scanning all plug into it once it exists. A minimal first version:
# .github/workflows/web-ci.yml (illustrative)
name: web-ci
on:
pull_request:
paths-ignore: ["go-services/**", "deploy/helm/**", "docs/**"]
concurrency: { group: "web-ci-${{ github.ref }}", cancel-in-progress: true }
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: pnpm }
- run: pnpm install --frozen-lockfile
- run: pnpm exec tsc --noEmit
- run: pnpm lint
- run: pnpm exec vitest run
- run: pnpm run build:frontend
9. Dimension 6 — UX, Engagement & Delight¶
Already strong¶
Deep, mature engagement system already exists: achievements (
/achievements, bronze/silver/gold + secret tiers), daily check-in streaks with milestones, ELO leaderboards + challenges (/ranked), weekly recap + wrapped, multi-type notifications, a coins economy (Plinko, shop), daily puzzles (/daily/*), predictions, quotes.Excellent UI primitives:
EmptyState,Skeleton,Spinner, RadixDialog, and a 29-theme system (vibes/zodiac/seasons/school) with FOUC-free hydration.Real onboarding seeds exist:
WelcomeModal(4-step, localStorage-gated) and an app-specificOnboardingTourfor RMHTube.Delight touches:
canvas-confettion RMHBox wins + Plinko, Framer/GSAP motion, a/secretvault of hidden games,useCardSheen.
Findings & opportunities¶
Pri |
Theme |
Evidence |
Opportunity |
|---|---|---|---|
High |
new accounts land in a bare feed; |
Add a |
|
High |
Celebration coverage |
confetti only in RMHBox + Plinko |
Centralize a |
High |
Loading consistency |
feed/recommendations/library show spinners or bare states, not layout-matched skeletons |
Add |
High |
no service worker; |
Add |
|
Med |
Virality loop |
deeplinks/embeds exist; no referral or quote-repost |
Add a referral link flow ( |
Med |
Public achievement showcase |
achievements aren’t surfaced on profiles |
Render an unlocked-badge grid on profiles with a “pin top 3” showcase. Makes status visible → drives completion. |
Med |
Creator-side coin loop |
coins skew toward game-play + streaks |
Add creator earn events (per-N likes, per-N followers, monthly active-creator bonus) so non-gamers have an economy too. |
Med |
Notification refinement |
no grouping, no preferences, no push |
Group (“5 people liked your post”), add a preferences panel, wire push (above), optional daily email digest. |
Low |
Discovery polish |
strong |
Add “Trending this week” to the feed, a trending-communities widget in the sidebar, recent-search history, and hover profile cards (Radix Popover: avatar/bio/follow). |
Low |
Theme-aware fun |
29 themes are cosmetic |
Optional flourishes per theme (gamer CRT scanlines, anime petals, zodiac constellation banners) — gated by |
10. Dimension 7 — Internationalization¶
Already strong¶
32 locales with a tiered (eager
en, on-demand others) loading model, RTL support forar/ur/faset at the document level before paint, SSR locale resolution from cookie →Accept-Language, and an i18next extraction/translation pipeline (i18n:extract,i18n:translate).
Findings¶
Sev |
Area |
Location |
Recommendation |
|---|---|---|---|
Med |
|
|
Emit per-locale |
Low |
Per-locale OG/social |
|
Ensure dynamic OG cards render non-Latin scripts (CJK/Arabic) correctly — confirm the font stack in |
Low |
Translation QA surface |
|
Add a CI check that fails on missing keys per locale (or reports coverage %), so untranslated strings are visible rather than silently English. |
Low |
RTL visual QA |
components with directional layout |
Add a small RTL visual smoke (Playwright screenshot in |
11. Dimension 8 — Documentation & Developer Experience¶
Findings¶
Sev |
Area |
Location |
Current state |
Recommendation |
|---|---|---|---|---|
Med |
Stale flagship doc |
|
Still describes the app as “Next.js 16 App Router” with old port/route facts — but the stack is TanStack Start + Nitro. New contributors will be misled. |
Rewrite to reflect TanStack Start/Vite/Nitro, the Go fleet, and current routes; or regenerate it. |
Low |
No |
repo root, |
No contribution guide or PR template to encode the (high) implicit quality bar. |
Add |
Low |
No formatter config |
repo root |
Formatting relies on convention. |
Add Prettier + an |
Low |
|
|
One-off scripts skip |
Add a |
12. Consolidated roadmap¶
Phased so the riskiest gaps close first and later work parallelizes cleanly. Sizes use the §3 key.
Phase 0 — “Stop the bleeding” (Week 1–2)¶
Removes the most acute risk for the least effort.
web-ci.yml: typecheck + lint +vitest run+ build, required on PRs. (S) — §8#ci-gapError tracking + root error boundary + top-route
errorComponents. (S–M) — §8#obs-errorsCreate
/images/og/default.png. (XS) — §7#seo-og<main id="main-content">landmark. (XS) — §5#a11y-landmarkseslint-plugin-jsx-a11y(warn). (XS) — §5#a11y-toolingIngress/gateway security-header parity + CSP Report-Only. (S) — §6
#sec-headers
Phase 1 — Visibility & correctness (Week 3–6)¶
Client RUM (Core Web Vitals + JS errors). (S) — §8
#obs-rumJSON-LD structured data across content types. (M) — §7
#seo-jsonldhreflang+ canonicals on public routes. (S) — §7/§10Stripe payment test suite. (M) — §8
Security
VERIFYsweep (XFF/IP keying, Range bounds, Socket.io CORS fail-closed, PAT-at-rest, AI quotas). (M) — §6Form labels + keyboard handlers for the top ~10 a11y offenders. (M) — §5
Phase 2 — Performance & engagement (Week 7–12)¶
List virtualization (threads/leaderboards/profile feeds). (M) — §4
#perf-virtualHot-path
memo()+AbortControllerfetch races. (M) — §4First-run onboarding checklist + celebration hook + skeletons. (M) — §9
Web Push + service worker (notifications + offline single-player). (M–L) — §9
#ux-pwaReduced-motion global gate + per-theme contrast fixes + High-Contrast theme. (M) — §5
Responsive
<img>cleanup throughOptimizedImage. (S) — §4
Phase 3 — Depth & polish (Quarter+)¶
CSP enforce (after Report-Only data), security scanning in CI (Dependabot/Renovate/Trivy/secret scan). — §6/§8
Component + Playwright e2e coverage for critical funnels; resilience patterns (backoff/breaker/DB retry); migration test + post-deploy smoke. — §8
Referral loop, quote-repost, public badges, creator coins, notification grouping/digests. — §9
Rewrite
codebase-overview.md; addCONTRIBUTING.md+ Prettier + PR template; i18n key-coverage check + RTL visual smoke. — §8/§10
Appendix A — KPIs & instrumentation¶
Track these before/after so the work is provably moving numbers, not just shipping.
Goal |
Metric |
Source once instrumented |
|---|---|---|
Faster real-user pages |
p75 LCP / INP / CLS on feed, profile, a game shell |
Client RUM (Phase 1) |
Fewer silent failures |
client error rate; % sessions with a JS error |
Error tracking (Phase 0) |
Safer merges |
% PRs passing typecheck/lint/test; mean time to red→green |
|
Better activation |
D1/D7 retention; % new users completing onboarding checklist |
Product analytics + onboarding events |
Discoverability |
indexed pages; rich-result eligibility; impressions/CTR |
Google Search Console |
Accessibility |
axe violations/route; keyboard-only task completion |
|
Engagement |
DAU/MAU; posts/session; games started/session; streak retention |
Product analytics |
Reliability |
error budget burn; deploy rollback rate; WS reconnect success |
Status dashboard + RUM |
Appendix B — Recommended tooling¶
CI: GitHub Actions (
web-ci.yml); Dependabot or Renovate; Trivy (image scan); GitHub secret scanning.Quality:
eslint-plugin-jsx-a11y, Prettier,jest-axe/@axe-core/playwright,@testing-library/react, Playwright (QA, not just features).Observability: a Sentry-class SDK (client + Nitro),
web-vitals→/api/rum(or Plausible/PostHog, consent-aware).Performance:
@tanstack/react-virtual; Lighthouse CI (budget gate, optional).SEO: a
lib/schema.tsJSON-LD builder; Search Console; a generated/images/og/default.png.
Appendix C — Definition of done / PR checklist¶
A reusable checklist to drop into .github/pull_request_template.md:
tsc --noEmit,pnpm lint, andpnpm vitest runpass locally (and in CI).New interactive UI is keyboard-operable, labeled, and respects
prefers-reduced-motion.New public route sets a unique
title/description, a canonical, and (if content) JSON-LD.User-facing errors are handled (route
errorComponent/boundary) and reported to error tracking.New server/API input is validated (zod) and rate-limited where it writes or costs money.
User-facing strings go through i18n (
t(...)), not hardcoded English.Data-heavy views have a layout-matched skeleton/empty state.
Security headers / CSP unaffected (or intentionally updated in both serving paths).
Prepared as a living document. Each finding is independently shippable; the roadmap is a suggested sequence, not a dependency chain (except that the Phase-0 CI gate makes every later change safer to land).