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>
9.0 KiB
TODO.md
Backlog of work NOT currently active. Read only when
CURRENT_TASK.mdstatus iscomplete. 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 lintcurrently reports 24 warnings (0 errors): mostlyreact-hooks/exhaustive-depsplus 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
filterwarningsignores added inwip(handoff): restore backend suite to green. Codex added narrowResourceWarningfilters 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-testidattributes to e2e-critical interactive elements. PR #152 fixed five Playwright tests by chasing UI-text changes (Sessions→Session History,Account Settings→Account 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 stabledata-testidattributes on the targeted elements (page heading wrappers, tab nav, primary action buttons) and switching tests togetByTestIdwould make these immune to copy/route renames. Scope it small — start withSessionHistoryPageheading, the AI/Flow Sessions tab buttons, the per-sessionResumebutton, and the command-palette FlowPilot option. -
Per-test transactional rollback in
test_dbfixture. Bigger engineering than xdist (which we already shipped). Instead ofDROP SCHEMA public CASCADEper 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-testmonfor 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
currentChatRefguard is a silent return —handleSend,handleTaskSubmit,selectChat,refreshFacts,refreshActiveFix, andrefreshPreviewall bail withif (currentChatRef.current !== sentForChatId) returnwhen 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 aconsole.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 everycurrentChatRef.current = ...assignment vs everysetActiveChatId(...)call to make sure they're paired everywhere. -
Allow peer-tech to escalate a colleague's session. Today
POST /ai-sessions/{session_id}/handoffin endpoints/session_handoffs.py:48 filters byAISession.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 torequire_engineer_or_admin+ same-account scope. Add ahanded_off_byaudit column (already exists onSessionHandoff) 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-hoverTailwind class doesn't resolve.frontend/src/components/layout/CommandPalette.tsx:450-451usesbg-card-hoveras a Tailwind utility, but Tailwind v4 generatesbg-{token}from--color-{token}— and the token infrontend/src/index.css:15is--color-bg-card-hover, which generatesbg-bg-card-hover, notbg-card-hover. So those classes silently produce nothing. Other call sites (KnowledgeBaseCards, TeamSummary, ProposalBanner) use the explicithover:bg-[var(--color-bg-card-hover)]form which works. Fix: change the CommandPalette classes to the explicit-var form, OR add a--color-card-hoversemantic mapping in index.css alongside--color-card. Surfaced 2026-05-01 during impeccable polish sweep. -
ConcludeSessionModalpaused/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); forescalatedpre-check Ticket Notes + Client Update; forpausedpre-check Client Update only. One "Generate" button fires all selected in parallel via existingaiSessionsApi.generateStatusUpdate(...)(already supports the threeaudiencevalues: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 atAccountDetailPage.tsx:443-445still offeredfree / pro / team— the deadteamslug (renamed toenterprisein migration4ce3e594cb87, 2026-05-07) and missingstarter/enterprise. Selecting "Team" sends{plan:"team"}toPUT /admin/accounts/{id}/subscription/plan, which 400s onif data.plan not in ("free","pro","starter","enterprise")(admin.py:994, duplicated at :975). The 400 detail was swallowed by a generictoast.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: backendadmin.py:975+:994(tuple, twice),schemas/admin.py:128(AdminAccountCreate.planLiteral), frontendAccountDetailPage.tsxdropdown,AccountsPage.tsxcreate-account dropdown,types/admin.ts+types/account.ts+types/billing.ts,hooks/useSubscription.ts(isPaidPlan),components/subscription/CheckoutButton.tsx(planLabels). Source of truth: theplan_limitstable (rows: free/starter/pro/enterprise) —PlanLimitWithBillingResponsealready exposesis_public+sort_order+display_namefor ordering/labels. End state (B): admin dropdown + pricing/checkout derive options from a plans endpoint backed byplan_limits(filteris_public, order bysort_order, label fromdisplay_name); backend validation checks against actualplan_limitsrows instead of a hardcoded tuple. Trivial first commit (land anytime to unblock the admin tool): fix theAccountDetailPagedropdown toFree / Starter / Pro / Enterpriseand surface the backend error detail in the toast. ⚠️ The'team'string inTree.visibility/StepLibrary.visibilityis a separate domain (shared-with-account) — do NOT touch it.