Skip to content

[CI] (ca5a767) next-js/15-pages-router-saas#2744

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

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

Conversation

@wizard-ci-bot

@wizard-ci-bot wizard-ci-bot Bot commented Jul 11, 2026

Copy link
Copy Markdown

Automated wizard CI run

Source: wizard-pr
Trigger ID: ca5a767
App: next-js/15-pages-router-saas
App directory: apps/next-js/15-pages-router-saas
Workbench branch: wizard-ci-ca5a767-next-js-15-pages-router-saas
Wizard branch: release-please--branches--main--components--wizard
Context Mill branch: main
PostHog (MCP) branch: master
Timestamp: 2026-07-11T00:35:13.281Z
Duration: 289.5s

YARA Scanner

✓ 118 tool calls scanned, 0 violations detected

No violations: ✓ 118 clean scans

@wizard-ci-bot

wizard-ci-bot Bot commented Jul 11, 2026

Copy link
Copy Markdown
Author

Now I have all the context needed. Let me produce the evaluation.


PR Evaluation Report

Summary

This PR integrates PostHog into a Next.js 15 Pages Router SaaS app with both client-side (posthog-js) and server-side (posthog-node) SDKs. It adds initialization via instrumentation-client.ts, a Next.js reverse proxy, user identification on auth flows, 10+ meaningful custom events across authentication, billing, and team management, and exception capture throughout.

Files changed Lines added Lines removed
19 +326 -7

Confidence score: 4/5 👍

  • Email fallback as distinct_id: Client-side identify() falls back to data.email when result.distinctId is absent (e.g., posthog.identify(result.distinctId || data.email, ...)). Raw email as a distinct_id causes fragmented person profiles and violates best practices. [MEDIUM]
  • Server-side captureException uses handler names as distinct_id: Calls like posthog.captureException(error, 'sign-in-handler') pass the second argument as distinctId, attributing exceptions to fake users like 'sign-in-handler' instead of the real user. [MEDIUM]
  • Environment variables not documented in .env.example: NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN and NEXT_PUBLIC_POSTHOG_HOST are used but .env.example was not updated — only the uncommitted .env.local contains them. [MEDIUM]

File changes

Filename Score Description
instrumentation-client.ts 5/5 New file: client-side PostHog init with reverse proxy, defaults, exception autocapture
lib/posthog-server.ts 4/5 New file: server-side singleton with flush helper; enableExceptionAutocapture is not a valid posthog-node option
next.config.ts 5/5 Reverse proxy rewrites for /ingest, /ingest/static, /ingest/array
package.json 5/5 Added posthog-js and posthog-node dependencies
pages/_app.tsx 5/5 Imports instrumentation-client to trigger init
components/login.tsx 4/5 Identify + capture on login/signup; email fallback for distinct_id is risky
components/header.tsx 5/5 Sign-out capture, reset(), and exception capture
pages/api/auth/sign-in.ts 4/5 Server-side identify + capture; captureException uses fake distinctId
pages/api/auth/sign-up.ts 4/5 Server-side identify + capture; same captureException concern
pages/api/auth/sign-out.ts 5/5 Server-side capture using forwarded distinct_id header
pages/api/account/update.ts 4/5 Server identify + capture; captureException uses fake distinctId
pages/api/stripe/create-checkout.ts 5/5 Checkout session capture with enriched properties
pages/api/stripe/webhook.ts 4/5 Stripe subscription event; Stripe customer ID as distinctId is acceptable
pages/api/team/invite.ts 5/5 Team invite capture with role and team_id
pages/api/team/remove-member.ts 5/5 Team member removal capture
pages/dashboard/general.tsx 4/5 Account update capture + identify; email fallback concern
pages/dashboard/index.tsx 5/5 Billing portal, team member invite/remove captures
pages/pricing.tsx 5/5 Pricing CTA capture with plan details
posthog-setup-report.md 5/5 Comprehensive setup report with event table

App sanity check ⚠️

Criteria Result Description
App builds and runs Yes No syntax or structural errors; valid TypeScript throughout
Preserves existing env vars & configs Yes Existing next.config.ts extended cleanly; no existing code removed
No syntax or type errors Yes All code is syntactically valid
Correct imports/exports Yes posthog-js imported client-side, posthog-node server-side; no cross-environment misuse
Minimal, focused changes Yes All changes serve PostHog integration; no unrelated edits
Pre-existing issues None N/A

