From 7c25b42fb04c8c33b60fa3274b83185f7a8ec66d Mon Sep 17 00:00:00 2001 From: Michael Chihlas Date: Sat, 30 May 2026 20:04:48 -0400 Subject: [PATCH] docs(handoff): Phase 2A backend (Tasks 1-12) complete; resume at frontend Task 13 Co-Authored-By: Claude Opus 4.7 --- .ai/HANDOFF.md | 120 +++++++++++++++++++++++++++---------------------- 1 file changed, 66 insertions(+), 54 deletions(-) diff --git a/.ai/HANDOFF.md b/.ai/HANDOFF.md index 2584bb87..0ab4aa01 100644 --- a/.ai/HANDOFF.md +++ b/.ai/HANDOFF.md @@ -2,67 +2,79 @@ # HANDOFF.md -**Last updated:** 2026-05-14 +**Last updated:** 2026-05-30 -**Active task:** Phase O cutover for self-serve signup. All code blockers remain closed on `main`. **Still 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. EIN application via IRS.gov was scheduled for 2026-05-13 — confirm status at next session start. Mailing-address decision (carried forward from 2026-05-12): user enters home address into Stripe's **private** business profile temporarily so live-mode isn't blocked on the P.O. Box; public `ContactPage`/`PoliciesPage` mailing-address TODOs stay "available on request" until the P.O. Box is purchased. Stripe accepts an address update later without re-verification. Apex DNS at Namecheap is still missing (separate user-side issue, only matters once Stripe runs site-verification). Nothing on the code side blocks live-mode flip. +**Active task:** Executing the **L1 AI Tree Builder Phase 2A** plan +(`docs/superpowers/plans/2026-05-29-l1-ai-tree-builder-phase-2a.md`, 19 tasks) via +subagent-driven-development on branch `feat/l1-ai-tree-builder-phase-2a` +(branched from `main` @ `87236b5`; **not pushed**, `main` untouched). -**Bug-pending-capture item (2026-05-12) — likely resolved:** Prior session noted "user reported finding a bug, will send screenshot next session." This session surfaced two concrete UX bugs that were fixed and merged (PR #168): the dashboard "Start a session" CTA was a dead link, and welcome step-2's PSA setup had a near-invisible "Connect now →" link that didn't even persist `primary_psa`. **Confirm with user at next session start whether the screenshot bug was one of these or something else still pending.** +## ⚠️ Tooling note (read first) +This session repeatedly hit an unreliable **Bash output channel** — intermittently +returns stale/cached output (e.g. a unique `echo` probe returned a prior `wc` +result) or fabricated success. The **Read/Write/Edit channel stayed reliable.** +Two derived lessons: +- Run backend tests as `docker exec resolutionflow_backend pytest --override-ini="addopts=" -q`. + Do **NOT** use `-p no:cov` — `pytest.ini` bakes `--cov` into `addopts`, so disabling + the cov plugin makes `--cov` an unrecognized arg and pytest exits non-zero **before + running**, which silently mislabels `&& echo PASS || echo FAIL` chains as failures. +- After any Bash result that matters, cross-check against a `Read`/`grep` of the file. + If a probe returns stale output, **stop and recover the shell** before committing. -## Where this session ended +## Status: backend complete (Tasks 1–12), frontend not started (Tasks 13–19) -Two PRs merged into main: +**Tasks 1–12 — DONE & committed** (17 commits `16b9abf`…`04b5511`). Last reliable +full run: **114 passed** across all 11 Phase 2A backend test files +(test_l1_ai_build_model, test_account_l1_categories_column, test_flow_proposal_l1_source, +test_l1_category_service, test_ai_tree_builder, test_match_or_build, +test_l1_session_service, test_l1_endpoints, test_l1_api_ai_build, test_l1_categories_api, +test_l1_ai_build_flow). 3 alembic migrations applied; head is `1fd88a68b145`. -- **PR #166** (`fe0e692`) — docs/handoff doc updates from prior session. Squash-merged 2026-05-14. -- **PR #168** (`3a35121`) — session expiration policy + dashboard NextStep CTA fix + welcome step-2 PSA CTA reshape. Merge-committed 2026-05-14. Three notable additions: - - `feat(dashboard)` `8d79dd9` — The "Start a session" CTAs on NextStepCard and SetupChecklist used to `Link`-navigate to `/`, leaving the user on the same page (the StartSessionInput lives on the dashboard) with no visible response. Replaced with a `FOCUS_START_SESSION_EVENT` window event the StartSessionInput listens for: scrolls input to viewport top (`scrollIntoView({block:'start'})`), focuses the textarea (with `preventScroll:true` so it doesn't fight the smooth scroll), pulses a `rgba(96,165,250,…)` ring for 900ms. NextStepCard hides itself via local `locallyHidden` state on click so the user isn't double-prompted while typing. SetupChecklist gets the same event-dispatch treatment for its `ran_session` row. - - `feat(welcome)` `dc88797` — Welcome step-2 PSA CTA reshaped. Selecting a real PSA now swaps the single Continue + tiny "Connect now →" link for an explicit two-button choice: `Connect now` (primary, blue — saves `primary_psa` then routes to `/account/integrations`) and `Connect later` (secondary outlined — saves `primary_psa` then continues to step 3). **Important pre-existing bug fixed**: the old subtle Link never actually persisted `primary_psa` before navigating away. Both new buttons do. "No PSA yet" and no-selection states still show the original single Continue. Skip-this-step and Skip-the-rest unchanged. Existing tests pass without edits (testids `welcome-step-2-connect-now` and `welcome-step-2-continue` reused). - - `docs:` `e5b2624` — added `docs/plans/2026-05-13-public-landing-routing-refactor.md`, `docs/architecture/` reports (god-node map + report 2026-05-06, workflows.json/html, workflows-analysis.html), `docs/tutorials/build-a-page.md`, and `abc-feat-self-serve-signup-phase-2-design-20260507-112020.md` at repo root. +What shipped backend-side: +- Migrations/models: `ai_build` session kind; `accounts.enabled_l1_categories` (10-key + default); `FlowProposal.l1_session_id` (+ `source_session_id` nullable + exactly-one + CHECK), `FlowProposalSummary` schema made source_session_id optional + l1_session_id added. +- Services: `l1_category_service` (defaults + hard floor + get/set), `ai_tree_builder` + (constrained node gen, validate, depth cap, `normalize_walked_path`, **skips `meta` + entries**), `match_or_build` (match→suggest→build bands; flow_id normalized to str), + `l1_session_service.start_ai_build_session` / `advance_ai_build` (records node incl. + `node_text`) / flywheel capture in `resolve` / engineer notify in `escalate`. +- Notifications: `l1.session.escalated` event + link `/escalations` + body/title templates; + `_resolve_recipients` now treats an explicit empty `target_user_ids` as "no recipients". +- API: `/l1/intake` dispatches via `match_or_build` (build seeds a hidden + `{"node_type":"meta","category":...}` walked_path entry); `POST /l1/sessions/{id}/next-node`; + `GET /l1/escalations` (require_engineer_or_admin); `GET|PATCH /accounts/me/l1-categories`; + `require_account_owner_or_admin` dep. Config action keys `l1_realtime_build`/`l1_classify`. -`tsc --project tsconfig.app.json --noEmit` clean across all changes. Local vitest blocked by root-owned `node_modules/.vite-temp` (same env issue noted in prior handoffs); CI ran the suite green. +## Resume point — Tasks 13–19 (all frontend + final) -**Two issues filed for session leftovers:** +1. **Task 13 — frontend api/types.** `frontend/src/types/l1.ts` + `frontend/src/api/l1.ts` + are still **Phase-1 stubs** (api/l1.ts is a rough 36-line stub — read it carefully). + Add: `IntakeOutcome`/`IntakeResult` (outcome matched|suggest|out_of_scope|build, + optional session fields, near_miss, category), `TreeNode` union, `NextNodeResult`, + `L1Categories`; methods `nextNode`, `getCategories`, `setCategories`, `escalations`; + retype `intake` to `IntakeResult`. **Carry-forward:** `nextNode` body must include + `node_text` (the rendered node text — backend `advance_ai_build` stores it). +2. **Task 14** — `L1Dashboard.tsx` dispatch on `outcome` (matched/build → walker; + suggest → prompt; out_of_scope → adhoc/escalate prompt). +3. **Task 15** — `L1WalkTreeVariant.tsx` real node rendering via `/next-node` + disclaimer + banner; pass `node.text` as `node_text`; terminal nodes → existing Resolve/Escalate. +4. **Task 16** — new `pages/account/L1CategoriesPage.tsx` + route + nav (owner/admin gated). +5. **Task 17** — `ProposalDetail.tsx` L1-source block (branch on `l1_session_id`); + `EscalationQueuePage.tsx` L1-escalations section via `l1Api.escalations()`. +6. **Task 18** — extend `frontend/e2e/l1-workspace.spec.ts` (network-stubbed); rely on CI + for the run (chromium can't launch here). +7. **Task 19** — full backend suite + `tsc -b`/`npm run lint`/`npm run build`; migration + downgrade/upgrade roundtrip; push branch + open PR to `main` listing deferred items. -- **Issue #171** — Test coverage for the new welcome step-2 "Connect now" path (existing tests still pass but don't exercise the new button's save + redirect-to-integrations behavior). -- **Issue #172** — Repo hygiene: gitignore `core.[0-9]*` + `**/.remember/`, and delete the existing 20MB core dumps (`core.144926`, `core.145678`, `docs/architecture/core.1392564`) and `docs/architecture/.remember/`. Carried forward across multiple sessions. +Frontend gate: `docker exec -w /app resolutionflow_frontend npx tsc -b` and +`docker exec -w /app resolutionflow_frontend npm run build` (per PROJECT_CONTEXT). -Working tree clean except those persistent untracked items (intentionally left for issue #172). +**Working tree:** uncommitted at handoff time — only this `HANDOFF.md` edit (and possibly +`backend/tests/test_l1_api_ai_build.py` if its last lint-clean edit wasn't committed; verify +with `git status` once the shell recovers, then commit WIP). -Single alembic head: `4ce3e594cb87` (no schema changes this session). - -## Resume point - -**First thing next session:** - -1. Confirm with user whether the "bug-pending-capture" screenshot bug from 2026-05-12 was one of the two PR #168 fixes or something else still pending. -2. Check EIN application status (filed 2026-05-13 via IRS.gov). If granted, unblocks the Phase O Stripe live-mode setup chain. - -After that — **Phase O manual ops, all user-side, all gated on EIN landing first:** - -1. **EIN application status check** (user, applied 2026-05-13). -2. **Stripe Dashboard live-mode** (once EIN is in hand): - - 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 = user's home address temporarily (private Stripe field; swap to P.O. Box later without re-verification). EIN = the newly-issued tax ID. -3. **Apex DNS fix at Namecheap** (re-add `@` ALIAS → `c9g7uku8.up.railway.app`, or re-add apex as a Railway custom domain). Becomes the next blocker once Stripe runs site-verification. -4. **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=`, prod Google + Microsoft OAuth credentials. -5. **Bootstrap prod super-admin** via `create_site_admin.py` (PR #167) — already done end-to-end on prod per 2026-05-12 user confirmation. Re-runnable if needed. -6. **Sync Stripe → DB**: `railway run python -m scripts.sync_stripe_plan_ids` (or via `railway ssh`). Verify `plan_billing` rows have `sk_live_*` price IDs. -7. **Internal validation (Phase O Task 46)**: 9 scenarios with internal testers whose emails match `INTERNAL_TESTER_EMAILS`. -8. **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). When `www` was reconfigured in Railway, the apex record got dropped from the zone. `www` works (cert provisioned 2026-05-08 01:40 UTC). User to re-add apex record at Namecheap (ALIAS `@` → `c9g7uku8.up.railway.app`) or re-add the apex as a Railway custom domain. 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 now at `docs/` root (`abc-feat-self-serve-signup-phase-2-design-20260507-112020.md`) as of this session. 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. -- `backend/scripts/create_site_admin.py` is the durable site-admin bootstrap tool — idempotent. Three modes: `--send-reset`, `--print-reset`, `--promote-only`. Run from inside the deployed backend container via `railway ssh`. -- Bot-crawlability of legal pages: still SPA-rendered. Stripe didn't enforce content scraping last time (issue was 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`. -- **Branch hygiene note (process learning):** PR #168 ended up bundling unrelated work — session expiration policy (the original scope of `feat/session-expiration-policy`) plus dashboard CTA fixes plus welcome step-2 reshape. The mixed scope was deliberate (user wanted it on the same PR), but worth flagging for future PRs: if onboarding-UX work continues, branch it separately from auth/session work. +## Carry-forward (Phase O — separate, still user-side, gated on EIN) +Phase O self-serve cutover (Stripe live-mode, apex DNS, Railway prod env, flag flip) remains +the prior active task — all code blockers closed; blocked on user's EIN. See SESSION_LOG / +git history for detail. Not touched this session.