Skip to content

[CI] (tune-5-med) next-js/15-app-router-saas#2409

Closed
wizard-ci-bot[bot] wants to merge 1 commit into
mainfrom
wizard-ci-tune-5-med-next-js-15-app-router-saas
Closed

[CI] (tune-5-med) next-js/15-app-router-saas#2409
wizard-ci-bot[bot] wants to merge 1 commit into
mainfrom
wizard-ci-tune-5-med-next-js-15-app-router-saas

Conversation

@wizard-ci-bot

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

Copy link
Copy Markdown

Automated wizard CI run

Source: manual
Trigger ID: tune-5-med
App: next-js/15-app-router-saas
App directory: apps/next-js/15-app-router-saas
Workbench branch: wizard-ci-tune-5-med-next-js-15-app-router-saas
Wizard branch: 52c54583828289f4969ad54a9fdfdd97ea7fde30
Context Mill branch: main
PostHog (MCP) branch: master
Timestamp: 2026-07-03T17:20:18.970Z
Duration: 1359.4s

YARA Scanner

✓ 90 tool calls scanned, 2 violations detected

  [BLOCKED] pii_in_capture_call (HIGH) — PostToolUse:Edit
  [BLOCKED] pii_in_capture_call (HIGH) — PostToolUse:Edit

No violations: ✓ 88 clean scans

⚠️ YARA violations detected — see report above

@wizard-ci-bot

wizard-ci-bot Bot commented Jul 3, 2026

Copy link
Copy Markdown
Author

Now I have everything needed. Let me produce the evaluation.


PR Evaluation Report

Summary

This PR integrates PostHog into a Next.js 15 App Router SaaS app with both client-side (posthog-js via instrumentation-client.ts) and server-side (posthog-node singleton) analytics. It adds a reverse proxy via Next.js rewrites, captures multiple meaningful events across auth, checkout, and subscription flows, and enables client-side exception autocapture. However, the reverse proxy is configured but not wired to the PostHog client, user identification is entirely missing, and the .env.example was not updated with PostHog env vars.

Files changed Lines added Lines removed
11 +156 -3

