Title: Day-0 Pro activation writes no server-side outcome row, so the failing cohort is invisible in Convex
Body:
Split out of #5600 (blind spot 3 of 4). The other three closing artifacts shipped in the #5600 PR:
failure-vs-skip telemetry, browser-error Sentry capture, and the bounded entitlement marker.
The gap
persistActivationOutcomeWithRetry returns early on the day-0 path:
// src/components/ProActivationInterstitial.ts
if (!options.onlyIfUnactivated || !options.expectedActivationKey || !options.activationClaimNonce) {
return;
}
onlyIfUnactivated: false (the day-0, post-checkout path) leaves expectedActivationKey and
activationClaimNonce undefined, so claimProActivationPresentation,
confirmProActivationPresentation, and recordProActivationOutcome are all skipped. The
proActivationPresentations table and report-activation-lift.mjs therefore cover only the
retro/backfill cohort — the one that was working.
Why it mattered
During the #5600 investigation, the day-0 cohort could only be reconstructed by joining Umami
sessions against Convex notificationChannels/alertRules rows and inferring absence. A
server-side outcome row would have made "5/5 day-0 sessions failed every write attempt" a single
query instead of a multi-source forensic exercise.
Why it was deferred
The retro path's ledger is guarded by a claim/nonce lease (claimProActivationPresentation →
confirmProActivationPresentation → recordProActivationOutcome) that exists to make the
markerless backfill fire exactly once per subscription. The day-0 path has no equivalent identity
to key on, so this is a design task, not a wiring task — it needs a decision about what the day-0
presentation key is and how it interacts with the existing already_presented / already_claimed
re-claim guards (see #5584/#5590 for the failure mode when that ordering is wrong).
Acceptance
- A day-0 activation session produces a
proActivationPresentations row with its step outcomes.
- The row cannot block a legitimate later retro re-claim for the same subscription.
report-activation-lift.mjs can separate the day-0 and retro cohorts.
- Regression coverage in
e2e/pro-activation.spec.ts (the markerless flow harness already exists).
Title: Day-0 Pro activation writes no server-side outcome row, so the failing cohort is invisible in Convex
Body:
Split out of #5600 (blind spot 3 of 4). The other three closing artifacts shipped in the #5600 PR:
failure-vs-skip telemetry, browser-error Sentry capture, and the bounded entitlement marker.
The gap
persistActivationOutcomeWithRetryreturns early on the day-0 path:onlyIfUnactivated: false(the day-0, post-checkout path) leavesexpectedActivationKeyandactivationClaimNonceundefined, soclaimProActivationPresentation,confirmProActivationPresentation, andrecordProActivationOutcomeare all skipped. TheproActivationPresentationstable andreport-activation-lift.mjstherefore cover only theretro/backfill cohort — the one that was working.
Why it mattered
During the #5600 investigation, the day-0 cohort could only be reconstructed by joining Umami
sessions against Convex
notificationChannels/alertRulesrows and inferring absence. Aserver-side outcome row would have made "5/5 day-0 sessions failed every write attempt" a single
query instead of a multi-source forensic exercise.
Why it was deferred
The retro path's ledger is guarded by a claim/nonce lease (
claimProActivationPresentation→confirmProActivationPresentation→recordProActivationOutcome) that exists to make themarkerless backfill fire exactly once per subscription. The day-0 path has no equivalent identity
to key on, so this is a design task, not a wiring task — it needs a decision about what the day-0
presentation key is and how it interacts with the existing
already_presented/already_claimedre-claim guards (see #5584/#5590 for the failure mode when that ordering is wrong).
Acceptance
proActivationPresentationsrow with its step outcomes.report-activation-lift.mjscan separate the day-0 and retro cohorts.e2e/pro-activation.spec.ts(the markerless flow harness already exists).