# CURRENT_TASK.md

**Active task:** Phase O cutover for self-serve signup. All code blockers are now closed on `main`. Only user-side manual ops remain: apex DNS fix at Namecheap, Stripe Dashboard live-mode config (with the new `/contact` and `/policies` URLs surfaced in the business profile), Railway prod env vars, internal validation pass, public flag flip. See `.ai/HANDOFF.md` for the resume point.

## Recently shipped

- **2026-05-14 — PR #168** Session expiration policy + dashboard onboarding-CTA fix + welcome step-2 PSA CTA reshape. Merge-committed into main as `3a35121`. Three threads bundled on one branch (`feat/session-expiration-policy`):
  - **Session expiration policy** (original branch scope): 3d idle / 14d absolute, per-account override, bulk revoke. New `AccountSecuritySettingsPage`, `RevokeSessionsModal`, `SessionExpiryToast`, `useAuthSessionExpiry` hook; backend dependencies in `accountSecurity.ts`.
  - **Dashboard onboarding CTA fix** (`8d79dd9`): The "Start a session" CTAs on `NextStepCard` and `SetupChecklist` used to `<Link to="/">` while themselves rendered on `/`, so clicks were silent no-ops. Replaced with a `FOCUS_START_SESSION_EVENT` window event that `StartSessionInput` listens for — scrolls itself into view (top of viewport), focuses the textarea, pulses a blue ring 900ms. `NextStepCard` hides itself locally on click so the prompt doesn't linger while the user types.
  - **Welcome step-2 PSA CTA reshape** (`dc88797`): Selecting a real PSA now swaps `[Continue] [Skip]` for `[Connect <PSA> now] [Connect later] [Skip this step]`. Primary blue button saves `primary_psa` and routes to `/account/integrations`; "Connect later" saves and continues to step 3. **Pre-existing bug fixed**: the old subtle "Connect now →" link never persisted `primary_psa` before navigating. Now it does. "No PSA yet" / no-selection states still show the original single Continue.