Issues

  • Environment variables not in .env.example: NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN and NEXT_PUBLIC_POSTHOG_HOST are required but .env.example was not updated. Collaborators won't know these variables are needed. Add them to .env.example with placeholder values. [MEDIUM]
  • Invalid posthog-node config option: enableExceptionAutocapture: true in posthog-server.ts is not a recognized posthog-node configuration option. It will be silently ignored but adds confusion. Remove it. [LOW]

Other completed criteria

  • App builds without errors — no missing dependencies or broken syntax
  • Existing app functionality fully preserved; env vars, configs, and routes intact
  • Changes are minimal and focused exclusively on PostHog integration
  • Correct separation of client (posthog-js) and server (posthog-node) imports
  • Build configuration is valid — package.json deps and next.config.ts rewrites are correct

PostHog implementation ⚠️

Criteria Result Description
PostHog SDKs installed Yes posthog-js@^1.399.2 and posthog-node@^5.40.0 added to package.json
PostHog client initialized Yes instrumentation-client.ts calls posthog.init() with env vars, reverse proxy host, defaults, and exception autocapture; imported from _app.tsx
capture() Yes 10+ meaningful capture calls across client and server for auth, billing, team, and account flows
identify() No Identify is implemented in auth and account flows, but client-side uses result.distinctId || data.email — raw email as fallback distinct_id causes fragmented profiles
Error tracking Yes captureException() in every catch block (client + server) plus capture_exceptions: true in client init
Reverse proxy Yes Next.js rewrites in next.config.ts correctly route /ingest/static/* and /ingest/array/* to us-assets.i.posthog.com and /ingest/* to us.i.posthog.com

Issues

  • Email fallback as distinct_id: In login.tsx and dashboard/general.tsx, posthog.identify(result.distinctId || data.email, ...) uses raw email as a fallback distinct_id. If the server ever fails to return distinctId, this creates fragmented person profiles. Remove the || data.email fallback — if distinctId is missing, skip the identify call or handle the error. [MEDIUM]
  • Server captureException uses handler names as distinct_id: In sign-in.ts, sign-up.ts, update.ts, etc., posthog.captureException(error, 'sign-in-handler') passes a descriptive label as the second argument. In posthog-node, this argument is distinctId, meaning exceptions are attributed to fake users like 'sign-in-handler'. Pass the actual user's distinct_id or use the context-based API instead. [MEDIUM]

Other completed criteria

  • API key correctly loaded from NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN environment variable — not hardcoded
  • Host configured via reverse proxy (/ingest) on client and NEXT_PUBLIC_POSTHOG_HOST on server
  • Server client uses singleton pattern with flushAt: 1, flushInterval: 0 per Next.js docs
  • posthog.reset() correctly called on sign-out in header.tsx
  • Reverse proxy routes both /static/* and /array/* to the assets origin as required

PostHog insights and events ✅

Filename PostHog events Description
components/login.tsx user_signed_in, user_signed_up Auth events with entrypoint, redirect target, and checkout context
components/header.tsx user_signed_out, captureException Sign-out with source, plus reset and error tracking
pages/dashboard/general.tsx account_updated, captureException Profile update with domain context and error tracking
pages/dashboard/index.tsx billing_portal_opened, team_member_removed, team_member_invited, captureException Dashboard actions with plan, subscription, and team size properties
pages/pricing.tsx pricing_cta_clicked, captureException CTA click with plan name, billing interval, trial days
pages/api/auth/sign-in.ts user_signed_in, captureException Server-side auth with method, redirect, team context
pages/api/auth/sign-up.ts user_signed_up, captureException Server-side signup with role, invite status, team context
pages/api/auth/sign-out.ts user_signed_out, captureException Server-side sign-out using forwarded distinct_id
pages/api/account/update.ts account_updated, captureException Server-side profile update with team context
pages/api/stripe/create-checkout.ts checkout_session_created, captureException Checkout with price_id, team, plan details
pages/api/stripe/webhook.ts stripe_subscription_updated, captureException Webhook processing with subscription status and event type
pages/api/team/invite.ts team_member_invited, captureException Invite with role and team context
pages/api/team/remove-member.ts team_member_removed, captureException Removal with member and team context

Issues

  • No critical or medium issues with event quality.

Other completed criteria

  • Events represent real user actions across the full SaaS lifecycle (auth, billing, team management)
  • Events enable product insights — can build signup→activation funnels, billing conversion, team growth analysis
  • Events include enriched contextual properties (team_id, plan_name, role, billing_interval, etc.)
  • No PII in event properties — email_domain extraction is fine, full emails only in identify person properties
  • Event names use consistent snake_case convention with descriptive, action-oriented names

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