Repository navigation
Conversation
An interactive install run whose scan found an email provider now asks one question after the outro: try sending email with PostHog Workflows? The answer goes out as `wizard: email provider offer answered` and is set on the person. Yes shows a link to Workflows in the project. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on PostHog#1493 (base
feat/detected-email-providers). Fork-only slice 18a of the Wizard email work; it mirrors no upstream PR yet.Problem
The Wizard's scan finds the email service a developer's app sends with, such as Resend. Today nothing asks them about it, so we can't tell how many of them would send email from PostHog instead, or with what intent.
With this PR, an interactive install that found an email provider can ask one question after the outro: "Your app sends email with Resend. PostHog can send it too, triggered by the events you already track. Want to try it?" The answer goes to analytics and onto the person. The question only shows while the flag
wizard-email-provider-offeris on for the run, and the flag starts at 0%.Spec slice 18 reuses this prompt for its
migrate resendoffer: the "Yes" screen already has a typed slot for the migrate command, which stays empty until slice 16'smigratefamily exists.Impact
Pays into Grow Workflows through Started orgs (orgs that create their first workflow). Measured by the funnel Wizard email provider offer in the internal project: offer shown, answered (by
answer), then a Workflows page view or an email sender created within 7 days, broken down byprovidersandanswer. It counts production builds only, and only runs wherewizard-email-provider-offeris on. Expected move: small, up. The first job of this PR is the intent read; the "Yes" link is the conversion path. Run cost: n/a.Merge Danger
Note
🔵 Merge risk: low · one-way door (merge-risk rubric)
wizard_email_offer_answerandwizard_email_offer_answered_atonto real person profiles once the flag is on. A revert stops new writes; values already set stay until a$unset.Changes
WIZARD_EMAIL_PROVIDER_OFFER_FLAG_KEYinsrc/shared/constants.ts):wizard-email-provider-offerjoins the Wizard's closed flag set,WIZARD_FLAG_KEYS. The run already evaluates that set once after login and caches it. The offer reads the cached snapshot, so it adds no flag request.'true'asks. A flag that is off, missing from the snapshot, or never evaluated means no question.emailProviderOfferinsrc/programs/posthog-integration/email-provider-offer.ts): the flag is on, the run is interactive and not--ci, scan results may be shared (sharing granted and telemetry on), the outro reports success, and the scan found at least one email provider. Otherwise it returns nothing and the step never shows. Headless runs never mount the TUI, so they never reach it.EmailProviderOfferScreen, a newemail-provider-offerstep between the outro and the MCP step):<app host>/project/<id>/workflows?source=wizard_cli" and moves on at the next key. The link stays on screen only.skipped.wizardCapturehelper:wizard: email provider offer shownwithproviders: every detected kind, such as["Resend", "SendGrid"], even when the prompt names only two.wizard: email provider offer answeredwithproviders,answer(yes,not_now,no,skipped), and$setofwizard_email_offer_answerandwizard_email_offer_answered_at. The run is logged in by then, so the$setlands on the identified person.src/tui/run.ts): after a successful run the run report has already gone out, so the interrupt'sanalytics.shutdown('cancelled')returned at once and the process could exit before theskippedevent left. The signal handler now unmounts the TUI first, which capturesskippedfor an open question, then awaits the shutdown and a flush before it exits. The screen-exit path already used that order.detectedEmailProviders(scan)inemail-providers.tsnow backs both feat(analytics): set detected email providers on the person PostHog/wizard#1493's person property and this offer, so the two can't drift. The kind list became a typed tuple, soEmailProviderKindkeys the migrate command map.Test plan
Lane gate on
324302a:pnpm typecheckgreen,pnpm lint0 errors (564 existing warnings),pnpm vitest run258 files and 3838 tests green. CI: the fork runs no workflows, so this PR has no checks; Michael accepted that gap, and the lane gate is the check.Test seams:
emailProviderOffer(session, flags)over a real scan session (email-provider-offer.test.ts):source=wizard_cli.--ci, telemetry off, sharing declined, a failed install, no login, and a scan with no email provider.providerskeeping all of them.Analytics.getAllFlagsForWizard(analytics.test.ts): a rejected flag request caches a snapshot with no flags, which the offer reads as off.EmailProviderOfferScreenrendered in the real store with@utils/analyticsmocked (EmailProviderOfferScreen.test.tsx): the exact prompt copy and the shown event;not_now,noand Esc; Yes shows the link and the next key moves on; one answer only; an unmount without an answer sendsskipped.runTuicancellation (run.test.ts):flows.test.ts,frames.test.tsxsnapshots at 80x28 and 120x40,store-invariants.test.ts,flags.test.ts, the e2e action registry). The analytics mocks in four flow tests and the harness test gained the flag read.Proofs per qa-swarm
PROOF.md, saved on324302a. That commit adds only the test above, so the captures froma71b308exercise the same source.Live run (saved): the real TUI in a PTY on a Next.js fixture, after the real scan and detection, a real login through
Analytics, and the real flag evaluation against the internal project.Flag off: the flag evaluated to
falseat 0%. The outro went straight to exit, with no offer screen and no offer events.Flag on, two providers: the Wizard's dev-only flag override turned the flag on, on a fixture with
resendand@sendgrid/mail. The prompt read "Your app sends email with Resend and SendGrid." and choice 3 read "No, I'm happy with my current setup". "Yes" showed the Workflows link, andshownandansweredcarried["Resend", "SendGrid"]withanswer: yes.Each answer, flag on, one provider. Each sent
shownthenansweredto the internal project, and the person gotwizard_email_offer_answer:answersentyesnot_nownoskippedskippedCtrl-C after a successful run, on
324302a: the harness sent the real success shutdown before the question, as a successful run does. Ctrl-C then ran the host's signal order: unmount, the cancelled shutdown (a no-op by then), the flush, and exit 130 with nothing after it. The internal project receivedsetup wizard finished(success), thenshown, thenansweredwithskipped, and the person gotwizard_email_offer_answer. So the flush alone delivers an answer captured after the run already reported its end.CI case (saved): flag on and
session.ciset. The outro goes straight to exit, with no offer screen and no offer events.Prod shape (gap, accepted by standing rule until 2026-11-02): how many real runs find an email provider stays unknown until read-only production access is set up.
UI walk (saved): PNG captures of the real TUI at 100x30, shown above: flag off, two providers, the link, one provider, the CI run, and the question after a successful run.
Post-deploy watch: owed after merge.
Gap: no LLM agent install ran, since this box has no gateway key. The harness arranges the successful run and outro; the scan, detection, login, flag evaluation, TUI and analytics client are the real ones. For the same reason, the live Ctrl-C runs the host's steps in its order but not the
runTuifunction itself: the repo's real-host route needs--ci, which hides the question. Therun.test.tscases driverunTuiitself.Decisions
migrate? Yes: one question after the install outro, measured, never in CI or headless runs or with sharing off.skipped.wizard-email-provider-offer, at 0%. Not the Workflows launch flag: that one stays on the PostHog org until launch and would hide the question from the developers it measures./project/<id>/workflowsor an email integration created, within 7 days; production builds only.LLM context
Built by Claude Code (Opus 5.5) as lane
wiz-offerof the Wizard email map. Michael answered the open questions on 2026-10-26.Involvement: no grilling rounds, no prototypes, one cross-family review (GPT-6.1 Sol, round 1: one high finding fixed, one low finding rejected), final reads of each later change, and two fixes from the proof judge (unmount before the final flush on a signal, and a test and live run for Ctrl-C after a successful run). Live-run and UI-walk proof captured.
🤖 Generated with Claude Code