- **2026-05-14 — PR #166** Docs/handoff doc updates carrying forward PR #164/#165 state and EIN blocker. Squash-merged into main as `fe0e692`.
- **2026-05-12 — PR #167** `backend/scripts/create_site_admin.py` site-wide super-admin bootstrap script. Squash-merged into main as `e50a215`. Idempotent CLI, three modes (`--send-reset`, `--print-reset`, `--promote-only`). Uses `ADMIN_DATABASE_URL` (BYPASSRLS). User confirmed end-to-end success against prod via `railway ssh` 2026-05-12 evening.
- **2026-05-12 — PR #165** Legal/contact pages for Stripe site review. Squash-merged into main as `ba45cfe`. Three new SPA pages: `/policies` (consolidated Customer Policies — refunds, cancellation, U.S. legal/export restrictions, promotional terms; anchor IDs per subsection), `/contact` (phone (470) 949-4131, support/sales/billing/security inboxes, response-time SLAs), `/promotions` (stub satisfying Policies §6.2). New `MarketingFooter` component (`components/common/MarketingFooter.tsx`) extracted from inline landing footer; mounted on `/landing`, `/pricing`, `/contact-sales` so all four legal links (Privacy/Terms/Policies/Contact) are reachable from every marketing surface. Component reuses existing `landing-footer*` CSS — must be inside a `.landing-page` wrapper (documented in JSX comment). Privacy and Terms closing sections updated to point at `/contact` + `/policies` with correct per-area inboxes; stale `hello@` mailto removed everywhere. Mailing address left as TODO comments in both `ContactPage.tsx` and `PoliciesPage.tsx`, rendered publicly as "available on request" until P.O. Box is purchased. tsc + eslint clean.
- **2026-05-08 — PR #164** Plan taxonomy reconciliation + `INTERNAL_TESTER_EMAILS` allowlist + Stripe sync script + page-title fix + frontend taxonomy followups + doc refresh. 5 commits on `feat/billing-plan-taxonomy` from main (`dad5e1f`); HEAD `2c9f5e9`. Migration `4ce3e594cb87` renames `plan_limits.plan='team'` → `'enterprise'` and adds `starter` row (caps interpolated between free and pro: `max_trees=10`, `sessions=75`, `ai=15/mo`). Resource visibility (`Tree.visibility='team'`, `StepLibrary.visibility='team'`) is a separate domain and intentionally untouched. New `backend/scripts/sync_stripe_plan_ids.py` upserts `plan_billing` rows from Stripe products by exact name match — annual fields stay NULL by design (user explicitly skipping annual pricing for exit flexibility). `Settings.is_internal_tester` + `is_self_serve_active_for` centralize the allowlist + global-flag check; new `get_current_user_optional` dep; `/config/public` honors allowlist for authenticated callers; `/auth/register` allows allowlisted emails without invite code. LandingPage page-title bug — `—` inside JSX attribute strings was rendering as 6 literal characters in browser tabs; replaced with literal em dash. PageMeta default tagline updated from "Decision Tree Platform" to "AI-Powered Troubleshooting for MSPs". 86/86 passing across subscription/billing/plan/invite/admin sweep; tsc + lint clean. See `.ai/DECISIONS.md` for the two architectural entries (taxonomy reconciliation, allowlist).
- **2026-05-06 — PR #163** Seed test users marked email-verified. Squash-merged into main as `dad5e1f`.
- **2026-05-06 — PR #162** Self-serve signup Phase 2 (frontend cutover). 18 commits across Tasks 27–44 of the plan. Backend remainders + frontend billing foundation + auth surfaces (OAuth + accept-invite + verify-email) + welcome wizard + dashboard redesign (TrialPill, NextStepCard, unified checklist) + public surfaces (`/pricing`, `/contact-sales`) + beta-signup deprecation. Squash-merged into main as `f1be3ab`. Single alembic head was `c6cbfc534fad` (no new migrations in Phase 2; PR #164 adds `4ce3e594cb87`).
- **2026-05-02 — PR #159** In-product User Guides rewrite. Merged into `main`. Replaced 15 feature-dump guides with 43 problem-oriented Diátaxis how-tos grouped under 10 categories. Dropped Maintenance Flows / AI Assistant / Flow Assist Sparkles (UI no longer exists). Renamed Step Library → Solutions Library. Authored 14 net-new how-tos for FlowPilot-era surfaces (tasklane keyboard flow, what-we-know, resolve, escalate, record-fix-outcome, post-docs-to-ticket, share-update, pause-and-leave, build-script-from-scratch, open-suggested-flow, pin-a-flow, invite-teammate, etc.). Schema additions: `category`, optional `relatedSlugs`; hub renders category sections; detail page renders related-guides footer. Fixed rendering bug where `**bold**` in `step.tip` rendered literally. Killed misleading "N sections" subtitle on guide cards. Browser-verified against engineer + owner login (sidebar labels, account sub-pages, pilot-screen header buttons, Tasks panel, integration form). Two unverified items intentionally deferred: change-teammate-role (requires non-owner test member to inspect role-change control) and detailed Resolve / Escalate modal contents (Resolve gated by 6 pending tasks in test data). tsc and Vite build clean.
- **2026-05-01 — PR #158** Session-screen UX impeccable pass + tasklane keyboard flow. Merged into `main` as `5e10005`.
  - **Impeccable pass** (5 sub-passes — distill / quieter / layout / typeset / polish): score 24/40 → 33/40. Removed the duplicate "Suggested checks" chip strip; added an inline `Next steps · N pending in Tasks` cue above the latest action-bearing AI bubble; consolidated the desktop session header to Resolve + Escalate + ⋯ kebab (Context / New Ticket / Update Ticket / Pause now under the kebab, mobile kebab gained Context + New Ticket parity); centered the messages column to `max-w-3xl` to match the composer; bubbles dropped to `rounded-xl`. Decoration sweep: dropped 3px side stripes (TaskLane done states, all 6 ProposalBanner modes, WhatWeKnowItem rows), gradient backgrounds (WhatWeKnow + every banner), accent borderTop on TaskLane header, backdrop-blur on handoff overlay, animate-pulse-amber ring in VerifyingBanner, bordered avatar boxes in banners. Type sweep: 14 distinct sizes → 5-step scale (10/11/12/13/14px). Icon disambiguation: `MessageCircleQuestion` split into `Pencil` (Answer CTA) + `HelpCircle` (per-check explainer). Dead `font-sans` audit (12 sites) and double `text-xs` cleanups.
  - **TaskLane keyboard-first flow** (real feature): Enter submits + auto-advances to next pending task, Shift+Enter newline, Esc cancels, focus jumps to Send Responses after the last submission. Mouse path also auto-advances. Subtle hint row teaches the shortcut.
  - **Banner ↔ script panel linked**: collapsing or dismissing the ProposalBanner now also hides the InlineNoTemplateDialog / TemplateMatchPanel; recording any outcome closes both surfaces.
  - **WhatWeKnow collapsible**: per-session preference in `sessionStorage` (`rf-whatweknow-collapsed:{sessionId}`); auto-collapses on first render at ≥5 facts.
  - **Side fix**: `ParameterizationPreview.tokenize()` word-boundary guard prevents over-eager highlighting of short values like `"D"` (no longer lights up every capital D in `Get-ADUser`).
  - Validation: tsc clean, ESLint clean, Vite build clean. Type-check + lint passed at every commit boundary.
