Files
resolutionflow/.ai/HANDOFF.md
Michael Chihlas 4a37a47887
Some checks failed
Mirror to GitHub / mirror (push) Successful in 5s
CI / frontend (pull_request) Failing after 1m8s
CI / e2e (pull_request) Successful in 12m9s
CI / backend (pull_request) Successful in 15m24s
chore(env): standardize backend python on 3.12
Co-Authored-By: Codex <noreply@openai.com>
2026-05-07 11:31:28 -04:00

8.5 KiB
Raw Blame History

HANDOFF.md

Last updated: 2026-05-07 (Python 3.12 standardization applied; cutover ops still pending)

Active task: None mid-flight. Branch feat/self-serve-signup-phase-2 (HEAD 502c0a4) is ready to PR.

Where this session ended

Tasks 2744 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 2731):

  • BillingService.open_customer_portal + GET /billing/portal-session (allowlisted for canceled/unverified users).
  • PATCH /users/me/onboarding-step + POST /users/me/onboarding-dismiss-rest.
  • POST /sales-leads (public, 5/hr/IP rate limit, fire-and-forget notification email, server-side PostHog event stub).
  • /admin/plan-limits GET/PUT round-trips plan_billing (Stripe IDs, prices, public/archived flags) in one transaction; BillingService.invalidate_billing_cache no-op stub for future cache.
  • GET /config/public ({self_serve_enabled, oauth_providers}); register-endpoint gate now REQUIRE_INVITE_CODE and not SELF_SERVE_ENABLED and not invite_code.
  • GET /accounts/invites/{code}/lookup (Phase 36).
  • OAuth callback extended to honor account_invite_code + invited_email for invitee-via-OAuth path; rejects existing-email user with email_already_registered_use_login.
  • GET /plans/public (Phase 42).
  • POST /beta-signup returns 307 to ${FRONTEND_URL}/register?from=beta.
  • OnboardingStatus extended with email_verified + shop_setup_done; UserResponse exposes onboarding_step_completed + onboarding_dismissed.

Frontend additions:

  • useBillingStore (Zustand, polled 60s via useBillingPoll mounted in AppLayout), useFeature / useFeatureLimit / useTrialBanner hooks, FeatureGate / UpgradePrompt / EmailVerificationGate components.
  • RegisterPage redesign: OAuth (Google/Microsoft) buttons + invite-code-conditional. OAuthCallbackPage at /auth/{google,microsoft}/callback with CSRF state validation. useAppConfig hook (consumes /config/public, falls back to VITE_SELF_SERVE_ENABLED).
  • AcceptInvitePage at /accept-invite?code=... (locked email, 3 sign-in options, /?welcome=teammate on success).
  • EmailVerificationBanner refactored to design-system tokens + grace-period hide; EmailVerificationWall polished; VerifyEmailPage at /verify-email?token=... (single-fire guard).
  • WelcomeRouter + WelcomeStep1/2/3 at /welcome*. PSA tile UI + bulk invite emails per row.
  • TrialPill in topbar (pristine/warning/urgent/expired/paid/complimentary/past_due/canceled).
  • NextStepCard + SetupChecklist (replaces orphaned OnboardingChecklist); unified list, no SOLO/TEAM split, no Script Builder item.
  • PricingPage at /pricing (B-style, 3 plan cards, hardcoded comparison v1).
  • ContactSalesPage at /contact-sales. LandingPage got "See pricing" CTA + replaced beta-signup form with <Link to="/register?from=beta">.

New env vars (Vite ARG/ENV in Dockerfile + .env.example): VITE_SELF_SERVE_ENABLED, VITE_GOOGLE_CLIENT_ID, VITE_MS_CLIENT_ID, VITE_OAUTH_REDIRECT_BASE, VITE_CALENDLY_URL. Backend env: SALES_LEAD_RECIPIENT_EMAIL (default sales@resolutionflow.com).

Resume point — DO THIS NEXT

  1. Review the branch and open a PR. 18 commits, all squashed-or-not at user discretion. Branch feat/self-serve-signup-phase-2 from main.
  2. Phase O — manual operational tasks (Tasks 4547 from the plan):
    • Task 45: Stripe live-mode setup (Products, Prices, Customer Portal, webhook endpoint + signing secret) and Railway prod env vars (STRIPE_*, OAUTH_REDIRECT_BASE, GOOGLE_CLIENT_*, MS_CLIENT_*). Run python -m scripts.sync_stripe_plan_ids to populate plan_billing rows with live Stripe IDs.
    • Task 46: Internal validation pass via INTERNAL_TESTER_EMAILS allowlist (per-email bypass of SELF_SERVE_ENABLED=false). Backend support for the allowlist itself is NOT implemented — needs a small addition to auth.py/config.py if the validation pass requires it. Otherwise validate in test mode with the flag flipped temporarily.
    • Task 47: Flip SELF_SERVE_ENABLED=true (backend) + VITE_SELF_SERVE_ENABLED=true (frontend rebuild). Watch PostHog funnel + Stripe webhook error rate.
  3. Followups deferred during Phase 2 (logged below).

Followups deferred from Phase 2

  • 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.
  • 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)

  • Code-server LXC now standardizes on Python 3.12. .python-version pins 3.12.13 to match the Docker image; native backend work should use backend/venv built from that interpreter. pytest --version and alembic --version pass natively when DEBUG=true SECRET_KEY=ci-test-secret-key-not-for-production are supplied to avoid local .env validation failures.
  • Pytest: docker exec resolutionflow_backend pytest tests/<file> -v --override-ini="addopts=".
  • Vitest: docker exec -w /app resolutionflow_frontend npm test -- <path> --run.
  • TS build: docker exec -w /app resolutionflow_frontend npx tsc -b.
  • Alembic: docker exec -w /app resolutionflow_backend alembic .... Never --rev-id.
  • No gh CLI — Gitea API via $GITEA_TOKEN for PR/issue work.
  • Single alembic head: c6cbfc534fad (from Phase 1). Phase 2 added no migrations.