feat: self-serve signup Phase 2 (frontend cutover) #162
@@ -2,13 +2,18 @@
|
||||
|
||||
# HANDOFF.md
|
||||
|
||||
**Last updated:** 2026-05-06 (Phase 2 frontend-cutover code complete; cutover ops still pending)
|
||||
**Last updated:** 2026-05-07 (Phase 2 code complete + four post-implementation fixes; cutover ops still pending)
|
||||
|
||||
**Active task:** None mid-flight. Branch `feat/self-serve-signup-phase-2` (HEAD `c75ce0c`) is ready to PR.
|
||||
**Active task:** None mid-flight. Branch `feat/self-serve-signup-phase-2` (HEAD `502c0a4`) is ready to PR.
|
||||
|
||||
## Where this session ended
|
||||
|
||||
Tasks 27–44 of Phase 2 implemented across 18 commits on `feat/self-serve-signup-phase-2`, branched off `main` (`f918b76`). Each task went through implementer + spec review + code-quality review per `superpowers:subagent-driven-development`; review findings either fixed in-flight (commit-amends, no merge churn) or noted as deliberate scope deferrals. Final cross-cutting review found one real bug (relative `/beta-signup` redirect target) — fixed in the last amend.
|
||||
Tasks 27–44 of Phase 2 implemented across 18 commits on `feat/self-serve-signup-phase-2`, branched off `main` (`f918b76`). Each task went through implementer + spec review + code-quality review per `superpowers:subagent-driven-development`. Cross-cutting review caught one redirect bug (fixed in-flight). After initial handoff, external code review surfaced four more real bugs — all fixed (commits `5e0c9d2`, `3630dd5`, `06200fa`, `502c0a4`):
|
||||
|
||||
1. **OAuth refresh tokens never stored** (Phase 1 latent): `_store_refresh_token` extracted to module-public `store_refresh_token`; both Google + Microsoft callbacks now persist the JTI before returning. Test covers callback → `/auth/refresh` round-trip.
|
||||
2. **OAuth callback never marked store authenticated** (Phase 2 introduced): `setTokens` now sets `isAuthenticated: true`. Verified safe for the refresh-interceptor caller.
|
||||
3. **Stripe webhook idempotency could permanently drop failed events** (Phase 1 latent): `apply_subscription_event` now adds StripeEvent + runs handler + commits atomically; handler exception → rollback → Stripe retries. Inner `_handle_*` commits removed.
|
||||
4. **Missing `/account/billing` and `/account/billing/select-plan` pages** (Phase 2 oversight): all the new billing CTAs (TrialPill, UpgradePrompt, NextStepCard, SetupChecklist) linked to non-existent routes. Built BillingPage (subscription summary + Manage-billing → portal session) and SelectPlanPage (plan cards + checkout flow).
|
||||
|
||||
**Backend additions (Phase I, Tasks 27–31):**
|
||||
- `BillingService.open_customer_portal` + `GET /billing/portal-session` (allowlisted for canceled/unverified users).
|
||||
@@ -48,12 +53,13 @@ New env vars (Vite ARG/ENV in Dockerfile + `.env.example`): `VITE_SELF_SERVE_ENA
|
||||
|
||||
- **PostHog server-side capture infrastructure missing.** `/sales-leads` calls `_capture_posthog_event` which lazy-imports `app.core.analytics.posthog` (no-op if module absent). Wire up the real PostHog server SDK before cutover — needs `app/core/analytics.py` with a configured client, plus `python-posthog` dep.
|
||||
- **Frontend `/usage/{field}` endpoint missing.** `useFeatureLimit` lazy-fetches `apiClient.get('/usage/' + field)` — endpoint doesn't exist; hook silently falls back to `used=0`. Build the backend endpoint before any UI surface relies on real usage counts.
|
||||
- **OAuth state CSRF — semi-validated.** RegisterPage path validates state round-trip on callback. AcceptInvitePage path uses a base64url-JSON envelope with embedded csrf — also validated. UTF-8-safe via `unescape(encodeURIComponent(...))`.
|
||||
- **Pre-existing dual subscription stores** (`authStore.subscription` legacy + `billingStore.subscription` new). `Subscription.status` union in `frontend/src/types/account.ts` is missing `'complimentary'`. Phase 2 didn't introduce — but worth deprecating the old store before any UI reads `complimentary` from the wrong source.
|
||||
- **`INTERNAL_TESTER_EMAILS` per-email allowlist** (Task 46): backend support not implemented. Add when Phase O Task 46 needs it.
|
||||
- **`accept-invite` not gated by `self_serve_enabled`** — by design (invites work in any mode). Confirm if pilot wants stricter gating.
|
||||
- **`UserResponse.onboarding_step_completed` validator** suggested by reviewer (reject `complete` without data) — skipped, spec types `data?` as optional.
|
||||
- **Welcome wizard behind `EmailVerificationGate`:** Day 7+ unverified users hit the wall on `/welcome/*` and have no path back into the wizard until they verify. Per spec — wall says "Resend verification" → user verifies → wall closes → wizard resumes. If product wants a different UX, allowlist `/welcome/*` in the gate.
|
||||
- **`SelectPlanPage` seat input doesn't validate against plan `max_seats`.** Stripe Checkout will reject if exceeded; nicer UX would cap client-side.
|
||||
- **`BillingPage` Manage-billing button always enabled** — clicking with no `stripe_customer_id` falls back to a toast. By design; could disable + tooltip if billing-state payload were extended with that flag.
|
||||
|
||||
## Environment notes (carry-forward)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user