feat(activation): onboard unactivated first-cycle Pro users#5564
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
koala73
force-pushed
the
codex/pro-activation-first-cycle-backfill
branch
from
July 24, 2026 19:23
7aec706 to
82c44f2
Compare
5 tasks
…t gaps - widget-store: legacy widgets stored before the `tier` field existed no longer get silently dropped by the resilient (non-strict) loadWidgets() path; they're normalized to 'basic' as before. Strict activation-eligibility reads still reject them as malformed. - billing: remove the byte-for-byte duplicate isProActivationCoveringAt and use the now-exported subscriptionHelpers.isCoveringAt instead, so the activation-boundary coverage rule can't silently drift from the canonical entitlement-reconciliation rule. - tests: cover the previously-untested controller not-eligible/retry branching, the confirmProActivationPresentation double-confirm idempotency path, and three decideActivationMount markerless-cohort branches (desktop, entitlement-not-loaded, subscription-not-loaded). Found during code review of #5564.
…ry exhaustion - mount(): wrap the body in try/finally so `this.mounting` releases on every exit path (including a thrown exception), and collapse the two near- identical auth-generation-mismatch blocks into one abortIfStale() helper. - openFlow(): thread a staleness check through so a destroy-triggered generation bump (e.g. a same-document app re-init while the interstitial's open flow is still in flight) now closes any interstitial that got opened, instead of leaving it orphaned on screen wired to a dead context. Also fixes a related gap where that path queued a follow-up evaluate() without checking ctx.isDestroyed, unlike the sibling check earlier in mount(). - scheduleFlowRetry(): when the bounded 5-attempt backoff schedule exhausts, fall back to the same passive entitlement/subscription listeners used elsewhere in this file (armRetry()) instead of permanently resolving the tab. A later external signal can still retrigger onboarding within the same session; a fresh trigger gets its own full backoff schedule. Found during code review of #5564.
This was referenced Jul 25, 2026
…s, and tests - reuse canonical withTimeout in the activation flow instead of bespoke deadline wrappers - normalize legacy pre-tier widgets to 'basic' in loadWidgetsStrict so the markerless strict read resolves instead of retrying forever - swallow the synchronous subscribe replay in armRetry so the backoff-exhaustion fallback cannot retrigger the retry loop - update stale pro-activation-state module header (markerless path, injected-storage helpers) - pin rule.enabled conjunct, same-nonce re-claim, and confirm transient-failure retry with tests - dedupe the in-memory storage stub in activation-state tests
koala73
added a commit
that referenced
this pull request
Jul 25, 2026
…cript (#5584) * feat(activation): persist wizard outcome + activation-lift analysis script Neither the Umami funnel side (no userId, joinable to nothing) nor the Convex side (no outcome record) could answer "did the Pro Activation wizard improve feature adoption" -- see #5582 for the full investigation, including the #5565 Umami outage that blacked out ~4 days spanning most of #5534's launch window. - convex/schema.ts: extend proActivationPresentations with confirmedSteps/ skippedSteps/failedSteps/exitedAt -- the durable per-subscription outcome record, independent of Umami. - convex/payments/billing.ts: new recordProActivationOutcome mutation, ownership + nonce checked like confirmProActivationPresentation. Best-effort by design (no-ops on a stale/mismatched presentation row). - src/services/billing.ts: client wrapper, swallows errors so a failed write never blocks the subscriber's exit. - src/services/pro-activation-state.ts: buildActivationOutcomeBuckets, a pure helper (alongside the existing summarizeActivationExit) that buckets step results by outcome -- unit-testable without a DOM. - src/components/ProActivationInterstitial.ts: call the new mutation from onExit, gated to the markerless cohort (the only flow with a presentation row to attach outcomes to). - scripts/report-activation-lift.mjs: read-only report comparing feature creation (notificationChannels/alertRules/userApiKeys/mcpProTokens) after wizard exit, engaged vs. presented-only-and-skipped-everything. Not a randomized control -- both groups were equally shown the wizard, so this isolates an engagement effect, not the wizard's existence. Enforces a 30-sample floor per group before printing a rate comparison. Verified against real (read-only) production Convex data: the new table/ fields don't exist yet since #5564 hasn't merged, and the script reports that state cleanly rather than erroring. * fix(activation): harden lift outcome reporting
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.
Summary
Verification
Follow-up to #5534.