wip(handoff): pr #164 cutover blockers + doc refresh + dns triage
Updates HANDOFF.md, CURRENT_TASK.md, SESSION_LOG.md to reflect this session's work: PR #164 ready for review (5 commits closing the last self-serve cutover code blockers), Phase O manual-ops sequence as the resume point, and the apex-DNS / Edge-HSTS issues open on the user's side. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,56 +2,47 @@
|
||||
|
||||
# HANDOFF.md
|
||||
|
||||
**Last updated:** 2026-05-07 (PR #162 CI investigation/fixes)
|
||||
**Last updated:** 2026-05-08
|
||||
|
||||
**Active task:** PR #162 (`feat/self-serve-signup-phase-2`) is open in Gitea. Current session is resolving its failing checks.
|
||||
**Active task:** PR #164 (`feat/billing-plan-taxonomy`) open in Gitea with 5 commits at head `2c9f5e9`. Closes the last code blockers for self-serve cutover. After merge, only manual ops remain (Stripe live-mode Dashboard config, Railway prod env vars, internal validation, flag flip). PR #162 and #163 merged into main this session as squash commits `f1be3ab` and `dad5e1f`.
|
||||
|
||||
## Where this session ended
|
||||
|
||||
PR #162 originally failed quickly in Gitea CI. Public Gitea status metadata was available, but job logs redirected to login and no `GITEA_TOKEN` was present. The branch was pushed over SSH.
|
||||
PR #164 commits (oldest → newest):
|
||||
|
||||
Fixed environment drift first:
|
||||
1. `ba36c47 feat(billing): reconcile plan taxonomy and add Stripe sync script` — migration `4ce3e594cb87` renames `plan_limits.plan='team'` → `'enterprise'` (defensive update of any subscriptions on the old slug; dev had zero), adds `starter` row with caps interpolated between free and pro. Code rename across schemas, `Subscription` paid-plan checks, admin endpoints, frontend `useSubscription`. Resource visibility (`Tree.visibility='team'`, `StepLibrary.visibility='team'`) is a separate domain and intentionally untouched. New `backend/scripts/sync_stripe_plan_ids.py` — idempotent upsert of `plan_billing` rows from Stripe products by exact name match, picks active monthly recurring price, leaves annual fields NULL by design.
|
||||
2. `a628b24 chore(dev): pass STRIPE_* env to backend container` — wires `STRIPE_*` + `SELF_SERVE_ENABLED` + `INTERNAL_TESTER_EMAILS` through `docker-compose.dev.yml`. New repo-root `.env.example`.
|
||||
3. `8494366 feat(billing): add INTERNAL_TESTER_EMAILS allowlist for self-serve soft cutover` — `Settings.is_internal_tester` + `is_self_serve_active_for`, new `get_current_user_optional` dep, `/config/public` honors allowlist for authenticated callers, `/auth/register` allows allowlisted emails without invite code. 5 regression tests in `test_config_public.py`.
|
||||
4. `8649a4a docs: refresh CURRENT-STATE, ROADMAP, README, DECISIONS for self-serve cutover` — CURRENT-STATE bumped with PR #159–164 entries; ROADMAP got a "Status as of 2026-05-07" preamble (historical content preserved underneath); README fixed legacy `patherly_postgres` and `UI-DESIGN-SYSTEM.md` references; DECISIONS appended two entries.
|
||||
5. `2c9f5e9 fix(frontend): page-title — escapes + propagate plan taxonomy through frontend types` — `LandingPage.tsx` had `—` (six literal characters) inside JSX attribute strings, rendering as literal text in browser tabs. Replaced with literal em dash. PageMeta default tagline updated from "Decision Tree Platform" (stale) to "AI-Powered Troubleshooting for MSPs". Fixed TS errors that surfaced from the previous taxonomy commit not propagating through frontend types — `types/{account,admin,billing}.ts`, `admin/{AccountsPage,InviteCodesPage}.tsx`, `AccountSettingsPage.tsx`, `subscription/CheckoutButton.tsx`. tsc -b clean, lint clean.
|
||||
|
||||
- Standardized backend native/dev/CI Python on 3.12.13 to match Docker.
|
||||
- Added `.python-version`.
|
||||
- Rebuilt `backend/venv` from pyenv Python 3.12.13 and verified native `pytest --version` / `alembic --version` with explicit local env.
|
||||
- Updated Gitea CI backend/e2e Python setup to 3.12.
|
||||
Stripe state (test mode via MCP, livemode=false): 3 active products (Starter $19.99/mo, Pro $29.99/mo, Enterprise no price); leftover Enterprise `$500/mo` test price archived (had to clear `default_price` on the product first); `plan_billing` populated for all three tiers in dev DB via `sync_stripe_plan_ids.py`.
|
||||
|
||||
Fixed Gitea runner assumptions next:
|
||||
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 handoff as "do not stage").
|
||||
|
||||
- Added `actions/setup-node@v4` with Node 20 to Gitea frontend and e2e jobs.
|
||||
- Pushed `fix(ci): set up node in gitea workflow`.
|
||||
|
||||
Local frontend validation then exposed real lint failures in Phase 2 React code under the current lint stack. The current WIP fixes:
|
||||
|
||||
- `react-refresh/only-export-components` for exported pure helpers used by tests/shared invite OAuth code.
|
||||
- `react-hooks/set-state-in-effect` warnings where local state intentionally mirrors route/config/cache state.
|
||||
- `react-hooks/purity` warnings from `Date.now()` during render.
|
||||
- Redundant loading-state write in pricing page.
|
||||
|
||||
Validation after those frontend changes:
|
||||
|
||||
- `docker exec -w /app resolutionflow_frontend npm run lint` passed.
|
||||
- `docker exec -w /app resolutionflow_frontend npm run test:coverage` passed (`198` tests).
|
||||
- `docker exec -w /app -e NODE_OPTIONS=--max-old-space-size=4096 resolutionflow_frontend npm run build` passed.
|
||||
|
||||
Known local noise:
|
||||
|
||||
- React `act(...)` warnings appeared in existing tests during coverage but did not fail the suite.
|
||||
- Vite emitted large chunk warnings during build.
|
||||
- Unrelated dirty/untracked files remain and should not be staged unless explicitly requested: `docker-compose.dev.yml`, `.env.example`, `abc-feat-self-serve-signup-phase-2-design-20260507-112020.md`, `core.*`, `docs/architecture/`, `docs/tutorials/`.
|
||||
Single alembic head: `4ce3e594cb87` after PR #164 merges (was `c6cbfc534fad`).
|
||||
|
||||
## Resume point
|
||||
|
||||
1. Commit the frontend lint fixes and `.ai/` handoff updates with the required Codex trailer.
|
||||
2. Push `feat/self-serve-signup-phase-2`.
|
||||
3. Poll Gitea PR #162 statuses for the new head SHA:
|
||||
`curl -fsSL https://gitea.resolutionflow.com/api/v1/repos/chihlasm/resolutionflow/statuses/<sha> | python -m json.tool`
|
||||
4. If statuses are still pending, report that local frontend CI is green and Gitea runner work is queued/running. If a check fails, public statuses may show only the context/description; logs require authenticated Gitea access.
|
||||
1. Verify PR #164 CI green:
|
||||
`curl -fsSL https://gitea.resolutionflow.com/api/v1/repos/chihlasm/resolutionflow/commits/2c9f5e9/status | python -m json.tool`
|
||||
2. Squash-merge PR #164.
|
||||
3. **Phase O manual ops** (after merge):
|
||||
- Stripe Dashboard live-mode: 3 Products, 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.
|
||||
- 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.
|
||||
- Run sync against prod backend: `railway run python -m scripts.sync_stripe_plan_ids`. Verify `plan_billing` rows have `sk_live_*` price IDs.
|
||||
4. Internal validation (Phase O 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 this 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. Cert IS valid on the wire (proven by `curl --resolve` returning 200 OK from the user's box).
|
||||
|
||||
## Carry-forward
|
||||
|
||||
- Phase O manual ops remain pending after PR review/merge: Stripe live setup, internal validation, feature-flag flip.
|
||||
- Backend env: `SALES_LEAD_RECIPIENT_EMAIL`.
|
||||
- Frontend env: `VITE_SELF_SERVE_ENABLED`, `VITE_GOOGLE_CLIENT_ID`, `VITE_MS_CLIENT_ID`, `VITE_OAUTH_REDIRECT_BASE`, `VITE_CALENDLY_URL`.
|
||||
- Single alembic head remains `c6cbfc534fad`; Phase 2 added no migrations.
|
||||
- Annual pricing intentionally NOT implemented — user wants exit flexibility ("want to be able to exit if necessary without breaching any terms"). Schema columns (`annual_price_cents`, `stripe_annual_price_id`) preserved as nullable for future re-enable. `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 from this session at `~/.gstack/projects/chihlasm-resolutionflow/abc-feat-self-serve-signup-phase-2-design-20260507-112020.md`. Captures the "documentation builder" thesis (cut branching Flows from pilot UI, focus on Day 1 onboarding checklist + 3 deep-capture procedures + Hudu/IT Glue/CW output). Pre-build assignment: 3 cold calls with external Directors of Onboarding before scoping the build. NOT yet adopted as roadmap — gated on the validation calls.
|
||||
- Frontend lint shows 3 warnings in `coverage/` (auto-generated). Untouched.
|
||||
- Backend env: `SALES_LEAD_RECIPIENT_EMAIL`. 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`.
|
||||
|
||||
Reference in New Issue
Block a user