Files
resolutionflow/.ai/HANDOFF.md
Michael Chihlas 1a433dc803
Some checks failed
Mirror to GitHub / mirror (push) Has been cancelled
CI / frontend (pull_request) Successful in 6m40s
CI / backend (pull_request) Successful in 10m33s
CI / e2e (pull_request) Successful in 9m51s
docs(handoff): PR #165 merged; Stripe activation blocked on EIN (user-side)
PR #165 (legal/contact pages + MarketingFooter) merged as ba45cfe.
All code blockers for self-serve cutover are now on main.

Records the real Stripe live-mode activation blocker: user does not
yet have an EIN for ResolutionFlow, LLC, and Stripe requires a tax
ID before it will activate live mode. Applying via IRS.gov 2026-05-13.
Calls out the P.O. Box / mailing address as the likely-next blocker
since Stripe live-mode also requires a business address (same gating
event as the TODOs in ContactPage.tsx and PoliciesPage.tsx).

Nothing on the code side blocks live-mode flip. Apex DNS at
Namecheap remains pending but only matters once Stripe runs its
site-verification step, which happens after the business-profile
fields are accepted.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 01:31:32 -04:00

6.7 KiB

HANDOFF.md

Last updated: 2026-05-12

Active task: Phase O cutover for self-serve signup. All code blockers are closed on main (PR #164 3f04911, PR #165 ba45cfe). Currently blocked on Stripe live-mode activation — root cause is EIN, not code. User does not yet have an EIN for ResolutionFlow, LLC; Stripe requires a tax ID for live-mode activation. Applying via IRS.gov on 2026-05-13. Second likely blocker fires immediately after: Stripe also requires a business mailing address, which the user is deferring until a P.O. Box is set up — same gating event as the TODO in ContactPage.tsx and PoliciesPage.tsx. Apex DNS at Namecheap is still missing as well (separate user-side issue tracked below); that one only matters once Stripe runs its site-verification step, which happens after the business-profile fields are accepted. Nothing on the code side blocks live-mode flip.

Where this session ended

PR #165 squash-merged (ba45cfe feat(legal): add /policies, /contact, /promotions pages + MarketingFooter (#165)):

  • New pages, all SPA, matching existing /privacy and /terms pattern: /policies (consolidated Customer Policies — customer service contact, return/refund/dispute policy, cancellation, U.S. legal and export restrictions, promotional terms; anchor IDs per subsection), /contact (phone (470) 949-4131, support/sales/billing/security inboxes, response-time SLAs), /promotions (stub stating no promotions currently active — satisfies Policies §6.2 cross-ref).
  • MarketingFooter (frontend/src/components/common/MarketingFooter.tsx) extracted from inline landing footer and mounted on /landing, /pricing, /contact-sales. Reuses existing landing-footer* CSS — must be rendered inside a .landing-page wrapper (documented in a JSX comment) because --lp-* vars are scoped there. All four legal links (Privacy / Terms / Policies / Contact) are now reachable from every marketing surface.
  • Privacy and Terms closing sections updated to point at /contact + /policies and the correct inbox per area (security@ and support@ respectively). Stale hello@resolutionflow.com mailto removed everywhere.
  • Mailing address left as TODO comments in ContactPage.tsx and PoliciesPage.tsx (one each). Rendered publicly as "available on request — email support@". Fill in when the P.O. Box is purchased.

tsc --project tsconfig.app.json --noEmit and eslint clean. Local vite build and tsc -b are blocked by root-owned node_modules/.tmp and node_modules/.vite-temp cache directories — CI rebuilds from a clean env and was green.

Working tree clean (only pre-existing untracked files: abc-feat-self-serve-signup-phase-2-design-...md, core.*, docs/architecture/, docs/tutorials/ — same set noted in prior handoffs as "do not stage").

Single alembic head: 4ce3e594cb87 (no schema changes in this PR).

Resume point

Phase O manual ops — entirely user-side, gated on the apex DNS fix below:

  1. Stripe Dashboard live-mode:
    • 3 Products (Starter, Pro, Enterprise). Monthly Prices for Starter ($19.99) + Pro ($29.99). No Prices on Enterprise (sales-led).
    • Customer Portal with plan-switching disabled.
    • Webhook at https://api.resolutionflow.com/api/v1/webhooks/stripe with 5 events. Save live signing secret.
    • Business profile fields: Customer service URL https://resolutionflow.com/contact. Refund/cancellation policy URL https://resolutionflow.com/policies. Terms https://resolutionflow.com/terms. Privacy https://resolutionflow.com/privacy. Phone (470) 949-4131. Mailing address per Stripe form (not required on website).
  2. Railway prod env: STRIPE_SECRET_KEY=sk_live_..., STRIPE_WEBHOOK_SECRET, STRIPE_PUBLISHABLE_KEY + VITE_STRIPE_PUBLISHABLE_KEY (frontend redeploy required — Vite bake-at-build, Lesson 60), OAUTH_REDIRECT_BASE=https://resolutionflow.com, SELF_SERVE_ENABLED=false (still false at this point), INTERNAL_TESTER_EMAILS=<allowlist>, prod Google + Microsoft OAuth credentials.
  3. Sync against prod: railway run python -m scripts.sync_stripe_plan_ids. Verify plan_billing rows have sk_live_* price IDs.
  4. Internal validation (Task 46): 9 scenarios with internal testers whose emails match INTERNAL_TESTER_EMAILS.
  5. Flag flip (Task 47): email pilots, set SELF_SERVE_ENABLED=true + VITE_SELF_SERVE_ENABLED=true (frontend redeploy). PostHog signup-funnel dashboard + Sentry alert at >1/hour Stripe webhook errors.

Open issues from prior session (non-code, user-side)

  • Apex DNS missing. resolutionflow.com (apex) returns no A/CNAME at the authoritative DNS (Namecheap per SOA dns1.registrar-servers.com.). When www was reconfigured in Railway, the apex record got dropped from the zone. www works (cert provisioned 2026-05-08 01:40 UTC, valid Let's Encrypt SAN). Symptom: apex unreachable from user's machine; Stripe verifier "URL couldn't be reached." User to re-add apex record at Namecheap (ALIAS Record host=@ value=c9g7uku8.up.railway.app) or re-add the apex as a Railway custom domain and follow Railway's DNS instructions. The Railway path is more durable.
  • Edge HSTS sticky state on user's machine. Browser remembers the earlier broken-cert visit. Fix: edge://net-internals/#hsts (delete resolutionflow.com and www.resolutionflow.com) + #dns clear host cache + #sockets flush.

Carry-forward

  • Annual pricing intentionally NOT implemented — user wants exit flexibility. Schema columns preserved as nullable. sync_stripe_plan_ids.py leaves annual fields NULL.
  • INTERNAL_TESTER_EMAILS parsed comma-separated → normalized lowercase list. Anonymous callers always see the global flag — allowlist never leaks via unauthenticated request content (regression test enforces).
  • Office-hours design doc at ~/.gstack/projects/chihlasm-resolutionflow/abc-feat-self-serve-signup-phase-2-design-20260507-112020.md (documentation-builder thesis). NOT yet adopted as roadmap — gated on 3 cold calls with external Directors of Onboarding.
  • Mailing address fill-in: search for TODO: replace with full mailing address in frontend/src/pages/ContactPage.tsx and frontend/src/pages/PoliciesPage.tsx (one each) once P.O. Box is purchased.
  • Bot-crawlability of legal pages: still SPA-rendered. Stripe didn't enforce content scraping last time (issue turned out to be DNS). If a future vendor review flags it, pre-render with vite-plugin-prerender-spa (~half day).
  • Frontend env additions for cutover: VITE_SELF_SERVE_ENABLED, VITE_GOOGLE_CLIENT_ID, VITE_MS_CLIENT_ID, VITE_OAUTH_REDIRECT_BASE, VITE_CALENDLY_URL, VITE_STRIPE_PUBLISHABLE_KEY.