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>
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
/privacyand/termspattern:/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 existinglanding-footer*CSS — must be rendered inside a.landing-pagewrapper (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+/policiesand the correct inbox per area (security@andsupport@respectively). Stalehello@resolutionflow.commailto removed everywhere. - Mailing address left as TODO comments in
ContactPage.tsxandPoliciesPage.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:
- 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/stripewith 5 events. Save live signing secret. - Business profile fields: Customer service URL
https://resolutionflow.com/contact. Refund/cancellation policy URLhttps://resolutionflow.com/policies. Termshttps://resolutionflow.com/terms. Privacyhttps://resolutionflow.com/privacy. Phone(470) 949-4131. Mailing address per Stripe form (not required on website).
- 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. - Sync against prod:
railway run python -m scripts.sync_stripe_plan_ids. Verifyplan_billingrows havesk_live_*price IDs. - Internal validation (Task 46): 9 scenarios with internal testers whose emails match
INTERNAL_TESTER_EMAILS. - 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 SOAdns1.registrar-servers.com.). Whenwwwwas reconfigured in Railway, the apex record got dropped from the zone.wwwworks (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(deleteresolutionflow.comandwww.resolutionflow.com) +#dnsclear host cache +#socketsflush.
Carry-forward
- Annual pricing intentionally NOT implemented — user wants exit flexibility. Schema columns preserved as nullable.
sync_stripe_plan_ids.pyleaves annual fields NULL. INTERNAL_TESTER_EMAILSparsed 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 addressinfrontend/src/pages/ContactPage.tsxandfrontend/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.