PR #165 (legal/contact pages + MarketingFooter) merged as ba45cfe.
All code blockers for self-serve cutover are now on main.
HANDOFF now flags that user is reporting trouble completing Stripe
live-mode activation (specific blocker not captured this session)
and lists likely candidates so next session can triage quickly:
apex DNS still missing at Namecheap, business-profile URL/phone
rejection, missing prod env var, webhook signing-secret mismatch,
or tax/identity verification step.
Adds the new /contact and /policies URLs to the Stripe business-
profile checklist, and a carry-forward note about the mailing-
address TODO that fires when the P.O. Box arrives. Appends a
2026-05-12 SESSION_LOG entry covering PR #164/#165 merges and the
current Stripe block.
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 — user reports trouble completing it as of 2026-05-12 evening; specific blocker not captured in this session's doc update. Next session: ask the user for the exact Stripe error or stuck step, then triage. Likely candidates given prior session work: apex DNS still missing at Namecheap (Stripe's site verifier can't reach resolutionflow.com apex; www works); Stripe business profile rejecting one of the new URLs (/contact, /policies) or the phone number; missing prod env var (STRIPE_SECRET_KEY / STRIPE_WEBHOOK_SECRET / VITE_STRIPE_PUBLISHABLE_KEY); webhook signing-secret mismatch; tax/identity verification step blocking activation.
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.