Skip to content

[CI] (cef44b4) react-router/react-router-v7-project#1405

Closed
wizard-ci-bot[bot] wants to merge 1 commit intomainfrom
wizard-ci-cef44b4-react-router-react-router-v7-project
Closed

[CI] (cef44b4) react-router/react-router-v7-project#1405
wizard-ci-bot[bot] wants to merge 1 commit intomainfrom
wizard-ci-cef44b4-react-router-react-router-v7-project

Conversation

@wizard-ci-bot
Copy link
Copy Markdown

@wizard-ci-bot wizard-ci-bot Bot commented May 1, 2026

Automated wizard CI run

Source: context-mill-pr
Trigger ID: cef44b4
App: react-router/react-router-v7-project
App directory: apps/react-router/react-router-v7-project
Workbench branch: wizard-ci-cef44b4-react-router-react-router-v7-project
Wizard branch: main
Context Mill branch: basic-skills-v2
PostHog (MCP) branch: master
Timestamp: 2026-05-01T21:08:20.538Z
Duration: 370.2s

@wizard-ci-bot
Copy link
Copy Markdown
Author

wizard-ci-bot Bot commented May 1, 2026


PR Evaluation Report

Summary

This PR integrates PostHog into a React Router v7 framework-mode app ("RESTExplorer"), adding client-side SDK initialization via PostHogProvider, server-side middleware with posthog-node, user identification on login/signup, error boundary capture, a reverse proxy config, and event tracking across multiple user flows.

Files changed Lines added Lines removed
13 +216 -12

