docs(handoff): Phase 2A backend (Tasks 1-12) complete; resume at frontend Task 13
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
120
.ai/HANDOFF.md
120
.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 <path> --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 <PSA> 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=<allowlist>`, 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.
|
||||
|
||||
Reference in New Issue
Block a user