Confidence score: 4/5 👍

  • Reverse proxy configured but not used: The next.config.ts rewrites /ingest/* to PostHog, but api_host in instrumentation-client.ts is set to the direct PostHog host (process.env.NEXT_PUBLIC_POSTHOG_HOST) instead of /ingest. The proxy is dead code. [CRITICAL]
  • No identify() calls anywhere: Neither client nor server code ever calls posthog.identify(). Users are never linked to their events on the client side. Server-side captures use String(userId) as distinctId, but the client session remains anonymous forever. [CRITICAL]
  • Fabricated distinctId values in server captures: The webhook handler uses distinctId: 'system' and the checkout error handler uses distinctId: 'anonymous'. These are hardcoded strings that create phantom person profiles in PostHog. [MEDIUM]
  • Duplicate pricing_get_started_clicked event: Captured both client-side in submit-button.tsx and server-side in lib/payments/actions.ts, causing double-counting. [MEDIUM]
  • .env.example not updated: PostHog env vars (NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN, NEXT_PUBLIC_POSTHOG_HOST) are not added to .env.example, only to .env.local (which contains a real API key phc_nmum...). [MEDIUM]

File changes

Filename Score Description
instrumentation-client.ts 3/5 Correct Next.js 15.3+ init pattern with capture_exceptions: true and defaults, but api_host not pointed to the reverse proxy
lib/posthog-server.ts 4/5 Clean singleton with flushAt: 1 / flushInterval: 0 and shutdown helper
next.config.ts 3/5 Reverse proxy rewrites are correctly structured (static, array, catch-all) but never used because client api_host doesn't point to /ingest
package.json 5/5 Both posthog-js and posthog-node added
app/(login)/actions.ts 4/5 Good event mapping from ActivityType to PostHog events with properties
app/api/stripe/checkout/route.ts 3/5 Captures checkout_completed but error path uses distinctId: 'anonymous'
app/api/stripe/webhook/route.ts 3/5 Captures subscription_updated but uses distinctId: 'system'
app/(dashboard)/pricing/submit-button.tsx 3/5 Direct import of posthog-js instead of usePostHog hook; duplicates server-side event
lib/payments/actions.ts 4/5 Good server-side capture with user context
lib/payments/stripe.ts 4/5 Clean checkout_started capture with enriched properties
posthog-setup-report.md 3/5 Lists auth_sign_in_failed and auth_sign_up_failed events that don't exist in the code

App sanity check ⚠️

Criteria Result Description
App builds and runs Yes No syntax errors; all imports resolve to installed packages
Preserves existing env vars & configs Yes Original env vars and app logic untouched; next.config.ts comments removed but no functional loss
No syntax or type errors Yes Valid TypeScript throughout
Correct imports/exports Yes posthog-js on client, posthog-node on server; no cross-environment imports
Minimal, focused changes Yes All changes are PostHog-related
Pre-existing issues None

Issues

  • .env.example not updated with PostHog vars: The .env.example file is committed but lacks NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN and NEXT_PUBLIC_POSTHOG_HOST. Collaborators won't know to set them. [MEDIUM]
  • Real API key committed in .env.local: The .env.local file contains a real PostHog project token (phc_nmum...). While .env.local may be gitignored, if it were committed it would leak the key. [LOW]

Other completed criteria

  • Both posthog-js and posthog-node correctly added to package.json
  • Build configuration remains valid with rewrites added
  • All existing app functionality preserved (auth, payments, dashboard)

PostHog implementation ❌

Criteria Result Description
PostHog SDKs installed Yes posthog-js@^1.396.6 and posthog-node@^5.39.4 in package.json
PostHog client initialized Yes Client via instrumentation-client.ts with correct pattern; server via singleton in lib/posthog-server.ts
capture() Yes 8+ meaningful capture calls across client and server
identify() No No posthog.identify() call anywhere — users are never linked to events on the client
Error tracking Yes capture_exceptions: true in client init enables exception autocapture
Reverse proxy No Rewrites configured correctly in next.config.ts but api_host points to direct PostHog host, not /ingest

Issues

  • No user identification: The app has a full auth system with sign-in/sign-up flows, but posthog.identify() is never called on the client. All client-side events remain anonymous. The server-side captures use String(userId) as distinctId, but these won't link to the anonymous client-side session. Add posthog.identify(user.id, { email: user.email }) after login and on session restoration, and posthog.reset() on logout. [CRITICAL]
  • Reverse proxy not connected: api_host in instrumentation-client.ts must be set to '/ingest' (not the env var) for the Next.js rewrites to take effect. Currently, all client requests go directly to us.i.posthog.com, bypassing the proxy entirely. [CRITICAL]
  • Fabricated distinctId values: 'system' in the webhook handler and 'anonymous' in the checkout error handler create phantom person profiles. For webhook events without user context, extract the customer ID from the Stripe subscription object. For the error handler, omit the capture or use the user ID if available. [MEDIUM]

Other completed criteria

  • API key loaded from process.env.NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN (not hardcoded)
  • Host loaded from process.env.NEXT_PUBLIC_POSTHOG_HOST
  • Server-side client uses correct flushAt: 1 / flushInterval: 0 for Next.js short-lived functions
  • defaults: '2026-05-30' correctly set per PostHog guidance

PostHog insights and events ⚠️

Filename PostHog events Description
instrumentation-client.ts captureException (autocapture) Client-side exception tracking via capture_exceptions: true
app/(dashboard)/pricing/submit-button.tsx pricing_get_started_clicked Client-side click on pricing CTA (duplicated server-side)
app/(login)/actions.ts auth_sign_in_succeeded, auth_sign_up_succeeded, account_password_updated, account_deleted, account_updated, team_member_invited, team_member_removed Server-side auth and account lifecycle events via activity log mapping
lib/payments/stripe.ts checkout_started Server-side capture when Stripe checkout session is created
app/api/stripe/checkout/route.ts checkout_completed Server-side capture on successful checkout return
app/api/stripe/webhook/route.ts subscription_updated Server-side capture on Stripe webhook subscription changes
lib/payments/actions.ts pricing_get_started_clicked, customer_portal_opened Server-side captures for pricing and portal actions

Issues

  • Duplicate pricing_get_started_clicked event: Captured both in submit-button.tsx (client) and lib/payments/actions.ts (server). Every click will generate two events, inflating metrics. Remove the client-side capture since the server-side one includes price_id context. [MEDIUM]
  • Setup report lists nonexistent events: posthog-setup-report.md lists auth_sign_in_failed and auth_sign_up_failed events that are not in the code. The eventMap in actions.ts has no entries for failed auth. [LOW]

Other completed criteria

  • Events represent real user actions mapped to actual product flows (auth, checkout, subscription, team management)
  • Events enable product insights — checkout funnel (pricing_get_started_clickedcheckout_startedcheckout_completed), auth funnel, subscription tracking
  • Events include relevant properties (team_id, price_id, subscription_status, has_customer, product_id)
  • No PII in event properties (YARA scanner blocked 2 PII attempts)
  • Consistent snake_case naming convention throughout

Reviewed by wizard workbench PR evaluator

@wizard-ci-bot wizard-ci-bot Bot added the CI/CD label Jul 3, 2026
@wizard-ci-bot wizard-ci-bot Bot closed this Jul 3, 2026
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