- **2026-05-01 — PR #156** Suggested-fix `applied_pending` non-terminal outcome. Merged into `main` as `3ba4532`. Adds:
  - Schema/API: `FixStatus="applied_pending"`, `pending_reason` Text column, migration `c0f3a4b7e91d`. `PATCH /suggested-fixes/{id}/outcome` accepts pending, requires notes, stamps `applied_at` only.
  - UI: `PendingBanner` (info-tone, worked / didn't / update reason / dismiss). "Waiting to verify…" overflow option in `VerifyingBanner`. Nudge "Still checking" records pending with a reason. Page-level Resolve auto-patches pending → success before resolution flow; page-level Escalate intercepts pending the same way verifying/partial does.
  - Generators: `resolution_note_generator` and `escalation_package_generator` system prompts handle the new status without real-looking examples.
  - Tests: 4 new in `test_fix_outcome_endpoint.py` (21/21 suite green); prompt anti-parrot guardrail green; tsc + Vite build clean.
  - QA report: `.gstack/qa-reports/qa-report-pending-verification-2026-04-30.md` (5/7 scripted checks PASS with concrete evidence; 2 entry-path checks deferred — same handlers verified via tested transitions).
- **2026-04-30 — PR #155** Escalation Mode wedge merged as `ac42f97`. Senior-tech magic-moment screen. Plan: [`docs/plans/2026-04-27-escalation-mode-wedge-design.md`](../docs/plans/2026-04-27-escalation-mode-wedge-design.md).

## Two-metric framing (Escalation Mode — read before quoting numbers)

The in-product `GET /analytics/flowpilot/escalations` endpoint measures *post-claim time-to-first-action*. The "minutes recovered" sales claim is `manual_baseline − in_product_metric`. Manual baseline comes from the founder's stopwatch on the next 5 escalations. Don't roll the in-product number alone into "minutes recovered" — that's the apples-to-oranges miscount Codex caught.

## Kill-switch (Escalation Mode)

Week 8: if 0 of 3 pilots produce a verifiable hours-saved-per-week number above 1.0, revisit the wedge.

## Notes for next session

- Drive checks 1 (VerifyingBanner overflow → "Waiting to verify…") and 5 (nudge "Still checking" with 3+ post-apply messages) in real pilot usage to close the QA gap left by `/qa` (the tested handlers cover the same mutations, but the entry-path UI rendering wasn't exercised end-to-end).
- Consider monitoring how often pending fixes get parked vs resolved — if engineers report losing track across sessions, revisit the cross-session "Follow-ups" dashboard rollup that was scoped out.
- After PR #158 lands in real ticket flow, eyeball the keyboard-hint contrast and the WhatWeKnow auto-collapse-at-5 threshold — both were judgment calls (5 was a guess; the contrast bump from `/70` to full muted-foreground was based on my read, not real screen testing). Adjust if the 5-fact threshold feels too aggressive or too lenient mid-session.
- Two follow-ups logged in `.ai/TODO.md` from the impeccable pass: `ConcludeSessionModal` paused/escalated step should allow multi-select (Ticket Notes + Client Update + Email Draft simultaneously) — real feature work; `bg-card-hover` Tailwind class doesn't resolve in `CommandPalette` — two-line fix.
