Files
resolutionflow/.ai/TODO.md
Michael Chihlas 5c38fb8904
All checks were successful
Mirror to GitHub / mirror (push) Successful in 5s
CI / frontend (pull_request) Successful in 6m55s
CI / e2e (pull_request) Successful in 10m27s
CI / backend (pull_request) Successful in 11m42s
docs(decisions): record plan-tier taxonomy centralization decision (Option B)
Captures the 2026-05-29 decision to derive admin plan dropdown + validation
from the plan_limits table rather than hand-duplicating the allow-list across
6+ sites. Triggered by the prod "AI sessions down" report that traced to the
admin dropdown still offering the dead 'team' slug. Adds the matching backlog
entry to TODO.md with duplication sites enumerated.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 11:25:28 -04:00

9.0 KiB

TODO.md

Backlog of work NOT currently active. Read only when CURRENT_TASK.md status is complete. Format: - [ ] short description — optional link to issue/PR

Up next

None selected. Pick from the backlog below or 03-DEVELOPMENT-ROADMAP.md.

Backlog

  • Frontend lint warnings cleanup. npm run lint currently reports 24 warnings (0 errors): mostly react-hooks/exhaustive-deps plus a few unused eslint-disable directives. Either fix them or audit known-safe ones and add/remove eslint-disable comments intentionally. Not blocking CI today.

  • Audit filterwarnings ignores added in wip(handoff): restore backend suite to green. Codex added narrow ResourceWarning filters for unclosed socket/transport/event-loop noise from pytest-asyncio teardown. Worth periodically reviewing whether those are still needed (e.g. when bumping pytest-asyncio) — if a real warning appears in those forms it would be silenced.

  • Add data-testid attributes to e2e-critical interactive elements. PR #152 fixed five Playwright tests by chasing UI-text changes (SessionsSession History, Account SettingsAccount Management, /assistant/pilot, "Flow Sessions" tab, Resume button on session cards). Each was a one-line selector update, but every UI churn re-breaks them. Adding stable data-testid attributes on the targeted elements (page heading wrappers, tab nav, primary action buttons) and switching tests to getByTestId would make these immune to copy/route renames. Scope it small — start with SessionHistoryPage heading, the AI/Flow Sessions tab buttons, the per-session Resume button, and the command-palette FlowPilot option.

  • Per-test transactional rollback in test_db fixture. Bigger engineering than xdist (which we already shipped). Instead of DROP SCHEMA public CASCADE per test, wrap each test in a savepoint and rollback at teardown. ~30-40% additional speedup on top of xdist for test-DB-heavy tests. Real refactor; only worth it if the suite gets significantly larger or runs more frequently.

  • Consider pytest-testmon for PR-time test selection. Tracks which tests touched which source files and only re-runs affected ones. Best for small PRs touching ~few files. Adds cache-invalidation complexity; only worth it if the suite stays painfully long even after xdist.

  • AssistantChatPage currentChatRef guard is a silent returnhandleSend, handleTaskSubmit, selectChat, refreshFacts, refreshActiveFix, and refreshPreview all bail with if (currentChatRef.current !== sentForChatId) return when stale. This is by design for chat switching, but it also silently masked the prefill-ref bug fixed in PR #153 — the user just saw "no AI response" with no log, no toast, no Sentry event. Either (a) log a console.warn/Sentry breadcrumb on the mismatch path so future drift is visible, or (b) split "expected stale" (chat switch) from "unexpected stale" (ref never updated) so only the latter alerts. Pair with an audit of every currentChatRef.current = ... assignment vs every setActiveChatId(...) call to make sure they're paired everywhere.

  • Allow peer-tech to escalate a colleague's session. Today POST /ai-sessions/{session_id}/handoff in endpoints/session_handoffs.py:48 filters by AISession.user_id == current_user.id, so only the session owner can escalate. Real MSP shops have peer hand-offs: Junior A is on lunch, Junior B sees the session is stuck and should be able to escalate it. Auth tweak: switch from session-owner check to require_engineer_or_admin + same-account scope. Add a handed_off_by audit column (already exists on SessionHandoff) so the original-owner-vs-actual-escalator distinction is preserved. Surfaced from /plan-eng-review on the Escalation-Mode wedge plan; v1 wedge demo doesn't need this (solo-founder pilot), but capture for v2 once 3+ pilots are live and a peer-claim need surfaces.

  • Mobile/responsive design for EscalationQueue + handoff-context screen. Pre-PMF wedge demo targets desktop only — MSP techs work on laptops/desktops in shop environments. Once 3+ paying customers exist and a tech requests mobile (likely on-call use case), spec the responsive behavior: stacked card layout below sm: breakpoint, full-bleed handoff-context overlay on mobile, swipe-to-claim gesture instead of Pick Up button. Surfaced from /plan-design-review on the Escalation-Mode wedge plan.

  • bg-card-hover Tailwind class doesn't resolve. frontend/src/components/layout/CommandPalette.tsx:450-451 uses bg-card-hover as a Tailwind utility, but Tailwind v4 generates bg-{token} from --color-{token} — and the token in frontend/src/index.css:15 is --color-bg-card-hover, which generates bg-bg-card-hover, not bg-card-hover. So those classes silently produce nothing. Other call sites (KnowledgeBaseCards, TeamSummary, ProposalBanner) use the explicit hover:bg-[var(--color-bg-card-hover)] form which works. Fix: change the CommandPalette classes to the explicit-var form, OR add a --color-card-hover semantic mapping in index.css alongside --color-card. Surfaced 2026-05-01 during impeccable polish sweep.

  • ConcludeSessionModal paused/escalated step forces single-artifact choice — should allow multi-select. frontend/src/components/assistant/ConcludeSessionModal.tsx ~lines 430-474 ("Paused/Escalated: status update options"). Today the engineer clicks ONE of Ticket Notes / Client Update / Email Draft, the buttons disappear, and the result replaces them. Real MSP escalations almost always need at least two: technical notes for the next engineer's PSA AND a non-technical client update. Same for pause (client update + ticket notes for context when resuming). Recommended shape: multi-select with smart defaults — three checkboxes (☑ Ticket Notes ☑ Client Update ☐ Email Draft); for escalated pre-check Ticket Notes + Client Update; for paused pre-check Client Update only. One "Generate" button fires all selected in parallel via existing aiSessionsApi.generateStatusUpdate(...) (already supports the three audience values: ticket_notes, client_update, email_draft). Each result renders in its own card with its own Copy / Post-to-PSA / Send-Email action. Surfaced 2026-05-01. Feature work, not polish — touches streaming wiring for parallel calls.

  • Centralize plan-tier taxonomy — derive admin plan dropdown (and validation) from plan_limits, not hardcoded lists. Chose Option B over a one-line patch (see DECISIONS.md 2026-05-29). Surfaced by a prod bug (2026-05-28): the admin "Change Plan" dropdown at AccountDetailPage.tsx:443-445 still offered free / pro / team — the dead team slug (renamed to enterprise in migration 4ce3e594cb87, 2026-05-07) and missing starter/enterprise. Selecting "Team" sends {plan:"team"} to PUT /admin/accounts/{id}/subscription/plan, which 400s on if data.plan not in ("free","pro","starter","enterprise") (admin.py:994, duplicated at :975). The 400 detail was swallowed by a generic toast.error('Failed to update plan') (AccountDetailPage.tsx:196), so it presented as "AI sessions are down" (real cause: owner account had no paid plan; AI is plan-gated). Root cause of the root cause: the allowed-plan list is hand-duplicated across ≥6 sites and drifted (2nd such incident). Duplication sites to consolidate: backend admin.py:975 + :994 (tuple, twice), schemas/admin.py:128 (AdminAccountCreate.plan Literal), frontend AccountDetailPage.tsx dropdown, AccountsPage.tsx create-account dropdown, types/admin.ts + types/account.ts + types/billing.ts, hooks/useSubscription.ts (isPaidPlan), components/subscription/CheckoutButton.tsx (planLabels). Source of truth: the plan_limits table (rows: free/starter/pro/enterprise) — PlanLimitWithBillingResponse already exposes is_public + sort_order + display_name for ordering/labels. End state (B): admin dropdown + pricing/checkout derive options from a plans endpoint backed by plan_limits (filter is_public, order by sort_order, label from display_name); backend validation checks against actual plan_limits rows instead of a hardcoded tuple. Trivial first commit (land anytime to unblock the admin tool): fix the AccountDetailPage dropdown to Free / Starter / Pro / Enterprise and surface the backend error detail in the toast. ⚠️ The 'team' string in Tree.visibility / StepLibrary.visibility is a separate domain (shared-with-account) — do NOT touch it.