Confidence score: 4/5 👍

  • Reverse proxy configured but not wired up: The Vite dev proxy routes /ingest/* correctly, but posthog.init() sets api_host to the raw env var (https://us.i.posthog.com) instead of /ingest, so the proxy is never used by the client. [CRITICAL]
  • PII in capture event properties: user_signed_up includes email in posthog.capture() properties. Email is PII and should only be in identify() person properties, not in event properties. [MEDIUM]
  • leaderboard_viewed fires on every render: The capture call in stats.tsx is placed directly in the component render body, not in a useEffect or event handler. This will fire duplicate events on every re-render. [MEDIUM]

File changes

Filename Score Description
app/entry.client.tsx 4/5 Initializes posthog-js with PostHogProvider, env vars, tracing headers. api_host should point to /ingest for reverse proxy.
app/lib/posthog-middleware.ts 5/5 New server-side middleware with posthog-node, session/distinct ID extraction, context-based capture, and shutdown. Follows docs exactly.
app/root.tsx 5/5 Registers middleware and adds captureException in ErrorBoundary using usePostHog hook.
app/routes/countries.tsx 5/5 Rich event tracking in event handlers for claim, like, visit, filter, and achievement unlock.
app/routes/home.tsx 5/5 CTA click capture in event handler. Minor whitespace cleanup.
app/routes/login.tsx 4/5 identify + capture on login. Uses username as distinct_id (acceptable for fake app).
app/routes/signup.tsx 3/5 identify + capture on signup. Email leaked into capture event properties (PII).
app/routes/profile.tsx 5/5 Logout capture + posthog.reset() — correct pattern.
app/routes/stats.tsx 2/5 Capture call in component render body fires on every re-render. Should use useEffect.
package.json 5/5 posthog-js, @posthog/react, posthog-node all added correctly.
vite.config.ts 4/5 Reverse proxy and SSR noExternal configured correctly, but proxy unused since api_host doesn't point to it.
react-router.config.ts 5/5 Enables v8_middleware future flag needed for middleware support.
posthog-setup-report.md 5/5 Comprehensive wizard report documenting all changes and events.

App sanity check ⚠️

Criteria Result Description
App builds and runs Yes No syntax errors; dependencies added correctly; SSR config valid
Preserves existing env vars & configs Yes Existing configs extended, not replaced
No syntax or type errors Yes All TypeScript/JSX is valid
Correct imports/exports Yes All imports resolve to correct packages
Minimal, focused changes Yes All changes relate to PostHog integration
Pre-existing issues None

Issues

  • leaderboard_viewed capture in render body: posthog?.capture('leaderboard_viewed', ...) in stats.tsx is called directly during render, not inside a useEffect or event handler. This violates React best practices and will send duplicate events on every re-render. Move to a useEffect with appropriate dependencies. [MEDIUM]
  • No .env.example committed: The .env file exists locally but is not committed. New developers won't know which environment variables are required. The setup report mentions them, but a .env.example is the standard approach. [LOW]

Other completed criteria

  • All changes are relevant to PostHog integration
  • Correct files modified for React Router v7 framework mode (entry.client.tsx, root.tsx, middleware, vite.config.ts)
  • Code follows existing codebase patterns (naming, structure, indentation)
  • Build configuration is valid (package.json scripts unmodified, vite config extended properly)

PostHog implementation ⚠️

Criteria Result Description
PostHog SDKs installed Yes posthog-js@^1.372.6, @posthog/react@^1.9.0, posthog-node@^5.32.1 in package.json
PostHog client initialized Yes posthog.init() in entry.client.tsx with env vars, defaults, and tracing headers; PostHogProvider wraps app; server middleware creates posthog-node client per request
capture() Yes 10+ meaningful capture calls across routes
identify() Yes Called on login (username) and signup (username + email as person props); reset() called on logout
Error tracking Yes posthog.captureException(error) in ErrorBoundary in root.tsx
Reverse proxy No Vite dev server proxy configured for /ingest/* with correct /static and /array asset routes, but api_host in posthog.init() points to import.meta.env.VITE_PUBLIC_POSTHOG_HOST (raw PostHog URL), not /ingest. The proxy is never hit.

Issues

  • Reverse proxy not wired to client: The Vite proxy at /ingest/* is correctly set up with /ingest/static and /ingest/array routing to us-assets.i.posthog.com, but posthog.init() uses api_host: import.meta.env.VITE_PUBLIC_POSTHOG_HOST which resolves to https://us.i.posthog.com. The api_host should be set to /ingest (or the proxy path) for the reverse proxy to be used. Additionally, this Vite proxy only works in dev mode — a production reverse proxy (e.g., via server rewrites) would be needed for production. [CRITICAL]
  • Username as distinct_id: posthog.identify(username, ...) uses a username string as the distinct ID. For a production app, a stable internal user ID is preferred. Acceptable for this fake/demo app. [LOW]

Other completed criteria

  • API key loaded from VITE_PUBLIC_POSTHOG_PROJECT_TOKEN environment variable (not hardcoded)
  • Host correctly configured from environment variable
  • Server-side middleware correctly extracts session/distinct IDs from tracing headers
  • posthog.reset() called on logout to unlink user
  • v8_middleware future flag enabled in react-router.config.ts for middleware support
  • ssr.noExternal configured for posthog-js and @posthog/react to avoid SSR bundling issues

PostHog insights and events ⚠️

Filename PostHog events Description
app/routes/signup.tsx user_signed_up Fires on successful signup with username and email properties; also calls identify
app/routes/login.tsx user_logged_in Fires on successful login with username; also calls identify
app/routes/profile.tsx user_logged_out Fires on logout button click; followed by posthog.reset()
app/routes/home.tsx explore_now_clicked CTA click on homepage — top of conversion funnel
app/routes/countries.tsx country_claimed, country_liked, country_visited, country_region_filtered, achievement_unlocked Rich interaction tracking with country/region properties and achievement detection
app/routes/stats.tsx leaderboard_viewed Page view with user_rank, total_points, claimed_countries — but fires on every render
app/root.tsx captureException Error boundary captures exceptions automatically

Issues

  • PII in user_signed_up event properties: posthog.capture('user_signed_up', { username: newUser.username, email: newUser.email }) includes email directly in event properties. Email is PII and should only appear in person properties (via identify()). The identify() call on the line above already sets email correctly. Remove email from the capture call properties. [MEDIUM]
  • leaderboard_viewed fires on every re-render: The capture call is in the render body of Stats component, not guarded by useEffect. Every state change or parent re-render will send a duplicate event. Wrap in useEffect(() => { posthog?.capture(...) }, []). [MEDIUM]

Other completed criteria

  • Events represent real user actions (signup, login, logout, claim, like, visit, filter, view leaderboard)
  • Events enable product insights — can build signup→explore→claim funnel, engagement trends, achievement analysis
  • Events include relevant properties (country, region, user_rank, total_points, achievement_name)
  • Event names are descriptive and use consistent snake_case convention

Reviewed by wizard workbench PR evaluator

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants