Skip to content

[CI] (cef44b4) next-js/15-pages-router-saas#1399

Closed
wizard-ci-bot[bot] wants to merge 1 commit into
mainfrom
wizard-ci-cef44b4-next-js-15-pages-router-saas
Closed

[CI] (cef44b4) next-js/15-pages-router-saas#1399
wizard-ci-bot[bot] wants to merge 1 commit into
mainfrom
wizard-ci-cef44b4-next-js-15-pages-router-saas

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: next-js/15-pages-router-saas
App directory: apps/next-js/15-pages-router-saas
Workbench branch: wizard-ci-cef44b4-next-js-15-pages-router-saas
Wizard branch: main
Context Mill branch: basic-skills-v2
PostHog (MCP) branch: master
Timestamp: 2026-05-01T20:58:17.620Z
Duration: 491.9s

@wizard-ci-bot
Copy link
Copy Markdown
Author

wizard-ci-bot Bot commented May 1, 2026

Now I have enough context. Let me produce the evaluation.


PR Evaluation Report

Summary

This PR integrates PostHog into a Next.js 15 Pages Router SaaS application, adding both client-side (posthog-js) and server-side (posthog-node) SDKs. It initializes PostHog via instrumentation-client.ts, configures a reverse proxy through Next.js rewrites, adds identify/capture calls across auth flows, Stripe checkout/webhook handlers, and team management endpoints, and includes error tracking via captureException.

Files changed Lines added Lines removed
16 +248 -3

Confidence score: 4/5 👍

  • PII in capture event properties: Multiple capture() calls include email directly in event properties (sign-in, sign-up, account update, team invite). Email belongs in person properties via identify(), not in capture() event properties. [CRITICAL]
  • Using email as distinct_id: All server-side events use user.email as the distinct_id. This is fragile — if users change their email, events fragment across identities. A stable database user ID should be used instead. [MEDIUM]
  • Missing posthog.reset() on logout: No posthog.reset() call is implemented anywhere for logout, which means shared devices could merge user identities. [MEDIUM]
  • .env.example not updated: The .env.example file does not include the new NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN or NEXT_PUBLIC_POSTHOG_HOST variables, making onboarding harder. [MEDIUM]
  • Missing await posthog.shutdown(): Server-side API routes never call await posthog.shutdown() after capturing events. With the singleton pattern used here, events may not flush before the response completes in serverless environments. [MEDIUM]

File changes

Filename Score Description
instrumentation-client.ts 4/5 Client-side PostHog init via instrumentation file with reverse proxy, exception capture, and debug mode
lib/posthog-server.ts 3/5 Singleton server-side PostHog client — correct pattern but uses NEXT_PUBLIC_POSTHOG_HOST which may be /ingest (proxy path), not the actual PostHog host
next.config.ts 5/5 Correctly configured reverse proxy rewrites for /ingest, /ingest/static, and /ingest/array
package.json 5/5 Both posthog-js and posthog-node added with appropriate versions
components/login.tsx 3/5 Identify + capture on login/signup, exception capture, tracing headers — but has PII in capture and uses email as distinct_id
pages/pricing.tsx 4/5 checkout_started capture with good properties, tracing headers, exception capture
pages/api/auth/sign-in.ts 3/5 Server-side identify + capture with session linking — but email in event properties and as distinct_id
pages/api/auth/sign-up.ts 3/5 Same pattern as sign-in with via_invitation flag — same PII issues
pages/api/stripe/checkout.ts 4/5 checkout_completed with good Stripe properties, no PII leak
pages/api/stripe/webhook.ts 4/5 Subscription lifecycle events with DB lookup for user correlation
pages/api/stripe/customer-portal.ts 4/5 Portal access tracking with plan context
pages/api/team/invite.ts 3/5 Team invite tracking — but includes invited_email PII in event properties
pages/api/team/remove-member.ts 4/5 Clean team member removal tracking
pages/api/account/update.ts 3/5 Account update tracking — includes updated_email PII in event properties
.gitignore 5/5 Correctly ignores .env.local
posthog-setup-report.md 5/5 Comprehensive setup report with event catalog and next steps

App sanity check ⚠️

Criteria Result Description
App builds and runs Yes All syntax is valid, imports resolve correctly, no type errors visible
Preserves existing env vars & configs Yes Existing next.config.ts extended cleanly; no existing code removed
No syntax or type errors Yes All TypeScript is valid; proper type assertions used
Correct imports/exports Yes posthog-js imported client-side, posthog-node server-side — correct separation
Minimal, focused changes Yes All changes relate to PostHog integration
Pre-existing issues None Base app appears clean

Issues

  • .env.example not updated with PostHog variables: New environment variables NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN and NEXT_PUBLIC_POSTHOG_HOST are not documented in .env.example, only in .env.local (which is gitignored). Other developers cloning the repo won't know these variables are needed. [MEDIUM]
  • Server-side PostHog host may resolve incorrectly: posthog-server.ts uses process.env.NEXT_PUBLIC_POSTHOG_HOST for the server-side client host. If this is set to https://us.i.posthog.com it's fine, but the .env.local value should be verified — the client uses /ingest via the reverse proxy, which wouldn't work server-side. [LOW]

Other completed criteria

  • All changes are PostHog-related with no unrelated edits
  • Code follows existing codebase patterns (async handlers, error handling style)
  • Both SDK packages correctly added to dependencies
  • Build configuration (next.config.ts) is valid with proper rewrite rules

PostHog implementation ⚠️

Criteria Result Description
PostHog SDKs installed Yes posthog-js@^1.372.6 and posthog-node@^5.32.1 in package.json
PostHog client initialized Yes Client via instrumentation-client.ts (correct for Next.js 15.3+), server via singleton in lib/posthog-server.ts
capture() Yes 10+ meaningful capture calls across client and server
identify() Yes Called on both client (login.tsx) and server (sign-in/sign-up APIs), though uses email as distinct_id which is fragile
Error tracking Yes capture_exceptions: true in init config, plus manual captureException() in catch blocks
Reverse proxy Yes Correctly configured via Next.js rewrites: /ingest/static/* and /ingest/array/*us-assets.i.posthog.com, /ingest/*us.i.posthog.com

Issues

  • Email used as distinct_id: All server-side capture() and identify() calls use user.email as the distinct_id. If a user changes their email (which this app supports via account update), their event history fragments. Should use a stable user ID (e.g., database user.id). [MEDIUM]
  • No posthog.reset() on logout: The app presumably has a logout flow, but no posthog.reset() call is implemented. This is important for shared devices and is a PostHog best practice. [MEDIUM]
  • Missing await posthog.shutdown() in API routes: The server-side singleton client is never shut down after captures. While flushAt: 1 helps, in serverless environments (common with Next.js), the process may terminate before the HTTP request completes. Each API route should call await posthog.shutdown() or at minimum await posthog.flush(). [MEDIUM]
  • API key env var naming: Uses NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN instead of the conventional NEXT_PUBLIC_POSTHOG_KEY. Not a bug, but unconventional. [LOW]

Other completed criteria

  • API key loaded from environment variable, not hardcoded
  • Host correctly configured via reverse proxy (/ingest) for client, env var for server
  • Tracing headers (X-POSTHOG-DISTINCT-ID, X-POSTHOG-SESSION-ID) passed from client to server for session correlation
  • `` used in server-side captures to support anonymous-to-identified user merging

PostHog insights and events ⚠️

Filename PostHog events Description
components/login.tsx user_signed_in, user_signed_up, captureException Client-side auth events with identify, plus error tracking
pages/pricing.tsx checkout_started, captureException Checkout initiation with plan/price properties
pages/api/auth/sign-in.ts user_signed_in Server-side sign-in with session linking
pages/api/auth/sign-up.ts user_signed_up Server-side sign-up with invitation flag
pages/api/stripe/checkout.ts checkout_completed Stripe checkout success with subscription details
pages/api/stripe/webhook.ts subscription_updated, subscription_cancelled Subscription lifecycle from Stripe webhooks
pages/api/stripe/customer-portal.ts customer_portal_accessed Portal access tracking
pages/api/team/invite.ts team_member_invited Team invitation with role context
pages/api/team/remove-member.ts team_member_removed Team member removal
pages/api/account/update.ts account_updated Account settings change

Issues

  • PII in event properties across multiple files: email appears in capture() event properties in sign-in.ts, sign-up.ts, login.tsx, invite.ts (invited_email), and update.ts (updated_email). Emails should only appear in person properties via identify() or ``, never in event properties. This violates PostHog best practices and may cause compliance issues. [CRITICAL]
  • Duplicate client+server captures for auth events: Both login.tsx and sign-in.ts/sign-up.ts capture user_signed_in/user_signed_up. This will result in duplicate events in PostHog for every auth action. Either the client or server should capture these, not both. [MEDIUM]

Other completed criteria

  • Events represent real user actions mapping to actual SaaS product flows (signup → checkout → subscription lifecycle)
  • Events enable product insights: signup-to-subscription funnel, churn tracking, team growth
  • Events include relevant contextual properties (plan names, subscription status, price intervals)
  • 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