Skip to content

Activation outcomes: a browser-blocked step is indistinguishable from a voluntary skip in the durable record #5617

Description

@koala73

Follow-up from #5609 / PR #5615.

That PR made a browser-denied notification permission move the activation wizard's alerts step into its blocked state. Because a blocked step resolves as skipped (same as a step blocked at mount), the denial would have become byte-identical to a voluntary skip in the record. PR #5615 closed that at the event level by adding pro-activation-step-blocked.

The durable side is still collapsed. ProActivationOutcomeSnapshot (src/services/billing.ts) buckets into confirmedSteps / skippedSteps / failedSteps only, and ActivationStepOutcome (src/services/pro-activation-state.ts) has no blocked member. So:

  • A real-time listener on the Umami event stream can tell "browser denied" from "user skipped".
  • Anything querying persisted per-account state in Convex after the fact cannot — a blocked step is in skippedSteps with no marker.

That matters for the questions this data is actually for: sizing the push-permission-denial population, and deciding whether re-prompting an account is worth anything (it never is, once permission is denied — the browser will not ask again).

Shape

Add a fourth bucket rather than widening an existing one, so behavioral consumers are untouched:

  • ActivationStepOutcome gains 'blocked'; the shell writes it on the advance-skip arm when blockedByConfirm.has(step.id).
  • buildActivationOutcomeBuckets gains blockedSteps, plumbed through recordProActivationOutcome and the Convex validator.
  • buildExitSummary must keep rendering it as pending, not failed — the summary says "we couldn't set this up" for failed, which is wrong for a browser refusal we never attempted.
  • selectStepEvent should keep emitting stepBlocked (already wired) rather than stepSkipped for the new outcome.

Note this is a server-contract change: the Convex validator and any existing rows need to tolerate the new bucket, so it wants its own PR rather than riding along with a UX fix.

Why it was split out

Raised during PR #5615's review as an explicit "observation, not a blocker". The event-level fix already covers funnel analysis and live monitoring, which is the majority of the value; this is the remaining after-the-fact-query gap.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions